Author

Topic: How to import an xpriv to a descriptor wallet in bitcoin core? (Read 47 times)

hero member
Activity: 1120
Merit: 540
Press F for Leo

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.

Thank you very much @Achow101, all questions regarding the list and importdescriptors commands were answered, I'd some tests here (not all yet) and they worked correctly.
staff
Activity: 3374
Merit: 6530
Just writing some code
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:

Code:
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:

Code:
$ 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:

Code:
tr(xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi/86h/0h/0h/0/*)#sffjysau

Now you can import it with importdescriptors:

Code:
$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:

Code:
$ 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:

Code:
$ 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:

Code:
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:

Code:
$ 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:

Code:
$ 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:

Code:
$ 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.
hero member
Activity: 1120
Merit: 540
Press F for Leo
Which wallet are you referring to? If you import seed phrase, master private key or private key (WIF) into a wallet, you should b able to spend your coins.

If you import the master public key or the address, that is just a watch-only wallet which you can not use to spend your coins. Watch-only wallet are used to make unsigned transaction, broadcast transaction that has been signed which is sent to it and also to track your transaction.
I edited the thread referring to Bitcoin Core.

Bitcoin Core does not work with seed phrases, only xpriv and Xpub (master key). And there is currently no way to import or dump a singular private key into bitcoin core due to the generated wallets being descriptors.

legendary
Activity: 1512
Merit: 4795
An important detail, when importing descriptors, whether xpriv or singular key in a blank wallet, the send button is not active (I cannot send funds to another address), it says that there is no change address, so how do I import both an xpriv ex and internal so that I can enable this blank wallet to enable spending?
Which wallet are you referring to? If you import seed phrase, master private key or private key (WIF) into a wallet, you will be able to spend the coins.

If you import the master public key or the address. That is just a watch-only wallet. You can not use such wallet to spend your coins. Watch-only wallet are used to make unsigned transaction, broadcast transaction that has been signed on another wallet which is sent to it. Also watch-only wallets are used to track your transactions. It also make it easy to have access to your addresses.
hero member
Activity: 1120
Merit: 540
Press F for Leo
Assumption:

How do I import a bech32, bech32m, nested-segwit, etc. master key into a blank wallet?

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).

An important detail, when importing descriptors, whether xpriv or singular key in a blank wallet, the send button is not active (I cannot send funds to another address), it says that there is no change address, so how do I import both an xpriv ex and internal so that I can enable this blank wallet to enable spending?

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).

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?




Jump to: