Multiple people, or even just one person with multiple devices, want to setup a multi-sig wallet. The end result is a descriptor wallet, perhaps miniscript enhanced.
Problem: how do you coordinate this setup? See Electrum thread about the same issue: https://github.com/spesmilo/electrum/issues/5715
Bonus points: it should also work with a protocol like MuSig that requires more than one round of communication.
Current situation
I made a tutorial and screencast of a setup flow, in this case using two signers: a ColdCard and an iOs app that I’m working on. It’s based on the flow ColdCard uses to setup a multisig between two of their devices. Specter wallet uses a similar flow and the same file format, last time I checked.
The general idea there is for each cosigner to export a (JSON) file with information about their public keys, and for the other signers to import that. Derivation paths are standardised (see also #16895). All participating devices will derive the same addresses; the user just needs to enter the (same) threshold on all devices.
The above is pretty easy to implement.
Flexible signer policies
The above approach requires everyone to contribute a single key. Individual participants can’t do fancy stuff like having their own internal multisig setup or some backup key after a timeout. Yet this could be very useful, e.g. imagine a user with two hardware wallets who wants to use an external service provider as the third key. That service provider may have some super fancy setup, but they don’t want to ship custom software to the user, let alone merge their special sauce into Bitcoin Core. What if you could use the Green Address service, but with your favourite orange QT wallet?
A more flexible setup could use miniscript, where each participant provides a policy language snippet they want to use. These are then aggregated with thresh(M, policy1, policy2, ... policyN)
. The result is then compiled by a coordinator (one of the participants) and shared with the others. Each participant could use a simple public key, or some super convoluted scheme. The cool thing about miniscript if that each participant can verify, without understanding the policy of the others, that they indeed control 1/Nth.
IIUC the plan is to combine the powers of miniscript and output descriptors, and eventually add that to the wallet. Hopefully this allows for putting xpubs and origin info in both the policy language and the miniscript itself.
There is no current plan to add the policy language to miniscript compiler to Bitcoin Core, but we can start with a naive concatenation of participant miniscripts. Also AFAIK existing hardware wallets don’t support anything other than plain OP_CHECKMULTISIG (thresh_m(2, xpub1, xpub2, xpub3))
anyway.
In other words, we would use future proof syntax like the policy language, but in practice “compile” only to trivial, non-optimized, miniscript(s) that match what we can already do with descriptors.
Wallet composer file?
What I have in mind is to (wait for someone else to) design a (binary) format, perhaps similar to PSBT, to compose a wallet interactively.
It would start with a coordinator. They populate the file with the “shape” of the wallet (YAML for illustrative purposes):
0- descriptor: null
1- policy: thresh(2, participant_1, participant_2, participant_3))
2- participant_1:
3 - policy: null
4- participant_2:
5 - policy: null
6- participant_3:
7 - policy: null
Then the first participant, probably the coordinator, fills in their details. They can also list their (advanced) capabilities, e.g. if they’re able to decompile miniscript or only understand very basic policies.
0- descriptor: null
1- policy: thresh(2, participant_1, participant_2, participant_3))
2- participant_1:
3 - policy: c:pk([00000000]/m/48h/0h/0h/2h/xpub_1/{0,1}/*)
4 - can_decompile_miniscript: false
5- participant_2:
6 - policy: null
7- participant_3:
8 - policy: null
The file is then passed to participants 2 and 3. Participant 3 will have all the info it needs to construct a final descriptor (set), and it can prompt the user to “commit” to that wallet:
0- descriptor: multi(2, [00000000]/m/48h/0h/0h/2h/xpub_1/{0,1}/*, ......)
1- policy: ...
2etc...
({0,1}
is a fake descriptor syntax; for now you need a separate receive and change descriptor)
Each participant needs to “commit” to the wallet, in the sense of storing it in their memory. This lets them display receive addresses, and it lets them check the change address before signing a transaction.
It then passes it to the other participants. If the devices all have screens, the user can now compare the first receive address as a sanity check.
Bitcoin Core should be able to import this a watch-only descriptor wallet (or maybe it has one of the keys).
If any of the devices isn’t smart / powerful enough to figure out the descriptor, then it needs to go back to the coordinator first. Conversely you could start with a round where each participant announces their capabilities (support for lock times, if they can decompile miniscript and understand their role in the top policy, taproot support, resource limits, etc). A coordinator can then propose a top level policy based on that.
Additional fields can be added e.g. to include nonces for a musig pubkey generation ritual. Also additional fields could contain friendly names for the participants.
Ideally the whole thing is small enough to copy-paste or put in a QR code (e.g. bech32-ish encoded).