bip360: add spend-path test vectors #2232

pull jeanpablojp wants to merge 1 commits into bitcoin:master from jeanpablojp:bip360-spend-vectors changing 1 files +1539 −0
  1. jeanpablojp commented at 9:55 PM on August 1, 2026: contributor

    BIP 360's vectors stop at construction: a script tree goes in, leaf hashes, Merkle root, scriptPubKey, address and control blocks come out. There is nothing to spend, unlike BIP 341's wallet-test-vectors.json, so every implementation ends up writing its own spend coverage. This adds the spend-path vectors offered in the Delving write-up (https://delvingbitcoin.org/t/bip-360-p2mr-implemented-in-bitcoin-core-on-regtest-vector-results-measurements-spec-feedback/2751).

    The schema mirrors bip-0341/wallet-test-vectors.json, adapted to an output type whose only spend path is the script path. Two transactions carry the valid cases, per-input given/intermediary/expected with the sighash midstates at group level. invalidSpending holds ten standalone transactions that must fail; error is a spec-level description for implementations to map onto their own codes, and several deliberately collapse onto one code in ours. Inputs spent without a signature have privkey/hashType/annex/sigMsg/sigHash null rather than absent.

    Valid:

    • depth-1 leaf, SIGHASH_DEFAULT
    • depth-2 leaf of a three-leaf tree, SIGHASH_ALL (65-byte signature)
    • annex, covered by the signature
    • p2mr_single_leaf_script_tree at depth 0, no signature: the v0.12.0 anyone-can-spend rule
    • p2mr_different_version_leaves through its 0xfa leaf: unknown leaf versions are unencumbered
    • depth-128 leaf, control block at its 4097-byte maximum: the accepting side of the depth limit
    • a second transaction with one input for each of the other five hash types (SIGHASH_NONE, SIGHASH_SINGLE and the three ANYONECANPAY forms), with SINGLE|ANYONECANPAY at input 1 so it signs output 1 and not output 0

    Invalid: flipped signature, tampered Merkle path, control byte with the last bit 0, single-element witness, two-element witness ending in an annex, no witness at all, a signature with the undefined hash type 0x80, and control blocks of 0, 34 and 4129 bytes, which is one too short for the control byte, one that is not 1 + 32*m, and one at m = 129.

    Both named trees come from p2mr_construction.json and are asserted against it when the file is generated, so the two files cross-check each other. Keys and prevout txids are derived deterministically and signing is BIP 340 with an all-zero aux, so the file regenerates from scratch.

    One case takes a position the spec leaves open: p2mr_spend_control_byte_low_bit_zero expects failure when the last bit of c[0] is 0. Script Validation says that bit "is unused and must be 1" and the footnote says a faulty deserialization "will cause an immediate error", but that bullet states no failure condition, while the length rule above it does. If non-enforcement is the intended reading I am happy to drop the vector and the footnote could be reworded; either way it would help to have it explicit.

    Verified against a Bitcoin Core implementation of the BIP: every valid input passes VerifyScript and every invalid case fails with the expected script error (https://github.com/jeanpablojp/bitcoin/tree/p2mr-regtest, src/test/p2mr_vector_tests.cpp). The generator is test/functional/tool_p2mr_spend_vectors.py on the same branch.

  2. jonatack added the label Proposed BIP modification on Aug 2, 2026
  3. jonatack added the label Pending acceptance on Aug 3, 2026
  4. murchandamus commented at 11:04 PM on August 13, 2026: member
  5. jeanpablojp force-pushed on Aug 15, 2026
  6. quantova-inc commented at 1:04 PM on November 1, 2026: none

    I loaded the file and checked it against my own BIP 360 work. The depth coverage is solid. There is a passing control block at the maximum depth of 128 with the matching reject at 129. The small end is covered down to a single leaf. The reject set with stable error ids reads clean.

    One gap on the passing side. The six valid inputs sign with SIGHASH_DEFAULT and SIGHASH_ALL only. SIGHASH_SINGLE, SIGHASH_NONE and the ANYONECANPAY variants are the flags that change which sighash midstate is used, so with none of them in a passing case an implementation that selects the wrong midstate for those flags would still pass every vector. Worth adding a few inputs that exercise them. I can send the signed inputs if that helps.

  7. bip360: add spend-path test vectors
    The existing vectors are construction-only, so implementations have to
    invent their own spend coverage. This adds a spending file in the shape
    of bip-0341/wallet-test-vectors.json, adapted to an output type whose
    only spend path is the script path.
    
    Two transactions cover the valid cases, each with the five shared
    BIP 341 sighash hashes at the group level and per-input given,
    intermediary and expected fields. The first has six inputs and signs
    only with SIGHASH_DEFAULT and SIGHASH_ALL, the second has five, one for
    each of the other hash types. A separate invalidSpending array has ten
    self-contained transactions that must fail validation, with the reason
    in a short spec-level error string.
    
    Private keys are sha256("p2mr_spending/key/<n>"), prevout txids are
    sha256("p2mr_spending/prevout/<n>") in internal byte order, and signing
    is BIP 340 with an all-zero aux, so the signatures can be re-derived
    rather than trusted. Two of the inputs reuse trees from
    p2mr_construction.json, which makes the two files cross-check each
    other.
    4daec02d1e
  8. jeanpablojp force-pushed on Nov 2, 2026
  9. jeanpablojp commented at 8:55 PM on November 2, 2026: contributor

    Right, nothing on the passing side reached NONE, SINGLE or ANYONECANPAY. I added a second transaction to scriptPathSpending with one input per remaining hash type, derived the same way as the rest of the file.

    I changed SignatureHashSchnorr three ways, to ignore ANYONECANPAY, to commit NONE to the outputs and to take output 0 for SINGLE. Each time the old file still passed and the new one failed.


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-11-03 20:10 UTC

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