From 17dd6573008c8aca9fc0da9419225c85a4f94330 Mon Sep 17 00:00:00 2001 From: Michael Dietz Date: Wed, 26 May 2021 10:37:24 -0400 Subject: doc: M-of-N multisig using descriptor wallets and PSBTs, as well as a signing flow --- doc/descriptors.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'doc/descriptors.md') diff --git a/doc/descriptors.md b/doc/descriptors.md index e27ff87546..ab04f64b6b 100644 --- a/doc/descriptors.md +++ b/doc/descriptors.md @@ -139,6 +139,32 @@ Key order does not matter for `sortedmulti()`. `sortedmulti()` behaves in the sa as `multi()` does but the keys are reordered in the resulting script such that they are lexicographically ordered as described in BIP67. +#### Basic multisig example + +For a good example of a basic M-of-N multisig between multiple participants using descriptor +wallets and PSBTs, as well as a signing flow, see [this functional test](/test/functional/wallet_multisig_descriptor_psbt.py). +The basic steps are: + + 1. Every participant generates an xpub. The most straightforward way is to create a new descriptor wallet. + Avoid reusing this wallet for any other purpose. Hint: extract the wallet's xpubs using `listdescriptors` + and pick the one from the `pkh` descriptor since it's least likely to be accidentally reused (legacy addresses) + 2. Create a watch-only descriptor wallet (blank, private keys disabled). Now the multisig is created by importing the two descriptors: + `wsh(sortedmulti(,XPUB1/0/*,XPUB2/0/*,…,XPUBN/0/*))` and `wsh(sortedmulti(,XPUB1/1/*,XPUB2/1/*,…,XPUBN/1/*))` + (one descriptor w/ `0` for receiving addresses and another w/ `1` for change). Every participant does this + 3. A receiving address is generated for the multisig. As a check to ensure step 2 was done correctly, every participant + should verify they get the same addresses + 4. Funds are sent to the resulting address + 5. A sending transaction is created using `walletcreatefundedpsbt` (anyone can initiate this). It is simple to do this in + the GUI by going to the `Send` tab in the multisig wallet and creating an unsigned transaction (PSBT) + 6. At least `M` users check the PSBT with `decodepsbt` and (if OK) signs it with `walletprocesspsbt`. It is simple to do + this in the GUI by Loading the PSBT from file and signing it + 7. The signed PSBTs are collected with `combinepsbt`, finalized w/ `finalizepsbt`, and + then the resulting transaction is broadcasted to the network + 8. Checks that balances are correct after the transaction has been included in a block + +[The test](/test/functional/wallet_multisig_descriptor_psbt.py) is meant to be documentation as much as it is a functional test, so +it is kept as simple and readable as possible. + ### BIP32 derived keys and chains Most modern wallet software and hardware uses keys that are derived using -- cgit v1.2.3 From e05cd0546a155afcd45c43ce730c4abecd40dfed Mon Sep 17 00:00:00 2001 From: Michael Dietz Date: Wed, 26 May 2021 10:38:12 -0400 Subject: doc: add another signing flow for multisig with descriptor wallets and PSBTs --- doc/descriptors.md | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'doc/descriptors.md') diff --git a/doc/descriptors.md b/doc/descriptors.md index ab04f64b6b..75b9582343 100644 --- a/doc/descriptors.md +++ b/doc/descriptors.md @@ -162,6 +162,11 @@ The basic steps are: then the resulting transaction is broadcasted to the network 8. Checks that balances are correct after the transaction has been included in a block +You may prefer a daisy chained signing flow where each participant signs the PSBT one after another until +the PSBT has been signed `M` times and is "complete." For the most part, the steps above remain the same, except (6, 7) +change slightly from signing the original PSBT in parallel to signing it in series. `combinepsbt` is not necessary with +this signing flow and the last (`m`th) signer can just broadcast the PSBT after signing. Note that a parallel signing flow may be +preferable in cases where there are more signers. This signing flow is also included in the test / Python example. [The test](/test/functional/wallet_multisig_descriptor_psbt.py) is meant to be documentation as much as it is a functional test, so it is kept as simple and readable as possible. -- cgit v1.2.3 From f9479e4626f6b5126ff8cdab3a7e718c609429ef Mon Sep 17 00:00:00 2001 From: Michael Dietz Date: Fri, 3 Sep 2021 13:36:07 -0500 Subject: test, doc: basic M-of-N multisig minor cleanup and clarifications wallet_multisig_descriptor_psbt.py is refactored in this commit. While behavior doesn't change we do cleanup the way wallets are accessed throughout the test as this is done a lot for the various signers and their multisigs. We also get rid of some shallow methods and instead inline them for improved readability. descriptors.md is improved to be more explicit about which wallet (ie the signer or multisig) is required for each step. --- doc/descriptors.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'doc/descriptors.md') diff --git a/doc/descriptors.md b/doc/descriptors.md index 75b9582343..f38caa81cb 100644 --- a/doc/descriptors.md +++ b/doc/descriptors.md @@ -145,22 +145,24 @@ For a good example of a basic M-of-N multisig between multiple participants usin wallets and PSBTs, as well as a signing flow, see [this functional test](/test/functional/wallet_multisig_descriptor_psbt.py). The basic steps are: - 1. Every participant generates an xpub. The most straightforward way is to create a new descriptor wallet. - Avoid reusing this wallet for any other purpose. Hint: extract the wallet's xpubs using `listdescriptors` - and pick the one from the `pkh` descriptor since it's least likely to be accidentally reused (legacy addresses) + 1. Every participant generates an xpub. The most straightforward way is to create a new descriptor wallet which we will refer to as + the participant's signer wallet. Avoid reusing this wallet for any purpose other than signing transactions from the + corresponding multisig we are about to create. Hint: extract the wallet's xpubs using `listdescriptors` and pick the one from the + `pkh` descriptor since it's least likely to be accidentally reused (legacy addresses) 2. Create a watch-only descriptor wallet (blank, private keys disabled). Now the multisig is created by importing the two descriptors: `wsh(sortedmulti(,XPUB1/0/*,XPUB2/0/*,…,XPUBN/0/*))` and `wsh(sortedmulti(,XPUB1/1/*,XPUB2/1/*,…,XPUBN/1/*))` (one descriptor w/ `0` for receiving addresses and another w/ `1` for change). Every participant does this 3. A receiving address is generated for the multisig. As a check to ensure step 2 was done correctly, every participant should verify they get the same addresses 4. Funds are sent to the resulting address - 5. A sending transaction is created using `walletcreatefundedpsbt` (anyone can initiate this). It is simple to do this in - the GUI by going to the `Send` tab in the multisig wallet and creating an unsigned transaction (PSBT) - 6. At least `M` users check the PSBT with `decodepsbt` and (if OK) signs it with `walletprocesspsbt`. It is simple to do - this in the GUI by Loading the PSBT from file and signing it - 7. The signed PSBTs are collected with `combinepsbt`, finalized w/ `finalizepsbt`, and - then the resulting transaction is broadcasted to the network - 8. Checks that balances are correct after the transaction has been included in a block + 5. A sending transaction from the multisig is created using `walletcreatefundedpsbt` (anyone can initiate this). It is simple to do + this in the GUI by going to the `Send` tab in the multisig wallet and creating an unsigned transaction (PSBT) + 6. At least `M` participants check the PSBT with their multisig using `decodepsbt` to verify the transaction is OK before signing it. + 7. (If OK) the participant signs the PSBT with their signer wallet using `walletprocesspsbt`. It is simple to do this in the GUI by + loading the PSBT from file and signing it + 8. The signed PSBTs are collected with `combinepsbt`, finalized w/ `finalizepsbt`, and then the resulting transaction is broadcasted + to the network. Note that any wallet (eg one of the signers or multisig) is capable of doing this. + 9. Checks that balances are correct after the transaction has been included in a block You may prefer a daisy chained signing flow where each participant signs the PSBT one after another until the PSBT has been signed `M` times and is "complete." For the most part, the steps above remain the same, except (6, 7) -- cgit v1.2.3 From 9de0d94508828f5fdfaf688ccda5a91d38b32c58 Mon Sep 17 00:00:00 2001 From: Michael Dietz Date: Fri, 3 Sep 2021 13:42:05 -0500 Subject: doc: add disclaimer highlighting shortcomings of the basic multisig example --- doc/descriptors.md | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'doc/descriptors.md') diff --git a/doc/descriptors.md b/doc/descriptors.md index f38caa81cb..70d0926a1b 100644 --- a/doc/descriptors.md +++ b/doc/descriptors.md @@ -143,6 +143,14 @@ are lexicographically ordered as described in BIP67. For a good example of a basic M-of-N multisig between multiple participants using descriptor wallets and PSBTs, as well as a signing flow, see [this functional test](/test/functional/wallet_multisig_descriptor_psbt.py). + +Disclaimers: It is important to note that this example serves as a quick-start and is kept basic for readability. A downside of the approach +outlined here is that each participant must maintain (and backup) two separate wallets: a signer and the corresponding multisig. +It should also be noted that privacy best-practices are not "by default" here - participants should take care to only use the signer to sign +transactions related to the multisig. Lastly, it is not recommended to use anything other than a Bitcoin Core descriptor wallet to serve as your +signer(s). Other wallets, whether hardware or software, likely impose additional checks and safeguards to prevent users from signing transactions that +could lead to loss of funds, or are deemed security hazards. Conforming to various 3rd-party checks and verifications is not in the scope of this example. + The basic steps are: 1. Every participant generates an xpub. The most straightforward way is to create a new descriptor wallet which we will refer to as -- cgit v1.2.3