|
| 1 | +--- |
| 2 | +title: "bdk-cli basics multi-sig 2 of 3 tutorial" |
| 3 | +description: "Tutorial using command-line to create a 2 of 3 multi-sig Wallet and Spend" |
| 4 | +authors: |
| 5 | + - waterstone |
| 6 | +date: "2022-10-17" |
| 7 | +tags: ["tutorial", "bdk-cli","multi-sig"] |
| 8 | +hidden: false |
| 9 | +draft: false |
| 10 | +--- |
| 11 | + |
| 12 | +## 2-of-3 Multi-Signature Descriptor Wallet using bdk-cli |
| 13 | + |
| 14 | +## Overview of the tutorial |
| 15 | +- The purpose of this tutorial is to continue learning `bdk-cli` as our tool to manage a 2 of 3 multi-signature wallet. |
| 16 | +- Generate a receive address with a spending Policy of 2 out of 3 escrow aka multi-signature. |
| 17 | +- Intro to more complex but standard policies to create custom encumberances aka custom spending conditions for transactions. |
| 18 | + |
| 19 | +Note that to complete this tutorial, you'll need to enable the `compiler` and `electrum` flags when installing or building bdk-cli, for example by installing using: |
| 20 | +```shell |
| 21 | +cargo install bdk-cli --features=compiler,electrum |
| 22 | +``` |
| 23 | + |
| 24 | +## Step 1: Generate the XPRVs (Extended-Keys) and Save to environment variables |
| 25 | + |
| 26 | +> Create three private keys and each in their own environment variable |
| 27 | +
|
| 28 | +:arrow_forward: `export XPRV_00=$(bdk-cli key generate | jq -r '.xprv')` |
| 29 | + |
| 30 | +:arrow_forward: `export XPRV_01=$(bdk-cli key generate | jq -r '.xprv')` |
| 31 | + |
| 32 | +:arrow_forward: `export XPRV_02=$(bdk-cli key generate | jq -r '.xprv')` |
| 33 | + |
| 34 | + |
| 35 | + |
| 36 | + |
| 37 | +### 1a: Verify XPRV environment variables are Active |
| 38 | + |
| 39 | +:arrow_forward: `env | grep XPRV` |
| 40 | + |
| 41 | + |
| 42 | + |
| 43 | + |
| 44 | +## Step 2: Generate XPUBs (Extended Public Keys) & Save to environment variables |
| 45 | + |
| 46 | +> Generate the three individual Public Keys aka XPUBs using our Private key and descriptor path. |
| 47 | +
|
| 48 | +:arrow_forward: `export XPUB_00=$(bdk-cli key derive --xprv $XPRV_00 --path "m/84'/1'/0'/0" | jq -r ".xpub")` |
| 49 | + |
| 50 | +:arrow_forward: `export XPUB_01=$(bdk-cli key derive --xprv $XPRV_01 --path "m/84'/1'/0'/0" | jq -r ".xpub")` |
| 51 | + |
| 52 | +:arrow_forward: `export XPUB_02=$(bdk-cli key derive --xprv $XPRV_02 --path "m/84'/1'/0'/0" | jq -r ".xpub")` |
| 53 | + |
| 54 | + |
| 55 | + |
| 56 | + |
| 57 | +### 2a: Verify XPUB environment variables |
| 58 | + |
| 59 | +:arrow_forward: `env | grep XPUB` |
| 60 | + |
| 61 | + |
| 62 | + |
| 63 | +*** |
| 64 | +## Step 3: Create Single-Wallet Descriptors |
| 65 | + |
| 66 | +> Create the wallet Descriptor for each wallet |
| 67 | +
|
| 68 | +:arrow_forward: `export DESCRIPTOR_00="$XPRV_00/84h/1h/0h/0/*"` |
| 69 | + |
| 70 | +:arrow_forward: `export DESCRIPTOR_01="$XPRV_01/84h/1h/0h/0/*"` |
| 71 | + |
| 72 | +:arrow_forward: `export DESCRIPTOR_02="$XPRV_02/84h/1h/0h/0/*"` |
| 73 | + |
| 74 | + |
| 75 | + |
| 76 | + |
| 77 | + |
| 78 | +## Step 4: Create Multi-Sig-Descriptor Wallets |
| 79 | +> This is how you create the 2-of-3 multi-sig output descriptor. You will need (one PrivateKey and two Xpubs) It consists of using the `compiler` function to parse `policy` to `mini-script` . |
| 80 | +
|
| 81 | +- When creating the descriptor the order matters so be aware of that when following tutorial if you are for any reason changing the order of the policy. |
| 82 | +#### Multi-Sig-Wallet 0 |
| 83 | +- [ ] :arrow_forward: `export MULTI_DESCRIPTOR_00=$(bdk-cli compile "thresh(2,pk($DESCRIPTOR_00),pk($XPUB_01),pk($XPUB_02))" | jq -r '.descriptor')` |
| 84 | + |
| 85 | +#### Multi-Sig-Wallet 1 |
| 86 | +- [ ] :arrow_forward: `export MULTI_DESCRIPTOR_01=$(bdk-cli compile "thresh(2,pk($XPUB_00),pk($DESCRIPTOR_01),pk($XPUB_02))" | jq -r '.descriptor')` |
| 87 | + |
| 88 | +#### Multi-Sig-Wallet 2 |
| 89 | +- [ ] :arrow_forward: `export MULTI_DESCRIPTOR_02=$(bdk-cli compile "thresh(2,pk($XPUB_00),pk($XPUB_01),pk($DESCRIPTOR_02))" | jq -r '.descriptor')` |
| 90 | + |
| 91 | + |
| 92 | + |
| 93 | +#### multi-sig 2 of 3 policy gets compiled to miniscript |
| 94 | +```shell |
| 95 | +# policy |
| 96 | +thresh(2,pk(XPRV_A),pk(XPUB_B),pk(XPUB_C)) |
| 97 | + |
| 98 | +# miniscript |
| 99 | +wsh(multi(2,XPRV_KEY,PUBKEY_B,XPUB_C)) |
| 100 | +``` |
| 101 | + |
| 102 | + |
| 103 | +*** |
| 104 | + |
| 105 | + |
| 106 | +### 4a: Verify Multi-Sig-Descriptor environment variables are active |
| 107 | + |
| 108 | +:arrow_forward: `env | grep MULTI` |
| 109 | + |
| 110 | + |
| 111 | + |
| 112 | + |
| 113 | +*** |
| 114 | + |
| 115 | +## Step 5: Generate Receive Address by using Multi-Sig-Descriptor Wallets |
| 116 | + |
| 117 | +:arrow_forward: `bdk-cli wallet --wallet wallet_name_msd00 --descriptor $MULTI_DESCRIPTOR_00 get_new_address` |
| 118 | + |
| 119 | +:arrow_forward: `bdk-cli wallet --wallet wallet_name_msd01 --descriptor $MULTI_DESCRIPTOR_01 get_new_address` |
| 120 | + |
| 121 | +:arrow_forward: `bdk-cli wallet --wallet wallet_name_msd02 --descriptor $MULTI_DESCRIPTOR_02 get_new_address` |
| 122 | + |
| 123 | + |
| 124 | + |
| 125 | + |
| 126 | +:red_circle: Did you generate the same address for all three? Good! Else, something might be incorrect. |
| 127 | + |
| 128 | +## Step 6: Send Testnet Bitcoin to the newly created receive-address |
| 129 | + |
| 130 | +[Bitcoin Testnet Faucet link:1](https://testnet-faucet.mempool.co) |
| 131 | +[Bitcoin Testnet Faucet link:2](https://bitcoinfaucet.uo1.net) |
| 132 | + |
| 133 | +## Step 7: Sync one of the Multi-Sig Wallets |
| 134 | + |
| 135 | +:arrow_forward: ` bdk-cli wallet --wallet wallet_name_msd00 --descriptor $MULTI_DESCRIPTOR_00 sync` |
| 136 | + |
| 137 | + |
| 138 | + |
| 139 | + |
| 140 | +## Step 8: Check Balance Multi-Sig Wallets |
| 141 | + |
| 142 | + |
| 143 | +:arrow_forward: ` bdk-cli wallet --wallet wallet_name_msd00 --descriptor $MULTI_DESCRIPTOR_00 get_balance` |
| 144 | + |
| 145 | + |
| 146 | + |
| 147 | + |
| 148 | +- Every wallet has access to sync and view balance. |
| 149 | + |
| 150 | +## Step 9: Check Multi-Sig Policies on Descriptor Wallet |
| 151 | +:arrow_forward:` bdk-cli wallet --wallet wallet_name_msd00 --descriptor $MULTI_DESCRIPTOR_00 policies` |
| 152 | + |
| 153 | +The output below confirms the command was successful. |
| 154 | +```shell |
| 155 | +{ |
| 156 | + "external": { |
| 157 | + "contribution": { |
| 158 | + "conditions": { |
| 159 | + "0": [ |
| 160 | + {} |
| 161 | + ] |
| 162 | + }, |
| 163 | + "items": [ |
| 164 | + 0 |
| 165 | + ], |
| 166 | + "m": 2, |
| 167 | + "n": 3, |
| 168 | + "sorted": false, |
| 169 | + "type": "PARTIAL" |
| 170 | + }, |
| 171 | + "id": "seaxtqqn", |
| 172 | + "keys": [ |
| 173 | + { |
| 174 | + "fingerprint": "7cdf2d46" |
| 175 | + }, |
| 176 | + { |
| 177 | + "fingerprint": "fc7870cd" |
| 178 | + }, |
| 179 | + { |
| 180 | + "fingerprint": "26b03333" |
| 181 | + } |
| 182 | + ], |
| 183 | + "satisfaction": { |
| 184 | + "items": [], |
| 185 | + "m": 2, |
| 186 | + "n": 3, |
| 187 | + "sorted": false, |
| 188 | + "type": "PARTIAL" |
| 189 | + }, |
| 190 | + "threshold": 2, |
| 191 | + "type": "MULTISIG" |
| 192 | + }, |
| 193 | + "internal": null |
| 194 | +} |
| 195 | + |
| 196 | + |
| 197 | +``` |
| 198 | + |
| 199 | +### SpendingPolicyRequired for complex descriptors |
| 200 | + |
| 201 | +```shell |
| 202 | +--external_policy "{\"seaxtqqn\": [0,1]}" |
| 203 | + <-rootnode-><children #0 and #1 of root node> |
| 204 | +``` |
| 205 | + |
| 206 | +> Save the "id": We will need to use this ''id'' later. |
| 207 | +
|
| 208 | +More info on [external policies here](https://bitcoindevkit.org/bdk-cli/interface/) |
| 209 | + |
| 210 | +## Step 10: Create a Transaction (PSBT) |
| 211 | +- 1st Create a PSBT using the first wallet |
| 212 | +- 2nd Sign the PSBT with the first wallet |
| 213 | +- 3rd Sign PSBT with the second wallet |
| 214 | +- Broadcast PSBT |
| 215 | + |
| 216 | +### Export UNSIGNED_PSBT to environment variable |
| 217 | +:arrow_forward: `export UNSIGNED_PSBT=$(bdk-cli wallet --wallet wallet_name_msd00 --descriptor $MULTI_DESCRIPTOR_00 create_tx --send_all --to mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt:0 --external_policy "{\"CHANGE_ID_HERE\": [0,1]}" | jq -r '.psbt')` |
| 218 | + |
| 219 | +### Verify UNSIGNED_PSBT environment variable |
| 220 | +:arrow_forward: `env | grep UNSIGNED` |
| 221 | + |
| 222 | + |
| 223 | +## Step 11: SIGN the Transaction |
| 224 | + |
| 225 | +### 1st Wallet Signs the transaction |
| 226 | + |
| 227 | +:arrow_forward: `bdk-cli wallet --wallet wallet_name_msd00 --descriptor $MULTI_DESCRIPTOR_00 sign --psbt $UNSIGNED_PSBT` |
| 228 | + |
| 229 | +:arrow_forward: `export ONESIG_PSBT=$(bdk-cli wallet --wallet wallet_name_msd00 --descriptor $MULTI_DESCRIPTOR_00 sign --psbt $UNSIGNED_PSBT | jq -r '.psbt')` |
| 230 | + |
| 231 | +:arrow_forward:`env | grep ONESIG` |
| 232 | + |
| 233 | +``` |
| 234 | +{ |
| 235 | + "is_finalized": false, |
| 236 | + "psbt": "cHNidP8BAFUBAAAAAdYCtva/7Rkt+fgFu3mxAdaPh4uTbgBL3HmYZgcEKWygAAAAAAD/////AQqGAQAAAAAAGXapFDRKD0jKFQ7CuQOBdmC5tosTpnAmiKwAAAAAAAEA6gIAAAAAAQFLyGFJFK884DGBM1WgskRZ6gKp/7oZ+Z30u0+wF3pZYAEAAAAA/v///wKghgEAAAAAACIAINHcOQLE6GpJ3J+FOzn/be+HApxW8sZtGqfA3TBW+NYX91hoOAAAAAAWABTPQDZx2wYYIn+ug2pZBmWBn0Tu/gJHMEQCIHu6GmRMDgPZyTx+klFMA9VujR3qDA/Y08kSkRvOaChjAiBAtExtGAYLuQ/DDJzCqLlNZ1bMB3MV+nxsLfTdI9YcYwEhA0b8lz+kt0xHfR/tjUKOc2Nt2L61pDd5vJ/lsKi8pw9MmFUjAAEBK6CGAQAAAAAAIgAg0dw5AsToakncn4U7Of9t74cCnFbyxm0ap8DdMFb41hciAgIjUCIdnyr6rDtuNhVNt4ZBDcvYLawfoJbzbPyxc/WNDUgwRQIhAJdILr7G3UzYylyr2fA13MFsz/jG4+iZlKeEkX79d082AiA99UF0/uFyXBVNUmuGaxdHL7wlhzqfbgGLMREN0z/O6QEBBWlSIQIjUCIdnyr6rDtuNhVNt4ZBDcvYLawfoJbzbPyxc/WNDSEDzsDXexRPSxeXiLJoS0i2fQlOoOGHmo+Dhaeaq3oHV6YhAjGKA2Dqg+QeMICBAifYslQF2WrehLEQ0iEOpp/+eQ0NU64iBgIjUCIdnyr6rDtuNhVNt4ZBDcvYLawfoJbzbPyxc/WNDRh83y1GVAAAgAEAAIAAAACAAAAAAAAAAAAiBgIxigNg6oPkHjCAgQIn2LJUBdlq3oSxENIhDqaf/nkNDRgmsDMzVAAAgAEAAIAAAACAAAAAAAAAAAAiBgPOwNd7FE9LF5eIsmhLSLZ9CU6g4Yeaj4OFp5qregdXphj8eHDNVAAAgAEAAIAAAACAAAAAAAAAAAAAAA==" |
| 237 | +} |
| 238 | +``` |
| 239 | + |
| 240 | + |
| 241 | + |
| 242 | +### 2nd Wallet Signs the transaction |
| 243 | +:arrow_forward: `bdk-cli wallet --wallet wallet_name_msd01 --descriptor $MULTI_DESCRIPTOR_01 sign --psbt $ONESIG_PSBT` |
| 244 | + |
| 245 | +:arrow_forward: `export SECONDSIG_PSBT=$(bdk-cli wallet --wallet wallet_name_msd01 --descriptor $MULTI_DESCRIPTOR_01 sign --psbt $ONESIG_PSBT | jq -r '.psbt')` |
| 246 | + |
| 247 | + |
| 248 | +:arrow_forward:`env | grep SECONDSIG` |
| 249 | + |
| 250 | +``` |
| 251 | +{ |
| 252 | + "is_finalized": true, |
| 253 | + "psbt": "cHNidP8BAFUBAAAAAdYCtva/7Rkt+fgFu3mxAdaPh4uTbgBL3HmYZgcEKWygAAAAAAD/////AQqGAQAAAAAAGXapFDRKD0jKFQ7CuQOBdmC5tosTpnAmiKwAAAAAAAEA6gIAAAAAAQFLyGFJFK884DGBM1WgskRZ6gKp/7oZ+Z30u0+wF3pZYAEAAAAA/v///wKghgEAAAAAACIAINHcOQLE6GpJ3J+FOzn/be+HApxW8sZtGqfA3TBW+NYX91hoOAAAAAAWABTPQDZx2wYYIn+ug2pZBmWBn0Tu/gJHMEQCIHu6GmRMDgPZyTx+klFMA9VujR3qDA/Y08kSkRvOaChjAiBAtExtGAYLuQ/DDJzCqLlNZ1bMB3MV+nxsLfTdI9YcYwEhA0b8lz+kt0xHfR/tjUKOc2Nt2L61pDd5vJ/lsKi8pw9MmFUjAAEBK6CGAQAAAAAAIgAg0dw5AsToakncn4U7Of9t74cCnFbyxm0ap8DdMFb41hciAgIjUCIdnyr6rDtuNhVNt4ZBDcvYLawfoJbzbPyxc/WNDUgwRQIhAJdILr7G3UzYylyr2fA13MFsz/jG4+iZlKeEkX79d082AiA99UF0/uFyXBVNUmuGaxdHL7wlhzqfbgGLMREN0z/O6QEiAgPOwNd7FE9LF5eIsmhLSLZ9CU6g4Yeaj4OFp5qregdXpkgwRQIhAO2aRERcublhAzToshkZRMg2I8GaE7mM2ECr0vYyuscmAiB5KK4ETlvrLqL0QbcRbGqrSwIa9lVuOqP3f5qCnGRMaQEBBWlSIQIjUCIdnyr6rDtuNhVNt4ZBDcvYLawfoJbzbPyxc/WNDSEDzsDXexRPSxeXiLJoS0i2fQlOoOGHmo+Dhaeaq3oHV6YhAjGKA2Dqg+QeMICBAifYslQF2WrehLEQ0iEOpp/+eQ0NU64iBgIjUCIdnyr6rDtuNhVNt4ZBDcvYLawfoJbzbPyxc/WNDRh83y1GVAAAgAEAAIAAAACAAAAAAAAAAAAiBgIxigNg6oPkHjCAgQIn2LJUBdlq3oSxENIhDqaf/nkNDRgmsDMzVAAAgAEAAIAAAACAAAAAAAAAAAAiBgPOwNd7FE9LF5eIsmhLSLZ9CU6g4Yeaj4OFp5qregdXphj8eHDNVAAAgAEAAIAAAACAAAAAAAAAAAABBwABCP3+AAQASDBFAiEAl0guvsbdTNjKXKvZ8DXcwWzP+Mbj6JmUp4SRfv13TzYCID31QXT+4XJcFU1Sa4ZrF0cvvCWHOp9uAYsxEQ3TP87pAUgwRQIhAO2aRERcublhAzToshkZRMg2I8GaE7mM2ECr0vYyuscmAiB5KK4ETlvrLqL0QbcRbGqrSwIa9lVuOqP3f5qCnGRMaQFpUiECI1AiHZ8q+qw7bjYVTbeGQQ3L2C2sH6CW82z8sXP1jQ0hA87A13sUT0sXl4iyaEtItn0JTqDhh5qPg4Wnmqt6B1emIQIxigNg6oPkHjCAgQIn2LJUBdlq3oSxENIhDqaf/nkNDVOuAAA=" |
| 254 | +} |
| 255 | +``` |
| 256 | + |
| 257 | + |
| 258 | + |
| 259 | +## Step 12: Broadcast Transaction |
| 260 | + |
| 261 | +:arrow_forward: `bdk-cli wallet --wallet wallet_name_msd01 --descriptor $MULTI_DESCRIPTOR_01 broadcast --psbt $SECONDSIG_PSBT` |
| 262 | + |
| 263 | +``` |
| 264 | +{ |
| 265 | + "txid": "61da2451874a483aa8d1d0787c7680d157639f284840de8885098cac43f6cc2f" |
| 266 | +} |
| 267 | +``` |
| 268 | + |
| 269 | + |
| 270 | + |
| 271 | +### Verify Transaction |
| 272 | +Verify transcation in the memory pool on testnet [Mempool-testnet!](https://mempool.space/testnet) |
0 commit comments