5

If the Bitcoin Core Wallet Migration Tool is supposed to be creating descriptors for sparse keys (as it's supposed to support pre deterministic wallets), then why doesn't Bitcoin Core allow using importprivkey on a descriptor wallet?

I'm trying to, and it's telling error code -4 (type of wallet does not support this command).

How can I import a private key to a descriptor wallet?

Murch
  • 75,206
  • 34
  • 186
  • 622
Mercedes
  • 802
  • 6
  • 25

2 Answers2

6

The migration tool has not been implemented yet, and it is not for manually importing keys. Rather it takes an existing wallet and migrates everything in that wallet.

As the error message says, descriptor wallets do not support importprivkey. You will have to use importdescriptors with the private key you want to import as part of the proper descriptor.

For example, if you wanted to import the P2WPKH address for a private key, you would make a descriptor of the form wpkh(<privkey>). Then use getdescriptorinfo to get the checksum and create wpkh(<privkey>)#<checksum>, and then use importdescriptors to import that descriptor.

Mercedes
  • 802
  • 6
  • 25
Ava Chow
  • 70,382
  • 5
  • 81
  • 161
  • getdescriptorinfo would just tell wpkh(): Uncompressed keys are not allowed – Mercedes Oct 01 '22 at 13:21
  • You shouldn't use uncompressed keys in wpkh; they aren't valid. – Pieter Wuille Oct 01 '22 at 13:57
  • @PieterWuille understood, thanks, now I got checksum. – Mercedes Oct 01 '22 at 14:17
  • Importing the descriptor would then answer error code -4 Cannot import descriptor without private keys to a wallet with private keys enabled, but the descriptor was created out of a dumped privkey and the wallet is an newly created descriptors wallet – Mercedes Oct 01 '22 at 14:44
  • @Mercedes getdescriptorinfo does not give you the descriptor with the private key. It sounds like you are taking the returned descriptor from getdescriptorinfo and importing that directly, when what you actually want is to get the checksum field of getdescriptorinfo's result and construct your descriptor with private key manually. – Ava Chow Oct 02 '22 at 02:36
  • OMG THAT WORKS like a charm tyvm – Mercedes Oct 02 '22 at 11:32
  • @AndrewChow Could you please revisit your answer? wpkh returns "Method not found (code -32601)" – Martin Braun Dec 19 '22 at 22:17
  • @MartinBraun wpkh is not an RPC, it's a function in the output descriptor language, which you'd use in commands such as importdescriptor. – Pieter Wuille Dec 19 '22 at 22:43
  • @PieterWuille Thank you, now it makes more sense, although numerous things still confuse me: Should I pick the checksum in the descriptor key, or in the checksum key when building my descriptor for import? Also, the importdescriptor command requires a timestamp. listtransactions can only work for imported addresses, is there even a way to get my timestamp without checking the blockchain externally? importdescriptor also requires an internal flag, so if my address will be a change or receive address. Since this address has funds, I assume I will make this an internal address? – Martin Braun Dec 19 '22 at 23:21
  • Those questions are too extensive to answer in a comment here; I'd suggest you read the documentation (https://bitcoincore.org/en/doc/24.0.0/rpc/wallet/importdescriptors/ and https://github.com/bitcoin/bitcoin/tree/v24.0.1/ e.g.) more, and open question(s) for things that aren't clear. – Pieter Wuille Dec 20 '22 at 00:27
  • @PieterWuille I get that, I will figure out how I can get the timestamp, but the checksum part seems to be apart of the problem. getdescriptorinfo returns two checksums (one in the descriptor itself, one in checksum). Could you please tell me which one I should pick? – Martin Braun Dec 21 '22 at 19:46
  • 1
    The checksum it reports is the checksum for the descriptor you provided. The descriptor it returns is a full, normalized, descriptor including the checksum (for that normalized descriptor). You can either use your input + checksum, or you can use descriptor. The latter will miss private key information if the former included any. – Pieter Wuille Dec 21 '22 at 19:48
  • @MartinBraun (for the sake of completeness) [on the current master branch] `internal' is an option with a default and the only mandatory data besides the descriptor is the timestamp. – Mercedes Sep 01 '23 at 20:03
  • I read all these comments. Why is the end user (a new/typical bitcoin gui user who has their older private key[s]) expected to do all of this to "simply" import an older key? After studying some more articles, I now have a greater appreciation for why backwards compatibility was broken. But why am I having to hunt around for all these explanations? No compatibility chart on the home page? No import wizard in GUI (or templates)? – Jon Grah Mar 04 '24 at 07:49
  • @JonGrah Importing and exporting has never really been a feature meant for most users - it's never been directly available in the GUI. There is an open PR that would make importing available in the GUI, and make it a lot easier to import descriptors, but it has not garnered any review. – Ava Chow Mar 05 '24 at 01:22
3

To give a bit more context to Andrew Chow's answer: the reason why this isn't any easier is that the concept of "importing a private key" is an insufficient method for describing what a wallet should do, and the wrong way to think about wallets in the first place. While it is possible to convert wallets with keys imported to them to approximately equivalent descriptor wallets, this does not mean that it's the best way when importing things. Descriptors and descriptor wallets allow you to be precise about what you want to import, and the importdescriptors RPC lets you do that.

In more detail, this is roughly the philosophy behind the two types of wallets supported by Bitcoin Core:

  • legacy wallets A legacy wallet is a collection of private keys, and loosely structured additional information such as scripts and addresses. Any output which can be spent or observed with those keys/scripts/addresses is considered to "belong" to the wallet. If you import a new key to a wallet, this immediately results in treating payments to the P2PK, P2PKH, P2WPKH, P2SH-P2WPKH, and possibly more, addresses derived from that key to be watched. This is inefficient, hard to describe and reason about, and just doesn't scale well with new wallet constructions being added (e.g. P2TR), and even harder to deal with when multisig across deviced or participants is added.
  • Descriptor wallets In a descriptor wallet, the outputs considered part of the wallet can be described exactly using a simple "language" which contains all metadata about how the keys are used. If you want just P2PKH addresses derived from a key, you can say that. If you want P2SH-wrapped P2WSH multisig across multiple devices you can say that too, and it will work exactly the same.

In short: the old model of how we thought of wallets wasn't manageable anymore, and "importing a key" only made sense in that kind of thinking. In the new model, you don't import keys, you import a description of exactly what you want the wallet to do.

Murch
  • 75,206
  • 34
  • 186
  • 622
Pieter Wuille
  • 105,497
  • 9
  • 194
  • 308
  • I think I understand all of your point, but I'm still not sure why importprivkey isn't reasonable high level sugar for a larger sequence of commands that import one descriptor wrapping one single isolated key. Isolated keys being wrong doesn't destroy existing isolated keys. I'd suppose it's easier to detect legacy behavior and warn against it by accepting importprivkeys than by having users use the new toolset in a conceptually wrong way, unable to warn of legacy behavior until importdescriptors is seen importing a single isolated key. – Mercedes Nov 14 '22 at 22:47
  • 1
    The question is just: what way of wrapping the private key? P2PKH? P2WPKH? P2SH-P2WPKH? P2SH-P2WSH-P2PKH? P2TR? There are many ways of constructing a scriptPubKey whose spendability depends on access to a given private key. The practice of watching all of them just doesn't scale. Descriptors allow one to be explicit about all of this. – Pieter Wuille Nov 14 '22 at 23:28
  • I take your word for it doesn't scale. Maybe some link can be added explaining further. – Mercedes Nov 15 '22 at 16:39
  • 1
    Also worth a read: https://achow101.com/2020/10/0.21-wallets#:~:text=Benefits%20of%20Descriptor%20Wallets,scripts%20necessary%20to%20sign%20them. – Martin Braun Dec 19 '22 at 22:10
  • @Mercedes You don't need to take my word for it; I gave the explanation above: there isn't just one way of turning a private key into an address. Descriptors add the necessary metadata (whether that means just fixing whether it's P2PKH or P2SH-P2WPKH or whatever, or including other keys if it's multisig, or ...). – Pieter Wuille Dec 19 '22 at 22:38
  • So is there unlimited ways to turning an old private key into an address? There are only so many variations among the Core apps. So can't a locally run script account for these different variations? Or templates offered for the common variations? the minimum assumption is that the user has private key and the matching public address. And has some idea when the original address/wallet was created. – Jon Grah Mar 04 '24 at 07:59
  • @JonGrah In practice, as long as you're only talking about single-key addresses (and not multisig or other more advanced things), there are only a few variations, and for recovery purposes it's easy enough to just try all of them. The problem is that that number increases over time, so for common usage you absolutely don't want to just try all - that's a growing multiplier to the cost every time new address types are introduced. – Pieter Wuille Mar 04 '24 at 11:49