|
| 1 | +# 10.3: Importing Secrets to Bitcoin Core |
| 2 | + |
| 3 | +You've used your seed (from §10.1) to generate account keys (in |
| 4 | +§10.2), which is what you need to create descriptors that will allow |
| 5 | +you to use Bitcoin Core with your external seed. |
| 6 | + |
| 7 | +## Use Mainnet |
| 8 | + |
| 9 | +You may have noted that we've been using the `coin_type` of `0` in |
| 10 | +this chapter's examples and generating `xprv`. That's because |
| 11 | +`keytool` is focused on real-world/mainnet uses, not testnet. That |
| 12 | +means you're going to need to use mainnet to test out how import and |
| 13 | +export works in this chapter. |
| 14 | + |
| 15 | +To ensure this, go to ~/.bitcoin/bitcoin.conf and change the `signet=1` line to `signet=0`: |
| 16 | + |
| 17 | +``` |
| 18 | +cat bitcoin.conf |
| 19 | +
|
| 20 | +| server=1 |
| 21 | +| dbcache=1536 |
| 22 | +| par=1 |
| 23 | +| maxuploadtarget=137 |
| 24 | +| maxconnections=16 |
| 25 | +| rpcuser=StandUp |
| 26 | +| rpcpassword=4737ef625b30471038897d1f44c3423d |
| 27 | +| rpcallowip=127.0.0.1 |
| 28 | +| debug=tor |
| 29 | +| prune=550 |
| 30 | +| signet=0 |
| 31 | +| [test] |
| 32 | +| rpcbind=127.0.0.1 |
| 33 | +| rpcport=18332 |
| 34 | +| [main] |
| 35 | +| rpcbind=127.0.0.1 |
| 36 | +| rpcport=8332 |
| 37 | +| [regtest] |
| 38 | +| rpcbind=127.0.0.1 |
| 39 | +| rpcport=18443 |
| 40 | +| [signet] |
| 41 | +| rpcbind=127.0.0.1 |
| 42 | +| rpcport=38332 |
| 43 | +``` |
| 44 | + |
| 45 | +You then need to dind `bitcoind` in your process table: |
| 46 | +``` |
| 47 | +ps auxww | grep bitcoin |
| 48 | +
|
| 49 | +| standup 24832 3.1 9.8 4931716 395660 ? SLsl 17:07 0:11 /usr/local/bin/bitcoind -conf=/home/standup/.bitcoin/bitcoin.conf |
| 50 | +| standup 24945 0.0 0.0 6520 2240 pts/0 S+ 17:13 0:00 grep bitcoin |
| 51 | +``` |
| 52 | + |
| 53 | +Thenconvince it to die (`kill -9`): |
| 54 | +``` |
| 55 | +kill -9 24832 |
| 56 | +``` |
| 57 | + |
| 58 | +This will restart `bitcoind` with the new config file, which will now be using mainnet instead of signet. |
| 59 | + |
| 60 | +## Create a Descriptor |
| 61 | + |
| 62 | +You're now ready to create a descriptor, just like you did in |
| 63 | +[§4.2](04_2_Integrating_Addresses_Descriptors.md), except this time |
| 64 | +you're going to be creating a wallet descriptor for an account. |
| 65 | + |
| 66 | +If you review |
| 67 | +[§3.4](3_4_Understanding_the_Descriptor_Wallet/#examine-descriptors-with-listdescriptors), you can see that WPKH descriptors look like this: |
| 68 | + |
| 69 | +``` |
| 70 | + "desc": "wpkh([e18dae20/84h/1h/0h]tpubDC4ujMbsd9REzpGk3gnTjkrfJFw1NnvCpx6QBbLj3CHBzcLmVzssTVP8meRAM1WW4pZnK6SCCPGyzi9eMfzSXoeFMNprqtgxG71VRXTmetu/0/*)#3658f8sn", |
| 71 | + ... |
| 72 | + "desc": "wpkh([1bbc6484/84h/0h/0h]xpub6BfHmLzHGdGL3yXKFead4LtYqkDES6aFYMpieipj1WvdnXyvj5UZ4KYGsnAphLLjovhvrvs8u7S7zPHE1wFg4yhdp7REJRsyiDqVfQbNB8f/1/*)#827z83pg", |
| 73 | +``` |
| 74 | + |
| 75 | +PKH descriptors look like this: |
| 76 | +``` |
| 77 | + "desc": "pkh([1bbc6484/44h/0h/0h]xpub6CegJzUt9VSpYTzQffpKVRcRDF9RV7XW4DdoReGXra1V3TPBVqGJ2dD9DtePRh5XzeePXZHjYBetzdccBV4jhtSdNPCNjUWS5zaNzFc1f9L/0/*)#03uc5rwf", |
| 78 | + ... |
| 79 | + "desc": "pkh([1bbc6484/44h/0h/0h]xpub6CegJzUt9VSpYTzQffpKVRcRDF9RV7XW4DdoReGXra1V3TPBVqGJ2dD9DtePRh5XzeePXZHjYBetzdccBV4jhtSdNPCNjUWS5zaNzFc1f9L/1/*)#79eefk73", |
| 80 | +``` |
| 81 | + |
| 82 | +You need to create new descriptors in the same format. |
| 83 | + |
| 84 | +### Put Together the Parts |
| 85 | + |
| 86 | +Looking at that format, the first thing you need to do is remove the |
| 87 | +`m` at the head of each account descriptor path. (They were used by |
| 88 | +`keytool`, but not `bitcoin-cli`.) |
| 89 | +``` |
| 90 | +ADP_NOM_WPKH=$(echo $ADP_WPKH | sed -e s'/^m//') |
| 91 | +ADP_NOM_PKH=$(echo $ADP_PKH | sed -e s'/^m//') |
| 92 | +``` |
| 93 | + |
| 94 | +Then you can organize all of your data into the format for these |
| 95 | +descriptors, laying each out as a ranged descriptor: |
| 96 | +``` |
| 97 | +DESC_WPKH="wpkh([$FINGERPRINT$ADP_NOM_WPKH]$AKEY58_WPKH/0/*)" |
| 98 | +DESC_PKH="pkh([$FINGERPRINT$ADP_NOM_PKH]$AKEY58_PKH/0/*)" |
| 99 | +
|
| 100 | +Here's your WPKH descriptor: |
| 101 | +``` |
| 102 | +echo $DESC_WPKH |
| 103 | + |
| 104 | +| wpkh([35dad980/84h/0h/0h]xprv9z7kiySAUeGtnrbQpzqFsE8uDQufoofkLqxW2kPRcLppKx6kthUEfp4mTjyQijpcfQ5iXgYVH9EzASguJ1PNTU3YnztKGmmtBJciRU9iFre/0/*) |
| 105 | +``` |
| 106 | +
|
| 107 | +Here's your PKH descriptor: |
| 108 | +``` |
| 109 | +$ echo $DESC_PKH |
| 110 | + |
| 111 | +| pkh([35dad980/44h/0h/0h]xprv9z9awxh6i3ii4MwxeQpBedgcS5utbeZwcThsWCSeHurKrMJ2FHFnzPFscd1WhpsNT18wMctBw6KcfgzPWiGUDTPDV59czbSUPdmD6L5yogy/0/*) |
| 112 | +``` |
| 113 | +
|
| 114 | +As an aside, you could `keytool` produce an output descriptor if |
| 115 | +you've uncertain about the format, but it produces a watch-only |
| 116 | +descriptor, which means you'll have to subsituted the `xprv` for the |
| 117 | +`xpub` and it also will doesn't show the fingerprint if you tell it a |
| 118 | +specific account derivation path, so it's probably better to do it by |
| 119 | +hand, and really get a good understanding of how the descriptor works. |
| 120 | +
|
| 121 | +Here's a clean output (minus the `xpub`) of a default `84h` descriptor: |
| 122 | +
|
| 123 | +``` |
| 124 | +keytool --seed $SEED --address-index '*' output-descriptor |
| 125 | + |
| 126 | +| wpkh([35dad980/84h/0h/0h]xpub6D778Uy4K1qC1Lfsw2NGEN5dmSkADGPbi4t6q8o3AgMoCkRuSEnVDcPFK28WujQRgvY47UBXJ2zJJ5ZBKELMwA3AS9Zo3yUU8XNu5dZE5As/0/*) |
| 127 | +``` |
| 128 | +
|
| 129 | +### Add the Checksum |
| 130 | +
|
| 131 | +As usual with Bitcoin Core, you need a checksum for your descriptor. |
| 132 | +
|
| 133 | +First, take a quick look at your WPKH descriptor using `getdescriptorinfo` just to be sure everything looks right. |
| 134 | +
|
| 135 | +``` |
| 136 | +bitcoin-cli getdescriptorinfo $DESC_WPKH |
| 137 | + |
| 138 | +| { |
| 139 | +| "descriptor": "wpkh([35dad980/84h/0h/0h]xpub6D778Uy4K1qC1Lfsw2NGEN5dmSkADGPbi4t6q8o3AgMoCkRuSEnVDcPFK28WujQRgvY47UBXJ2zJJ5ZBKELMwA3AS9Zo3yUU8XNu5dZE5As/0/*)#p5auymld", |
| 140 | +| "checksum": "ac2yuqwk", |
| 141 | +| "isrange": true, |
| 142 | +| "issolvable": true, |
| 143 | +| "hasprivatekeys": true |
| 144 | +| } |
| 145 | +``` |
| 146 | +
|
| 147 | +Then you can use `jq` to capture the checksums for both descriptors: |
| 148 | +
|
| 149 | +``` |
| 150 | +CS_WPKH=$(bitcoin-cli getdescriptorinfo $DESC_WPKH | jq -r '.checksum') |
| 151 | +CS_PKH=$(bitcoin-cli getdescriptorinfo $DESC_PKH | jq -r '.checksum') |
| 152 | +``` |
| 153 | +
|
| 154 | +And you can finalize the descriptors for use with Bitcoin Core: |
| 155 | +``` |
| 156 | +DESC_WITH_CS_WPKH=$DESC_WPKH#$CS_WPKH |
| 157 | +DESC_WITH_CS_PKH=$DESC_PKH#$CS_PKH |
| 158 | +``` |
| 159 | +
|
| 160 | +## Check Addresses |
| 161 | +
|
| 162 | +When you're moving wallets (or descriptors) between apps, you always |
| 163 | +want to make sure that they look the same on your source and |
| 164 | +destination systems. The best way to do that is to check addresses. |
| 165 | +
|
| 166 | +As we've seen, we can do that with `deriveaddresses`, which will show |
| 167 | +the addresses associated with a descriptor. |
| 168 | +
|
| 169 | +Here are the first three WPKH addresses on Bitcoin Core: |
| 170 | +``` |
| 171 | +bitcoin-cli deriveaddresses $DESC_WITH_CS_WPKH 2 |
| 172 | + |
| 173 | +| [ |
| 174 | +| "bc1q7jn4qxknxr3d58jwzjyw3x93w7qxwhkypftgaz", |
| 175 | +| "bc1q3jal42asrk6jpsyaxxcl5wtevjkc4xaszcq89c", |
| 176 | +| "bc1q96auv67f4ell2ayrgauwghhkrf50e30g8mthgy" |
| 177 | +| ] |
| 178 | +``` |
| 179 | +
|
| 180 | +On `keytool`, we can go all the way back to the seed, and link that |
| 181 | +with the account derivation path to addresses of a specific type. This |
| 182 | +is a very robust check, because it verifies that our derivation of the |
| 183 | +account key was done correctly: |
| 184 | +
|
| 185 | +``` |
| 186 | +keytool --seed $SEED --account-derivation-path m/84h/0h/0h --address-index 0 address-segwit |
| 187 | +| bc1q7jn4qxknxr3d58jwzjyw3x93w7qxwhkypftgaz |
| 188 | + |
| 189 | +keytool --seed $SEED --account-derivation-path m/84h/0h/0h --address-index 1 address-segwit |
| 190 | +| bc1q3jal42asrk6jpsyaxxcl5wtevjkc4xaszcq89c |
| 191 | + |
| 192 | +keytool --seed $SEED --account-derivation-path m/84h/0h/0h --address-index 2 address-segwit |
| 193 | +bc1q96auv67f4ell2ayrgauwghhkrf50e30g8mthgy |
| 194 | +``` |
| 195 | +
|
| 196 | +All three check out! |
| 197 | +
|
| 198 | +We can do the same with our PKH addresses. |
| 199 | +
|
| 200 | +Here they are in Bitcoin Core: |
| 201 | +``` |
| 202 | +bitcoin-cli deriveaddresses $DESC_WITH_CS_PKH 2 |
| 203 | + |
| 204 | +| [ |
| 205 | +| "1GPJoLSUCZ1Bb9KFmevNHY28hsgVoDMexH", |
| 206 | +| "1KUvmWCL7uCQ3FNSLpkATTTfMqpB1zcs6c", |
| 207 | +| "1Q48NmPqPCkqxqJwSnj1ZEja8eVRuBvJxp" |
| 208 | +| ] |
| 209 | +``` |
| 210 | +
|
| 211 | +Here's the check, now looking for PKH addresses from `keytool`: |
| 212 | +
|
| 213 | +``` |
| 214 | +keytool --seed $SEED --account-derivation-path m/44h/0h/0h --address-index 0 address-pkh |
| 215 | +| 1GPJoLSUCZ1Bb9KFmevNHY28hsgVoDMexH |
| 216 | + |
| 217 | +keytool --seed $SEED --account-derivation-path m/44h/0h/0h --address-index 1 address-pkh |
| 218 | +| 1KUvmWCL7uCQ3FNSLpkATTTfMqpB1zcs6c |
| 219 | + |
| 220 | +keytool --seed $SEED --account-derivation-path m/44h/0h/0h --address-index 2 address-pkh |
| 221 | +| 1Q48NmPqPCkqxqJwSnj1ZEja8eVRuBvJxp |
| 222 | +``` |
| 223 | +
|
| 224 | +## Import Addresses |
| 225 | +
|
| 226 | +You're ready to import the addrsses. |
| 227 | +
|
| 228 | +First, you need to create a wallet without any descriptors of its own: |
| 229 | +``` |
| 230 | +$ bitcoin-cli -named createwallet wallet_name="seed" blank=true |
| 231 | +{ |
| 232 | + "name": "seed" |
| 233 | +} |
| 234 | +``` |
| 235 | +
|
| 236 | +Now, you just need to import the descriptor with the `importdescriptors` command that you've used previously: |
| 237 | +
|
| 238 | +``` |
| 239 | +bitcoin-cli -rpcwallet=seed importdescriptors '''[{ "desc": "'$DESC_WITH_CS_WPKH'", "timestamp":1770329126, "active": true, "range": [0,100] }]''' |
| 240 | + |
| 241 | +| [ |
| 242 | +| { |
| 243 | +| "success": true |
| 244 | +| } |
| 245 | +| ] |
| 246 | +``` |
| 247 | +
|
| 248 | +Again we want to check an address: |
| 249 | +
|
| 250 | +``` |
| 251 | +bitcoin-cli -rpcwallet=seed getnewaddress |
| 252 | + |
| 253 | +| bc1q7jn4qxknxr3d58jwzjyw3x93w7qxwhkypftgaz |
| 254 | +``` |
| 255 | +
|
| 256 | +We can do the same with the PKH address now: |
| 257 | +``` |
| 258 | +bitcoin-cli -rpcwallet=seed importdescriptors '''[{ "desc": "'$DESC_WITH_CS_PKH'", "timestamp":1770329126, "active": true, "range": [0,100] }]''' |
| 259 | + |
| 260 | +| [ |
| 261 | +| { |
| 262 | +| "success": true |
| 263 | +| } |
| 264 | +| ] |
| 265 | +``` |
| 266 | +
|
| 267 | +And check the address one last time: |
| 268 | +
|
| 269 | +``` |
| 270 | +bitcoin-cli -named -rpcwallet=seed getnewaddress address_type=legacy |
| 271 | + |
| 272 | +| 1GPJoLSUCZ1Bb9KFmevNHY28hsgVoDMexH |
| 273 | +``` |
| 274 | +
|
| 275 | +## Make Change |
| 276 | +
|
| 277 | +You can generate WPKH and PKH addresses derived from your seed using your Bitcoin Core wallet. |
| 278 | +
|
| 279 | +There's one catch that's obvious when you try to make change: |
| 280 | +
|
| 281 | +``` |
| 282 | +$ bitcoin-cli -rpcwallet="seed" getrawchangeaddress |
| 283 | +error code: -4 |
| 284 | +error message: |
| 285 | +Error: This wallet has no available keys |
| 286 | +``` |
| 287 | +
|
| 288 | +This is entirely expected. As we've seen previously, the fourth digit |
| 289 | +in a derivation path says whether an address is intended for external |
| 290 | +usage (`0`) or for change (`1`). To make a change address we use the |
| 291 | +same account key, but change the address derivation range from `0/*` |
| 292 | +to `1/*. |
| 293 | +
|
| 294 | +We do this in new variables: |
| 295 | +
|
| 296 | +``` |
| 297 | +DESC_WPKH_C="wpkh([$FINGERPRINT$ADP_NOM_WPKH]$AKEY58_WPKH/1/*)" |
| 298 | +CS_WPKH_C=$(bitcoin-cli getdescriptorinfo $DESC_WPKH_C | jq -r '.checksum') |
| 299 | +DESC_WITH_CS_WPKH_C=$DESC_WPKH_C#$CS_WPKH_C |
| 300 | +``` |
| 301 | +
|
| 302 | +Then import. We also need to set the `internal:` value to true for this to work. |
| 303 | +
|
| 304 | +``` |
| 305 | +bitcoin-cli -rpcwallet=seed importdescriptors '''[{ "desc": "'$DESC_WITH_CS_WPKH_C'", "timestamp":1770329126, "internal": true, "active": true, "range": [0,999] }]''' |
| 306 | +[ |
| 307 | + { |
| 308 | + "success": true |
| 309 | + } |
| 310 | +] |
| 311 | +``` |
| 312 | +
|
| 313 | +Now we can make change: |
| 314 | +
|
| 315 | +``` |
| 316 | +bitcoin-cli -rpcwallet="seed" getrawchangeaddress |
| 317 | + |
| 318 | +| bc1qpecala06gpe3cm3xamfnga92h3yvksvxspgdld |
| 319 | +``` |
| 320 | +
|
| 321 | +## Examing Your Wallet |
| 322 | +
|
| 323 | +If you `listdescriptors` on your seed-based wallet, you can now see three different descriptors: |
| 324 | +
|
| 325 | +``` |
| 326 | +bitcoin-cli -rpcwallet="seed" listdescriptors |
| 327 | + |
| 328 | +| { |
| 329 | +| "wallet_name": "seed", |
| 330 | +| "descriptors": [ |
| 331 | +| { |
| 332 | +| "desc": "pkh([35dad980/44h/0h/0h]xpub6D8wMUDzYRH1Gr2RkSMC1mdLz7kP17HnygdUJarFrFPJj9dAnpa3YBaMTu2qevi53aZzMtdhzM215NWeCT6pZmNWkkm9AyoPq4yb9U1ojhy/0/*)#93z28stv", |
| 333 | +| "timestamp": 1770329126, |
| 334 | +| "active": true, |
| 335 | +| "internal": false, |
| 336 | +| "range": [ |
| 337 | +| 0, |
| 338 | +| 999 |
| 339 | +| ], |
| 340 | +| "next": 1, |
| 341 | +| "next_index": 1 |
| 342 | +| }, |
| 343 | +| { |
| 344 | +| "desc": "wpkh([35dad980/84h/0h/0h]xpub6D778Uy4K1qC1Lfsw2NGEN5dmSkADGPbi4t6q8o3AgMoCkRuSEnVDcPFK28WujQRgvY47UBXJ2zJJ5ZBKELMwA3AS9Zo3yUU8XNu5dZE5As/0/*)#p5auymld", |
| 345 | +| "timestamp": 1770329126, |
| 346 | +| "active": false, |
| 347 | +| "range": [ |
| 348 | +| 0, |
| 349 | +| 1000 |
| 350 | +| ], |
| 351 | +| "next": 2, |
| 352 | +| "next_index": 2 |
| 353 | +| }, |
| 354 | +| { |
| 355 | +| "desc": "wpkh([35dad980/84h/0h/0h]xpub6D778Uy4K1qC1Lfsw2NGEN5dmSkADGPbi4t6q8o3AgMoCkRuSEnVDcPFK28WujQRgvY47UBXJ2zJJ5ZBKELMwA3AS9Zo3yUU8XNu5dZE5As/1/*)#sqcaew04", |
| 356 | +| "timestamp": 1770329126, |
| 357 | +| "active": true, |
| 358 | +| "internal": true, |
| 359 | +| "range": [ |
| 360 | +| 0, |
| 361 | +| 999 |
| 362 | +| ], |
| 363 | +| "next": 1, |
| 364 | +| "next_index": 1 |
| 365 | +| } |
| 366 | +| ] |
| 367 | +| } |
| 368 | +``` |
| 369 | +
|
| 370 | +You've now got three descriptors in your wallet: an external WPKH |
| 371 | +address, a change WPKH address, and an eternal PKH address. |
| 372 | +
|
| 373 | +It would be trivial to keep expanding this wallet using account keys |
| 374 | +based on your seed until you had all 8 descriptors that are in the |
| 375 | +standard wallet created by Bitcoin Core. (The difference would be that |
| 376 | +you would have clear control of a seed, without having to worry about |
| 377 | +Bitcoin Core's unique file format for their wallets.) |
| 378 | +
|
| 379 | +## Summary: Importing Secrets to Bitcoin Core |
| 380 | +
|
| 381 | +In the previous section, you generated account keys for specific |
| 382 | +derivation paths based on your seed. That was most of the battle! |
| 383 | +Here, you just had to incorporate those keys into descriptors in order |
| 384 | +to import them into Bitcoin Core. The result? A wallet where you have |
| 385 | +a resilient seed that you can backup without depending on Bitcoin |
| 386 | +Core's unique file format. |
| 387 | +
|
| 388 | +## What's Next? |
| 389 | +
|
| 390 | +Continue "Exploring the Ecosystem" with [§10.4: Exporting Secrets from |
| 391 | +Bitcoin Core](10_4_Exporting_Secrets_from_Bitcoin_Core.md). |
| 392 | +
|
| 393 | +
|
| 394 | +
|
0 commit comments