BIP-379: note that the type properties assume their requirements #2267

pull guggero wants to merge 1 commits into bitcoin:master from guggero:bip379-malleability-caveat changing 1 files +12 −0
  1. guggero commented at 12:46 PM on August 22, 2026: contributor

    The "s", "f" and "e" descriptions in the Security section read as guarantees for every expression, but they only hold for expressions that meet the malleability requirements of the type system.

    thresh is the clearest case: its rule is e=all are s, so thresh(2,or_i(pk(A),pk(B)),a:or_i(pk(C),pk(D))) is "e" even though each or_i child has two unconditional dissatisfactions, giving the threshold four; the opposite of the "unique unconditional dissatisfaction" the description promises. (Confirmed by spending or_d(thresh(...),pk(E)) through its second branch: all four witnesses, differing only in the two branch-selector bytes, are accepted by the script engine.)

    Nothing is wrong with the rule. Such a threshold fails the "e" requirement that the malleability table imposes for non-malleability, and non-malleability is conjunctive, so the expression and everything containing it is malleable either way; the property value is a don't-care there. This just says so, so that implementations agreeing on those values are not read as contradicting the descriptions.

    Prompted by bitcoin/bitcoin#36028 and the BIP 379 test vector PR, where the type of a malleable expression has to be agreed on as well.

    I'm going to create a PR soon that updates rust-bitcoin to align with this change and bitcoin/bitcoin#36028 (and https://github.com/btcsuite/btcd/pull/2592 is updated as well).

    cc @sipa, @apoelstra

  2. jonatack added the label Proposed BIP modification on Aug 22, 2026
  3. jonatack added the label Pending acceptance on Aug 22, 2026
  4. sipa commented at 1:10 PM on August 22, 2026: member

    The same is true for the correctness properties. They're all written assuming the Requirements condition already holds.

  5. guggero commented at 1:37 PM on August 22, 2026: contributor

    The same is true for the correctness properties. They're all written assuming the Requirements condition already holds.

    Hmm, I see... Thanks for taking a look. Are you suggesting to formulate the caveat more broadly to also include the correctness properties? Or are you saying that this change is not necessary at all as it should already be clear to a reader of the BIP that the requirements conditions need to hold first?

  6. apoelstra commented at 2:17 PM on August 22, 2026: contributor

    The same is true for the correctness properties

    I don't think this is true. At the very least, I think this is a bit more subtle than "you need to be non-malleable to guarantee correctness". If we said that the implication is that you cannot construct malleable Miniscripts or else your coins might be locked.

    If the correctness properties are conditional at all (I spent several minutes checking and could not find an instance where they were), the conditionality will be limited to e.g. n's claim about "no satisfaciton needs 0" needing to be amended to "no nonmalleable satisfaction needs 0".

  7. apoelstra commented at 2:18 PM on August 22, 2026: contributor

    Oh, @sipa did you mean to say:

    • the security properties all assume correctness holds (definitely true); or
    • the correctness properties all assume that nonmalleability holds (what I initially read you as saying)
  8. apoelstra commented at 2:30 PM on August 22, 2026: contributor

    Okay, rereading the PR and assuming sipa meant "security requires correctness", I think we should instead change the text to say something like:

    "While the s property matches its description for all correct Miniscripts, regardless of malleability, f and e may fail to match their descriptions in malleable scripts. That is, a malleable f expression may be dissatisfiable without a signature, and a malleable e expression may have multiple unconditional satisfactions and/or conditional satisfactions that can be produced without a signature.

    "In other words, once an expression is determined to be malleable, any expression containing that expression is malleable. These three type properties can recursively preserve malleability, but they cannot recover it once it is lost.

    "If the correctness properties are not met, none of these properties are meaningful and this BIP says nothing at all about the script.

  9. sipa commented at 2:34 PM on August 22, 2026: member

    IIIRC:

    • The correctness-related type properties in the BIP/website are all written with the assumption that the correctness-Requirement condition on the same fragment holds.
    • The malleability-related type properties in the BIP/website are all written with the assumption that the nonmalleability-Requirement condition on the same fragment holds.

    I'm in favor of clarifying this in the BIP, as it's pretty non-obvious.

    An alternative approach that we could pursue (but makes it a more substantial BIP change) is to define all correctness properties as "Valid AND x", for example make "z" mean "Valid AND zero arguments consumer", and the same with the malleability type properties, making "e" mean "Nonmalleable AND uniquely dissatisfiable".

    This would remove all implementation-specific freedom in typing of invalid/nonmalleable things. It also wouldn't really change the test vector implementations, because they could be written as "check that validity matches, and if valid, check that the validity type properties match. If so, also check that malleability matches, and if nonmalleable, check that the malleability type properties match".

  10. apoelstra commented at 2:37 PM on August 22, 2026: contributor

    @guggero why can't we just fix the thresh rule in the BIP to say that e requires the children to be e? Are there other cases where the f/e/s rules fail to match their descriptions?

    Okay, I like sipa's framing here. The tables give the requirements for correctness/nonmalleability, and once you fail to meet the requirements, it's game over and the table stops providing meaningful information. Agree we should make this explicit in the BIP. @sipa ok, great, agreed on all counts. (And I don't think we should attempt the invasive "valid AND" change, which feels like it'd add a ton of noise, for as you say, no benefit.)

  11. sipa commented at 2:45 PM on August 22, 2026: member

    What about doing this implicitly?

    Add a sentence above/below both tables that says "The type properties all implicitly require the condition under Requirement. Their only purpose is helping reason about correctness/malleability, so they are useful in expressions that already invalid/malleability."

    The actual logic implementing this can be pushed to the tests, so no internal code changes are needed, but the tests vectors would drop z/o/n/d/u from invalid cases, and drop s/f/e from malleable cases.

  12. apoelstra commented at 2:53 PM on August 22, 2026: contributor

    the tests vectors would drop z/o/n/d/u from invalid cases, and drop s/f/e from malleable cases

    I like this idea, and proposed the same change in rust-miniscript here https://github.com/rust-bitcoin/rust-miniscript/pull/1032#issuecomment-5380985927. It would also clarify the situation for rust-miniscript users (if they fail correctness the Miniscript won't parse, but if they fail malleability they will get a usable expression that might have s/f/e properties but for which these properties are meaningless).

    It's just a question of whether it's worth updating all the test vectors, which might require some mildly annoying sed work. Or a few cents of LLM tokens.

  13. guggero commented at 8:42 AM on August 25, 2026: contributor

    Great, thank you both for the feedback and suggestions!

    Add a sentence above/below both tables that says "The type properties all implicitly require the condition under Requirement. Their only purpose is helping reason about correctness/malleability, so they are useful in expressions that already invalid/malleability."

    I like that too! I'll change this PR to add that sentence instead, sounds to me like there's agreement on that so far.

    the tests vectors would drop z/o/n/d/u from invalid cases, and drop s/f/e from malleable cases

    No problem, I'm happy to do that. Shouldn't be too big of a lift.

    I'm going to create a new PR for the suggested approach in https://github.com/rust-bitcoin/rust-miniscript/pull/1032#issuecomment-5380985927, then I can rebase my "test vector extraction" branch on that, which should prevent quite a bit of manual work already.

  14. guggero force-pushed on Aug 26, 2026
  15. guggero renamed this:
    BIP-379: note that the malleability property descriptions are conditional
    BIP-379: note that the type properties assume their requirements
    on Aug 26, 2026
  16. guggero commented at 6:03 AM on August 26, 2026: contributor

    I've updated the wording to match your suggestions. And a proposal for implementing the suggested property erasure on malleable expressions can be found in https://github.com/rust-bitcoin/rust-miniscript/pull/1037.

  17. apoelstra commented at 3:33 PM on August 30, 2026: contributor

    Concept ACK https://github.com/rust-bitcoin/rust-miniscript/pull/1037 which sets s/f/e to false for all nonmalleable scripts and updates the test vectors accordingly.

  18. apoelstra commented at 3:35 PM on August 30, 2026: contributor

    ACK a320fe8d7e3f37388fe1c57f026471237c3167ac.

    But I would suggest adding one more paragraph Implementors of APIs which unconditionally return values for these properties should always return false for malleable expressions.

  19. bip379: note that the type properties assume their requirements
    Both tables give the properties of a fragment in function of those of its
    subexpressions, and both are written assuming that the conditions in the
    "Requires" column of that fragment hold. Where they do not, the tables
    still assign values, but the values carry none of the meaning the
    descriptions of the properties give them, and implementations are free to
    differ on them.
    
    thresh is the case that surfaced this. Its rule is "e=all are s", so
    thresh(2,or_i(pk(A),pk(B)),a:or_i(pk(C),pk(D))) is "e" even though each
    or_i child has two unconditional dissatisfactions, giving the threshold
    four, which is the opposite of the "unique unconditional dissatisfaction"
    that the description of "e" promises. The threshold fails the "e"
    requirement the same table imposes, though, so it is malleable and the
    value is a don't-care.
    
    Say so below both tables, so that implementations which disagree on the
    type of an invalid or malleable expression are not read as contradicting
    this document. Bitcoin Core (bitcoin/bitcoin#36028) and rust-miniscript
    are aligning on the values the tables give, and the test vectors proposed
    in #2240 leave out the properties that are not meaningful: z/o/n/d/u for
    invalid expressions and s/f/e for malleable ones.
    acbc7b000b
  20. guggero force-pushed on Aug 30, 2026
  21. guggero commented at 5:46 PM on August 30, 2026: contributor

    ACK a320fe8.

    But I would suggest adding one more paragraph Implementors of APIs which unconditionally return values for these properties should always return false for malleable expressions.

    Thank you, added that paragraph below the malleability properties table.

  22. guggero referenced this in commit 9e277855b6 on Aug 30, 2026
  23. apoelstra commented at 7:21 PM on August 30, 2026: contributor

    ACK acbc7b000bf8655ecf6ef03359ba291b9e9b891f

  24. jonatack removed the label Pending acceptance on Aug 30, 2026
  25. jonatack commented at 10:06 PM on August 30, 2026: member

    LGTM

  26. jonatack merged this on Aug 30, 2026
  27. jonatack closed this on Aug 30, 2026

  28. guggero deleted the branch on Aug 31, 2026

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bips. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-09-13 02:10 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me