Overview
Thelight-batched-merkle-tree crate provides batched Merkle tree implementations for the account compression program. Instead of updating trees one leaf at a time, this library batches multiple insertions and updates them with zero-knowledge proofs (ZKPs), enabling efficient on-chain verification.
Crate: light-batched-merkle-treeLocation:
program-libs/batched-merkle-tree/Error Codes: 14301-14312
Key Concepts
Batched Updates
Group multiple tree operations and verify with a single ZK proof
Root History
Maintain cyclic buffer of 200 roots for validity proofs
Bloom Filters
Enable non-inclusion proofs while batches are filling
Immediate Spending
Access newly created accounts before tree insertion
Tree Types
State Trees (2 accounts)
Purpose: Store compressed account hashes (state compression) Accounts:BatchedMerkleTreeAccount- Main tree with integrated input queue (nullifiers)BatchedQueueAccount- Output queue for new compressed accounts
Address Trees (1 account)
Purpose: Register new addresses for compressed accounts Account:BatchedMerkleTreeAccountwith integrated queue (addresses only)- No separate output queue needed
Account Types
BatchedMerkleTreeAccount
Account type identifier:
[111, 35, 172, 196, 215, 118, 11, 198]Tree configuration and state:
tree_type: StateV2 or AddressV2height: Merkle tree height (26-40)next_index: Next available leaf indexnum_batches: Number of batches (always 2)batch_size: Leaves per batch (10-500)zkp_batch_size: Leaves per ZK proof (10 or 500)rollover_threshold: Tree capacity threshold (default: 95%)root_history_capacity: Number of roots stored (200)network_fee: Optional fee for insertions
Cyclic buffer of recent Merkle roots for validity proofs
Input queue with bloom filters for nullifiers (state) or addresses (address trees)
BatchedQueueAccount
Account type identifier:
[119, 102, 42, 168, 46, 208, 215, 195]Queue configuration and batch state
Two alternating batches:
- Fill - Currently accepting insertions
- Full - Ready for ZKP updates
- Inserted - All values inserted into tree
Actual leaf values for each batch. Values accessible by index even before tree insertion (enables immediate spending).
Batching System
Alternating Batches
Trees use 2 alternating batches for continuous operation:ZKP Batches
Each batch is divided into smaller ZKP batches:- batch_size: Total capacity (e.g., 500 leaves)
- zkp_batch_size: Leaves per proof (e.g., 10)
- num_zkp_batches: batch_size / zkp_batch_size (e.g., 50)
Hash Chains
Each ZKP batch has a hash chain - a Poseidon hash of all values in that batch:Operations
Initialization
initialize_state_tree
initialize_state_tree
Create state tree + output queue pair.Parameters:
height: Tree height (26, 30, 32, 40)batch_size: 10, 100, 500, 1000zkp_batch_size: 10 or 500bloom_filter_capacity: Nullifier bloom filter size
initialize_address_tree
initialize_address_tree
Create address tree with integrated queue.Parameters:
height: Tree height (26, 30, 32, 40)batch_size: 10, 100, 250, 500zkp_batch_size: 10 or 250bloom_filter_capacity: Address bloom filter size
H(0, HIGHEST_ADDRESS_PLUS_ONE)Queue Insertions
insert_into_output_queue
insert_into_output_queue
Insert compressed account hash into output queue (state trees).Returns: Leaf index for proof-by-indexUse case: Called by system program when creating compressed accounts
insert_nullifier_into_queue
insert_nullifier_into_queue
Insert nullifier into input queue (state trees).Side effects: Updates bloom filter for non-inclusion checksUse case: Called by system program when spending compressed accounts
insert_address_into_queue
insert_address_into_queue
Insert address into address queue.Side effects: Updates bloom filter for uniqueness checksUse case: Called by system program when creating new addresses
Tree Updates
update_tree_from_output_queue
update_tree_from_output_queue
Batch append with ZKP verification (state trees).ZK Proof public inputs:
old_root: Root before updatenew_root: Root after updatehash_chain: Commitment to batch valuesstart_index: First leaf index in this ZKP batch
update_tree_from_input_queue
update_tree_from_input_queue
Batch nullify/address updates with ZKP.ZK Proof public inputs:
old_root: Root before updatenew_root: Root after updatehash_chain: Commitment to batch values- For address trees:
next_index: Next available leaf index
Bloom Filters
Purpose
Bloom filters enable non-inclusion proofs while batches are filling:- State trees: Check nullifier hasn’t been used
- Address trees: Check address hasn’t been registered
Lifecycle
False Positives
Bloom filters can have false positives (claim value exists when it doesn’t). This is why they must be zeroed after the next batch is 50% full - by then, all values have been fully inserted into the tree.Root History
Trees maintain a cyclic buffer of 200 recent roots:root_index = num_insertions % 200
Rollover
When to Rollover
When a tree reaches the rollover threshold (default: 95% full):Rollover Process
Error Codes
| Code | Error | Description |
|---|---|---|
| 14301 | BatchNotReady | Batch not ready for insertion |
| 14302 | BatchAlreadyInserted | Batch already inserted into tree |
| 14303 | InvalidBatchIndex | Invalid batch index (must be 0 or 1) |
| 14304 | InvalidZkpBatchIndex | ZKP batch index out of range |
| 14305 | QueueFull | Queue at capacity, cannot insert |
| 14306 | InvalidProof | ZK proof verification failed |
| 14307 | InvalidRootIndex | Root index not in history |
| 14308 | InvalidTreeHeight | Unsupported tree height |
| 14309 | InvalidBatchSize | Invalid batch size configuration |
| 14310 | TreeIsFull | Tree reached capacity |
| 14311 | NonInclusionCheckFailed | Value exists in bloom filter |
| 14312 | BloomFilterNotZeroed | Bloom filter must be zeroed before reuse |
Usage Examples
Query Tree State
Check Non-Inclusion
Insert Values
ZKP Verification
Batch updates require zero-knowledge proofs generated by the Light Protocol prover: Prover Server:prover/server/ - Go implementation using GnarkCircuits:
prover/server/prover/v2/ - Batch append, update, address circuits
Supported batch sizes:
- State trees: 10, 500 leaves per ZKP
- Address trees: 10, 250 leaves per ZKP
Best Practices
Choose Appropriate Batch Sizes
Choose Appropriate Batch Sizes
Larger batches are more efficient but take longer to fill. For high-traffic applications, use larger batches (500). For development or low traffic, use smaller batches (10).
Monitor Rollover Threshold
Monitor Rollover Threshold
Watch
next_index relative to capacity. Create new trees before old ones fill completely.Verify Bloom Filter State
Verify Bloom Filter State
Always check bloom filters are properly zeroed before reusing batches.
Cache Tree Metadata
Cache Tree Metadata
Tree metadata changes infrequently. Cache it to avoid repeated account reads.
Resources
Source Code
View on GitHub
API Docs
Rust documentation
Account Compression Program
On-chain program documentation
Forester Service
Tree maintenance service