Have createwalletdescriptor auto-detect an unused(KEY) #32861

pull Sjors wants to merge 8 commits into bitcoin:master from Sjors:2025/07/smart-createwalletdescriptor changing 12 files +389 −6
  1. Sjors commented at 8:55 am on July 3, 2025: member

    The createwalletdescriptor was introduced in #29130 to let users add a tr() descriptor to an existing SegWit wallet. The new addhdkey method from #29136 introduces a new potential workflow: start from a blank wallet, generate an HD and then add only the descriptors you need, e.g.:

    0bitcoin rpc createwallet TaprootMaxi blank=true
    1bitcoin rpc addhdkey
    2bitcoin rpc createwalletdescriptor bech32m
    

    Before this PR the last line would fail, requiring the user to call gethdkeys and copy-paste the xpub.

    This PR makes createwalletdescriptor a bit smarter so it just finds the unused(KEY) generated by hdkey and uses that.

    If multiple unused(KEY) descriptors are present the user still has to pick one.

    A potential followup is to make our multisig tutorial slightly safer to use. Rather than creating a full wallet, the instruction could be changed to start with a blank wallet and only generate the default legacy descriptor. This avoids accidental use of single sig p2sh-segwit and bech32 addresses.

    (I might do that in #32784, which introduces some other improvements to the tutorial. The situation is still not great; ideally the importdescriptors RPC is enhanced to detect when an xpub is a child of an hd key and then treats it as if an xpriv was imported.)

    Builds on #29136.

  2. DrahtBot commented at 8:55 am on July 3, 2025: contributor

    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/32861.

    Reviews

    See the guideline for information on the review process. A summary of reviews will appear here.

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #33008 (wallet: support bip388 policy with external signer 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.

  3. rkrux commented at 9:38 am on July 18, 2025: contributor

    This PR makes createwalletdescriptor a bit smarter so it just finds the unused(KEY) generated by hdkey and uses that.

    Once the unused(KEY) descriptor is used, doesn’t it become used? Keeping it as an unused descriptor later might come across as confusing.

  4. in src/wallet/rpc/wallet.cpp:912 in e8b1440aa3 outdated
    908+                    }
    909+
    910+                    if (wallet_xpubs.empty()) {
    911+                        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No HD key found. Please generate one with 'addhdkey' or import an active descriptor.");
    912+                    } else if (wallet_xpubs.size() > 1) {
    913+                        throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to determine which HD key to use. Please specify with 'hdkey'");
    


    rkrux commented at 9:44 am on July 18, 2025:

    In e8b1440aa3ed7efe3a9c2d10afb05e7247fff1f6 “rpc: make createwalletdescriptor smarter”

    Though this check here seems more thorough, but the presence of more than one spkm also seems sufficient to throw this error?

  5. Sjors commented at 9:50 am on July 18, 2025: member

    Keeping it as an unused descriptor later might come across as confusing.

    I agree, and it was also brought up here: #29136 (comment). It’s orthogonal to this PR.

  6. rkrux commented at 10:19 am on July 18, 2025: contributor

    Thanks, I recall reading this comment earlier but forgot about it later. If I am not missing anything, I don’t suppose this point is orthogonal to this PR?

    The linked comment also states that any RPC using the unused descriptor should delete it immediately afterwords.

    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)

    I don’t believe generatewalletdescriptor is present as an RPC in the codebase, and createwalletdescriptor stores the newly created descriptor in the database as I verified from the below patch. I can see the unused descriptor present after this RPC as well. Shouldn’t we add the deletion of the unused descriptor in this PR itself?

     0diff --git a/test/functional/wallet_createwalletdescriptor.py b/test/functional/wallet_createwalletdescriptor.py
     1index 6de0ca4782..a7086c5b9e 100755
     2--- a/test/functional/wallet_createwalletdescriptor.py
     3+++ b/test/functional/wallet_createwalletdescriptor.py
     4@@ -125,7 +125,11 @@ class WalletCreateDescriptorTest(BitcoinTestFramework):
     5 
     6         # Create unused(KEY) descriptor and try again
     7         w1.addhdkey()
     8+        print("listdescriptors: ", w1.listdescriptors())
     9+        print("gethdkeys: ", w1.gethdkeys())
    10         w1.createwalletdescriptor(type="bech32")
    11+        print("listdescriptors: ", w1.listdescriptors())
    12+        print("gethdkeys: ", w1.gethdkeys())
    13 
    14         self.nodes[0].createwallet("w2", blank=True)
    15         w2 = self.nodes[0].get_wallet_rpc("w2")
    16(END)
    
  7. descriptor: Add unused(KEY) descriptor
    unused() descriptors do not have scriptPubKeys. Instead, the wallet uses
    them to store keys without having any scripts to watch for.
    18062990b8
  8. test: Simple test for importing unused(KEY) fb2eb22905
  9. wallet: Add addhdkey RPC 71b4900f85
  10. wallet, rpc: Disallow import of unused() if key already exists 3aff52c989
  11. wallet, rpc: Disallow importing unused() to wallets without privkeys 844dd63a7d
  12. doc: Release note for addhdkey 439734e0ef
  13. wallet: add GetScriptlessSPKMs() helper
    A helper method to obtain all unused(key) descriptor SPKMs.
    293c7e9475
  14. rpc: make createwalletdescriptor smarter
    When a wallet contains only an unused(KEY) descriptor, use it. Previously the user would have to call listdescriptors and manually specify it.
    604955304d
  15. Sjors force-pushed on Jul 25, 2025
  16. Sjors commented at 7:12 am on July 25, 2025: member

    The linked comment also states that any RPC using the unused descriptor should delete it immediately afterwords.

    That should be done in #29136 which introduces createwalletdescriptor , or in a followup, but not in this PR (which consists of only the last two commits).


github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2025-08-02 03:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me