Source code for primitives.merkle

"""Binary Merkle tree using Poseidon2 hash.

Leaf hash: PaddingFreeSponge (hash_to_digest).
Node compress: TruncatedPermutation (compress).

Reference:
    p3-merkle-tree (FieldMerkleTreeMmcs).
"""

from primitives.field import Digest, MerklePath
from primitives.poseidon2 import hash_to_digest, compress, compress_batch, hash_batch


# <doc-anchor id="build-tree">
[docs] def build_merkle_tree( leaves: list[list[int]], ) -> tuple[Digest, list[list[Digest]]]: """Build binary Merkle tree, returning (root, levels). Uses batch Poseidon2 operations (rayon-parallelized) for leaf hashing and internal node compression. Args: leaves: Leaf data (each a list of field elements, hashed internally). Returns: (root_digest, tree_levels) where tree_levels[0] is leaf digests. Reference: p3-merkle-tree (FieldMerkleTreeMmcs) """ # Batch hash all leaves in parallel digests = hash_batch(leaves) # Build tree bottom-up with batch compression tree = [digests] current_level = digests while len(current_level) > 1: lefts = current_level[0::2] rights = current_level[1::2] current_level = compress_batch(lefts, rights) tree.append(current_level) root = list(current_level[0]) # defensive copy to avoid aliasing with tree return root, tree
# <doc-anchor id="open-proof">
[docs] def get_opening_proof( tree: list[list[Digest]], leaf_index: int ) -> MerklePath: """Get Merkle opening proof (sibling digests, leaf to root). Reference: p3-merkle-tree (FieldMerkleTreeMmcs) """ proof = [] idx = leaf_index for level in tree[:-1]: sibling_idx = idx ^ 1 proof.append(level[sibling_idx]) idx >>= 1 return proof
# <doc-anchor id="verify-opening">
[docs] def verify_opening_prehashed( root: Digest, leaf_digest: Digest, leaf_index: int, proof: MerklePath, ) -> bool: """Verify Merkle opening proof for pre-hashed leaf. Reference: p3-merkle-tree (FieldMerkleTreeMmcs::verify_batch) """ current = leaf_digest idx = leaf_index for sibling in proof: if idx % 2 == 0: current = compress(current, sibling) else: current = compress(sibling, current) idx >>= 1 return current == root
[docs] def verify_opening( root: Digest, leaf: list[int], leaf_index: int, proof: MerklePath, ) -> bool: """Verify Merkle opening proof for unhashed leaf data. Reference: p3-merkle-tree (FieldMerkleTreeMmcs::verify_batch) """ return verify_opening_prehashed(root, hash_to_digest(leaf), leaf_index, proof)