Skip to content

fix(miniscript): count exactly k satisfactions in thresh max-size bounds - #1040

Merged
apoelstra merged 1 commit into
rust-bitcoin:masterfrom
portlandhodl:fix/thresh-max-sat-size
Sep 4, 2026
Merged

apoelstra merged 1 commit into
rust-bitcoin:masterfrom
portlandhodl:fix/thresh-max-sat-size

Conversation

@portlandhodl

Copy link
Copy Markdown
Collaborator

A thresh(k, ...) witness satisfies exactly k children: the script checks
the running sum against k with OP_EQUAL, so any witness with a different
number of satisfied children is invalid. The maximum-satisfaction
computation in ExtData::threshold nevertheless selected k+1 children as
satisfied (i <= k instead of i < k), e.g. computing the maximum witness
size of thresh(1,pk(A),s:pk(B)) as 146 bytes (two signatures) instead of
74 (one signature plus one empty dissatisfaction).

This inflates Miniscript::max_satisfaction_weight() and everything
derived from it, making fee/weight estimates overestimate by up to one
largest-child satisfaction per thresh node.

Found by differential testing against Bitcoin Core's miniscript
implementation (Node::GetWitnessSize/GetStackSize agree with the fixed
values, and with the reference implementation's satisfaction table,
which permits exactly k satisfactions).

A thresh(k, ...) witness satisfies exactly k children: the script checks
the running sum against k with OP_EQUAL, so any witness with a different
number of satisfied children is invalid. The maximum-satisfaction
computation in ExtData::threshold nevertheless selected k+1 children as
satisfied (i <= k instead of i < k), e.g. computing the maximum witness
size of thresh(1,pk(A),s:pk(B)) as 146 bytes (two signatures) instead of
74 (one signature plus one empty dissatisfaction).

This inflates Miniscript::max_satisfaction_weight() and everything
derived from it, making fee/weight estimates overestimate by up to one
largest-child satisfaction per thresh node.

Found by differential testing against Bitcoin Core's miniscript
implementation (Node::GetWitnessSize/GetStackSize agree with the fixed
values, and with the reference implementation's satisfaction table,
which permits exactly k satisfactions).
@apoelstra

Copy link
Copy Markdown
Member

Nice! This is a pretty subtle difference in outputs.

@apoelstra apoelstra left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK d7801d1; successfully ran local tests

@apoelstra
apoelstra merged commit 5a5f749 into rust-bitcoin:master Sep 4, 2026
13 checks passed
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.

2 participants