8000 wallet: `addhdkey` RPC to add just keys to wallets via new `unused(KEY)` descriptor by achow101 · Pull Request #29136 · bitcoin/bitcoin · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

wallet: addhdkey RPC to add just keys to wallets via new unused(KEY) descriptor #29136

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

achow101
Copy link
Member
@achow101 achow101 commented Dec 22, 2023

It is sometimes useful for the wallet to have keys that it can sign with but are not (initially) involved in any scripts, e.g. for setting up a multisig. Ryanofsky suggested A unused(KEY) descriptor which allows for a key to be specified, but produces no scripts. These can be imported into the wallet, and subsequently retrieved with gethdkeys. Additionally, listdescriptors will output these descriptors so that they can be easily backed up.

In order to make it easier for people to add HD keys to their wallet, and to generate a new one if they want to rotate their descriptors, an addhdkey RPC is also added. Without arguments, it will generate a new HD key and add it to the wallet via a unused(KEY) descriptor. If provided a private key, it will construct the descriptor and add it to the wallet.

See also: #26728 (comment)

Based on #29130 as gethdkeys is useful for testing this.

@DrahtBot
Copy link
Contributor
DrahtBot commented Dec 22, 2023

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Code Coverage & Benchmarks

For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/29136.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK Sjors

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #bitcoin-core/gui/872 (Menu action to export a watchonly wallet by achow101)
  • #32784 (wallet: derivehdkey RPC to get xpub at arbitrary path by Sjors)
  • #32652 (wallet: add codex32 argument to addhdkey by roconnor-blockstream)
  • #32489 (wallet: Add exportwatchonlywallet RPC to export a watchonly version of a wallet by achow101)
  • #30243 (descriptors: taproot partial descriptors by Eunovo)
  • #28333 (wallet: Construct ScriptPubKeyMans with all data rather than loaded progressively by achow101)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

@ryanofsky
Copy link
Contributor
ryanofsky commented Dec 23, 2023

This is great, amazed you could implement this so quickly!

I was thinking about the void(KEY) idea more today, and think it would be good to make 3 tweaks:

1 - rename "void(KEY)" to "unused(KEY)"
2 - add logic to importdescriptors and generatewalletdescriptor to delete any unused(KEY) descriptor as soon as the KEY which it references is used by an new descriptor (taking into account both public and private parts of the key)
3 - add logic to importdescriptors to disallow importing an unused(KEY) if KEY is used by another descriptor.

I think these changes would help make these descriptors easier to understand and also enhance backwards compatibility, because IIUC wallets containing unknown descriptor types can't be opened by older software. Also keeping redundant descriptors in the wallet that were only temporarily needed is confusing.

If making these tweaks isn't possible or is not a good idea. I still think probably we should rename void(KEY) to data(KEY) or something like that. I think while "void" makes a certain amount of sense as programming jargon, it's not really a familiar term and doesn't describe the purpose of these descriptors, which is just to hold an inert piece of data, and not be used for generating or matching scriptpubkeys.

I also have a number of ideas to improve the RPC interface for generating keys and descriptors, and will try to post them soon. (EDIT: now posted #29130 (comment)). But this seems like a very good start and very functional.

@achow101
Copy link
Member Author
achow101 commented Jan 4, 2024

1 - rename "void(KEY)" to "unused(KEY)"
3 - add logic to importdescriptors to disallow importing an unused(KEY) if KEY is used by another descriptor.

Done these. I've also added a further restriction that unused() cannot be import to a wallet without private keys. It isn't useful in such wallets so I think it makes sense to disallow their import.

2 - add logic to importdescriptors and generatewalletdescriptor to delete any unused(KEY) descriptor as soon as the KEY which it references is used by an new descriptor (taking into account both public and private parts of the key)

Still working and thinking on this. However we've generally held to the policy of not deleting any records from the wallet since that could result in private keys being accidentally deleted. The only exception to that was adding encryption.

@ryanofsky
Copy link
Contributor

However we've generally held to the policy of not deleting any records from the wallet since that could result in private keys being accidentally deleted. The only exception to that was adding encryption.

Oh, I didn't know that but it makes sense.

One approach you could take would just be to delete the descriptor from memory without actually deleting it from the database, and ignore it the next time the wallet is loaded. But a drawback of this would be that once addhdkey was used the wallet would be forever incompatible with older versions of the software, when one of the benefits of deleting the descriptor was to make wallets backward compatible.

Another approach that might be acceptable could be to add a DatabaseBatch::MarkErased method to call instead of DatabaseBatch::Erase. This could just prepend a serialized string like const std::string TOMBSTONE{"tombstone"} to the key and otherwise leave the record unchanged.

Or maybe just decide in this case that it is ok to delete a record after verifying all the information it contains is present in other records.

Would also want to think about it more, though.

@achow101 achow101 force-pushed the sethdseed-void-descriptor branch from 51d18d6 to d457faf Compare May 19, 2025 17:30
@Sjors
Copy link
Member
Sjors commented May 19, 2025

So I don't think it should be documented as if it were.

Indeed, this is a bit of a hack that happens to work nicely with our wallet design. If other wallets end up using it too, we could reconsider that.

Copy link
Member
@Sjors Sjors left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly happy with d457faf

@achow101 achow101 force-pushed the sethdseed-void-descriptor branch from d457faf to b23cefb Compare May 20, 2025 18:48
@Sjors
Copy link
Member
Sjors commented May 21, 2025

utACK b23cefb

@DrahtBot
Copy link
Contributor

🚧 At least one of the CI tasks failed.
Task previous releases, depends DEBUG: https://github.com/bitcoin/bitcoin/runs/42580005912
LLM reason (✨ experimental): The CI failure is caused by a compilation error in wallet/rpc/wallet.cpp due to an invalid dynamic_cast attempt on a non-pointer type.

Hints

Try to run the tests locally, according to the documentation. However, a CI failure may still
happen due to a number of reasons, for example:

  • Possibly due to a silent merge conflict (the changes in this pull request being
    incompatible with the current code in the target branch). If so, make sure to rebase on the latest
    commit of the target branch.

  • A sanitizer issue, which can only be found by compiling with the sanitizer and running the
    affected test.

  • An intermittent issue.

Leave a comment here, if you need help tracking down a confusing failure.

achow101 added 2 commits May 28, 2025 12:38
unused() descriptors do not have scriptPubKeys. Instead, the wallet uses
them to store keys without having any scripts to watch for.
@achow101 achow101 force-pushed the sethdseed-void-descriptor branch from b23cefb to b4ef67a Compare May 28, 2025 19:43
@achow101
Copy link
Member Author

Rebased for silent merge conflict

@Sjors
Copy link
Member
Sjors commented May 29, 2025

re-utACK b4ef67a

Changes seem to be due to #32475.

@Sjors
Copy link
Member
Sjors commented Jun 2, 2025

There might be a silent conflict with #32514.

Sjors added a commit to Sjors/bitcoin that referenced this pull request Jun 2, 2025
One use case for this is a multisig setup that starts from a blank
wallet with an unused(KEY) descriptor (proposed in bitcoin#29136). A
descriptor is then imported that contains both a key from this
wallet and that of an external signer.
@Sjors
Copy link
Member
Sjors commented Jun 3, 2025

re-ACK 6100108

@Sjors
Copy link
Member
Sjors commented Jun 20, 2025

While working on #32784 I noticed we don't seem to be rotating unused(KEY) descriptors when encrypting the wallet, though I have to double check this...

Another thing I noticed is that createwalletdescriptor is not smart enough to just use the only available unused(KEY) descriptor.

Sjors added a commit to Sjors/bitcoin that referenced this pull request Jun 25, 2025
One use case for this is a multisig setup that starts from a blank
wallet with an unused(KEY) descriptor (proposed in bitcoin#29136). A
descriptor is then imported that contains both a key from this
wallet and that of an external signer.
Sjors added a commit to Sjors/bitcoin that referenced this pull request Jun 25, 2025
One use case for this is a multisig setup that starts from a blank
wallet with an unused(KEY) descriptor (proposed in bitcoin#29136). A
descriptor is then imported that contains both a key from this
wallet and that of an external signer.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants
0