Skip to content

bip360: add spend-path test vectors - #2232

Open
jeanpablojp wants to merge 1 commit into
bitcoin:masterfrom
jeanpablojp:bip360-spend-vectors
Open

bip360: add spend-path test vectors#2232
jeanpablojp wants to merge 1 commit into
bitcoin:masterfrom
jeanpablojp:bip360-spend-vectors

Conversation

@jeanpablojp

@jeanpablojp jeanpablojp commented Aug 1, 2026

Copy link
Copy Markdown
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. One transaction with six inputs carries the valid cases, per-input given/intermediary/expected with the sighash midstates at group level. invalidSpending holds nine 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

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, 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.

@jonatack jonatack added Proposed BIP modification PR by non-owner to update BIP content Pending acceptance This BIP modification requires sign-off by the champion of the BIP being modified labels Aug 2, 2026
@murchandamus

Copy link
Copy Markdown
Member

cc: @cryptoquick

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.

One transaction with six inputs covers the valid cases, with the five
shared BIP 341 sighash hashes at the group level and per-input given,
intermediary and expected fields. A separate invalidSpending array has
nine 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.
@jeanpablojp
jeanpablojp force-pushed the bip360-spend-vectors branch from 92cb0b1 to adbc31f Compare August 15, 2026 18:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Pending acceptance This BIP modification requires sign-off by the champion of the BIP being modified Proposed BIP modification PR by non-owner to update BIP content

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants