Skip to main content

Overview

The account compression program (compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq) manages Merkle trees for state compression. It handles tree initialization, updates, and rollover operations. Program ID: compr6CUsB5m2jS4Y3831ztGSTnDpnKJTKS95d64XVq
Framework: Anchor
Documentation: Account Compression Program

Instruction Categories

Tree Management

Initialize and rollover Merkle trees

Queue Operations

Insert values into tree queues

Tree Updates

Batch update trees with ZK proofs

Tree Initialization

InitializeStateMerkleTreeAndNullifierQueue

Initialize state Merkle tree and nullifier queue. Anchor Method: initialize_state_merkle_tree_and_nullifier_queue
pub fn initialize_state_merkle_tree_and_nullifier_queue(
    ctx: Context<InitializeStateMerkleTreeAndNullifierQueue>,
    index: u64,
    program_owner: Option<Pubkey>,
    forester: Option<Pubkey>,
    state_merkle_tree_config: StateMerkleTreeConfig,
    nullifier_queue_config: NullifierQueueConfig,
    additional_bytes: u64,
) -> Result<()>
index
u64
Optional identifier for the tree (not enforced by program)
program_owner
Option<Pubkey>
Optional program that owns this tree (restricts which programs can use it)
forester
Option<Pubkey>
Optional dedicated forester authority (who can update the tree)
state_merkle_tree_config
StateMerkleTreeConfig
Tree configuration:
  • height: Tree height (26, 30, 32, 40)
  • batch_size: Leaves per batch (10, 100, 500, 1000)
  • zkp_batch_size: Leaves per ZK proof (10, 500)
  • bloom_filter_capacity: Nullifier bloom filter size
  • rollover_threshold: % capacity before rollover (default: 95)
  • network_fee: Optional fee per insertion
nullifier_queue_config
NullifierQueueConfig
Nullifier queue configuration (integrated input queue)
Creates: 2 accounts
  1. State Merkle tree account (tree + integrated nullifier queue)
  2. Output queue account (for new compressed accounts)

InitializeAddressMerkleTreeAndQueue

Initialize address Merkle tree. Anchor Method: initialize_address_merkle_tree_and_queue
pub fn initialize_address_merkle_tree_and_queue(
    ctx: Context<InitializeAddressMerkleTreeAndQueue>,
    index: u64,
    program_owner: Option<Pubkey>,
    forester: Option<Pubkey>,
    address_merkle_tree_config: AddressMerkleTreeConfig,
    address_queue_config: AddressQueueConfig,
) -> Result<()>
address_merkle_tree_config
AddressMerkleTreeConfig
Tree configuration:
  • height: Tree height (26, 30, 32, 40)
  • batch_size: Leaves per batch (10, 100, 250, 500)
  • zkp_batch_size: Leaves per ZK proof (10, 250)
  • bloom_filter_capacity: Address bloom filter size
  • rollover_threshold: % capacity before rollover
  • network_fee: Optional fee per insertion
Creates: 1 account
  • Address Merkle tree with integrated address queue
Initial state: Tree starts with single leaf H(0, HIGHEST_ADDRESS_PLUS_ONE)

Queue Insertion

InsertIntoQueues

Insert values into Merkle tree queues. Anchor Method: insert_into_queues
pub fn insert_into_queues(
    ctx: Context<InsertIntoQueues>,
    nullifiers: Vec<[u8; 32]>,
    new_addresses: Vec<[u8; 32]>,
    new_compressed_account_hashes: Vec<[u8; 32]>,
) -> Result<()>
nullifiers
Vec<[u8; 32]>
Nullifiers to insert into state tree input queue (spent accounts)
new_addresses
Vec<[u8; 32]>
Addresses to insert into address tree queue
new_compressed_account_hashes
Vec<[u8; 32]>
Account hashes to insert into state tree output queue
Side effects:
  • Updates bloom filters for non-inclusion checks
  • Increments batch fill counters
  • May transition batch from Fill → Full
Called by: System program during compressed account operations

Tree Updates

AppendLeavesWithProof

Batch append leaves to state tree from output queue. Anchor Method: append_leaves_with_proof
pub fn append_leaves_with_proof(
    ctx: Context<AppendLeavesWithProof>,
    zkp_batch_index: u64,
) -> Result<()>
zkp_batch_index
u64
Index of ZKP batch to process (0 to num_zkp_batches-1)
Accounts:
  • merkle_tree: State Merkle tree (mut)
  • output_queue: Output queue account (mut)
  • registered_forester_pda: Forester authorization
ZK Proof verification:
Public inputs = H(
    old_root,
    new_root,
    hash_chain,  // H(leaf_1, leaf_2, ...)
    start_index
)
Side effects:
  • Inserts leaves into tree
  • Updates current root
  • Adds old root to root history
  • Increments next_index
Called by: Forester service

UpdateAddressMerkleTree

Update address Merkle tree with new address. Anchor Method: update_address_merkle_tree
pub fn update_address_merkle_tree(
    ctx: Context<UpdateAddressMerkleTree>,
    changelog_index: u16,
    indexed_changelog_index: u16,
    value: u16,
    low_address_index: u64,
    low_address_value: [u8; 32],
    low_address_next_index: u64,
    low_address_next_value: [u8; 32],
    low_address_proof: [[u8; 32]; 16],
) -> Result<()>
Indexed Merkle Tree Update: Address trees use indexed Merkle trees to maintain sorted order:
  1. Find low leaf (closest address < new address)
  2. Verify low leaf’s Merkle proof
  3. Update low leaf to point to new address
  4. Insert new address pointing to next address
low_address_value
[u8; 32]
Address value of low leaf
low_address_next_value
[u8; 32]
Address value that low leaf currently points to
low_address_proof
[[u8; 32]; 16]
Merkle proof for low leaf
Called by: Forester service

NullifyLeaves

Batch nullify leaves from state tree input queue. Anchor Method: nullify_leaves
pub fn nullify_leaves(
    ctx: Context<NullifyLeaves>,
    zkp_batch_index: u64,
) -> Result<()>
Similar to append but for nullifiers:
  • Processes nullifier queue
  • Updates tree with spent account proofs
  • Verifies ZK proof of correct update

Rollover

RolloverStateMerkleTreeAndNullifierQueue

Create new state tree when old tree reaches capacity. Anchor Method: rollover_state_merkle_tree_and_nullifier_queue
pub fn rollover_state_merkle_tree_and_nullifier_queue(
    ctx: Context<RolloverStateMerkleTreeAndNullifierQueue>,
) -> Result<()>
Requirements:
  • Old tree ≥ rollover threshold
  • New tree accounts provided and uninitialized
  • Authority signature
Process:
  1. Validate old tree is eligible for rollover
  2. Initialize new tree with same config
  3. Mark old tree as rolled over
  4. Transfer any pending queue items to new tree
Accounts:
  • Old merkle tree (mut)
  • Old output queue (mut)
  • New merkle tree (mut, uninitialized)
  • New output queue (mut, uninitialized)
  • Authority (signer)
  • Fee payer (signer, mut)

RolloverAddressMerkleTreeAndQueue

Create new address tree when old tree reaches capacity. Anchor Method: rollover_address_merkle_tree_and_queue
pub fn rollover_address_merkle_tree_and_queue(
    ctx: Context<RolloverAddressMerkleTreeAndQueue>,
) -> Result<()>
Similar process to state tree rollover Note: Only one account to rollover (address trees don’t have separate output queues)

Program Registration

InitializeGroupAuthority

Initialize group authority account. Anchor Method: initialize_group_authority
pub fn initialize_group_authority(
    ctx: Context<InitializeGroupAuthority>,
    authority: Pubkey,
) -> Result<()>
Purpose: Groups allow multiple programs to share Merkle trees

RegisterProgramToGroup

Register program to access group’s Merkle trees. Anchor Method: register_program_to_group
pub fn register_program_to_group(
    ctx: Context<RegisterProgramToGroup>,
) -> Result<()>
Creates: RegisteredProgramPda account Purpose: Proves program is authorized to use group’s trees

DeregisterProgram

Remove program from group. Anchor Method: deregister_program Closes: RegisteredProgramPda account

Error Codes

CodeErrorDescription
6000InvalidAuthorityAuthority check failed
6001InvalidProofZK proof verification failed
6002TreeIsFullTree at capacity
6003InvalidBatchIndexInvalid ZKP batch index
6004BatchNotReadyBatch not ready for update
6005InvalidMerkleProofMerkle proof verification failed
6006InvalidTreeHeightUnsupported tree height
6007InvalidRolloverThresholdInvalid rollover threshold
6008CannotRolloverYetTree below rollover threshold
6009AlreadyRolledOverTree already rolled over
6010InvalidForesterForester not authorized

Compute Budget

Approximate compute units for operations:
OperationCompute Units
Initialize tree~50,000 CU
Insert into queue~5,000 CU
Append leaves (10)~100,000 CU
Append leaves (500)~120,000 CU
Update address tree~95,000 CU
Nullify leaves (10)~100,000 CU
Rollover~60,000 CU

Best Practices

Larger trees hold more data but take longer to fill. Consider your expected transaction volume:
  • Low traffic: Height 26 (67M leaves)
  • High traffic: Height 32 (4B leaves) or 40 (1T leaves)
Batch size affects how quickly trees can be updated:
  • Development: Use small batches (10) for faster testing
  • Production: Use larger batches (500) for efficiency
Track next_index relative to capacity. Start rollover when approaching threshold.
Programs must be registered to insert into queues. Register during deployment.
Trees don’t self-update. Run forester service or use hosted foresters to process queues.

Resources

Program Source

View on GitHub

Batched Merkle Tree Lib

Core tree implementation

Forester

Tree maintenance service

Examples

Program examples

Build docs developers (and LLMs) love