bip360: fix leafHashes order and a control byte in the pqc test vectors #2220

pull jeanpablojp wants to merge 1 commits into bitcoin:master from jeanpablojp:bip360-fix-pqc-vectors changing 1 files +5 −5
  1. jeanpablojp commented at 8:44 PM on July 27, 2026: none

    Two fixes in bip-0360/ref-impl/common/tests/data/p2mr_pqc_construction.json, found while implementing BIP 360 against the published vectors.

    First, the three-leaf vectors (p2mr_three_leaf_complex and p2mr_three_leaf_alternative) list intermediary.leafHashes with entries 0 and 2 swapped relative to the depth-first order of the script tree. The merkle root, scriptPubKey, address and control blocks in those same vectors all follow depth-first order, as do the equivalent trees in p2mr_construction.json. This looks like a leftover from the ordering problem that #2202 fixed for the control blocks.

    Check against the spec formulas:

    import json, hashlib
    
    def tagged(tag, data):
        t = hashlib.sha256(tag.encode()).digest()
        return hashlib.sha256(t + t + data).digest()
    
    d = json.load(open("p2mr_pqc_construction.json"))
    tv = [t for t in d["test_vectors"] if t["id"] == "p2mr_three_leaf_complex"][0]
    leaves = []
    def walk(n):
        if isinstance(n, list):
            for x in n: walk(x)
        else: leaves.append(n)
    walk(tv["given"]["scriptTree"])
    for i, leaf in enumerate(leaves):
        s = bytes.fromhex(leaf["script"])
        h = tagged("TapLeaf", bytes([leaf["leafVersion"]]) + bytes([len(s)]) + s).hex()
        print(i, h == tv["intermediary"]["leafHashes"][i])
    

    Before this change that prints "0 False / 1 True / 2 False", i.e. entries 0 and 2 hold each other's values. Same for p2mr_three_leaf_alternative.

    Second, in p2mr_different_version_leaves the second leaf has "leafVersion": 250 (0xfa), but scriptPathControlBlocks[1] started with c1. The control byte carries the leaf version in its upper 7 bits with the low bit set, so it should be fb. The vector's leafHashes[1] and merkle root already correspond to the leaf hashed under 0xfa and the path bytes are correct, so only the first byte was wrong. As published, a spender using that control block would recompute the leaf hash under 0xc0 and fail the merkle check.

    After both fixes every success vector in the file passes a full recomputation from the spec formulas: leaf hashes, merkle root, scriptPubKey, bech32m address, control blocks, and walking each control block path back to the root.

  2. bip360: fix leafHashes order and a control byte in the pqc vectors
    In p2mr_pqc_construction.json, the three-leaf vectors
    (p2mr_three_leaf_complex, p2mr_three_leaf_alternative) listed
    intermediary.leafHashes with entries 0 and 2 swapped relative to the
    depth-first order of the script tree, while the merkle root,
    scriptPubKey, address and control blocks all follow depth-first
    order (as do the same trees in p2mr_construction.json).
    
    In p2mr_different_version_leaves, scriptPathControlBlocks[1] started
    with byte 0xc1 although the leaf it commits to has leafVersion 0xfa;
    the control byte carries the leaf version in its upper 7 bits with
    the low bit set, so it must be 0xfb. The vector's leafHashes[1] and
    merkle root already correspond to the 0xfa leaf.
    1baf0e71bb
  3. jeanpablojp referenced this in commit afef53452a on Jul 27, 2026
  4. jeanpablojp referenced this in commit acfa5ba3cf on Jul 27, 2026
  5. jonatack added the label Pending acceptance on Jul 28, 2026
  6. jonatack added the label Bug fix on Jul 28, 2026
  7. jonatack commented at 12:08 AM on July 28, 2026: member

    Fixes LGTM (thanks!) pinging @cryptoquick @EthanHeilman for feedback or sign-off.


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-07-28 16:10 UTC

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