Skip to main content

CommitmentTree

The CommitmentTree contract maintains an incremental Merkle tree of deposit commitments. It stores a ring buffer of historical roots so that proofs generated against recent states remain valid even after new deposits are recorded.

Deployed address: 0xE29DD14998f6FE8e7862571c883090d14FE29475

Constants

uint256 public constant TREE_DEPTH = 20;

The Merkle tree has a fixed depth of 20, supporting up to 2^20 (1,048,576) commitment leaves.

uint256 public constant ROOT_HISTORY_SIZE = 100;

The contract maintains a ring buffer of the last 100 Merkle roots. Any proof referencing one of these roots is considered valid, providing a window for concurrent deposits and reveals.

Functions

recordCommitment

function recordCommitment(bytes32 commitment) external returns (uint256 leafIndex)

Inserts a new commitment leaf into the Merkle tree.

Parameters:

NameTypeDescription
commitmentbytes32The Pedersen commitment to record as a leaf in the tree.

Returns:

NameTypeDescription
leafIndexuint256The zero-based index of the newly inserted leaf.

Access control: Only the CommitRevealVault contract can call this function.

Behavior:

  1. Assigns the commitment to the next available leaf slot.
  2. Increments the internal commitment counter.
  3. Returns the leaf index for use in the Committed event.

Reverts if:

  • Caller is not the authorized vault.
  • Tree is full (all 2^20 leaves occupied).

updateRoot

function updateRoot(bytes32 newRoot) external

Pushes a new Merkle root into the ring buffer. Called by the off-chain operator after recomputing the tree root from the latest leaves.

Parameters:

NameTypeDescription
newRootbytes32The newly computed Merkle root.

Access control: Only the designated operator address can call this function.

Behavior:

  1. Writes newRoot into the ring buffer at position currentIndex % ROOT_HISTORY_SIZE.
  2. Advances the ring buffer index.

isKnownRoot

function isKnownRoot(bytes32 root) external view returns (bool)

Checks whether a given root exists in the root history ring buffer.

Parameters:

NameTypeDescription
rootbytes32The Merkle root to check.

Returns:

NameTypeDescription
(unnamed)booltrue if the root is found in the ring buffer, false otherwise.

Usage: Called by CommitRevealVault.reveal() to verify that the proof's Merkle root is recent and valid.


getLastRoot

function getLastRoot() external view returns (bytes32)

Returns the most recently stored Merkle root.

Returns:

NameTypeDescription
(unnamed)bytes32The latest Merkle root in the ring buffer.

getCommitmentCount

function getCommitmentCount() external view returns (uint256)

Returns the total number of commitments recorded in the tree.

Returns:

NameTypeDescription
(unnamed)uint256The number of leaves inserted so far (0-indexed next slot).

Storage Layout

SlotTypeDescription
rootsbytes32[ROOT_HISTORY_SIZE]Ring buffer of historical Merkle roots.
currentRootIndexuint256Current write position in the ring buffer.
nextLeafIndexuint256Index of the next leaf to be inserted.
commitmentsmapping(uint256 => bytes32)Maps leaf index to commitment value.

Ring Buffer Mechanics

The root history ring buffer works as follows:

Index:  0   1   2   ... 99  0   1   ...
Root: R0 R1 R2 ... R99 R100 R101 ...

When a new root is stored, it overwrites the oldest entry once the buffer wraps. This means:

  • The most recent 100 roots are always available for proof verification.
  • Proofs generated against a root older than 100 updates will fail isKnownRoot and cannot be used.
  • Users should generate and submit proofs promptly to avoid root expiration.

Usage Example

// Check if a root is valid (called internally by CommitRevealVault)
bool valid = CommitmentTree(treeAddress).isKnownRoot(proofRoot);
require(valid, "Unknown merkle root");

// Query the current state
uint256 totalDeposits = CommitmentTree(treeAddress).getCommitmentCount();
bytes32 latestRoot = CommitmentTree(treeAddress).getLastRoot();

Integration Notes

  • The tree uses Poseidon hashing (via PoseidonT3) for internal node computation, matching the ZK circuit's hash function.
  • recordCommitment only stores the leaf; it does not recompute the Merkle root on-chain. Root updates are performed by the operator via updateRoot after off-chain tree recomputation. This design saves gas while maintaining verifiability.
  • The operator must call updateRoot before reveals referencing the newly inserted leaves can succeed.