descriptor: expose WalletPolicy template + key_info accessors#2
descriptor: expose WalletPolicy template + key_info accessors#2bg002h wants to merge 1 commit intoapoelstra:2026-04/followup-895from
Conversation
The internal `template: Descriptor<KeyExpression>` and `key_info: Vec<DescriptorPublicKey>` fields are needed by external consumers that need to inspect a `WalletPolicy`'s placeholder structure without consuming the policy via `into_descriptor()`. The motivating case is multipath-shared `@N` placeholders — e.g. `sh(multi(1,@0/**,@0/<2;3>/*))`, where the same placeholder appears at two distinct AST positions. After `into_descriptor()` the placeholder labels are erased and the materialized descriptor surfaces two separate keys; the template preserves placeholder identity via the public `KeyExpression::index` field. Encoders and other consumers that need the `(AST position) -> (placeholder index)` mapping have no public way to obtain it today. Adds two read accessors on `WalletPolicy`: - `pub fn template(&self) -> &Descriptor<KeyExpression>` — yields the template; iterating gives `(AST position, KeyExpression)` pairs and the placeholder index is `ke.index.0`. - `pub fn key_info(&self) -> &[DescriptorPublicKey]` — yields the key-information vector (symmetric with the existing `set_key_info(&mut self, ...)`). Re-exports `KeyExpression` and `KeyIndex` at `miniscript::descriptor::*` so the new return type is namable from outside the crate, and adds rustdoc to `KeyIndex` and `KeyExpression::is_disjoint` (now reachable via the re-exports; required by the crate's missing_docs lint). Internal `validate()` already uses `self.template.iter_pk()` the same way external callers will; this commit just makes the access pattern available on the public API.
|
Heads-up: I noticed Separately filed rust-bitcoin/rust-miniscript#934 — a related concern about —Brian (via Claude — forgive me, I'm a physician!) |
|
Closing in favor of rust-bitcoin/rust-miniscript#936 — re-routed to true upstream alongside #1 (now rust-bitcoin/rust-miniscript#935). Same branch head ( —Brian (via Claude — forgive me, I'm a physician!) |
Summary
Adds two read accessors on
WalletPolicyso external consumers can inspect a policy's placeholder structure without consuming it viainto_descriptor():pub fn template(&self) -> &Descriptor<KeyExpression>pub fn key_info(&self) -> &[DescriptorPublicKey](symmetric with the existingset_key_info)Re-exports
KeyExpressionandKeyIndexatminiscript::descriptor::*so the new return type is namable from outside the crate, and adds rustdoc to two pre-existingpubitems (KeyIndex,KeyExpression::is_disjoint) that are now reachable via the re-exports — required by the crate'smissing_docslint.Motivation
The internal fields are already accessed by
WalletPolicy::validate()for exactly the iteration pattern external code needs; this commit just exposes the same access for downstream consumers.The motivating case is multipath-shared
@Nplaceholders — e.g.sh(multi(1,@0/**,@0/<2;3>/*)), where the same placeholder appears at two distinct AST positions. Afterinto_descriptor()the placeholder labels are erased and the materialized descriptor surfaces two separate (but BIP67-disjoint) keys; the template preserves placeholder identity via the publicKeyExpression::indexfield. Encoders and other consumers that need the(AST position) -> (placeholder index)mapping have no public way to obtain it today.Concrete downstream use case: md-codec v0.10's per-
@Ndivergent origin path encoding (BIP draft) — currently has the placeholder-walk stubbed because there's no public accessor.Tests
Three unit tests in
mod tests:template_accessor_yields_keyexpressions_with_placeholder_indices— basic round trip on a 2-of-2wsh(sortedmulti(...))templatetemplate_accessor_distinguishes_ast_position_from_placeholder_index— the multipath-shared@0case, asserts[(0,0), (1,0)](would fail if the implementation returnedKeyIndex(ast_pos)instead of the real index)key_info_accessor_yields_concrete_keys_in_ast_order— full concrete-key descriptor populateskey_infoin AST orderTest plan
cargo buildclean (nomissing_docswarnings)cargo test wallet_policy— 7 tests pass (was 4 + 3 new)cargo +nightly fmt --checkcleancargo +nightly clippy --all-targets -- -D warningsclean🤖 Generated with Claude Code