Skip to content

Bip360 implementation#25

Merged
BarneyChambers merged 3 commits intomasterfrom
BIP360-implementation
Feb 20, 2026
Merged

Bip360 implementation#25
BarneyChambers merged 3 commits intomasterfrom
BIP360-implementation

Conversation

@BarneyChambers
Copy link
Collaborator

Consensus Changes

  • New SigVersion::P2MR_TAPSCRIPT and SCRIPT_VERIFY_P2MR flag
  • VerifyP2MRCommitment — Merkle root verification without the Taproot tweak step
  • New witversion == 2 branch in VerifyWitnessProgram:
    • No key path — witness stacks with fewer than 2 elements are rejected
    • Compact control block1 + 32*m bytes (no internal key, 32 bytes smaller than P2TR)
    • Parity bit enforcement — must be 1 per BIP360 specification
    • Validation weight — Dilithium signature checks deduct from a per-input weight budget
  • Full routing of P2MR_TAPSCRIPT through EvalChecksigTapscript, SignatureHashSchnorr, CheckSchnorrSignature, and ExecuteWitnessScript
  • Dilithium opcodes re-enabled in P2MR tapscript context (remain blocked in P2TR)
  • OP_CHECKMULTISIG blocked in P2MR tapscript (inherited from BIP342 rules)

Address and Type System

Component Details
Destination type WitnessV2P2MR (32-byte Merkle root)
Transaction output type TxoutType::WITNESS_V2_P2MR
Address format bc1z... on mainnet, qcrt1z... on regtest (SegWit v2, bech32m)
Builder P2MRBuilder — constructs P2MR outputs from script trees (no internal key)
Spend data P2MRSpendData — control blocks in 1 + 32*m format

Policy and Activation

  • P2MR witness standardness checks added (annex rejection, stack item size limits)
  • SCRIPT_VERIFY_P2MR activated on all networks

Test Coverage

test/functional/feature_p2mr.py — 15 test groups with BIP-level rigor:

# Test What it verifies
1 Output format scriptPubKey = OP_2 <32 bytes>, same size as P2TR
2 Script path spend OP_TRUE and hash lock scripts, confirmed in mined blocks
3 No key path 0-element and 1-element witnesses rejected
4 Dilithium P2MR vs P2TR All 5 Dilithium opcodes blocked in P2TR, allowed in P2MR
5 Error messages Exact error strings verified for P2TR vs P2MR
6 OP_CHECKMULTISIG Blocked in P2MR tapscript (use OP_CHECKSIGADD)
7 Two-leaf tree Both leaves independently spendable and mined
8 Four-leaf tree Depth-2 balanced tree, all 4 leaves spendable
9 Wrong Merkle proof Corrupted path bytes rejected
10 Cross-tree spend Leaf from tree B cannot spend tree A
11 Invalid control blocks Sizes not matching 1 + 32*m rejected
12 Control block format Size verification: 1B, 33B, 65B; 32-byte savings vs P2TR
13 Parity bit Bit=1 accepted, bit=0 rejected per BIP360
14 Address encoding SegWit v2 address prefix verified
15 Multiple inputs Two P2MR inputs from different trees in one transaction

Every successful spend is mined in a block and verified at the consensus level, not just mempool acceptance. Every rejection asserts the specific error message to prevent false passes.

Why P2MR Matters

Taproot (P2TR) outputs expose the internal public key on-chain, making them vulnerable to long-exposure quantum attacks. Even if you use Dilithium signatures in a P2TR script leaf, an attacker with a CRQC can bypass it entirely via the key path.

P2MR removes the key path, so there is no exposed public key to attack. Combined with Dilithium opcodes in the script tree, P2MR provides end-to-end quantum resistance for BTQ outputs.

Files Modified

Fix three security issues with Dilithium opcode integration:

1. Dilithium opcodes (OP_CHECKSIGDILITHIUM, OP_CHECKSIGDILITHIUMVERIFY,
   OP_CHECKMULTISIGDILITHIUM, OP_CHECKMULTISIGDILITHIUMVERIFY) were not
   counted by GetSigOpCount, allowing blocks with unbounded Dilithium
   signature verifications that bypass the 80k sigop limit (DoS vector).

2. Dilithium opcodes (0xbb-0xbf) fell within the OP_SUCCESSx range
   (0xbb-0xfe) in tapscript, causing any tapscript containing a
   Dilithium opcode to unconditionally succeed without validation,
   making affected outputs anyone-can-spend.

3. Even with the OP_SUCCESSx fix, Dilithium opcodes in tapscript
   provide a false sense of security because P2TR outputs expose the
   internal public key on-chain, leaving them vulnerable to quantum key
   recovery via the key path (see BIP360). Dilithium opcodes are now
   explicitly disabled in tapscript with SCRIPT_ERR_TAPSCRIPT_DILITHIUM,
   following the same pattern as OP_CHECKMULTISIG in tapscript.

Changes:
- script: count Dilithium opcodes in CScript::GetSigOpCount()
- script: narrow IsOpSuccess() range from 0xbb-0xfe to 0xc0-0xfe
- script: reject Dilithium opcodes in tapscript execution context
- script: add SCRIPT_ERR_TAPSCRIPT_DILITHIUM error code
- test: add Dilithium opcode definitions to Python test framework
- test: fix is_op_success() in Python test framework to match C++
- test: add feature_dilithium_sigops.py integration test suite~
Implement SegWit version 2 P2MR outputs as specified in BIP360. P2MR
provides a quantum-resistant script tree output type by removing the
Taproot key path spend, which exposes a public key vulnerable to
quantum key recovery via Shor's algorithm.

P2MR commits directly to the script tree Merkle root (no internal key,
no tweak), enabling Dilithium post-quantum signature opcodes to provide
real protection inside the script tree.

Consensus changes:
- Add SigVersion::P2MR_TAPSCRIPT and SCRIPT_VERIFY_P2MR flag
- Add VerifyP2MRCommitment (Merkle root verification without tweak)
- Add witversion==2 branch in VerifyWitnessProgram
- No key path (reject witness stack with fewer than 2 elements)
- Control block format 1+32*m bytes (no internal key)
- Parity bit enforcement (must be 1 per BIP360)
- Validation weight for Dilithium signature checks
- Route P2MR_TAPSCRIPT through EvalChecksigTapscript, SignatureHashSchnorr,
  CheckSchnorrSignature, and ExecuteWitnessScript
- Re-enable Dilithium opcodes in P2MR tapscript context (blocked in P2TR)
- Block OP_CHECKMULTISIG in P2MR (inherited from BIP342 tapscript rules)

Address and type system:
- Add WitnessV2P2MR destination type and TxoutType::WITNESS_V2_P2MR
- Add bc1z address encoding/decoding (SegWit v2, bech32m)
- Add P2MRBuilder for constructing P2MR outputs from script trees
- Add P2MRSpendData for script path spending

Policy and activation:
- Add P2MR witness standardness checks
- Activate SCRIPT_VERIFY_P2MR on all networks

Tests:
- Add feature_p2mr.py with 15 test groups covering output format,
  script path spending with mining verification, no key path, all 5
  Dilithium opcodes (P2MR vs P2TR comparative), error messages,
  OP_CHECKMULTISIG blocking, multi-leaf trees (2 and 4 leaves),
  invalid Merkle proofs, invalid control block sizes, parity bit
  enforcement, address encoding, and multi-input transactions
- Add p2mr_construct() and P2MRInfo to Python test framework
@BarneyChambers BarneyChambers merged commit 1e1be0e into master Feb 20, 2026
1 of 6 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.

1 participant