Skip to content

bip360: 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

bip360: 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
Copy Markdown

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.
@murchandamus
Copy link
Copy Markdown
Member

cc: @cryptoquick, @EthanHeilman

@murchandamus murchandamus changed the title fix: use correct leafVersion in control block byte for non-default version leaves bip360: use correct leafVersion in control block byte for non-default version leaves Feb 17, 2026
@EthanHeilman
Copy link
Copy Markdown
Contributor

@murchandamus Looking into it

@murchandamus murchandamus added the Pending acceptance This BIP modification requires sign-off by the champion of the BIP being modified label Feb 27, 2026
@murchandamus
Copy link
Copy Markdown
Member

Given from the look of this, that a new PR by this author was authored by Openclaw, and from the general activity on this author’s profile, I assume that this PR is AI slop. It may be useful AI slop, which is why I asked for your opinion, but I tend to close this. Will close this in a couple weeks (on or after 2026-03-30), if I don’t hear more from @cryptoquick, or @EthanHeilman.

@notmike-5
Copy link
Copy Markdown

Given from the look of this, that a new PR by this author was authored by Openclaw, and from the general activity on this author’s profile, I assume that this PR is AI slop. It may be useful AI slop, which is why I asked for your opinion, but I tend to close this. Will close this in a couple weeks (on or after 2026-03-30), if I don’t hear more from @cryptoquick, or @EthanHeilman.

This PR is obviously correct.

@EthanHeilman
Copy link
Copy Markdown
Contributor

EthanHeilman commented Mar 16, 2026

@murchandamus @notmike-5 I'm confused about what this PR is trying to say. Let me try to work through it out loud tell me where I'm wrong.

The only supported leaf version is 0xc0 or 192. P2MR uses this leaf version.

The PR says:

0xC1 → leafVersion = 0xC0 = 192 ✗ (should be 250)

Why should the leaf version be 250? 192 seems correct.

"scriptPathControlBlocks": [
"c1f224a923cd0021ab202ab139cc56802ddb92dcfc172b9212261a539df79a112a",
"c18ad69ec7cf41c2a4001fd1f738bf1e505ce2277acdcaa63fe4765192497f47a7"
"fb8ad69ec7cf41c2a4001fd1f738bf1e505ce2277acdcaa63fe4765192497f47a7"
Copy link
Copy Markdown
Member

@murchandamus murchandamus Mar 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@EthanHeilman: The description of the test states that the two leaves have different leaf versions and the second leaf uses a hypothetical leaf version of 250 (see line 74).

One of the reasons I surface this is to ask whether you want to take the proposed action or whether you want to do something else, or additional actions if this is indicative of other issues in your reference implementation. E.g., what was this test with different leaf versions supposed to test? Is it an issue that it didn’t test that and still passed?

Copy link
Copy Markdown
Contributor

@EthanHeilman EthanHeilman Mar 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, this test case is about different leaf versions, that makes sense. I probably should have noticed the name of the test case was p2mr_different_version_leaves I retract my comment.

I don't maintain the reference implementation. @cryptoquick is taking care of it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I should have distinguished. The first line was in response to you, but the rest was generally directed at the authors.

@jbride
Copy link
Copy Markdown

jbride commented Mar 16, 2026

I believe the correct behavior when encountering a non-standard leaf version in a P2MR merkle tree should be to throw an error.

My pull request here implements this approach.

@cryptoquick
Copy link
Copy Markdown
Contributor

Glad this was pointed out, thank you @jasonandjay. That said, we're going to discuss the proper approach together as a team and address concerns in the PR that @jbride linked: cryptoquick#44

We are also going to need more time to investigate #2102.

Any other thoughts or improvements should be directed there to better tie up loose ends and polish things even further.

@murchandamus
Copy link
Copy Markdown
Member

Thanks for taking a look. I’m going to wait to hear what you want to do about this topic.

@notmike-5
Copy link
Copy Markdown

According to the section "Script Validation" of BIP-360, which was adapted from BIP-341, it is implied that "for the future leaf versions (non-0xC0) the execution must succeed".

As others have pointed to previously, there is a further issue of what to do for leaf version 0xC0 in the context of P2MR. Tapscript validation rules, as defined, explicitly do not apply in this context.

So, might want to consider lumping these two issues together.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug fix Pending acceptance This BIP modification requires sign-off by the champion of the BIP being modified Proposed BIP modification

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants