# RLN tree storage strategies This document describes the various methods to store an RLN membership set (sparse merkle tree). The two broad categories are, off-chain, and on-chain storage. ## On-Chain Storage This category involves storing the RLN membership set on-chain with various techniques. ### 1. Stateless storage This strategy involves using a smart contract to merely serve as a coordination layer for registrations and withdrawals. This is made possible by the usage of events emitted by the contract to signal new insertions/removals to be made by users to their local copy of the tree. **Solidity pseudocode:** ```solidity= function register(uint256 commitment) external { require(commitments[commitment] == 0, "double registration"); // there are other checks as well. commitments[commitment] = idCommitmentIndex; emit MemberRegistered(idCommitmentIndex, commitment); idCommitmentIndex += 1; } ``` #### Advantages - This seems to be the most gas efficient option, consuming about 45k - 70k gas - Cheaper to register memberships, thereby lowering barrier of entry - No race conditions, allows multiple registrations per block #### Disadvantages - Brings a lot of problems for coordination, since users have to maintain a copy of the tree locally and modify it every time there is a state change - Handling chain forks, reorgs gracefully - Handling missed insertions - A user who just wants to be able to verify proofs MUST sync the whole tree, which is non-intuitive - Complexities with having insertion time < block time of the chain ### 2. Full tree storage This strategy involves using a smart contract with a full implementation of an incremental binary tree for registrations and withdrawals. This is made possible by using `@zk-kit/imt.sol/BinaryIMT.sol`, which provides a BinaryIMT library for insertions, removals and root updates. **Solidity pseudocode:** ```solidity= BinaryIMTStorage binStorage; function register(uint256 commitment) external { require(commitments[commitment] == 0, "double registration"); // there are other checks as well. BinaryIMT.insert(binStorage, idCommitmentIndex, commitment); emit MemberRegistered(idCommitmentIndex, commitment); idCommitmentIndex += 1; } function root() public pure { return BinaryIMT.root(binStorage); } ``` #### Advantages - The merkle root is available on-chain, allowing super-light verifiers who just need to look upon the state of the contract to be able to verify proofs - Increases the barrier of entry (lower sybil count) by having registration intrinsically expensive, regardless of the staking strategy - No race conditions, allows for multiple registrations per block - No problem with coordination since the tree is stored onchain, storage can be saved on provers and verifiers #### Disadvantages - Expensive to register a membership, consuming about 800k gas ### 3. State transition proofs This strategy involves using a smart contract which serves as a coordination layer for registrations, withdrawals and updates to the merkle root without having the whole tree on-chain. This can be achieved by making use of "state transition proofs" of the merkle tree, which each user provides while registering to the membership set ```solidity= Verifier private v; uint256 public root; function register(uint256 commitment, uint256[8] proof) external { require(commitments[commitment] == 0, "double registration"); // there are other checks as well. v.verifyProof(proof); emit MemberRegistered(idCommitmentIndex, commitment); // extract root from proof provided root = extractRoot(proof); idCommitmentIndex += 1; } ``` #### Advantages - The merkle root is available on-chain, allowing super-light verifiers who just need to look upon the state of the contract to be able to verify proofs - Increases the barrier of entry (lower sybil count) by having registration intrinsically expensive, regardless of the staking strategy - No problem with coordination since the tree is stored onchain, storage can be saved on provers and verifiers #### Disadvantages - Expensive to register a membership, should consume about 400k gas - Race conditions when multiple registrations occur per block, only one will be valid, the rest will revert