Assumption:
How do I import a bech32, bech32m, nested-segwit, etc. master key into a blank wallet?
You create a descriptor including your key for whatever address type you want and import it with
importdescriptors.
For example, let's say you have the xprv
xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6Ln F5kejMRNNU3TGtRBeJgk33yuGBxrMPHi and wanted to import that to create Taproot addresses derived using the
BIP 86 derivation path standard.
First you can look at Bitcoin Core's
document on descriptors and see that a
tr() type descriptor creates P2TR addresses. Now you can construct the descriptor without a checksum:
tr(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/86h/0h/0h/0/*)
However, in order to import this, you need to the checksum, so you can put it through
getdescriptorinfo for that:
$ bitcoin-cli getdescriptorinfo "tr(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/86h/0h/0h/0/*)"
{
"descriptor": "tr(xpub661MyMwAqRbcFtXgS5sYJABqqG9YLmC4Q1Rdap9gSE8NqtwybGhePY2gZ29ESFjqJoCu1Rupje8YtGqsefD265TMg7usUDFdp6W1EGMcet8/86h/0h/0h/0/*)#vfh547jk",
"checksum": "sffjysau",
"isrange": true,
"issolvable": true,
"hasprivatekeys": true
}
Notice how the descriptor it gives you back contains the xpub, not the xprv. This is intentional to avoid reflecting back private keys. But also see how the checksum field is different from the checksum in the returned descriptor. That's because the checksum field is the checksum for whatever you input, while the returned descriptor has its own valid checksum.
So you can now construct the full descriptor by taking the part that you've already constructed, adding the checksum separator character of
#, and adding the checksum:
tr(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/86h/0h/0h/0/*)#sffjysau
Now you can import it with
importdescriptors:
$bitcoin-cli importdescriptors '[{"desc":"tr(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/86h/0h/0h/0/*)#sffjysau","timestamp":"now"}]'
[
{
"success": true,
"warnings": [
"Range not given, using default keypool range"
]
}
]
That is the most basic type of import - one which contains just the descriptor and a timestamp to start rescanning from. This descriptor is now in your wallet, but you cannot use it to retrieve new addresses. But it will see any transactions to and from addresses derived from it that were setup elsewhere. If there is a transaction history, you'll need to set the timestamp to be older so that this history is picked up. It can be given as a unix timestamp, or as a block height. This is used to find the oldest block to start scanning from.
To be able to get addresses from this, you would have to add
"active": true to your import command:
$ bitcoin-cli importdescriptors '[{"desc":"tr(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/86h/0h/0h/0/*)#sffjysau","timestamp":"now","active":true}]'
[
{
"success": true,
"warnings": [
"Range not given, using default keypool range"
]
}
]
Now you can use
getnewaddress to get new bech32m addresses:
$ bitcoin-cli getnewaddress "" "bech32m"
bc1pqqeyhah6g75dwr942xv40h255q4nshqw4k8ylyhe7plej2eg3mnqz9w4np
But what about change addresses? Change addresses are derived from a different descriptor (well you could use the same one, but that's not recommended. It would still work though). The BIP 86 standard specifies a different derivation path for change addresses, so you can still use the same key but make a different descriptor that has a different derivation path:
tr(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/86h/0h/0h/1/*)#pavne9dy
To import this to be used for new bech32m change addresses, you need to also specify
"internal":true, in addition to
"active": true:
$ bitcoin-cli importdescriptors '[{"desc":"tr(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/86h/0h/0h/1/*)#pavne9dy","timestamp":"now","active":true,"internal":true}]'
[
{
"success": true,
"warnings": [
"Range not given, using default keypool range"
]
}
]
This can be verified by using
getrawchangeaddress:
$ bitcoin-cli getrawchangeaddress "bech32m"
bc1pze68k8k30zu29nzzllmhvet26avtxyfec5n88ymwzmd59mxw0wpqlt3mwu
Now importing these one by one is kind of tedious, but if you know JSON and looked closely, you'd see that
importdescriptors accepts an array. So you can actually provide multiple descriptors in the same command, e.g. to import both descriptors used for change and receiving:
$ bitcoin-cli importdescriptors '[{"desc":"tr(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/86h/0h/0h/0/*)#sffjysau","timestamp":"now","active":true},{"desc":"tr(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/86h/0h/0h/1/*)#pavne9dy","timestamp":"now","active":true,"internal":true}]'
[
{
"success": true,
"warnings": [
"Range not given, using default keypool range"
]
},
{
"success": true,
"warnings": [
"Range not given, using default keypool range"
]
}
]
The same goes if I just want to import an xpub (I believe it's the same command, just changing the descriptor and checksum, correct me if I'm wrong).
Yes, if you want to import a xpub to watch, then you do the same as I described above but with an xpub. Note however that descriptors without any private keys can only be imported to wallets that have private keys disabled (i.e. watch only wallets). This is something you must turn on when creating the wallet. Likewise, descriptors with private keys can only be imported to wallets that have private keys enabled (the default when making a new wallet). Furthermore, wallets with private keys enabled requires all descriptors to have at least one private key.
And is there any command in bitcoin core to import a bunch of private keys into a descriptor wallet at once? (Similar to the importwallet command).
importdescriptors can be used to import multiple descriptors simultaneously, as I showed above. They do not have to be related, and is equivalent to just running the command once per descriptor, but much faster.
And can I import as many descriptors (xpriv, xpub etc) into a descriptor wallet as I want or is there a limit? Or does the wallet have a limit and replace the native descriptors with the new imported descriptors?
There's no explicit limit. It is not necessary to replace the native descriptors nor only have replacements for them. You can have however many descriptors that you want, so long as you have the disk space and are willing to put up with the wallet eventually getting slower. I've done some test and somewhere in the neighborhood of a few thousand descriptors it'll get noticeably slow to respond. But there are ongoing changes to improve that, and if you use xpubs and xprvs, then it shouldn't be a problem.