merkl
A no_std + alloc sparse Merkle tree with a pluggable key-value storage backend.
How it works
Each leaf is addressed by the hash of its data. The hash bits (MSB-first) determine
the path from the root: bit 0 selects left (0) or right (1) at depth 0, bit 1 at
depth 1, and so on. Internal nodes are stored in the backend, keyed by their own hash
and holding the 64-byte serialised Node (left ‖ right child hashes).
The root lives outside the tree. MerkleTree holds no state beyond its backend
and hash-function marker. Every operation receives a root Hash and returns a new
root — making historical roots and independent sub-trees free.
root ──► Node{ left, right }
│ │
Node{…} leaf_hash ← terminal: no backend entry
│
leaf_hash ← terminal
An all-zero Hash (Hash::default()) is the canonical empty root.
Quick start
In-memory tree with a custom hasher
use ;
;
let tree = new;
// An empty tree starts from the zero root.
let root0 = default;
// Insert leaves; each call returns a new root without mutating the old one.
let root1 = tree.insert.unwrap;
let root2 = tree.insert.unwrap;
// Retrieve the stored leaf hash.
assert_eq!;
// root1 is still valid and does not contain "bob".
assert_eq!;
// Insertion is order-independent.
let root_ba = ;
assert_eq!;
With the built-in SHA-256 hasher (sha2 feature)
use ;
let tree = new;
let root =
.iter
.fold;
Inclusion proofs
verify is a pure hash computation — it does not access the backend:
use ;
// Build siblings bottom-up (leaf-level first, root-level last):
let siblings: = collect_proof;
assert!;
ProofSibling carries the sibling's hash and its ProofSide (Left / Right).
merkl-redb
merkl-redb provides RedbBackend, a redb-backed
KvsBackend.
[]
= { = "redb", = ["sha2"] }
use Hash;
use ;
// Ephemeral in-memory database — no files created.
let tree = new;
let root = tree.insert.unwrap;
assert_eq!;
For a persistent file-backed database (requires the std feature, which is on by default):
let backend = create.unwrap;
Cloning a RedbBackend is cheap — all clones share the same underlying Database
via Rc (or Arc with the multi-thread feature).
Feature flags
merkl
| Feature | Default | Description |
|---|---|---|
sha2 |
no | Enables Sha256Hasher and the Sha256MerkleTree<B> alias. |
merkl-redb
merkl-redb always requires std (redb 3.x does not support no_std).
| Feature | Default | Description |
|---|---|---|
sha2 |
no | Re-exports Sha256Hasher and the Sha256RedbMerkleTree alias. |
multi-thread |
no | Wraps the database in Arc instead of Rc, making RedbBackend Send + Sync. |
Implementing KvsBackend for bare-metal targets
merkl is no_std + alloc, so a global allocator is required.
The backend trait is intentionally simple: get and set operate on raw byte slices,
and set returns the previous value (if any) as an owned Vec<u8>.
All keys are 32-byte parent hashes; all values are 64-byte serialised Nodes.
The tree never calls the backend with the zero key, so you may safely use an
all-zero slot as the "empty" sentinel.
use RefCell;
use Vec;
use Result;
use KvsBackend;
/// Fixed-capacity store backed by a statically allocated array.
/// Each slot holds a 32-byte key followed by a 64-byte value (96 bytes total).
/// An all-zero key marks an unused slot.
For interrupt-driven or multi-core embedded targets, replace RefCell with a
critical_section::Mutex or a hardware-specific primitive that provides the same
interior-mutability guarantee.
To back the store with external flash, replace the RefCell<[[u8; 96]; N]> body
with reads and writes to your flash driver, taking care to erase pages before
writing and to handle wear-levelling as needed by your device.
License
MIT — see LICENSE.