Designing a Signing Service for Miniscript Policies. Part 1.

Bryan Elee
3 min readJul 5, 2023

--

Bitcoin is able to express complex and powerful spending conditions using Script, a language that Bitcoin uses to define the spending conditions of UTXOs. This capability is currently underused because actually constructing scripts for non-trivial tasks is cumbersome: it’s hard to verify their correctness and security, and even harder to find the most economical way to write things.

That’s only part of the problem: even when you know the right script, designing applications and protocols to negotiate and construct transactions using it needs a lot of ad-hoc development work each time a new construction is designed.

Enter Miniscript, a language for representing Bitcoin Scripts in a structured way, enabling efficient analysis, composition, generic signing and more. Miniscript enables wallet software developers to take advantage of the full power enabled by Bitcoin Script, introducing new possibilities for self custody.

Miniscript is a relatively new technology, and we’ve barely scratched the surface of what we can do with it. I am currently working on a summer of bitcoin project for Ledger, that intends to build on its (miniscript) capabilities, to develop an easy-to-program signing server.

The signing server provides the following features:

  • Enforce preset rules (spending conditions, not miniscript) on transactions.
  • The service cannot initiate a transaction on its own.
  • Stealing a user’s key is not sufficient to steal funds.
  • The user can recover funds if the service is no longer available, after a given period of time (as specified by the locking script).

We achieve the above in a trust-less way using miniscript as illustrated below

Miniscript #1

and(pk(MY_KEY),pk(SIGNING_SERVER_KEY))

This miniscript is simple and achieves part of our goals, it’s satisfaction includes both the ours and the signing server signature. Our funds are protected if we lose our keys, or the servers keys gets leaked. But with this policy we could lose access to our funds in a scenario where were our keys are lost completely or the server ceases to exist.

We will fix this with another miniscript.

Miniscript #2

and_v(v:pk(user),or_d(pk(server_key),older(12960)))

With this miniscript we can spend the funds without the server, about 3 months after the utxo was mined.

Miniscript #3

andor(pk(MY_KEY),pk(_SERVER),and_v(v:pk(MY_RECOVERY_KEY),older(12960)))

We have three keys in this miniscript. The users key, the servers key and a recovery key. It solves the problem of us losing access of we were to misplace our key.

Like the previous miniscript, there are two ways we could satisfy it.

  1. With both the user’s and servers signatures
  2. and with the recovery key, if the utxo has been mined for at-least 3 months

Miniscript #4

and_v(or_c(pk(resigner),v:older(12960)),multi(2,participant_1,participant_2,participant_3))

This miniscript is fairly interesting, It allow us to use the signing service with the familiar ‘n of m multisig’. In this case we need the service’s signature in combination with the signatures of two of three of the participants, but after three months it reverts to a ‘2 of 3 multisig’.

A interesting point to note is that after three months from when the transaction was mined all the utxos will have to be respent to an address created from a similar script, in other to keep using the signing service.

In other to allow users to still access their funds in any event that the server is unavailable, we enforce a relative time lock on the descriptors as specified in the miniscript above. A minimum time lock of about 3 months seems reasonable enough.

There's only support for WSH descriptors for now. We should have taproot support later in the year. In other to allow users to generate receive and change address irrespective of other participants in the miniscript e.g signing server, at least there should be one the keys should be a wild card. The signing server's keys should always be a wild card.

The signing server generates a number of number of addresses if descriptor policy allows (It would), minding the ‘gap limit’. In other to keep watch for receives.

Spending conditions

The main goal of the signing service is to enforce some spending conditions before it signs.

  • Spending limits in satoshis (per day/week/month).
  • Presence of additional 2FA in the PSBT.
  • Filters to Limit outputs only to a predetermined set of addresses.
  • Presence of additional 2FA in the PSBT.
  • Fraud detection/prevention (Requires some brainstorming on your part).
  • Level of compliance with some (arbitrary) regulatory environment. (not cyberphunk!)
  • Say you are using a descriptor with a miniscript policy such as that in Miniscript #4, and a participant in the ‘2 of 3 multisig’ goes awol. Considering blacklisting his/her keys so that any PSBT containing the partial signature would be rejected

The possibilties are huge and we shall be exploring this in the coming weeks as the project progresses.

--

--