How-to: setup your multisignature Cold wallet in Bitcoin Core 0.20 (highest security setup)

Last release of Core is amazing !

The main new feature is sortedmulti descriptor. This allows you to import your multisig setup in Core almost as if it was Electrum when combine to the new PSBT export in GUI !

As it needs command line and some weird checksum, you also need to input very long command in the console and if you made a mistake, you cannot copy the last command you made. So take your time when the commands are long to check everything and don't miss anything, use copy paste before validating the long command. You only have to do this once fortunately :)

I detail here how you do it with a k of n setup, good luck:

  • Use HWI to get the n xpub and fingerprint of all your hardware wallets signers (basically, you will do some command like ./hwi.py enumerate to get the fingerprint fffffff and ./hwi.py -f fffffff getxpub derivation_path for each of the hardware wallets used in the multisig setup, for air-gapped ones you normally have native way to get xpub). If you use other tools, remember it must be xpub formated, not Zpub or Ypub (which may represent the same key but very differently, use https://jlopp.github.io/xpub-converter/ at your own risk if you don't want to use HWI).
  • Create a blank wallet in Core console (or cli) with createwallet "Name_wallet" true true, the private key are also disabled. Load this wallet in command line of QT (you must select Name_wallet in Qt at the top of command line, or load it with loadwallet). All command must be run with this wallet loaded (don't load another one in between ....)
  • Let details the descriptors we can use. For a P2SH (legacy) multisig setup it looks like sh(sortedmulti(k,...)), for a P2WSH (native segwit) setup wsh(sortedmulti(k,...)), and for a P2SH-P2WSH (segwit) sh(wsh(sortedmulti(k,...))) because we all want to spare fees, I will take P2WSH for what happens next but you know how to get other addresses type. Our descriptors will look like wsh(sortedmulti(k,[path1]xpub1.../0/*,[path2]xpub2.../0/*,...,[pathn]xpubn/0/*)) for receiving addresses and wsh(sortedmulti(k,[path1]xpub1.../1/*,[path2]xpub2.../1/*,...,[pathn]xpubn/1/*)) for change. pathm is the derivation path of the mth xpub of our setup (order of xpub doesn't matter). But be careful: for a derivation path like m/44'/0'/0'/312 you must write pathm as fffffff/44h/0h/0h/312 where fffffff is the fingerpring of the xpub m given by HWI and h replace ' (else it is harder to input in command-line, you need to input \' instead of ' each time)
  • We need to get the checksum of the descriptors we use. We call getdescriptorinfo "wsh(sortedmulti(k,[path1]xpub1.../0/*,[path2]xpub2.../0/*,...,[pathn]xpubn/0/*))" and getdescriptorinfo "wsh(sortedmulti(k,[path1]xpub1.../1/*,[path2]xpub2.../1/*,...,[pathn]xpubn/1/*))". In the two cases, the result is a JSON with a new descriptor (we don't care of it) and a field like so "checksum": "nefdbkdf". This second string is what we want, we name checksum0 and checksum1 the checksum results of the two calls.
  • We import the 2000 first receiving addresses with importmulti '[{"desc": "wsh(sortedmulti(k,[path1]xpub1.../0/*,[path2]xpub2.../0/*,...,[pathn]xpubn/0/*))#check_sum0", "timestamp": birth_timestamp, "range": [0,2000], "watchonly": true, "keypool": true}]' . You recognize the same first descriptor and we append checksum0 to it with a # . If you didn't use this wallet setup before (in Electrum ...), set birth_timestamp to "now" else input a timestamp close to the first time you used the wallet to avoid a full rescan (if you don't remember, delete it from the call and enjoy a coffee during full rescans)
  • We import the 2000 first change addresses with importmulti '[{"desc": "wsh(sortedmulti(k,[path1]xpub1.../1/*,[path2]xpub2.../1/*,...,[pathn]xpubn/1/*))#check_sum1", "timestamp": birth_timestamp, "range": [0,2000], "watchonly": true, "internal": true}]'. Notice that keypool is not set, so it is set to false: the keypool is needed to show you receiving addresses one by one to avoid addresses reuse. For change addresses, we need to set internal to true so that they are added to the outputs as change automatically.

And you are DONE ! You should get the exact same addresses than Electrum and you can created receiving addresses in Qt ! To send money, just go to the send section, use the new coin control feature and export a partially signed transaction. You can use HWI or Electrum to sign it with your hardware wallets !

Notice: You can import more or less than 2000 addresses of each type. If less, blockchain rescan is faster but you may need to redo what we have done here later when all addresses will have been used once. If more, it is the contrary.

You now have the most possibly secure setup in one software: multisig with hardware on the full node wallet. When Bitcoin Core 0.21.0 will be out, we will also have native descriptor wallet so maybe we will have HD version of this. But for now, this is the best you can do ! Enjoy :)

P.S. : if you like doing things in one shot you can do the last two steps in one big command: importmulti '[{"desc": "wsh(sortedmulti(k,[path1]xpub1.../0/*,[path2]xpub2.../0/*,...,[pathn]xpubn/0/*))#check_sum0", "timestamp": birth_timestamp, "range": [0,2000], "watchonly": true, "keypool": true}, {"desc": "wsh(sortedmulti(k,[path1]xpub1.../1/*,[path2]xpub2.../1/*,...,[pathn]xpubn/1/*))#check_sum1", "timestamp": birth_timestamp, "range": [0,2000], "watchonly": true, "internal": true}]'

submitted by /u/Pantamis
[link] [comments]

Comments