A production-ready Merkle tree implementation in Go with cryptographic proof generation, verification, and compact binary serialization.
- Merkle tree construction from arbitrary leaf data
- Inclusion proof generation and verification with O(1) proof lookup
- Multiple hash algorithms: SHA-256, SHA-384, SHA-512
- Configurable balancing strategies: duplicate last leaf or static padding for odd leaf counts
- Compact binary serialization of proofs for storage and transport
- Second-preimage attack protection via internal node prefix
- OpenTelemetry tracing for observability
- Context support for cancellation and timeouts
go get github.com/hyperscale-stack/merkleRequires Go 1.25+.
package main
import (
"context"
"fmt"
"github.com/hyperscale-stack/merkle"
)
func main() {
ctx := context.Background()
// Hash your data into leaves
leaves := [][]byte{
merkle.SHA256([]byte("alice")),
merkle.SHA256([]byte("bob")),
merkle.SHA256([]byte("charlie")),
merkle.SHA256([]byte("dave")),
}
// Build the tree
tree := merkle.New(ctx, leaves, merkle.WithHashType(merkle.HashTypeSHA256))
// Get the root hash
root := tree.RootHash()
fmt.Printf("Root: %x\n", root)
// Generate a proof for leaf at index 0
proof := tree.GenerateProof(0)
// Verify the proof
valid := proof.Verify(leaves[0], root)
fmt.Printf("Proof valid: %v\n", valid)
}tree := merkle.New(ctx, leaves, merkle.WithHashType(merkle.HashTypeSHA256))// SHA-256 (default)
tree := merkle.New(ctx, leaves, merkle.WithHashType(merkle.HashTypeSHA256))
// SHA-384
tree := merkle.New(ctx, leaves, merkle.WithHashType(merkle.HashTypeSHA384))
// SHA-512
tree := merkle.New(ctx, leaves, merkle.WithHashType(merkle.HashTypeSHA512))When the number of leaves is odd, you can choose how to balance the tree:
// Duplicate last leaf (default)
tree := merkle.New(ctx, leaves, merkle.WithBalanceStrategy(merkle.DuplicateLastLeaf))
// Pad with a static hash value
tree := merkle.New(ctx, leaves, merkle.WithPaddingValue(merkle.SHA256([]byte("padding"))))Generate an inclusion proof for any leaf, then verify it:
// Generate proof for leaf at index 2
proof := tree.GenerateProof(2)
// Verify against the root
valid := proof.Verify(leaves[2], tree.RootHash())Proofs can be serialized to a compact binary format for storage or network transport:
// Serialize
data := proof.Serialize()
// Deserialize
restored, err := merkle.DeserializeProof(data)
if err != nil {
log.Fatal(err)
}
// Verify the deserialized proof
valid := restored.Verify(leaf, root)| Offset | Size | Description |
|---|---|---|
| 0 | 1 | Version (0x01) |
| 1 | 1 | Hash type (0x00=SHA-256, 0x01=SHA-384, 0x02=SHA-512) |
| 2 | 1 | Number of siblings |
| 3+ | N | Sibling entries: 1 byte direction + H bytes hash |
| Function / Method | Description |
|---|---|
New(ctx, leaves, opts...) |
Build a tree with proof generation support |
tree.RootHash() |
Return the root hash |
tree.GenerateProof(index) |
Generate an inclusion proof for a leaf |
tree.LeafCount() |
Return the number of original leaves |
| Function / Method | Description |
|---|---|
proof.Verify(leaf, root) |
Verify the proof against a leaf and root hash |
proof.Serialize() |
Serialize the proof to binary |
DeserializeProof(data) |
Deserialize a proof from binary |
| Option | Description |
|---|---|
WithHashType(hashType) |
Set the hash algorithm (preferred) |
WithBalanceStrategy(strategy) |
Set the balancing strategy for odd leaf counts |
WithPaddingValue(value) |
Set a static padding value (implies PadWithStaticHash) |
| Constant | Algorithm | Output Size |
|---|---|---|
HashTypeSHA256 |
SHA-256 | 32 bytes |
HashTypeSHA384 |
SHA-384 | 48 bytes |
HashTypeSHA512 |
SHA-512 | 64 bytes |
- Internal node prefix (
0x01): Leaf and internal node hashes use different domains, protecting against second-preimage attacks. - Proof direction tracking: Each sibling records whether it is on the left or right, preventing proof reordering.
# Run tests with race detection and coverage
make test
# Run linter
make lint
# Generate coverage report
make coverage-htmlMIT License - Copyright (c) Axel Etcheverry.