#36443 descriptor: Reconstruct a multipath descriptor from its expansion

ReviewabilityReviewsAgreementSize
#36443 achow101 descriptor: Reconstruct a multipath descriptor from its expansionWaiting on author0Blocked900 + 331 tests
  • Recover a unified multipath descriptor from its expanded single-path sibling descriptors
  • Allow reversing descriptor expansion back into the original combined multipath form
  • Review #36442 first
  • Ready to review now because the rebase conflict against master is mechanical
  • Unaddressed objection: accepts repeated path values instead of failing (fametrano)
  • Verified by testing against btclib-wallet that repeated values yield invalid output (fametrano)
  • Concept approval without stated reasons (w0xlt)
  • 900 lines added or modified outside tests
  • 331 lines added or modified in tests
  • 709 lines removed in total
  • 39 files, 12 commits
Wallet#32 of 92
P2 · new feature
  • P2 after comparing with the other PRs here (assessed alone as P3): Raised from P3: it is the prerequisite for P2 #36444 and cannot sit below it.
  • P3 because it provides a helper to reconstruct unified multipath descriptors from their expansions
  • Offers clear utility for descriptor workflows without addressing critical blockers or fund-safety risks
Reviewability: ReadyStalePaused
Agreement: StrongPositivePositive w/ caveatsNeutralMildDisputedBlockedCrickets
Reviews: current code-review ACKs, then (+stale ACKs) and -NACKs; greener = more ACKs.
Size: lines added or modified outside tests, then in tests; greener = smaller.

Goal

Adds a `ReconstructMultipath` method to the `Descriptor` interface, allowing a unified multipath descriptor object to be reconstructed from its expanded single-path sibling descriptors. The reconstruction inspects the derivation paths of the sibling descriptors and reconstitutes the combined multipath expression. Unit tests, negative invalid-path tests, and fuzzing harness updates are added to verify round-tripping.

Problem: Multipath descriptors expand into individual single-path descriptors, but there is no mechanism to invert this expansion and recover the original unified multipath descriptor from those expanded instances.

Evidence of importance:

  • PR description: 'This expansion is reversible - given the expansion in the order of the multipath elements, it is possible to determine what the original multipath descriptor was. This PR adds ReconstructMultipath to produce a new Descriptor representing the multipath descriptor that produced the expansion.'

Scope: This PR is stacked on PR #36442 and includes its commits; only the final four commits implement and test the multipath reconstruction functionality.

Risk of a mistake: medium (wallet, rpc_api, tests). A flaw in multipath reconstruction could produce incorrect derivation paths or invalid descriptors, potentially causing wallet derivation errors or script pubkey mismatches.

Changed since the previous assessment: Rebased onto the latest commit stack of #36442, which introduces `KeyPathElement` and changes `Parse` to return a single `std::unique_ptr<Descriptor>`; the reconstruction logic itself remains the same.

Reviewability

Paused · Waiting on author

Open objections from fametrano with no author reply for 9 days; review would repeat what has been asked.

Waiting on (from the thread read):

  • author: addressing repeated-value bug in ReconstructMultipath 2026-10-23

Agreement

Blocked

objection that needs a change, nobody has replied (fametrano)

Thread notes: fametrano tested reconstruction against btclib-wallet and reported that repeated path values are not rejected, providing inline code suggestions and unit tests to fix the issue.

Objections, suggestions, and questions:

ReviewerTypeHarm, request, or questionStatusMust beAuthor repliedQuote
fametrano (contributor)objection, correctnessReconstructMultipath accepts relatives that repeat a path value, returning an invalid descriptor or an incorrect number of elements instead of failingopenchangedno2026-10-23: 'ReconstructMultipath accepts relatives that repeat a path value, and then returns a descriptor that is not their multipath form.'
fametrano (contributor)suggestion, correctnessReject a relative whose index is already in the stepopenansweredno2026-10-23: 'A relative whose index is already in the step is a repeat, so reject it.'
fametrano (contributor)suggestion, correctnessReject a relative whose index is already in the step for the musig pathopenansweredno2026-10-23: 'Same check for the musig path.'
fametrano (contributor)suggestion, correctnessFail reconstruction unless the step ends with one index per input, rejecting identical inputsopenansweredno2026-10-23: 'Fail unless the step ends with one index per input. This also rejects identical inputs.'
fametrano (contributor)suggestion, correctnessAdd test cases covering repeated-value inputs, with and without musigopenansweredno2026-10-23: 'Add the repeated-value cases, with and without musig.'

Support:

  • w0xlt (contributor) (Concept ACK, 2026-10-08): no reason given [not substantive]

Participants:

  • w0xlt (contributor, support, 1 statements 2026-10-08 to 2026-10-08): Concept ACK
  • fametrano (contributor, objection, 5 statements 2026-10-23 to 2026-10-23): found that ReconstructMultipath accepts repeated path values and returns invalid descriptors

Dependencies

Depends on: #36442

Files

490 lines under test/bench/ci.

  • src/script/descriptor.cpp +517/-408
  • src/test/descriptor_tests.cpp +194/-37
  • src/util/bip32.cpp +149/-27
  • src/test/bip32_tests.cpp +77/-79
  • src/util/bip32.h +113/-10
  • src/rpc/output_script.cpp +14/-19
  • src/wallet/wallet.cpp +18/-14
  • src/wallet/imports.cpp +13/-12
  • src/wallet/scriptpubkeyman.cpp +13/-11
  • src/test/fuzz/descriptor_parse.cpp +20/-2
  • src/wallet/walletdb.cpp +12/-10
  • src/rpc/util.cpp +8/-8
  • src/test/fuzz/parse_hd_keypath.cpp +8/-6
  • src/script/descriptor.h +9/-1
  • src/test/psbt_tests.cpp +5/-5
  • src/wallet/test/util.cpp +4/-6
  • src/wallet/rpc/spend.cpp +4/-4
  • src/wallet/test/fuzz/scriptpubkeyman.cpp +4/-4
  • src/wallet/test/walletload_tests.cpp +5/-3
  • src/wallet/walletutil.h +4/-4
  • src/key.cpp +4/-3
  • src/qt/test/wallettests.cpp +3/-4
  • src/rpc/mining.cpp +3/-4
  • src/wallet/export.cpp +4/-3
  • src/script/sign.cpp +3/-3
  • src/wallet/test/psbt_wallet_tests.cpp +2/-3
  • src/bench/descriptors.cpp +2/-2
  • src/bench/wallet_encrypt.cpp +2/-2
  • src/bench/wallet_ismine.cpp +2/-2
  • src/wallet/rpc/wallet.cpp +2/-2
  • src/wallet/walletutil.cpp +2/-2
  • src/key.h +2/-1
  • src/rpc/util.h +2/-1
  • src/script/keyorigin.h +2/-1
  • src/wallet/test/wallet_tests.cpp +1/-2
  • src/psbt.h +1/-1
  • src/test/fuzz/util/wallet.h +1/-1
  • src/wallet/rpc/addresses.cpp +1/-1
  • test/functional/rpc_getdescriptorinfo.py +1/-1

Card

The short text the ranking pass compares across PRs.

Adds ReconstructMultipath to reconstruct a single multipath descriptor object from an ordered set of its expanded single-path descriptors. This makes descriptor expansion reversible in memory for descriptor-aware tooling and wallet flows. The change is backed by unit tests, invalid-input tests, and fuzz testing. Depends on #36442.

Data

dossier JSON · extract JSON · model ledger, generated 2026-10-24T01:18, confidence high, input hash 0b9471c9bd5aec33
thread read through 2026-10-23 (6 statements), head 62e70eb6f0
labels: Needs rebase, Descriptors, CI failed · draft

Categories

Wallet

P2 · new feature (assessed alone as P3; Raised from P3: it is the prerequisite for P2 #36444 and cannot sit below it.)

Adds `ReconstructMultipath` to allow recovering a unified multipath descriptor from its expanded single-path descriptors. While an inversion mechanism provides clear utility for descriptor handling, there is no stated fund-safety risk or critical blocker making it urgent.

Membership: Directly implements descriptor logic under the 'Descriptors' label, modifying src/script/descriptor.cpp and wallet descriptor management.

Factors: security/stability 0, bug 0, performance 0, user value 1, leverage 1