descriptor: Represent multipath descriptors in a single Descriptor object #36442

pull achow101 wants to merge 8 commits into bitcoin:master from achow101:multipath-first-class changing 39 files +805 −712
  1. achow101 commented at 10:14 PM on October 5, 2026: member

    Currently, multipath descriptor strings treat the multipath specifiers as syntactic sugar. Once we finish parsing such a string, the multipath elements are lost as the result is the multiple distinct descriptors that the multipath is a shorthand for. This means that we are unable to return the multipath string for such descriptors.

    This PR allows descriptors to actually be multipath where the multipath specifiers are represented inside of the paths of BIP32PubkeyProviders. This allows for the multipath string to be produced.

    In order to achieve this, a lot of derivation path parsing is refactored. KeyPath is promoted to being its own class (which subclasses std::vector), while the individual derivation indexes become a KeyPathElement object so that the multipath elements can be represented. Furthermore, hardened derivation characters are now being stored within KeyPathElements so each element knows what the original hardened character was. This also cleans up a little bit of the descriptor implementation as we no longer have to carry around bool apostrophe in a bunch of places.

    Lastly, Parse now returns a single Descriptor object, rather than a vector of them. If the descriptor is a multipath descriptor, the expansion can be retrieved using GetMultipathExpansion(). The wallet uses this and still must not store multipath descriptors.

  2. DrahtBot added the label Descriptors on Oct 5, 2026
  3. DrahtBot commented at 10:15 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:

    • #36443 <sub><img src="https://drahtbot.space/ack_count/bitcoin/bitcoin/36443.svg"></sub> (descriptor: Reconstruct a multipath descriptor from its expansion by achow101)
    • #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)
    • #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:

    • src/util/bip32.h: give → given [misspelling]

    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):

    • src/qt/test/wallettests.cpp: WalletDescriptor(std::move(desc), 0, 0, 1, 1)
    • src/rpc/mining.cpp: Parse(descriptor, key_provider, error, false)
    • src/rpc/output_script.cpp: Parse(desc_str, key_provider, error, true)
    • src/script/descriptor.cpp: std::make_unique<ConstPubkeyProvider>(key_exp_index, pubkey, false)
    • src/script/descriptor.cpp: std::make_unique<ConstPubkeyProvider>(key_exp_index, pubkey, true)
    • src/script/descriptor.cpp: std::make_unique<ConstPubkeyProvider>(0, pubkey, false)
    • src/script/descriptor.cpp: std::make_unique<ConstPubkeyProvider>(0, pubkey, true)
    • src/wallet/scriptpubkeyman.cpp: WalletDescriptor(std::move(desc), creation_time, 0, 0, 0)
    • src/wallet/scriptpubkeyman.cpp: WalletDescriptor(std::move(desc), 0, 0, chain_counter, 0)
    • src/wallet/test/wallet_tests.cpp: WalletDescriptor{std::move(desc), 0, 0, 0, 0}
    • src/wallet/wallet.cpp: WalletDescriptor(std::move(desc), creation_time, 0, 0, 0)
    • src/wallet/wallet.cpp: Parse(desc_str, keys, desc_error, false)
    • src/wallet/walletutil.cpp: WalletDescriptor(std::move(desc), creation_time, 0, 0, 0)
    • src/wallet/test/util.cpp: Parse(desc_str, keys, error, false)

    <sup>2026-10-07 21:48:25</sup>

  4. achow101 force-pushed on Oct 5, 2026
  5. DrahtBot added the label CI failed on Oct 5, 2026
  6. DrahtBot commented at 10:35 PM on October 5, 2026: contributor

    <!--85328a0da195eb286784d51f73fa0af9-->

    🚧 At least one of the CI tasks failed. <sub>Task ASan + LSan + UBSan + integer: https://github.com/bitcoin/bitcoin/actions/runs/37381217840/job/112003412636</sub> <sub>LLM reason (✨ experimental): The build failed because walletload_tests.cpp assigns Parse()’s std::unique_ptr<Descriptor> result to a vector of unique pointers.</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>

  7. DrahtBot added the label Needs rebase on Oct 7, 2026
  8. util: Make KeyPathElement class with private members 2ff1009dbe
  9. achow101 force-pushed on Oct 7, 2026
  10. DrahtBot removed the label Needs rebase on Oct 7, 2026
  11. util: Change all key path operations to operate with std::vector<KeyPathElement> 9f3a86f4b1
  12. 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.
    7851cfa16c
  13. util: Store hardened character in KeyPathElement e29d6efd19
  14. util: Make KeyPathElement handle multipaths
    Moves all of the multipath parsing into ParseHDKeypath, and let
    KeyPathElement be a multipath element.
    b84641e4c6
  15. descriptor: Get the multipath expansion from a multipath descriptor 177874ccd3
  16. 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.
    d689425c0c
  17. 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.
    74915efa6e
  18. achow101 force-pushed on Oct 7, 2026
  19. w0xlt commented at 6:05 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 11:51 UTC

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