Skip to main content

A Merkle tree implementation for Tezos

Project description

tzmerkle

A Merkle tree implementation for Tezos blockchain applications.

Python 3.8+ License: MIT

Features

  • 🔐 Multiple hash functions (SHA256, SHA512, SHA3, BLAKE2b, Keccak)
  • 🎯 Tezos-native support with Michelson type encoding
  • 🔗 SmartPy integration for on-chain verification
  • 💾 Serialization support for tree persistence
  • Fully typed with comprehensive type hints

Installation

pip install tzmerkle

For development:

pip install -e ".[dev]"

Quick Start

from tzmerkle import MerkleTree
from tzmerkle.hashes import sha256

# Create a tree for natural numbers
tree = MerkleTree("nat", hash_fn=sha256)

# Add leaves
tree.add_leaves([1, 2, 3, 4, 5])

# Build the tree
tree.build()

# Get the root hash
root = tree.merkle_root
print(f"Root: {root.hex()}")

# Generate a proof for a leaf
encoded_leaf = tree.encode_raw_leaf(3)
proof = tree.get_proof(encoded_leaf)
print(f"Proof length: {len(proof)}")

Usage Examples

Working with Different Types

from tzmerkle import MerkleTree

# String type
tree = MerkleTree("string")
tree.add_leaves(["alice", "bob", "charlie"])
tree.build()

# Complex types (pairs)
tree = MerkleTree("pair nat string")
tree.add_leaves([
    (1, "alice"),
    (2, "bob"),
    (3, "charlie")
])
tree.build()

Using Different Hash Functions

from tzmerkle import MerkleTree
from tzmerkle.hashes import blake2b, sha512, keccak

# BLAKE2b
tree = MerkleTree("nat", hash_fn=blake2b)

# SHA512
tree = MerkleTree("nat", hash_fn=sha512)

# Keccak (Ethereum-compatible)
tree = MerkleTree("nat", hash_fn=keccak)

Proof Generation and Verification

from tzmerkle import MerkleTree

tree = MerkleTree("nat")
tree.add_leaves(list(range(100)))
tree.build()

# Get proof for a specific leaf
leaf_value = 42
encoded_leaf = tree.encode_raw_leaf(leaf_value)
proof = tree.get_proof(encoded_leaf)

print(f"Proof size: {len(proof)} hashes")
print(f"Root: {tree.merkle_root.hex()}")

SmartPy Integration

import smartpy as sp
from tzmerkle import MerkleTree
from tzmerkle.hashes import sha256
from tzmerkle.smartpy.merkle_utils import verify_merkle_proof

# Use in SmartPy contract
@sp.module
def my_module():
    import verify_merkle_proof

    class MyContract(sp.Contract):
        def __init__(self, merkle_root):
            self.data.merkle_root = merkle_root

        @sp.entrypoint
        def verify(self, leaf: sp.bytes, proof: sp.list[sp.bytes]):
            assert self.data.merkle_root == verify_merkle_proof.sha256(sp.record(
                leaf=leaf,
                proof=proof
            ))

@sp.add_test()
def _():
    s = sp.test_scenario("/tmp/test")

    # Create and build tree
    tree = MerkleTree("nat", hash_fn=sha256)
    tree.add_leaves([1, 2, 3, 4])
    tree.build()

    c = my_module.MyContract(tree.merkle_root_spy)

    s += c

    leaf_raw = 1
    encoded_leaf = tree.encode_raw_leaf(leaf_raw)
    proof_spy = tree.get_proof_spy(encoded_leaf)
    c.verify(sp.record(leaf=tree.encode_raw_leaf_spy(leaf_raw), proof=proof_spy))

Serialization and Persistence

from tzmerkle import MerkleTree
from pathlib import Path

# Create and save tree
tree = MerkleTree("nat")
tree.add_leaves(range(1000))
tree.build()

# Save with tree structure for fast loading
tree.serialise("tree.json", include_tree=True, include_proofs=True)

# Load from file
loaded_tree = MerkleTree.from_file("tree.json")
print(f"Loaded {len(loaded_tree._leaves)} leaves")
print(f"Root matches: {loaded_tree.merkle_root == tree.merkle_root}")

Error Handling

from tzmerkle import (
    MerkleTree,
    EmptyTreeError,
    TreeNotBuiltError,
    LeafNotFoundError,
    InvalidLeafError
)

tree = MerkleTree("nat")

# Proper error handling
try:
    tree.build()
except EmptyTreeError:
    print("Add leaves before building!")

try:
    root = tree.merkle_root
except TreeNotBuiltError:
    print("Build tree first!")

tree.add_leaves([1, 2, 3])
tree.build()

try:
    encoded = tree.encode_raw_leaf(999)
    proof = tree.get_proof(encoded)
except LeafNotFoundError:
    print("Leaf not in tree!")

API Reference

MerkleTree

Constructor

MerkleTree(leaf_type: str, hash_fn: TezosHash = sha256)

Create a new Merkle tree.

  • leaf_type: Michelson type string (e.g., "nat", "string", "pair nat string")
  • hash_fn: Hash function from tzmerkle.hashes

Methods

  • add_leaf(leaf) - Add a single leaf (auto-encoded)
  • add_leaves(leaves) - Add multiple leaves (auto-encoded)
  • add_encoded_leaf(leaf: bytes) - Add pre-encoded leaf
  • add_encoded_leaves(leaves: List[bytes]) - Add pre-encoded leaves
  • encode_raw_leaf(leaf) -> bytes - Encode a leaf to bytes
  • build() - Build the Merkle tree
  • get_proof(leaf: bytes) -> List[bytes] - Generate proof for a leaf
  • serialise(path, include_tree=False, include_proofs=False) - Save to file
  • from_file(path) -> MerkleTree - Load from file (class method)

Properties

  • merkle_root: bytes - Get the root hash
  • merkle_root_spy: sp.bytes - Get root for SmartPy

Hash Functions

All hash functions follow the same interface:

from tzmerkle.hashes import sha256, sha512, sha3, blake2b, keccak

# Use as function
digest = sha256(b"data")

# Get hex digest
hex_digest = sha256.hexdigest(b"data")

# Properties
print(sha256.name)          # "sha256"
print(sha256.digest_size)   # 32

Exceptions

  • MerkleTreeError - Base exception
  • EmptyTreeError - Tree has no leaves
  • TreeNotBuiltError - Tree not calculated yet
  • LeafNotFoundError - Leaf not in tree
  • InvalidLeafError - Invalid leaf data or encoding

Development

Setup

# Clone the repository
git clone https://github.com/objkt-com/tzmerkle.git
cd tzmerkle

# Install with dev dependencies
pip install -e ".[dev]"

Running Tests

# Run all tests
pytest

# With coverage
pytest --cov=tzmerkle --cov-report=html

# Verbose output
pytest -v

Code Quality

# Format code
black tzmerkle tests

# Lint
flake8 tzmerkle tests

Performance

Benchmark results for various tree sizes:

Operation 100 leaves 1,000 leaves 10,000 leaves
Build tree 5ms 50ms 520ms
Get proof <1ms <1ms <1ms
Verify proof <1ms <1ms <1ms

License

MIT License - see LICENSE file for details.

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass
  5. Submit a pull request

Credits

Developed by objkt.com for the Tezos ecosystem.

Links

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tzmerkle-0.1.1.tar.gz (15.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tzmerkle-0.1.1-py3-none-any.whl (10.2 kB view details)

Uploaded Python 3

File details

Details for the file tzmerkle-0.1.1.tar.gz.

File metadata

  • Download URL: tzmerkle-0.1.1.tar.gz
  • Upload date:
  • Size: 15.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for tzmerkle-0.1.1.tar.gz
Algorithm Hash digest
SHA256 05459e4106707c168459dea1f73a05fb56c84d39a2407ad8738e49bafc817609
MD5 dd7576c1e4c702b70e976b3b166a13fe
BLAKE2b-256 66b4719ec3597bfb9ed6e8cc31d638719690fe4b334bfc95a8be0312201a3672

See more details on using hashes here.

File details

Details for the file tzmerkle-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: tzmerkle-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 10.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for tzmerkle-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 6f5cb7e35234e6bf42d3be710425076b4a0cc9b8b6ae279d34fb2b57329a852d
MD5 4f274955a78cb9f393fa58e3d06e86f2
BLAKE2b-256 e393b4d583fbac80f19020df46d4894851a531c78d952ad2e6f89c48f8a697f3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page