Skip to content
This repository was archived by the owner on Oct 22, 2026. It is now read-only.

Check descriptor constructor input against the script context - #1069

Open
starius wants to merge 3 commits into
rust-bitcoin:masterfrom
starius:constructor-context-checks
Open

starius wants to merge 3 commits into
rust-bitcoin:masterfrom
starius:constructor-context-checks

Conversation

@starius

@starius starius commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Before: the descriptor constructors skipped checks that parsing runs. Wsh::new, Sh::new and Bare::new checked only the top level, Tr::new checked only the internal key, and new_sortedmulti checked nothing since #915. So new_wsh_sortedmulti accepted an x-only key, and new_sh_sortedmulti accepted 16 keys, a 547-byte redeem script that made address() panic.

After: these constructors run the per-node checks that parsing runs.

Split out of #954. #991 doesn't cover new_sortedmulti or the Tr::new leaves yet.

🤖 AI disclosure: the patch was generated with Claude Opus 5.5 and reviewed with Codex.

Miniscript::from_tree checks the global rules of the script context on
every node. Move that loop into check_global_validity_recursive, so
that the descriptor constructors can run the same checks. No behavior
change.

Assisted-by: Claude Opus 5.5
Parsing a descriptor checks the rules of the script context on every
node of its Miniscript: which keys the context allows and its script
size limits. Wsh::new, Sh::new and Bare::new run only the top-level
checks, Tr::new checks only the internal key, and new_sortedmulti runs
no checks at all since SortedMultiVec was removed in rust-bitcoin#915. Constructors
like Miniscript::multi and Miniscript::pk don't check the context
either, and Miniscript::from_ast checks only the node it builds. So the
descriptor constructors accept Miniscript that parsing rejects.

For example, Descriptor::new_wsh_sortedmulti accepts an x-only key. The
result doesn't parse back from its Display, and into_definite panics
when the translation checks the context again.
Descriptor::new_sh_sortedmulti accepts 16 keys, which make a 547-byte
redeem script, and address() then panics on its assertion that the size
was checked. Descriptor::new_tr accepts a leaf with an uncompressed
key, and into_definite panics on it too.

Call check_global_validity_recursive in Wsh::new, Sh::new and
Bare::new, and on every leaf in Tr::new. Build both new_sortedmulti
constructors through Wsh::new and Sh::new. SortedMultiVec also ran the
local checks of the context, but a multisig that passes the global
checks passes the local ones too.

Assisted-by: Claude Opus 5.5
Check that the descriptor constructors reject the keys and the script
size that parsing rejects, also for a key below the root and in a
Taproot leaf, and that the sortedmulti constructors build what parsing
builds, with the keys sorted in the script.

Assisted-by: Claude Opus 5.5
@fametrano

Copy link
Copy Markdown

Tested ACK eac11e6. The description's cases are accepted on master and rejected on this head, as Bitcoin Core 31.1 rejects them, and the new test fails with the fix reverted; details on request.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants