<p>To give a bit more context to Ava Chow's answer: the reason why this isn't any easier is that the concept of &quot;importing a private key&quot; 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 <code>importdescriptors</code> RPC lets you do that.</p>
<p>In more detail, this is roughly the philosophy behind the two types of wallets supported by Bitcoin Core:</p>
<ul>
<li><strong>Legacy wallets</strong>: 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 &quot;belong&quot; 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 devices or participants is added.</li>
<li><strong>Descriptor wallets</strong>. In a descriptor wallet, the outputs considered part of the wallet can be described exactly using a simple &quot;language&quot; 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.</li>
</ul>
<p>In short: the old model of how we thought of wallets wasn't manageable anymore, and &quot;importing a key&quot; 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.</p>

CC BY-SA 4.0
2022-11-14T22:47:52.367Z
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
CC BY-SA 4.0
2022-11-14T23:28:31.193Z
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
CC BY-SA 4.0
2022-11-15T16:39:19.017Z
I take your word for it doesn't scale. Maybe some link can be added explaining further.
Mercedes
CC BY-SA 4.0
2022-12-19T22:10:08.720Z
Also worth a read: https://achow101.com/2020/10/0.21-wallets#:~:text=Benefits%20of%20Descriptor%20Wallets,scripts%20necessary%20to%20sign%20them.
Martin Braun
CC BY-SA 4.0
2022-12-19T22:38:22.167Z
@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
CC BY-SA 4.0
2024-03-04T07:59:34.007Z
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
CC BY-SA 4.0
2024-03-04T11:49:46.770Z
@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
CC BY-SA 4.0
2022-05-23T17:57:22.390Z
2024-05-09T14:07:03.310Z
2024-05-09T14:07:03.310Z
Pieter Wuille
<p>Here are example commands which can be used with <code>bitcoin-cli</code> to import wallet using WIF into new wallet:</p>
<pre><code>wallet_name=mywallet
wif=5MyWifPass
bitcoin-cli -named createwallet wallet_name=${wallet_name} descriptors=true disable_private_keys=true
bitcoin-cli getdescriptorinfo &quot;pkh(${wif})&quot; # Legacy: pkh(), for P2SH-SegWit use sh(wpkh())
descriptor=$(bitcoin-cli getdescriptorinfo &quot;pkh(${wif})&quot; | jq -r .descriptor)
bitcoin-cli -rpcwallet=${wallet_name} importdescriptors '[{ &quot;desc&quot;: &quot;'${descriptor}'&quot;, &quot;internal&quot;: false, &quot;timestamp&quot;:&quot;now&quot;, &quot;label&quot;: &quot;'${wallet_name}'&quot;}]'
bitcoin-cli -rpcwallet=${wallet_name} listdescriptors
bitcoin-cli -rpcwallet=${wallet_name} listreceivedbyaddress 1 true
</code></pre>
<p>The above needs to be run in shell.</p>
<p>It assumes you've <code>rpcuser</code> and <code>rpcpassword</code> setup in your <code>~/.bitcoin/bitcoin.conf</code>, otherwise add <code>-rpcuser=youruser -rpcpassword=yourpass</code> to the command.</p>
<p>Related:</p>
<ul>
<li><a href="https://bitcointalk.org/index.php?topic=5498210.0" rel="nofollow noreferrer">[HOW-TO] Import privkeys into a Bitcoin Core descriptor wallet</a></li>
</ul>

CC BY-SA 4.0
2024-11-19T04:26:23.173Z
The how-to link worked great! https://bitcointalk.org/index.php?topic=5498210.0
user1652110
CC BY-SA 4.0
2024-09-12T22:26:49.923Z
2024-09-12T22:47:56.003Z
2024-09-12T22:47:56.003Z
kenorb
<p>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.</p>
<p>As the error message says, descriptor wallets do not support <code>importprivkey</code>. You will have to use <code>importdescriptors</code> with the private key you want to import as part of the proper descriptor.</p>
<p>For example, if you wanted to import the P2WPKH address for a private key, you would make a descriptor of the form <code>wpkh(&lt;privkey&gt;)</code>. Then use <code>getdescriptorinfo</code> to get the checksum and create <code>wpkh(&lt;privkey&gt;)#&lt;checksum&gt;</code>, and then use <code>importdescriptors</code> to import that descriptor.</p>

CC BY-SA 4.0
2022-10-01T13:21:17.807Z
getdescriptorinfo would just tell wpkh(): Uncompressed keys are not allowed
Mercedes
CC BY-SA 4.0
2022-10-01T13:57:17.310Z
You shouldn't use uncompressed keys in wpkh; they aren't valid.
Pieter Wuille
CC BY-SA 4.0
2022-10-01T14:17:18.033Z
@PieterWuille understood, thanks, now I got checksum.
Mercedes
CC BY-SA 4.0
2022-10-01T14:44:43.423Z
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
CC BY-SA 4.0
2022-10-02T02:36:39.363Z
@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
CC BY-SA 4.0
2022-10-02T11:32:43.080Z
OMG THAT WORKS like a charm tyvm
Mercedes
CC BY-SA 4.0
2022-12-19T22:17:50.470Z
@AndrewChow Could you please revisit your answer? `wpkh` returns "Method not found (code -32601)"
Martin Braun
CC BY-SA 4.0
2022-12-19T22:43:38.470Z
@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
CC BY-SA 4.0
2022-12-19T23:21:45.183Z
@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
CC BY-SA 4.0
2022-12-20T00:27:42.407Z
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
CC BY-SA 4.0
2022-12-21T19:46:13.450Z
@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
CC BY-SA 4.0
2022-12-21T19:48:42.667Z
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
CC BY-SA 4.0
2023-09-01T20:03:41.787Z
@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
CC BY-SA 4.0
2024-03-04T07:49:46.827Z
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](https://achow101.com/2020/10/0.21-wallets#:~:text=Benefits%20of%20Descriptor%20Wallets,scripts%20necessary%20to%20sign%20them), 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
CC BY-SA 4.0
2024-03-05T01:22:06.727Z
@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](https://github.com/bitcoin-core/gui/pull/650) 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
CC BY-SA 4.0
2022-05-23T16:26:55.770Z
2023-09-05T17:46:13.583Z
2023-09-05T17:46:13.583Z
Ava Chow
<p>If <a href="https://github.com/bitcoin/bitcoin/pull/19602#issuecomment-928369685" rel="noreferrer">the Bitcoin Core Wallet Migration Tool is supposed to be creating descriptors for sparse keys (as it's supposed to support pre deterministic wallets)</a>, then why doesn't Bitcoin Core allow using <code>importprivkey</code> on a descriptor wallet?</p>
<p>I'm trying to, and it's telling error code -4 (type of wallet does not support this command).</p>
<p>How can I import a private key to a descriptor wallet?</p>

<bitcoin-core><wallet><private-key-import><output-script-descriptors>
How can a private key be imported to a descriptor wallet?
Mercedes
# How can a private key be imported to a descriptor wallet?

(Asked by: username_0 on 2022-05-23)

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)](https://github.com/bitcoin/bitcoin/pull/19602#issuecomment-928369685), 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?

**Tags:** bitcoin-core, wallet, private-key-import, output-script-descriptors


---

## 3 answers

### Answer by username_6 on 2022-05-23. Score: 10 | Accepted answer

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.

> **Comments:**
> **username_0**: getdescriptorinfo would just tell wpkh(): Uncompressed keys are not allowed  
> **username_1**: You shouldn't use uncompressed keys in wpkh; they aren't valid.  
> **username_0**: @username_1 understood, thanks, now I got checksum.  
> **username_0**: 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  
> **username_6**: @username_0 `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.  
> **username_0**: OMG THAT WORKS like a charm tyvm  
> **username_2**: @AndrewChow Could you please revisit your answer? `wpkh` returns "Method not found (code -32601)"  
> **username_1**: @username_2 `wpkh` is not an RPC, it's a function in the output descriptor language, which you'd use in commands such as `importdescriptor`.  
> **username_2**: @username_1 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?  
> **username_1**: 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.  
> **username_2**: @username_1 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?  
> **username_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.  
> **username_0**: @username_2 (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.  
> **username_3**: 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](https://achow101.com/2020/10/0.21-wallets#:~:text=Benefits%20of%20Descriptor%20Wallets,scripts%20necessary%20to%20sign%20them), 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)?  
> **username_6**: @username_3 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](https://github.com/bitcoin-core/gui/pull/650) that would make importing available in the GUI, and make it a lot easier to import descriptors, but it has not garnered any review.  

---

### Answer by username_1 on 2022-05-23. Score: 5

To give a bit more context to username_6'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 devices 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.

> **Comments:**
> **username_0**: 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.  
> **username_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.  
> **username_0**: I take your word for it doesn't scale. Maybe some link can be added explaining further.  
> **username_2**: Also worth a read: https://achow101.com/2020/10/0.21-wallets#:~:text=Benefits%20of%20Descriptor%20Wallets,scripts%20necessary%20to%20sign%20them.  
> **username_1**: @username_0 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 ...).  
> **username_3**: 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.  
> **username_1**: @username_3 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.  

---

### Answer by username_4 on 2024-09-12. Score: -1

Here are example commands which can be used with `bitcoin-cli` to import wallet using WIF into new wallet:

```
wallet_name=mywallet
wif=5MyWifPass
bitcoin-cli -named createwallet wallet_name=${wallet_name} descriptors=true disable_private_keys=true
bitcoin-cli getdescriptorinfo "pkh(${wif})" # Legacy: pkh(), for P2SH-SegWit use sh(wpkh())
descriptor=$(bitcoin-cli getdescriptorinfo "pkh(${wif})" | jq -r .descriptor)
bitcoin-cli -rpcwallet=${wallet_name} importdescriptors '[{ "desc": "'${descriptor}'", "internal": false, "timestamp":"now", "label": "'${wallet_name}'"}]'
bitcoin-cli -rpcwallet=${wallet_name} listdescriptors
bitcoin-cli -rpcwallet=${wallet_name} listreceivedbyaddress 1 true

```

The above needs to be run in shell.

It assumes you've `rpcuser` and `rpcpassword` setup in your `~/.bitcoin/bitcoin.conf`, otherwise add `-rpcuser=youruser -rpcpassword=yourpass` to the command.

Related:

* [[HOW-TO] Import privkeys into a Bitcoin Core descriptor wallet](https://bitcointalk.org/index.php?topic=5498210.0)

> **Comments:**
> **username_5**: The how-to link worked great! https://bitcointalk.org/index.php?topic=5498210.0  

---

