HWI recently added a registerdescriptor command (https://github.com/bitcoin-core/HWI/pull/842) which some hardware wallets require in order to (more safely) support multisig. This registration can then be used with displayaddress and signtx (using the --registration argument, see https://github.com/bitcoin-core/HWI/pull/841 and https://github.com/bitcoin-core/HWI/pull/841). Internally HWI converts the descriptor to a BIP388 policy. The only catch is that we need to provide a multipath descriptor, rather than separate receive and change descriptors. This PR makes that possible.
Our wallet already supported importing multipath descriptors, but upon import they are irreversibly turned into change and receive descriptors. We don't overhaul this fundamental descriptor wallet design. Rather (as per approach 5 from #36075) we store an additional record to hold on to the multipath descriptor strings and point to its matching receive and change descriptor.
Existing wallets are not impacted (and not "upgraded").
The main commits are:
- refactor: introduce descriptor ParseState: the parser first collects keys, generates replacements (not yet normalized, just converting to public keys) and then applies those replacements (in reverse order).
- descriptor: normalize keys in the multipath descriptor string: no record is generated if hardened derivation blocks normalization (those would be useless).
- descriptor: let Parse return the multipath descriptor string
- wallet: store multipath descriptor record on import
These commits make wallet handling consistent and expose the record through RPC:
- wallet: store multipath descriptor record on wallet creation: covers default wallets and
createwalletdescriptor. - wallet: expand multipath descriptors with 'h' hardened marker: we don't preserve the exact imported string anyway, so might as well make it consistent
- wallet: copy multipath record in exportwatchonlywallet: remaps descriptor IDs to the destination wallet.
- rpc: return multipath descriptor from getaddressinfo
- rpc: return multipath descriptor from listdescriptors
The remaining commits provide parser plumbing, tests, and refactors that make the main changes easier to follow and keep the descriptor code maintainable:
- miniscript: let FromString take a string_view
- test: add MULTIPATH flag to descriptor test vectors: tags existing vectors
- refactor: use util::Expected for descriptor parsers: replaces
&errorand introducesParsePubkeyResultand other aliases that are expanded later (with less churn). - Four small helper extractions, kept as separate commits to make each change easier to review:
- refactor: extract LastHardenedIndex helper
- refactor: extract OriginKeyString helper
- refactor: extract MergeNormalizedOrigin helper
- refactor: extract HardenedPrefix helper
- refactor: setup wallet descriptors per output type pair (dropping the loop over
internal)