waterStone


Computer Science @parallelogos
Bitcoin Contributor @BitcoinDevKit
Open Source /#linux
Share: 

BDK-CLI BASICS: 2-of-3 Multi-Signature Testnet Transaction (Tutorial)

Bitcoin Dev Kit 2-of-3 Multi-Signature Descriptor Wallet using BDK using Command-Line

Overview of the tutorial


Installation Pre-requisites

1. Install Rust and Cargo :```

$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

2. Installl sqlite3

#sqlite is a database

# Linux machine
$ apt install libsqlite3-dev

# macOS machine
$ brew sqlite

3. Install jq

# jq is a tool used for data parsing

# linux machine
$ apt install jq

# macOS machine
$ brew install jq

Note: If you need remove the cache wallet data at any time use this is the command.


bdk-cli installation:

$  cargo install --features "electrum,compiler"  --version 0.6.0 bdk-cli

Step 1: Generate the XPRVs (Extended-Keys) and Save to environment variables

Create three private keys and each in their own environment variable

$ export XPRV_00=$(bdk-cli key generate | jq -r '.xprv')

$ export XPRV_01=$(bdk-cli key generate | jq -r '.xprv')

$ export XPRV_02=$(bdk-cli key generate | jq -r '.xprv')

1a: Verify XPRV environment variables are Active

$ env | grep XPRV


Step 2: Generate XPUBs (Extended Public Keys) & Save to environment variables

Generate the three individual Public Keys aka XPUBs using our Private key and descriptor path.

$ export XPUB_00=$(bdk-cli key derive --xprv $XPRV_00 --path "m/84'/1'/0'/0" | jq -r ".xpub")

$ export XPUB_01=$(bdk-cli key derive --xprv $XPRV_01 --path "m/84'/1'/0'/0" | jq -r ".xpub")

$ export XPUB_02=$(bdk-cli key derive --xprv $XPRV_02 --path "m/84'/1'/0'/0" | jq -r ".xpub")

2a: Verify XPUB environment variables

$ env | grep XPUB


Step 3: Create Single-Wallet Descriptors

Create the wallet Descriptor for each wallet

$ export DESCRIPTOR_00="$XPRV_00/84h/1h/0h/0/*"

$ export DESCRIPTOR_01="$XPRV_01/84h/1h/0h/0/*"

$ export DESCRIPTOR_02="$XPRV_02/84h/1h/0h/0/*"


Step 4: Create Multi-Sig-Descriptor Wallets

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 .

Multi-Sig-Wallet 1

Multi-Sig-Wallet 2

What is Miniscript? + Resources

More details Miniscript, Policy:

MiniScript

Minsc is a high-level scripting language

Notes :

multi-sig 2 of 3 policy gets compiled to miniscript

# policy

thresh(2,pk(XPRV_A),pk(XPUB_B),pk(XPUB_C)) 

# miniscript

wsh(multi(2,XPRV_KEY,PUBKEY_B,XPUB_C))

4a: Verify Multi-Sig-Descriptor environment variables are active

$ env | grep MULTI


Step 5: Generate Receive Address by using Multi-Sig-Descriptor Wallets

$ bdk-cli wallet --wallet wallet_name_msd00 --descriptor $MULTI_DESCRIPTOR_00 get_new_address

$ bdk-cli wallet --wallet wallet_name_msd01 --descriptor $MULTI_DESCRIPTOR_01 get_new_address

$ bdk-cli wallet --wallet wallet_name_msd02 --descriptor $MULTI_DESCRIPTOR_02 get_new_address

Did you generate the same address for all three? Good! Else, something might be incorrect.


Step 6: Send Testnet Bitcoin to the newly created receive-address

Bitcoin Testnet Faucet link:1 Bitcoin Testnet Faucet link:2


Step 7: Sync one of the Multi-Sig Wallets

$ ` bdk-cli wallet –wallet wallet_name_msd00 –descriptor $MULTI_DESCRIPTOR_00 sync`


Step 8: Check Balance Multi-Sig Wallets

$ ` bdk-cli wallet –wallet wallet_name_msd00 –descriptor $MULTI_DESCRIPTOR_00 get_balance`


Step 9: Check Multi-Sig Policies on Descriptor Wallet

$ bdk-cli wallet --wallet wallet_name_msd00 --descriptor $MULTI_DESCRIPTOR_00 policies

:::info :+1: The output below confirms the command was successful. ::::

{
  "external": {
    "contribution": {
      "conditions": {
        "0": [
          {}
        ]
      },
      "items": [
        0
      ],
      "m": 2,
      "n": 3,
      "sorted": false,
      "type": "PARTIAL"
    },
    "id": "seaxtqqn",
    "keys": [
      {
        "fingerprint": "7cdf2d46"
      },
      {
        "fingerprint": "fc7870cd"
      },
      {
        "fingerprint": "26b03333"
      }
    ],
    "satisfaction": {
      "items": [],
      "m": 2,
      "n": 3,
      "sorted": false,
      "type": "PARTIAL"
    },
    "threshold": 2,
    "type": "MULTISIG"
  },
  "internal": null
}



### SpendingPolicyRequired for complex descriptors 

--external_policy "{\"seaxtqqn\": [0,1]}"
     <-rootnode-><children #0 and #1 of root node>

Save the “id”: We will need to use this ‘‘id’’ later.


Step 10: Create a Transaction (PSBT)

Create TXN and Export UNSIGNED_PSBT to environment variable

$ 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')

Verify UNSIGNED_PSBT environment variable

$ env | grep UNSIGNED


Step 11: SIGN the Transaction

1st Wallet Signs the transaction

$ bdk-cli wallet --wallet wallet_name_msd00 --descriptor $MULTI_DESCRIPTOR_00 sign --psbt $UNSIGNED_PSBT

$ export ONESIG_PSBT=$(bdk-cli wallet --wallet wallet_name_msd00 --descriptor $MULTI_DESCRIPTOR_00 sign --psbt $UNSIGNED_PSBT | jq -r '.psbt')

$env | grep ONESIG

{
  "is_finalized": false,
  "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=="
}



2nd Wallet Signs the transaction

$ bdk-cli wallet --wallet wallet_name_msd01 --descriptor $MULTI_DESCRIPTOR_01 sign --psbt $ONESIG_PSBT

$ export SECONDSIG_PSBT=$(bdk-cli wallet --wallet wallet_name_msd01 --descriptor $MULTI_DESCRIPTOR_01 sign --psbt $ONESIG_PSBT | jq -r '.psbt')

$env | grep SECONDSIG

{
  "is_finalized": true,
  "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="
}



Step 12: Broadcast Transaction

$ bdk-cli wallet --wallet wallet_name_msd01 --descriptor $MULTI_DESCRIPTOR_01 broadcast --psbt $SECONDSIG_PSBT

{
  "txid": "61da2451874a483aa8d1d0787c7680d157639f284840de8885098cac43f6cc2f"
}


Verify Transaction

, , ,