Skip to content

fix: use correct leafVersion in control block byte for non-default version leaves#2103

Open
jasonandjay wants to merge 1 commit intobitcoin:masterfrom
jasonandjay:fix/control-block-leaf-version-byte
Open

fix: use correct leafVersion in control block byte for non-default version leaves#2103
jasonandjay wants to merge 1 commit intobitcoin:masterfrom
jasonandjay:fix/control-block-leaf-version-byte

Conversation

@jasonandjay
Copy link

Summary

Fix the first byte of scriptPathControlBlocks[1] in the p2mr_different_version_leaves
test vector. The control block incorrectly uses 0xC1 (leafVersion 192) instead of 0xFB
(leafVersion 250) for a leaf explicitly declared with "leafVersion": 250.

Problem

The P2MR control block's first byte encodes both the leaf version and parity bit:

controlBlock[0] = (leafVersion & 0xfe) | parityBit

For P2MR, the parity bit is always 1. So:

  • leafVersion 192 (0xC0) → 0xC1
  • leafVersion 250 (0xFA) → 0xFB

The test vector defines two leaves:

{ "id": 0, "leafVersion": 192, "script": "...OP_CHECKSIG" }
{ "id": 1, "leafVersion": 250, "script": "06424950333431" }

But the current control block for leaf1 starts with 0xC1:

scriptPathControlBlocks[1] = "c1 8ad69ec7cf41c2a4001fd1f738bf1e505ce2277acdcaa63fe4765192497f47a7"
                               ^^
                               0xC1 → leafVersion = 0xC0 = 192  ✗ (should be 250)

This is incorrect because a verifier would extract leafVersion = 0xC1 & 0xFE = 0xC0 = 192
from this byte, then compute tapleafHash(script, version=192) which produces a
completely different hash than the correct tapleafHash(script, version=250), making
it impossible to reconstruct the correct Merkle root.

Fix

Change the first byte from 0xC1 to 0xFB:

scriptPathControlBlocks[1] = "fb 8ad69ec7cf41c2a4001fd1f738bf1e505ce2277acdcaa63fe4765192497f47a7"
                               ^^
                               0xFB → leafVersion = 0xFA = 250  ✓

Verification:

tapleafHash(script="06424950333431", version=250) = f224a923cd0021ab202ab139cc56802ddb92dcfc172b9212261a539df79a112a
sibling = leafHash[0] = 8ad69ec7cf41c2a4001fd1f738bf1e505ce2277acdcaa63fe4765192497f47a7

tapBranchHash(f224a923..., 8ad69ec7...) → hashes are sorted internally
→ 6c2dc106ab816b73f9d07e3cd1ef2c8c1256f519748e0813e4edd2405d277bef = merkleRoot ✓

Changes

  • p2mr_different_version_leaves: change scriptPathControlBlocks[1] first byte from c1 to fb

…rsion leaves

The P2MR control block's first byte encodes both the leaf version and
parity bit: controlBlock[0] = (leafVersion & 0xfe) | parityBit.
For the leaf with leafVersion 250 (0xFA), the correct first byte is
0xFB (with parity bit 1), not 0xC1 (which corresponds to leafVersion
192).

Fix p2mr_different_version_leaves scriptPathControlBlocks[1] first
byte from c1 to fb.
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