|
81 | 81 |
|
82 | 82 | Use a computer you believe is malware-free and whose other software you trust. |
83 | 83 | Only codex32 and Bitcoin Core should perform recovery, derivation, wallet |
84 | | -initialization, or signing. The QR tools below transport only public |
85 | | -descriptors, PSBTs, and signed transactions. |
| 84 | +initialization, or signing. |
86 | 85 |
|
87 | 86 | Bitcoin Core wallet encryption is strongly recommended. Bitcoin Core owns the |
88 | 87 | passphrase and its prompts; codex32 never asks for, reads, or forwards it. |
@@ -192,133 +191,16 @@ worth the extra steps. |
192 | 191 |
|
193 | 192 | ## More protection: watch-only wallet and offline signer |
194 | 193 |
|
195 | | -This setup follows Bitcoin Core v32's |
196 | | -[offline-signing tutorial](https://github.com/bitcoin/bitcoin/blob/v32.0rc1/doc/offline-signing-tutorial.md) |
197 | | -and its two wallets: `offline_wallet` holds the signing keys on a computer that |
198 | | -never connects to a network, and `watch_only_wallet` runs on an ordinary online |
199 | | -node. Where the tutorial carries a file between the computers, this guide shows |
200 | | -the same public data as a QR on one screen and scans it with the other |
201 | | -computer's camera. |
| 194 | +On the offline signer, create an empty encrypted descriptor wallet with private |
| 195 | +keys enabled and run `ms32 wallet`. Take that computer and its USB stick offline |
| 196 | +before you restore the signer, and keep them offline for good. |
202 | 197 |
|
203 | | -Before disconnecting the offline computer for good, install Bitcoin Core and |
204 | | -codex32. Both computers also use `python3`, `gzip`, `qr`, and ZBar's |
205 | | -`zbarcam`, which Tails already includes. Disable Ethernet, internet, Tor, Wi-Fi, |
206 | | -Bluetooth, cellular, and every other network path on the offline computer. |
207 | | - |
208 | | -### 1. Restore the offline signer |
209 | | - |
210 | | -On the offline computer, create an empty encrypted descriptor wallet named |
211 | | -`offline_wallet` with private keys enabled and run `ms32 wallet`. Keep that |
212 | | -computer disconnected from every network while recovery text or signing keys |
213 | | -are present. |
214 | | - |
215 | | -### 2. Create the online watch-only wallet |
216 | | - |
217 | | -The tutorial moves a watch-only wallet file made by `exportwatchonlywallet`. |
218 | | -That file does not fit in a QR even compressed, so send the public descriptors |
219 | | -instead, compressed with `gzip`. On the offline computer: |
220 | | - |
221 | | -```bash |
222 | | -bitcoin-cli -rpcwallet=offline_wallet listdescriptors | |
223 | | - python3 -c 'import json, sys; print(json.dumps(json.load(sys.stdin)["descriptors"]), end="")' | |
224 | | - gzip -9 | qr |
225 | | -``` |
226 | | - |
227 | | -The compressed descriptors take about 640 bytes; see |
228 | | -[QR troubleshooting](#qr-troubleshooting) if the QR does not fit. Public |
229 | | -descriptors cannot spend, but they reveal wallet activity. Do not use a |
230 | | -website, cloud scanner, chat service, or synced clipboard. |
231 | | - |
232 | | -On the online computer, create a blank watch-only wallet, then scan and |
233 | | -decompress the QR into it: |
234 | | - |
235 | | -```bash |
236 | | -bitcoin-cli -named createwallet wallet_name=watch_only_wallet disable_private_keys=true blank=true |
237 | | -zbarcam --raw --oneshot -Sdisable -Sqrcode.enable -Sbinary | gunzip | |
238 | | - bitcoin-cli -rpcwallet=watch_only_wallet -stdin importdescriptors |
239 | | -``` |
240 | | - |
241 | | -Every result must say `"success": true`. Core's descriptor export keeps the |
242 | | -stored timestamps, so the online node rescans from the same point. Run |
243 | | -`getnewaddress` in each wallet and compare the two addresses on the two |
244 | | -screens. Do not receive funds if they differ. |
245 | | - |
246 | | -### 3. Receive to a checked address |
247 | | - |
248 | | -Get receiving addresses and set labels in `watch_only_wallet`, as the tutorial |
249 | | -does, so one wallet tracks which addresses are used. Malware on the online |
250 | | -computer could show an address it controls, so check every address on the |
251 | | -offline computer before giving it out. On the online computer: |
252 | | - |
253 | | -```bash |
254 | | -bitcoin-cli -rpcwallet=watch_only_wallet getnewaddress "LABEL" | qr |
255 | | -``` |
256 | | - |
257 | | -On the offline computer, scan it and look it up in the signing wallet: |
258 | | - |
259 | | -```bash |
260 | | -address=$(zbarcam --raw --oneshot -Sdisable -Sqrcode.enable) |
261 | | -bitcoin-cli -rpcwallet=offline_wallet getaddressinfo "$address" |
262 | | -``` |
263 | | - |
264 | | -Give out the address only if the result shows `"ismine": true`. This works |
265 | | -while `offline_wallet` is locked. The offline wallet recognizes its first 1,000 |
266 | | -addresses of each type; past that, a real address shows `"ismine": false` until |
267 | | -you unlock `offline_wallet` and run `keypoolrefill` with a larger number. |
268 | | - |
269 | | -When you pay yourself from a phone wallet, or the payer is with you, show the |
270 | | -checked address as a QR on the offline screen and scan it there; nothing needs |
271 | | -comparing: |
272 | | - |
273 | | -```bash |
274 | | -printf %s "$address" | qr |
275 | | -``` |
276 | | - |
277 | | -For an exchange withdrawal or a payer over the internet, the address must pass |
278 | | -through a networked computer or phone, where malware could swap it after the |
279 | | -check. Paste it there, then compare the address on the last screen before you |
280 | | -submit or send, such as the exchange's confirmation page or your sent message, |
281 | | -character by character with the `"address"` shown offline. |
282 | | - |
283 | | -### 4. Spend with a PSBT |
284 | | - |
285 | | -On the online computer, create the unsigned PSBT with your destination and |
286 | | -amount, and show it as a QR: |
287 | | - |
288 | | -```bash |
289 | | -bitcoin-cli -rpcwallet=watch_only_wallet send '{"DESTINATION_ADDRESS": AMOUNT}' | |
290 | | - python3 -c 'import json, sys; print(json.load(sys.stdin)["psbt"], end="")' > funded_psbt.txt && |
291 | | - qr < funded_psbt.txt |
292 | | -``` |
293 | | - |
294 | | -On the offline computer, scan it, then check every destination, amount, and |
295 | | -fee before signing: |
296 | | - |
297 | | -```bash |
298 | | -zbarcam --raw --oneshot -Sdisable -Sqrcode.enable > funded_psbt.txt |
299 | | -bitcoin-cli decodepsbt "$(cat funded_psbt.txt)" |
300 | | -bitcoin-cli analyzepsbt "$(cat funded_psbt.txt)" |
301 | | -``` |
302 | | - |
303 | | -Unlock `offline_wallet` as the tutorial shows, then sign and show the signed |
304 | | -transaction as a QR: |
305 | | - |
306 | | -```bash |
307 | | -bitcoin-cli -rpcwallet=offline_wallet walletprocesspsbt "$(cat funded_psbt.txt)" | |
308 | | - python3 -c 'import json, sys; r = json.load(sys.stdin); print(r["hex"] if r["complete"] else sys.exit("The PSBT is not fully signed."), end="")' > final_psbt.txt && |
309 | | - qr < final_psbt.txt |
310 | | -``` |
311 | | - |
312 | | -On the online computer, scan it and broadcast: |
313 | | - |
314 | | -```bash |
315 | | -zbarcam --raw --oneshot -Sdisable -Sqrcode.enable > final_psbt.txt |
316 | | -bitcoin-cli sendrawtransaction "$(cat final_psbt.txt)" |
317 | | -``` |
318 | | - |
319 | | -If a PSBT is too large for a reliable QR, use a dedicated removable drive. The |
320 | | -drive crosses the security boundary: keep it for this purpose, treat every file |
321 | | -on it as untrusted, and still verify the transaction on the offline screen. |
| 198 | +After the signer is restored, follow Bitcoin Core's maintained |
| 199 | +[offline-signing tutorial](https://github.com/bitcoin/bitcoin/blob/master/doc/offline-signing-tutorial.md). |
| 200 | +That workflow owns the watch-only export/import and PSBT transport steps. In |
| 201 | +Bitcoin Core v32, `exportwatchonlywallet` creates the watch-only wallet file and |
| 202 | +`restorewallet` loads it on the online node. Do not improvise a codex32-specific |
| 203 | +descriptor-transfer procedure in place of that maintained workflow. |
322 | 204 |
|
323 | 205 | ## Recover an existing or inherited wallet |
324 | 206 |
|
@@ -348,8 +230,8 @@ its public wallet data with the separate wallet record. |
348 | 230 | It gives Core the master private key, asks Core to create the standard |
349 | 231 | account-0 descriptors, scans history, and relocks an encrypted wallet. |
350 | 232 | 6. If you need an online watch-only counterpart, keep the restored signer |
351 | | - offline and follow Bitcoin Core v32's |
352 | | - [offline-signing tutorial](https://github.com/bitcoin/bitcoin/blob/v32.0rc1/doc/offline-signing-tutorial.md) |
| 233 | + offline and follow Bitcoin Core's |
| 234 | + [offline-signing tutorial](https://github.com/bitcoin/bitcoin/blob/master/doc/offline-signing-tutorial.md) |
353 | 235 | to export and restore the watch-only wallet. Let the online node synchronize, |
354 | 236 | then compare the recovered fingerprint, account, policy, addresses, balance, |
355 | 237 | and transaction history with the wallet record. |
@@ -410,15 +292,6 @@ Keep the matching application worksheet and original wallet instructions with |
410 | 292 | the inheritance plan. Published BIP-93 still defines the `ms` application; the |
411 | 293 | arbitrary-HRP format direction is not yet merged into that specification. |
412 | 294 |
|
413 | | -### QR troubleshooting |
414 | | - |
415 | | -Maximize the terminal and reduce its font size if a QR does not fit. Keep `qr` |
416 | | -connected to the terminal; redirecting its output creates an image file. |
417 | | -Reading the compressed descriptors needs ZBar 0.23.1 or newer for `-Sbinary`; |
418 | | -without it, ZBar rewrites the bytes as text and `gunzip` fails. Only public |
419 | | -descriptors, xpubs, PSBTs, and signed transactions may cross the offline |
420 | | -boundary by QR. |
421 | | - |
422 | 295 | ## Technical references |
423 | 296 |
|
424 | 297 | Automation, low-level private exports, parser behavior, correction mathematics, |
|
0 commit comments