wallet: Store information about created and imported multipath descriptors #36444

pull achow101 wants to merge 18 commits into bitcoin:master from achow101:wallet-store-multipath changing 45 files +1436 −740
  1. achow101 commented at 10:25 PM on October 5, 2026: member

    The wallet does not store any information about multipath descriptors. When a multipath descriptor is imported, it is first expanded, then the expansion descriptors are imported into the wallet.

    This PR adds a field to WalletDescriptor which stores the IDs of the ScriptPubKeyMans of the descriptors that are part of the expansion, in the order that they are expanded. This allows us to use ReconstructMultipath to produce the multipath descriptor when necessary.

    This is useful for working with hardware wallets that require policy registration, particularly those that require resending the descriptor that was registered.

    listdescriptors is updated to include a multipath_descriptor field which contains the multipath descriptor for each individual descriptor if it originally came from a multipath descriptor.

    Lastly, the automatically generated descriptors are now produced and stored as multipath descriptors, so listdescriptors will provide a multipath_descriptor for everything that the wallet automatically generates.

    Note that the IDs stored in the wallet are ScriptPubKeyMan IDs, which we cannot compute from a descriptor as we assume to be opaque blobs since #35445.

    Alternative to #36133, depends on #36442 and #36443

  2. util: Make KeyPathElement class with private members bcb79a3190
  3. util: Change all key path operations to operate with std::vector<KeyPathElement> 4a4101e66a
  4. util: Make KeyPath a subclass of std::vector and not only a typedef
    We will want to have member functions on KeyPath later, so make it a
    class now.
    d76009b966
  5. util: Store hardened character in KeyPathElement 3c432dd787
  6. util: Make KeyPathElement handle multipaths
    Moves all of the multipath parsing into ParseHDKeypath, and let
    KeyPathElement be a multipath element.
    3ef7ee8558
  7. descriptor: Get the multipath expansion from a multipath descriptor 907d103d95
  8. descriptor: Produce multipath descriptor in ParseScript
    Internally, descriptor parsing will produce multipath descriptor objects
    rather than std::vector<DescriptorImpl>. This single multipath
    descriptor becomes std::vector<Descriptor> within Parse. Future commits
    will return the single multipath descriptor object for all descriptor parsing.
    ee1cc66063
  9. DrahtBot added the label Wallet on Oct 5, 2026
  10. DrahtBot commented at 10:25 PM on October 5, 2026: contributor

    <!--e57a25ab6845829454e8d69fc972939a-->

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

    <!--006a51241073e994b41acfe9ec718e94-->

    External sites

    <!--021abf342d371248e50ceaed478a90ca-->

    Reviews

    See the guideline and AI policy for information on the review process.

    Type Reviewers
    Concept ACK w0xlt

    If your review is incorrectly listed, please copy-paste <code>&lt;!--meta-tag:bot-skip--&gt;</code> into the comment that the bot should ignore.

    <!--174a7506f384e20aa4161008e828411d-->

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #36447 <sub><img src="https://drahtbot.space/ack_count/bitcoin/bitcoin/36447.svg"></sub> (test: Misc fixes for pending validation events races by maflcko)
    • #36402 <sub><img src="https://drahtbot.space/ack_count/bitcoin/bitcoin/36402.svg"></sub> (refactor: wallet_importdescriptors to separate run_test() contents into specific functions by nebula-21)
    • #36236 <sub><img src="https://drahtbot.space/ack_count/bitcoin/bitcoin/36236.svg"></sub> (wallet, rpc: add verify_balance option to importdescriptors by musaHaruna)
    • #36122 <sub><img src="https://drahtbot.space/ack_count/bitcoin/bitcoin/36122.svg"></sub> (BIP460: CISA for Taproot key path spends by fjahr)
    • #36070 <sub><img src="https://drahtbot.space/ack_count/bitcoin/bitcoin/36070.svg"></sub> (wallet: Add deriveHDKey interface by PraneethGunas)
    • #35444 <sub><img src="https://drahtbot.space/ack_count/bitcoin/bitcoin/35444.svg"></sub> (wallet: make descriptor SPKM mutex non-recursive by w0xlt)
    • #35377 <sub><img src="https://drahtbot.space/ack_count/bitcoin/bitcoin/35377.svg"></sub> (wallet: Allow importing of descriptors without private keys when the wallet has the private keys by achow101)
    • #35170 <sub><img src="https://drahtbot.space/ack_count/bitcoin/bitcoin/35170.svg"></sub> (test: Better test coverage for ParseHDKeypath() by optout21)
    • #34909 <sub><img src="https://drahtbot.space/ack_count/bitcoin/bitcoin/34909.svg"></sub> (wallet, refactor: modularise wallet by extracting out legacy wallet migration by rkrux)

    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.

    <!--5faf32d7da4f0f540f40219e4f7537a3-->

    LLM Linter (✨ experimental)

    Possible typos and grammar issues:

    • Failed reconstrcted to private string: -> Failed reconstructed to private string: [misspelled word]
    • Get the KeyPath of single KeyPathElements for a give multipath position -> Get the KeyPath of single KeyPathElements for a given multipath position [“give” is the wrong word; intended meaning is unclear until corrected]

    Possible places where named args for integral literals may be used (e.g. func(x, /*named_arg=*/0) in C++, and func(x, named_arg=0) in Python):

    • Parse(descriptor_str, keys, error, true) in src/wallet/walletutil.h
    • Parse(desc_str, keys, error, false) in src/wallet/walletutil.cpp
    • Parse(desc_str, keys, error, false) in src/wallet/test/util.cpp
    • Parse(desc_str.value(), keys, error, false) in src/wallet/test/fuzz/scriptpubkeyman.cpp
    • Parse(desc_str, provider, error, false) in src/wallet/scriptpubkeyman.cpp
    • WalletDescriptor(std::move(desc), 0, 0, 1, 1) in src/qt/test/wallettests.cpp
    • WalletDescriptor(std::move(desc), 0, 0, 10, 0) in src/test/psbt_tests.cpp
    • WalletDescriptor(std::move(desc), 0, 0, 1, 1) in src/wallet/test/util.cpp
    • WalletDescriptor(std::move(desc), 0, 0, 1, 1) in src/wallet/test/wallet_tests.cpp
    • WalletDescriptor(std::move(desc), creation_time, 0, 0, 0) in src/wallet/test/walletload_tests.cpp
    • WalletDescriptor(std::move(desc), creation_time, 0, 0, 0) in src/wallet/wallet.cpp

    <sup>2026-10-05 22:36:14</sup>

  11. descriptor: Return multipath descriptor from Parse
    Instead of a std::vector<std::unique_ptr<Descriptor>>, Parse can return
    a single std::unique_ptr<Descriptor> that contains the multipath
    information if the descriptor was multipath. Callers that
    require the multipath expansion use GetMultipathExpansion() to get the
    expanded descriptors.
    3752814f45
  12. descriptor: Implement multipath reconstruction 2cda830d1b
  13. test: Add multipath reconstruction round trip checks bcbf29aa1d
  14. fuzz: Check multipath descriptor reconstruction cb6ccca033
  15. test: Invalid multipath reconstruction tests 9422834d21
  16. wallet: Store multipath relative spkm ids in WalletDescriptor 39a2443538
  17. wallet, rpc: Store multipath relative ids on import 61543e0f79
  18. wallet, export: Export multipath relative ids for exportwatchonly d5df3a17b8
  19. wallet, export, rpc: also list original multipath descriptors c70397503d
  20. wallet: Store automatically created descriptors as multipath 65fd869daf
  21. test: Check for multipath descriptors in listdescriptors
    Check that the correct multipath descriptor appears in listdescriptors
    for a typical single key wallet. Also check that the correct
    non-multipath descriptors appears, and also do for private
    listdescriptors.
    a49e42b8bb
  22. achow101 force-pushed on Oct 5, 2026
  23. DrahtBot added the label CI failed on Oct 5, 2026
  24. DrahtBot commented at 10:36 PM on October 5, 2026: contributor

    <!--85328a0da195eb286784d51f73fa0af9-->

    🚧 At least one of the CI tasks failed. <sub>Task lint: https://github.com/bitcoin/bitcoin/actions/runs/37382356159/job/112007221776</sub> <sub>LLM reason (✨ experimental): CI failed the Python lint check: wallet_importdescriptors.py has undefined extended_key/xpub names, and wallet_listdescriptors.py has an unused import.</sub>

    <details><summary>Hints</summary>

    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.

    </details>

  25. DrahtBot added the label Needs rebase on Oct 7, 2026
  26. DrahtBot commented at 1:58 PM on October 7, 2026: contributor

    <!--cf906140f33d8803c4a75a2196329ecb-->

    🐙 This pull request conflicts with the target branch and needs rebase.

  27. w0xlt commented at 6:06 PM on October 8, 2026: contributor

    Concept ACK


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: 2026-10-11 10:51 UTC

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