From 0c10c76d62d23f0a0b0ac672a221ec2ad217e747 Mon Sep 17 00:00:00 2001
From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com>
Date: Thu, 11 Jul 2024 07:46:21 +0000
Subject: [PATCH] Complete porting for MACI gatekeepers (#31)
## Description
This PR adds the `GitcoinPassportExcubia`,
`ZKEdDSAEventTicketPCDExcubia` (prev Zupass) and, `HatsExcubia`. During
the porting process, certain interfaces were extended and controls and
methods generalised. The code coverage is 100%.
Also, this PR introduces the concept of `trait` aka the specific type of
an Excubia contract. For example, `SemaphoreExcubia` has trait
`Semaphore` and so on. This will make easy to discriminate and query
multiple Excubiae sharing the same characteristics.
## Related Issue(s)
closes #18
## Checklist
- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] My changes generate no new warnings
- [x] I have run `yarn format` and `yarn compile` without getting any
errors
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
---
excubiae/README.md | 85 -----
excubiae/src/Excubia.sol | 59 ---
excubiae/src/IExcubia.sol | 46 ---
excubiae/src/extensions/EASExcubia.sol | 80 ----
excubiae/src/extensions/ERC721Excubia.sol | 57 ---
excubiae/src/extensions/FreeForAllExcubia.sol | 37 --
excubiae/src/extensions/SemaphoreExcubia.sol | 75 ----
imt/README.md | 60 ---
imt/src/BinaryIMT.sol | 52 ---
imt/src/Constants.sol | 5 -
imt/src/InternalBinaryIMT.sol | 285 ---------------
imt/src/InternalQuinaryIMT.sol | 241 ------------
imt/src/LICENSE | 21 --
imt/src/QuinaryIMT.sol | 44 ---
lazy-imt/README.md | 60 ---
lazy-imt/src/Constants.sol | 5 -
lazy-imt/src/InternalLazyIMT.sol | 265 --------------
lazy-imt/src/LazyIMT.sol | 52 ---
lazytower/README.md | 138 -------
lazytower/src/LazyTowerHashChain.sol | 100 -----
lean-imt/README.md | 59 ---
lean-imt/src/Constants.sol | 4 -
lean-imt/src/InternalLeanIMT.sol | 342 ------------------
lean-imt/src/LeanIMT.sol | 42 ---
24 files changed, 2214 deletions(-)
delete mode 100644 excubiae/README.md
delete mode 100644 excubiae/src/Excubia.sol
delete mode 100644 excubiae/src/IExcubia.sol
delete mode 100644 excubiae/src/extensions/EASExcubia.sol
delete mode 100644 excubiae/src/extensions/ERC721Excubia.sol
delete mode 100644 excubiae/src/extensions/FreeForAllExcubia.sol
delete mode 100644 excubiae/src/extensions/SemaphoreExcubia.sol
delete mode 100644 imt/README.md
delete mode 100644 imt/src/BinaryIMT.sol
delete mode 100644 imt/src/Constants.sol
delete mode 100644 imt/src/InternalBinaryIMT.sol
delete mode 100644 imt/src/InternalQuinaryIMT.sol
delete mode 100644 imt/src/LICENSE
delete mode 100644 imt/src/QuinaryIMT.sol
delete mode 100644 lazy-imt/README.md
delete mode 100644 lazy-imt/src/Constants.sol
delete mode 100644 lazy-imt/src/InternalLazyIMT.sol
delete mode 100644 lazy-imt/src/LazyIMT.sol
delete mode 100644 lazytower/README.md
delete mode 100644 lazytower/src/LazyTowerHashChain.sol
delete mode 100644 lean-imt/README.md
delete mode 100644 lean-imt/src/Constants.sol
delete mode 100644 lean-imt/src/InternalLeanIMT.sol
delete mode 100644 lean-imt/src/LeanIMT.sol
diff --git a/excubiae/README.md b/excubiae/README.md
deleted file mode 100644
index 71a4bac..0000000
--- a/excubiae/README.md
+++ /dev/null
@@ -1,85 +0,0 @@
-
-
- Excubiae
-
- A flexible and modular framework for general-purpose on-chain gatekeepers.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-> [!NOTE]
-> This library is experimental and untested yet - use at your own discretion...
-
-Excubiae is a generalized framework for on-chain gatekeepers that allows developers to define custom access control mechanisms using different on-chain credentials. By abstracting the gatekeeper logic, excubiae provides a reusable and composable solution for securing decentralised applications. This package provides a pre-defined set of specific excubia (_extensions_) for credentials based on different protocols.
-
-## 🛠 Install
-
-### npm or yarn
-
-Install the ` @zk-kit/excubiae` package with npm:
-
-```bash
-npm i @zk-kit/excubiae --save
-```
-
-or yarn:
-
-```bash
-yarn add @zk-kit/excubiae
-```
-
-## 📜 Usage
-
-To build your own Excubia:
-
-1. Inherit from the [Excubia](./Excubia.sol) abstract contract that conforms to the [IExcubia](./IExcubia.sol) interface.
-2. Implement the `_check()` and `_pass()` methods logic defining your own checks to prevent unwanted access as sybils or avoid to pass the gate twice with the same data / identity.
-
-```solidity
-// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.0;
-
-import { Excubia } from "excubiae/contracts/Excubia.sol";
-
-contract MyExcubia is Excubia {
- // ...
-
- function _pass(address passerby, bytes calldata data) internal override {
- // Implement your logic to prevent unwanted access here.
- }
-
- function _check(address passerby, bytes calldata data) internal view override returns (bool) {
- // Implement custom access control logic here.
-
- return true;
- }
-
- // ...
-}
-```
-
-Please see the [extensions](./extensions/) folder for more complex reference implementations and the [test contracts](./test) folder for guidance on using the libraries.
diff --git a/excubiae/src/Excubia.sol b/excubiae/src/Excubia.sol
deleted file mode 100644
index a91a55a..0000000
--- a/excubiae/src/Excubia.sol
+++ /dev/null
@@ -1,59 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.0;
-
-import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
-import {IExcubia} from "./IExcubia.sol";
-
-/// @title Excubia.
-/// @notice Abstract base contract which can be extended to implement a specific excubia.
-/// @dev Inherit from this contract and implement the `_pass` & `_check` methods to define
-/// your custom gatekeeping logic.
-abstract contract Excubia is IExcubia, Ownable(msg.sender) {
- /// @notice The excubia-protected contract address.
- /// @dev The gate can be any contract address that requires a prior check to enable logic.
- /// For example, the gate is a Semaphore group that requires the passerby
- /// to meet certain criteria before joining.
- address public gate;
-
- /// @dev Modifier to restrict function calls to only from the gate address.
- modifier onlyGate() {
- if (msg.sender != gate) revert GateOnly();
- _;
- }
-
- /// @inheritdoc IExcubia
- function setGate(address _gate) public virtual onlyOwner {
- if (_gate == address(0)) revert ZeroAddress();
- if (gate != address(0)) revert GateAlreadySet();
-
- gate = _gate;
-
- emit GateSet(_gate);
- }
-
- /// @inheritdoc IExcubia
- function pass(address passerby, bytes calldata data) external onlyGate {
- _pass(passerby, data);
- }
-
- /// @inheritdoc IExcubia
- function check(address passerby, bytes calldata data) external view {
- _check(passerby, data);
- }
-
- /// @notice Internal function to enforce the custom gate passing logic.
- /// @dev Calls the `_check` internal logic and emits the relative event if successful.
- /// @param passerby The address of the entity attempting to pass the gate.
- /// @param data Additional data required for the check (e.g., encoded token identifier).
- function _pass(address passerby, bytes calldata data) internal virtual {
- _check(passerby, data);
-
- emit GatePassed(passerby, gate);
- }
-
- /// @notice Internal function to define the custom gate protection logic.
- /// @dev Custom logic to determine if the passerby can pass the gate.
- /// @param passerby The address of the entity attempting to pass the gate.
- /// @param data Additional data that may be required for the check.
- function _check(address passerby, bytes calldata data) internal view virtual {}
-}
diff --git a/excubiae/src/IExcubia.sol b/excubiae/src/IExcubia.sol
deleted file mode 100644
index 86a4a80..0000000
--- a/excubiae/src/IExcubia.sol
+++ /dev/null
@@ -1,46 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.0;
-
-/// @title IExcubia.
-/// @notice Excubia contract interface.
-interface IExcubia {
- /// @notice Event emitted when someone passes the gate check.
- /// @param passerby The address of those who have successfully passed the check.
- /// @param gate The address of the excubia-protected contract address.
- event GatePassed(address indexed passerby, address indexed gate);
-
- /// @notice Event emitted when the gate address is set.
- /// @param gate The address of the contract set as the gate.
- event GateSet(address indexed gate);
-
- /// @notice Error thrown when an address equal to zero is given.
- error ZeroAddress();
-
- /// @notice Error thrown when the gate address is not set.
- error GateNotSet();
-
- /// @notice Error thrown when the callee is not the gate contract.
- error GateOnly();
-
- /// @notice Error thrown when the gate address has been already set.
- error GateAlreadySet();
-
- /// @notice Error thrown when the passerby has already passed the gate.
- error AlreadyPassed();
-
- /// @notice Sets the gate address.
- /// @dev Only the owner can set the destination gate address.
- /// @param _gate The address of the contract to be set as the gate.
- function setGate(address _gate) external;
-
- /// @notice Enforces the custom gate passing logic.
- /// @dev Must call the `check` to handle the logic of checking passerby for specific gate.
- /// @param passerby The address of the entity attempting to pass the gate.
- /// @param data Additional data required for the check (e.g., encoded token identifier).
- function pass(address passerby, bytes calldata data) external;
-
- /// @dev Defines the custom gate protection logic.
- /// @param passerby The address of the entity attempting to pass the gate.
- /// @param data Additional data that may be required for the check.
- function check(address passerby, bytes calldata data) external view;
-}
diff --git a/excubiae/src/extensions/EASExcubia.sol b/excubiae/src/extensions/EASExcubia.sol
deleted file mode 100644
index afe5692..0000000
--- a/excubiae/src/extensions/EASExcubia.sol
+++ /dev/null
@@ -1,80 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.0;
-
-import {Excubia} from "../Excubia.sol";
-import {IEAS} from "@ethereum-attestation-service/eas-contracts/contracts/IEAS.sol";
-import {Attestation} from "@ethereum-attestation-service/eas-contracts/contracts/Common.sol";
-
-/// @title EAS Excubia Contract.
-/// @notice This contract extends the Excubia contract to integrate with the Ethereum Attestation Service (EAS).
-/// This contract checks an EAS attestation to permit access through the gate.
-/// @dev The contract uses a specific attestation schema & attester to admit the recipient of the attestation.
-contract EASExcubia is Excubia {
- /// @notice The Ethereum Attestation Service contract interface.
- IEAS public immutable EAS;
- /// @notice The specific schema ID that attestations must match to pass the gate.
- bytes32 public immutable SCHEMA;
- /// @notice The trusted attester address whose attestations are considered
- /// the only ones valid to pass the gate.
- address public immutable ATTESTER;
-
- /// @notice Mapping to track which attestations have been registered by the contract to
- /// avoid pass the gate twice with the same attestation.
- mapping(bytes32 => bool) public registeredAttestations;
-
- /// @notice Error thrown when the attestation does not match the designed schema.
- error UnexpectedSchema();
-
- /// @notice Error thrown when the attestation does not match the designed trusted attester.
- error UnexpectedAttester();
-
- /// @notice Error thrown when the attestation does not match the passerby as recipient.
- error UnexpectedRecipient();
-
- /// @notice Error thrown when the attestation has been revoked.
- error RevokedAttestation();
-
- /// @notice Constructor to initialize with target EAS contract with specific attester and schema.
- /// @param _eas The address of the EAS contract.
- /// @param _attester The address of the trusted attester.
- /// @param _schema The schema ID that attestations must match.
- constructor(address _eas, address _attester, bytes32 _schema) {
- if (_eas == address(0) || _attester == address(0)) revert ZeroAddress();
-
- EAS = IEAS(_eas);
- ATTESTER = _attester;
- SCHEMA = _schema;
- }
-
- /// @notice Internal function to handle the passing logic with check.
- /// @dev Calls the parent `_pass` function and registers the attestation to avoid pass the gate twice.
- /// @param passerby The address of the entity attempting to pass the gate.
- /// @param data Additional data required for the check (e.g., encoded attestation ID).
- function _pass(address passerby, bytes calldata data) internal override {
- bytes32 attestationId = abi.decode(data, (bytes32));
-
- // Avoiding passing the gate twice using the same attestation.
- if (registeredAttestations[attestationId]) revert AlreadyPassed();
-
- super._pass(passerby, data);
-
- registeredAttestations[attestationId] = true;
- }
-
- /// @notice Internal function to handle the gate protection (attestation check) logic.
- /// @dev Checks if the attestation matches the schema, attester, recipient, and is not revoked.
- /// @param passerby The address of the entity attempting to pass the gate.
- /// @param data Additional data required for the check (e.g., encoded attestation ID).
- function _check(address passerby, bytes calldata data) internal view override {
- super._check(passerby, data);
-
- bytes32 attestationId = abi.decode(data, (bytes32));
-
- Attestation memory attestation = EAS.getAttestation(attestationId);
-
- if (attestation.schema != SCHEMA) revert UnexpectedSchema();
- if (attestation.attester != ATTESTER) revert UnexpectedAttester();
- if (attestation.recipient != passerby) revert UnexpectedRecipient();
- if (attestation.revocationTime != 0) revert RevokedAttestation();
- }
-}
diff --git a/excubiae/src/extensions/ERC721Excubia.sol b/excubiae/src/extensions/ERC721Excubia.sol
deleted file mode 100644
index 8e93dc7..0000000
--- a/excubiae/src/extensions/ERC721Excubia.sol
+++ /dev/null
@@ -1,57 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.0;
-
-import {Excubia} from "../Excubia.sol";
-import {IERC721} from "@openzeppelin/contracts/interfaces/IERC721.sol";
-
-/// @title ERC721 Excubia Contract.
-/// @notice This contract extends the Excubia contract to integrate with an ERC721 token.
-/// This contract checks the ownership of an ERC721 token to permit access through the gate.
-/// @dev The contract refers to a contract implementing the ERC721 standard to admit the owner of the token.
-contract ERC721Excubia is Excubia {
- /// @notice The ERC721 token contract interface.
- IERC721 public immutable NFT;
-
- /// @notice Mapping to track which token IDs have been registered by the contract to
- /// avoid passing the gate twice with the same token ID.
- mapping(uint256 => bool) public registeredTokenIds;
-
- /// @notice Error thrown when the passerby is not the owner of the token.
- error UnexpectedTokenOwner();
-
- /// @notice Constructor to initialize with target ERC721 contract.
- /// @param _erc721 The address of the ERC721 contract.
- constructor(address _erc721) {
- if (_erc721 == address(0)) revert ZeroAddress();
-
- NFT = IERC721(_erc721);
- }
-
- /// @notice Internal function to handle the passing logic with check.
- /// @dev Calls the parent `_pass` function and registers the NFT ID to avoid passing the gate twice.
- /// @param passerby The address of the entity attempting to pass the gate.
- /// @param data Additional data required for the check (e.g., encoded token ID).
- function _pass(address passerby, bytes calldata data) internal override {
- uint256 tokenId = abi.decode(data, (uint256));
-
- // Avoiding passing the gate twice with the same token ID.
- if (registeredTokenIds[tokenId]) revert AlreadyPassed();
-
- super._pass(passerby, data);
-
- registeredTokenIds[tokenId] = true;
- }
-
- /// @notice Internal function to handle the gate protection (token ownership check) logic.
- /// @dev Checks if the passerby is the owner of the token.
- /// @param passerby The address of the entity attempting to pass the gate.
- /// @param data Additional data required for the check (e.g., encoded token ID).
- function _check(address passerby, bytes calldata data) internal view override {
- super._check(passerby, data);
-
- uint256 tokenId = abi.decode(data, (uint256));
-
- // Check if the user owns the token.
- if (!(NFT.ownerOf(tokenId) == passerby)) revert UnexpectedTokenOwner();
- }
-}
diff --git a/excubiae/src/extensions/FreeForAllExcubia.sol b/excubiae/src/extensions/FreeForAllExcubia.sol
deleted file mode 100644
index 936566d..0000000
--- a/excubiae/src/extensions/FreeForAllExcubia.sol
+++ /dev/null
@@ -1,37 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.0;
-
-import {Excubia} from "../Excubia.sol";
-
-/// @title FreeForAll Excubia Contract.
-/// @notice This contract extends the Excubia contract to allow free access through the gate.
-/// This contract does not perform any checks and allows any passerby to pass the gate.
-/// @dev The contract overrides the `_check` function to always return true.
-contract FreeForAllExcubia is Excubia {
- /// @notice Constructor for the FreeForAllExcubia contract.
- constructor() {}
-
- /// @notice Mapping to track already registered passersby.
- mapping(address => bool) public registeredPassersby;
-
- /// @notice Internal function to handle the gate passing logic.
- /// @dev This function calls the parent `_pass` function and then tracks the passerby.
- /// @param passerby The address of the entity passing the gate.
- /// @param data Additional data required for the pass (not used in this implementation).
- function _pass(address passerby, bytes calldata data) internal override {
- // Avoiding passing the gate twice with the same address.
- if (registeredPassersby[passerby]) revert AlreadyPassed();
-
- super._pass(passerby, data);
-
- registeredPassersby[passerby] = true;
- }
-
- /// @notice Internal function to handle the gate protection logic.
- /// @dev This function always returns true, signaling that any passerby is able to pass the gate.
- /// @param passerby The address of the entity attempting to pass the gate.
- /// @param data Additional data required for the check (e.g., encoded attestation ID).
- function _check(address passerby, bytes calldata data) internal view override {
- super._check(passerby, data);
- }
-}
diff --git a/excubiae/src/extensions/SemaphoreExcubia.sol b/excubiae/src/extensions/SemaphoreExcubia.sol
deleted file mode 100644
index bd43112..0000000
--- a/excubiae/src/extensions/SemaphoreExcubia.sol
+++ /dev/null
@@ -1,75 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity >=0.8.0;
-
-import {Excubia} from "../Excubia.sol";
-import {ISemaphore} from "@semaphore-protocol/contracts/interfaces/ISemaphore.sol";
-
-/// @title Semaphore Excubia Contract
-/// @notice This contract extends the Excubia contract to integrate with the Semaphore protocol.
-/// It verifies the passerby Semaphore group membership proofs to grant access through the gate.
-/// @dev To allow only specific Semaphore identities from a group, the contract stores the specific group identifier.
-/// To avoid identities from passing twice, nullifiers are stored upon successful verification of the proofs.
-contract SemaphoreExcubia is Excubia {
- /// @notice The Semaphore contract interface.
- ISemaphore public immutable SEMAPHORE;
- /// @notice The specific group identifier that proofs must match to pass the gate.
- /// @dev Used as a `scope` to ensure consistency during proof membership verification.
- uint256 public immutable GROUP_ID;
-
- /// @notice Mapping to track which nullifiers have been used to avoid passing the
- /// gate twice using the same Semaphore identity.
- /// @dev The nullifier is derived from the hash of the secret and group identifier,
- /// ensuring that the same identity cannot be registered twice for the same group.
- mapping(uint256 => bool) public passedNullifiers;
-
- /// @notice Error thrown when the group identifier does not match the expected one.
- error InvalidGroup();
-
- /// @notice Error thrown when the proof is invalid.
- error InvalidProof();
-
- /// @notice Error thrown when the proof scope does not match the expected group identifier.
- error UnexpectedScope();
-
- /// @notice Constructor to initialize with target Semaphore contract and specific group identifier.
- /// @param _semaphore The address of the Semaphore contract.
- /// @param _groupId The group identifier that proofs must match.
- constructor(address _semaphore, uint256 _groupId) {
- if (_semaphore == address(0)) revert ZeroAddress();
-
- SEMAPHORE = ISemaphore(_semaphore);
-
- if (ISemaphore(_semaphore).groupCounter() <= _groupId) revert InvalidGroup();
-
- GROUP_ID = _groupId;
- }
-
- /// @notice Internal function to handle the passing logic with check.
- /// @dev Calls the parent `_pass` function and registers the nullifier to avoid passing the gate twice.
- /// @param passerby The address of the entity attempting to pass the gate.
- /// @param data Additional data required for the check (ie., encoded Semaphore proof).
- function _pass(address passerby, bytes calldata data) internal override {
- ISemaphore.SemaphoreProof memory proof = abi.decode(data, (ISemaphore.SemaphoreProof));
-
- // Avoiding passing the gate twice using the same nullifier.
- if (passedNullifiers[proof.nullifier]) revert AlreadyPassed();
-
- super._pass(passerby, data);
-
- passedNullifiers[proof.nullifier] = true;
- }
-
- /// @notice Internal function to handle the gate protection (proof check) logic.
- /// @dev Checks if the proof matches the group ID, scope, and is valid.
- /// @param passerby The address of the entity attempting to pass the gate.
- /// @param data Additional data required for the check (i.e., encoded Semaphore proof).
- function _check(address passerby, bytes calldata data) internal view override {
- super._check(passerby, data);
-
- ISemaphore.SemaphoreProof memory proof = abi.decode(data, (ISemaphore.SemaphoreProof));
-
- if (GROUP_ID != proof.scope) revert UnexpectedScope();
-
- if (!SEMAPHORE.verifyProof(GROUP_ID, proof)) revert InvalidProof();
- }
-}
diff --git a/imt/README.md b/imt/README.md
deleted file mode 100644
index 737ec58..0000000
--- a/imt/README.md
+++ /dev/null
@@ -1,60 +0,0 @@
-
-
- Incremental Merkle Tree (Solidity)
-
- Incremental Merkle tree implementation in Solidity.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-> [!WARNING]
-> These library has **not** been audited.
-
-> [!WARNING]
-> If you are looking for the first version of this package, please visit this [link](https://github.com/privacy-scaling-explorations/zk-kit/tree/imt-v1/packages/incremental-merkle-tree.sol).
-
----
-
-## 🛠 Install
-
-### npm or yarn
-
-Install the `@zk-kit/imt.sol` package with npm:
-
-```bash
-npm i @zk-kit/imt.sol --save
-```
-
-or yarn:
-
-```bash
-yarn add @zk-kit/imt.sol
-```
-
-## 📜 Usage
-
-Please, see the [test contracts](./test) for guidance on utilizing the libraries.
diff --git a/imt/src/BinaryIMT.sol b/imt/src/BinaryIMT.sol
deleted file mode 100644
index e14a16f..0000000
--- a/imt/src/BinaryIMT.sol
+++ /dev/null
@@ -1,52 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
-
-import {InternalBinaryIMT, BinaryIMTData} from "./InternalBinaryIMT.sol";
-
-library BinaryIMT {
- using InternalBinaryIMT for *;
-
- function defaultZero(uint256 index) public pure returns (uint256) {
- return InternalBinaryIMT._defaultZero(index);
- }
-
- function init(BinaryIMTData storage self, uint256 depth, uint256 zero) public {
- InternalBinaryIMT._init(self, depth, zero);
- }
-
- function initWithDefaultZeroes(BinaryIMTData storage self, uint256 depth) public {
- InternalBinaryIMT._initWithDefaultZeroes(self, depth);
- }
-
- function insert(BinaryIMTData storage self, uint256 leaf) public returns (uint256) {
- return InternalBinaryIMT._insert(self, leaf);
- }
-
- function update(
- BinaryIMTData storage self,
- uint256 leaf,
- uint256 newLeaf,
- uint256[] calldata proofSiblings,
- uint8[] calldata proofPathIndices
- ) public {
- InternalBinaryIMT._update(self, leaf, newLeaf, proofSiblings, proofPathIndices);
- }
-
- function remove(
- BinaryIMTData storage self,
- uint256 leaf,
- uint256[] calldata proofSiblings,
- uint8[] calldata proofPathIndices
- ) public {
- InternalBinaryIMT._remove(self, leaf, proofSiblings, proofPathIndices);
- }
-
- function verify(
- BinaryIMTData storage self,
- uint256 leaf,
- uint256[] calldata proofSiblings,
- uint8[] calldata proofPathIndices
- ) private view returns (bool) {
- return InternalBinaryIMT._verify(self, leaf, proofSiblings, proofPathIndices);
- }
-}
diff --git a/imt/src/Constants.sol b/imt/src/Constants.sol
deleted file mode 100644
index 9f3c455..0000000
--- a/imt/src/Constants.sol
+++ /dev/null
@@ -1,5 +0,0 @@
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.4;
-
-uint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
-uint8 constant MAX_DEPTH = 32;
diff --git a/imt/src/InternalBinaryIMT.sol b/imt/src/InternalBinaryIMT.sol
deleted file mode 100644
index 98f4512..0000000
--- a/imt/src/InternalBinaryIMT.sol
+++ /dev/null
@@ -1,285 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
-
-import {PoseidonT3} from "poseidon-solidity/PoseidonT3.sol";
-import {SNARK_SCALAR_FIELD, MAX_DEPTH} from "./Constants.sol";
-
-// Each incremental tree has certain properties and data that will
-// be used to add new leaves.
-struct BinaryIMTData {
- uint256 depth; // Depth of the tree (levels - 1).
- uint256 root; // Root hash of the tree.
- uint256 numberOfLeaves; // Number of leaves of the tree.
- mapping(uint256 => uint256) zeroes; // Zero hashes used for empty nodes (level -> zero hash).
- // The nodes of the subtrees used in the last addition of a leaf (level -> [left node, right node]).
- mapping(uint256 => uint256[2]) lastSubtrees; // Caching these values is essential to efficient appends.
- bool useDefaultZeroes;
-}
-
-error ValueGreaterThanSnarkScalarField();
-error DepthNotSupported();
-error WrongDefaultZeroIndex();
-error TreeIsFull();
-error NewLeafCannotEqualOldLeaf();
-error LeafDoesNotExist();
-error LeafIndexOutOfRange();
-error WrongMerkleProofPath();
-
-/// @title Incremental binary Merkle tree.
-/// @dev The incremental tree allows to calculate the root hash each time a leaf is added, ensuring
-/// the integrity of the tree.
-library InternalBinaryIMT {
- uint256 internal constant Z_0 = 0;
- uint256 internal constant Z_1 = 14744269619966411208579211824598458697587494354926760081771325075741142829156;
- uint256 internal constant Z_2 = 7423237065226347324353380772367382631490014989348495481811164164159255474657;
- uint256 internal constant Z_3 = 11286972368698509976183087595462810875513684078608517520839298933882497716792;
- uint256 internal constant Z_4 = 3607627140608796879659380071776844901612302623152076817094415224584923813162;
- uint256 internal constant Z_5 = 19712377064642672829441595136074946683621277828620209496774504837737984048981;
- uint256 internal constant Z_6 = 20775607673010627194014556968476266066927294572720319469184847051418138353016;
- uint256 internal constant Z_7 = 3396914609616007258851405644437304192397291162432396347162513310381425243293;
- uint256 internal constant Z_8 = 21551820661461729022865262380882070649935529853313286572328683688269863701601;
- uint256 internal constant Z_9 = 6573136701248752079028194407151022595060682063033565181951145966236778420039;
- uint256 internal constant Z_10 = 12413880268183407374852357075976609371175688755676981206018884971008854919922;
- uint256 internal constant Z_11 = 14271763308400718165336499097156975241954733520325982997864342600795471836726;
- uint256 internal constant Z_12 = 20066985985293572387227381049700832219069292839614107140851619262827735677018;
- uint256 internal constant Z_13 = 9394776414966240069580838672673694685292165040808226440647796406499139370960;
- uint256 internal constant Z_14 = 11331146992410411304059858900317123658895005918277453009197229807340014528524;
- uint256 internal constant Z_15 = 15819538789928229930262697811477882737253464456578333862691129291651619515538;
- uint256 internal constant Z_16 = 19217088683336594659449020493828377907203207941212636669271704950158751593251;
- uint256 internal constant Z_17 = 21035245323335827719745544373081896983162834604456827698288649288827293579666;
- uint256 internal constant Z_18 = 6939770416153240137322503476966641397417391950902474480970945462551409848591;
- uint256 internal constant Z_19 = 10941962436777715901943463195175331263348098796018438960955633645115732864202;
- uint256 internal constant Z_20 = 15019797232609675441998260052101280400536945603062888308240081994073687793470;
- uint256 internal constant Z_21 = 11702828337982203149177882813338547876343922920234831094975924378932809409969;
- uint256 internal constant Z_22 = 11217067736778784455593535811108456786943573747466706329920902520905755780395;
- uint256 internal constant Z_23 = 16072238744996205792852194127671441602062027943016727953216607508365787157389;
- uint256 internal constant Z_24 = 17681057402012993898104192736393849603097507831571622013521167331642182653248;
- uint256 internal constant Z_25 = 21694045479371014653083846597424257852691458318143380497809004364947786214945;
- uint256 internal constant Z_26 = 8163447297445169709687354538480474434591144168767135863541048304198280615192;
- uint256 internal constant Z_27 = 14081762237856300239452543304351251708585712948734528663957353575674639038357;
- uint256 internal constant Z_28 = 16619959921569409661790279042024627172199214148318086837362003702249041851090;
- uint256 internal constant Z_29 = 7022159125197495734384997711896547675021391130223237843255817587255104160365;
- uint256 internal constant Z_30 = 4114686047564160449611603615418567457008101555090703535405891656262658644463;
- uint256 internal constant Z_31 = 12549363297364877722388257367377629555213421373705596078299904496781819142130;
- uint256 internal constant Z_32 = 21443572485391568159800782191812935835534334817699172242223315142338162256601;
-
- function _defaultZero(uint256 index) internal pure returns (uint256) {
- if (index == 0) return Z_0;
- if (index == 1) return Z_1;
- if (index == 2) return Z_2;
- if (index == 3) return Z_3;
- if (index == 4) return Z_4;
- if (index == 5) return Z_5;
- if (index == 6) return Z_6;
- if (index == 7) return Z_7;
- if (index == 8) return Z_8;
- if (index == 9) return Z_9;
- if (index == 10) return Z_10;
- if (index == 11) return Z_11;
- if (index == 12) return Z_12;
- if (index == 13) return Z_13;
- if (index == 14) return Z_14;
- if (index == 15) return Z_15;
- if (index == 16) return Z_16;
- if (index == 17) return Z_17;
- if (index == 18) return Z_18;
- if (index == 19) return Z_19;
- if (index == 20) return Z_20;
- if (index == 21) return Z_21;
- if (index == 22) return Z_22;
- if (index == 23) return Z_23;
- if (index == 24) return Z_24;
- if (index == 25) return Z_25;
- if (index == 26) return Z_26;
- if (index == 27) return Z_27;
- if (index == 28) return Z_28;
- if (index == 29) return Z_29;
- if (index == 30) return Z_30;
- if (index == 31) return Z_31;
- if (index == 32) return Z_32;
- revert WrongDefaultZeroIndex();
- }
-
- /// @dev Initializes a tree.
- /// @param self: Tree data.
- /// @param depth: Depth of the tree.
- /// @param zero: Zero value to be used.
- function _init(BinaryIMTData storage self, uint256 depth, uint256 zero) internal {
- if (zero >= SNARK_SCALAR_FIELD) {
- revert ValueGreaterThanSnarkScalarField();
- } else if (depth <= 0 || depth > MAX_DEPTH) {
- revert DepthNotSupported();
- }
-
- self.depth = depth;
-
- for (uint8 i = 0; i < depth;) {
- self.zeroes[i] = zero;
- zero = PoseidonT3.hash([zero, zero]);
-
- unchecked {
- ++i;
- }
- }
-
- self.root = zero;
- }
-
- function _initWithDefaultZeroes(BinaryIMTData storage self, uint256 depth) internal {
- if (depth <= 0 || depth > MAX_DEPTH) {
- revert DepthNotSupported();
- }
-
- self.depth = depth;
- self.useDefaultZeroes = true;
-
- self.root = _defaultZero(depth);
- }
-
- /// @dev Inserts a leaf in the tree.
- /// @param self: Tree data.
- /// @param leaf: Leaf to be inserted.
- function _insert(BinaryIMTData storage self, uint256 leaf) internal returns (uint256) {
- uint256 depth = self.depth;
-
- if (leaf >= SNARK_SCALAR_FIELD) {
- revert ValueGreaterThanSnarkScalarField();
- } else if (self.numberOfLeaves >= 2 ** depth) {
- revert TreeIsFull();
- }
-
- uint256 index = self.numberOfLeaves;
- uint256 hash = leaf;
- bool useDefaultZeroes = self.useDefaultZeroes;
-
- for (uint8 i = 0; i < depth;) {
- if (index & 1 == 0) {
- self.lastSubtrees[i] = [hash, useDefaultZeroes ? _defaultZero(i) : self.zeroes[i]];
- } else {
- self.lastSubtrees[i][1] = hash;
- }
-
- hash = PoseidonT3.hash(self.lastSubtrees[i]);
- index >>= 1;
-
- unchecked {
- ++i;
- }
- }
-
- self.root = hash;
- self.numberOfLeaves += 1;
- return hash;
- }
-
- /// @dev Updates a leaf in the tree.
- /// @param self: Tree data.
- /// @param leaf: Leaf to be updated.
- /// @param newLeaf: New leaf.
- /// @param proofSiblings: Array of the sibling nodes of the proof of membership.
- /// @param proofPathIndices: Path of the proof of membership.
- function _update(
- BinaryIMTData storage self,
- uint256 leaf,
- uint256 newLeaf,
- uint256[] calldata proofSiblings,
- uint8[] calldata proofPathIndices
- ) internal {
- if (newLeaf == leaf) {
- revert NewLeafCannotEqualOldLeaf();
- } else if (newLeaf >= SNARK_SCALAR_FIELD) {
- revert ValueGreaterThanSnarkScalarField();
- } else if (!_verify(self, leaf, proofSiblings, proofPathIndices)) {
- revert LeafDoesNotExist();
- }
-
- uint256 depth = self.depth;
- uint256 hash = newLeaf;
- uint256 updateIndex;
-
- for (uint8 i = 0; i < depth;) {
- updateIndex |= uint256(proofPathIndices[i]) << uint256(i);
-
- if (proofPathIndices[i] == 0) {
- if (proofSiblings[i] == self.lastSubtrees[i][1]) {
- self.lastSubtrees[i][0] = hash;
- }
-
- hash = PoseidonT3.hash([hash, proofSiblings[i]]);
- } else {
- if (proofSiblings[i] == self.lastSubtrees[i][0]) {
- self.lastSubtrees[i][1] = hash;
- }
-
- hash = PoseidonT3.hash([proofSiblings[i], hash]);
- }
-
- unchecked {
- ++i;
- }
- }
-
- if (updateIndex >= self.numberOfLeaves) {
- revert LeafIndexOutOfRange();
- }
-
- self.root = hash;
- }
-
- /// @dev Removes a leaf from the tree.
- /// @param self: Tree data.
- /// @param leaf: Leaf to be removed.
- /// @param proofSiblings: Array of the sibling nodes of the proof of membership.
- /// @param proofPathIndices: Path of the proof of membership.
- function _remove(
- BinaryIMTData storage self,
- uint256 leaf,
- uint256[] calldata proofSiblings,
- uint8[] calldata proofPathIndices
- ) internal {
- _update(self, leaf, self.useDefaultZeroes ? Z_0 : self.zeroes[0], proofSiblings, proofPathIndices);
- }
-
- /// @dev Verify if the path is correct and the leaf is part of the tree.
- /// @param self: Tree data.
- /// @param leaf: Leaf to be removed.
- /// @param proofSiblings: Array of the sibling nodes of the proof of membership.
- /// @param proofPathIndices: Path of the proof of membership.
- /// @return True or false.
- function _verify(
- BinaryIMTData storage self,
- uint256 leaf,
- uint256[] calldata proofSiblings,
- uint8[] calldata proofPathIndices
- ) internal view returns (bool) {
- uint256 depth = self.depth;
-
- if (leaf >= SNARK_SCALAR_FIELD) {
- revert ValueGreaterThanSnarkScalarField();
- } else if (proofPathIndices.length != depth || proofSiblings.length != depth) {
- revert WrongMerkleProofPath();
- }
-
- uint256 hash = leaf;
-
- for (uint8 i = 0; i < depth;) {
- if (proofSiblings[i] >= SNARK_SCALAR_FIELD) {
- revert ValueGreaterThanSnarkScalarField();
- } else if (proofPathIndices[i] != 1 && proofPathIndices[i] != 0) {
- revert WrongMerkleProofPath();
- }
-
- if (proofPathIndices[i] == 0) {
- hash = PoseidonT3.hash([hash, proofSiblings[i]]);
- } else {
- hash = PoseidonT3.hash([proofSiblings[i], hash]);
- }
-
- unchecked {
- ++i;
- }
- }
-
- return hash == self.root;
- }
-}
diff --git a/imt/src/InternalQuinaryIMT.sol b/imt/src/InternalQuinaryIMT.sol
deleted file mode 100644
index 5376702..0000000
--- a/imt/src/InternalQuinaryIMT.sol
+++ /dev/null
@@ -1,241 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
-
-import {PoseidonT6} from "poseidon-solidity/PoseidonT6.sol";
-import {SNARK_SCALAR_FIELD, MAX_DEPTH} from "./Constants.sol";
-
-// Each incremental tree has certain properties and data that will
-// be used to add new leaves.
-struct QuinaryIMTData {
- uint256 depth; // Depth of the tree (levels - 1).
- uint256 root; // Root hash of the tree.
- uint256 numberOfLeaves; // Number of leaves of the tree.
- mapping(uint256 => uint256) zeroes; // Zero hashes used for empty nodes (level -> zero hash).
- // The nodes of the subtrees used in the last addition of a leaf (level -> [nodes]).
- mapping(uint256 => uint256[5]) lastSubtrees; // Caching these values is essential to efficient appends.
-}
-
-error ValueGreaterThanSnarkScalarField();
-error DepthNotSupported();
-error TreeIsFull();
-error NewLeafCannotEqualOldLeaf();
-error LeafDoesNotExist();
-error LeafIndexOutOfRange();
-error WrongMerkleProofPath();
-
-/// @title Incremental quinary Merkle tree.
-/// @dev The incremental tree allows to calculate the root hash each time a leaf is added, ensuring
-/// the integrity of the tree.
-library InternalQuinaryIMT {
- /// @dev Initializes a tree.
- /// @param self: Tree data.
- /// @param depth: Depth of the tree.
- /// @param zero: Zero value to be used.
- function _init(QuinaryIMTData storage self, uint256 depth, uint256 zero) internal {
- if (zero >= SNARK_SCALAR_FIELD) {
- revert ValueGreaterThanSnarkScalarField();
- } else if (depth <= 0 || depth > MAX_DEPTH) {
- revert DepthNotSupported();
- }
-
- self.depth = depth;
-
- for (uint8 i = 0; i < depth;) {
- self.zeroes[i] = zero;
- uint256[5] memory zeroChildren;
-
- for (uint8 j = 0; j < 5;) {
- zeroChildren[j] = zero;
- unchecked {
- ++j;
- }
- }
-
- zero = PoseidonT6.hash(zeroChildren);
-
- unchecked {
- ++i;
- }
- }
-
- self.root = zero;
- }
-
- /// @dev Inserts a leaf in the tree.
- /// @param self: Tree data.
- /// @param leaf: Leaf to be inserted.
- function _insert(QuinaryIMTData storage self, uint256 leaf) internal {
- uint256 depth = self.depth;
-
- if (leaf >= SNARK_SCALAR_FIELD) {
- revert ValueGreaterThanSnarkScalarField();
- } else if (self.numberOfLeaves >= 5 ** depth) {
- revert TreeIsFull();
- }
-
- uint256 index = self.numberOfLeaves;
- uint256 hash = leaf;
-
- for (uint8 i = 0; i < depth;) {
- uint8 position = uint8(index % 5);
-
- self.lastSubtrees[i][position] = hash;
-
- if (position == 0) {
- for (uint8 j = 1; j < 5;) {
- self.lastSubtrees[i][j] = self.zeroes[i];
- unchecked {
- ++j;
- }
- }
- }
-
- hash = PoseidonT6.hash(self.lastSubtrees[i]);
- index /= 5;
-
- unchecked {
- ++i;
- }
- }
-
- self.root = hash;
- self.numberOfLeaves += 1;
- }
-
- /// @dev Updates a leaf in the tree.
- /// @param self: Tree data.
- /// @param leaf: Leaf to be updated.
- /// @param newLeaf: New leaf.
- /// @param proofSiblings: Array of the sibling nodes of the proof of membership.
- /// @param proofPathIndices: Path of the proof of membership.
- function _update(
- QuinaryIMTData storage self,
- uint256 leaf,
- uint256 newLeaf,
- uint256[4][] calldata proofSiblings,
- uint8[] calldata proofPathIndices
- ) internal {
- if (newLeaf == leaf) {
- revert NewLeafCannotEqualOldLeaf();
- } else if (newLeaf >= SNARK_SCALAR_FIELD) {
- revert ValueGreaterThanSnarkScalarField();
- } else if (!_verify(self, leaf, proofSiblings, proofPathIndices)) {
- revert LeafDoesNotExist();
- }
-
- uint256 depth = self.depth;
- uint256 hash = newLeaf;
- uint256 updateIndex;
-
- for (uint8 i = 0; i < depth;) {
- uint256[5] memory nodes;
- updateIndex += proofPathIndices[i] * 5 ** i;
-
- for (uint8 j = 0; j < 5;) {
- if (j < proofPathIndices[i]) {
- nodes[j] = proofSiblings[i][j];
- } else if (j == proofPathIndices[i]) {
- nodes[j] = hash;
- } else {
- nodes[j] = proofSiblings[i][j - 1];
- }
- unchecked {
- ++j;
- }
- }
-
- if (nodes[0] == self.lastSubtrees[i][0] || nodes[4] == self.lastSubtrees[i][4]) {
- self.lastSubtrees[i][proofPathIndices[i]] = hash;
- }
-
- hash = PoseidonT6.hash(nodes);
-
- unchecked {
- ++i;
- }
- }
-
- if (updateIndex >= self.numberOfLeaves) {
- revert LeafIndexOutOfRange();
- }
-
- self.root = hash;
- }
-
- /// @dev Removes a leaf from the tree.
- /// @param self: Tree data.
- /// @param leaf: Leaf to be removed.
- /// @param proofSiblings: Array of the sibling nodes of the proof of membership.
- /// @param proofPathIndices: Path of the proof of membership.
- function _remove(
- QuinaryIMTData storage self,
- uint256 leaf,
- uint256[4][] calldata proofSiblings,
- uint8[] calldata proofPathIndices
- ) internal {
- _update(self, leaf, self.zeroes[0], proofSiblings, proofPathIndices);
- }
-
- /// @dev Verify if the path is correct and the leaf is part of the tree.
- /// @param self: Tree data.
- /// @param leaf: Leaf to be removed.
- /// @param proofSiblings: Array of the sibling nodes of the proof of membership.
- /// @param proofPathIndices: Path of the proof of membership.
- /// @return True or false.
- function _verify(
- QuinaryIMTData storage self,
- uint256 leaf,
- uint256[4][] calldata proofSiblings,
- uint8[] calldata proofPathIndices
- ) internal view returns (bool) {
- uint256 depth = self.depth;
-
- if (leaf >= SNARK_SCALAR_FIELD) {
- revert ValueGreaterThanSnarkScalarField();
- } else if (proofPathIndices.length != depth || proofSiblings.length != depth) {
- revert WrongMerkleProofPath();
- }
-
- uint256 hash = leaf;
-
- for (uint8 i = 0; i < depth;) {
- uint256[5] memory nodes;
-
- if (proofPathIndices[i] < 0 || proofPathIndices[i] >= 5) {
- revert WrongMerkleProofPath();
- }
-
- for (uint8 j = 0; j < 5;) {
- if (j < proofPathIndices[i]) {
- require(
- proofSiblings[i][j] < SNARK_SCALAR_FIELD,
- "QuinaryIMT: sibling node must be < SNARK_SCALAR_FIELD"
- );
-
- nodes[j] = proofSiblings[i][j];
- } else if (j == proofPathIndices[i]) {
- nodes[j] = hash;
- } else {
- require(
- proofSiblings[i][j - 1] < SNARK_SCALAR_FIELD,
- "QuinaryIMT: sibling node must be < SNARK_SCALAR_FIELD"
- );
-
- nodes[j] = proofSiblings[i][j - 1];
- }
-
- unchecked {
- ++j;
- }
- }
-
- hash = PoseidonT6.hash(nodes);
-
- unchecked {
- ++i;
- }
- }
-
- return hash == self.root;
- }
-}
diff --git a/imt/src/LICENSE b/imt/src/LICENSE
deleted file mode 100644
index 8ef16f7..0000000
--- a/imt/src/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2024 Ethereum Foundation
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/imt/src/QuinaryIMT.sol b/imt/src/QuinaryIMT.sol
deleted file mode 100644
index 41f028f..0000000
--- a/imt/src/QuinaryIMT.sol
+++ /dev/null
@@ -1,44 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
-
-import {InternalQuinaryIMT, QuinaryIMTData} from "./InternalQuinaryIMT.sol";
-
-library QuinaryIMT {
- using InternalQuinaryIMT for *;
-
- function init(QuinaryIMTData storage self, uint256 depth, uint256 zero) public {
- InternalQuinaryIMT._init(self, depth, zero);
- }
-
- function insert(QuinaryIMTData storage self, uint256 leaf) public {
- InternalQuinaryIMT._insert(self, leaf);
- }
-
- function update(
- QuinaryIMTData storage self,
- uint256 leaf,
- uint256 newLeaf,
- uint256[4][] calldata proofSiblings,
- uint8[] calldata proofPathIndices
- ) public {
- InternalQuinaryIMT._update(self, leaf, newLeaf, proofSiblings, proofPathIndices);
- }
-
- function remove(
- QuinaryIMTData storage self,
- uint256 leaf,
- uint256[4][] calldata proofSiblings,
- uint8[] calldata proofPathIndices
- ) public {
- InternalQuinaryIMT._remove(self, leaf, proofSiblings, proofPathIndices);
- }
-
- function verify(
- QuinaryIMTData storage self,
- uint256 leaf,
- uint256[4][] calldata proofSiblings,
- uint8[] calldata proofPathIndices
- ) private view returns (bool) {
- return InternalQuinaryIMT._verify(self, leaf, proofSiblings, proofPathIndices);
- }
-}
diff --git a/lazy-imt/README.md b/lazy-imt/README.md
deleted file mode 100644
index 8073eaa..0000000
--- a/lazy-imt/README.md
+++ /dev/null
@@ -1,60 +0,0 @@
-
-
- Lazy Incremental Merkle Tree (Solidity)
-
- Lazy Incremental Merkle tree implementation in Solidity.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-> [!WARNING]
-> These library has **not** been audited.
-
-> [!WARNING]
-> If you are looking for the first version of this package, please visit this [link](https://github.com/privacy-scaling-explorations/zk-kit/tree/imt-v1/packages/incremental-merkle-tree.sol).
-
----
-
-## 🛠 Install
-
-### npm or yarn
-
-Install the `@zk-kit/lazy-imt.sol` package with npm:
-
-```bash
-npm i @zk-kit/lazy-imt.sol --save
-```
-
-or yarn:
-
-```bash
-yarn add @zk-kit/lazy-imt.sol
-```
-
-## 📜 Usage
-
-Please, see the [test contracts](./test) for guidance on utilizing the libraries.
diff --git a/lazy-imt/src/Constants.sol b/lazy-imt/src/Constants.sol
deleted file mode 100644
index 9f3c455..0000000
--- a/lazy-imt/src/Constants.sol
+++ /dev/null
@@ -1,5 +0,0 @@
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.4;
-
-uint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
-uint8 constant MAX_DEPTH = 32;
diff --git a/lazy-imt/src/InternalLazyIMT.sol b/lazy-imt/src/InternalLazyIMT.sol
deleted file mode 100644
index 15ba265..0000000
--- a/lazy-imt/src/InternalLazyIMT.sol
+++ /dev/null
@@ -1,265 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
-
-import {PoseidonT3} from "poseidon-solidity/PoseidonT3.sol";
-import {SNARK_SCALAR_FIELD, MAX_DEPTH} from "./Constants.sol";
-
-struct LazyIMTData {
- uint40 maxIndex;
- uint40 numberOfLeaves;
- mapping(uint256 => uint256) elements;
-}
-
-library InternalLazyIMT {
- uint40 internal constant MAX_INDEX = (1 << 32) - 1;
-
- uint256 internal constant Z_0 = 0;
- uint256 internal constant Z_1 = 14744269619966411208579211824598458697587494354926760081771325075741142829156;
- uint256 internal constant Z_2 = 7423237065226347324353380772367382631490014989348495481811164164159255474657;
- uint256 internal constant Z_3 = 11286972368698509976183087595462810875513684078608517520839298933882497716792;
- uint256 internal constant Z_4 = 3607627140608796879659380071776844901612302623152076817094415224584923813162;
- uint256 internal constant Z_5 = 19712377064642672829441595136074946683621277828620209496774504837737984048981;
- uint256 internal constant Z_6 = 20775607673010627194014556968476266066927294572720319469184847051418138353016;
- uint256 internal constant Z_7 = 3396914609616007258851405644437304192397291162432396347162513310381425243293;
- uint256 internal constant Z_8 = 21551820661461729022865262380882070649935529853313286572328683688269863701601;
- uint256 internal constant Z_9 = 6573136701248752079028194407151022595060682063033565181951145966236778420039;
- uint256 internal constant Z_10 = 12413880268183407374852357075976609371175688755676981206018884971008854919922;
- uint256 internal constant Z_11 = 14271763308400718165336499097156975241954733520325982997864342600795471836726;
- uint256 internal constant Z_12 = 20066985985293572387227381049700832219069292839614107140851619262827735677018;
- uint256 internal constant Z_13 = 9394776414966240069580838672673694685292165040808226440647796406499139370960;
- uint256 internal constant Z_14 = 11331146992410411304059858900317123658895005918277453009197229807340014528524;
- uint256 internal constant Z_15 = 15819538789928229930262697811477882737253464456578333862691129291651619515538;
- uint256 internal constant Z_16 = 19217088683336594659449020493828377907203207941212636669271704950158751593251;
- uint256 internal constant Z_17 = 21035245323335827719745544373081896983162834604456827698288649288827293579666;
- uint256 internal constant Z_18 = 6939770416153240137322503476966641397417391950902474480970945462551409848591;
- uint256 internal constant Z_19 = 10941962436777715901943463195175331263348098796018438960955633645115732864202;
- uint256 internal constant Z_20 = 15019797232609675441998260052101280400536945603062888308240081994073687793470;
- uint256 internal constant Z_21 = 11702828337982203149177882813338547876343922920234831094975924378932809409969;
- uint256 internal constant Z_22 = 11217067736778784455593535811108456786943573747466706329920902520905755780395;
- uint256 internal constant Z_23 = 16072238744996205792852194127671441602062027943016727953216607508365787157389;
- uint256 internal constant Z_24 = 17681057402012993898104192736393849603097507831571622013521167331642182653248;
- uint256 internal constant Z_25 = 21694045479371014653083846597424257852691458318143380497809004364947786214945;
- uint256 internal constant Z_26 = 8163447297445169709687354538480474434591144168767135863541048304198280615192;
- uint256 internal constant Z_27 = 14081762237856300239452543304351251708585712948734528663957353575674639038357;
- uint256 internal constant Z_28 = 16619959921569409661790279042024627172199214148318086837362003702249041851090;
- uint256 internal constant Z_29 = 7022159125197495734384997711896547675021391130223237843255817587255104160365;
- uint256 internal constant Z_30 = 4114686047564160449611603615418567457008101555090703535405891656262658644463;
- uint256 internal constant Z_31 = 12549363297364877722388257367377629555213421373705596078299904496781819142130;
- uint256 internal constant Z_32 = 21443572485391568159800782191812935835534334817699172242223315142338162256601;
-
- function _defaultZero(uint8 index) internal pure returns (uint256) {
- if (index == 0) return Z_0;
- if (index == 1) return Z_1;
- if (index == 2) return Z_2;
- if (index == 3) return Z_3;
- if (index == 4) return Z_4;
- if (index == 5) return Z_5;
- if (index == 6) return Z_6;
- if (index == 7) return Z_7;
- if (index == 8) return Z_8;
- if (index == 9) return Z_9;
- if (index == 10) return Z_10;
- if (index == 11) return Z_11;
- if (index == 12) return Z_12;
- if (index == 13) return Z_13;
- if (index == 14) return Z_14;
- if (index == 15) return Z_15;
- if (index == 16) return Z_16;
- if (index == 17) return Z_17;
- if (index == 18) return Z_18;
- if (index == 19) return Z_19;
- if (index == 20) return Z_20;
- if (index == 21) return Z_21;
- if (index == 22) return Z_22;
- if (index == 23) return Z_23;
- if (index == 24) return Z_24;
- if (index == 25) return Z_25;
- if (index == 26) return Z_26;
- if (index == 27) return Z_27;
- if (index == 28) return Z_28;
- if (index == 29) return Z_29;
- if (index == 30) return Z_30;
- if (index == 31) return Z_31;
- if (index == 32) return Z_32;
- revert("LazyIMT: defaultZero bad index");
- }
-
- function _init(LazyIMTData storage self, uint8 depth) internal {
- require(depth <= MAX_DEPTH, "LazyIMT: Tree too large");
- self.maxIndex = uint40((1 << depth) - 1);
- self.numberOfLeaves = 0;
- }
-
- function _reset(LazyIMTData storage self) internal {
- self.numberOfLeaves = 0;
- }
-
- function _indexForElement(uint8 level, uint40 index) internal pure returns (uint40) {
- // store the elements sparsely
- return MAX_INDEX * level + index;
- }
-
- function _insert(LazyIMTData storage self, uint256 leaf) internal {
- uint40 index = self.numberOfLeaves;
- require(leaf < SNARK_SCALAR_FIELD, "LazyIMT: leaf must be < SNARK_SCALAR_FIELD");
- require(index < self.maxIndex, "LazyIMT: tree is full");
-
- self.numberOfLeaves = index + 1;
-
- uint256 hash = leaf;
-
- for (uint8 i = 0;;) {
- self.elements[_indexForElement(i, index)] = hash;
- // it's a left element so we don't hash until there's a right element
- if (index & 1 == 0) break;
- uint40 elementIndex = _indexForElement(i, index - 1);
- hash = PoseidonT3.hash([self.elements[elementIndex], hash]);
- unchecked {
- index >>= 1;
- i++;
- }
- }
- }
-
- function _update(LazyIMTData storage self, uint256 leaf, uint40 index) internal {
- require(leaf < SNARK_SCALAR_FIELD, "LazyIMT: leaf must be < SNARK_SCALAR_FIELD");
- uint40 numberOfLeaves = self.numberOfLeaves;
- require(index < numberOfLeaves, "LazyIMT: leaf must exist");
-
- uint256 hash = leaf;
-
- for (uint8 i = 0; true;) {
- self.elements[_indexForElement(i, index)] = hash;
- uint256 levelCount = numberOfLeaves >> (i + 1);
- if (levelCount <= index >> 1) break;
- if (index & 1 == 0) {
- uint40 elementIndex = _indexForElement(i, index + 1);
- hash = PoseidonT3.hash([hash, self.elements[elementIndex]]);
- } else {
- uint40 elementIndex = _indexForElement(i, index - 1);
- hash = PoseidonT3.hash([self.elements[elementIndex], hash]);
- }
- unchecked {
- index >>= 1;
- i++;
- }
- }
- }
-
- function _root(LazyIMTData storage self) internal view returns (uint256) {
- // this will always short circuit if self.numberOfLeaves == 0
- uint40 numberOfLeaves = self.numberOfLeaves;
- // dynamically determine a depth
- uint8 depth = 1;
- while (uint40(2) ** uint40(depth) < numberOfLeaves) {
- depth++;
- }
- return _root(self, numberOfLeaves, depth);
- }
-
- function _root(LazyIMTData storage self, uint8 depth) internal view returns (uint256) {
- require(depth > 0, "LazyIMT: depth must be > 0");
- require(depth <= MAX_DEPTH, "LazyIMT: depth must be <= MAX_DEPTH");
- uint40 numberOfLeaves = self.numberOfLeaves;
- require(uint40(2) ** uint40(depth) >= numberOfLeaves, "LazyIMT: ambiguous depth");
- return _root(self, numberOfLeaves, depth);
- }
-
- // Here it's assumed that the depth value is valid. If it is either 0 or > 2^8-1
- // this function will panic.
- function _root(LazyIMTData storage self, uint40 numberOfLeaves, uint8 depth) internal view returns (uint256) {
- require(depth <= MAX_DEPTH, "LazyIMT: depth must be <= MAX_DEPTH");
- // this should always short circuit if self.numberOfLeaves == 0
- if (numberOfLeaves == 0) return _defaultZero(depth);
- uint256[] memory levels = new uint256[](depth + 1);
- _levels(self, numberOfLeaves, depth, levels);
- return levels[depth];
- }
-
- function _levels(LazyIMTData storage self, uint40 numberOfLeaves, uint8 depth, uint256[] memory levels)
- internal
- view
- {
- require(depth <= MAX_DEPTH, "LazyIMT: depth must be <= MAX_DEPTH");
- require(numberOfLeaves > 0, "LazyIMT: number of leaves must be > 0");
- // this should always short circuit if self.numberOfLeaves == 0
- uint40 index = numberOfLeaves - 1;
-
- if (index & 1 == 0) {
- levels[0] = self.elements[_indexForElement(0, index)];
- } else {
- levels[0] = _defaultZero(0);
- }
-
- for (uint8 i = 0; i < depth;) {
- if (index & 1 == 0) {
- levels[i + 1] = PoseidonT3.hash([levels[i], _defaultZero(i)]);
- } else {
- uint256 levelCount = (numberOfLeaves) >> (i + 1);
- if (levelCount > index >> 1) {
- uint256 parent = self.elements[_indexForElement(i + 1, index >> 1)];
- levels[i + 1] = parent;
- } else {
- uint256 sibling = self.elements[_indexForElement(i, index - 1)];
- levels[i + 1] = PoseidonT3.hash([sibling, levels[i]]);
- }
- }
- unchecked {
- index >>= 1;
- i++;
- }
- }
- }
-
- function _merkleProofElements(LazyIMTData storage self, uint40 index, uint8 depth)
- internal
- view
- returns (uint256[] memory)
- {
- uint40 numberOfLeaves = self.numberOfLeaves;
- require(index < numberOfLeaves, "LazyIMT: leaf must exist");
-
- uint8 targetDepth = 1;
- while (uint40(2) ** uint40(targetDepth) < numberOfLeaves) {
- targetDepth++;
- }
- require(depth >= targetDepth, "LazyIMT: proof depth");
- // pass depth -1 because we don't need the root value
- uint256[] memory _elements = new uint256[](depth);
- _levels(self, numberOfLeaves, targetDepth - 1, _elements);
-
- // unroll the bottom entry of the tree because it will never need to
- // be pulled from _levels
- if (index & 1 == 0) {
- if (index + 1 >= numberOfLeaves) {
- _elements[0] = _defaultZero(0);
- } else {
- _elements[0] = self.elements[_indexForElement(0, index + 1)];
- }
- } else {
- _elements[0] = self.elements[_indexForElement(0, index - 1)];
- }
- index >>= 1;
-
- for (uint8 i = 1; i < depth;) {
- uint256 currentLevelCount = numberOfLeaves >> i;
- if (index & 1 == 0) {
- // if the element is an uncomputed edge node we'll use the value set
- // from _levels above
- // otherwise set as usual below
- if (index + 1 < currentLevelCount) {
- _elements[i] = self.elements[_indexForElement(i, index + 1)];
- } else if (((numberOfLeaves - 1) >> i) <= index) {
- _elements[i] = _defaultZero(i);
- }
- } else {
- _elements[i] = self.elements[_indexForElement(i, index - 1)];
- }
- unchecked {
- index >>= 1;
- i++;
- }
- }
- return _elements;
- }
-}
diff --git a/lazy-imt/src/LazyIMT.sol b/lazy-imt/src/LazyIMT.sol
deleted file mode 100644
index 48a2da8..0000000
--- a/lazy-imt/src/LazyIMT.sol
+++ /dev/null
@@ -1,52 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
-
-import {InternalLazyIMT, LazyIMTData} from "./InternalLazyIMT.sol";
-
-library LazyIMT {
- using InternalLazyIMT for *;
-
- function init(LazyIMTData storage self, uint8 depth) public {
- InternalLazyIMT._init(self, depth);
- }
-
- function defaultZero(uint8 index) public pure returns (uint256) {
- return InternalLazyIMT._defaultZero(index);
- }
-
- function reset(LazyIMTData storage self) public {
- InternalLazyIMT._reset(self);
- }
-
- function indexForElement(uint8 level, uint40 index) public pure returns (uint40) {
- return InternalLazyIMT._indexForElement(level, index);
- }
-
- function insert(LazyIMTData storage self, uint256 leaf) public {
- InternalLazyIMT._insert(self, leaf);
- }
-
- function update(LazyIMTData storage self, uint256 leaf, uint40 index) public {
- InternalLazyIMT._update(self, leaf, index);
- }
-
- function root(LazyIMTData storage self) public view returns (uint256) {
- return InternalLazyIMT._root(self);
- }
-
- function root(LazyIMTData storage self, uint8 depth) public view returns (uint256) {
- return InternalLazyIMT._root(self, depth);
- }
-
- function merkleProofElements(LazyIMTData storage self, uint40 index, uint8 depth)
- public
- view
- returns (uint256[] memory)
- {
- return InternalLazyIMT._merkleProofElements(self, index, depth);
- }
-
- function _root(LazyIMTData storage self, uint40 numberOfLeaves, uint8 depth) internal view returns (uint256) {
- return InternalLazyIMT._root(self, numberOfLeaves, depth);
- }
-}
diff --git a/lazytower/README.md b/lazytower/README.md
deleted file mode 100644
index dcaa8a5..0000000
--- a/lazytower/README.md
+++ /dev/null
@@ -1,138 +0,0 @@
-
-
- LazyTower (Solidity)
-
- LazyTower Solidity library.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-> [!WARNING]
-> These library has **not** been audited.
-
----
-
-## 🛠 Install
-
-### npm or yarn
-
-Install the `@zk-kit/lazytower.sol` package with npm:
-
-```bash
-npm i @zk-kit/lazytower.sol --save
-```
-
-or yarn:
-
-```bash
-yarn add @zk-kit/lazytower.sol
-```
-
-## 📜 Usage
-
-### Importing and using the library
-
-```solidity
-// SPDX-License-Identifier: MIT
-
-pragma solidity ^0.8.4;
-
-import "../LazyTowerHashChain.sol";
-
-contract LazyTowerHashChainTest {
- using LazyTowerHashChain for LazyTowerHashChainData;
-
- event Add(uint256 item);
-
- // map for multiple test cases
- mapping(bytes32 => LazyTowerHashChainData) public towers;
-
- function add(bytes32 _towerId, uint256 _item) external {
- towers[_towerId].add(_item);
- emit Add(_item);
- }
-
- function getDataForProving(bytes32 _towerId) external view returns (uint256, uint256[] memory, uint256) {
- return towers[_towerId].getDataForProving();
- }
-}
-```
-
-### Creating an Hardhat task to deploy the contract
-
-```typescript
-import { Contract } from "ethers"
-import { task, types } from "hardhat/config"
-
-task("deploy:lazytower-test", "Deploy a LazyTowerHashChainTest contract")
- .addOptionalParam("logs", "Print the logs", true, types.boolean)
- .setAction(async ({ logs }, { ethers }): Promise => {
- const PoseidonT3Factory = await ethers.getContractFactory("PoseidonT3")
- const PoseidonT3 = await PoseidonT3Factory.deploy()
-
- if (logs) {
- console.info(`PoseidonT3 library has been deployed to: ${PoseidonT3.address}`)
- }
-
- const LazyTowerLibFactory = await ethers.getContractFactory("LazyTowerHashChain", {
- libraries: {
- PoseidonT3: PoseidonT3.address
- }
- })
- const lazyTowerLib = await LazyTowerLibFactory.deploy()
-
- await lazyTowerLib.deployed()
-
- if (logs) {
- console.info(`LazyTowerHashChain library has been deployed to: ${lazyTowerLib.address}`)
- }
-
- const ContractFactory = await ethers.getContractFactory("LazyTowerHashChainTest", {
- libraries: {
- LazyTowerHashChain: lazyTowerLib.address
- }
- })
-
- const contract = await ContractFactory.deploy()
-
- await contract.deployed()
-
- if (logs) {
- console.info(`Test contract has been deployed to: ${contract.address}`)
- }
-
- return contract
- })
-```
-
-## Contacts
-
-### Developers
-
-- e-mail : lcamel@gmail.com
-- github : [@LCamel](https://github.com/LCamel)
-- website : https://www.facebook.com/LCamel
diff --git a/lazytower/src/LazyTowerHashChain.sol b/lazytower/src/LazyTowerHashChain.sol
deleted file mode 100644
index a0fd6d1..0000000
--- a/lazytower/src/LazyTowerHashChain.sol
+++ /dev/null
@@ -1,100 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
-
-import {PoseidonT3} from "poseidon-solidity/PoseidonT3.sol";
-// CAPACITY = W * (W**0 + W**1 + ... + W**(H - 1)) = W * (W**H - 1) / (W - 1)
-// 4 * (4**24 - 1) / (4 - 1) = 375_299_968_947_540;
-
-uint256 constant H = 24;
-uint256 constant W = 4;
-
-uint256 constant bitsPerLevel = 4;
-uint256 constant levelBitmask = 15; // (1 << bitsPerLevel) - 1
-uint256 constant ones = 0x111111111111111111111111; // H ones
-
-// Each LazyTower has certain properties and data that will
-// be used to add new items.
-struct LazyTowerHashChainData {
- uint256 levelLengths; // length of each level
- uint256[H] digests; // digest of each level
- uint256[H] digestOfDigests; // digest of digests
-}
-
-/// @title LazyTower.
-/// @dev The LazyTower allows to calculate the digest of digests each time an item is added, ensuring
-/// the integrity of the LazyTower.
-library LazyTowerHashChain {
- uint256 internal constant SNARK_SCALAR_FIELD =
- 21888242871839275222246405745257275088548364400416034343698204186575808495617;
-
- function findLowestNonFullLevelThenInc(uint256 levelLengths)
- internal
- pure
- returns (uint256 level, bool isHead, bool isTop, uint256 newLevelLengths)
- {
- // find the lowest non-full level
- uint256 levelLength;
- while (true) {
- levelLength = levelLengths & levelBitmask;
- if (levelLength < W) break;
- level++;
- levelLengths >>= bitsPerLevel;
- }
-
- isHead = (levelLength == 0);
- isTop = ((levelLengths >> bitsPerLevel) == 0);
-
- // increment the non-full levelLength(s) by one
- // all full levels below become ones
- uint256 fullLevelBits = level * bitsPerLevel;
- uint256 onesMask = (1 << fullLevelBits) - 1;
- newLevelLengths = ((levelLengths + 1) << fullLevelBits) + (onesMask & ones);
- }
-
- /// @dev Add an item.
- /// @param self: LazyTower data
- /// @param item: item to be added
- function add(LazyTowerHashChainData storage self, uint256 item) public {
- require(item < SNARK_SCALAR_FIELD, "LazyTower: item must be < SNARK_SCALAR_FIELD");
-
- uint256 level;
- bool isHead;
- bool isTop;
- (level, isHead, isTop, self.levelLengths) = findLowestNonFullLevelThenInc(self.levelLengths);
-
- uint256 digest;
- uint256 digestOfDigests;
- uint256 toAdd;
-
- // append at the first non-full level
- toAdd = (level == 0) ? item : self.digests[level - 1];
- digest = isHead ? toAdd : PoseidonT3.hash([self.digests[level], toAdd]);
- digestOfDigests = isTop ? digest : PoseidonT3.hash([self.digestOfDigests[level + 1], digest]);
- self.digests[level] = digest;
- self.digestOfDigests[level] = digestOfDigests;
-
- // the rest of levels are all full
- while (level != 0) {
- level--;
-
- toAdd = (level == 0) ? item : self.digests[level - 1];
- digest = toAdd;
- digestOfDigests = PoseidonT3.hash([digestOfDigests, digest]); // top-down
- self.digests[level] = digest;
- self.digestOfDigests[level] = digestOfDigests;
- }
- }
-
- function getDataForProving(LazyTowerHashChainData storage self)
- external
- view
- returns (uint256, uint256[] memory, uint256)
- {
- uint256 len = self.digests.length;
- uint256[] memory digests = new uint256[](len); // for returning a dynamic array
- for (uint256 i = 0; i < len; i++) {
- digests[i] = self.digests[i];
- }
- return (self.levelLengths, digests, self.digestOfDigests[0]);
- }
-}
diff --git a/lean-imt/README.md b/lean-imt/README.md
deleted file mode 100644
index 8c5e1cb..0000000
--- a/lean-imt/README.md
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
- Lean Incremental Merkle Tree (Solidity)
-
- Lean Incremental Merkle tree implementation in Solidity.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-> [!NOTE]
-> This library has been audited as part of the Semaphore V4 PSE audit: https://semaphore.pse.dev/Semaphore_4.0.0_Audit.pdf.
-
-The LeanIMT is an optimized binary version of the [IMT](https://github.com/privacy-scaling-explorations/zk-kit.solidity/tree/main/packages/imt) into binary-focused model, eliminating the need for zero values and allowing dynamic depth adjustment. Unlike the IMT, which uses a zero hash for incomplete nodes, the LeanIMT directly adopts the left child's value when a node lacks a right counterpart. The tree's depth dynamically adjusts to the count of leaves, enhancing efficiency by reducing the number of required hash calculations. To understand more about the LeanIMT, take a look at this [visual explanation](https://hackmd.io/@vplasencia/S1whLBN16).
-
----
-
-## 🛠 Install
-
-### npm or yarn
-
-Install the `@zk-kit/lean-imt.sol` package with npm:
-
-```bash
-npm i @zk-kit/lean-imt.sol --save
-```
-
-or yarn:
-
-```bash
-yarn add @zk-kit/lean-imt.sol
-```
-
-## 📜 Usage
-
-Please, see the [test contracts](./test) for guidance on utilizing the libraries.
diff --git a/lean-imt/src/Constants.sol b/lean-imt/src/Constants.sol
deleted file mode 100644
index 4e86b6e..0000000
--- a/lean-imt/src/Constants.sol
+++ /dev/null
@@ -1,4 +0,0 @@
-// SPDX-License-Identifier: UNLICENSED
-pragma solidity ^0.8.4;
-
-uint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
diff --git a/lean-imt/src/InternalLeanIMT.sol b/lean-imt/src/InternalLeanIMT.sol
deleted file mode 100644
index 8f5c593..0000000
--- a/lean-imt/src/InternalLeanIMT.sol
+++ /dev/null
@@ -1,342 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
-
-import {PoseidonT3} from "poseidon-solidity/PoseidonT3.sol";
-import {SNARK_SCALAR_FIELD} from "./Constants.sol";
-
-struct LeanIMTData {
- // Tracks the current number of leaves in the tree.
- uint256 size;
- // Represents the current depth of the tree, which can increase as new leaves are inserted.
- uint256 depth;
- // A mapping from each level of the tree to the node value of the last even position at that level.
- // Used for efficient inserts, updates and root calculations.
- mapping(uint256 => uint256) sideNodes;
- // A mapping from leaf values to their respective indices in the tree.
- // This facilitates checks for leaf existence and retrieval of leaf positions.
- mapping(uint256 => uint256) leaves;
-}
-
-error WrongSiblingNodes();
-error LeafGreaterThanSnarkScalarField();
-error LeafCannotBeZero();
-error LeafAlreadyExists();
-error LeafDoesNotExist();
-
-/// @title Lean Incremental binary Merkle tree.
-/// @dev The LeanIMT is an optimized version of the BinaryIMT.
-/// This implementation eliminates the use of zeroes, and make the tree depth dynamic.
-/// When a node doesn't have the right child, instead of using a zero hash as in the BinaryIMT,
-/// the node's value becomes that of its left child. Furthermore, rather than utilizing a static tree depth,
-/// it is updated based on the number of leaves in the tree. This approach
-/// results in the calculation of significantly fewer hashes, making the tree more efficient.
-library InternalLeanIMT {
- /// @dev Inserts a new leaf into the incremental merkle tree.
- /// The function ensures that the leaf is valid according to the
- /// constraints of the tree and then updates the tree's structure accordingly.
- /// @param self: A storage reference to the 'LeanIMTData' struct.
- /// @param leaf: The value of the new leaf to be inserted into the tree.
- /// @return The new hash of the node after the leaf has been inserted.
- function _insert(LeanIMTData storage self, uint256 leaf) internal returns (uint256) {
- if (leaf >= SNARK_SCALAR_FIELD) {
- revert LeafGreaterThanSnarkScalarField();
- } else if (leaf == 0) {
- revert LeafCannotBeZero();
- } else if (_has(self, leaf)) {
- revert LeafAlreadyExists();
- }
-
- uint256 index = self.size;
-
- // Cache tree depth to optimize gas
- uint256 treeDepth = self.depth;
-
- // A new insertion can increase a tree's depth by at most 1,
- // and only if the number of leaves supported by the current
- // depth is less than the number of leaves to be supported after insertion.
- if (2 ** treeDepth < index + 1) {
- ++treeDepth;
- }
-
- self.depth = treeDepth;
-
- uint256 node = leaf;
-
- for (uint256 level = 0; level < treeDepth;) {
- if ((index >> level) & 1 == 1) {
- node = PoseidonT3.hash([self.sideNodes[level], node]);
- } else {
- self.sideNodes[level] = node;
- }
-
- unchecked {
- ++level;
- }
- }
-
- self.size = ++index;
-
- self.sideNodes[treeDepth] = node;
- self.leaves[leaf] = index;
-
- return node;
- }
-
- /// @dev Inserts many leaves into the incremental merkle tree.
- /// The function ensures that the leaves are valid according to the
- /// constraints of the tree and then updates the tree's structure accordingly.
- /// @param self: A storage reference to the 'LeanIMTData' struct.
- /// @param leaves: The values of the new leaves to be inserted into the tree.
- /// @return The root after the leaves have been inserted.
- function _insertMany(LeanIMTData storage self, uint256[] calldata leaves) internal returns (uint256) {
- // Cache tree size to optimize gas
- uint256 treeSize = self.size;
-
- // Check that all the new values are correct to be added.
- for (uint256 i = 0; i < leaves.length;) {
- if (leaves[i] >= SNARK_SCALAR_FIELD) {
- revert LeafGreaterThanSnarkScalarField();
- } else if (leaves[i] == 0) {
- revert LeafCannotBeZero();
- } else if (_has(self, leaves[i])) {
- revert LeafAlreadyExists();
- }
-
- self.leaves[leaves[i]] = treeSize + 1 + i;
-
- unchecked {
- ++i;
- }
- }
-
- // Array to save the nodes that will be used to create the next level of the tree.
- uint256[] memory currentLevelNewNodes;
-
- currentLevelNewNodes = leaves;
-
- // Cache tree depth to optimize gas
- uint256 treeDepth = self.depth;
-
- // Calculate the depth of the tree after adding the new values.
- // Unlike the 'insert' function, we need a while here as
- // N insertions can increase the tree's depth more than once.
- while (2 ** treeDepth < treeSize + leaves.length) {
- ++treeDepth;
- }
-
- self.depth = treeDepth;
-
- // First index to change in every level.
- uint256 currentLevelStartIndex = treeSize;
-
- // Size of the level used to create the next level.
- uint256 currentLevelSize = treeSize + leaves.length;
-
- // The index where changes begin at the next level.
- uint256 nextLevelStartIndex = currentLevelStartIndex >> 1;
-
- // The size of the next level.
- uint256 nextLevelSize = ((currentLevelSize - 1) >> 1) + 1;
-
- for (uint256 level = 0; level < treeDepth;) {
- // The number of nodes for the new level that will be created,
- // only the new values, not the entire level.
- uint256 numberOfNewNodes = nextLevelSize - nextLevelStartIndex;
- uint256[] memory nextLevelNewNodes = new uint256[](numberOfNewNodes);
- for (uint256 i = 0; i < numberOfNewNodes;) {
- uint256 leftNode;
-
- // Assign the left node using the saved path or the position in the array.
- if ((i + nextLevelStartIndex) * 2 < currentLevelStartIndex) {
- leftNode = self.sideNodes[level];
- } else {
- leftNode = currentLevelNewNodes[(i + nextLevelStartIndex) * 2 - currentLevelStartIndex];
- }
-
- uint256 rightNode;
-
- // Assign the right node if the value exists.
- if ((i + nextLevelStartIndex) * 2 + 1 < currentLevelSize) {
- rightNode = currentLevelNewNodes[(i + nextLevelStartIndex) * 2 + 1 - currentLevelStartIndex];
- }
-
- uint256 parentNode;
-
- // Assign the parent node.
- // If it has a right child the result will be the hash(leftNode, rightNode) if not,
- // it will be the leftNode.
- if (rightNode != 0) {
- parentNode = PoseidonT3.hash([leftNode, rightNode]);
- } else {
- parentNode = leftNode;
- }
-
- nextLevelNewNodes[i] = parentNode;
-
- unchecked {
- ++i;
- }
- }
-
- // Update the `sideNodes` variable.
- // If `currentLevelSize` is odd, the saved value will be the last value of the array
- // if it is even and there are more than 1 element in `currentLevelNewNodes`, the saved value
- // will be the value before the last one.
- // If it is even and there is only one element, there is no need to save anything because
- // the correct value for this level was already saved before.
- if (currentLevelSize & 1 == 1) {
- self.sideNodes[level] = currentLevelNewNodes[currentLevelNewNodes.length - 1];
- } else if (currentLevelNewNodes.length > 1) {
- self.sideNodes[level] = currentLevelNewNodes[currentLevelNewNodes.length - 2];
- }
-
- currentLevelStartIndex = nextLevelStartIndex;
-
- // Calculate the next level startIndex value.
- // It is the position of the parent node which is pos/2.
- nextLevelStartIndex >>= 1;
-
- // Update the next array that will be used to calculate the next level.
- currentLevelNewNodes = nextLevelNewNodes;
-
- currentLevelSize = nextLevelSize;
-
- // Calculate the size of the next level.
- // The size of the next level is (currentLevelSize - 1) / 2 + 1.
- nextLevelSize = ((nextLevelSize - 1) >> 1) + 1;
-
- unchecked {
- ++level;
- }
- }
-
- // Update tree size
- self.size = treeSize + leaves.length;
-
- // Update tree root
- self.sideNodes[treeDepth] = currentLevelNewNodes[0];
-
- return currentLevelNewNodes[0];
- }
-
- /// @dev Updates the value of an existing leaf and recalculates hashes
- /// to maintain tree integrity.
- /// @param self: A storage reference to the 'LeanIMTData' struct.
- /// @param oldLeaf: The value of the leaf that is to be updated.
- /// @param newLeaf: The new value that will replace the oldLeaf in the tree.
- /// @param siblingNodes: An array of sibling nodes that are necessary to recalculate the path to the root.
- /// @return The new hash of the updated node after the leaf has been updated.
- function _update(LeanIMTData storage self, uint256 oldLeaf, uint256 newLeaf, uint256[] calldata siblingNodes)
- internal
- returns (uint256)
- {
- if (newLeaf >= SNARK_SCALAR_FIELD) {
- revert LeafGreaterThanSnarkScalarField();
- } else if (!_has(self, oldLeaf)) {
- revert LeafDoesNotExist();
- } else if (_has(self, newLeaf)) {
- revert LeafAlreadyExists();
- }
-
- uint256 index = _indexOf(self, oldLeaf);
- uint256 node = newLeaf;
- uint256 oldRoot = oldLeaf;
-
- uint256 lastIndex = self.size - 1;
- uint256 i = 0;
-
- // Cache tree depth to optimize gas
- uint256 treeDepth = self.depth;
-
- for (uint256 level = 0; level < treeDepth;) {
- if ((index >> level) & 1 == 1) {
- if (siblingNodes[i] >= SNARK_SCALAR_FIELD) {
- revert LeafGreaterThanSnarkScalarField();
- }
-
- node = PoseidonT3.hash([siblingNodes[i], node]);
- oldRoot = PoseidonT3.hash([siblingNodes[i], oldRoot]);
-
- unchecked {
- ++i;
- }
- } else {
- if (index >> level != lastIndex >> level) {
- if (siblingNodes[i] >= SNARK_SCALAR_FIELD) {
- revert LeafGreaterThanSnarkScalarField();
- }
-
- node = PoseidonT3.hash([node, siblingNodes[i]]);
- oldRoot = PoseidonT3.hash([oldRoot, siblingNodes[i]]);
-
- unchecked {
- ++i;
- }
- } else {
- self.sideNodes[i] = node;
- }
- }
-
- unchecked {
- ++level;
- }
- }
-
- if (oldRoot != _root(self)) {
- revert WrongSiblingNodes();
- }
-
- self.sideNodes[treeDepth] = node;
-
- if (newLeaf != 0) {
- self.leaves[newLeaf] = self.leaves[oldLeaf];
- }
-
- self.leaves[oldLeaf] = 0;
-
- return node;
- }
-
- /// @dev Removes a leaf from the tree by setting its value to zero.
- /// This function utilizes the update function to set the leaf's value
- /// to zero and update the tree's state accordingly.
- /// @param self: A storage reference to the 'LeanIMTData' struct.
- /// @param oldLeaf: The value of the leaf to be removed.
- /// @param siblingNodes: An array of sibling nodes required for updating the path to the root after removal.
- /// @return The new root hash of the tree after the leaf has been removed.
- function _remove(LeanIMTData storage self, uint256 oldLeaf, uint256[] calldata siblingNodes)
- internal
- returns (uint256)
- {
- return _update(self, oldLeaf, 0, siblingNodes);
- }
-
- /// @dev Checks if a leaf exists in the tree.
- /// @param self: A storage reference to the 'LeanIMTData' struct.
- /// @param leaf: The value of the leaf to check for existence.
- /// @return A boolean value indicating whether the leaf exists in the tree.
- function _has(LeanIMTData storage self, uint256 leaf) internal view returns (bool) {
- return self.leaves[leaf] != 0;
- }
-
- /// @dev Retrieves the index of a given leaf in the tree.
- /// @param self: A storage reference to the 'LeanIMTData' struct.
- /// @param leaf: The value of the leaf whose index is to be found.
- /// @return The index of the specified leaf within the tree. If the leaf is not present, the function
- /// reverts with a custom error.
- function _indexOf(LeanIMTData storage self, uint256 leaf) internal view returns (uint256) {
- if (self.leaves[leaf] == 0) {
- revert LeafDoesNotExist();
- }
-
- return self.leaves[leaf] - 1;
- }
-
- /// @dev Retrieves the root of the tree from the 'sideNodes' mapping using the
- /// current tree depth.
- /// @param self: A storage reference to the 'LeanIMTData' struct.
- /// @return The root hash of the tree.
- function _root(LeanIMTData storage self) internal view returns (uint256) {
- return self.sideNodes[self.depth];
- }
-}
diff --git a/lean-imt/src/LeanIMT.sol b/lean-imt/src/LeanIMT.sol
deleted file mode 100644
index 552c88e..0000000
--- a/lean-imt/src/LeanIMT.sol
+++ /dev/null
@@ -1,42 +0,0 @@
-// SPDX-License-Identifier: MIT
-pragma solidity ^0.8.4;
-
-import {InternalLeanIMT, LeanIMTData} from "./InternalLeanIMT.sol";
-
-library LeanIMT {
- using InternalLeanIMT for *;
-
- function insert(LeanIMTData storage self, uint256 leaf) public returns (uint256) {
- return InternalLeanIMT._insert(self, leaf);
- }
-
- function insertMany(LeanIMTData storage self, uint256[] calldata leaves) public returns (uint256) {
- return InternalLeanIMT._insertMany(self, leaves);
- }
-
- function update(LeanIMTData storage self, uint256 oldLeaf, uint256 newLeaf, uint256[] calldata siblingNodes)
- public
- returns (uint256)
- {
- return InternalLeanIMT._update(self, oldLeaf, newLeaf, siblingNodes);
- }
-
- function remove(LeanIMTData storage self, uint256 oldLeaf, uint256[] calldata siblingNodes)
- public
- returns (uint256)
- {
- return InternalLeanIMT._remove(self, oldLeaf, siblingNodes);
- }
-
- function has(LeanIMTData storage self, uint256 leaf) public view returns (bool) {
- return InternalLeanIMT._has(self, leaf);
- }
-
- function indexOf(LeanIMTData storage self, uint256 leaf) public view returns (uint256) {
- return InternalLeanIMT._indexOf(self, leaf);
- }
-
- function root(LeanIMTData storage self) public view returns (uint256) {
- return InternalLeanIMT._root(self);
- }
-}