forked from bitcoin/bitcoin
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ab42b2e
commit f22625f
Showing
2 changed files
with
194 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
# Offline Signing Tutorial | ||
|
||
Welcome to this tutorial on how to sign transactions offline using PSBT (Partially Signed Bitcoin Transactions). This tutorial will guide you through the process of securely signing Bitcoin transactions using an offline wallet and an online watch-only wallet. | ||
|
||
## Overview | ||
In this tutorial, we have two hosts: [offline] which is totally offline and without a copy of the blockchain and [online] which is a regular online node, both running Bitcoin 25.0 | ||
|
||
In this example, we are going to create an `offline_wallet` on the [offline] host. We will then create a `watch_only_wallet` on the [online] host using descriptors imported from the `offline_wallet` and load funds to the wallet. we'll create a PSBT transaction using the `watch_only_wallet`, sign it with the `offline_wallet` then broadcast the transaction using the [online] host | ||
|
||
### Requirements | ||
- [jq](https://jqlang.github.io/jq/) installation - This tutorial uses jq to process JSON. | ||
|
||
> [!NOTE] | ||
> Tested using Signet with a connected host machine and an offline docker container (representing the offline wallet) both running Signet, should also work with Regtest and Mainnet | ||
### Create and Prepare the `offline_wallet` | ||
|
||
1. On the offline machine, create an offline wallet named `offline_wallet`. | ||
|
||
```sh | ||
[offline]$ ./src/bitcoin-cli -signet -named createwallet wallet_name="offline_wallet" | ||
{ | ||
"name": "offline_wallet" | ||
} | ||
``` | ||
|
||
2. Export the offline wallet descriptors to a JSON file named `descriptors.json`. | ||
|
||
```sh | ||
[offline]$ ./src/bitcoin-cli -signet -rpcwallet="offline_wallet" listdescriptors | jq -r '.descriptors' >> /path/to/descriptors.json | ||
``` | ||
|
||
> [!NOTE] | ||
> The `descriptors.json` file will be exported to the online machine (e.g. using a USB) to create a watch-only wallet. (This is an easier way to export the descriptors compared to manual export) | ||
### Create the online `watch_only_wallet` | ||
|
||
1. On the online machine, create an online watch-only wallet named `watch_only_wallet` without private keys (disable_private_keys=true) and should be blank with no keys or HD seed (blank=true). | ||
- The `watch_only_wallet` wallet will be used to track transactions received to the `offline_wallet` and for creating PSBT transactions. | ||
|
||
```sh | ||
[online]$ ./src/bitcoin-cli -signet -named createwallet wallet_name="watch_only_wallet" disable_private_keys=true blank=true | ||
{ | ||
"name": "watch_only_wallet" | ||
} | ||
``` | ||
|
||
2. Import the `offline_wallet` descriptors to the online `watch_only_wallet` using the `descriptors.json` file created on the offline wallet | ||
|
||
```sh | ||
[online]$ ./src/bitcoin-cli -signet -rpcwallet="watch_only_wallet" importdescriptors "$(cat /path/to/descriptors.json)" | ||
[ | ||
{ | ||
"success": true | ||
}, | ||
{ | ||
"success": true | ||
}, | ||
{ | ||
"success": true | ||
}, | ||
{ | ||
"success": true | ||
}, | ||
{ | ||
"success": true | ||
}, | ||
{ | ||
"success": true | ||
}, | ||
{ | ||
"success": true | ||
}, | ||
{ | ||
"success": true | ||
} | ||
] | ||
``` | ||
> [!NOTE] | ||
> Importing the multiple descriptors from the `offline_wallet` provides the ability to generate a variety of address types by the [online] `watch_only_wallets` | ||
### Load Funds to the `offline_wallet` | ||
1. Generate an address for the `offline_wallet` using the `watch_only_wallet` to load funds. | ||
|
||
```sh | ||
[online]$ ./src/bitcoin-cli -signet -rpcwallet="watch_only_wallet" getnewaddress | ||
tb1qtu5qgc6ddhmqm5yqjvhg83qgk2t4ewajg0h6yh | ||
``` | ||
|
||
2. Visit a faucet like https://signet.bc-2.jp to load funds into the generated address. | ||
|
||
3. Confirm the received funds (to the offline_wallet) using the online watch_only_wallet. | ||
```sh | ||
[online]$ ./src/bitcoin-cli -signet -rpcwallet="watch_only_wallet" listunspent | ||
[ | ||
{ | ||
"txid": "0f3953dfc3eb8e753cd1633151837c5b9953992914ff32b7de08c47f1f29c762", | ||
"vout": 1, | ||
"address": "tb1qtu5qgc6ddhmqm5yqjvhg83qgk2t4ewajg0h6yh", | ||
"label": "", | ||
"scriptPubKey": "00145f2804634d6df60dd080932e83c408b2975cbbb2", | ||
"amount": 0.01000000, | ||
"confirmations": 4, | ||
"spendable": true, | ||
"solvable": true, | ||
"desc": "wpkh([306c734f/84h/1h/0h/0/0]025932ccee7590158f7e08bb36290d135d30a0b045163da896e1cd7645ec4223a9)#xytvyr4a", | ||
"parent_descs": [ | ||
"wpkh([306c734f/84h/1h/0h]tpubDCJnY92ib4Zu3qd6wrBXEjG436tQdA2tDiJU2iSJYjkNS1darssPWKaBfojhjUF5vMLBcxbN2r93pmFMz2zyTEZuNx9JDo9rWqoHhATW3Uz/0/*)#7mh08dkg" | ||
], | ||
"safe": true | ||
} | ||
] | ||
``` | ||
|
||
### Create and Export a PSBT Transaction | ||
|
||
1. Create a PSBT transaction using the online `watch_only_wallet`, sending funds to the `online_receiving_wallet` address. (`walletcreatefundedpsbt inputs[], outputs[address: amount]`) | ||
- Export the psbt transaction to `funded_psbt.txt` for easy portability to the `offline_wallet` | ||
|
||
```sh | ||
[online]$ ./src/bitcoin-cli -signet -rpcwallet="watch_only_wallet" walletcreatefundedpsbt '[]' '[{"tb1q9k5w0nhnhyeh78snpxh0t5t7c3lxdeg3erez32": 0.009}]' | jq -r '.psbt' >> /path/to/funded_psbt.txt | ||
[online]$ cat /path/to/funded_psbt.txt | ||
cHNidP8BAHECAAAAAWLHKR9/xAjetzL/FCmZU5lbfINRMWPRPHWO68PfUzkPAQAAAAD9////AoA4AQAAAAAAFgAULajnzvO5M38eEwmu9dF+xH5m5RGs0g0AAAAAABYAFMaT0f/Wp2DCZzL6dkJ3GhWj4Y9vAAAAAAABAHECAAAAAY+dRPEBrGopkw4ugSzS9npzJDEIrE/bq1XXI0KbYnYrAQAAAAD+////ArKaXgAAAAAAFgAUwEc4LdoxSjbWo/2Ue+HS+QjwfiBAQg8AAAAAABYAFF8oBGNNbfYN0ICTLoPECLKXXLuyYW8CAAEBH0BCDwAAAAAAFgAUXygEY01t9g3QgJMug8QIspdcu7IiBgJZMszudZAVj34IuzYpDRNdMKCwRRY9qJbhzXZF7EIjqRgwbHNPVAAAgAEAAIAAAACAAAAAAAAAAAAAACICA7BlBnyAR4F2UkKuSX9MFhYCsn6j//z9i7lHDm1O0CU0GDBsc09UAACAAQAAgAAAAIABAAAAAAAAAAA= | ||
``` | ||
|
||
### Decode and Analyze the PSBT Transaction | ||
|
||
1. Decode and analyze the PSBT transaction on the `offline_wallet` using the `funded_psbt.txt` file. | ||
|
||
```sh | ||
[offline]$ ./src/bitcoin-cli -signet decodepsbt $(cat /path/to/funded_psbt.txt) | ||
{ | ||
... | ||
} | ||
[offline]$ ./src/bitcoin-cli -signet analyzepsbt $(cat /path/to/funded_psbt.txt) | ||
{ | ||
"inputs": [ | ||
{ | ||
"has_utxo": true, | ||
"is_final": false, | ||
"next": "signer", | ||
"missing": { | ||
"signatures": [ | ||
"5f2804634d6df60dd080932e83c408b2975cbbb2" | ||
] | ||
} | ||
} | ||
], | ||
"estimated_vsize": 141, | ||
"estimated_feerate": 0.00100000, | ||
"fee": 0.00014100, | ||
"next": "signer" | ||
} | ||
``` | ||
|
||
### Process and Sign the PSBT Transaction | ||
|
||
1. Process, Sign and Finalize the PSBT transaction on the `offline_wallet`. | ||
- Save the final PSBT hex to the `final_psbt.txt` file, to be exported and broadcasted by the `online_wallet` | ||
|
||
```sh | ||
[offline]$ ./src/bitcoin-cli -signet -rpcwallet="offline_wallet" walletprocesspsbt $(cat /path/to/funded_psbt.txt) | jq -r .hex >> /path/to/final_psbt.txt | ||
|
||
[offline]$ cat ~/final_psbt.txt | ||
0200000000010162c7291f7fc408deb732ff14299953995b7c83513163d13c758eebc3df53390f0100000000fdffffff028c4f010000000000160014dda0f427f67bfeca9f0e7252e458ee39b82c7e06a0bb0d00000000001600142da8e7cef3b9337f1e1309aef5d17ec47e66e5110247304402200d245ee92df8be0183c98fb26bcbc474307ccdb764877273644b4c4eb359138202206c4cf787f120828a812b47043902683e24d50a60216e3b2fe0f104be2806e54f0121025932ccee7590158f7e08bb36290d135d30a0b045163da896e1cd7645ec4223a900000000 | ||
``` | ||
|
||
### Broadcast the Finalized PSBT Transaction | ||
1. Broadcast the signed and finalized PSBT transaction (on the `final_psbt.txt` file) using the online wallet | ||
|
||
```sh | ||
[online]$ ./src/bitcoin-cli -signet sendrawtransaction $(cat /path/to/final_psbt.txt) | ||
c2430a0e46df472b04b0ca887bbcd5c4abf7b2ce2eb71de981444a80e2b96d52 | ||
``` | ||
|
||
### Confirm Wallet Balances | ||
|
||
1. Confirm the updated balance of the offline wallet using the `watch_only_wallet`. | ||
|
||
```sh | ||
[online]$ ./src/bitcoin-cli -signet -rpcwallet="watch_only_wallet" getbalances | ||
{ | ||
"mine": { | ||
"trusted": 0.00085900, | ||
"untrusted_pending": 0.00000000, | ||
"immature": 0.00000000 | ||
}, | ||
"lastprocessedblock": { | ||
"hash": "0000003065c0669fff27edb4a71928cb48e5a6cfcdf06f491a83fd86822d18a6", | ||
"height": 159592 | ||
} | ||
} | ||
``` |