Offline Merkle Computation
Compute Merkle roots locally without an indexer. Useful for testing, verification, and analytics.
Functions
empty_root_hex()
Returns the root of an empty Merkle tree (all leaves zero). This is the initial root stored on-chain during init-contract.
use r14_sdk::merkle::empty_root_hex;
let root = empty_root_hex();
assert_eq!(root.len(), 64); // 32 bytes, no 0x prefix
The empty root is computed by iterating hash2(0, 0) for MERKLE_DEPTH (20) levels.
compute_root_from_leaves()
Compute the Merkle root for a given set of leaf commitments. Produces the same result as the indexer's SparseMerkleTree::root() for the same leaf set.
use r14_sdk::merkle::compute_root_from_leaves;
use r14_sdk::wallet::hex_to_fr;
let cm1 = hex_to_fr("0x1a2b3c...")?;
let cm2 = hex_to_fr("0x4d5e6f...")?;
let root = compute_root_from_leaves(&[cm1, cm2]);
println!("root: {}", root); // 64-char hex, no 0x prefix
Properties
- Order matters:
compute_root_from_leaves(&[a, b])differs fromcompute_root_from_leaves(&[b, a]). Leaves are ordered by insertion index. - Deterministic: Same inputs always produce the same root.
- Hash function: Poseidon
hash2at every level. - Tree depth: 20 levels, supporting up to 1,048,576 (2^20) leaves.
- Empty subtrees: Unpopulated subtrees use precomputed zero hashes (
hash2(zero_i, zero_i)per level).
CLI
# Empty tree root
r14 compute-root
# Root with specific commitments (no 0x prefix)
r14 compute-root 1a2b3c4d5e6f... 7a8b9c0d1e2f...
With JSON output:
r14 compute-root --json 1a2b3c4d5e6f...
{
"root": "a1b2c3d4e5f6..."
}
Use Cases
Testing: Verify that your local Merkle tree matches the indexer's without making network calls.
use r14_sdk::merkle::{empty_root_hex, compute_root_from_leaves};
use r14_sdk::{Note, commitment, owner_hash, SecretKey};
use r14_sdk::wallet::crypto_rng;
let mut rng = crypto_rng();
let sk = SecretKey::random(&mut rng);
let owner = owner_hash(&sk);
let note = Note::new(1000, 1, owner.0, &mut rng);
let cm = commitment(¬e);
let root_before = empty_root_hex();
let root_after = compute_root_from_leaves(&[cm]);
assert_ne!(root_before, root_after);
Verification: Confirm that a deposit correctly changed the root.
// Before deposit: root_before = compute_root_from_leaves(&existing_leaves)
// After deposit: root_after = compute_root_from_leaves(&[existing_leaves..., new_cm])
Analytics: Compute roots for arbitrary subsets of commitments offline for auditing or reconciliation purposes.