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.2.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.2-py3-none-any.whl (10.1 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for tzmerkle-0.1.2.tar.gz
Algorithm Hash digest
SHA256 315b5915ccf92e43047aaedf1a732083a61a74466c74bbc4e84d4842b686a722
MD5 413a53db20ad283f4ffa864367c3825e
BLAKE2b-256 cd4bbf7f8cbfbbad3643574f1503302ff10eee7d2231525c483ee118137ade74

See more details on using hashes here.

File details

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

File metadata

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

File hashes

Hashes for tzmerkle-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 9a38f8bb85f8951ecb0974679553b2565e77cfee01de7b2086c2890a2722ade3
MD5 ec596f6f963aad6b0c209b1df19ea253
BLAKE2b-256 c5be7f0977fdc630901b74878ad6517c212b1953e0eafe6b3714b3e66aead5de

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