Skip to content

Commit c02a82f

Browse files
committed
added new §10.4
1 parent ab529b5 commit c02a82f

2 files changed

Lines changed: 249 additions & 1 deletion

File tree

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
# 10.4: Exporting Secrets from Bitcoin Core
2+
3+
We've spent three sections importing a secret into Bitcoin Core. We're
4+
now going to reverse course and see how to export a secret. (It's a
5+
lot easier!)
6+
7+
## Reset Your Wallet
8+
9+
We're going to continue working in
10+
[mainnet](https://learningbitcoin.blockchaincommons.com/10_3_Importing_Secrets_to_Bitcoin_Core/#use-mainnet)
11+
for this example, but you should use a Bitcoin Core-generated wallet,
12+
not the descriptor-based wallet we imported in the last section.
13+
14+
Unload your "seed" wallet from the last section or create a new ""
15+
wallet depending on what you've done to date with Bitcoin Core.
16+
17+
```
18+
bitcoin-cli unloadwallet "seed"
19+
20+
| {
21+
| }
22+
23+
bitcoin-cli listwallets
24+
25+
| [
26+
| ""
27+
| ]
28+
```
29+
30+
## Understand What You Can Export
31+
32+
It would be ideal to export seeds or seed phrases, but Bitcoin Core
33+
doesn't currently support doing so.
34+
35+
That leaves you with two options:
36+
37+
1. Export a master private key, which can be used to recreate all descriptors.
38+
2. Export individual descriptors.
39+
40+
The first option makes it easy to store just a tiny bit of information
41+
and restore everything from it, but you have to know what derivation
42+
paths your addresses are attached to (or else, you have to check
43+
addresses for every possible derivation path when you import).
44+
45+
The second option requires more data storage, but ensures that you
46+
have the precise data on the accounts (derivation paths) where you
47+
might have funds.
48+
49+
## Understand Bitcoin Core Outputs
50+
51+
The `listdescriptors` RPC command is what you use to retrieve
52+
information about your keys and descriptors for export. You've seen it a number
53+
of times:
54+
55+
```
56+
bitcoin-cli listdescriptors
57+
58+
| {
59+
| "wallet_name": "",
60+
| "descriptors": [
61+
| {
62+
| "desc": "pkh([3ce1d173/44h/0h/0h]xpub6BxVLeeQGSK4tVGLLZopaabNwFUZe223FfLCAa3PWNxpwqnCcZVRvDNH13fKh4Ea5w785U9a7u1LX5Tu9m9gpdi2i5PEE6aMNvXGi2LuKdV/0/*)#q8nl9w4h",
63+
| "timestamp": 1779999396,
64+
| "active": true,
65+
| "internal": false,
66+
| "range": [
67+
| 0,
68+
| 999
69+
| ],
70+
| "next": 0,
71+
| "next_index": 0
72+
| },
73+
| {
74+
| "desc": "pkh([3ce1d173/44h/0h/0h]xpub6BxVLeeQGSK4tVGLLZopaabNwFUZe223FfLCAa3PWNxpwqnCcZVRvDNH13fKh4Ea5w785U9a7u1LX5Tu9m9gpdi2i5PEE6aMNvXGi2LuKdV/1/*)#3nk7cm90",
75+
| "timestamp": 1779999396,
76+
| "active": true,
77+
| "internal": true,
78+
| "range": [
79+
| 0,
80+
| 999
81+
| ],
82+
| "next": 0,
83+
| "next_index": 0
84+
| },
85+
| {
86+
| "desc": "sh(wpkh([3ce1d173/49h/0h/0h]xpub6BopUUpLTpQDti8eEjdRKhp1bGcxHXJ9Mvdm23iSPSPh84ofH6icrDg8sH4kowsZEDcYynaHPqVS4mvrMvgHpi2sNKSBEG9s11SeR9yfvJ2/0/*))#32q0qkdf",
87+
| "timestamp": 1779999396,
88+
| "active": true,
89+
| "internal": false,
90+
| "range": [
91+
| 0,
92+
| 999
93+
| ],
94+
| "next": 0,
95+
| "next_index": 0
96+
| },
97+
|
98+
| ...
99+
| ]
100+
| }
101+
...
102+
103+
By default, each descriptor shown by `listdescriptors` contains the
104+
account public key for that descriptor. You'll note that they're the
105+
same for the external and internal descriptors for the address, but
106+
when you move on to a different derivation path, they're different.
107+
108+
So the public keys for the two `44h/0h/0h` addresses are
109+
`xpub6BxVLe...` while the public key for the `49h/0h/0h` address is
110+
`xpub6BopUU...`. You know that these addresses are account keys
111+
because the account derivation path is given before the key in the
112+
brackets.
113+
114+
Now, public keys aren't at all useful if you're trying to backup your
115+
secrets. Fortunately, `listdescriptors` will give you private keys if
116+
you add a `true` argument (which sets the `private` named argument to
117+
true).
118+
119+
```
120+
bitcoin-cli -rpcwallet="" listdescriptors true
121+
122+
| {
123+
| "wallet_name": "",
124+
| "descriptors": [
125+
| {
126+
| "desc": "pkh(xprv9s21ZrQH143K4YGw3kaAmh3ezx1Xo4AanFmnX8TbaGghuNxXP9MDt2rPrLDyouX5kg9fiDJn7BELPnWRKwtVFn6aUBaNYJC9Mu6s39DnAo4/44h/0h/0h/0/*)#e0uydrgt",
127+
| "timestamp": 1779999396,
128+
| "active": true,
129+
| "internal": false,
130+
| "range": [
131+
| 0,
132+
| 999
133+
| ],
134+
| "next": 0,
135+
| "next_index": 0
136+
| },
137+
| {
138+
| "desc": "pkh(xprv9s21ZrQH143K4YGw3kaAmh3ezx1Xo4AanFmnX8TbaGghuNxXP9MDt2rPrLDyouX5kg9fiDJn7BELPnWRKwtVFn6aUBaNYJC9Mu6s39DnAo4/44h/0h/0h/1/*)#gme9skcn",
139+
| "timestamp": 1779999396,
140+
| "active": true,
141+
| "internal": true,
142+
| "range": [
143+
| 0,
144+
| 999
145+
| ],
146+
| "next": 0,
147+
| "next_index": 0
148+
| },
149+
| {
150+
| "desc": "sh(wpkh(xprv9s21ZrQH143K4YGw3kaAmh3ezx1Xo4AanFmnX8TbaGghuNxXP9MDt2rPrLDyouX5kg9fiDJn7BELPnWRKwtVFn6aUBaNYJC9Mu6s39DnAo4/49h/0h/0h/0/*))#pfqdkhdr",
151+
| "timestamp": 1779999396,
152+
| "active": true,
153+
| "internal": false,
154+
| "range": [
155+
| 0,
156+
| 999
157+
| ],
158+
| "next": 0,
159+
| "next_index": 0
160+
| },
161+
|
162+
| ...
163+
|
164+
| ]
165+
| }
166+
```
167+
168+
You'll note that the [bracketed] prefix is now gone. Instead, the
169+
entire derivation path appears _after_ the key (which is now a private
170+
key). That means that you're now seeing the master private key, before
171+
any derivation occurs. As you'd expect, that master key is exactly the
172+
same for each of the accounts (derivation paths).
173+
174+
It's great that Bitcoin Core gives you access to the master private
175+
key.
176+
177+
It's not great that it shows the underived master key in each of its
178+
account descriptors, because that means that if any of them is
179+
compromised, they all are. (But we can use `keytool` to change what we
180+
store, which we'll do in the next section.)
181+
182+
### Testing Derivation
183+
184+
If you're ever unsure of what Bitcoin Core (or any other Bitcoin
185+
program) is showing you, then you can always use `keytool` to test
186+
your understanding.
187+
188+
In this case, we think we understand that `xprv9s21Zr...` is a master
189+
private key and ``xpub6BxVLe...` is an account public key, derived
190+
from it for `44h/0h/0h`. You can verify that by inputting the
191+
(purported) master key into `keytool` and verifying that the account
192+
public key for `44h/0h/0h` is what you expect.
193+
194+
```
195+
keytool --master-key xprv9s21ZrQH143K4YGw3kaAmh3ezx1Xo4AanFmnX8TbaGghuNxXP9MDt2rPrLDyouX5kg9fiDJn7BELPnWRKwtVFn6aUBaNYJC9Mu6s39DnAo4 --account-derivation-path "m/44h/0h/0h" account-pub-key-base58
196+
197+
| xpub6BxVLeeQGSK4tVGLLZopaabNwFUZe223FfLCAa3PWNxpwqnCcZVRvDNH13fKh4Ea5w785U9a7u1LX5Tu9m9gpdi2i5PEE6aMNvXGi2LuKdV
198+
```
199+
200+
It is!
201+
202+
## Store Your Data
203+
204+
You can now collect the information you'd need to restore this
205+
data. `jq` can be used to pull out the whole list of descriptors:
206+
207+
```
208+
DESCS=$(bitcoin-cli listdescriptors true | jq -r '.descriptors[].desc')
209+
DESC_ARRAY=($DESCS)
210+
211+
for ((i = 0; i < 8; i++)); do
212+
echo "$i: ${DESC_ARRAY[$i]}";
213+
done
214+
215+
| 0: pkh(xprv9s21ZrQH143K4YGw3kaAmh3ezx1Xo4AanFmnX8TbaGghuNxXP9MDt2rPrLDyouX5kg9fiDJn7BELPnWRKwtVFn6aUBaNYJC9Mu6s39DnAo4/44h/0h/0h/0/*)#e0uydrgt
216+
| 1: pkh(xprv9s21ZrQH143K4YGw3kaAmh3ezx1Xo4AanFmnX8TbaGghuNxXP9MDt2rPrLDyouX5kg9fiDJn7BELPnWRKwtVFn6aUBaNYJC9Mu6s39DnAo4/44h/0h/0h/1/*)#gme9skcn
217+
| 2: sh(wpkh(xprv9s21ZrQH143K4YGw3kaAmh3ezx1Xo4AanFmnX8TbaGghuNxXP9MDt2rPrLDyouX5kg9fiDJn7BELPnWRKwtVFn6aUBaNYJC9Mu6s39DnAo4/49h/0h/0h/0/*))#pfqdkhdr
218+
| 3: sh(wpkh(xprv9s21ZrQH143K4YGw3kaAmh3ezx1Xo4AanFmnX8TbaGghuNxXP9MDt2rPrLDyouX5kg9fiDJn7BELPnWRKwtVFn6aUBaNYJC9Mu6s39DnAo4/49h/0h/0h/1/*))#82ggd6xh
219+
| 4: tr(xprv9s21ZrQH143K4YGw3kaAmh3ezx1Xo4AanFmnX8TbaGghuNxXP9MDt2rPrLDyouX5kg9fiDJn7BELPnWRKwtVFn6aUBaNYJC9Mu6s39DnAo4/86h/0h/0h/0/*)#q58cmp0l
220+
| 5: tr(xprv9s21ZrQH143K4YGw3kaAmh3ezx1Xo4AanFmnX8TbaGghuNxXP9MDt2rPrLDyouX5kg9fiDJn7BELPnWRKwtVFn6aUBaNYJC9Mu6s39DnAo4/86h/0h/0h/1/*)#3qzex5l8
221+
| 6: wpkh(xprv9s21ZrQH143K4YGw3kaAmh3ezx1Xo4AanFmnX8TbaGghuNxXP9MDt2rPrLDyouX5kg9fiDJn7BELPnWRKwtVFn6aUBaNYJC9Mu6s39DnAo4/84h/0h/0h/0/*)#u8jwrsql
222+
| 7: wpkh(xprv9s21ZrQH143K4YGw3kaAmh3ezx1Xo4AanFmnX8TbaGghuNxXP9MDt2rPrLDyouX5kg9fiDJn7BELPnWRKwtVFn6aUBaNYJC9Mu6s39DnAo4/84h/0h/0h/1/*)#dnh079s8
223+
```
224+
225+
Since all of the descriptors include the master private key, you can
226+
retrieve it from any of them.
227+
228+
```
229+
MP_KEY=$(echo ${DESC_ARRAY[0]} | awk -F"[()]" '{print $2}')
230+
echo $MP_KEY
231+
232+
| xprv9s21ZrQH143K4YGw3kaAmh3ezx1Xo4AanFmnX8TbaGghuNxXP9MDt2rPrLDyouX5kg9fiDJn7BELPnWRKwtVFn6aUBaNYJC9Mu6s39DnAo4/44h/0h/0h/0/*
233+
```
234+
235+
## Summary: Exporting Secrets from Bitcoin Core
236+
237+
Bitcoin Core doesn't currently allow you to export the seed. However,
238+
you can export either your master private key or your descriptors from
239+
the `listdescriptors` command
240+
241+
## What's Next?
242+
243+
Conclude "Exploring the Ecosystem" with [§10.5: Storing Secrets with
244+
Envelope](0_5_Storing_Secrets_with_Envelope.md).
245+
246+
247+
248+

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ complete state.</i>
6363
* [10.1: Creating Secrets from the Command Line](10_1_Creating_Secrets_from_the_Command_Line.md)
6464
* [10.2: Converting Secrets with Keytool](10_2_Converting_Secrets_with_Keytool.md)
6565
* [10.3: Importing Secrets to Bitcoin Core](10_3_Importing_Secrets_to_Bitcoin_Core.md)
66-
* [10.4: Exporting Secrets from Bitcoin Core] (10_4_Exporting_Secrets_from_Bitcoin_Core.md)
66+
* [10.4: Exporting Secrets from Bitcoin Core](10_4_Exporting_Secrets_from_Bitcoin_Core.md)
6767
* [10.5: Storing Secrets with Envelope] (10_5_Storing_Secrets_with_Envelope.md)
6868

6969
---

0 commit comments

Comments
 (0)