Skip to content

Commit 0fc858f

Browse files
added chapter 10
1 parent b619114 commit 0fc858f

1 file changed

Lines changed: 394 additions & 0 deletions

File tree

Lines changed: 394 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,394 @@
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

Comments
 (0)