diff --git a/README.md b/README.md index 40d401b..bb4d650 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,81 @@ # Zerokit -A set of Zero Knowledge modules, written in Rust and designed to be used in other system programming environments. +[![Crates.io](https://img.shields.io/crates/v/rln.svg)](https://crates.io/crates/rln) +[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/vacp2p/zerokit/ci.yml?branch=master&label=CI)](https://github.com/vacp2p/zerokit/actions) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) +[![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) -## Initial scope +A collection of Zero Knowledge modules written in Rust and designed to be used in other system programming environments. -Focus on RLN and being able to use [Circom](https://iden3.io/circom) based -version through ark-circom, as opposed to the native one that currently exists -in Rust. +## Overview -## Acknowledgements +Zerokit provides zero-knowledge cryptographic primitives with a focus on performance, security, and usability. +The current focus is on Rate-Limiting Nullifier [RLN](https://github.com/Rate-Limiting-Nullifier) implementation. -- Uses [ark-circom](https://github.com/gakonst/ark-circom), Rust wrapper around Circom. +Current implementation is based on the following [specification](https://github.com/vacp2p/rfc-index/blob/main/vac/raw/rln-v2.md) +and focused on RLNv2 which allows to set a rate limit for the number of messages that can be sent by a user. -- Inspired by Applied ZKP group work, e.g. [zk-kit](https://github.com/appliedzkp/zk-kit). +## Features -- [RLN library](https://github.com/kilic/rln) written in Rust based on Bellman. +- **RLN Implementation**: Efficient Rate-Limiting Nullifier using zkSNARKs +- **Circom Compatibility**: Uses Circom-based circuits for RLN +- **Cross-Platform**: Support for multiple architectures (see compatibility note below) +- **FFI-Friendly**: Easy to integrate with other languages -- [semaphore-rs](https://github.com/worldcoin/semaphore-rs) written in Rust based on ark-circom. +## Architecture -- The circom witness calculation code of the rln crate is based on [circom-witnesscalc](https://github.com/iden3/circom-witnesscalc) by iden3. The execution graph file used by this code has been generated by means of the same iden3 software. The circom-witnesscalc code fragments have been borrowed instead of depending on this crate, because its types of input and output data were incompatible with the corresponding zerokit code fragments, and circom-witnesscalc has some dependencies, which are redundant for our purpose. - -## Users - -Zerokit is used by - - -- [nwaku](https://github.com/waku-org/nwaku) -- [js-rln](https://github.com/waku-org/js-rln) +Zerokit currently focuses on RLN (Rate-Limiting Nullifier) implementation using [Circom](https://iden3.io/circom) circuits through ark-circom, providing an alternative to existing native Rust implementations. ## Build and Test -To install missing dependencies, run the following commands from the root folder +> [!IMPORTANT] +> For WASM support or x32 architecture builds, use version `0.6.1`. The current version has dependency issues for these platforms. WASM support will return in a future release. + +### Install Dependencies ```bash make installdeps ``` -To build and test all crates, run the following commands from the root folder +### Build and Test All Crates ```bash make build make test ``` -## Release assets +## Release Assets -We use [`cross-rs`](https://github.com/cross-rs/cross) to cross-compile and generate release assets for rln. +We use [`cross-rs`](https://github.com/cross-rs/cross) to cross-compile and generate release assets: + +```bash +# Example: Build for specific target +cross build --target x86_64-unknown-linux-gnu --release -p rln +``` + +## Used By + +Zerokit powers zero-knowledge functionality in: + +- [**nwaku**](https://github.com/waku-org/nwaku) - Nim implementation of the Waku v2 protocol +- [**js-rln**](https://github.com/waku-org/js-rln) - JavaScript bindings for RLN + +## Acknowledgements + +- Inspired by [Applied ZKP](https://zkp.science/) group work, including [zk-kit](https://github.com/appliedzkp/zk-kit) +- Uses [ark-circom](https://github.com/gakonst/ark-circom) for zkey and Groth16 proof generation +- Witness calculation based on [circom-witnesscalc](https://github.com/iden3/circom-witnesscalc) by iden3. +The execution graph file used by this code has been generated by means of the same iden3 software. + +> [!IMPORTANT] +> The circom-witnesscalc code fragments have been borrowed instead of depending on this crate, + because its types of input and output data were incompatible with the corresponding zerokit code fragments, + and circom-witnesscalc has some dependencies, which are redundant for our purpose. + +## Documentation + +For detailed documentation on each module: + +```bash +cargo doc --open +``` diff --git a/rln/README.md b/rln/README.md index cd0678b..c991bda 100644 --- a/rln/README.md +++ b/rln/README.md @@ -1,77 +1,17 @@ # Zerokit RLN Module -This module provides APIs to manage, compute and verify [RLN](https://rfc.vac.dev/spec/32/) zkSNARK proofs and RLN primitives. +[![Crates.io](https://img.shields.io/crates/v/rln.svg)](https://crates.io/crates/rln) -## Pre-requisites +The Zerokit RLN Module provides a Rust implementation for working with Rate-Limiting Nullifier [RLN](https://rfc.vac.dev/spec/32/) zkSNARK proofs and primitives. This module allows you to: -### Install dependencies and clone repo +- Generate and verify RLN proofs +- Work with Merkle trees for commitment storage +- Implement rate-limiting mechanisms for distributed systems -```sh -git clone https://github.com/vacp2p/zerokit.git -make installdeps -cd zerokit/rln -``` +## Quick Start -### Build and Test - -To build and test, run the following commands within the module folder - -``` bash - cargo make build - cargo make test -``` - -To run tests with a specific feature, use the following command: - -``` bash - cargo make build - cargo make test_{mode} -``` - -The {mode} placeholder corresponds to the feature name and should be replaced with - -* **arkzkey** for the tests with the arkzkey feature; -* **stateless** for the tests with the stateless feature. - -### Compile ZK circuits - -The `rln` () repository, which contains the RLN circuit implementation is a submodule of zerokit RLN. - -To compile the RLN circuit - -```sh -# Update submodules -git submodule update --init --recursive - -# Install rln dependencies -cd vendor/rln/ && npm install - -# Build circuits -./scripts/build-circuits.sh rln - -# Copy over assets -cp build/zkeyFiles/rln-final.zkey ../../resources/tree_height_15 -cp build/zkeyFiles/rln.wasm ../../resources/tree_height_15 -``` - -Note that the above code snippet will compile a RLN circuit with a Merkle tree of height equal `15` based on the default value set in `vendor/rln/circuit/rln.circom`. - -In order to compile a RLN circuit with Merkle tree height `N`, it suffices to change `vendor/rln/circuit/rln.circom` to - -```text -pragma circom 2.0.0; - -include "./rln-base.circom"; - -component main {public [x, epoch, rln_identifier ]} = RLN(N); -``` - -However, if `N` is too big, this might require a bigger Powers of Tau ceremony than the one hardcoded in `./scripts/build-circuits.sh`, which is `2^14`. -In such case we refer to the official [Circom documentation](https://docs.circom.io/getting-started/proving-circuits/#powers-of-tau) for instructions on how to run an appropriate Powers of Tau ceremony and Phase 2 in order to compile the desired circuit. - -Currently, the `rln` module comes with 2 [pre-compiled](https://github.com/vacp2p/zerokit/tree/master/rln/resources) RLN circuits having Merkle tree of height `20` and `32`, respectively. - -## Getting started +> [!IMPORTANT] +> Version 0.6.1 is required for WASM support or x32 architecture. Current version doesn't support these platforms due to dependency issues. WASM support will return in a future release. ### Add RLN as dependency @@ -82,134 +22,195 @@ We start by adding zerokit RLN to our `Cargo.toml` rln = { git = "https://github.com/vacp2p/zerokit" } ``` -### Create a RLN object +## Basic Usage Example -First, we need to create a RLN object for a chosen input Merkle tree size. - -Note that we need to pass to RLN object constructor the path where the circuit (`rln.wasm`, built for the input tree size), the corresponding proving key (`rln_final.zkey`) or (`rln_final.arkzkey`) and verification key (`verification_key.arkvkey`, optional) are found. +Note that we need to pass to RLN object constructor the path where the graph file (`graph.bin`, built for the input tree size), the corresponding proving key (`rln_final.zkey`) or (`rln_final_uncompr.arkzkey`) and verification key (`verification_key.arkvkey`, optional) are found. In the following we will use [cursors](https://doc.rust-lang.org/std/io/struct.Cursor.html) as readers/writers for interfacing with RLN public APIs. ```rust -use rln::protocol::*; -use rln::public::*; use std::io::Cursor; -// We set the RLN parameters: -// - the tree height; -// - the tree config, if it is not defined, the default value will be set -let tree_height = 20; -let input = Cursor::new(json!({}).to_string()); +use rln::{ + circuit::Fr, + hashers::{hash_to_field, poseidon_hash}, + protocol::{keygen, prepare_verify_input}, + public::RLN, + utils::{fr_to_bytes_le, normalize_usize}, +}; +use serde_json::json; -// We create a new RLN instance -let mut rln = RLN::new(tree_height, input); +fn main() { + // 1. Initialize RLN with parameters: + // - the tree height; + // - the tree config, if it is not defined, the default value will be set + let tree_height = 20; + let input = Cursor::new(json!({}).to_string()); + let mut rln = RLN::new(tree_height, input).unwrap(); + + // 2. Generate an identity keypair + let (identity_secret_hash, id_commitment) = keygen(); + + // 3. Add a rate commitment to the Merkle tree + let id_index = 10; + let user_message_limit = 10; + let rate_commitment = poseidon_hash(&[id_commitment, Fr::from(user_message_limit)]); + let mut buffer = Cursor::new(fr_to_bytes_le(&rate_commitment)); + rln.set_leaf(id_index, &mut buffer).unwrap(); + + // 4. Set up external nullifier (epoch + app identifier) + // We generate epoch from a date seed and we ensure is + // mapped to a field element by hashing-to-field its content + let epoch = hash_to_field(b"Today at noon, this year"); + // We generate rln_identifier from a date seed and we ensure is + // mapped to a field element by hashing-to-field its content + let rln_identifier = hash_to_field(b"test-rln-identifier"); + let external_nullifier = poseidon_hash(&[epoch, rln_identifier]); + + // 5. Generate and verify a proof for a message + let signal = b"RLN is awesome"; + + // 6. Prepare input for generate_rln_proof API + // input_data is [ identity_secret<32> | id_index<8> | external_nullifier<32> | user_message_limit<32> | message_id<32> | signal_len<8> | signal ] + let mut serialized: Vec = Vec::new(); + serialized.append(&mut fr_to_bytes_le(&identity_secret_hash)); + serialized.append(&mut normalize_usize(id_index)); + serialized.append(&mut fr_to_bytes_le(&Fr::from(user_message_limit))); + serialized.append(&mut fr_to_bytes_le(&Fr::from(1))); + serialized.append(&mut fr_to_bytes_le(&external_nullifier)); + serialized.append(&mut normalize_usize(signal.len())); + serialized.append(&mut signal.to_vec()); + + // 7. Generate a RLN proof + // We generate a RLN proof for proof_input + let mut input_buffer = Cursor::new(serialized); + let mut output_buffer = Cursor::new(Vec::::new()); + rln.generate_rln_proof(&mut input_buffer, &mut output_buffer) + .unwrap(); + + // We get the public outputs returned by the circuit evaluation + // The byte vector `proof_data` is serialized as `[ zk-proof | tree_root | external_nullifier | share_x | share_y | nullifier ]`. + let proof_data = output_buffer.into_inner(); + + // 8. Verify a RLN proof + // Input buffer is serialized as `[proof_data | signal_len | signal ]`, where `proof_data` is (computed as) the output obtained by `generate_rln_proof`. + let verify_data = prepare_verify_input(proof_data, signal); + + // We verify the zk-proof against the provided proof values + let mut input_buffer = Cursor::new(verify_data); + let verified = rln.verify_rln_proof(&mut input_buffer).unwrap(); + + // We ensure the proof is valid + assert!(verified); +} ``` -### Generate an identity keypair - -We generate an identity keypair - -```rust -// We generate an identity pair -let mut buffer = Cursor::new(Vec::::new()); -rln.key_gen(&mut buffer).unwrap(); - -// We deserialize the keygen output to obtain -// the identity_secret and id_commitment -let (identity_secret_hash, id_commitment) = deserialize_identity_pair(buffer.into_inner()); -``` - -### Add Rate commitment to the RLN Merkle tree - -```rust -// We define the tree index where id_commitment will be added -let id_index = 10; -let user_message_limit = 10; - -// We serialize id_commitment and pass it to set_leaf -let rate_commitment = poseidon_hash(&[id_commitment, user_message_limit]); -let mut buffer = Cursor::new(serialize_field_element(rate_commitment)); -rln.set_leaf(id_index, &mut buffer).unwrap(); -``` - -Note that when tree leaves are not explicitly set by the user (in this example, all those with index less and greater than `10`), their values is set to an hardcoded default (all-`0` bytes in current implementation). - -### Set external nullifier +### Comments for the code above for point 4 The `external nullifier` includes two parameters. -The first one is `epoch` and it's used to identify messages received in a certain time frame. It usually corresponds to the current UNIX time but can also be set to a random value or generated by a seed, provided that it corresponds to a field element. +The first one is `epoch` and it's used to identify messages received in a certain time frame. +It usually corresponds to the current UNIX time but can also be set to a random value or generated by a seed, provided that it corresponds to a field element. The second one is `rln_identifier` and it's used to prevent a RLN ZK proof generated for one application to be re-used in another one. -```rust -// We generate epoch from a date seed and we ensure is -// mapped to a field element by hashing-to-field its content -let epoch = hash_to_field(b"Today at noon, this year"); -// We generate rln_identifier from a date seed and we ensure is -// mapped to a field element by hashing-to-field its content -let rln_identifier = hash_to_field(b"test-rln-identifier"); +### Features -let external_nullifier = poseidon_hash(&[epoch, rln_identifier]); +- **Multiple Backend Support**: Choose between different zkey formats with feature flags + - `arkzkey`: Use the optimized Arkworks-compatible zkey format (faster loading) + - `stateless`: For stateless proof verification +- **Pre-compiled Circuits**: Ready-to-use circuits with Merkle tree height of 20 + +## Building and Testing + +### Prerequisites + +```sh +git clone https://github.com/vacp2p/zerokit.git +make installdeps +cd zerokit/rln ``` -### Set signal +### Build Commands -The signal is the message for which we are computing a RLN proof. +```sh +# Build with default features +cargo make build -```rust -// We set our signal -let signal = b"RLN is awesome"; +# Test with default features +cargo make test + +# Test with specific features +cargo make test_arkzkey # For arkzkey feature +cargo make test_stateless # For stateless feature ``` -### Generate a RLN proof +## Advanced: Custom Circuit Compilation -We prepare the input to the proof generation routine. +The `rln` () repository, which contains the RLN circuit implementation is using for pre-compiled RLN circuit for zerokit RLN. +If you want to compile your own RLN circuit, you can follow the instructions below. -Input buffer is serialized as `[ identity_key | id_index | external_nullifier | user_message_limit | message_id | signal_len | signal ]`. +### 1. Compile ZK Circuits for getting the zkey and verification key files -```rust -// We prepare input to the proof generation routine -let proof_input = prepare_prove_input(identity_secret_hash, id_index, external_nullifier, signal); +This script actually generates not only the zkey and verification key files for the RLN circuit, but also the execution wasm file used for witness calculation. +However, the wasm file is not needed for the `rln` module, because current implementation uses the iden3 graph file for witness calculation. +This graph file is generated by the `circom-witnesscalc` tool in step 2. + +To customize the circuit parameters, modify `circom-rln/circuit/rln.circom`: + +```circom +pragma circom 2.1.0; +include "./rln.circom"; +component main { public [x, externalNullifier] } = RLN(N, M); // N = tree height, M = limit bit size ``` -We are now ready to generate a RLN ZK proof along with the _public outputs_ of the ZK circuit evaluation. +> [!NOTE] +> However, if `N` is too big, this might require a bigger Powers of Tau ceremony than the one hardcoded in `./scripts/build-circuits.sh`, which is `2^14`. \ +> In such case we refer to the official [Circom documentation](https://docs.circom.io/getting-started/proving-circuits/#powers-of-tau) for instructions on how to run an appropriate Powers of Tau ceremony and Phase 2 in order to compile the desired circuit. \ +> Currently, the `rln` module comes with one [pre-compiled](https://github.com/vacp2p/zerokit/tree/master/rln/resources) RLN circuit having Merkle tree of height `20`. -```rust +#### Generate the zkey and verification key files example -// We generate a RLN proof for proof_input -let mut in_buffer = Cursor::new(proof_input); -let mut out_buffer = Cursor::new(Vec::::new()); -rln.generate_rln_proof(&mut in_buffer, &mut out_buffer) - .unwrap(); +```sh +# Clone the circom-rln repository +git clone https://github.com/rate-limiting-nullifier/circom-rln -// We get the public outputs returned by the circuit evaluation -let proof_data = out_buffer.into_inner(); +# Install dependencies +cd circom-rln && npm install + +# Build circuits +./scripts/build-circuits.sh rln + +# Copy assets to resources directory +cp build/zkeyFiles/final.zkey ./resources/tree_height_20/rln_final.zkey ``` -The byte vector `proof_data` is serialized as `[ zk-proof | tree_root | external_nullifier | share_x | share_y | nullifier ]`. +### 2. Generate Witness Calculation Graph -### Verify a RLN proof +The execution graph file used for witness calculation can be compiled following instructions in the [circom-witnesscalc](https://github.com/iden3/circom-witnesscalc) repository. +As mentioned in step 1, we should use `rln.circom` file from `circom-rln` repository. -We prepare the input to the proof verification routine. +```sh +# Clone the circom-witnesscalc repository +git clone https://github.com/iden3/circom-witnesscalc -Input buffer is serialized as `[proof_data | signal_len | signal ]`, where `proof_data` is (computed as) the output obtained by `generate_rln_proof`. +# Load the submodules +git submodule update --init --recursive -```rust -// We prepare input to the proof verification routine -let verify_data = prepare_verify_input(proof_data, signal); +# Build the circom-witnesscalc tool +cargo build -// We verify the zk-proof against the provided proof values -let mut in_buffer = Cursor::new(verify_data); -let verified = rln.verify(&mut in_buffer).unwrap(); +# Generate the witness calculation graph +cargo run --package circom_witnesscalc --bin build-circuit ../circom-rln/circuits/rln.circom ``` -We check if the proof verification was successful: +The `rln` module comes with [pre-compiled](https://github.com/vacp2p/zerokit/tree/master/rln/resources) execution graph files for the RLN circuit. -```rust -// We ensure the proof is valid -assert!(verified); -``` +### 3. Generate Arkzkey Representation for zkey and verification key files + +For faster loading, compile the zkey file into the arkzkey format using [ark-zkey](https://github.com/zkmopro/ark-zkey). + +Currently, the `rln` module comes with [pre-compiled](https://github.com/vacp2p/zerokit/tree/master/rln/resources) arkzkey keys for the RLN circuit. ## Get involved @@ -222,3 +223,20 @@ cargo doc --no-deps ``` and look at unit tests to have an hint on how to interface and use them. + +## Detailed Protocol Flow + +1. **Identity Creation**: Generate a secret key and commitment +2. **Rate Commitment**: Add commitment to a Merkle tree +3. **External Nullifier Setup**: Combine epoch and application identifier +4. **Proof Generation**: Create a zkSNARK proof that: + - Proves membership in the Merkle tree + - Ensures rate-limiting constraints are satisfied + - Generates a nullifier to prevent double-usage +5. **Proof Verification**: Verify the proof without revealing the prover's identity + +## Getting Involved + +- Check the [unit tests](https://github.com/vacp2p/zerokit/tree/master/rln/tests) for more usage examples +- [RFC specification](https://rfc.vac.dev/spec/32/) for the Rate-Limiting Nullifier protocol +- [GitHub repository](https://github.com/vacp2p/zerokit) for the latest updates diff --git a/rln/src/protocol.rs b/rln/src/protocol.rs index d5b975c..f1cb493 100644 --- a/rln/src/protocol.rs +++ b/rln/src/protocol.rs @@ -227,7 +227,7 @@ pub fn proof_inputs_to_rln_witness( )) } -/// Creates `RLNWitnessInput` from it's fields. +/// Creates [`RLNWitnessInput`] from it's fields. /// /// # Errors /// @@ -746,7 +746,7 @@ where a.map_err(serde::de::Error::custom) } -/// Converts a JSON value into [`RLNWitnessInput`](crate::protocol::RLNWitnessInput) object. +/// Converts a JSON value into [`RLNWitnessInput`] object. /// /// # Errors /// @@ -758,7 +758,7 @@ pub fn rln_witness_from_json(input_json: serde_json::Value) -> Result Result | id_index<8> | user_message_limit<32> | message_id<32> | external_nullifier<32> | signal_len<8> | signal ]` /// - /// The function returns the corresponding [`RLNWitnessInput`](crate::protocol::RLNWitnessInput) object serialized using [`rln::protocol::serialize_witness`](crate::protocol::serialize_witness). + /// The function returns the corresponding [`RLNWitnessInput`] object serialized using [`rln::protocol::serialize_witness`](crate::protocol::serialize_witness). #[cfg(not(feature = "stateless"))] pub fn get_serialized_rln_witness(&mut self, mut input_data: R) -> Result> { // We read input RLN witness and we serialize_compressed it @@ -1268,24 +1268,24 @@ impl RLN { serialize_witness(&rln_witness) } - /// Converts a byte serialization of a [`RLNWitnessInput`](crate::protocol::RLNWitnessInput) object to the corresponding JSON serialization. + /// Converts a byte serialization of a [`RLNWitnessInput`] object to the corresponding JSON serialization. /// /// Input values are: - /// - `serialized_witness`: the byte serialization of a [`RLNWitnessInput`](crate::protocol::RLNWitnessInput) object (serialization done with [`rln::protocol::serialize_witness`](crate::protocol::serialize_witness)). + /// - `serialized_witness`: the byte serialization of a [`RLNWitnessInput`] object (serialization done with [`rln::protocol::serialize_witness`](crate::protocol::serialize_witness)). /// - /// The function returns the corresponding JSON encoding of the input [`RLNWitnessInput`](crate::protocol::RLNWitnessInput) object. + /// The function returns the corresponding JSON encoding of the input [`RLNWitnessInput`] object. pub fn get_rln_witness_json(&mut self, serialized_witness: &[u8]) -> Result { let (rln_witness, _) = deserialize_witness(serialized_witness)?; rln_witness_to_json(&rln_witness) } - /// Converts a byte serialization of a [`RLNWitnessInput`](crate::protocol::RLNWitnessInput) object to the corresponding JSON serialization. + /// Converts a byte serialization of a [`RLNWitnessInput`] object to the corresponding JSON serialization. /// Before serialization the data will be translated into big int for further calculation in the witness calculator. /// /// Input values are: - /// - `serialized_witness`: the byte serialization of a [`RLNWitnessInput`](crate::protocol::RLNWitnessInput) object (serialization done with [`rln::protocol::serialize_witness`](crate::protocol::serialize_witness)). + /// - `serialized_witness`: the byte serialization of a [`RLNWitnessInput`] object (serialization done with [`rln::protocol::serialize_witness`](crate::protocol::serialize_witness)). /// - /// The function returns the corresponding JSON encoding of the input [`RLNWitnessInput`](crate::protocol::RLNWitnessInput) object. + /// The function returns the corresponding JSON encoding of the input [`RLNWitnessInput`] object. pub fn get_rln_witness_bigint_json( &mut self, serialized_witness: &[u8], diff --git a/utils/README.md b/utils/README.md index 6e521e0..88b682a 100644 --- a/utils/README.md +++ b/utils/README.md @@ -1,15 +1,77 @@ -# Utils crate +# Zerokit Utils Crate -## Building +[![Crates.io](https://img.shields.io/crates/v/zerokit_utils.svg)](https://crates.io/crates/zerokit_utils) -1. `cargo build` +Cryptographic primitives for zero-knowledge applications, featuring efficient Merkle tree implementations and a Poseidon hash function. -## Testing +## Overview -1. `cargo test` +This crate provides core cryptographic components optimized for zero-knowledge proof systems: -## Benchmarking +1. Multiple Merkle tree implementations with different space/time tradeoffs +2. A Poseidon hash implementation -1. `cargo bench` +## Merkle Tree Implementations + +The crate supports two interchangeable Merkle tree implementations: + +- **FullMerkleTree** + - Stores each tree node in memory +- **OptimalMerkleTree** + - Only stores nodes used to prove accumulation of set leaves + +## Poseidon Hash Implementation + +This crate provides an implementation to compute the Poseidon hash round constants and MDS matrices: + +- **Customizable parameters**: Supports different security levels and input sizes +- **Arkworks-friendly**: Adapted to work over arkworks field traits and custom data structures + +### Security Note + +The MDS matrices are generated iteratively using the Grain LFSR until certain criteria are met. +According to the paper, such matrices must respect specific conditions which are checked by 3 different algorithms in the reference implementation. + +These validation algorithms are not currently implemented in this crate. +For the hardcoded parameters, the first random matrix generated satisfies these conditions. +If using different parameters, you should check against the reference implementation how many matrices are generated before outputting the correct one, +and pass this number to the `skip_matrices` parameter of the `find_poseidon_ark_and_mds` function. + +## Installation + +Add Zerokit Utils to your Rust project: + +```toml +[dependencies] +zerokit-utils = "0.5.1" +``` + +## Performance Considerations + +- **FullMerkleTree**: Use when memory is abundant and operation speed is critical +- **OptimalMerkleTree**: Use when memory efficiency is more important than raw speed +- **Poseidon**: Offers a good balance between security and performance for ZK applications + +## Building and Testing + +```bash +# Build the crate +cargo build + +# Run tests +cargo test + +# Run benchmarks +cargo bench +``` To view the results of the benchmark, open the `target/criterion/report/index.html` file generated after the bench + +## Acknowledgements + +- The Merkle tree implementations are adapted from: + - [kilic/rln](https://github.com/kilic/rln/blob/master/src/merkle.rs) + - [worldcoin/semaphore-rs](https://github.com/worldcoin/semaphore-rs/blob/d462a4372f1fd9c27610f2acfe4841fab1d396aa/src/merkle_tree.rs) + +- The Poseidon implementation references: + - [Poseidon reference implementation](https://extgit.iaik.tugraz.at/krypto/hadeshash/-/blob/master/code/generate_parameters_grain.sage)