Skip to content

descriptor: expose WalletPolicy template + key_info accessors#2

Closed
bg002h wants to merge 1 commit intoapoelstra:2026-04/followup-895from
bg002h:feat/wallet-policy-template-accessor
Closed

descriptor: expose WalletPolicy template + key_info accessors#2
bg002h wants to merge 1 commit intoapoelstra:2026-04/followup-895from
bg002h:feat/wallet-policy-template-accessor

Conversation

@bg002h
Copy link
Copy Markdown

@bg002h bg002h commented Apr 30, 2026

Summary

Adds two read accessors on WalletPolicy so external consumers can inspect a policy's placeholder structure without consuming it via into_descriptor():

  • pub fn template(&self) -> &Descriptor<KeyExpression>
  • pub fn key_info(&self) -> &[DescriptorPublicKey] (symmetric with the existing set_key_info)

Re-exports KeyExpression and KeyIndex at miniscript::descriptor::* so the new return type is namable from outside the crate, and adds rustdoc to two pre-existing pub items (KeyIndex, KeyExpression::is_disjoint) that are now reachable via the re-exports — required by the crate's missing_docs lint.

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 @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 (but BIP67-disjoint) 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.

Concrete downstream use case: md-codec v0.10's per-@N divergent 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-2 wsh(sortedmulti(...)) template
  • template_accessor_distinguishes_ast_position_from_placeholder_index — the multipath-shared @0 case, asserts [(0,0), (1,0)] (would fail if the implementation returned KeyIndex(ast_pos) instead of the real index)
  • key_info_accessor_yields_concrete_keys_in_ast_order — full concrete-key descriptor populates key_info in AST order

Test plan

  • cargo build clean (no missing_docs warnings)
  • cargo test wallet_policy — 7 tests pass (was 4 + 3 new)
  • cargo +nightly fmt --check clean
  • cargo +nightly clippy --all-targets -- -D warnings clean

🤖 Generated with Claude Code

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.
@bg002h
Copy link
Copy Markdown
Author

bg002h commented Apr 30, 2026

Heads-up: I noticed wallet_policy/ already lives at master in rust-bitcoin/rust-miniscript (issues enabled there too) — happy to close this PR and re-open against rust-bitcoin/rust-miniscript:master if that's the canonical home for downstream-facing API additions. Tell me your preference and I'll move it (or leave it here if you'd rather keep wallet_policy work in your staging fork for now).

Separately filed rust-bitcoin/rust-miniscript#934 — a related concern about set_key_info not enforcing AST-order correspondence, found while consuming the accessors from this PR. Cross-linking for context; not blocking on this PR.

—Brian (via Claude — forgive me, I'm a physician!)

@bg002h
Copy link
Copy Markdown
Author

bg002h commented Apr 30, 2026

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 (bg002h:feat/wallet-policy-template-accessor), same content. Apologies for the spam.

—Brian (via Claude — forgive me, I'm a physician!)

@bg002h bg002h closed this Apr 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant