[
  {
    "path": ".github/workflows/build-and-test.yml",
    "content": "name: Build and Test\non: push\n\nenv:\n  RUST_VERSION: 1.94.0\n\njobs:\n  dependabot-dependency-review:\n    runs-on: ubuntu-latest\n    steps:\n      - name: \"Checkout Repository\"\n        uses: actions/checkout@v4\n      - name: \"Dependency Review\"\n        uses: actions/dependency-review-action@v4\n        with:\n          base-ref: ${{ inputs.base-ref || github.event.pull_request.base.sha || 'main' }}\n          head-ref: ${{ inputs.head-ref || github.event.pull_request.head.sha || github.ref }}\n\n  test:\n    name: Test\n    runs-on: ubuntu-latest\n    env:\n      RUSTFLAGS: \"-D warnings\"\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@v4\n\n      - name: Set up Rust\n        run: |\n          rustup update ${{ env.RUST_VERSION }} && rustup default ${{ env.RUST_VERSION }} && rustup component add rustfmt --toolchain ${{ env.RUST_VERSION }} && rustup component add clippy --toolchain ${{ env.RUST_VERSION }}\n\n      - name: Cache Cargo registry\n        uses: actions/cache@v4\n        with:\n          path: ~/.cargo/registry\n          key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.toml') }}\n          restore-keys: |\n            ${{ runner.os }}-cargo-registry-\n\n      - name: Cache Cargo index\n        uses: actions/cache@v4\n        with:\n          path: ~/.cargo/git\n          key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.toml') }}\n          restore-keys: |\n            ${{ runner.os }}-cargo-index-\n\n      - name: Cache Cargo build\n        uses: actions/cache@v4\n        with:\n          path: target\n          key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.toml') }}\n          restore-keys: |\n            ${{ runner.os }}-cargo-build-\n\n      - name: Check\n        run: cargo check --workspace --tests --benches --features depth_16,depth_20,depth_30\n\n      - name: Check Formatting\n        run: cargo fmt --all -- --check\n\n      - name: Run Clippy\n        run: cargo clippy --all-targets --features depth_16,depth_20,depth_30\n\n      - name: Run test\n        run: cargo test --workspace --features depth_16,depth_20,depth_30\n\n  wasm:\n    name: WASM Check\n    runs-on: ubuntu-latest\n    env:\n      RUSTFLAGS: \"-D warnings\"\n    steps:\n      - name: Checkout Repository\n        uses: actions/checkout@v4\n\n      - name: Set up Rust\n        run: |\n          rustup update ${{ env.RUST_VERSION }} && rustup default ${{ env.RUST_VERSION }}\n          rustup target add wasm32-unknown-unknown\n\n      - name: Cache Cargo registry\n        uses: actions/cache@v4\n        with:\n          path: ~/.cargo/registry\n          key: ${{ runner.os }}-wasm-cargo-registry-${{ hashFiles('**/Cargo.toml') }}\n          restore-keys: |\n            ${{ runner.os }}-wasm-cargo-registry-\n\n      - name: Cache Cargo index\n        uses: actions/cache@v4\n        with:\n          path: ~/.cargo/git\n          key: ${{ runner.os }}-wasm-cargo-index-${{ hashFiles('**/Cargo.toml') }}\n          restore-keys: |\n            ${{ runner.os }}-wasm-cargo-index-\n\n      - name: Cache Cargo build\n        uses: actions/cache@v4\n        with:\n          path: target\n          key: ${{ runner.os }}-wasm-cargo-build-${{ hashFiles('**/Cargo.toml') }}\n          restore-keys: |\n            ${{ runner.os }}-wasm-cargo-build-\n\n      - name: Check (storage)\n        run: cargo check --target wasm32-unknown-unknown -p semaphore-rs-storage\n\n      - name: Check (trees)\n        run: cargo check --target wasm32-unknown-unknown -p semaphore-rs-trees\n\n      - name: Check (semaphore-rs)\n        run: cargo check --target wasm32-unknown-unknown -p semaphore-rs --features depth_16\n\n  # vet:\n  #   name: Vet Dependencies\n  #   runs-on: ubuntu-latest\n  #   steps:\n  #   - uses: actions/checkout@master\n  #   - name: Install Rust\n  #     uses: actions-rs/toolchain@v1\n  #     with:\n  #       profile: minimal\n  #       toolchain: ${{ env.RUST_VERSION }}\n  #       override: true\n  #   - uses: actions-rs/cargo@v1\n  #     with:\n  #       command: build\n  #   - uses: actions/cache@v3\n  #     with:\n  #       path: |\n  #         ~/.cargo/registry/index/\n  #         ~/.cargo/registry/cache/\n  #         ~/.cargo/git/db/\n  #         target/\n  #       key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}\n  #   - name: Install cargo-vet\n  #     run: cargo install cargo-vet --version ${{ env.CARGO_VET_VERSION }} --git ${{ env.CARGO_VET_REPO }}\n  #   - name: Prune (If some import got updated)\n  #     run: cargo vet prune\n  #   - name: Invoke cargo-vet\n  #     run: cargo vet\n"
  },
  {
    "path": ".github/workflows/release-crates.yml",
    "content": "name: Release Crates\n\non:\n  push:\n    branches:\n      - main\n  workflow_dispatch:\n\n# Prevent concurrent release runs — release-plz is not safe to run in parallel\n# against the same repo state. Keep in-progress runs; don't cancel them.\nconcurrency:\n  group: release-plz\n  cancel-in-progress: false\n\njobs:\n  release-crates:\n    name: Release Crates\n    runs-on: ubuntu-latest\n    permissions:\n      contents: write\n      pull-requests: write\n      id-token: write\n    steps:\n      - name: Checkout code\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n          token: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Install Rust toolchain\n        uses: dtolnay/rust-toolchain@stable\n\n      - name: Run release-plz\n        uses: release-plz/action@1efcf74dfcd6e500990dad806e286899ae384064 # v0.5.119 (https://github.com/release-plz/action/releases/tag/v0.5.119)\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".github/workflows/relyance-sci.yml",
    "content": "name: Relyance SCI Scan\n\non:\n  schedule:\n    - cron: \"30 0 * * *\"\n  workflow_dispatch:\n\npermissions:\n  contents: read\n\njobs:\n  execute-relyance-sci:\n    name: Relyance SCI Job\n    runs-on:\n      group: arc-public-large-amd64-runner\n    permissions:\n      contents: read\n\n    steps:\n      - name: Run Relyance SCI\n        uses: worldcoin/gh-actions-public/relyance@main\n        # More information: https://github.com/worldcoin/gh-actions-public/tree/main/relyance\n        with:\n          secrets-dpp-sci-key: ${{ secrets.DPP_SCI_KEY }}\n"
  },
  {
    "path": ".gitignore",
    "content": "/target\n*.profraw\nsnarkfiles_tmp\nsemaphore_files\n.idea\nlcov.info\n*.DS_Store\n\nCargo.lock\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [Unreleased]\n\n## [0.5.3](https://github.com/worldcoin/semaphore-rs/compare/semaphore-rs-v0.5.2...semaphore-rs-v0.5.3) - 2026-03-20\n\n### Other\n\n- Optimize auth proofs and restore depth test macros ([#137](https://github.com/worldcoin/semaphore-rs/pull/137))\n\n## [0.5.2](https://github.com/worldcoin/semaphore-rs/compare/semaphore-rs-v0.5.1...semaphore-rs-v0.5.2) - 2026-03-20\n\n### Other\n\n- update Cargo.toml dependencies\n\n## [0.5.1](https://github.com/worldcoin/semaphore-rs/compare/semaphore-rs-v0.5.0...semaphore-rs-v0.5.1) - 2026-03-16\n\n### Added\n\n- gate mmap-rs and lazy trees for WASM compatibility ([#131](https://github.com/worldcoin/semaphore-rs/pull/131))\n- cascade improvements ([#130](https://github.com/worldcoin/semaphore-rs/pull/130))\n- check for pushing past tree depth\n- check for empty range\n\n### Other\n\n- satisfy clippy\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[workspace]\nmembers = [\"crates/*\"]\nresolver = \"2\"\n\n[workspace.package]\nversion = \"0.5.3\"\nedition = \"2021\"\nhomepage = \"https://github.com/worldcoin/semaphore-rs\"\nlicense = \"MIT\"\nrepository = \"https://github.com/worldcoin/semaphore-rs\"\nauthors = [\n  \"Remco Bloemen <remco@worldcoin.org>\",\n  \"Philipp Sippl <philipp@worldcoin.org>\",\n]\ndescription = \"Rust support library for Semaphore\"\nkeywords = [\"worldcoin\", \"protocol\", \"signup\"]\ncategories = [\"cryptography\"]\n\n[workspace.dependencies]\n# Internal\nsemaphore-rs-utils = { version = \"0.5.3\", path = \"crates/utils\" }\nsemaphore-rs-ark-circom = { version = \"0.5.3\", path = \"crates/ark-circom\" }\nsemaphore-rs-ark-zkey = { version = \"0.5.3\", path = \"crates/ark-zkey\" }\nsemaphore-rs-proof = { version = \"0.5.3\", path = \"crates/proof\", default-features = false }\nsemaphore-rs-poseidon = { version = \"0.5.3\", path = \"crates/poseidon\" }\nsemaphore-rs-hasher = { version = \"0.5.3\", path = \"crates/hasher\" }\nsemaphore-rs-keccak = { version = \"0.5.3\", path = \"crates/keccak\" }\nsemaphore-rs-trees = { version = \"0.5.3\", path = \"crates/trees\" }\nsemaphore-rs-storage = { version = \"0.5.3\", path = \"crates/storage\" }\nsemaphore-rs-depth-config = { version = \"0.5.3\", path = \"crates/semaphore-depth-config\" }\nsemaphore-rs-depth-macros = { version = \"0.5.3\", path = \"crates/semaphore-depth-macros\" }\nsemaphore-rs-witness = { version = \"0.5.3\", path = \"crates/circom-witness-rs\" }\n\n# 3rd Party\nalloy-core = { version = \"1.0\", default-features = false, features = [\n  \"sol-types\",\n] }\nbincode = \"1.3.3\"\nbytemuck = \"1.18\"\nbyteorder = \"1\"\ncolor-eyre = \"0.6\"\ncriterion = { version = \"0.5\", features = [\"async_tokio\", \"html_reports\"] }\ncxx = \"1\"\ncxx-build = \"1\"\nderive-where = \"1.6.0\"\nhex = \"0.4.0\"\nhex-literal = \"0.4\"\nitertools = \"0.13\"\nlazy_static = \"1\"\nmmap-rs = \"0.6.1\"\nnum-bigint = { version = \"0.4\", default-features = false, features = [\"rand\"] }\nnum-traits = \"0.2.19\"\nonce_cell = \"1.8\"\npostcard = \"1\"\nproptest = \"1.0\"\nrand = { version = \"0.8.4\", features = [\"small_rng\"] }\nrand_chacha = \"0.3.1\"\nrayon = \"1.5.1\"\nreqwest = { version = \"0.12\", default-features = false, features = [\n  \"blocking\",\n  \"rustls-tls\",\n] }\nruint = { version = \"1.12.3\", features = [\n  \"rand\",\n  \"bytemuck\",\n  \"serde\",\n  \"num-bigint\",\n  \"ark-ff-05\",\n] }\nserde = { version = \"1.0\", features = [\"derive\"] }\nserde_json = \"1.0.79\"\nserial_test = \"3\"\nsha2 = \"0.10.1\"\ntest-case = \"3.3.1\"\ntempfile = \"3.0\"\nthiserror = \"1.0.0\"\ntiny-keccak = { version = \"2.0.2\", features = [\"sha3\", \"keccak\"] }\ntokio = \"1\"\ntracing-test = \"0.2\"\nzeroize = \"1.6.0\"\nmemmap2 = \"0.9\"\nflame = \"0.2\"\nflamer = \"0.5\"\n\n# WASM\nwasm-bindgen = \"0.2\"\njs-sys = \"0.3\"\n# Required if we're compiling to WASM\ngetrandom = { version = \"0.2.15\", features = [\"js\"] }\nwasm-bindgen-test = \"0.3\"\n\n# Proc macros\nsyn = { version = \"2.0.9\", features = [\"full\", \"visit-mut\", \"extra-traits\"] }\nproc-macro2 = \"1.0.53\"\nquote = \"1.0.26\"\n\n# Ark\nark-bn254 = { version = \"0.5.0\" }\nark-ec = { version = \"0.5.0\", default-features = false, features = [\n  \"parallel\",\n] }\nark-ff = { version = \"0.5.0\", default-features = false, features = [\n  \"parallel\",\n  \"asm\",\n] }\nark-groth16 = { version = \"0.5.0\", features = [\"parallel\"] }\nark-relations = { version = \"0.5.0\", default-features = false }\nark-std = { version = \"0.5.0\", default-features = false, features = [\n  \"parallel\",\n] }\nark-serialize = { version = \"0.5.0\", features = [\"derive\"] }\nark-poly = { version = \"0.5.0\" }\nark-crypto-primitives = { version = \"0.5.0\" }\n\n[profile.release]\ncodegen-units = 1\nlto = true\npanic = \"abort\"\nopt-level = 3\n\n# Compilation profile for any non-workspace member.\n# Dependencies are optimized, even in a dev build. This improves dev performance\n# while having neglible impact on incremental build times.\n[profile.dev.package.\"*\"]\nopt-level = 3\n"
  },
  {
    "path": "crates/ark-circom/Cargo.toml",
    "content": "[package]\nname = \"semaphore-rs-ark-circom\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nauthors.workspace = true\ndescription.workspace = true\nkeywords.workspace = true\ncategories.workspace = true\n\n[dependencies]\nark-ff.workspace = true\nark-relations.workspace = true\nark-serialize.workspace = true\nark-std.workspace = true\nark-bn254.workspace = true\nark-groth16.workspace = true\nark-poly.workspace = true\nark-crypto-primitives.workspace = true\n\nruint.workspace = true\nserde_json.workspace = true\nbyteorder.workspace = true\nnum-traits.workspace = true\nnum-bigint.workspace = true\n\nthiserror.workspace = true\n\n[features]\n# This features does nothing but the ark_std::cfg_into_iter expectes it\nparallel = []\n"
  },
  {
    "path": "crates/ark-circom/src/circom.rs",
    "content": "use ark_ff::PrimeField;\nuse ark_groth16::r1cs_to_qap::{evaluate_constraint, LibsnarkReduction, R1CSToQAP};\nuse ark_poly::EvaluationDomain;\nuse ark_relations::r1cs::{ConstraintMatrices, ConstraintSystemRef, SynthesisError};\nuse ark_std::{cfg_into_iter, cfg_iter, cfg_iter_mut, vec};\n\n/// Implements the witness map used by snarkjs. The arkworks witness map calculates the\n/// coefficients of H through computing (AB-C)/Z in the evaluation domain and going back to the\n/// coefficients domain. snarkjs instead precomputes the Lagrange form of the powers of tau bases\n/// in a domain twice as large and the witness map is computed as the odd coefficients of (AB-C)\n/// in that domain. This serves as HZ when computing the C proof element.\npub struct CircomReduction;\n\nimpl R1CSToQAP for CircomReduction {\n    #[allow(clippy::type_complexity)]\n    fn instance_map_with_evaluation<F: PrimeField, D: EvaluationDomain<F>>(\n        cs: ConstraintSystemRef<F>,\n        t: &F,\n    ) -> Result<(Vec<F>, Vec<F>, Vec<F>, F, usize, usize), SynthesisError> {\n        LibsnarkReduction::instance_map_with_evaluation::<F, D>(cs, t)\n    }\n\n    fn witness_map_from_matrices<F: PrimeField, D: EvaluationDomain<F>>(\n        matrices: &ConstraintMatrices<F>,\n        num_inputs: usize,\n        num_constraints: usize,\n        full_assignment: &[F],\n    ) -> Result<Vec<F>, SynthesisError> {\n        let zero = F::zero();\n        let domain =\n            D::new(num_constraints + num_inputs).ok_or(SynthesisError::PolynomialDegreeTooLarge)?;\n        let domain_size = domain.size();\n\n        let mut a = vec![zero; domain_size];\n        let mut b = vec![zero; domain_size];\n\n        cfg_iter_mut!(a[..num_constraints])\n            .zip(cfg_iter_mut!(b[..num_constraints]))\n            .zip(cfg_iter!(&matrices.a))\n            .zip(cfg_iter!(&matrices.b))\n            .for_each(|(((a, b), at_i), bt_i)| {\n                *a = evaluate_constraint(at_i, full_assignment);\n                *b = evaluate_constraint(bt_i, full_assignment);\n            });\n\n        {\n            let start = num_constraints;\n            let end = start + num_inputs;\n            a[start..end].clone_from_slice(&full_assignment[..num_inputs]);\n        }\n\n        let mut c = vec![zero; domain_size];\n        cfg_iter_mut!(c[..num_constraints])\n            .zip(&a)\n            .zip(&b)\n            .for_each(|((c_i, &a), &b)| {\n                *c_i = a * b;\n            });\n\n        domain.ifft_in_place(&mut a);\n        domain.ifft_in_place(&mut b);\n\n        let root_of_unity = {\n            let domain_size_double = 2 * domain_size;\n            let domain_double =\n                D::new(domain_size_double).ok_or(SynthesisError::PolynomialDegreeTooLarge)?;\n            domain_double.element(1)\n        };\n        D::distribute_powers_and_mul_by_const(&mut a, root_of_unity, F::one());\n        D::distribute_powers_and_mul_by_const(&mut b, root_of_unity, F::one());\n\n        domain.fft_in_place(&mut a);\n        domain.fft_in_place(&mut b);\n\n        let mut ab = domain.mul_polynomials_in_evaluation_domain(&a, &b);\n        drop(a);\n        drop(b);\n\n        domain.ifft_in_place(&mut c);\n        D::distribute_powers_and_mul_by_const(&mut c, root_of_unity, F::one());\n        domain.fft_in_place(&mut c);\n\n        cfg_iter_mut!(ab)\n            .zip(c)\n            .for_each(|(ab_i, c_i)| *ab_i -= &c_i);\n\n        Ok(ab)\n    }\n\n    fn h_query_scalars<F: PrimeField, D: EvaluationDomain<F>>(\n        max_power: usize,\n        t: F,\n        _: F,\n        delta_inverse: F,\n    ) -> Result<Vec<F>, SynthesisError> {\n        // the usual H query has domain-1 powers. Z has domain powers. So HZ has 2*domain-1 powers.\n        let mut scalars = cfg_into_iter!(0..2 * max_power + 1)\n            .map(|i| delta_inverse * t.pow([i as u64]))\n            .collect::<Vec<_>>();\n        let domain_size = scalars.len();\n        let domain = D::new(domain_size).ok_or(SynthesisError::PolynomialDegreeTooLarge)?;\n        // generate the lagrange coefficients\n        domain.ifft_in_place(&mut scalars);\n        Ok(cfg_into_iter!(scalars).skip(1).step_by(2).collect())\n    }\n}\n"
  },
  {
    "path": "crates/ark-circom/src/ethereum.rs",
    "content": "//! Helpers for converting Arkworks types to U256-tuples as expected by the\n//! Solidity Groth16 Verifier smart contracts\nuse ark_ff::{BigInteger, PrimeField};\nuse num_traits::Zero;\n\nuse ark_bn254::{Bn254, Fq, Fq2, Fr, G1Affine, G2Affine};\nuse ark_serialize::CanonicalDeserialize;\nuse ruint::aliases::U256;\nuse thiserror::Error;\n\n#[derive(Error, Debug)]\npub enum AffineError {\n    #[error(\"point is not on curve\")]\n    NotOnCurve,\n    #[error(\"point is not in correct subgroup\")]\n    NotInCorrectSubgroup,\n}\n\npub struct Inputs(pub Vec<U256>);\n\nimpl From<&[Fr]> for Inputs {\n    fn from(src: &[Fr]) -> Self {\n        let els = src.iter().map(|point| point_to_u256(*point)).collect();\n\n        Self(els)\n    }\n}\n\n#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]\npub struct G1 {\n    pub x: U256,\n    pub y: U256,\n}\n\nimpl TryFrom<G1> for G1Affine {\n    type Error = AffineError;\n\n    fn try_from(value: G1) -> Result<Self, Self::Error> {\n        let x: Fq = u256_to_point(value.x);\n        let y: Fq = u256_to_point(value.y);\n        if x.is_zero() && y.is_zero() {\n            Ok(G1Affine::identity())\n        } else {\n            let point = G1Affine {\n                x,\n                y,\n                infinity: false,\n            };\n            if !point.is_on_curve() {\n                return Err(AffineError::NotOnCurve);\n            }\n            if !point.is_in_correct_subgroup_assuming_on_curve() {\n                return Err(AffineError::NotInCorrectSubgroup);\n            }\n            Ok(point)\n        }\n    }\n}\n\ntype G1Tup = (U256, U256);\n\nimpl G1 {\n    pub fn as_tuple(&self) -> (U256, U256) {\n        (self.x, self.y)\n    }\n}\n\nimpl From<&G1Affine> for G1 {\n    fn from(p: &G1Affine) -> Self {\n        Self {\n            x: point_to_u256(p.x),\n            y: point_to_u256(p.y),\n        }\n    }\n}\n\n#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]\npub struct G2 {\n    pub x: [U256; 2],\n    pub y: [U256; 2],\n}\n\nimpl TryFrom<G2> for G2Affine {\n    type Error = AffineError;\n\n    fn try_from(src: G2) -> Result<G2Affine, AffineError> {\n        let c0 = u256_to_point(src.x[0]);\n        let c1 = u256_to_point(src.x[1]);\n        let x = Fq2::new(c0, c1);\n\n        let c0 = u256_to_point(src.y[0]);\n        let c1 = u256_to_point(src.y[1]);\n        let y = Fq2::new(c0, c1);\n\n        if x.is_zero() && y.is_zero() {\n            Ok(G2Affine::identity())\n        } else {\n            let point = G2Affine {\n                x,\n                y,\n                infinity: false,\n            };\n            if !point.is_on_curve() {\n                return Err(AffineError::NotOnCurve);\n            }\n            if !point.is_in_correct_subgroup_assuming_on_curve() {\n                return Err(AffineError::NotInCorrectSubgroup);\n            }\n            Ok(point)\n        }\n    }\n}\n\ntype G2Tup = ([U256; 2], [U256; 2]);\n\nimpl G2 {\n    // NB: Serialize the c1 limb first.\n    pub fn as_tuple(&self) -> G2Tup {\n        ([self.x[1], self.x[0]], [self.y[1], self.y[0]])\n    }\n}\n\nimpl From<&G2Affine> for G2 {\n    fn from(p: &G2Affine) -> Self {\n        Self {\n            x: [point_to_u256(p.x.c0), point_to_u256(p.x.c1)],\n            y: [point_to_u256(p.y.c0), point_to_u256(p.y.c1)],\n        }\n    }\n}\n\n#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]\npub struct Proof {\n    pub a: G1,\n    pub b: G2,\n    pub c: G1,\n}\n\nimpl Proof {\n    pub fn as_tuple(&self) -> (G1Tup, G2Tup, G1Tup) {\n        (self.a.as_tuple(), self.b.as_tuple(), self.c.as_tuple())\n    }\n}\n\nimpl From<ark_groth16::Proof<Bn254>> for Proof {\n    fn from(proof: ark_groth16::Proof<Bn254>) -> Self {\n        Self {\n            a: G1::from(&proof.a),\n            b: G2::from(&proof.b),\n            c: G1::from(&proof.c),\n        }\n    }\n}\n\nimpl TryFrom<Proof> for ark_groth16::Proof<Bn254> {\n    type Error = AffineError;\n\n    fn try_from(src: Proof) -> Result<ark_groth16::Proof<Bn254>, AffineError> {\n        Ok(ark_groth16::Proof {\n            a: src.a.try_into()?,\n            b: src.b.try_into()?,\n            c: src.c.try_into()?,\n        })\n    }\n}\n\n#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord)]\npub struct VerifyingKey {\n    pub alpha1: G1,\n    pub beta2: G2,\n    pub gamma2: G2,\n    pub delta2: G2,\n    pub ic: Vec<G1>,\n}\n\nimpl VerifyingKey {\n    pub fn as_tuple(&self) -> (G1Tup, G2Tup, G2Tup, G2Tup, Vec<G1Tup>) {\n        (\n            self.alpha1.as_tuple(),\n            self.beta2.as_tuple(),\n            self.gamma2.as_tuple(),\n            self.delta2.as_tuple(),\n            self.ic.iter().map(|i| i.as_tuple()).collect(),\n        )\n    }\n}\n\nimpl From<ark_groth16::VerifyingKey<Bn254>> for VerifyingKey {\n    fn from(vk: ark_groth16::VerifyingKey<Bn254>) -> Self {\n        Self {\n            alpha1: G1::from(&vk.alpha_g1),\n            beta2: G2::from(&vk.beta_g2),\n            gamma2: G2::from(&vk.gamma_g2),\n            delta2: G2::from(&vk.delta_g2),\n            ic: vk.gamma_abc_g1.iter().map(G1::from).collect(),\n        }\n    }\n}\n\nimpl TryFrom<VerifyingKey> for ark_groth16::VerifyingKey<Bn254> {\n    type Error = AffineError;\n\n    fn try_from(src: VerifyingKey) -> Result<ark_groth16::VerifyingKey<Bn254>, AffineError> {\n        Ok(ark_groth16::VerifyingKey {\n            alpha_g1: src.alpha1.try_into()?,\n            beta_g2: src.beta2.try_into()?,\n            gamma_g2: src.gamma2.try_into()?,\n            delta_g2: src.delta2.try_into()?,\n            gamma_abc_g1: src\n                .ic\n                .into_iter()\n                .map(TryInto::try_into)\n                .collect::<Result<_, _>>()?,\n        })\n    }\n}\n\n// Helper for converting a PrimeField to its U256 representation for Ethereum compatibility\nfn u256_to_point<F: PrimeField>(point: U256) -> F {\n    let buf: [u8; 32] = point.to_le_bytes();\n    let bigint = F::BigInt::deserialize_uncompressed(&buf[..]).expect(\"always works\");\n    F::from_bigint(bigint).expect(\"always works\")\n}\n\n// Helper for converting a PrimeField to its U256 representation for Ethereum compatibility\n// (U256 reads data as big endian)\nfn point_to_u256<F: PrimeField>(point: F) -> U256 {\n    let point = point.into_bigint();\n    let point_bytes = point.to_bytes_be();\n    U256::try_from_be_slice(&point_bytes[..]).expect(\"always works\")\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use ark_bn254::Fq;\n    use ark_std::UniformRand;\n\n    fn fq() -> Fq {\n        Fq::from(2)\n    }\n\n    fn fr() -> Fr {\n        Fr::from(2)\n    }\n\n    fn g1() -> G1Affine {\n        let rng = &mut ark_std::test_rng();\n        G1Affine::rand(rng)\n    }\n\n    fn g2() -> G2Affine {\n        let rng = &mut ark_std::test_rng();\n        G2Affine::rand(rng)\n    }\n\n    #[test]\n    fn convert_fq() {\n        let el = fq();\n        let el2 = point_to_u256(el);\n        let el3: Fq = u256_to_point(el2);\n        let el4 = point_to_u256(el3);\n        assert_eq!(el, el3);\n        assert_eq!(el2, el4);\n    }\n\n    #[test]\n    fn convert_fr() {\n        let el = fr();\n        let el2 = point_to_u256(el);\n        let el3: Fr = u256_to_point(el2);\n        let el4 = point_to_u256(el3);\n        assert_eq!(el, el3);\n        assert_eq!(el2, el4);\n    }\n\n    #[test]\n    fn convert_g1() {\n        let el = g1();\n        let el2 = G1::from(&el);\n        let el3: G1Affine = el2.try_into().unwrap();\n        let el4 = G1::from(&el3);\n        assert_eq!(el, el3);\n        assert_eq!(el2, el4);\n    }\n\n    #[test]\n    fn convert_g2() {\n        let el = g2();\n        let el2 = G2::from(&el);\n        let el3: G2Affine = el2.try_into().unwrap();\n        let el4 = G2::from(&el3);\n        assert_eq!(el, el3);\n        assert_eq!(el2, el4);\n    }\n\n    #[test]\n    fn convert_vk() {\n        let vk = ark_groth16::VerifyingKey::<Bn254> {\n            alpha_g1: g1(),\n            beta_g2: g2(),\n            gamma_g2: g2(),\n            delta_g2: g2(),\n            gamma_abc_g1: vec![g1(), g1(), g1()],\n        };\n        let vk_ethers = VerifyingKey::from(vk.clone());\n        let ark_vk: ark_groth16::VerifyingKey<Bn254> = vk_ethers.try_into().unwrap();\n        assert_eq!(ark_vk, vk);\n    }\n\n    #[test]\n    fn convert_proof() {\n        let p = ark_groth16::Proof::<Bn254> {\n            a: g1(),\n            b: g2(),\n            c: g1(),\n        };\n        let p2 = Proof::from(p.clone());\n        let p3 = ark_groth16::Proof::try_from(p2).unwrap();\n        assert_eq!(p, p3);\n    }\n}\n"
  },
  {
    "path": "crates/ark-circom/src/lib.rs",
    "content": "pub mod circom;\npub mod ethereum;\npub mod zkey;\n\npub use circom::CircomReduction;\npub use zkey::read_zkey;\n"
  },
  {
    "path": "crates/ark-circom/src/zkey.rs",
    "content": "use ark_ff::{BigInteger256, PrimeField};\nuse ark_relations::r1cs::ConstraintMatrices;\nuse ark_serialize::{CanonicalDeserialize, SerializationError};\nuse ark_std::log2;\nuse byteorder::{LittleEndian, ReadBytesExt};\n\nuse std::{\n    collections::HashMap,\n    io::{Read, Seek, SeekFrom},\n};\n\nuse ark_bn254::{Bn254, Fq, Fq2, Fr, G1Affine, G2Affine};\nuse ark_groth16::{ProvingKey, VerifyingKey};\nuse num_traits::Zero;\n\ntype IoResult<T> = Result<T, SerializationError>;\n\n#[derive(Clone, Debug)]\nstruct Section {\n    position: u64,\n    #[allow(dead_code)]\n    size: usize,\n}\n\n/// Reads a SnarkJS ZKey file into an Arkworks ProvingKey.\npub fn read_zkey<R: Read + Seek>(\n    reader: &mut R,\n) -> IoResult<(ProvingKey<Bn254>, ConstraintMatrices<Fr>)> {\n    let mut binfile = BinFile::new(reader)?;\n    let proving_key = binfile.proving_key()?;\n    let matrices = binfile.matrices()?;\n    Ok((proving_key, matrices))\n}\n\n#[derive(Debug)]\nstruct BinFile<'a, R> {\n    #[allow(dead_code)]\n    ftype: String,\n    #[allow(dead_code)]\n    version: u32,\n    sections: HashMap<u32, Vec<Section>>,\n    reader: &'a mut R,\n}\n\nimpl<'a, R: Read + Seek> BinFile<'a, R> {\n    fn new(reader: &'a mut R) -> IoResult<Self> {\n        let mut magic = [0u8; 4];\n        reader.read_exact(&mut magic)?;\n\n        let version = reader.read_u32::<LittleEndian>()?;\n\n        let num_sections = reader.read_u32::<LittleEndian>()?;\n\n        let mut sections = HashMap::new();\n        for _ in 0..num_sections {\n            let section_id = reader.read_u32::<LittleEndian>()?;\n            let section_length = reader.read_u64::<LittleEndian>()?;\n\n            let section = sections.entry(section_id).or_insert_with(Vec::new);\n            section.push(Section {\n                position: reader.stream_position()?,\n                size: section_length as usize,\n            });\n\n            reader.seek(SeekFrom::Current(section_length as i64))?;\n        }\n\n        Ok(Self {\n            ftype: std::str::from_utf8(&magic[..]).unwrap().to_string(),\n            version,\n            sections,\n            reader,\n        })\n    }\n\n    fn proving_key(&mut self) -> IoResult<ProvingKey<Bn254>> {\n        let header = self.groth_header()?;\n        let ic = self.ic(header.n_public)?;\n\n        let a_query = self.a_query(header.n_vars)?;\n        let b_g1_query = self.b_g1_query(header.n_vars)?;\n        let b_g2_query = self.b_g2_query(header.n_vars)?;\n        let l_query = self.l_query(header.n_vars - header.n_public - 1)?;\n        let h_query = self.h_query(header.domain_size as usize)?;\n\n        let vk = VerifyingKey::<Bn254> {\n            alpha_g1: header.verifying_key.alpha_g1,\n            beta_g2: header.verifying_key.beta_g2,\n            gamma_g2: header.verifying_key.gamma_g2,\n            delta_g2: header.verifying_key.delta_g2,\n            gamma_abc_g1: ic,\n        };\n\n        let pk = ProvingKey::<Bn254> {\n            vk,\n            beta_g1: header.verifying_key.beta_g1,\n            delta_g1: header.verifying_key.delta_g1,\n            a_query,\n            b_g1_query,\n            b_g2_query,\n            h_query,\n            l_query,\n        };\n\n        Ok(pk)\n    }\n\n    fn get_section(&self, id: u32) -> Section {\n        self.sections.get(&id).unwrap()[0].clone()\n    }\n\n    fn groth_header(&mut self) -> IoResult<HeaderGroth> {\n        let section = self.get_section(2);\n        let header = HeaderGroth::new(&mut self.reader, &section)?;\n        Ok(header)\n    }\n\n    fn ic(&mut self, n_public: usize) -> IoResult<Vec<G1Affine>> {\n        // the range is non-inclusive so we do +1 to get all inputs\n        self.g1_section(n_public + 1, 3)\n    }\n\n    /// Returns the [`ConstraintMatrices`] corresponding to the zkey\n    pub fn matrices(&mut self) -> IoResult<ConstraintMatrices<Fr>> {\n        let header = self.groth_header()?;\n\n        let section = self.get_section(4);\n        self.reader.seek(SeekFrom::Start(section.position))?;\n        let num_coeffs: u32 = self.reader.read_u32::<LittleEndian>()?;\n\n        // insantiate AB\n        let mut matrices = vec![vec![vec![]; header.domain_size as usize]; 2];\n        let mut max_constraint_index = 0;\n        for _ in 0..num_coeffs {\n            let matrix: u32 = self.reader.read_u32::<LittleEndian>()?;\n            let constraint: u32 = self.reader.read_u32::<LittleEndian>()?;\n            let signal: u32 = self.reader.read_u32::<LittleEndian>()?;\n\n            let value: Fr = deserialize_field_fr(&mut self.reader)?;\n            max_constraint_index = std::cmp::max(max_constraint_index, constraint);\n            matrices[matrix as usize][constraint as usize].push((value, signal as usize));\n        }\n\n        let num_constraints = max_constraint_index as usize - header.n_public;\n        // Remove the public input constraints, Arkworks adds them later\n        matrices.iter_mut().for_each(|m| {\n            m.truncate(num_constraints);\n        });\n        // This is taken from Arkworks' to_matrices() function\n        let a = matrices[0].clone();\n        let b = matrices[1].clone();\n        let a_num_non_zero: usize = a.iter().map(|lc| lc.len()).sum();\n        let b_num_non_zero: usize = b.iter().map(|lc| lc.len()).sum();\n        let matrices = ConstraintMatrices {\n            num_instance_variables: header.n_public + 1,\n            num_witness_variables: header.n_vars - header.n_public,\n            num_constraints,\n\n            a_num_non_zero,\n            b_num_non_zero,\n            c_num_non_zero: 0,\n\n            a,\n            b,\n            c: vec![],\n        };\n\n        Ok(matrices)\n    }\n\n    fn a_query(&mut self, n_vars: usize) -> IoResult<Vec<G1Affine>> {\n        self.g1_section(n_vars, 5)\n    }\n\n    fn b_g1_query(&mut self, n_vars: usize) -> IoResult<Vec<G1Affine>> {\n        self.g1_section(n_vars, 6)\n    }\n\n    fn b_g2_query(&mut self, n_vars: usize) -> IoResult<Vec<G2Affine>> {\n        self.g2_section(n_vars, 7)\n    }\n\n    fn l_query(&mut self, n_vars: usize) -> IoResult<Vec<G1Affine>> {\n        self.g1_section(n_vars, 8)\n    }\n\n    fn h_query(&mut self, n_vars: usize) -> IoResult<Vec<G1Affine>> {\n        self.g1_section(n_vars, 9)\n    }\n\n    fn g1_section(&mut self, num: usize, section_id: usize) -> IoResult<Vec<G1Affine>> {\n        let section = self.get_section(section_id as u32);\n        self.reader.seek(SeekFrom::Start(section.position))?;\n        deserialize_g1_vec(self.reader, num as u32)\n    }\n\n    fn g2_section(&mut self, num: usize, section_id: usize) -> IoResult<Vec<G2Affine>> {\n        let section = self.get_section(section_id as u32);\n        self.reader.seek(SeekFrom::Start(section.position))?;\n        deserialize_g2_vec(self.reader, num as u32)\n    }\n}\n\n#[derive(Default, Clone, Debug, CanonicalDeserialize)]\npub struct ZVerifyingKey {\n    alpha_g1: G1Affine,\n    beta_g1: G1Affine,\n    beta_g2: G2Affine,\n    gamma_g2: G2Affine,\n    delta_g1: G1Affine,\n    delta_g2: G2Affine,\n}\n\nimpl ZVerifyingKey {\n    fn new<R: Read>(reader: &mut R) -> IoResult<Self> {\n        let alpha_g1 = deserialize_g1(reader)?;\n        let beta_g1 = deserialize_g1(reader)?;\n        let beta_g2 = deserialize_g2(reader)?;\n        let gamma_g2 = deserialize_g2(reader)?;\n        let delta_g1 = deserialize_g1(reader)?;\n        let delta_g2 = deserialize_g2(reader)?;\n\n        Ok(Self {\n            alpha_g1,\n            beta_g1,\n            beta_g2,\n            gamma_g2,\n            delta_g1,\n            delta_g2,\n        })\n    }\n}\n\n#[derive(Clone, Debug)]\nstruct HeaderGroth {\n    #[allow(dead_code)]\n    n8q: u32,\n    #[allow(dead_code)]\n    q: BigInteger256,\n    #[allow(dead_code)]\n    n8r: u32,\n    #[allow(dead_code)]\n    r: BigInteger256,\n\n    n_vars: usize,\n    n_public: usize,\n\n    domain_size: u32,\n    #[allow(dead_code)]\n    power: u32,\n\n    verifying_key: ZVerifyingKey,\n}\n\nimpl HeaderGroth {\n    fn new<R: Read + Seek>(reader: &mut R, section: &Section) -> IoResult<Self> {\n        reader.seek(SeekFrom::Start(section.position))?;\n        Self::read(reader)\n    }\n\n    fn read<R: Read>(mut reader: &mut R) -> IoResult<Self> {\n        // TODO: Impl From<u32> in Arkworks\n        let n8q: u32 = u32::deserialize_uncompressed(&mut reader)?;\n        // group order r of Bn254\n        let q = BigInteger256::deserialize_uncompressed(&mut reader)?;\n\n        let n8r: u32 = u32::deserialize_uncompressed(&mut reader)?;\n        // Prime field modulus\n        let r = BigInteger256::deserialize_uncompressed(&mut reader)?;\n\n        let n_vars = u32::deserialize_uncompressed(&mut reader)? as usize;\n        let n_public = u32::deserialize_uncompressed(&mut reader)? as usize;\n\n        let domain_size: u32 = u32::deserialize_uncompressed(&mut reader)?;\n        let power = log2(domain_size as usize);\n\n        let verifying_key = ZVerifyingKey::new(&mut reader)?;\n\n        Ok(Self {\n            n8q,\n            q,\n            n8r,\n            r,\n            n_vars,\n            n_public,\n            domain_size,\n            power,\n            verifying_key,\n        })\n    }\n}\n\n// need to divide by R, since snarkjs outputs the zkey with coefficients\n// multiplieid by R^2\nfn deserialize_field_fr<R: Read>(reader: &mut R) -> IoResult<Fr> {\n    let bigint = BigInteger256::deserialize_uncompressed(reader)?;\n    Ok(Fr::new_unchecked(Fr::new_unchecked(bigint).into_bigint()))\n}\n\n// skips the multiplication by R because Circom points are already in Montgomery form\nfn deserialize_field<R: Read>(reader: &mut R) -> IoResult<Fq> {\n    let bigint = BigInteger256::deserialize_uncompressed(reader)?;\n    // if you use Fq::new it multiplies by R\n    Ok(Fq::new_unchecked(bigint))\n}\n\npub fn deserialize_field2<R: Read>(reader: &mut R) -> IoResult<Fq2> {\n    let c0 = deserialize_field(reader)?;\n    let c1 = deserialize_field(reader)?;\n    Ok(Fq2::new(c0, c1))\n}\n\nfn deserialize_g1<R: Read>(reader: &mut R) -> IoResult<G1Affine> {\n    let x = deserialize_field(reader)?;\n    let y = deserialize_field(reader)?;\n    let infinity = x.is_zero() && y.is_zero();\n    if infinity {\n        Ok(G1Affine::identity())\n    } else {\n        Ok(G1Affine::new(x, y))\n    }\n}\n\nfn deserialize_g2<R: Read>(reader: &mut R) -> IoResult<G2Affine> {\n    let f1 = deserialize_field2(reader)?;\n    let f2 = deserialize_field2(reader)?;\n    let infinity = f1.is_zero() && f2.is_zero();\n    if infinity {\n        Ok(G2Affine::identity())\n    } else {\n        Ok(G2Affine::new(f1, f2))\n    }\n}\n\nfn deserialize_g1_vec<R: Read>(reader: &mut R, n_vars: u32) -> IoResult<Vec<G1Affine>> {\n    (0..n_vars).map(|_| deserialize_g1(reader)).collect()\n}\n\nfn deserialize_g2_vec<R: Read>(reader: &mut R, n_vars: u32) -> IoResult<Vec<G2Affine>> {\n    (0..n_vars).map(|_| deserialize_g2(reader)).collect()\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use ark_bn254::{G1Projective, G2Projective};\n    use num_bigint::BigUint;\n    use serde_json::Value;\n    use std::fs::File;\n\n    use num_traits::{One, Zero};\n    use std::str::FromStr;\n\n    use std::convert::TryFrom;\n\n    fn fq_from_str(s: &str) -> Fq {\n        BigInteger256::try_from(BigUint::from_str(s).unwrap())\n            .unwrap()\n            .into()\n    }\n\n    // Circom snarkjs code:\n    // console.log(curve.G1.F.one)\n    fn fq_buf() -> Vec<u8> {\n        vec![\n            157, 13, 143, 197, 141, 67, 93, 211, 61, 11, 199, 245, 40, 235, 120, 10, 44, 70, 121,\n            120, 111, 163, 110, 102, 47, 223, 7, 154, 193, 119, 10, 14,\n        ]\n    }\n\n    // Circom snarkjs code:\n    // const buff = new Uint8Array(curve.G1.F.n8*2);\n    // curve.G1.toRprLEM(buff, 0, curve.G1.one);\n    // console.dir( buff, { 'maxArrayLength': null })\n    fn g1_buf() -> Vec<u8> {\n        vec![\n            157, 13, 143, 197, 141, 67, 93, 211, 61, 11, 199, 245, 40, 235, 120, 10, 44, 70, 121,\n            120, 111, 163, 110, 102, 47, 223, 7, 154, 193, 119, 10, 14, 58, 27, 30, 139, 27, 135,\n            186, 166, 123, 22, 142, 235, 81, 214, 241, 20, 88, 140, 242, 240, 222, 70, 221, 204,\n            94, 190, 15, 52, 131, 239, 20, 28,\n        ]\n    }\n\n    // Circom snarkjs code:\n    // const buff = new Uint8Array(curve.G2.F.n8*2);\n    // curve.G2.toRprLEM(buff, 0, curve.G2.one);\n    // console.dir( buff, { 'maxArrayLength': null })\n    fn g2_buf() -> Vec<u8> {\n        vec![\n            38, 32, 188, 2, 209, 181, 131, 142, 114, 1, 123, 73, 53, 25, 235, 220, 223, 26, 129,\n            151, 71, 38, 184, 251, 59, 80, 150, 175, 65, 56, 87, 25, 64, 97, 76, 168, 125, 115,\n            180, 175, 196, 216, 2, 88, 90, 221, 67, 96, 134, 47, 160, 82, 252, 80, 233, 9, 107,\n            123, 234, 58, 131, 240, 254, 20, 246, 233, 107, 136, 157, 250, 157, 97, 120, 155, 158,\n            245, 151, 210, 127, 254, 254, 125, 27, 35, 98, 26, 158, 255, 6, 66, 158, 174, 235, 126,\n            253, 40, 238, 86, 24, 199, 86, 91, 9, 100, 187, 60, 125, 50, 34, 249, 87, 220, 118, 16,\n            53, 51, 190, 53, 249, 85, 130, 100, 253, 147, 230, 160, 164, 13,\n        ]\n    }\n\n    // Circom logs in Projective coordinates: console.log(curve.G1.one)\n    fn g1_one() -> G1Affine {\n        let x = Fq::one();\n        let y = Fq::one() + Fq::one();\n        let z = Fq::one();\n        G1Affine::from(G1Projective::new(x, y, z))\n    }\n\n    // Circom logs in Projective coordinates: console.log(curve.G2.one)\n    fn g2_one() -> G2Affine {\n        let x = Fq2::new(\n            fq_from_str(\n                \"10857046999023057135944570762232829481370756359578518086990519993285655852781\",\n            ),\n            fq_from_str(\n                \"11559732032986387107991004021392285783925812861821192530917403151452391805634\",\n            ),\n        );\n\n        let y = Fq2::new(\n            fq_from_str(\n                \"8495653923123431417604973247489272438418190587263600148770280649306958101930\",\n            ),\n            fq_from_str(\n                \"4082367875863433681332203403145435568316851327593401208105741076214120093531\",\n            ),\n        );\n        let z = Fq2::new(Fq::one(), Fq::zero());\n        G2Affine::from(G2Projective::new(x, y, z))\n    }\n\n    #[test]\n    fn can_deser_fq() {\n        let buf = fq_buf();\n        let fq = deserialize_field(&mut &buf[..]).unwrap();\n        assert_eq!(fq, Fq::one());\n    }\n\n    #[test]\n    fn can_deser_g1() {\n        let buf = g1_buf();\n        assert_eq!(buf.len(), 64);\n        let g1 = deserialize_g1(&mut &buf[..]).unwrap();\n        let expected = g1_one();\n        assert_eq!(g1, expected);\n    }\n\n    #[test]\n    fn can_deser_g1_vec() {\n        let n_vars = 10;\n        let buf = vec![g1_buf(); n_vars]\n            .iter()\n            .flatten()\n            .cloned()\n            .collect::<Vec<_>>();\n        let expected = vec![g1_one(); n_vars];\n\n        let de = deserialize_g1_vec(&mut &buf[..], n_vars as u32).unwrap();\n        assert_eq!(expected, de);\n    }\n\n    #[test]\n    fn can_deser_g2() {\n        let buf = g2_buf();\n        assert_eq!(buf.len(), 128);\n        let g2 = deserialize_g2(&mut &buf[..]).unwrap();\n\n        let expected = g2_one();\n        assert_eq!(g2, expected);\n    }\n\n    #[test]\n    fn can_deser_g2_vec() {\n        let n_vars = 10;\n        let buf = vec![g2_buf(); n_vars]\n            .iter()\n            .flatten()\n            .cloned()\n            .collect::<Vec<_>>();\n        let expected = vec![g2_one(); n_vars];\n\n        let de = deserialize_g2_vec(&mut &buf[..], n_vars as u32).unwrap();\n        assert_eq!(expected, de);\n    }\n\n    #[test]\n    fn header() {\n        // `circom --r1cs` using the below file:\n        //\n        //  template Multiplier() {\n        //     signal private input a;\n        //     signal private input b;\n        //     signal output c;\n        //\n        //     c <== a*b;\n        // }\n        //\n        // component main = Multiplier();\n        //\n        // Then:\n        // `snarkjs zkey new circuit.r1cs powersOfTau28_hez_final_10.ptau test.zkey`\n        let path = \"./test-vectors/test.zkey\";\n        let mut file = File::open(path).unwrap();\n        let mut binfile = BinFile::new(&mut file).unwrap();\n        let header = binfile.groth_header().unwrap();\n        assert_eq!(header.n_vars, 4);\n        assert_eq!(header.n_public, 1);\n        assert_eq!(header.domain_size, 4);\n        assert_eq!(header.power, 2);\n    }\n\n    #[test]\n    fn deser_key() {\n        let path = \"./test-vectors/test.zkey\";\n        let mut file = File::open(path).unwrap();\n        let (params, _matrices) = read_zkey(&mut file).unwrap();\n\n        // Check IC\n        let expected = vec![\n            deserialize_g1(\n                &mut &[\n                    11, 205, 205, 176, 2, 105, 129, 243, 153, 58, 137, 89, 61, 95, 99, 161, 133,\n                    201, 153, 192, 119, 19, 113, 136, 43, 105, 47, 206, 166, 55, 81, 22, 154, 77,\n                    58, 119, 28, 230, 160, 206, 134, 98, 4, 115, 112, 184, 46, 117, 61, 180, 103,\n                    138, 141, 202, 110, 252, 199, 252, 141, 211, 5, 46, 244, 10,\n                ][..],\n            )\n            .unwrap(),\n            deserialize_g1(\n                &mut &[\n                    118, 135, 198, 156, 63, 190, 210, 98, 194, 59, 169, 168, 204, 168, 76, 208,\n                    109, 170, 24, 193, 57, 31, 184, 88, 234, 218, 118, 58, 107, 129, 90, 36, 230,\n                    98, 62, 243, 3, 55, 68, 227, 117, 64, 188, 81, 81, 247, 161, 68, 68, 210, 142,\n                    191, 174, 43, 110, 194, 253, 128, 217, 4, 54, 196, 111, 43,\n                ][..],\n            )\n            .unwrap(),\n        ];\n        assert_eq!(expected, params.vk.gamma_abc_g1);\n\n        // Check A Query\n        let expected = vec![\n            deserialize_g1(\n                &mut &[\n                    240, 165, 110, 187, 72, 39, 218, 59, 128, 85, 50, 174, 229, 1, 86, 58, 125,\n                    244, 145, 205, 248, 253, 120, 2, 165, 140, 154, 55, 220, 253, 14, 19, 212, 106,\n                    59, 19, 125, 198, 202, 4, 59, 74, 14, 62, 20, 248, 219, 47, 234, 205, 54, 183,\n                    33, 119, 165, 84, 46, 75, 39, 17, 229, 42, 192, 2,\n                ][..],\n            )\n            .unwrap(),\n            deserialize_g1(\n                &mut &[\n                    93, 53, 177, 82, 50, 5, 123, 116, 91, 35, 14, 196, 43, 180, 54, 15, 88, 144,\n                    197, 105, 57, 167, 54, 5, 188, 109, 17, 89, 9, 223, 80, 1, 39, 193, 211, 168,\n                    203, 119, 169, 105, 17, 156, 53, 106, 11, 102, 44, 92, 123, 220, 158, 240, 97,\n                    253, 30, 121, 4, 236, 171, 23, 100, 34, 133, 11,\n                ][..],\n            )\n            .unwrap(),\n            deserialize_g1(\n                &mut &[\n                    177, 47, 21, 237, 244, 73, 76, 98, 80, 10, 10, 142, 80, 145, 40, 254, 100, 214,\n                    103, 33, 38, 84, 238, 248, 252, 181, 75, 32, 109, 16, 93, 23, 135, 157, 206,\n                    122, 107, 105, 202, 164, 197, 124, 242, 100, 70, 108, 9, 180, 224, 102, 250,\n                    149, 130, 14, 133, 185, 132, 189, 193, 230, 180, 143, 156, 30,\n                ][..],\n            )\n            .unwrap(),\n            deserialize_g1(\n                &mut &[\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                ][..],\n            )\n            .unwrap(),\n        ];\n        assert_eq!(expected, params.a_query);\n\n        // B G1 Query\n        let expected = vec![\n            deserialize_g1(\n                &mut &[\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                ][..],\n            )\n            .unwrap(),\n            deserialize_g1(\n                &mut &[\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                ][..],\n            )\n            .unwrap(),\n            deserialize_g1(\n                &mut &[\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                ][..],\n            )\n            .unwrap(),\n            deserialize_g1(\n                &mut &[\n                    177, 47, 21, 237, 244, 73, 76, 98, 80, 10, 10, 142, 80, 145, 40, 254, 100, 214,\n                    103, 33, 38, 84, 238, 248, 252, 181, 75, 32, 109, 16, 93, 23, 192, 95, 174, 93,\n                    171, 34, 86, 151, 199, 77, 127, 3, 75, 254, 119, 227, 124, 241, 134, 235, 51,\n                    55, 203, 254, 164, 226, 111, 250, 189, 190, 199, 17,\n                ][..],\n            )\n            .unwrap(),\n        ];\n        assert_eq!(expected, params.b_g1_query);\n\n        // B G2 Query\n        let expected = vec![\n            deserialize_g2(\n                &mut &[\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                ][..],\n            )\n            .unwrap(),\n            deserialize_g2(\n                &mut &[\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                ][..],\n            )\n            .unwrap(),\n            deserialize_g2(\n                &mut &[\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                ][..],\n            )\n            .unwrap(),\n            deserialize_g2(\n                &mut &[\n                    240, 25, 157, 232, 164, 49, 152, 204, 244, 190, 178, 178, 29, 133, 205, 175,\n                    172, 28, 12, 123, 139, 202, 196, 13, 67, 165, 204, 42, 74, 40, 6, 36, 112, 104,\n                    61, 67, 107, 112, 72, 41, 213, 210, 249, 75, 89, 144, 144, 34, 177, 228, 18,\n                    70, 80, 195, 124, 82, 40, 122, 91, 21, 198, 100, 154, 1, 16, 235, 41, 4, 176,\n                    106, 9, 113, 141, 251, 100, 233, 188, 128, 194, 173, 0, 100, 206, 110, 53, 223,\n                    163, 47, 166, 235, 25, 12, 151, 238, 45, 0, 78, 210, 56, 53, 57, 212, 67, 189,\n                    253, 132, 62, 62, 116, 20, 235, 15, 245, 113, 30, 182, 33, 127, 203, 231, 124,\n                    149, 74, 223, 39, 190, 217, 41,\n                ][..],\n            )\n            .unwrap(),\n        ];\n        assert_eq!(expected, params.b_g2_query);\n\n        // Check L Query\n        let expected = vec![\n            deserialize_g1(\n                &mut &[\n                    146, 142, 29, 235, 9, 162, 84, 255, 6, 119, 86, 214, 154, 18, 12, 190, 202, 19,\n                    168, 45, 29, 76, 174, 130, 6, 59, 146, 15, 229, 82, 81, 40, 50, 25, 124, 247,\n                    129, 12, 147, 35, 108, 119, 178, 116, 238, 145, 33, 184, 74, 201, 128, 41, 151,\n                    6, 60, 84, 156, 225, 200, 14, 240, 171, 128, 20,\n                ][..],\n            )\n            .unwrap(),\n            deserialize_g1(\n                &mut &[\n                    26, 32, 112, 226, 161, 84, 188, 236, 141, 226, 119, 169, 235, 218, 253, 176,\n                    157, 184, 108, 243, 73, 122, 239, 217, 39, 190, 239, 105, 147, 190, 80, 47,\n                    211, 68, 155, 212, 139, 173, 229, 160, 123, 117, 243, 110, 162, 188, 217, 206,\n                    102, 19, 36, 189, 87, 183, 113, 8, 164, 133, 43, 142, 138, 109, 66, 33,\n                ][..],\n            )\n            .unwrap(),\n        ];\n        assert_eq!(expected, params.l_query);\n\n        // Check H Query\n        let expected = vec![\n            deserialize_g1(\n                &mut &[\n                    21, 76, 104, 34, 28, 236, 135, 204, 218, 16, 160, 115, 185, 44, 19, 62, 43, 24,\n                    57, 99, 207, 105, 10, 139, 195, 60, 17, 57, 85, 244, 167, 10, 166, 166, 165,\n                    55, 38, 75, 116, 116, 182, 87, 217, 112, 28, 237, 239, 123, 231, 180, 122, 109,\n                    77, 116, 88, 67, 102, 48, 80, 214, 137, 47, 94, 30,\n                ][..],\n            )\n            .unwrap(),\n            deserialize_g1(\n                &mut &[\n                    144, 175, 205, 119, 119, 192, 11, 10, 148, 224, 87, 161, 157, 231, 101, 208,\n                    55, 15, 13, 16, 24, 59, 9, 22, 63, 215, 255, 30, 77, 188, 71, 37, 84, 227, 59,\n                    29, 159, 116, 101, 93, 212, 220, 159, 141, 204, 107, 131, 87, 174, 149, 175,\n                    72, 199, 109, 64, 109, 180, 150, 160, 249, 246, 33, 212, 29,\n                ][..],\n            )\n            .unwrap(),\n            deserialize_g1(\n                &mut &[\n                    129, 169, 52, 179, 66, 88, 123, 199, 222, 69, 24, 17, 219, 235, 118, 195, 156,\n                    210, 14, 21, 76, 155, 178, 210, 223, 4, 233, 5, 8, 18, 156, 24, 82, 68, 183,\n                    186, 7, 126, 2, 201, 207, 207, 74, 45, 44, 199, 16, 165, 25, 65, 157, 199, 90,\n                    159, 12, 150, 250, 17, 177, 193, 244, 93, 230, 41,\n                ][..],\n            )\n            .unwrap(),\n            deserialize_g1(\n                &mut &[\n                    207, 61, 229, 214, 21, 61, 103, 165, 93, 145, 54, 138, 143, 214, 5, 83, 183,\n                    22, 174, 87, 108, 59, 99, 96, 19, 20, 25, 139, 114, 238, 198, 40, 182, 88, 1,\n                    255, 206, 132, 156, 165, 178, 171, 0, 226, 179, 30, 192, 4, 79, 198, 69, 43,\n                    145, 133, 116, 86, 36, 144, 190, 119, 79, 241, 76, 16,\n                ][..],\n            )\n            .unwrap(),\n        ];\n        assert_eq!(expected, params.h_query);\n    }\n\n    #[test]\n    fn deser_vk() {\n        let path = \"./test-vectors/test.zkey\";\n        let mut file = File::open(path).unwrap();\n        let (params, _matrices) = read_zkey(&mut file).unwrap();\n\n        let json = std::fs::read_to_string(\"./test-vectors/verification_key.json\").unwrap();\n        let json: Value = serde_json::from_str(&json).unwrap();\n\n        assert_eq!(json_to_g1(&json, \"vk_alpha_1\"), params.vk.alpha_g1);\n        assert_eq!(json_to_g2(&json, \"vk_beta_2\"), params.vk.beta_g2);\n        assert_eq!(json_to_g2(&json, \"vk_gamma_2\"), params.vk.gamma_g2);\n        assert_eq!(json_to_g2(&json, \"vk_delta_2\"), params.vk.delta_g2);\n        assert_eq!(json_to_g1_vec(&json, \"IC\"), params.vk.gamma_abc_g1);\n    }\n\n    fn json_to_g1(json: &Value, key: &str) -> G1Affine {\n        let els: Vec<String> = json\n            .get(key)\n            .unwrap()\n            .as_array()\n            .unwrap()\n            .iter()\n            .map(|i| i.as_str().unwrap().to_string())\n            .collect();\n        G1Affine::from(G1Projective::new(\n            fq_from_str(&els[0]),\n            fq_from_str(&els[1]),\n            fq_from_str(&els[2]),\n        ))\n    }\n\n    fn json_to_g1_vec(json: &Value, key: &str) -> Vec<G1Affine> {\n        let els: Vec<Vec<String>> = json\n            .get(key)\n            .unwrap()\n            .as_array()\n            .unwrap()\n            .iter()\n            .map(|i| {\n                i.as_array()\n                    .unwrap()\n                    .iter()\n                    .map(|x| x.as_str().unwrap().to_string())\n                    .collect::<Vec<String>>()\n            })\n            .collect();\n\n        els.iter()\n            .map(|coords| {\n                G1Affine::from(G1Projective::new(\n                    fq_from_str(&coords[0]),\n                    fq_from_str(&coords[1]),\n                    fq_from_str(&coords[2]),\n                ))\n            })\n            .collect()\n    }\n\n    fn json_to_g2(json: &Value, key: &str) -> G2Affine {\n        let els: Vec<Vec<String>> = json\n            .get(key)\n            .unwrap()\n            .as_array()\n            .unwrap()\n            .iter()\n            .map(|i| {\n                i.as_array()\n                    .unwrap()\n                    .iter()\n                    .map(|x| x.as_str().unwrap().to_string())\n                    .collect::<Vec<String>>()\n            })\n            .collect();\n\n        let x = Fq2::new(fq_from_str(&els[0][0]), fq_from_str(&els[0][1]));\n        let y = Fq2::new(fq_from_str(&els[1][0]), fq_from_str(&els[1][1]));\n        let z = Fq2::new(fq_from_str(&els[2][0]), fq_from_str(&els[2][1]));\n        G2Affine::from(G2Projective::new(x, y, z))\n    }\n}\n"
  },
  {
    "path": "crates/ark-circom/test-vectors/verification_key.json",
    "content": "{\n \"protocol\": \"groth16\",\n \"curve\": \"bn128\",\n \"nPublic\": 1,\n \"vk_alpha_1\": [\n  \"20491192805390485299153009773594534940189261866228447918068658471970481763042\",\n  \"9383485363053290200918347156157836566562967994039712273449902621266178545958\",\n  \"1\"\n ],\n \"vk_beta_2\": [\n  [\n   \"6375614351688725206403948262868962793625744043794305715222011528459656738731\",\n   \"4252822878758300859123897981450591353533073413197771768651442665752259397132\"\n  ],\n  [\n   \"10505242626370262277552901082094356697409835680220590971873171140371331206856\",\n   \"21847035105528745403288232691147584728191162732299865338377159692350059136679\"\n  ],\n  [\n   \"1\",\n   \"0\"\n  ]\n ],\n \"vk_gamma_2\": [\n  [\n   \"10857046999023057135944570762232829481370756359578518086990519993285655852781\",\n   \"11559732032986387107991004021392285783925812861821192530917403151452391805634\"\n  ],\n  [\n   \"8495653923123431417604973247489272438418190587263600148770280649306958101930\",\n   \"4082367875863433681332203403145435568316851327593401208105741076214120093531\"\n  ],\n  [\n   \"1\",\n   \"0\"\n  ]\n ],\n \"vk_delta_2\": [\n  [\n   \"10857046999023057135944570762232829481370756359578518086990519993285655852781\",\n   \"11559732032986387107991004021392285783925812861821192530917403151452391805634\"\n  ],\n  [\n   \"8495653923123431417604973247489272438418190587263600148770280649306958101930\",\n   \"4082367875863433681332203403145435568316851327593401208105741076214120093531\"\n  ],\n  [\n   \"1\",\n   \"0\"\n  ]\n ],\n \"vk_alphabeta_12\": [\n  [\n   [\n    \"2029413683389138792403550203267699914886160938906632433982220835551125967885\",\n    \"21072700047562757817161031222997517981543347628379360635925549008442030252106\"\n   ],\n   [\n    \"5940354580057074848093997050200682056184807770593307860589430076672439820312\",\n    \"12156638873931618554171829126792193045421052652279363021382169897324752428276\"\n   ],\n   [\n    \"7898200236362823042373859371574133993780991612861777490112507062703164551277\",\n    \"7074218545237549455313236346927434013100842096812539264420499035217050630853\"\n   ]\n  ],\n  [\n   [\n    \"7077479683546002997211712695946002074877511277312570035766170199895071832130\",\n    \"10093483419865920389913245021038182291233451549023025229112148274109565435465\"\n   ],\n   [\n    \"4595479056700221319381530156280926371456704509942304414423590385166031118820\",\n    \"19831328484489333784475432780421641293929726139240675179672856274388269393268\"\n   ],\n   [\n    \"11934129596455521040620786944827826205713621633706285934057045369193958244500\",\n    \"8037395052364110730298837004334506829870972346962140206007064471173334027475\"\n   ]\n  ]\n ],\n \"IC\": [\n  [\n   \"6819801395408938350212900248749732364821477541620635511814266536599629892365\",\n   \"9092252330033992554755034971584864587974280972948086568597554018278609861372\",\n   \"1\"\n  ],\n  [\n   \"17882351432929302592725330552407222299541667716607588771282887857165175611387\",\n   \"18907419617206324833977586007131055763810739835484972981819026406579664278293\",\n   \"1\"\n  ]\n ]\n}"
  },
  {
    "path": "crates/ark-zkey/.gitignore",
    "content": "# Generated by Cargo\n# will have compiled files and executables\ndebug/\ntarget/\n\n# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries\n# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html\nCargo.lock\n\n# These are backup files generated by rustfmt\n**/*.rs.bk\n\n# MSVC Windows builds of rustc generate these, which store debugging information\n*.pdb\n"
  },
  {
    "path": "crates/ark-zkey/Cargo.toml",
    "content": "[package]\nname = \"semaphore-rs-ark-zkey\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nauthors.workspace = true\ndescription.workspace = true\nkeywords.workspace = true\ncategories.workspace = true\n\n[dependencies]\nsemaphore-rs-ark-circom.workspace = true\n\ncolor-eyre.workspace = true\nmemmap2.workspace = true\n\nark-serialize.workspace = true\nark-bn254.workspace = true\nark-groth16.workspace = true\nark-relations.workspace = true\nark-ff.workspace = true\nark-ec.workspace = true\n"
  },
  {
    "path": "crates/ark-zkey/README.md",
    "content": "# ark-zkey\n\nLibrary to read `zkey` faster by serializing to `arkworks` friendly format.\n\nSee https://github.com/oskarth/mopro/issues/25 for context.\n\n## To generate arkzkey\n\nHacky, but the way we generate `arkzkey` now is by running the corresponding test.\n\nNote that we also neeed to change the const `ZKEY_BYTES` above.\n\nE.g.:\n\n```\ncargo test multiplier2 --release -- --nocapture\ncargo test keccak256 --release -- --nocapture\ncargo test rsa --release -- --nocapture\n```\n\nWill take corresponding `zkey` and put `arkzkey`` in same folder.\n\n## Multiplier\n\nNOTE: Need to change const ZKEY here\n\n`cargo test multiplier2 --release -- --nocapture`\n\n```\nrunning 1 test\n[build] Processing zkey data...\n[build] Time to process zkey data: 3.513041ms\n[build] Serializing proving key and constraint matrices\n[build] Time to serialize proving key and constraint matrices: 42ns\n[build] Writing arkzkey to: ../mopro-core/examples/circom/multiplier2/target/multiplier2_final.arkzkey\n[build] Time to write arkzkey: 1.884875ms\nReading arkzkey from: ../mopro-core/examples/circom/multiplier2/target/multiplier2_final.arkzkey\n\nTime to open arkzkey file: 18.084µs\nTime to mmap arkzkey: 8.542µs\nTime to deserialize proving key: 305.75µs\nTime to deserialize matrices: 5µs\nTime to read arkzkey: 348.083µs\ntest tests::test_multiplier2_serialization_deserialization ... ok\n```\n\nNaive test: `cargo test naive --release -- --nocapture` (with right zkey constant).\n\n**Result: `350µs` vs naive `3.3ms`**\n\n## Keccak\n\nNOTE: Need to change const ZKEY here\n\n`cargo test keccak256 --release -- --nocapture`\n\n```\n[build] Processing zkey data...\ntest tests::test_keccak256_serialization_deserialization has been running for over 60 seconds\n[build]Time to process zkey data: 158.753181958s\n[build] Serializing proving key and constraint matrices\n[build] Time to serialize proving key and constraint matrices: 42ns\n[build] Writing arkzkey to: ../mopro-core/examples/circom/keccak256/target/keccak256_256_test_final.arkzkey\n[build] Time to write arkzkey: 16.204274125s\nReading arkzkey from: ../mopro-core/examples/circom/keccak256/target/keccak256_256_test_final.arkzkey\nTime to open arkzkey file: 51.75µs\nTime to mmap arkzkey: 17.25µs\nTime to deserialize proving key: 18.323550083s\nTime to deserialize matrices: 46.935792ms\nTime to read arkzkey: 18.3730695s\ntest tests::test_keccak256_serialization_deserialization ... ok\n```\n\nVs naive:\n\n`[build] Time to process zkey data: 158.753181958s`\n\n\n**Result: 18s vs 158s**\n"
  },
  {
    "path": "crates/ark-zkey/src/lib.rs",
    "content": "use std::fs::File;\nuse std::io::BufReader;\nuse std::path::PathBuf;\n\nuse ark_bn254::{Bn254, Fr};\nuse ark_ff::Field;\nuse ark_groth16::ProvingKey;\nuse ark_relations::r1cs::ConstraintMatrices;\nuse ark_serialize::{CanonicalDeserialize, CanonicalSerialize};\nuse color_eyre::eyre::{Result, WrapErr};\nuse semaphore_rs_ark_circom::read_zkey;\n\n#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, PartialEq)]\npub struct SerializableProvingKey(pub ProvingKey<Bn254>);\n\n#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, PartialEq)]\npub struct SerializableMatrix<F: Field> {\n    pub data: Vec<Vec<(F, usize)>>,\n}\n\n#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, PartialEq)]\npub struct SerializableConstraintMatrices<F: Field> {\n    pub num_instance_variables: usize,\n    pub num_witness_variables: usize,\n    pub num_constraints: usize,\n    pub a_num_non_zero: usize,\n    pub b_num_non_zero: usize,\n    pub c_num_non_zero: usize,\n    pub a: SerializableMatrix<F>,\n    pub b: SerializableMatrix<F>,\n    pub c: SerializableMatrix<F>,\n}\n\n// TODO: Return ProvingKey<Bn254>, ConstraintMatrices<Fr>?\npub fn read_arkzkey_from_bytes(\n    arkzkey_bytes: &[u8],\n) -> Result<(ProvingKey<Bn254>, ConstraintMatrices<Fr>)> {\n    let mut cursor = std::io::Cursor::new(arkzkey_bytes);\n\n    let serialized_proving_key =\n        SerializableProvingKey::deserialize_compressed_unchecked(&mut cursor)\n            .wrap_err(\"Failed to deserialize proving key\")?;\n\n    let serialized_constraint_matrices =\n        SerializableConstraintMatrices::deserialize_compressed_unchecked(&mut cursor)\n            .wrap_err(\"Failed to deserialize constraint matrices\")?;\n\n    // Get on right form for API\n    let proving_key: ProvingKey<Bn254> = serialized_proving_key.0;\n    let constraint_matrices: ConstraintMatrices<Fr> = ConstraintMatrices {\n        num_instance_variables: serialized_constraint_matrices.num_instance_variables,\n        num_witness_variables: serialized_constraint_matrices.num_witness_variables,\n        num_constraints: serialized_constraint_matrices.num_constraints,\n        a_num_non_zero: serialized_constraint_matrices.a_num_non_zero,\n        b_num_non_zero: serialized_constraint_matrices.b_num_non_zero,\n        c_num_non_zero: serialized_constraint_matrices.c_num_non_zero,\n        a: serialized_constraint_matrices.a.data,\n        b: serialized_constraint_matrices.b.data,\n        c: serialized_constraint_matrices.c.data,\n    };\n\n    Ok((proving_key, constraint_matrices))\n}\n\npub fn read_proving_key_and_matrices_from_zkey(\n    zkey_path: &str,\n) -> Result<(SerializableProvingKey, SerializableConstraintMatrices<Fr>)> {\n    let zkey_file_path = PathBuf::from(zkey_path);\n    let zkey_file = File::open(zkey_file_path).wrap_err(\"Failed to open zkey file\")?;\n\n    let mut buf_reader = BufReader::new(zkey_file);\n\n    let (proving_key, matrices) =\n        read_zkey(&mut buf_reader).wrap_err(\"Failed to read zkey file\")?;\n\n    let serializable_proving_key = SerializableProvingKey(proving_key);\n    let serializable_constrain_matrices = SerializableConstraintMatrices {\n        num_instance_variables: matrices.num_instance_variables,\n        num_witness_variables: matrices.num_witness_variables,\n        num_constraints: matrices.num_constraints,\n        a_num_non_zero: matrices.a_num_non_zero,\n        b_num_non_zero: matrices.b_num_non_zero,\n        c_num_non_zero: matrices.c_num_non_zero,\n        a: SerializableMatrix { data: matrices.a },\n        b: SerializableMatrix { data: matrices.b },\n        c: SerializableMatrix { data: matrices.c },\n    };\n\n    Ok((serializable_proving_key, serializable_constrain_matrices))\n}\n\npub fn convert_zkey(\n    proving_key: SerializableProvingKey,\n    constraint_matrices: SerializableConstraintMatrices<Fr>,\n    arkzkey_path: &str,\n) -> Result<()> {\n    let arkzkey_file_path = PathBuf::from(arkzkey_path);\n\n    let mut file = File::create(&arkzkey_file_path)\n        .wrap_err(\"Failed to create serialized proving key file\")?;\n\n    proving_key\n        .serialize_compressed(&mut file)\n        .wrap_err(\"Failed to serialize proving key\")?;\n\n    constraint_matrices\n        .serialize_compressed(&mut file)\n        .wrap_err(\"Failed to serialize constraint matrices\")?;\n\n    Ok(())\n}\n\n#[cfg(test)]\nmod tests {\n    use std::time::Instant;\n\n    use super::*;\n\n    #[test]\n    fn test_read_arkzkey_from_bytes() -> Result<()> {\n        const ARKZKEY_BYTES: &[u8] = include_bytes!(\"./semaphore.16.arkzkey\");\n\n        println!(\"Reading arkzkey from bytes (keccak)\");\n        let now = Instant::now();\n        let (_deserialized_proving_key, _deserialized_constraint_matrices) =\n            read_arkzkey_from_bytes(ARKZKEY_BYTES)?;\n        println!(\"Time to read arkzkey: {:?}\", now.elapsed());\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/circom-witness-rs/.gitignore",
    "content": "/target\n*_cpp\n*.new\n.DS_Store\ncircuit.cc\nconstants.dat\n.idea\n"
  },
  {
    "path": "crates/circom-witness-rs/Cargo.toml",
    "content": "[package]\nname = \"semaphore-rs-witness\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nauthors.workspace = true\ndescription.workspace = true\nkeywords.workspace = true\ncategories.workspace = true\n\n[dependencies]\nark-bn254 = { workspace = true, features = [\"std\"] }\nark-ff = { workspace = true, features = [\"std\"] }\nark-serialize.workspace = true\nbyteorder.workspace = true\ncolor-eyre.workspace = true\nhex.workspace = true\npostcard = { workspace = true, features = [\"use-std\"] } \nrand.workspace = true\nruint.workspace = true\nserde.workspace = true\nserde_json.workspace = true\n\n[build-dependencies]\ncxx-build.workspace = true\n\n[features]\nbuild-witness = []\n"
  },
  {
    "path": "crates/circom-witness-rs/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2023 Philipp Sippl\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "crates/circom-witness-rs/README.md",
    "content": "# 🏎️ circom-witness-rs\n\n## Description\n\nThis crate provides a fast witness generator for Circom circuits, serving as a drop-in replacement for Circom's witness generator. It was created in response to the slow performance of Circom's WASM generator for larger circuits, which also necessitates a WASM runtime, often a cumbersome requirement. The native C++ generator, though faster, depends on x86 assembly for field operations, rendering it impractical for use on other platforms (e.g., cross-compiling to ARM for mobile devices).\n\n`circom-witness-rs` comes with two modes:\n\n1. Generate the static execution graph required for the witness generation at build time (`--features=build-witness`).\n2. Generate the witness elements at runtime from serialized graph.\n\nIn the first mode, it generates the c++ version of the witness generator through circom and links itself against it. The c++ code is made accessible to rust through [`cxx`](https://github.com/dtolnay/cxx). It hooks all field functions (which are x86 assembly in the original generator), such that it can recreate the execution graph through symblic execution. The execution graph is further optimized through constant propagation and dead code elimination. The resulting graph is then serialized to a binary format. At runtime, the graph can be embedded in the binary and interpreted to generate the witness.\n\n## Usage\n\nSee this [example project](https://github.com/philsippl/semaphore-witness-example) for Semaphore with more details on building. \n\nSee `semaphore-rs` for an [example at runtime](https://github.com/worldcoin/semaphore-rs/blob/62f556bdc1a2a25021dcccc97af4dfa522ab5789/src/protocol/mod.rs#L161-L163).\n\nAll of those example were used with `circom compiler 2.1.6` ([dcf7d68](https://github.com/iden3/circom/tree/dcf7d687a81c6d9b3e3840181fd83cdaf5f4ac05)). Using a different version of circom might cause issues due to different c++ code being generated.\n\n## Benchmarks\n\n### [semaphore-rs](https://github.com/worldcoin/semaphore-rs/tree/main)\n**TLDR: For semaphore circuit (depth 30) `circom-witness-rs` is ~25x faster than wasm and ~10x faster than native c++ version.**\n```\ncargo bench --bench=criterion --features=bench,depth_30\n```\n\nWith `circom-witness-rs`:q\n```\nwitness_30              time:   [993.84 µs 996.62 µs 999.42 µs]\n```\n\nWith wasm witness generator from [`circom-compat`](https://github.com/arkworks-rs/circom-compat/blob/master/src/witness/witness_calculator.rs):\n```\nwitness_30              time:   [24.630 ms 24.693 ms 24.759 ms]\n```\n\nWith native c++ witness generator from circom: `9.640ms`\n\nAs a nice side effect of the graph optimizations, the binary size is also reduced heavily. In the example of Semaphore the binary size is reduced from `1.3MB` (`semaphore.wasm`) to `350KB` (`graph.bin`). \n\n## Unimplemented features\n\nThere are still quite a few missing operations that need to be implemented. The list of supported and unsupported operations can be found here. Support for the missing operations is very straighfoward and will be added in the future.\nhttps://github.com/philsippl/circom-witness-rs/blob/e889cedde49a8929812b825aede55d9668118302/src/generate.rs#L61-L89\n"
  },
  {
    "path": "crates/circom-witness-rs/build.rs",
    "content": "use std::{env, fs, path::Path, process::Command};\n\nfn main() {\n    if cfg!(feature = \"build-witness\") {\n        let witness_cpp = env::var(\"WITNESS_CPP\").unwrap();\n        let circuit_file = Path::new(&witness_cpp);\n        let circuit_name = circuit_file.file_stem().unwrap().to_str().unwrap();\n\n        let status = Command::new(\"circom\")\n            .args([\n                fs::canonicalize(circuit_file).unwrap().to_str().unwrap(),\n                \"--c\",\n            ])\n            .status()\n            .unwrap();\n        assert!(status.success());\n\n        let cpp = Path::new(\"./\")\n            .join(circuit_name.to_owned() + \"_cpp\")\n            .join(circuit_name.to_owned() + \".cpp\");\n\n        println!(\"cargo:warning=\\\"{}\\\"\", cpp.to_str().unwrap());\n\n        let status = Command::new(\"./script/replace.sh\")\n            .arg(cpp.to_str().unwrap())\n            .status()\n            .unwrap();\n        assert!(status.success());\n\n        cxx_build::bridge(\"src/generate.rs\")\n            .file(\"src/circuit.cc\")\n            .flag_if_supported(\"-std=c++14\")\n            .flag_if_supported(\"-w\")\n            .flag_if_supported(\"-d\")\n            .flag_if_supported(\"-g\")\n            .compile(\"witness\");\n\n        println!(\"cargo:rerun-if-changed=src/main.rs\");\n        println!(\"cargo:rerun-if-changed=src/circuit.cc\");\n        println!(\"cargo:rerun-if-changed=include/circuit.h\");\n    }\n}\n"
  },
  {
    "path": "crates/circom-witness-rs/include/witness.h",
    "content": "#pragma once\n#include \"rust/cxx.h\"\n#include <memory>\n\ntypedef unsigned long long u64;\ntypedef uint32_t u32;\ntypedef uint8_t u8;\n\nstruct Circom_CalcWit;\n\nvoid run(Circom_CalcWit *buf);\nuint get_size_of_io_map();\nuint get_total_signal_no();\nuint get_main_input_signal_no();\nuint get_main_input_signal_start();\nuint get_number_of_components();\nuint get_size_of_constants();\nuint get_size_of_input_hashmap();\nuint get_size_of_witness();\n"
  },
  {
    "path": "crates/circom-witness-rs/script/replace.sh",
    "content": "#!/bin/sh\n\n# Check for input file\nif [ \"$#\" -ne 1 ]; then\n    echo \"Usage: $0 <filename>\"\n    exit 1\nfi\n\nfilename=$(basename \"$1\" .cpp)\n\n# Add header\ncat <<EOT > \"$filename.new\"\n#include \"witness/include/witness.h\"\n#include \"witness/src/generate.rs.h\"\n\n/// We need this accessor since cxx doesn't support hashmaps yet\nclass IOSignalInfoAccessor {\nprivate:\n  Circom_CalcWit *calcWitContext;\n\npublic:\n  explicit IOSignalInfoAccessor(Circom_CalcWit *calcWit)\n      : calcWitContext(calcWit) {}\n  auto operator[](size_t index) const -> decltype(auto) {\n    return (calcWitContext\n                ->templateInsId2IOSignalInfoList)[index % get_size_of_input_hashmap()];\n  }\n};\n\ntypedef void (*Circom_TemplateFunction)(uint __cIdx, Circom_CalcWit* __ctx);\n\n//////////////////////////////////////////////////////////////////\n/// Generated code from circom compiler below\n//////////////////////////////////////////////////////////////////\n\nEOT\n\n# Replace a few things we can't do in cxx\nsed -e 's/FrElement\\* signalValues/rust::Vec<FrElement> \\&signalValues/g' \\\n    -e 's/std::string/rust::string/g' \\\n    -e 's/ctx->templateInsId2IOSignalInfo/IOSignalInfoAccessor(ctx)/g' \\\n    -e 's/u32\\* mySubcomponents/rust::Vec<u32> mySubcomponents/g' \\\n    -e 's/FrElement\\* circuitConstants/rust::Vec<FrElement> \\&circuitConstants/g' \\\n    -e 's/rust::string\\* listOfTemplateMessages/rust::Vec<rust::string> \\&listOfTemplateMessages/g' \\\n    -e 's/FrElement expaux\\[\\([0-9]*\\)\\];/rust::Vec<FrElement> expaux = create_vec(\\1);/g' \\\n    -e 's/FrElement lvar\\[\\([0-9]*\\)\\];/rust::Vec<FrElement> lvar = create_vec(\\1);/g' \\\n    -e 's/FrElement lvarcall\\[\\([0-9]*\\)\\];/rust::Vec<FrElement> lvarcall = create_vec(\\1);/g' \\\n    -e 's/PFrElement aux_dest/FrElement \\*aux_dest/g' \\\n    -e 's/subcomponents = new uint\\[\\([0-9]*\\)\\];/subcomponents = create_vec_u32(\\1);/g' \\\n    -e '/trace/d' \\\n    -e 's/\\(ctx,\\)\\(lvarcall,\\)\\(myId,\\)/\\1\\&\\2\\3/g' \\\n    -e '/^#include/d' \\\n    -e '/assert/d' \\\n    -e '/mySubcomponentsParallel/d' \\\n    -e 's/FrElement lvarcall\\[\\([0-9]*\\)\\];/rust::Vec<FrElement> lvarcall = create_vec(\\1);/g' \\\n    -e 's/,FrElement\\* lvar,/,rust::Vec<FrElement>\\& lvar,/g' \\\n    -e 's/ctx,\\&lvarcall,myId,/ctx,lvarcall,myId,/g' \\\n    -e '/delete \\[\\][^;]*;/d' -e 'N;/\\ndelete/!P;D' \\\n    -e '/^#include/d' \"$1\" >> \"$filename.new\"\n\n\nsed -E -e 's/\"([^\"]+)\"\\+ctx->generate_position_array\\(([^)]+)\\)/generate_position_array(\"\\1\", \\2)/g' \\\n    -e 's/subcomponents = new uint\\[([0-9]+)\\]\\{0\\};/subcomponents = create_vec_u32(\\1);/g' \\\n    -e 's/^uint aux_dimensions\\[([0-9]+)\\] = \\{([^}]+)\\};$/rust::Vec<uint> aux_dimensions = rust::Vec<uint32_t>{\\2};/' \"$filename.new\" > \"src/circuit.cc\"\n\ncp \"$(echo $filename)_cpp/$filename.dat\" src/constants.dat\n"
  },
  {
    "path": "crates/circom-witness-rs/src/field.rs",
    "content": "#![allow(unused, non_snake_case)]\n\nuse crate::graph::{Node, Operation};\nuse ruint::{aliases::U256, uint};\nuse std::{ptr, sync::Mutex};\n\npub const M: U256 =\n    uint!(21888242871839275222246405745257275088548364400416034343698204186575808495617_U256);\n\npub const INV: u64 = 14042775128853446655;\n\npub const R: U256 = uint!(0x0e0a77c19a07df2f666ea36f7879462e36fc76959f60cd29ac96341c4ffffffb_U256);\n\nstatic NODES: Mutex<Vec<Node>> = Mutex::new(Vec::new());\nstatic VALUES: Mutex<Vec<U256>> = Mutex::new(Vec::new());\nstatic CONSTANT: Mutex<Vec<bool>> = Mutex::new(Vec::new());\n\n#[derive(Debug, Default, Clone, Copy)]\npub struct FrElement(pub usize);\n\npub fn print_eval() {\n    let nodes = NODES.lock().unwrap();\n    let values = VALUES.lock().unwrap();\n    let constant = CONSTANT.lock().unwrap();\n\n    let mut constants = 0_usize;\n    for (i, node) in nodes.iter().enumerate() {\n        print!(\"{}: {:?}\", i, node);\n        if constant[i] {\n            constants += 1;\n            println!(\" = {}\", values[i]);\n        } else {\n            println!();\n        }\n    }\n    eprintln!(\n        \"{} nodes of which {} constant and {} dynamic\",\n        nodes.len(),\n        constants,\n        nodes.len() - constants\n    );\n}\n\npub fn get_graph() -> Vec<Node> {\n    NODES.lock().unwrap().clone()\n}\n\npub fn get_values() -> Vec<U256> {\n    VALUES.lock().unwrap().clone()\n}\n\npub fn undefined() -> FrElement {\n    FrElement(usize::MAX)\n}\n\npub fn constant(c: U256) -> FrElement {\n    let mut nodes = NODES.lock().unwrap();\n    let mut values = VALUES.lock().unwrap();\n    let mut constant = CONSTANT.lock().unwrap();\n    assert_eq!(nodes.len(), values.len());\n    assert_eq!(nodes.len(), constant.len());\n\n    nodes.push(Node::Constant(c));\n    values.push(c);\n    constant.push(true);\n\n    FrElement(nodes.len() - 1)\n}\n\npub fn input(i: usize, value: U256) -> FrElement {\n    let mut nodes = NODES.lock().unwrap();\n    let mut values = VALUES.lock().unwrap();\n    let mut constant = CONSTANT.lock().unwrap();\n    assert_eq!(nodes.len(), values.len());\n    assert_eq!(nodes.len(), constant.len());\n\n    nodes.push(Node::Input(i));\n    values.push(value);\n    constant.push(false);\n\n    FrElement(nodes.len() - 1)\n}\n\nfn binop(op: Operation, to: *mut FrElement, a: *const FrElement, b: *const FrElement) {\n    let mut nodes = NODES.lock().unwrap();\n    let mut values = VALUES.lock().unwrap();\n    let mut constant = CONSTANT.lock().unwrap();\n    assert_eq!(nodes.len(), values.len());\n    assert_eq!(nodes.len(), constant.len());\n\n    let (a, b, to) = unsafe { ((*a).0, (*b).0, &mut (*to).0) };\n    assert!(a < nodes.len());\n    assert!(b < nodes.len());\n    nodes.push(Node::Op(op, a, b));\n    *to = nodes.len() - 1;\n\n    let (va, vb) = (values[a], values[b]);\n    values.push(op.eval(va, vb));\n\n    let (ca, cb) = (constant[a], constant[b]);\n    constant.push(ca && cb);\n}\n\npub fn Fr_mul(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {\n    binop(Operation::Mul, to, a, b);\n}\n\n#[allow(warnings)]\npub unsafe fn Fr_add(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {\n    binop(Operation::Add, to, a, b);\n}\n\n#[allow(warnings)]\npub unsafe fn Fr_sub(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {\n    binop(Operation::Sub, to, a, b);\n}\n\n#[allow(warnings)]\npub fn Fr_copy(to: *mut FrElement, a: *const FrElement) {\n    unsafe {\n        *to = *a;\n    }\n}\n\n#[allow(warnings)]\npub fn Fr_copyn(to: *mut FrElement, a: *const FrElement, n: usize) {\n    unsafe {\n        ptr::copy_nonoverlapping(a, to, n);\n    }\n}\n\n/// Create a vector of FrElement with length `len`.\n/// Needed because the default constructor of opaque type is not implemented.\npub fn create_vec(len: usize) -> Vec<FrElement> {\n    vec![FrElement(usize::MAX); len]\n}\n\npub fn create_vec_u32(len: usize) -> Vec<u32> {\n    vec![0; len]\n}\n\npub fn generate_position_array(\n    prefix: String,\n    dimensions: Vec<u32>,\n    size_dimensions: u32,\n    index: u32,\n) -> String {\n    let mut positions: String = prefix;\n    let mut index = index;\n    for i in 0..size_dimensions {\n        let last_pos = index % dimensions[size_dimensions as usize - 1 - i as usize];\n        index /= dimensions[size_dimensions as usize - 1 - i as usize];\n        let new_pos = format!(\"[{}]\", last_pos);\n        positions = new_pos + &positions;\n    }\n    positions\n}\n\npub unsafe fn Fr_toInt(a: *const FrElement) -> u64 {\n    let nodes = NODES.lock().unwrap();\n    let values = VALUES.lock().unwrap();\n    let constant = CONSTANT.lock().unwrap();\n    assert_eq!(nodes.len(), values.len());\n    assert_eq!(nodes.len(), constant.len());\n\n    let a = unsafe { (*a).0 };\n    assert!(a < nodes.len());\n    assert!(constant[a]);\n    values[a].try_into().unwrap()\n}\n\npub unsafe fn print(a: *const FrElement) {\n    println!(\"DEBUG>> {:?}\", (*a).0);\n}\n\npub fn Fr_isTrue(a: *mut FrElement) -> bool {\n    let nodes = NODES.lock().unwrap();\n    let values = VALUES.lock().unwrap();\n    let constant = CONSTANT.lock().unwrap();\n    assert_eq!(nodes.len(), values.len());\n    assert_eq!(nodes.len(), constant.len());\n\n    let a = unsafe { (*a).0 };\n    assert!(a < nodes.len());\n    assert!(constant[a]);\n    values[a] != U256::ZERO\n}\n\npub unsafe fn Fr_eq(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {\n    binop(Operation::Eq, to, a, b);\n}\n\npub unsafe fn Fr_neq(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {\n    binop(Operation::Neq, to, a, b);\n}\n\npub unsafe fn Fr_lt(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {\n    binop(Operation::Lt, to, a, b);\n}\n\npub unsafe fn Fr_gt(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {\n    binop(Operation::Gt, to, a, b);\n}\n\npub unsafe fn Fr_leq(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {\n    binop(Operation::Leq, to, a, b);\n}\n\npub unsafe fn Fr_geq(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {\n    binop(Operation::Geq, to, a, b);\n}\n\npub unsafe fn Fr_lor(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {\n    binop(Operation::Lor, to, a, b);\n}\n\npub unsafe fn Fr_shl(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {\n    binop(Operation::Shl, to, a, b);\n}\n\npub unsafe fn Fr_shr(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {\n    binop(Operation::Shr, to, a, b);\n}\n\npub unsafe fn Fr_band(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {\n    binop(Operation::Band, to, a, b);\n}\n"
  },
  {
    "path": "crates/circom-witness-rs/src/generate.rs",
    "content": "#![allow(non_snake_case)]\n\nuse crate::field::{self, *};\nuse crate::graph::{self, Node};\nuse crate::HashSignalInfo;\nuse byteorder::{LittleEndian, ReadBytesExt};\nuse ffi::InputOutputList;\nuse ruint::{aliases::U256, uint};\nuse serde::{Deserialize, Serialize};\nuse std::{io::Read, time::Instant};\n\n#[cxx::bridge]\nmod ffi {\n\n    #[derive(Debug, Default, Clone)]\n    pub struct InputOutputList {\n        pub defs: Vec<IODef>,\n    }\n\n    #[derive(Debug, Clone, Default)]\n    pub struct IODef {\n        pub code: usize,\n        pub offset: usize,\n        pub lengths: Vec<usize>,\n    }\n\n    #[derive(Debug, Default, Clone)]\n    struct Circom_Component {\n        templateId: u64,\n        signalStart: u64,\n        inputCounter: u64,\n        templateName: String,\n        componentName: String,\n        idFather: u64,\n        subcomponents: Vec<u32>,\n        outputIsSet: Vec<bool>,\n    }\n\n    #[derive(Debug)]\n    struct Circom_CalcWit {\n        signalValues: Vec<FrElement>,\n        componentMemory: Vec<Circom_Component>,\n        circuitConstants: Vec<FrElement>,\n        templateInsId2IOSignalInfoList: Vec<InputOutputList>,\n        listOfTemplateMessages: Vec<String>,\n    }\n\n    // Rust types and signatures exposed to C++.\n    extern \"Rust\" {\n        type FrElement;\n\n        fn create_vec(len: usize) -> Vec<FrElement>;\n        fn create_vec_u32(len: usize) -> Vec<u32>;\n        fn generate_position_array(\n            prefix: String,\n            dimensions: Vec<u32>,\n            size_dimensions: u32,\n            index: u32,\n        ) -> String;\n\n        // Field operations\n        unsafe fn Fr_mul(to: *mut FrElement, a: *const FrElement, b: *const FrElement);\n        unsafe fn Fr_add(to: *mut FrElement, a: *const FrElement, b: *const FrElement);\n        unsafe fn Fr_sub(to: *mut FrElement, a: *const FrElement, b: *const FrElement);\n        unsafe fn Fr_copy(to: *mut FrElement, a: *const FrElement);\n        unsafe fn Fr_copyn(to: *mut FrElement, a: *const FrElement, n: usize);\n        // unsafe fn Fr_neg(to: *mut FrElement, a: *const FrElement);\n        // unsafe fn Fr_inv(to: *mut FrElement, a: *const FrElement);\n        // unsafe fn Fr_div(to: *mut FrElement, a: *const FrElement, b: *const FrElement);\n        // unsafe fn Fr_square(to: *mut FrElement, a: *const FrElement);\n        unsafe fn Fr_shl(to: *mut FrElement, a: *const FrElement, b: *const FrElement);\n        unsafe fn Fr_shr(to: *mut FrElement, a: *const FrElement, b: *const FrElement);\n        unsafe fn Fr_band(to: *mut FrElement, a: *const FrElement, b: *const FrElement);\n        // fn Fr_bor(to: &mut FrElement, a: &FrElement, b: &FrElement);\n        // fn Fr_bxor(to: &mut FrElement, a: &FrElement, b: &FrElement);\n        // fn Fr_bnot(to: &mut FrElement, a: &FrElement);\n        unsafe fn Fr_eq(to: *mut FrElement, a: *const FrElement, b: *const FrElement);\n        unsafe fn Fr_neq(to: *mut FrElement, a: *const FrElement, b: *const FrElement);\n        unsafe fn Fr_lt(to: *mut FrElement, a: *const FrElement, b: *const FrElement);\n        unsafe fn Fr_gt(to: *mut FrElement, a: *const FrElement, b: *const FrElement);\n        unsafe fn Fr_leq(to: *mut FrElement, a: *const FrElement, b: *const FrElement);\n        unsafe fn Fr_geq(to: *mut FrElement, a: *const FrElement, b: *const FrElement);\n        unsafe fn Fr_isTrue(a: *mut FrElement) -> bool;\n        // fn Fr_fromBool(to: &mut FrElement, a: bool);\n        unsafe fn Fr_toInt(a: *mut FrElement) -> u64;\n        unsafe fn Fr_lor(to: *mut FrElement, a: *const FrElement, b: *const FrElement);\n        unsafe fn print(a: *mut FrElement);\n        // fn Fr_pow(to: &mut FrElement, a: &FrElement, b: &FrElement);\n        // fn Fr_idiv(to: &mut FrElement, a: &FrElement, b: &FrElement);\n    }\n\n    // C++ types and signatures exposed to Rust.\n    unsafe extern \"C++\" {\n        include!(\"witness/include/witness.h\");\n\n        unsafe fn run(ctx: *mut Circom_CalcWit);\n        fn get_size_of_io_map() -> u32;\n        fn get_total_signal_no() -> u32;\n        fn get_main_input_signal_no() -> u32;\n        fn get_main_input_signal_start() -> u32;\n        fn get_number_of_components() -> u32;\n        fn get_size_of_constants() -> u32;\n        fn get_size_of_input_hashmap() -> u32;\n        fn get_size_of_witness() -> u32;\n    }\n}\n\nconst DAT_BYTES: &[u8] = include_bytes!(\"constants.dat\");\n\npub fn get_input_hash_map() -> Vec<HashSignalInfo> {\n    let mut bytes = &DAT_BYTES[..(ffi::get_size_of_input_hashmap() as usize) * 24];\n    let mut input_hash_map =\n        vec![HashSignalInfo::default(); ffi::get_size_of_input_hashmap() as usize];\n    for i in 0..ffi::get_size_of_input_hashmap() as usize {\n        let hash = bytes.read_u64::<LittleEndian>().unwrap();\n        let signalid = bytes.read_u64::<LittleEndian>().unwrap();\n        let signalsize = bytes.read_u64::<LittleEndian>().unwrap();\n        input_hash_map[i] = HashSignalInfo {\n            hash,\n            signalid,\n            signalsize,\n        };\n    }\n    input_hash_map\n}\n\npub fn get_witness_to_signal() -> Vec<usize> {\n    let mut bytes = &DAT_BYTES[(ffi::get_size_of_input_hashmap() as usize) * 24\n        ..(ffi::get_size_of_input_hashmap() as usize) * 24\n            + (ffi::get_size_of_witness() as usize) * 8];\n    let mut signal_list = Vec::with_capacity(ffi::get_size_of_witness() as usize);\n    for i in 0..ffi::get_size_of_witness() as usize {\n        signal_list.push(bytes.read_u64::<LittleEndian>().unwrap() as usize);\n    }\n    signal_list\n}\n\npub fn get_constants() -> Vec<FrElement> {\n    if ffi::get_size_of_constants() == 0 {\n        return vec![];\n    }\n\n    // skip the first part\n    let mut bytes = &DAT_BYTES[(ffi::get_size_of_input_hashmap() as usize) * 24\n        + (ffi::get_size_of_witness() as usize) * 8..];\n    let mut constants = vec![field::constant(U256::from(0)); ffi::get_size_of_constants() as usize];\n    for i in 0..ffi::get_size_of_constants() as usize {\n        let sv = bytes.read_i32::<LittleEndian>().unwrap() as i32;\n        let typ = bytes.read_u32::<LittleEndian>().unwrap() as u32;\n\n        let mut buf = [0; 32];\n        bytes.read_exact(&mut buf);\n\n        if typ & 0x80000000 == 0 {\n            constants[i] = field::constant(U256::from(sv));\n        } else {\n            constants[i] =\n                field::constant(U256::from_le_bytes(buf).mul_redc(uint!(1_U256), M, INV));\n        }\n    }\n\n    return constants;\n}\n\npub fn get_iosignals() -> Vec<InputOutputList> {\n    if ffi::get_size_of_io_map() == 0 {\n        return vec![];\n    }\n\n    // skip the first part\n    let mut bytes = &DAT_BYTES[(ffi::get_size_of_input_hashmap() as usize) * 24\n        + (ffi::get_size_of_witness() as usize) * 8\n        + (ffi::get_size_of_constants() as usize * 40)..];\n    let io_size = ffi::get_size_of_io_map() as usize;\n    let hashmap_size = ffi::get_size_of_input_hashmap() as usize;\n    let mut indices = vec![0usize; io_size];\n    let mut map: Vec<InputOutputList> = vec![InputOutputList::default(); hashmap_size];\n\n    (0..io_size).for_each(|i| {\n        let t32 = bytes.read_u32::<LittleEndian>().unwrap() as usize;\n        indices[i] = t32;\n    });\n\n    (0..io_size).for_each(|i| {\n        let l32 = bytes.read_u32::<LittleEndian>().unwrap() as usize;\n        let mut io_list: InputOutputList = InputOutputList { defs: vec![] };\n\n        (0..l32).for_each(|_j| {\n            let offset = bytes.read_u32::<LittleEndian>().unwrap() as usize;\n            let len = bytes.read_u32::<LittleEndian>().unwrap() as usize + 1;\n\n            let mut lengths = vec![0usize; len];\n\n            (1..len).for_each(|k| {\n                lengths[k] = bytes.read_u32::<LittleEndian>().unwrap() as usize;\n            });\n\n            io_list.defs.push(ffi::IODef {\n                code: 0,\n                offset,\n                lengths,\n            });\n        });\n        map[indices[i] % hashmap_size] = io_list;\n    });\n    map\n}\n\n/// Run cpp witness generator and optimize graph\npub fn build_witness() -> color_color_eyre::Result<()> {\n    let mut signal_values = vec![field::undefined(); ffi::get_total_signal_no() as usize];\n    signal_values[0] = field::constant(uint!(1_U256));\n\n    let total_input_len =\n        (ffi::get_main_input_signal_no() + ffi::get_main_input_signal_start()) as usize;\n\n    for i in 0..total_input_len {\n        signal_values[i + 1] = field::input(i + 1, uint!(0_U256));\n    }\n\n    let mut ctx = ffi::Circom_CalcWit {\n        signalValues: signal_values,\n        componentMemory: vec![\n            ffi::Circom_Component::default();\n            ffi::get_number_of_components() as usize\n        ],\n        circuitConstants: get_constants(),\n        templateInsId2IOSignalInfoList: get_iosignals(),\n        listOfTemplateMessages: vec![],\n    };\n\n    // measure time\n    let now = Instant::now();\n    unsafe {\n        ffi::run(&mut ctx as *mut _);\n    }\n    eprintln!(\"Calculation took: {:?}\", now.elapsed());\n\n    let signal_values = get_witness_to_signal();\n    let mut signals = signal_values\n        .into_iter()\n        .map(|i| ctx.signalValues[i].0)\n        .collect::<Vec<_>>();\n    let mut nodes = field::get_graph();\n    eprintln!(\"Graph with {} nodes\", nodes.len());\n\n    // Optimize graph\n    graph::optimize(&mut nodes, &mut signals);\n\n    // Store graph to file.\n    let input_map = get_input_hash_map();\n    let bytes = postcard::to_stdvec(&(&nodes, &signals, &input_map)).unwrap();\n    eprintln!(\"Graph size: {} bytes\", bytes.len());\n    std::fs::write(\"graph.bin\", bytes).unwrap();\n\n    // Evaluate the graph.\n    let input_len = (ffi::get_main_input_signal_no() + ffi::get_main_input_signal_start()) as usize; // TODO: fetch from file\n    let mut inputs = vec![U256::from(0); input_len];\n    inputs[0] = U256::from(1);\n    for i in 1..nodes.len() {\n        if let Node::Input(j) = nodes[i] {\n            inputs[j] = get_values()[i];\n        } else {\n            break;\n        }\n    }\n\n    let now = Instant::now();\n    for _ in 0..10 {\n        _ = graph::evaluate(&nodes, &inputs, &signals);\n    }\n    eprintln!(\"Calculation took: {:?}\", now.elapsed() / 10);\n\n    // Print graph\n    // for (i, node) in nodes.iter().enumerate() {\n    //     println!(\"node[{}] = {:?}\", i, node);\n    // }\n    // for (i, j) in signals.iter().enumerate() {\n    //     println!(\"signal[{}] = node[{}]\", i, j);\n    // }\n\n    Ok(())\n}\n"
  },
  {
    "path": "crates/circom-witness-rs/src/graph.rs",
    "content": "use std::{\n    collections::HashMap,\n    ops::{Shl, Shr},\n};\n\nuse crate::field::M;\nuse ark_bn254::Fr;\nuse ark_ff::PrimeField;\nuse rand::Rng;\nuse ruint::aliases::U256;\nuse serde::{Deserialize, Serialize};\n\nuse ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress, Validate};\n\nfn ark_se<S, A: CanonicalSerialize>(a: &A, s: S) -> Result<S::Ok, S::Error>\nwhere\n    S: serde::Serializer,\n{\n    let mut bytes = vec![];\n    a.serialize_with_mode(&mut bytes, Compress::Yes)\n        .map_err(serde::ser::Error::custom)?;\n    s.serialize_bytes(&bytes)\n}\n\nfn ark_de<'de, D, A: CanonicalDeserialize>(data: D) -> Result<A, D::Error>\nwhere\n    D: serde::de::Deserializer<'de>,\n{\n    let s: Vec<u8> = serde::de::Deserialize::deserialize(data)?;\n    let a = A::deserialize_with_mode(s.as_slice(), Compress::Yes, Validate::Yes);\n    a.map_err(serde::de::Error::custom)\n}\n\n#[derive(Hash, PartialEq, Eq, Debug, Clone, Copy, Serialize, Deserialize)]\npub enum Operation {\n    Mul,\n    MMul,\n    Add,\n    Sub,\n    Eq,\n    Neq,\n    Lt,\n    Gt,\n    Leq,\n    Geq,\n    Lor,\n    Shl,\n    Shr,\n    Band,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]\npub enum Node {\n    Input(usize),\n    Constant(U256),\n    #[serde(serialize_with = \"ark_se\", deserialize_with = \"ark_de\")]\n    MontConstant(Fr),\n    Op(Operation, usize, usize),\n}\n\nimpl Operation {\n    pub fn eval(&self, a: U256, b: U256) -> U256 {\n        use Operation::*;\n        match self {\n            Add => a.add_mod(b, M),\n            Sub => a.add_mod(M - b, M),\n            Mul => a.mul_mod(b, M),\n            Eq => U256::from(a == b),\n            Neq => U256::from(a != b),\n            Lt => U256::from(a < b),\n            Gt => U256::from(a > b),\n            Leq => U256::from(a <= b),\n            Geq => U256::from(a >= b),\n            Lor => U256::from(a != U256::ZERO || b != U256::ZERO),\n            Shl => compute_shl_uint(a, b),\n            Shr => compute_shr_uint(a, b),\n            Band => a.bitand(b),\n            _ => unimplemented!(\"operator {:?} not implemented\", self),\n        }\n    }\n\n    pub fn eval_fr(&self, a: Fr, b: Fr) -> Fr {\n        use Operation::*;\n        match self {\n            Add => a + b,\n            Sub => a - b,\n            Mul => a * b,\n            _ => unimplemented!(\"operator {:?} not implemented for Montgomery\", self),\n        }\n    }\n}\n\nfn compute_shl_uint(a: U256, b: U256) -> U256 {\n    debug_assert!(b.lt(&U256::from(256)));\n    let ls_limb = b.as_limbs()[0];\n    a.shl(ls_limb as usize)\n}\n\nfn compute_shr_uint(a: U256, b: U256) -> U256 {\n    debug_assert!(b.lt(&U256::from(256)));\n    let ls_limb = b.as_limbs()[0];\n    a.shr(ls_limb as usize)\n}\n\n/// All references must be backwards.\nfn assert_valid(nodes: &[Node]) {\n    for (i, &node) in nodes.iter().enumerate() {\n        if let Node::Op(_, a, b) = node {\n            assert!(a < i);\n            assert!(b < i);\n        }\n    }\n}\n\npub fn optimize(nodes: &mut Vec<Node>, outputs: &mut [usize]) {\n    tree_shake(nodes, outputs);\n    propagate(nodes);\n    value_numbering(nodes, outputs);\n    constants(nodes);\n    tree_shake(nodes, outputs);\n    montgomery_form(nodes);\n}\n\n#[allow(clippy::unnecessary_fallible_conversions)] // Prevents the false positive on line 143\npub fn evaluate(nodes: &[Node], inputs: &[U256], outputs: &[usize]) -> Vec<U256> {\n    // assert_valid(nodes);\n\n    // Evaluate the graph.\n    let mut values = Vec::with_capacity(nodes.len());\n    for &node in nodes.iter() {\n        let value = match node {\n            Node::Constant(c) => Fr::new(c.into()),\n            Node::MontConstant(c) => c,\n            Node::Input(i) => Fr::new(inputs[i].into()),\n            Node::Op(op, a, b) => op.eval_fr(values[a], values[b]),\n        };\n        values.push(value);\n    }\n\n    // Convert from Montgomery form and return the outputs.\n    let mut out = vec![U256::ZERO; outputs.len()];\n    for i in 0..outputs.len() {\n        out[i] = U256::try_from(values[outputs[i]].into_bigint()).unwrap();\n    }\n\n    out\n}\n\n/// Constant propagation\npub fn propagate(nodes: &mut [Node]) {\n    assert_valid(nodes);\n    let mut constants = 0_usize;\n    for i in 0..nodes.len() {\n        if let Node::Op(op, a, b) = nodes[i] {\n            if let (Node::Constant(va), Node::Constant(vb)) = (nodes[a], nodes[b]) {\n                nodes[i] = Node::Constant(op.eval(va, vb));\n                constants += 1;\n            } else if a == b {\n                // Not constant but equal\n                use Operation::*;\n                if let Some(c) = match op {\n                    Eq | Leq | Geq => Some(true),\n                    Neq | Lt | Gt => Some(false),\n                    _ => None,\n                } {\n                    nodes[i] = Node::Constant(U256::from(c));\n                    constants += 1;\n                }\n            }\n        }\n    }\n\n    eprintln!(\"Propagated {constants} constants\");\n}\n\n/// Remove unused nodes\npub fn tree_shake(nodes: &mut Vec<Node>, outputs: &mut [usize]) {\n    assert_valid(nodes);\n\n    // Mark all nodes that are used.\n    let mut used = vec![false; nodes.len()];\n    for &i in outputs.iter() {\n        used[i] = true;\n    }\n\n    // Work backwards from end as all references are backwards.\n    for i in (0..nodes.len()).rev() {\n        if used[i] {\n            if let Node::Op(_, a, b) = nodes[i] {\n                used[a] = true;\n                used[b] = true;\n            }\n        }\n    }\n\n    // Remove unused nodes\n    let n = nodes.len();\n    let mut retain = used.iter();\n    nodes.retain(|_| *retain.next().unwrap());\n    let removed = n - nodes.len();\n\n    // Renumber references.\n    let mut renumber = vec![None; n];\n    let mut index = 0;\n    for (i, &used) in used.iter().enumerate() {\n        if used {\n            renumber[i] = Some(index);\n            index += 1;\n        }\n    }\n    assert_eq!(index, nodes.len());\n    for (&used, renumber) in used.iter().zip(renumber.iter()) {\n        assert_eq!(used, renumber.is_some());\n    }\n\n    // Renumber references.\n    for node in nodes.iter_mut() {\n        if let Node::Op(_, a, b) = node {\n            *a = renumber[*a].unwrap();\n            *b = renumber[*b].unwrap();\n        }\n    }\n    for output in outputs.iter_mut() {\n        *output = renumber[*output].unwrap();\n    }\n\n    eprintln!(\"Removed {removed} unused nodes\");\n}\n\n/// Randomly evaluate the graph\nfn random_eval(nodes: &mut [Node]) -> Vec<U256> {\n    let mut rng = rand::thread_rng();\n    let mut values = Vec::with_capacity(nodes.len());\n    let mut inputs = HashMap::new();\n    let mut prfs = HashMap::new();\n    for node in nodes.iter() {\n        use Operation::*;\n        let value = match node {\n            // Constants evaluate to themselves\n            Node::Constant(c) => *c,\n\n            Node::MontConstant(_c) => unimplemented!(\"should not be used\"),\n\n            // Algebraic Ops are evaluated directly\n            // Since the field is large, by Swartz-Zippel if\n            // two values are the same then they are likely algebraically equal.\n            Node::Op(op @ (Add | Sub | Mul), a, b) => op.eval(values[*a], values[*b]),\n\n            // Input and non-algebraic ops are random functions\n            // TODO: https://github.com/recmo/uint/issues/95 and use .gen_range(..M)\n            Node::Input(i) => *inputs.entry(*i).or_insert_with(|| rng.gen::<U256>() % M),\n            Node::Op(op, a, b) => *prfs\n                .entry((*op, values[*a], values[*b]))\n                .or_insert_with(|| rng.gen::<U256>() % M),\n        };\n        values.push(value);\n    }\n    values\n}\n\n/// Value numbering\npub fn value_numbering(nodes: &mut [Node], outputs: &mut [usize]) {\n    assert_valid(nodes);\n\n    // Evaluate the graph in random field elements.\n    let values = random_eval(nodes);\n\n    // Find all nodes with the same value.\n    let mut value_map = HashMap::new();\n    for (i, &value) in values.iter().enumerate() {\n        value_map.entry(value).or_insert_with(Vec::new).push(i);\n    }\n\n    // For nodes that are the same, pick the first index.\n    let mut renumber = Vec::with_capacity(nodes.len());\n    for value in values {\n        renumber.push(value_map[&value][0]);\n    }\n\n    // Renumber references.\n    for node in nodes.iter_mut() {\n        if let Node::Op(_, a, b) = node {\n            *a = renumber[*a];\n            *b = renumber[*b];\n        }\n    }\n    for output in outputs.iter_mut() {\n        *output = renumber[*output];\n    }\n\n    eprintln!(\"Global value numbering applied\");\n}\n\n/// Probabilistic constant determination\npub fn constants(nodes: &mut [Node]) {\n    assert_valid(nodes);\n\n    // Evaluate the graph in random field elements.\n    let values_a = random_eval(nodes);\n    let values_b = random_eval(nodes);\n\n    // Find all nodes with the same value.\n    let mut constants = 0;\n    for i in 0..nodes.len() {\n        if let Node::Constant(_) = nodes[i] {\n            continue;\n        }\n        if values_a[i] == values_b[i] {\n            nodes[i] = Node::Constant(values_a[i]);\n            constants += 1;\n        }\n    }\n    eprintln!(\"Found {} constants\", constants);\n}\n\n/// Convert to Montgomery form\npub fn montgomery_form(nodes: &mut [Node]) {\n    for node in nodes.iter_mut() {\n        use Node::*;\n        use Operation::*;\n        match node {\n            Constant(c) => *node = MontConstant(Fr::new((*c).into())),\n            MontConstant(..) => (),\n            Input(..) => (),\n            Op(Add | Sub | Mul, ..) => (),\n            Op(..) => unimplemented!(\"Operators Montgomery form\"),\n        }\n    }\n    eprintln!(\"Converted to Montgomery form\");\n}\n"
  },
  {
    "path": "crates/circom-witness-rs/src/lib.rs",
    "content": "mod field;\npub mod graph;\n\n#[cfg(feature = \"build-witness\")]\npub mod generate;\n\nuse std::collections::HashMap;\n\nuse ruint::aliases::U256;\nuse serde::{Deserialize, Serialize};\n\nuse crate::graph::Node;\n\n#[derive(Debug, Default, Clone, Serialize, Deserialize)]\npub struct HashSignalInfo {\n    pub hash: u64,\n    pub signalid: u64,\n    pub signalsize: u64,\n}\n\npub struct Graph {\n    pub nodes: Vec<Node>,\n    pub signals: Vec<usize>,\n    pub input_mapping: Vec<HashSignalInfo>,\n}\n\nfn fnv1a(s: &str) -> u64 {\n    let mut hash: u64 = 0xCBF29CE484222325;\n    for c in s.bytes() {\n        hash ^= c as u64;\n        hash = hash.wrapping_mul(0x100000001B3);\n    }\n    hash\n}\n\n/// Loads the graph from bytes\npub fn init_graph(graph_bytes: &[u8]) -> color_eyre::Result<Graph> {\n    let (nodes, signals, input_mapping): (Vec<Node>, Vec<usize>, Vec<HashSignalInfo>) =\n        postcard::from_bytes(graph_bytes)?;\n\n    Ok(Graph {\n        nodes,\n        signals,\n        input_mapping,\n    })\n}\n\n/// Calculates the number of needed inputs\npub fn get_inputs_size(graph: &Graph) -> usize {\n    let mut start = false;\n    let mut max_index = 0usize;\n    for &node in graph.nodes.iter() {\n        if let Node::Input(i) = node {\n            if i > max_index {\n                max_index = i;\n            }\n            start = true\n        } else if start {\n            break;\n        }\n    }\n    max_index + 1\n}\n\n/// Allocates inputs vec with position 0 set to 1\npub fn get_inputs_buffer(size: usize) -> Vec<U256> {\n    let mut inputs = vec![U256::ZERO; size];\n    inputs[0] = U256::from(1);\n    inputs\n}\n\n/// Calculates the position of the given signal in the inputs buffer\npub fn get_input_mapping(input_list: &Vec<String>, graph: &Graph) -> HashMap<String, usize> {\n    let mut input_mapping = HashMap::new();\n    for key in input_list {\n        let h = fnv1a(key);\n        let pos = graph\n            .input_mapping\n            .iter()\n            .position(|x| x.hash == h)\n            .unwrap();\n        let si = (graph.input_mapping[pos].signalid) as usize;\n        input_mapping.insert(key.to_string(), si);\n    }\n    input_mapping\n}\n\n/// Sets all provided inputs given the mapping and inputs buffer\npub fn populate_inputs(\n    input_list: &HashMap<String, Vec<U256>>,\n    input_mapping: &HashMap<String, usize>,\n    input_buffer: &mut [U256],\n) {\n    for (key, value) in input_list {\n        let start = input_mapping[key];\n        let end = start + value.len();\n        input_buffer[start..end].copy_from_slice(value);\n    }\n}\n\n/// Calculate witness based on serialized graph and inputs\npub fn calculate_witness(\n    input_list: HashMap<String, Vec<U256>>,\n    graph: &Graph,\n) -> color_eyre::Result<Vec<U256>> {\n    let mut inputs_buffer = get_inputs_buffer(get_inputs_size(graph));\n    let input_mapping = get_input_mapping(&input_list.keys().cloned().collect(), graph);\n    populate_inputs(&input_list, &input_mapping, &mut inputs_buffer);\n    Ok(graph::evaluate(\n        &graph.nodes,\n        &inputs_buffer,\n        &graph.signals,\n    ))\n}\n"
  },
  {
    "path": "crates/hasher/Cargo.toml",
    "content": "[package]\nname = \"semaphore-rs-hasher\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nauthors.workspace = true\ndescription.workspace = true\nkeywords.workspace = true\ncategories.workspace = true\n\n[dependencies]\nbytemuck.workspace = true\n"
  },
  {
    "path": "crates/hasher/src/lib.rs",
    "content": "use bytemuck::Pod;\n\n/// Hash types, values and algorithms for a Merkle tree\npub trait Hasher {\n    /// Type of the leaf and node hashes\n    type Hash;\n\n    /// Compute the hash of an intermediate node\n    fn hash_node(left: &Self::Hash, right: &Self::Hash) -> Self::Hash;\n}\n\n/// A marker trait that indicates some useful properties of a hash type\n///\n/// It's not strictly necessary, but for many implementations it's a useful set of constraints\npub trait Hash: Pod + Eq + Send + Sync {}\n\nimpl<T> Hash for T where T: Pod + Eq + Send + Sync {}\n"
  },
  {
    "path": "crates/js/.gitignore",
    "content": "/target\n**/*.rs.bk\nCargo.lock\nbin/\npkg/\npkg-nodejs/\npkg-bundler/\npkg-web/\nwasm-pack.log\n"
  },
  {
    "path": "crates/js/Cargo.toml",
    "content": "[package]\nname = \"semaphore-rs-js\"\nversion = \"0.3.0\"\nedition.workspace = true\npublish = false # independently versioned; not part of the workspace release cycle\nhomepage.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nauthors.workspace = true\ndescription.workspace = true\nkeywords.workspace = true\ncategories.workspace = true\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\nsemaphore-rs-proof = { workspace = true, default-features = false }\nruint.workspace = true\nhex.workspace = true\n\nwasm-bindgen.workspace = true\njs-sys.workspace = true\ngetrandom.workspace = true\n\n[dev-dependencies]\nwasm-bindgen-test.workspace = true\n"
  },
  {
    "path": "crates/js/README.md",
    "content": "# WASM bindings for semaphore-rs related functionality\n\nThis crate exposes semaphore-rs functionality to WASM. Currently it only exposes proof compression.\n\n## Building & publishing\n\nwasm-pack doesn't allow us to compile to a single target for node and browser usage. Instead we'll publish a package for each target.\n\nThe `build_and_publish.sh` script handles all of that.\n\nTo build and publish a new version simply run `./build_and_publish.sh`. Note that the package will likely fail to publish if using your own npm account.\n\nTo only check the build output run `DRY_RUN=1 ./build_and_publish.sh`.\n\n## Example\n\nRefer to `example/index.mjs` or `example/index.ts` for usage\n"
  },
  {
    "path": "crates/js/build.mjs",
    "content": "#!/usr/bin/env node\n// build.mjs\n// This script performs the following steps:\n// 1. Uses `cargo metadata` to extract package metadata.\n// 2. Constructs a detailed package.json using that metadata.\n// 3. Invokes `cargo build --target wasm32-unknown-unknown` for the local package.\n// 4. Locates the wasm artifact (by searching upward for the target directory),\n//    and runs wasm-bindgen (with --target web, --omit-imports, --omit-default-module-path)\n//    outputting the JS bindings into a pkg directory.\n// 5. Generates an inline loader script that embeds the wasm as a base64 string.\n// 6. Publishes the package (unless the `--dry-run` CLI argument is present).\n\nimport fs from 'fs';\nimport path from 'path';\nimport { execSync } from 'child_process';\n\nconst isDryRun = process.argv.includes('--dry-run');\n\nasync function main() {\n  // 1. Extract package metadata using cargo metadata.\n  console.log(\"Extracting package metadata via cargo metadata...\");\n  const metadataJson = execSync('cargo metadata --no-deps --format-version 1', { encoding: 'utf-8' });\n  const metadata = JSON.parse(metadataJson);\n  const manifestPath = path.resolve('Cargo.toml');\n  const pkgMeta = metadata.packages.find(pkg => path.resolve(pkg.manifest_path) === manifestPath);\n  if (!pkgMeta) {\n    throw new Error(\"Could not find package metadata for the current Cargo.toml\");\n  }\n  const pkgName = pkgMeta.name;\n  const pkgVersion = pkgMeta.version;\n  const pkgDescription = pkgMeta.description || \"\";\n  const pkgLicense = pkgMeta.license || \"\";\n  const pkgHomepage = pkgMeta.homepage || \"\";\n  const pkgRepository = pkgMeta.repository || \"\";\n  const pkgKeywords = pkgMeta.keywords || [];\n  // We'll use authors as collaborators.\n  const pkgCollaborators = pkgMeta.authors || [];\n\n  // Convert crate name to snake_case for file naming (dashes become underscores).\n  const pkgBaseName = pkgName.replace(/-/g, '_');\n  // wasm-bindgen will output files named like `<pkgBaseName>.js` and `<pkgBaseName>_bg.wasm`\n  const wasmBindgenJs = `${pkgBaseName}.js`;\n  const wasmBindgenWasm = `${pkgBaseName}_bg.wasm`;\n  const wasmBindgenDts = `${pkgBaseName}.d.ts`;\n\n  // 2. Build the Rust crate for the wasm target.\n  console.log(\"Building the Rust project with cargo...\");\n  execSync('cargo build --target wasm32-unknown-unknown', { stdio: 'inherit' });\n\n  // 3. Locate the target directory by searching upward from the current directory.\n  let targetDir = null;\n  let currentDir = process.cwd();\n  while (currentDir !== path.parse(currentDir).root) {\n    const potentialTarget = path.join(currentDir, 'target');\n    if (fs.existsSync(potentialTarget)) {\n      targetDir = potentialTarget;\n      break;\n    }\n    currentDir = path.dirname(currentDir);\n  }\n  if (!targetDir) {\n    throw new Error(\"Could not locate the target directory\");\n  }\n  // Assume a debug build; the wasm artifact should be at:\n  //   target/wasm32-unknown-unknown/debug/<pkgBaseName>.wasm\n  const wasmArtifactPath = path.join(targetDir, 'wasm32-unknown-unknown', 'debug', `${pkgBaseName}.wasm`);\n  if (!fs.existsSync(wasmArtifactPath)) {\n    throw new Error(`Wasm artifact not found at ${wasmArtifactPath}`);\n  }\n\n  // 4. Run wasm-bindgen on the artifact.\n  console.log(\"Running wasm-bindgen...\");\n  const pkgDir = path.resolve('pkg');\n  if (!fs.existsSync(pkgDir)) {\n    fs.mkdirSync(pkgDir);\n  }\n  const wasmBindgenCmd = `wasm-bindgen ${wasmArtifactPath} --out-dir ${pkgDir} --target web --omit-imports --omit-default-module-path`;\n  execSync(wasmBindgenCmd, { stdio: 'inherit' });\n\n  // 5. Construct the inline loader.\n  const wasmOutputPath = path.join(pkgDir, wasmBindgenWasm);\n  if (!fs.existsSync(wasmOutputPath)) {\n    throw new Error(`Wasm file not found in pkg directory: ${wasmOutputPath}`);\n  }\n  const wasmBuffer = fs.readFileSync(wasmOutputPath);\n  const wasmBase64 = wasmBuffer.toString('base64');\n\n  const inlineLoaderContent = `\n// This file is auto-generated by build.mjs.\n// It inlines the wasm module as a base64 string and loads it synchronously.\n\nimport { initSync } from './${wasmBindgenJs}';\n\nconst base64Wasm = \"${wasmBase64}\";\n\n// Convert a base64 string to a Uint8Array.\nfunction base64ToUint8Array(base64) {\n  if (typeof atob === 'function') {\n    const binaryString = atob(base64);\n    const len = binaryString.length;\n    const bytes = new Uint8Array(len);\n    for (let i = 0; i < len; i++) {\n      bytes[i] = binaryString.charCodeAt(i);\n    }\n    return bytes;\n  } else if (typeof Buffer === 'function') {\n    return new Uint8Array(Buffer.from(base64, 'base64'));\n  } else {\n    throw new Error('No base64 decoder available');\n  }\n}\n\nconst wasmBytes = base64ToUint8Array(base64Wasm);\n\n// Initialize the generated bindings with the inlined wasm instance.\ninitSync({ module: wasmBytes });\n\nexport * from './${wasmBindgenJs}';\n`.trim();\n\n  const inlineLoaderPath = path.join(pkgDir, 'index.js');\n  fs.writeFileSync(inlineLoaderPath, inlineLoaderContent);\n  console.log(`Generated inline loader: ${inlineLoaderPath}`);\n\n  // 6. Construct a fleshed-out package.json.\n  const packageJson = {\n    name: pkgName,\n    type: \"module\",\n    collaborators: pkgCollaborators,\n    description: pkgDescription,\n    version: pkgVersion,\n    license: pkgLicense,\n    repository: pkgRepository ? { type: \"git\", url: pkgRepository } : undefined,\n    files: [\n      // With inlined wasm, ship the loader and generated JS bindings.\n      \"index.js\",\n      wasmBindgenJs,\n      wasmBindgenDts\n    ],\n    main: \"index.js\",\n    homepage: pkgHomepage,\n    types: wasmBindgenDts,\n    sideEffects: [\n      \"./snippets/*\"\n    ],\n    keywords: pkgKeywords\n  };\n\n  // Remove any keys that are undefined.\n  Object.keys(packageJson).forEach(key => {\n    if (packageJson[key] === undefined) {\n      delete packageJson[key];\n    }\n  });\n\n  const pkgJsonPath = path.join(pkgDir, 'package.json');\n  fs.writeFileSync(pkgJsonPath, JSON.stringify(packageJson, null, 2));\n  console.log(`Updated package.json: ${pkgJsonPath}`);\n\n  // 7. Publish the package unless --dry-run is provided.\n  if (!isDryRun) {\n    console.log(\"Publishing package...\");\n    execSync('npm publish', { stdio: 'inherit', cwd: pkgDir });\n  } else {\n    console.log(\"--dry-run flag present, skipping npm publish.\");\n  }\n\n  console.log(\"Build complete: Wasm module inlined and package.json regenerated.\");\n}\n\nmain().catch(err => {\n  console.error(err);\n  process.exit(1);\n});\n\n"
  },
  {
    "path": "crates/js/example/.gitignore",
    "content": "node_modules/\n"
  },
  {
    "path": "crates/js/example/index.mjs",
    "content": "import { compressProof, decompressProof } from \"semaphore-rs-js\";\n\nconst proof = [\n  \"0x2d77679b613036865f4518894c80691cf65338fe7834fe3dd5f98c4f0f5a9e6d\",\n  \"0x24018e845edf74d69528a63eed053296a397df13a1d08873e2b2d673837b31c3\",\n  \"0x099d39b2cbca524b5916ac97dbc4afc1b8a5f59d65ba583fc49ec2677226e926\",\n  \"0x0da5812d7b4e0beb22d25c194431674396aec70751873edb9ac8c933ba1f0f2e\",\n  \"0x0723caca23efb9aa44db59ead0eeb28c2efb9c766d9a3f994ed047179e37b347\",\n  \"0x02166d9fc2d4cf446b120e5663880e0927825aa36a02b896ac0f3a5ef6e0239b\",\n  \"0x287fb1d0415a734ba76df9eb50ca6758bb806272f8fe40e3adbad3a850c05167\",\n  \"0x1240cf8aa43cf4ea4a2d8dffac653a6467cefd0f19e129cffad85299d6705444\",\n];\n\nconst compressed = compressProof(proof);\nconst decompressed = decompressProof(compressed);\n\nfor (let i = 0; i < 8; i++) {\n  if (proof[i] !== decompressed[i]) {\n    console.log(\"Proof not equal after decompression\");\n  }\n}\n"
  },
  {
    "path": "crates/js/example/index.ts",
    "content": "import { compressProof, decompressProof } from \"semaphore-rs-js\";\n\nconst proof: [string, string, string, string, string, string, string, string] =\n  [\n    \"0x2d77679b613036865f4518894c80691cf65338fe7834fe3dd5f98c4f0f5a9e6d\",\n    \"0x24018e845edf74d69528a63eed053296a397df13a1d08873e2b2d673837b31c3\",\n    \"0x099d39b2cbca524b5916ac97dbc4afc1b8a5f59d65ba583fc49ec2677226e926\",\n    \"0x0da5812d7b4e0beb22d25c194431674396aec70751873edb9ac8c933ba1f0f2e\",\n    \"0x0723caca23efb9aa44db59ead0eeb28c2efb9c766d9a3f994ed047179e37b347\",\n    \"0x02166d9fc2d4cf446b120e5663880e0927825aa36a02b896ac0f3a5ef6e0239b\",\n    \"0x287fb1d0415a734ba76df9eb50ca6758bb806272f8fe40e3adbad3a850c05167\",\n    \"0x1240cf8aa43cf4ea4a2d8dffac653a6467cefd0f19e129cffad85299d6705444\",\n  ];\n\nconst compressed = compressProof(proof);\nconst decompressed = decompressProof(compressed);\n\nfor (let i = 0; i < 8; i++) {\n  if (proof[i] !== decompressed[i]) {\n    console.log(\"Proof not equal after decompression\");\n  }\n}\n"
  },
  {
    "path": "crates/js/example/package.json",
    "content": "{\n  \"name\": \"testing\",\n  \"version\": \"1.0.0\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n  },\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"description\": \"\",\n  \"dependencies\": {\n    \"semaphore-rs-js\": \"0.3.0\"\n  }\n}\n"
  },
  {
    "path": "crates/js/src/lib.rs",
    "content": "use js_sys::Array;\nuse ruint::aliases::U256;\nuse semaphore_rs_proof::{compression::CompressedProof, Proof};\nuse wasm_bindgen::prelude::*;\n\n/// Compresses a Groth16 proof\n#[wasm_bindgen(\n    js_name = \"compressProof\",\n    unchecked_return_type = \"[string, string, string, string]\",\n    return_description = \"An array of 4 0x prefixed, hex encoded strings representing a compressed proof\"\n)]\npub fn compress_proof(\n    #[wasm_bindgen(\n        unchecked_param_type = \"[string, string, string, string, string, string, string, string]\",\n        param_description = \"An array of 8 hex encoded strings (with optional 0x prefixes) that represent an uncompressed proof\"\n    )]\n    proof: Array,\n) -> Result<Array, JsError> {\n    let proof: Vec<String> = proof\n        .iter()\n        .map(|v| v.as_string().unwrap_or_default())\n        .collect();\n\n    let proof = from_vec(proof)?;\n    let proof = Proof::from_flat(proof);\n\n    let proof = semaphore_rs_proof::compression::compress_proof(proof)\n        .ok_or_else(|| JsError::new(\"Failed to compress proof\"))?\n        .flatten();\n\n    Ok(to_js_array(proof))\n}\n\n/// Decompresses a Groth16 proof\n#[wasm_bindgen(\n    js_name = \"decompressProof\",\n    unchecked_return_type = \"[string, string, string, string, string, string, string, string]\",\n    return_description = \"An array of 8 0x prefixed, hex encoded strings representing an uncompressed proof\"\n)]\npub fn decompress_proof(\n    #[wasm_bindgen(\n        js_name = \"compressedProof\",\n        unchecked_param_type = \"[string, string, string, string]\",\n        param_description = \"An array of 4 hex encoded strings (with optional 0x prefixes) that represent a compressed proof\"\n    )]\n    compressed_proof: Array,\n) -> Result<Array, JsError> {\n    let compressed_proof: Vec<String> = compressed_proof\n        .iter()\n        .map(|v| v.as_string().unwrap_or_default())\n        .collect();\n\n    let proof = from_vec(compressed_proof)?;\n    let proof = CompressedProof::from_flat(proof);\n\n    let proof = semaphore_rs_proof::compression::decompress_proof(proof)\n        .ok_or_else(|| JsError::new(\"Failed to decompress proof\"))?;\n    let proof = proof.flatten();\n\n    Ok(to_js_array(proof))\n}\n\nfn from_vec<const N: usize>(proof: Vec<String>) -> Result<[U256; N], JsError> {\n    if proof.len() != N {\n        return Err(JsError::new(&format!(\"Proof length must be {N}\")));\n    }\n\n    let proof: Vec<U256> = proof\n        .into_iter()\n        .map(|s| {\n            U256::from_str_radix(s.trim_start_matches(\"0x\"), 16)\n                .map_err(|err| JsError::new(&err.to_string()))\n        })\n        .collect::<Result<_, _>>()?;\n\n    let proof: [U256; N] = proof.try_into().unwrap();\n\n    Ok(proof)\n}\n\nfn to_js_array<const N: usize>(arr: [U256; N]) -> Array {\n    let js_array = Array::new();\n    arr.iter().take(N).for_each(|v| {\n        js_array.push(&JsValue::from_str(&format!(\"{:#066x}\", v)));\n    });\n    js_array\n}\n"
  },
  {
    "path": "crates/keccak/Cargo.toml",
    "content": "[package]\nname = \"semaphore-rs-keccak\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nauthors.workspace = true\ndescription.workspace = true\nkeywords.workspace = true\ncategories.workspace = true\n\n[dependencies]\nsemaphore-rs-hasher.workspace = true\ntiny-keccak = { workspace = true, features = [\"keccak\"] }\n\n[features]\ndefault = [\"sha3\"]\nsha3 = [\"tiny-keccak/sha3\"]\n"
  },
  {
    "path": "crates/keccak/src/keccak.rs",
    "content": "use semaphore_rs_hasher::Hasher;\nuse tiny_keccak::{Hasher as _, Keccak};\npub struct Keccak256;\n\nimpl Hasher for Keccak256 {\n    type Hash = [u8; 32];\n\n    fn hash_node(left: &Self::Hash, right: &Self::Hash) -> Self::Hash {\n        let mut keccak = Keccak::v256();\n        let mut output = [0; 32];\n\n        keccak.update(left);\n        keccak.update(right);\n        keccak.finalize(&mut output);\n\n        output\n    }\n}\n"
  },
  {
    "path": "crates/keccak/src/lib.rs",
    "content": "pub mod keccak;\n\n#[cfg(feature = \"sha3\")]\npub mod sha3;\n"
  },
  {
    "path": "crates/keccak/src/sha3.rs",
    "content": "use semaphore_rs_hasher::Hasher;\nuse tiny_keccak::{Hasher as _, Sha3};\n\npub struct Sha3_256;\n\nimpl Hasher for Sha3_256 {\n    type Hash = [u8; 32];\n\n    fn hash_node(left: &Self::Hash, right: &Self::Hash) -> Self::Hash {\n        let mut sha3_hasher = Sha3::v256();\n\n        sha3_hasher.update(left);\n        sha3_hasher.update(right);\n\n        let mut out = [0u8; 32];\n        sha3_hasher.finalize(&mut out);\n\n        out\n    }\n}\n"
  },
  {
    "path": "crates/poseidon/Cargo.toml",
    "content": "[package]\nname = \"semaphore-rs-poseidon\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nauthors.workspace = true\ndescription.workspace = true\nkeywords.workspace = true\ncategories.workspace = true\n\n[dependencies]\nsemaphore-rs-hasher.workspace = true\nark-bn254.workspace = true\nark-ff.workspace = true\nonce_cell.workspace = true\nruint.workspace = true\n"
  },
  {
    "path": "crates/poseidon/src/constants.rs",
    "content": "use ruint::aliases::U256;\nuse ruint::uint;\n\nuint! {\n\npub const M1: [[U256; 2]; 2] = [\n    [\n        0x066f6f85d6f68a85ec10345351a23a3aaf07f38af8c952a7bceca70bd2af7ad5_U256,\n        0x2b9d4b4110c9ae997782e1509b1d0fdb20a7c02bbd8bea7305462b9f8125b1e8_U256,\n    ], [\n        0x0cc57cdbb08507d62bf67a4493cc262fb6c09d557013fff1f573f431221f8ff9_U256,\n        0x1274e649a32ed355a31a6ed69724e1adade857e86eb5c3a121bcd147943203c8_U256,\n    ],\n];\n\npub const C1: [[U256; 2]; 64] = [\n    [\n        0x09c46e9ec68e9bd4fe1faaba294cba38a71aa177534cdd1b6c7dc0dbd0abd7a7_U256,\n        0x0c0356530896eec42a97ed937f3135cfc5142b3ae405b8343c1d83ffa604cb81_U256,\n    ], [\n        0x1e28a1d935698ad1142e51182bb54cf4a00ea5aabd6268bd317ea977cc154a30_U256,\n        0x27af2d831a9d2748080965db30e298e40e5757c3e008db964cf9e2b12b91251f_U256,\n    ], [\n        0x1e6f11ce60fc8f513a6a3cfe16ae175a41291462f214cd0879aaf43545b74e03_U256,\n        0x2a67384d3bbd5e438541819cb681f0be04462ed14c3613d8f719206268d142d3_U256,\n    ], [\n        0x0b66fdf356093a611609f8e12fbfecf0b985e381f025188936408f5d5c9f45d0_U256,\n        0x012ee3ec1e78d470830c61093c2ade370b26c83cc5cebeeddaa6852dbdb09e21_U256,\n    ], [\n        0x0252ba5f6760bfbdfd88f67f8175e3fd6cd1c431b099b6bb2d108e7b445bb1b9_U256,\n        0x179474cceca5ff676c6bec3cef54296354391a8935ff71d6ef5aeaad7ca932f1_U256,\n    ], [\n        0x2c24261379a51bfa9228ff4a503fd4ed9c1f974a264969b37e1a2589bbed2b91_U256,\n        0x1cc1d7b62692e63eac2f288bd0695b43c2f63f5001fc0fc553e66c0551801b05_U256,\n    ], [\n        0x255059301aada98bb2ed55f852979e9600784dbf17fbacd05d9eff5fd9c91b56_U256,\n        0x28437be3ac1cb2e479e1f5c0eccd32b3aea24234970a8193b11c29ce7e59efd9_U256,\n    ], [\n        0x28216a442f2e1f711ca4fa6b53766eb118548da8fb4f78d4338762c37f5f2043_U256,\n        0x2c1f47cd17fa5adf1f39f4e7056dd03feee1efce03094581131f2377323482c9_U256,\n    ], [\n        0x07abad02b7a5ebc48632bcc9356ceb7dd9dafca276638a63646b8566a621afc9_U256,\n        0x0230264601ffdf29275b33ffaab51dfe9429f90880a69cd137da0c4d15f96c3c_U256,\n    ], [\n        0x1bc973054e51d905a0f168656497ca40a864414557ee289e717e5d66899aa0a9_U256,\n        0x2e1c22f964435008206c3157e86341edd249aff5c2d8421f2a6b22288f0a67fc_U256,\n    ], [\n        0x1224f38df67c5378121c1d5f461bbc509e8ea1598e46c9f7a70452bc2bba86b8_U256,\n        0x02e4e69d8ba59e519280b4bd9ed0068fd7bfe8cd9dfeda1969d2989186cde20e_U256,\n    ], [\n        0x1f1eccc34aaba0137f5df81fc04ff3ee4f19ee364e653f076d47e9735d98018e_U256,\n        0x1672ad3d709a353974266c3039a9a7311424448032cd1819eacb8a4d4284f582_U256,\n    ], [\n        0x283e3fdc2c6e420c56f44af5192b4ae9cda6961f284d24991d2ed602df8c8fc7_U256,\n        0x1c2a3d120c550ecfd0db0957170fa013683751f8fdff59d6614fbd69ff394bcc_U256,\n    ], [\n        0x216f84877aac6172f7897a7323456efe143a9a43773ea6f296cb6b8177653fbd_U256,\n        0x2c0d272becf2a75764ba7e8e3e28d12bceaa47ea61ca59a411a1f51552f94788_U256,\n    ], [\n        0x16e34299865c0e28484ee7a74c454e9f170a5480abe0508fcb4a6c3d89546f43_U256,\n        0x175ceba599e96f5b375a232a6fb9cc71772047765802290f48cd939755488fc5_U256,\n    ], [\n        0x0c7594440dc48c16fead9e1758b028066aa410bfbc354f54d8c5ffbb44a1ee32_U256,\n        0x1a3c29bc39f21bb5c466db7d7eb6fd8f760e20013ccf912c92479882d919fd8d_U256,\n    ], [\n        0x0ccfdd906f3426e5c0986ea049b253400855d349074f5a6695c8eeabcd22e68f_U256,\n        0x14f6bc81d9f186f62bdb475ce6c9411866a7a8a3fd065b3ce0e699b67dd9e796_U256,\n    ], [\n        0x0962b82789fb3d129702ca70b2f6c5aacc099810c9c495c888edeb7386b97052_U256,\n        0x1a880af7074d18b3bf20c79de25127bc13284ab01ef02575afef0c8f6a31a86d_U256,\n    ], [\n        0x10cba18419a6a332cd5e77f0211c154b20af2924fc20ff3f4c3012bb7ae9311b_U256,\n        0x057e62a9a8f89b3ebdc76ba63a9eaca8fa27b7319cae3406756a2849f302f10d_U256,\n    ], [\n        0x287c971de91dc0abd44adf5384b4988cb961303bbf65cff5afa0413b44280cee_U256,\n        0x21df3388af1687bbb3bca9da0cca908f1e562bc46d4aba4e6f7f7960e306891d_U256,\n    ], [\n        0x1be5c887d25bce703e25cc974d0934cd789df8f70b498fd83eff8b560e1682b3_U256,\n        0x268da36f76e568fb68117175cea2cd0dd2cb5d42fda5acea48d59c2706a0d5c1_U256,\n    ], [\n        0x0e17ab091f6eae50c609beaf5510ececc5d8bb74135ebd05bd06460cc26a5ed6_U256,\n        0x04d727e728ffa0a67aee535ab074a43091ef62d8cf83d270040f5caa1f62af40_U256,\n    ], [\n        0x0ddbd7bf9c29341581b549762bc022ed33702ac10f1bfd862b15417d7e39ca6e_U256,\n        0x2790eb3351621752768162e82989c6c234f5b0d1d3af9b588a29c49c8789654b_U256,\n    ], [\n        0x1e457c601a63b73e4471950193d8a570395f3d9ab8b2fd0984b764206142f9e9_U256,\n        0x21ae64301dca9625638d6ab2bbe7135ffa90ecd0c43ff91fc4c686fc46e091b0_U256,\n    ], [\n        0x0379f63c8ce3468d4da293166f494928854be9e3432e09555858534eed8d350b_U256,\n        0x002d56420359d0266a744a080809e054ca0e4921a46686ac8c9f58a324c35049_U256,\n    ], [\n        0x123158e5965b5d9b1d68b3cd32e10bbeda8d62459e21f4090fc2c5af963515a6_U256,\n        0x0be29fc40847a941661d14bbf6cbe0420fbb2b6f52836d4e60c80eb49cad9ec1_U256,\n    ], [\n        0x1ac96991dec2bb0557716142015a453c36db9d859cad5f9a233802f24fdf4c1a_U256,\n        0x1596443f763dbcc25f4964fc61d23b3e5e12c9fa97f18a9251ca3355bcb0627e_U256,\n    ], [\n        0x12e0bcd3654bdfa76b2861d4ec3aeae0f1857d9f17e715aed6d049eae3ba3212_U256,\n        0x0fc92b4f1bbea82b9ea73d4af9af2a50ceabac7f37154b1904e6c76c7cf964ba_U256,\n    ], [\n        0x1f9c0b1610446442d6f2e592a8013f40b14f7c7722236f4f9c7e965233872762_U256,\n        0x0ebd74244ae72675f8cde06157a782f4050d914da38b4c058d159f643dbbf4d3_U256,\n    ], [\n        0x2cb7f0ed39e16e9f69a9fafd4ab951c03b0671e97346ee397a839839dccfc6d1_U256,\n        0x1a9d6e2ecff022cc5605443ee41bab20ce761d0514ce526690c72bca7352d9bf_U256,\n    ], [\n        0x2a115439607f335a5ea83c3bc44a9331d0c13326a9a7ba3087da182d648ec72f_U256,\n        0x23f9b6529b5d040d15b8fa7aee3e3410e738b56305cd44f29535c115c5a4c060_U256,\n    ], [\n        0x05872c16db0f72a2249ac6ba484bb9c3a3ce97c16d58b68b260eb939f0e6e8a7_U256,\n        0x1300bdee08bb7824ca20fb80118075f40219b6151d55b5c52b624a7cdeddf6a7_U256,\n    ], [\n        0x19b9b63d2f108e17e63817863a8f6c288d7ad29916d98cb1072e4e7b7d52b376_U256,\n        0x015bee1357e3c015b5bda237668522f613d1c88726b5ec4224a20128481b4f7f_U256,\n    ], [\n        0x2953736e94bb6b9f1b9707a4f1615e4efe1e1ce4bab218cbea92c785b128ffd1_U256,\n        0x0b069353ba091618862f806180c0385f851b98d372b45f544ce7266ed6608dfc_U256,\n    ], [\n        0x304f74d461ccc13115e4e0bcfb93817e55aeb7eb9306b64e4f588ac97d81f429_U256,\n        0x15bbf146ce9bca09e8a33f5e77dfe4f5aad2a164a4617a4cb8ee5415cde913fc_U256,\n    ], [\n        0x0ab4dfe0c2742cde44901031487964ed9b8f4b850405c10ca9ff23859572c8c6_U256,\n        0x0e32db320a044e3197f45f7649a19675ef5eedfea546dea9251de39f9639779a_U256,\n    ], [\n        0x0a1756aa1f378ca4b27635a78b6888e66797733a82774896a3078efa516da016_U256,\n        0x044c4a33b10f693447fd17177f952ef895e61d328f85efa94254d6a2a25d93ef_U256,\n    ], [\n        0x2ed3611b725b8a70be655b537f66f700fe0879d79a496891d37b07b5466c4b8b_U256,\n        0x1f9ba4e8bab7ce42c8ecc3d722aa2e0eadfdeb9cfdd347b5d8339ea7120858aa_U256,\n    ], [\n        0x1b233043052e8c288f7ee907a84e518aa38e82ac4502066db74056f865c5d3da_U256,\n        0x2431e1cc164bb8d074031ab72bd55b4c902053bfc0f14db0ca2f97b020875954_U256,\n    ], [\n        0x082f934c91f5aac330cd6953a0a7db45a13e322097583319a791f273965801fd_U256,\n        0x2b9a0a223e7538b0a34be074315542a3c77245e2ae7cbe999ad6bb930c48997c_U256,\n    ], [\n        0x0e1cd91edd2cfa2cceb85483b887a9be8164163e75a8a00eb0b589cc70214e7d_U256,\n        0x2e1eac0f2bfdfd63c951f61477e3698999774f19854d00f588d324601cebe2f9_U256,\n    ], [\n        0x0cbfa95f37fb74060c76158e769d6d157345784d8efdb33c23d748115b500b83_U256,\n        0x08f05b3be923ed44d65ad49d8a61e9a676d991e3a77513d9980c232dfa4a4f84_U256,\n    ], [\n        0x22719e2a070bcd0852bf8e21984d0443e7284925dc0758a325a2dd510c047ef6_U256,\n        0x041f596a9ee1cb2bc060f7fcc3a1ab4c7bdbf036119982c0f41f62b2f26830c0_U256,\n    ], [\n        0x233fd35de1be520a87628eb06f6b1d4c021be1c2d0dc464a19fcdd0986b10f89_U256,\n        0x0524b46d1aa87a5e4325e0a423ebc810d31e078aa1b4707eefcb453c61c9c267_U256,\n    ], [\n        0x2c34f424c81e5716ce47fcac894b85824227bb954b0f3199cc4486237c515211_U256,\n        0x0b5f2a4b63387819207effc2b5541fb72dd2025b5457cc97f33010327de4915e_U256,\n    ], [\n        0x22207856082ccc54c5b72fe439d2cfd6c17435d2f57af6ceaefac41fe05c659f_U256,\n        0x24d57a8bf5da63fe4e24159b7f8950b5cdfb210194caf79f27854048ce2c8171_U256,\n    ], [\n        0x0afab181fdd5e0583b371d75bd693f98374ad7097bb01a8573919bb23b79396e_U256,\n        0x2dba9b108f208772998a52efac7cbd5676c0057194c16c0bf16290d62b1128ee_U256,\n    ], [\n        0x26349b66edb8b16f56f881c788f53f83cbb83de0bd592b255aff13e6bce420b3_U256,\n        0x25af7ce0e5e10357685e95f92339753ad81a56d28ecc193b235288a3e6f137db_U256,\n    ], [\n        0x25b4ce7bd2294390c094d6a55edd68b970eed7aae88b2bff1f7c0187fe35011f_U256,\n        0x22c543f10f6c89ec387e53f1908a88e5de9cef28ebdf30b18cb9d54c1e02b631_U256,\n    ], [\n        0x0236f93e7789c4724fc7908a9f191e1e425e906a919d7a34df668e74882f87a9_U256,\n        0x29350b401166ca010e7d27e37d05da99652bdae114eb01659cb497af980c4b52_U256,\n    ], [\n        0x0eed787d65820d3f6bd31bbab547f75a65edb75d844ebb89ee1260916652363f_U256,\n        0x07cc1170f13b46f2036a753f520b3291fdcd0e99bd94297d1906f656f4de6fad_U256,\n    ], [\n        0x22b939233b1d7205f49bcf613a3d30b1908786d7f9f5d10c2059435689e8acea_U256,\n        0x01451762a0aab81c8aad1dc8bc33e870740f083a5aa85438add650ace60ae5a6_U256,\n    ], [\n        0x23506bb5d8727d4461fabf1025d46d1fe32eaa61dec7da57e704fec0892fce89_U256,\n        0x2e484c44e838aea0bac06ae3f71bdd092a3709531e1efea97f8bd68907355522_U256,\n    ], [\n        0x0f4bc7d07ebafd64379e78c50bd2e42baf4a594545cedc2545418da26835b54c_U256,\n        0x1f4d3c8f6583e9e5fa76637862faaee851582388725df460e620996d50d8e74e_U256,\n    ], [\n        0x093514e0c70711f82660d07be0e4a988fae02abc7b681d9153eb9bcb48fe7389_U256,\n        0x1adab0c8e2b3bad346699a2b5f3bc03643ee83ece47228f24a58e0a347e153d8_U256,\n    ], [\n        0x1672b1726057d99dd14709ebb474641a378c1b94b8072bac1a22dbef9e80dad2_U256,\n        0x1dfd53d4576af2e38f44f53fdcab468cc5d8e2fae0acc4ee30d47b239b479c14_U256,\n    ], [\n        0x0c6888a10b75b0f3a70a36263a37e17fe6d77d640f6fc3debc7f207753205c60_U256,\n        0x1addb933a65be77092b34a7e77d12fe8611a61e00ee6848b85091ecca9d1e508_U256,\n    ], [\n        0x00d7540dcd268a845c10ae18d1de933cf638ff5425f0afff7935628e299d1791_U256,\n        0x140c0e42687e9ead01b2827a5664ca9c26fedde4acd99db1d316939d20b82c0e_U256,\n    ], [\n        0x2f0c3a115d4317d191ba89b8d13d1806c20a0f9b24f8c5edc091e2ae56565984_U256,\n        0x0c4ee778ff7c14553006ed220cf9c81008a0cff670b22b82d8c538a1dc958c61_U256,\n    ], [\n        0x1704f2766d46f82c3693f00440ccc3609424ed26c0acc66227c3d7485de74c69_U256,\n        0x2f2d19cc3ea5d78ea7a02c1b51d244abf0769c9f8544e40239b66fe9009c3cfa_U256,\n    ], [\n        0x1ae03853b75fcaba5053f112e2a8e8dcdd7ee6cb9cfed9c7d6c766a806fc6629_U256,\n        0x0971aabf795241df51d131d0fa61aa5f3556921b2d6f014e4e41a86ddaf056d5_U256,\n    ], [\n        0x1408c316e6014e1a91d4cf6b6e0de73eda624f8380df1c875f5c29f7bfe2f646_U256,\n        0x1667f3fe2edbe850248abe42b543093b6c89f1f773ef285341691f39822ef5bd_U256,\n    ], [\n        0x13bf7c5d0d2c4376a48b0a03557cdf915b81718409e5c133424c69576500fe37_U256,\n        0x07620a6dfb0b6cec3016adf3d3533c24024b95347856b79719bc0ba743a62c2c_U256,\n    ], [\n        0x1574c7ef0c43545f36a8ca08bdbdd8b075d2959e2f322b731675de3e1982b4d0_U256,\n        0x269e4b5b7a2eb21afd567970a717ceec5bd4184571c254fdc06e03a7ff8378f0_U256,\n    ],\n];\n\npub const M: [[U256; 3]; 3] = [\n    [\n        0x109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b_U256,\n        0x16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e0_U256,\n        0x2b90bba00fca0589f617e7dcbfe82e0df706ab640ceb247b791a93b74e36736d_U256,\n    ], [\n        0x2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771_U256,\n        0x2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe23_U256,\n        0x101071f0032379b697315876690f053d148d4e109f5fb065c8aacc55a0f89bfa_U256,\n    ], [\n        0x143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7_U256,\n        0x176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee2911_U256,\n        0x19a3fc0a56702bf417ba7fee3802593fa644470307043f7773279cd71d25d5e0_U256,\n    ],\n];\n\npub const C: [[U256; 3]; 65] = [\n    [\n        0x0ee9a592ba9a9518d05986d656f40c2114c4993c11bb29938d21d47304cd8e6e_U256,\n        0x00f1445235f2148c5986587169fc1bcd887b08d4d00868df5696fff40956e864_U256,\n        0x08dff3487e8ac99e1f29a058d0fa80b930c728730b7ab36ce879f3890ecf73f5_U256,\n    ], [\n        0x2f27be690fdaee46c3ce28f7532b13c856c35342c84bda6e20966310fadc01d0_U256,\n        0x2b2ae1acf68b7b8d2416bebf3d4f6234b763fe04b8043ee48b8327bebca16cf2_U256,\n        0x0319d062072bef7ecca5eac06f97d4d55952c175ab6b03eae64b44c7dbf11cfa_U256,\n    ], [\n        0x28813dcaebaeaa828a376df87af4a63bc8b7bf27ad49c6298ef7b387bf28526d_U256,\n        0x2727673b2ccbc903f181bf38e1c1d40d2033865200c352bc150928adddf9cb78_U256,\n        0x234ec45ca27727c2e74abd2b2a1494cd6efbd43e340587d6b8fb9e31e65cc632_U256,\n    ], [\n        0x15b52534031ae18f7f862cb2cf7cf760ab10a8150a337b1ccd99ff6e8797d428_U256,\n        0x0dc8fad6d9e4b35f5ed9a3d186b79ce38e0e8a8d1b58b132d701d4eecf68d1f6_U256,\n        0x1bcd95ffc211fbca600f705fad3fb567ea4eb378f62e1fec97805518a47e4d9c_U256,\n    ], [\n        0x10520b0ab721cadfe9eff81b016fc34dc76da36c2578937817cb978d069de559_U256,\n        0x1f6d48149b8e7f7d9b257d8ed5fbbaf42932498075fed0ace88a9eb81f5627f6_U256,\n        0x1d9655f652309014d29e00ef35a2089bfff8dc1c816f0dc9ca34bdb5460c8705_U256,\n    ], [\n        0x04df5a56ff95bcafb051f7b1cd43a99ba731ff67e47032058fe3d4185697cc7d_U256,\n        0x0672d995f8fff640151b3d290cedaf148690a10a8c8424a7f6ec282b6e4be828_U256,\n        0x099952b414884454b21200d7ffafdd5f0c9a9dcc06f2708e9fc1d8209b5c75b9_U256,\n    ], [\n        0x052cba2255dfd00c7c483143ba8d469448e43586a9b4cd9183fd0e843a6b9fa6_U256,\n        0x0b8badee690adb8eb0bd74712b7999af82de55707251ad7716077cb93c464ddc_U256,\n        0x119b1590f13307af5a1ee651020c07c749c15d60683a8050b963d0a8e4b2bdd1_U256,\n    ], [\n        0x03150b7cd6d5d17b2529d36be0f67b832c4acfc884ef4ee5ce15be0bfb4a8d09_U256,\n        0x2cc6182c5e14546e3cf1951f173912355374efb83d80898abe69cb317c9ea565_U256,\n        0x005032551e6378c450cfe129a404b3764218cadedac14e2b92d2cd73111bf0f9_U256,\n    ], [\n        0x233237e3289baa34bb147e972ebcb9516469c399fcc069fb88f9da2cc28276b5_U256,\n        0x05c8f4f4ebd4a6e3c980d31674bfbe6323037f21b34ae5a4e80c2d4c24d60280_U256,\n        0x0a7b1db13042d396ba05d818a319f25252bcf35ef3aeed91ee1f09b2590fc65b_U256,\n    ], [\n        0x2a73b71f9b210cf5b14296572c9d32dbf156e2b086ff47dc5df542365a404ec0_U256,\n        0x1ac9b0417abcc9a1935107e9ffc91dc3ec18f2c4dbe7f22976a760bb5c50c460_U256,\n        0x12c0339ae08374823fabb076707ef479269f3e4d6cb104349015ee046dc93fc0_U256,\n    ], [\n        0x0b7475b102a165ad7f5b18db4e1e704f52900aa3253baac68246682e56e9a28e_U256,\n        0x037c2849e191ca3edb1c5e49f6e8b8917c843e379366f2ea32ab3aa88d7f8448_U256,\n        0x05a6811f8556f014e92674661e217e9bd5206c5c93a07dc145fdb176a716346f_U256,\n    ], [\n        0x29a795e7d98028946e947b75d54e9f044076e87a7b2883b47b675ef5f38bd66e_U256,\n        0x20439a0c84b322eb45a3857afc18f5826e8c7382c8a1585c507be199981fd22f_U256,\n        0x2e0ba8d94d9ecf4a94ec2050c7371ff1bb50f27799a84b6d4a2a6f2a0982c887_U256,\n    ], [\n        0x143fd115ce08fb27ca38eb7cce822b4517822cd2109048d2e6d0ddcca17d71c8_U256,\n        0x0c64cbecb1c734b857968dbbdcf813cdf8611659323dbcbfc84323623be9caf1_U256,\n        0x028a305847c683f646fca925c163ff5ae74f348d62c2b670f1426cef9403da53_U256,\n    ], [\n        0x2e4ef510ff0b6fda5fa940ab4c4380f26a6bcb64d89427b824d6755b5db9e30c_U256,\n        0x0081c95bc43384e663d79270c956ce3b8925b4f6d033b078b96384f50579400e_U256,\n        0x2ed5f0c91cbd9749187e2fade687e05ee2491b349c039a0bba8a9f4023a0bb38_U256,\n    ], [\n        0x30509991f88da3504bbf374ed5aae2f03448a22c76234c8c990f01f33a735206_U256,\n        0x1c3f20fd55409a53221b7c4d49a356b9f0a1119fb2067b41a7529094424ec6ad_U256,\n        0x10b4e7f3ab5df003049514459b6e18eec46bb2213e8e131e170887b47ddcb96c_U256,\n    ], [\n        0x2a1982979c3ff7f43ddd543d891c2abddd80f804c077d775039aa3502e43adef_U256,\n        0x1c74ee64f15e1db6feddbead56d6d55dba431ebc396c9af95cad0f1315bd5c91_U256,\n        0x07533ec850ba7f98eab9303cace01b4b9e4f2e8b82708cfa9c2fe45a0ae146a0_U256,\n    ], [\n        0x21576b438e500449a151e4eeaf17b154285c68f42d42c1808a11abf3764c0750_U256,\n        0x2f17c0559b8fe79608ad5ca193d62f10bce8384c815f0906743d6930836d4a9e_U256,\n        0x2d477e3862d07708a79e8aae946170bc9775a4201318474ae665b0b1b7e2730e_U256,\n    ], [\n        0x162f5243967064c390e095577984f291afba2266c38f5abcd89be0f5b2747eab_U256,\n        0x2b4cb233ede9ba48264ecd2c8ae50d1ad7a8596a87f29f8a7777a70092393311_U256,\n        0x2c8fbcb2dd8573dc1dbaf8f4622854776db2eece6d85c4cf4254e7c35e03b07a_U256,\n    ], [\n        0x1d6f347725e4816af2ff453f0cd56b199e1b61e9f601e9ade5e88db870949da9_U256,\n        0x204b0c397f4ebe71ebc2d8b3df5b913df9e6ac02b68d31324cd49af5c4565529_U256,\n        0x0c4cb9dc3c4fd8174f1149b3c63c3c2f9ecb827cd7dc25534ff8fb75bc79c502_U256,\n    ], [\n        0x174ad61a1448c899a25416474f4930301e5c49475279e0639a616ddc45bc7b54_U256,\n        0x1a96177bcf4d8d89f759df4ec2f3cde2eaaa28c177cc0fa13a9816d49a38d2ef_U256,\n        0x066d04b24331d71cd0ef8054bc60c4ff05202c126a233c1a8242ace360b8a30a_U256,\n    ], [\n        0x2a4c4fc6ec0b0cf52195782871c6dd3b381cc65f72e02ad527037a62aa1bd804_U256,\n        0x13ab2d136ccf37d447e9f2e14a7cedc95e727f8446f6d9d7e55afc01219fd649_U256,\n        0x1121552fca26061619d24d843dc82769c1b04fcec26f55194c2e3e869acc6a9a_U256,\n    ], [\n        0x00ef653322b13d6c889bc81715c37d77a6cd267d595c4a8909a5546c7c97cff1_U256,\n        0x0e25483e45a665208b261d8ba74051e6400c776d652595d9845aca35d8a397d3_U256,\n        0x29f536dcb9dd7682245264659e15d88e395ac3d4dde92d8c46448db979eeba89_U256,\n    ], [\n        0x2a56ef9f2c53febadfda33575dbdbd885a124e2780bbea170e456baace0fa5be_U256,\n        0x1c8361c78eb5cf5decfb7a2d17b5c409f2ae2999a46762e8ee416240a8cb9af1_U256,\n        0x151aff5f38b20a0fc0473089aaf0206b83e8e68a764507bfd3d0ab4be74319c5_U256,\n    ], [\n        0x04c6187e41ed881dc1b239c88f7f9d43a9f52fc8c8b6cdd1e76e47615b51f100_U256,\n        0x13b37bd80f4d27fb10d84331f6fb6d534b81c61ed15776449e801b7ddc9c2967_U256,\n        0x01a5c536273c2d9df578bfbd32c17b7a2ce3664c2a52032c9321ceb1c4e8a8e4_U256,\n    ], [\n        0x2ab3561834ca73835ad05f5d7acb950b4a9a2c666b9726da832239065b7c3b02_U256,\n        0x1d4d8ec291e720db200fe6d686c0d613acaf6af4e95d3bf69f7ed516a597b646_U256,\n        0x041294d2cc484d228f5784fe7919fd2bb925351240a04b711514c9c80b65af1d_U256,\n    ], [\n        0x154ac98e01708c611c4fa715991f004898f57939d126e392042971dd90e81fc6_U256,\n        0x0b339d8acca7d4f83eedd84093aef51050b3684c88f8b0b04524563bc6ea4da4_U256,\n        0x0955e49e6610c94254a4f84cfbab344598f0e71eaff4a7dd81ed95b50839c82e_U256,\n    ], [\n        0x06746a6156eba54426b9e22206f15abca9a6f41e6f535c6f3525401ea0654626_U256,\n        0x0f18f5a0ecd1423c496f3820c549c27838e5790e2bd0a196ac917c7ff32077fb_U256,\n        0x04f6eeca1751f7308ac59eff5beb261e4bb563583ede7bc92a738223d6f76e13_U256,\n    ], [\n        0x2b56973364c4c4f5c1a3ec4da3cdce038811eb116fb3e45bc1768d26fc0b3758_U256,\n        0x123769dd49d5b054dcd76b89804b1bcb8e1392b385716a5d83feb65d437f29ef_U256,\n        0x2147b424fc48c80a88ee52b91169aacea989f6446471150994257b2fb01c63e9_U256,\n    ], [\n        0x0fdc1f58548b85701a6c5505ea332a29647e6f34ad4243c2ea54ad897cebe54d_U256,\n        0x12373a8251fea004df68abcf0f7786d4bceff28c5dbbe0c3944f685cc0a0b1f2_U256,\n        0x21e4f4ea5f35f85bad7ea52ff742c9e8a642756b6af44203dd8a1f35c1a90035_U256,\n    ], [\n        0x16243916d69d2ca3dfb4722224d4c462b57366492f45e90d8a81934f1bc3b147_U256,\n        0x1efbe46dd7a578b4f66f9adbc88b4378abc21566e1a0453ca13a4159cac04ac2_U256,\n        0x07ea5e8537cf5dd08886020e23a7f387d468d5525be66f853b672cc96a88969a_U256,\n    ], [\n        0x05a8c4f9968b8aa3b7b478a30f9a5b63650f19a75e7ce11ca9fe16c0b76c00bc_U256,\n        0x20f057712cc21654fbfe59bd345e8dac3f7818c701b9c7882d9d57b72a32e83f_U256,\n        0x04a12ededa9dfd689672f8c67fee31636dcd8e88d01d49019bd90b33eb33db69_U256,\n    ], [\n        0x27e88d8c15f37dcee44f1e5425a51decbd136ce5091a6767e49ec9544ccd101a_U256,\n        0x2feed17b84285ed9b8a5c8c5e95a41f66e096619a7703223176c41ee433de4d1_U256,\n        0x1ed7cc76edf45c7c404241420f729cf394e5942911312a0d6972b8bd53aff2b8_U256,\n    ], [\n        0x15742e99b9bfa323157ff8c586f5660eac6783476144cdcadf2874be45466b1a_U256,\n        0x1aac285387f65e82c895fc6887ddf40577107454c6ec0317284f033f27d0c785_U256,\n        0x25851c3c845d4790f9ddadbdb6057357832e2e7a49775f71ec75a96554d67c77_U256,\n    ], [\n        0x15a5821565cc2ec2ce78457db197edf353b7ebba2c5523370ddccc3d9f146a67_U256,\n        0x2411d57a4813b9980efa7e31a1db5966dcf64f36044277502f15485f28c71727_U256,\n        0x002e6f8d6520cd4713e335b8c0b6d2e647e9a98e12f4cd2558828b5ef6cb4c9b_U256,\n    ], [\n        0x2ff7bc8f4380cde997da00b616b0fcd1af8f0e91e2fe1ed7398834609e0315d2_U256,\n        0x00b9831b948525595ee02724471bcd182e9521f6b7bb68f1e93be4febb0d3cbe_U256,\n        0x0a2f53768b8ebf6a86913b0e57c04e011ca408648a4743a87d77adbf0c9c3512_U256,\n    ], [\n        0x00248156142fd0373a479f91ff239e960f599ff7e94be69b7f2a290305e1198d_U256,\n        0x171d5620b87bfb1328cf8c02ab3f0c9a397196aa6a542c2350eb512a2b2bcda9_U256,\n        0x170a4f55536f7dc970087c7c10d6fad760c952172dd54dd99d1045e4ec34a808_U256,\n    ], [\n        0x29aba33f799fe66c2ef3134aea04336ecc37e38c1cd211ba482eca17e2dbfae1_U256,\n        0x1e9bc179a4fdd758fdd1bb1945088d47e70d114a03f6a0e8b5ba650369e64973_U256,\n        0x1dd269799b660fad58f7f4892dfb0b5afeaad869a9c4b44f9c9e1c43bdaf8f09_U256,\n    ], [\n        0x22cdbc8b70117ad1401181d02e15459e7ccd426fe869c7c95d1dd2cb0f24af38_U256,\n        0x0ef042e454771c533a9f57a55c503fcefd3150f52ed94a7cd5ba93b9c7dacefd_U256,\n        0x11609e06ad6c8fe2f287f3036037e8851318e8b08a0359a03b304ffca62e8284_U256,\n    ], [\n        0x1166d9e554616dba9e753eea427c17b7fecd58c076dfe42708b08f5b783aa9af_U256,\n        0x2de52989431a859593413026354413db177fbf4cd2ac0b56f855a888357ee466_U256,\n        0x3006eb4ffc7a85819a6da492f3a8ac1df51aee5b17b8e89d74bf01cf5f71e9ad_U256,\n    ], [\n        0x2af41fbb61ba8a80fdcf6fff9e3f6f422993fe8f0a4639f962344c8225145086_U256,\n        0x119e684de476155fe5a6b41a8ebc85db8718ab27889e85e781b214bace4827c3_U256,\n        0x1835b786e2e8925e188bea59ae363537b51248c23828f047cff784b97b3fd800_U256,\n    ], [\n        0x28201a34c594dfa34d794996c6433a20d152bac2a7905c926c40e285ab32eeb6_U256,\n        0x083efd7a27d1751094e80fefaf78b000864c82eb571187724a761f88c22cc4e7_U256,\n        0x0b6f88a3577199526158e61ceea27be811c16df7774dd8519e079564f61fd13b_U256,\n    ], [\n        0x0ec868e6d15e51d9644f66e1d6471a94589511ca00d29e1014390e6ee4254f5b_U256,\n        0x2af33e3f866771271ac0c9b3ed2e1142ecd3e74b939cd40d00d937ab84c98591_U256,\n        0x0b520211f904b5e7d09b5d961c6ace7734568c547dd6858b364ce5e47951f178_U256,\n    ], [\n        0x0b2d722d0919a1aad8db58f10062a92ea0c56ac4270e822cca228620188a1d40_U256,\n        0x1f790d4d7f8cf094d980ceb37c2453e957b54a9991ca38bbe0061d1ed6e562d4_U256,\n        0x0171eb95dfbf7d1eaea97cd385f780150885c16235a2a6a8da92ceb01e504233_U256,\n    ], [\n        0x0c2d0e3b5fd57549329bf6885da66b9b790b40defd2c8650762305381b168873_U256,\n        0x1162fb28689c27154e5a8228b4e72b377cbcafa589e283c35d3803054407a18d_U256,\n        0x2f1459b65dee441b64ad386a91e8310f282c5a92a89e19921623ef8249711bc0_U256,\n    ], [\n        0x1e6ff3216b688c3d996d74367d5cd4c1bc489d46754eb712c243f70d1b53cfbb_U256,\n        0x01ca8be73832b8d0681487d27d157802d741a6f36cdc2a0576881f9326478875_U256,\n        0x1f7735706ffe9fc586f976d5bdf223dc680286080b10cea00b9b5de315f9650e_U256,\n    ], [\n        0x2522b60f4ea3307640a0c2dce041fba921ac10a3d5f096ef4745ca838285f019_U256,\n        0x23f0bee001b1029d5255075ddc957f833418cad4f52b6c3f8ce16c235572575b_U256,\n        0x2bc1ae8b8ddbb81fcaac2d44555ed5685d142633e9df905f66d9401093082d59_U256,\n    ], [\n        0x0f9406b8296564a37304507b8dba3ed162371273a07b1fc98011fcd6ad72205f_U256,\n        0x2360a8eb0cc7defa67b72998de90714e17e75b174a52ee4acb126c8cd995f0a8_U256,\n        0x15871a5cddead976804c803cbaef255eb4815a5e96df8b006dcbbc2767f88948_U256,\n    ], [\n        0x193a56766998ee9e0a8652dd2f3b1da0362f4f54f72379544f957ccdeefb420f_U256,\n        0x2a394a43934f86982f9be56ff4fab1703b2e63c8ad334834e4309805e777ae0f_U256,\n        0x1859954cfeb8695f3e8b635dcb345192892cd11223443ba7b4166e8876c0d142_U256,\n    ], [\n        0x04e1181763050e58013444dbcb99f1902b11bc25d90bbdca408d3819f4fed32b_U256,\n        0x0fdb253dee83869d40c335ea64de8c5bb10eb82db08b5e8b1f5e5552bfd05f23_U256,\n        0x058cbe8a9a5027bdaa4efb623adead6275f08686f1c08984a9d7c5bae9b4f1c0_U256,\n    ], [\n        0x1382edce9971e186497eadb1aeb1f52b23b4b83bef023ab0d15228b4cceca59a_U256,\n        0x03464990f045c6ee0819ca51fd11b0be7f61b8eb99f14b77e1e6634601d9e8b5_U256,\n        0x23f7bfc8720dc296fff33b41f98ff83c6fcab4605db2eb5aaa5bc137aeb70a58_U256,\n    ], [\n        0x0a59a158e3eec2117e6e94e7f0e9decf18c3ffd5e1531a9219636158bbaf62f2_U256,\n        0x06ec54c80381c052b58bf23b312ffd3ce2c4eba065420af8f4c23ed0075fd07b_U256,\n        0x118872dc832e0eb5476b56648e867ec8b09340f7a7bcb1b4962f0ff9ed1f9d01_U256,\n    ], [\n        0x13d69fa127d834165ad5c7cba7ad59ed52e0b0f0e42d7fea95e1906b520921b1_U256,\n        0x169a177f63ea681270b1c6877a73d21bde143942fb71dc55fd8a49f19f10c77b_U256,\n        0x04ef51591c6ead97ef42f287adce40d93abeb032b922f66ffb7e9a5a7450544d_U256,\n    ], [\n        0x256e175a1dc079390ecd7ca703fb2e3b19ec61805d4f03ced5f45ee6dd0f69ec_U256,\n        0x30102d28636abd5fe5f2af412ff6004f75cc360d3205dd2da002813d3e2ceeb2_U256,\n        0x10998e42dfcd3bbf1c0714bc73eb1bf40443a3fa99bef4a31fd31be182fcc792_U256,\n    ], [\n        0x193edd8e9fcf3d7625fa7d24b598a1d89f3362eaf4d582efecad76f879e36860_U256,\n        0x18168afd34f2d915d0368ce80b7b3347d1c7a561ce611425f2664d7aa51f0b5d_U256,\n        0x29383c01ebd3b6ab0c017656ebe658b6a328ec77bc33626e29e2e95b33ea6111_U256,\n    ], [\n        0x10646d2f2603de39a1f4ae5e7771a64a702db6e86fb76ab600bf573f9010c711_U256,\n        0x0beb5e07d1b27145f575f1395a55bf132f90c25b40da7b3864d0242dcb1117fb_U256,\n        0x16d685252078c133dc0d3ecad62b5c8830f95bb2e54b59abdffbf018d96fa336_U256,\n    ], [\n        0x0a6abd1d833938f33c74154e0404b4b40a555bbbec21ddfafd672dd62047f01a_U256,\n        0x1a679f5d36eb7b5c8ea12a4c2dedc8feb12dffeec450317270a6f19b34cf1860_U256,\n        0x0980fb233bd456c23974d50e0ebfde4726a423eada4e8f6ffbc7592e3f1b93d6_U256,\n    ], [\n        0x161b42232e61b84cbf1810af93a38fc0cece3d5628c9282003ebacb5c312c72b_U256,\n        0x0ada10a90c7f0520950f7d47a60d5e6a493f09787f1564e5d09203db47de1a0b_U256,\n        0x1a730d372310ba82320345a29ac4238ed3f07a8a2b4e121bb50ddb9af407f451_U256,\n    ], [\n        0x2c8120f268ef054f817064c369dda7ea908377feaba5c4dffbda10ef58e8c556_U256,\n        0x1c7c8824f758753fa57c00789c684217b930e95313bcb73e6e7b8649a4968f70_U256,\n        0x2cd9ed31f5f8691c8e39e4077a74faa0f400ad8b491eb3f7b47b27fa3fd1cf77_U256,\n    ], [\n        0x23ff4f9d46813457cf60d92f57618399a5e022ac321ca550854ae23918a22eea_U256,\n        0x09945a5d147a4f66ceece6405dddd9d0af5a2c5103529407dff1ea58f180426d_U256,\n        0x188d9c528025d4c2b67660c6b771b90f7c7da6eaa29d3f268a6dd223ec6fc630_U256,\n    ], [\n        0x3050e37996596b7f81f68311431d8734dba7d926d3633595e0c0d8ddf4f0f47f_U256,\n        0x15af1169396830a91600ca8102c35c426ceae5461e3f95d89d829518d30afd78_U256,\n        0x1da6d09885432ea9a06d9f37f873d985dae933e351466b2904284da3320d8acc_U256,\n    ], [\n        0x2796ea90d269af29f5f8acf33921124e4e4fad3dbe658945e546ee411ddaa9cb_U256,\n        0x202d7dd1da0f6b4b0325c8b3307742f01e15612ec8e9304a7cb0319e01d32d60_U256,\n        0x096d6790d05bb759156a952ba263d672a2d7f9c788f4c831a29dace4c0f8be5f_U256,\n    ], [\n        0x054efa1f65b0fce283808965275d877b438da23ce5b13e1963798cb1447d25a4_U256,\n        0x1b162f83d917e93edb3308c29802deb9d8aa690113b2e14864ccf6e18e4165f1_U256,\n        0x21e5241e12564dd6fd9f1cdd2a0de39eedfefc1466cc568ec5ceb745a0506edc_U256,\n    ], [\n        0x1cfb5662e8cf5ac9226a80ee17b36abecb73ab5f87e161927b4349e10e4bdf08_U256,\n        0x0f21177e302a771bbae6d8d1ecb373b62c99af346220ac0129c53f666eb24100_U256,\n        0x1671522374606992affb0dd7f71b12bec4236aede6290546bcef7e1f515c2320_U256,\n    ], [\n        0x0fa3ec5b9488259c2eb4cf24501bfad9be2ec9e42c5cc8ccd419d2a692cad870_U256,\n        0x193c0e04e0bd298357cb266c1506080ed36edce85c648cc085e8c57b1ab54bba_U256,\n        0x102adf8ef74735a27e9128306dcbc3c99f6f7291cd406578ce14ea2adaba68f8_U256,\n    ], [\n        0x0fe0af7858e49859e2a54d6f1ad945b1316aa24bfbdd23ae40a6d0cb70c3eab1_U256,\n        0x216f6717bbc7dedb08536a2220843f4e2da5f1daa9ebdefde8a5ea7344798d22_U256,\n        0x1da55cc900f0d21f4a3e694391918a1b3c23b2ac773c6b3ef88e2e4228325161_U256,\n    ],\n];\n\n}\n"
  },
  {
    "path": "crates/poseidon/src/lib.rs",
    "content": "use ruint::aliases::U256;\nuse semaphore_rs_hasher::Hasher;\n\npub mod constants;\npub mod poseidon;\n\npub struct Poseidon;\n\nimpl Hasher for Poseidon {\n    type Hash = U256;\n\n    fn hash_node(left: &Self::Hash, right: &Self::Hash) -> Self::Hash {\n        poseidon::hash2(*left, *right)\n    }\n}\n"
  },
  {
    "path": "crates/poseidon/src/poseidon.rs",
    "content": "use ark_bn254::Fr;\nuse ark_ff::{Field, Zero};\nuse once_cell::sync::Lazy;\nuse ruint::aliases::U256;\n\nuse crate::constants;\n\nstatic M1: Lazy<[[Fr; 2]; 2]> = Lazy::new(|| {\n    constants::M1\n        .iter()\n        .map(|row| {\n            row.iter()\n                .map(Fr::try_from)\n                .collect::<Result<Vec<Fr>, _>>()\n                .unwrap()\n                .try_into()\n                .unwrap()\n        })\n        .collect::<Vec<[Fr; 2]>>()\n        .try_into()\n        .unwrap()\n});\n\nstatic C1: Lazy<[[Fr; 2]; 64]> = Lazy::new(|| {\n    constants::C1\n        .iter()\n        .map(|row| {\n            row.iter()\n                .map(Fr::try_from)\n                .collect::<Result<Vec<Fr>, _>>()\n                .unwrap()\n                .try_into()\n                .unwrap()\n        })\n        .collect::<Vec<[Fr; 2]>>()\n        .try_into()\n        .unwrap()\n});\n\nstatic M: Lazy<[[Fr; 3]; 3]> = Lazy::new(|| {\n    constants::M\n        .iter()\n        .map(|row| {\n            row.iter()\n                .map(Fr::try_from)\n                .collect::<Result<Vec<Fr>, _>>()\n                .unwrap()\n                .try_into()\n                .unwrap()\n        })\n        .collect::<Vec<[Fr; 3]>>()\n        .try_into()\n        .unwrap()\n});\n\nstatic C: Lazy<[[Fr; 3]; 65]> = Lazy::new(|| {\n    constants::C\n        .iter()\n        .map(|row| {\n            row.iter()\n                .map(Fr::try_from)\n                .collect::<Result<Vec<Fr>, _>>()\n                .unwrap()\n                .try_into()\n                .unwrap()\n        })\n        .collect::<Vec<[Fr; 3]>>()\n        .try_into()\n        .unwrap()\n});\n\n/// Compute the one-value Poseidon hash function.\n///\n/// # Panics\n///\n/// Panics if `input` is not a valid field element.\n#[must_use]\npub fn hash1(value: U256) -> U256 {\n    let value = value.try_into().unwrap();\n    let mut state = [Fr::zero(), value];\n\n    for i in 0..64 {\n        // Add round constants\n        state[0] += C1[i][0];\n        state[1] += C1[i][1];\n\n        // SubWords, S-Box: Exponentiate\n        state[0] = state[0].pow([5]);\n        if !(4..60).contains(&i) {\n            state[1] = state[1].pow([5]);\n        }\n\n        // MixLayer: Multiply by maximum distance separable matrix\n        state = [\n            M1[0][0] * state[0] + M1[0][1] * state[1],\n            M1[1][0] * state[0] + M1[1][1] * state[1],\n        ];\n    }\n    state[0].into()\n}\n\n/// Compute the two-value Poseidon hash function.\n///\n/// # Panics\n///\n/// Panics if `left`, `right` are not a valid field element.\n#[must_use]\npub fn hash2(left: U256, right: U256) -> U256 {\n    let left = left.try_into().unwrap();\n    let right = right.try_into().unwrap();\n    let mut state = [Fr::zero(), left, right];\n\n    for i in 0..65 {\n        // Add round constants\n        state[0] += C[i][0];\n        state[1] += C[i][1];\n        state[2] += C[i][2];\n\n        // SubWords, S-Box: Exponentiate\n        state[0] = state[0].pow([5]);\n        if !(4..61).contains(&i) {\n            state[1] = state[1].pow([5]);\n            state[2] = state[2].pow([5]);\n        }\n\n        // MixLayer: Multiply by maximum distance separable matrix\n        state = [\n            M[0][0] * state[0] + M[0][1] * state[1] + M[0][2] * state[2],\n            M[1][0] * state[0] + M[1][1] * state[1] + M[1][2] * state[2],\n            M[2][0] * state[0] + M[2][1] * state[1] + M[2][2] * state[2],\n        ];\n    }\n    state[0].into()\n}\n\n#[cfg(test)]\nmod tests {\n    use ruint::uint;\n\n    use super::*;\n\n    #[test]\n    fn test_hash1() {\n        uint! {\n            assert_eq!(hash1(0_U256), 0x2a09a9fd93c590c26b91effbb2499f07e8f7aa12e2b4940a3aed2411cb65e11c_U256);\n\n        }\n    }\n\n    #[test]\n    fn test_hash2() {\n        uint! {\n            assert_eq!(hash2(0_U256, 0_U256), 0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864_U256);\n            assert_eq!(hash2(31213_U256, 132_U256), 0x303f59cd0831b5633bcda50514521b33776b5d4280eb5868ba1dbbe2e4d76ab5_U256);\n        }\n    }\n}\n"
  },
  {
    "path": "crates/proof/Cargo.toml",
    "content": "[package]\nname = \"semaphore-rs-proof\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nauthors.workspace = true\ndescription.workspace = true\nkeywords.workspace = true\ncategories.workspace = true\n\n[dependencies]\nsemaphore-rs-utils.workspace = true\nsemaphore-rs-ark-circom = { workspace = true, optional = true }\n\nruint.workspace = true\nserde.workspace = true\nserde_json.workspace = true\nark-ec = { workspace = true, optional = true }\nark-groth16 = { workspace = true, optional = true }\nark-bn254 = { workspace = true, optional = true }\nalloy-core = { workspace = true }\nlazy_static.workspace = true\ngetrandom.workspace = true\nhex.workspace = true\n\n[features]\ndefault = [\"ark\"]\nark = [\"dep:semaphore-rs-ark-circom\", \"dep:ark-ec\", \"dep:ark-groth16\", \"dep:ark-bn254\"]\n"
  },
  {
    "path": "crates/proof/src/ark.rs",
    "content": "use super::Proof;\nuse ark_bn254::Config;\nuse ark_ec::bn::Bn;\nuse ark_groth16::Proof as ArkProof;\nuse semaphore_rs_ark_circom::ethereum::AffineError;\n\nimpl From<ArkProof<Bn<Config>>> for Proof {\n    fn from(proof: ArkProof<Bn<Config>>) -> Self {\n        let proof = semaphore_rs_ark_circom::ethereum::Proof::from(proof);\n        let (a, b, c) = proof.as_tuple();\n        Self(a, b, c)\n    }\n}\n\nimpl TryFrom<Proof> for ArkProof<Bn<Config>> {\n    type Error = AffineError;\n\n    fn try_from(proof: Proof) -> Result<Self, AffineError> {\n        let eth_proof = semaphore_rs_ark_circom::ethereum::Proof {\n            a: semaphore_rs_ark_circom::ethereum::G1 {\n                x: proof.0 .0,\n                y: proof.0 .1,\n            },\n            #[rustfmt::skip] // Rustfmt inserts some confusing spaces\n            b: semaphore_rs_ark_circom::ethereum::G2 {\n                // The order of coefficients is flipped.\n                x: [proof.1.0[1], proof.1.0[0]],\n                y: [proof.1.1[1], proof.1.1[0]],\n            },\n            c: semaphore_rs_ark_circom::ethereum::G1 {\n                x: proof.2 .0,\n                y: proof.2 .1,\n            },\n        };\n        // This conversion can fail if points are not on the curve.\n        eth_proof.try_into()\n    }\n}\n"
  },
  {
    "path": "crates/proof/src/compression.rs",
    "content": "//! Groth16 proof compression\n//!\n//! Ported from https://github.com/worldcoin/world-id-state-bridge/blob/main/src/SemaphoreVerifier.sol\n//!\n//! Based upon work in https://xn--2-umb.com/23/bn254-compression/\n\nuse ruint::aliases::U256;\nuse ruint::uint;\nuse serde::{Deserialize, Serialize};\n\nuse super::{Proof, G1, G2};\nuse lazy_static::lazy_static;\n\n/// Base field Fp order P\npub const P: U256 =\n    uint! { 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47_U256 };\n\n// A helper for a frequently used constants\npub const ONE: U256 = uint! { 1_U256 };\npub const TWO: U256 = uint! { 2_U256 };\npub const THREE: U256 = uint! { 3_U256 };\npub const FOUR: U256 = uint! { 4_U256 };\n\nlazy_static! {\n    /// Exponent for the square root in Fp\n    pub static ref EXP_SQRT_FP: U256 = (P + ONE) / FOUR;\n\n    /// Exponent for the inverse in Fp\n    pub static ref EXP_INVERSE_FP: U256 = P - TWO;\n}\n\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]\npub struct CompressedProof(pub U256, pub (U256, U256), pub U256);\n\nimpl CompressedProof {\n    pub const fn from_flat(flat: [U256; 4]) -> Self {\n        let [a, b0, b1, c] = flat;\n\n        Self(a, (b0, b1), c)\n    }\n\n    pub const fn flatten(self) -> [U256; 4] {\n        let Self(a, (b0, b1), c) = self;\n        [a, b0, b1, c]\n    }\n}\n\npub fn compress_proof(proof: Proof) -> Option<CompressedProof> {\n    let Proof(g1a, g2, g1b) = proof;\n\n    // NOTE: Order of real and imaginary parts in the proof data is flipped\n    let ([x0, x1], [y0, y1]) = g2;\n    let g2 = ([x1, x0], [y1, y0]);\n\n    let a = compress_g1(g1a)?;\n    // NOTE: G2 compressed repr is flipped\n    let (c0, c1) = compress_g2(g2)?;\n    let c = (c1, c0);\n\n    let b = compress_g1(g1b)?;\n\n    Some(CompressedProof(a, c, b))\n}\n\npub fn decompress_proof(compressed: CompressedProof) -> Option<Proof> {\n    let CompressedProof(a, c, b) = compressed;\n\n    let g1a = decompress_g1(a)?;\n\n    // NOTE: G2 compressed repr is flipped\n    let (c1, c0) = c;\n    let c = (c0, c1);\n    let g2 = decompress_g2(c)?;\n\n    let g1b = decompress_g1(b)?;\n\n    // Unswap\n    let ([x1, x0], [y1, y0]) = g2;\n    let g2 = ([x0, x1], [y0, y1]);\n\n    Some(Proof(g1a, g2, g1b))\n}\n\npub fn compress_g1((x, y): G1) -> Option<U256> {\n    if x >= P || y >= P {\n        return None; // Point not in field\n    }\n    if x == U256::ZERO && y == U256::ZERO {\n        return Some(U256::ZERO); // Point at infinity\n    }\n    let y_pos = sqrt_fp(x.pow_mod(THREE, P).add_mod(THREE, P))?;\n    if y == y_pos {\n        Some(x << 1)\n    } else if y == neg_fp(y_pos) {\n        Some(x << 1 | ONE)\n    } else {\n        None\n    }\n}\n\npub fn decompress_g1(c: U256) -> Option<G1> {\n    if c == U256::ZERO {\n        return Some((U256::ZERO, U256::ZERO)); // Point at infinity\n    }\n\n    let negate = c & ONE == ONE;\n    let x: U256 = c >> 1;\n    if x >= P {\n        return None;\n    }\n\n    let y2 = x.pow_mod(THREE, P).add_mod(THREE, P);\n    let mut y = sqrt_fp(y2)?;\n\n    if negate {\n        y = neg_fp(y);\n    }\n    Some((x, y))\n}\n\n/// Compresses the\npub fn compress_g2(([x0, x1], [y0, y1]): G2) -> Option<(U256, U256)> {\n    if x0 >= P || x1 >= P || y0 >= P || y1 >= P {\n        return None; // Point not in field\n    }\n    if (x0 | x1 | y0 | y1) == U256::ZERO {\n        return Some((U256::ZERO, U256::ZERO)); // Point at infinity\n    }\n\n    // Compute y^2\n    let n3ab = x0.mul_mod(x1, P).mul_mod(P - THREE, P);\n    let a_3 = x0.pow_mod(THREE, P);\n    let b_3 = x1.pow_mod(THREE, P);\n\n    let y0_pos = U256::from(27)\n        .mul_mod(U256::from(82).inv_mod(P).unwrap(), P)\n        .add_mod(a_3.add_mod(n3ab.mul_mod(x1, P), P), P);\n\n    let y1_pos = neg_fp(\n        THREE\n            .mul_mod(U256::from(82).inv_mod(P).unwrap(), P)\n            .add_mod(b_3.add_mod(n3ab.mul_mod(x0, P), P), P),\n    );\n\n    // Determine hint bit\n    let d = sqrt_fp(\n        y0_pos\n            .mul_mod(y0_pos, P)\n            .add_mod(y1_pos.mul_mod(y1_pos, P), P),\n    )?;\n    let hint = !is_square_fp(y0_pos.add_mod(d, P).mul_mod(TWO.inv_mod(P).unwrap(), P));\n\n    // Recover y\n    let (new_y0_pos, new_y1_pos) = sqrt_fp2(y0_pos, y1_pos, hint)?;\n\n    let hint = if hint { TWO } else { U256::ZERO };\n    if y0 == new_y0_pos && y1 == new_y1_pos {\n        Some(((x0 << 2) | hint, x1))\n    } else if y0 == neg_fp(new_y0_pos) && y1 == neg_fp(new_y1_pos) {\n        Some(((x0 << 2) | hint | ONE, x1))\n    } else {\n        None\n    }\n}\n\npub fn decompress_g2((c0, c1): (U256, U256)) -> Option<G2> {\n    if c0 == U256::ZERO && c1 == U256::ZERO {\n        return Some(([U256::ZERO, U256::ZERO], [U256::ZERO, U256::ZERO])); // Point at infinity\n    }\n\n    let negate = c0 & ONE == ONE;\n    let hint = c0 & TWO == TWO;\n\n    let x0: U256 = c0 >> 2;\n    let x1 = c1;\n\n    if x0 >= P || x1 >= P {\n        return None;\n    }\n\n    let n3ab = x0.mul_mod(x1, P).mul_mod(P - THREE, P);\n    let a_3 = x0.pow_mod(THREE, P);\n    let b_3 = x1.pow_mod(THREE, P);\n\n    let y0 = U256::from(27)\n        .mul_mod(U256::from(82).inv_mod(P)?, P)\n        .add_mod(a_3.add_mod(n3ab.mul_mod(x1, P), P), P);\n    let y1 = neg_fp(\n        THREE\n            .mul_mod(U256::from(82).inv_mod(P)?, P)\n            .add_mod(b_3.add_mod(n3ab.mul_mod(x0, P), P), P),\n    );\n\n    let (mut y0, mut y1) = sqrt_fp2(y0, y1, hint)?;\n    if negate {\n        y0 = neg_fp(y0);\n        y1 = neg_fp(y1);\n    }\n\n    Some(([x0, x1], [y0, y1]))\n}\n\nfn sqrt_fp(a: U256) -> Option<U256> {\n    let x = a.pow_mod(*EXP_SQRT_FP, P);\n    if x.mul_mod(x, P) == a {\n        Some(x)\n    } else {\n        None\n    }\n}\n\nfn sqrt_fp2(a0: U256, a1: U256, hint: bool) -> Option<(U256, U256)> {\n    let mut d = sqrt_fp(a0.pow_mod(TWO, P).add_mod(a1.pow_mod(TWO, P), P))?;\n\n    if hint {\n        d = neg_fp(d);\n    }\n\n    let frac_1_2 = ONE.mul_mod(TWO.inv_mod(P)?, P);\n    let x0 = sqrt_fp(a0.add_mod(d, P).mul_mod(frac_1_2, P))?;\n    let x1 = a1.mul_mod(invert_fp(x0.mul_mod(TWO, P))?, P);\n\n    if a0 != x0.pow_mod(TWO, P).add_mod(neg_fp(x1.pow_mod(TWO, P)), P)\n        || a1 != TWO.mul_mod(x0.mul_mod(x1, P), P)\n    {\n        return None;\n    }\n\n    Some((x0, x1))\n}\n\nfn is_square_fp(a: U256) -> bool {\n    let x = a.pow_mod(*EXP_SQRT_FP, P);\n    x.mul_mod(x, P) == a\n}\n\n/// Inversion in Fp\n///\n/// Returns a number x such that a * x = 1 in Fp\n/// Returns None if the inverse does not exist\nfn invert_fp(a: U256) -> Option<U256> {\n    let x = a.pow_mod(*EXP_INVERSE_FP, P);\n\n    if a.mul_mod(x, P) != ONE {\n        return None;\n    }\n\n    Some(x)\n}\n\nfn neg_fp(a: U256) -> U256 {\n    P.wrapping_sub(a % P) % P\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn inversion() {\n        let v = uint! { 4598362786468342265918458423096940256393720972438048893356218087518821823203_U256 };\n        let inverted = invert_fp(v).unwrap();\n        let exp_inverted = uint! { 4182222526301715069940346543278816173622053692765626450942898397518664864041_U256 };\n\n        assert_eq!(exp_inverted, inverted);\n    }\n\n    #[test]\n    fn square_root_fp() {\n        let v = uint! { 14471043194638943579446425262583282548539507047061604313953794288955195726209_U256 };\n        let exp_sqrt = uint! { 13741342543520938546471415319044405232187715299443307089577869276344592329757_U256 };\n\n        let sqrt = sqrt_fp(v).unwrap();\n        assert_eq!(exp_sqrt, sqrt);\n    }\n\n    #[test]\n    fn square_root_fp_2() {\n        let (a, b) = sqrt_fp2(uint!{17473058728477435457299093362519578563618705081729024467362715416915525458528_U256}, uint!{17683468329848516541101685027677188007795188556813329975791177956431310972350_U256}, false).unwrap();\n\n        let exp_a = uint! {10193706077588260514783319931179623845729747565730309463634080055351233087269_U256};\n        let exp_b = uint! {2911435556167431587172450261242327574185987927358833959334220021362478804490_U256};\n\n        assert_eq!(exp_a, a);\n        assert_eq!(exp_b, b);\n    }\n\n    // The literal values below are taken from the proof in the following tx: https://etherscan.io/tx/0x53309842294be8c2b9fd694c4e86a5ab031c0d58750978fb3d6f60de16eaa897\n    // Raw proof data is:\n    // 20565048055856194013099208963146657799256893353279242520150547463020687826541\n    // 16286013012747852737396822706018267259565592188907848191354824303311847109059\n    // 4348608846293503080802796983494208797681981448804902149317789801083784587558\n    // 6172488348732750834133346196464201580503416389945891763609808290085997580078\n    // 3229429189805934086496276224876305383924675874777054942516982958483565949767\n    // 944252930093106871283598150477854448876343937304805759422971930315581301659\n    // 18318130744212307125672524358864792312717149086464333958791498157127232409959\n    // 8256141885907329266852096557308020923997215847794048916749940281741155521604\n    //\n    // Note that for the G2 compression test the order of real and imaginary is flipped\n    //\n    // The expected compressed data is generated with the SemaphoreVerifier implementation\n    // in world-id-state-bridge using chisel.\n    //\n    // Unfortunately the `compress_g1` and `compress_g2` methods are set to `internal` so\n    // the approach is a little hacky, but steps to regenerate these values are as follows:\n    // 1. Change `internal` to `public` in `SemaphoreVerifier.sol`\n    // 2. Start `chisel`\n    // 3. Execute the following in chisel repl\n    //    ```\n    //    > import {SemaphoreVerifier} from \"src/SemaphoreVerifier.sol\";\n    //    > SemaphoreVerifier ve = new SemaphoreVerifier();\n    //    ```\n    // 4. Now you can generate the expected data fixtures using e.g.\n    //    ```\n    //    > ve.compress_g1(0x19ded61ab5c58fdb12367526c6bc04b9186d0980c4b6fd48a44093e80f9b4206, 0x2e619a034be10e9aab294f1c77a480378e84782c8519449aef0c8f6952382bda)\n    //    ```\n    // Note that for some reason chisel doesn't handle multiple return values that well, so you\n    // might have to pattern match the return types, e.g.\n    // ```\n    // > (uint256 a, uint256 b) = ve.compress_g2(...);\n    // > a;\n    // Type: uint256\n    // ├ Hex: 0x1dd212f101a320736a9662cac57929556777fad3e7882b022d4ba3261cf14db6\n    // ├ Hex (full word): 0x1dd212f101a320736a9662cac57929556777fad3e7882b022d4ba3261cf14db6\n    // └ Decimal: 13488241221471993734368286196608381596836013455766665997449768358320614231478\n    // ```\n\n    #[test]\n    fn proof_compression() {\n        let flat_proof: [U256; 8] = uint! { [\n            20565048055856194013099208963146657799256893353279242520150547463020687826541_U256,\n            16286013012747852737396822706018267259565592188907848191354824303311847109059_U256,\n            4348608846293503080802796983494208797681981448804902149317789801083784587558_U256,\n            6172488348732750834133346196464201580503416389945891763609808290085997580078_U256,\n            3229429189805934086496276224876305383924675874777054942516982958483565949767_U256,\n            944252930093106871283598150477854448876343937304805759422971930315581301659_U256,\n            18318130744212307125672524358864792312717149086464333958791498157127232409959_U256,\n            8256141885907329266852096557308020923997215847794048916749940281741155521604_U256,\n        ]};\n        let proof = Proof::from_flat(flat_proof);\n\n        let compressed = compress_proof(proof).unwrap();\n        let exp_flat_compressed: [U256; 4] = uint! {[\n            41130096111712388026198417926293315598513786706558485040301094926041375653083_U256,\n            4348608846293503080802796983494208797681981448804902149317789801083784587558_U256,\n            24689953394931003336533384785856806322013665559783567054439233160343990320315_U256,\n            36636261488424614251345048717729584625434298172928667917582996314254464819918_U256,\n        ]};\n\n        assert_eq!(exp_flat_compressed, compressed.flatten());\n\n        let decompressed = decompress_proof(compressed).unwrap();\n\n        assert_eq!(proof, decompressed);\n    }\n\n    #[test]\n    fn g1_compression() {\n        let point: G1 = uint! {\n            (\n                0x19ded61ab5c58fdb12367526c6bc04b9186d0980c4b6fd48a44093e80f9b4206_U256,\n                0x2e619a034be10e9aab294f1c77a480378e84782c8519449aef0c8f6952382bda_U256\n            )\n        };\n        let exp_compressed =\n            uint! { 0x33bdac356b8b1fb6246cea4d8d78097230da1301896dfa91488127d01f36840c_U256 };\n\n        let compressed = compress_g1(point).unwrap();\n        assert_eq!(exp_compressed, compressed);\n\n        let decompressed = decompress_g1(compressed).unwrap();\n        assert_eq!(point, decompressed);\n    }\n\n    #[test]\n    fn g2_compression() {\n        let point: G2 = uint! {\n            (\n                [\n                    0x077484BC4068C81CDAA598B2B15E4A5559DDFEB4F9E20AC08B52E8C9873C536D_U256,\n                    0x25E744163329AABFB40086C09E0B54D09DFBD302CE975E71150133E46E75F0AA_U256,\n                ],\n                [\n                    0x20AF3E3AFED950A86937F4319100B19A1141FF59DA42B9670CFA57E5D83BE618_U256,\n                    0x089C901AA5603652F8CC748F04907233C63A75302244D67FF974B05AF09948D2_U256,\n                ]\n            )\n        };\n\n        let compressed = compress_g2(point).unwrap();\n        let exp_compressed = uint! { (0x1dd212f101a320736a9662cac57929556777fad3e7882b022d4ba3261cf14db6_U256, 0x25e744163329aabfb40086c09e0b54d09dfbd302ce975e71150133e46e75f0aa_U256) };\n\n        assert_eq!(exp_compressed, compressed);\n        let decompressed = decompress_g2(compressed).unwrap();\n\n        assert_eq!(point, decompressed);\n    }\n\n    #[test]\n    fn deser() {\n        let s = r#\"[\"0x1\",[\"0x2\",\"0x3\"],\"0x4\"]\"#;\n\n        let deserialized: CompressedProof = serde_json::from_str(s).unwrap();\n        let reserialized = serde_json::to_string(&deserialized).unwrap();\n\n        assert_eq!(s, reserialized);\n    }\n}\n"
  },
  {
    "path": "crates/proof/src/lib.rs",
    "content": "use ruint::aliases::U256;\nuse serde::{Deserialize, Serialize};\n\n#[cfg(feature = \"ark\")]\nmod ark;\n\npub mod compression;\n\npub mod packing;\n\n// Matches the private G1Tup type in ark-circom.\npub type G1 = (U256, U256);\n\n// Matches the private G2Tup type in ark-circom.\npub type G2 = ([U256; 2], [U256; 2]);\n\n/// Wrap a proof object so we have serde support\n#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]\npub struct Proof(pub G1, pub G2, pub G1);\n\nimpl Proof {\n    pub const fn from_flat(flat: [U256; 8]) -> Self {\n        let [x0, x1, x2, x3, x4, x5, x6, x7] = flat;\n        Self((x0, x1), ([x2, x3], [x4, x5]), (x6, x7))\n    }\n\n    pub const fn flatten(self) -> [U256; 8] {\n        let Self((a0, a1), ([bx0, bx1], [by0, by1]), (c0, c1)) = self;\n        [a0, a1, bx0, bx1, by0, by1, c0, c1]\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn deser() {\n        let s = r#\"[[\"0x1\",\"0x2\"],[[\"0x3\",\"0x4\"],[\"0x5\",\"0x6\"]],[\"0x7\",\"0x8\"]]\"#;\n\n        let deserialized: Proof = serde_json::from_str(s).unwrap();\n        let reserialized = serde_json::to_string(&deserialized).unwrap();\n\n        assert_eq!(s, reserialized);\n    }\n}\n"
  },
  {
    "path": "crates/proof/src/packing.rs",
    "content": "use std::{\n    fmt::Display,\n    str::{from_utf8, FromStr},\n};\n\nuse alloy_core::sol_types::{\n    sol_data::{FixedArray, Uint},\n    SolType, SolValue,\n};\nuse serde::{Deserialize, Deserializer, Serialize, Serializer};\n\nuse crate::Proof;\nuse semaphore_rs_utils::{bytes_from_hex, bytes_to_hex, deserialize_bytes, serialize_bytes};\n\n/// A packed proof is a representation of the ZKP in a single attribute (as\n/// opposed to array of arrays) which is easier to transport\n#[derive(Clone, Copy, Debug, PartialEq, Eq)]\npub struct PackedProof(pub [u8; 256]);\n\nimpl From<Proof> for PackedProof {\n    fn from(proof: Proof) -> Self {\n        let flat_proof = [\n            proof.0 .0,\n            proof.0 .1,\n            proof.1 .0[0],\n            proof.1 .0[1],\n            proof.1 .1[0],\n            proof.1 .1[1],\n            proof.2 .0,\n            proof.2 .1,\n        ];\n\n        let bytes = flat_proof.abi_encode();\n        let mut encoded = [0u8; 256];\n        encoded.copy_from_slice(&bytes[..256]);\n        Self(encoded)\n    }\n}\n\nimpl From<PackedProof> for Proof {\n    fn from(proof: PackedProof) -> Self {\n        let decoded = FixedArray::<Uint<256>, 8>::abi_decode(&proof.0).unwrap();\n\n        let a = (decoded[0], decoded[1]);\n        let b = ([decoded[2], decoded[3]], [decoded[4], decoded[5]]);\n        let c = (decoded[6], decoded[7]);\n\n        Self(a, b, c)\n    }\n}\n\nimpl Display for PackedProof {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let hex = bytes_to_hex::<256, 514>(&self.0);\n        write!(\n            f,\n            \"{}\",\n            from_utf8(&hex).expect(\"failed to convert to string\")\n        )\n    }\n}\n\nimpl FromStr for PackedProof {\n    type Err = hex::FromHexError;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        bytes_from_hex::<256>(s).map(Self)\n    }\n}\n\nimpl Serialize for PackedProof {\n    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {\n        serialize_bytes::<256, 514, S>(serializer, &self.0)\n    }\n}\n\nimpl<'de> Deserialize<'de> for PackedProof {\n    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {\n        let bytes = deserialize_bytes::<256, _>(deserializer)?;\n        Ok(Self(bytes))\n    }\n}\n\n#[cfg(test)]\npub mod test {\n    use super::*;\n    use ruint::aliases::U256;\n\n    #[test]\n    fn test_serializing_proof_into_packed_proof() {\n        let proof = Proof(\n            (U256::from(1), U256::from(2)),\n            (\n                [U256::from(3), U256::from(4)],\n                [U256::from(5), U256::from(6)],\n            ),\n            (U256::from(7), U256::from(8)),\n        );\n\n        let packed_proof = PackedProof::from(proof);\n\n        assert_eq!(packed_proof.to_string(), \"0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008\");\n\n        let proof2 = Proof::from(packed_proof);\n\n        assert_eq!(proof, proof2);\n    }\n\n    #[test]\n    fn test_parse_from_string() {\n        let packed_proof_str =  \"0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008\";\n\n        let packed_proof = PackedProof::from_str(packed_proof_str).unwrap();\n\n        let expected_proof = Proof(\n            (U256::from(1), U256::from(2)),\n            (\n                [U256::from(3), U256::from(4)],\n                [U256::from(5), U256::from(6)],\n            ),\n            (U256::from(7), U256::from(8)),\n        );\n\n        let proof: Proof = packed_proof.into();\n\n        assert_eq!(proof, expected_proof);\n    }\n\n    #[test]\n    fn test_parse_from_string_without_prefix() {\n        // note the lack of 0x prefix\n        let packed_proof_str =  \"00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008\";\n\n        let packed_proof = PackedProof::from_str(packed_proof_str).unwrap();\n\n        let expected_proof = Proof(\n            (U256::from(5), U256::from(6)),\n            (\n                [U256::from(3), U256::from(4)],\n                [U256::from(5), U256::from(6)],\n            ),\n            (U256::from(7), U256::from(8)),\n        );\n\n        let proof: Proof = packed_proof.into();\n\n        assert_eq!(proof, expected_proof);\n    }\n\n    #[test]\n    fn test_serialize_proof_to_json() {\n        let packed_proof_str =  \"0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008\";\n\n        let packed_proof = PackedProof::from_str(packed_proof_str).unwrap();\n        let proof: Proof = packed_proof.into();\n\n        let serialized = serde_json::to_value(proof).unwrap();\n\n        assert_eq!(\n            serialized,\n            serde_json::json!([\n                [\"0x1\", \"0x2\"],\n                [[\"0x3\", \"0x4\"], [\"0x5\", \"0x6\"]],\n                [\"0x7\", \"0x8\"]\n            ])\n        );\n    }\n\n    #[test]\n    fn test_serialize_proof_to_json_real_numbers() {\n        let packed_proof_str =  \"0x15c1fc6907219676890dfe147ee6f10b580c7881dddacb1567b3bcbfc513a54d233afda3efff43a7631990d2e79470abcbae3ccad4b920476e64745bfe97bb0a0c8c7d7434c382d590d601d951c29c8463d555867db70f9e84f7741c81c2e1e6241d2ddf1c9e6670a24109a0e9c915cd6e07d0248a384dd38d3c91e9b0419f5f0b23c5467a06eff56cc2c246ada1e7d5705afc4dc8b43fd5a6972c679a2019c5091ed6522f7924d3674d08966a008f947f9aa016a4100bb12f911326f3e1befd0acdf5a5996e00933206cbec48f3bbdcee2a4ca75f8db911c00001e5a05474872446d6f1c1506837392a30fdc73d66fd89f4e1b1a5d14b93e2ad0c5f7b777520\";\n\n        let packed_proof = PackedProof::from_str(packed_proof_str).unwrap();\n        let proof: Proof = packed_proof.into();\n\n        let serialized = serde_json::to_value(proof).unwrap();\n\n        assert_eq!(\n            serialized,\n            serde_json::json!([\n                [\n                    \"0x15c1fc6907219676890dfe147ee6f10b580c7881dddacb1567b3bcbfc513a54d\",\n                    \"0x233afda3efff43a7631990d2e79470abcbae3ccad4b920476e64745bfe97bb0a\"\n                ],\n                [\n                    [\n                        \"0xc8c7d7434c382d590d601d951c29c8463d555867db70f9e84f7741c81c2e1e6\",\n                        \"0x241d2ddf1c9e6670a24109a0e9c915cd6e07d0248a384dd38d3c91e9b0419f5f\"\n                    ],\n                    [\n                        \"0xb23c5467a06eff56cc2c246ada1e7d5705afc4dc8b43fd5a6972c679a2019c5\",\n                        \"0x91ed6522f7924d3674d08966a008f947f9aa016a4100bb12f911326f3e1befd\"\n                    ]\n                ],\n                [\n                    \"0xacdf5a5996e00933206cbec48f3bbdcee2a4ca75f8db911c00001e5a0547487\",\n                    \"0x2446d6f1c1506837392a30fdc73d66fd89f4e1b1a5d14b93e2ad0c5f7b777520\"\n                ]\n            ])\n        );\n    }\n\n    #[test]\n    fn test_deserialize_proof_from_json() {\n        let proof_str = \"[\n            [\n                \\\"0x15c1fc6907219676890dfe147ee6f10b580c7881dddacb1567b3bcbfc513a54d\\\",\n                \\\"0x233afda3efff43a7631990d2e79470abcbae3ccad4b920476e64745bfe97bb0a\\\"\n            ],\n            [\n                [\n                    \\\"0xc8c7d7434c382d590d601d951c29c8463d555867db70f9e84f7741c81c2e1e6\\\",\n                    \\\"0x241d2ddf1c9e6670a24109a0e9c915cd6e07d0248a384dd38d3c91e9b0419f5f\\\"\n                ],\n                [\n                    \\\"0xb23c5467a06eff56cc2c246ada1e7d5705afc4dc8b43fd5a6972c679a2019c5\\\",\n                    \\\"0x91ed6522f7924d3674d08966a008f947f9aa016a4100bb12f911326f3e1befd\\\"\n                ]\n            ],\n            [\n                \\\"0xacdf5a5996e00933206cbec48f3bbdcee2a4ca75f8db911c00001e5a0547487\\\",\n                \\\"0x2446d6f1c1506837392a30fdc73d66fd89f4e1b1a5d14b93e2ad0c5f7b777520\\\"\n            ]\n        ]\";\n\n        let proof = serde_json::from_str::<Proof>(proof_str).unwrap();\n\n        let packed_proof = PackedProof::from(proof);\n\n        let expected_proof =  \"0x15c1fc6907219676890dfe147ee6f10b580c7881dddacb1567b3bcbfc513a54d233afda3efff43a7631990d2e79470abcbae3ccad4b920476e64745bfe97bb0a0c8c7d7434c382d590d601d951c29c8463d555867db70f9e84f7741c81c2e1e6241d2ddf1c9e6670a24109a0e9c915cd6e07d0248a384dd38d3c91e9b0419f5f0b23c5467a06eff56cc2c246ada1e7d5705afc4dc8b43fd5a6972c679a2019c5091ed6522f7924d3674d08966a008f947f9aa016a4100bb12f911326f3e1befd0acdf5a5996e00933206cbec48f3bbdcee2a4ca75f8db911c00001e5a05474872446d6f1c1506837392a30fdc73d66fd89f4e1b1a5d14b93e2ad0c5f7b777520\";\n\n        assert_eq!(packed_proof.to_string(), expected_proof);\n    }\n\n    #[test]\n    fn test_invalid_parsing() {\n        // note this is only 7 numbers\n        let packed_proof_str =  \"0x0000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000007\";\n        PackedProof::from_str(packed_proof_str).expect_err(\"parsing should fail\");\n\n        // not a valid number\n        let packed_proof_str =  \"0000000000000000p000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008\";\n        PackedProof::from_str(packed_proof_str).expect_err(\"parsing should fail\");\n\n        // completely invalid\n        let packed_proof_str = \"0x0\";\n        PackedProof::from_str(packed_proof_str).expect_err(\"parsing should fail\");\n    }\n}\n"
  },
  {
    "path": "crates/semaphore/Cargo.toml",
    "content": "[package]\nname = \"semaphore-rs\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nauthors.workspace = true\ndescription.workspace = true\nkeywords.workspace = true\ncategories.workspace = true\n\n[dependencies]\n# Internal\nsemaphore-rs-utils.workspace = true\nsemaphore-rs-ark-zkey.workspace = true\nsemaphore-rs-ark-circom.workspace = true\nsemaphore-rs-proof = { workspace = true, features = [\"ark\"] }\nsemaphore-rs-poseidon.workspace = true\nsemaphore-rs-hasher.workspace = true\nsemaphore-rs-keccak.workspace = true\nsemaphore-rs-trees.workspace = true\nsemaphore-rs-storage.workspace = true\nsemaphore-rs-depth-config.workspace = true\nsemaphore-rs-depth-macros.workspace = true\nsemaphore-rs-witness.workspace = true\n\n# 3rd Party\nbincode.workspace = true\nbytemuck.workspace = true\ncolor-eyre.workspace = true\nhex.workspace = true\nhex-literal.workspace = true\nitertools.workspace = true\nlazy_static.workspace = true\nnum-bigint.workspace = true\nonce_cell.workspace = true\nrand.workspace = true\nrayon.workspace = true\nruint.workspace = true\nserde.workspace = true\nsha2.workspace = true\nthiserror.workspace = true\ntiny-keccak.workspace = true\nzeroize.workspace = true\n\n# Ark\nark-bn254.workspace = true\nark-ec.workspace = true\nark-ff.workspace = true\nark-groth16.workspace = true\nark-relations.workspace = true\nark-std.workspace = true\n\n[dev-dependencies]\nserial_test.workspace = true\ntokio = { workspace = true, features = [\"macros\", \"rt-multi-thread\"] }\ncriterion.workspace = true\nbincode.workspace = true\nproptest.workspace = true\nrand_chacha.workspace = true\nserde_json.workspace = true\ntempfile.workspace = true\ntiny-keccak.workspace = true\ntracing-test.workspace = true\n\n[build-dependencies]\nsemaphore-rs-ark-zkey.workspace = true\ncolor-eyre.workspace = true\nreqwest.workspace = true\nsemaphore-rs-depth-config.workspace = true\n\n[[bench]]\nname = \"cascading_merkle_tree\"\nharness = false\n\n[[bench]]\nname = \"lazy_merkle_tree\"\nharness = false\n\n[features]\ndefault = []\ndepth_16 = [\n    \"semaphore-rs-depth-config/depth_16\",\n    \"semaphore-rs-depth-macros/depth_16\",\n]\ndepth_20 = [\n    \"semaphore-rs-depth-config/depth_20\",\n    \"semaphore-rs-depth-macros/depth_20\",\n]\ndepth_30 = [\n    \"semaphore-rs-depth-config/depth_30\",\n    \"semaphore-rs-depth-macros/depth_30\",\n]\n"
  },
  {
    "path": "crates/semaphore/README.md",
    "content": "# 🦀 semaphore-rs\n\nRust support library for using [semaphore](https://github.com/appliedzkp/semaphore). It's mostly a Rust rewrite of [zk-kit](https://github.com/appliedzkp/zk-kit), but just focuses on semaphore (for now) and still covers a much smaller scope. It's using [ark-circom](https://github.com/gakonst/ark-circom) under the hood for generating the groth16 proofs.\n\n## Usage\n\nAdd this line to your `cargo.toml`:\n\n```toml\nsemaphore = { git = \"https://github.com/worldcoin/semaphore-rs\" }\n```\n\n## Building semaphore circuits\n\n1. Check out submodule (if not done before already): `git submodule update --init --recursive`\n1. Install semaphore dependencies `cd semaphore && npm install`\n1. Compile circuits `npm exec ts-node ./scripts/compile-circuits.ts`\n1. You'll find the `zkey` and `wasm` file in `semaphore/build/snark`\n\n## Example\n\nExample as in `src/lib.rs`, run with `cargo test`.\n\n```rust,no_run\nuse semaphore_rs::{get_supported_depths, hash_to_field, Field, identity::Identity,\n                poseidon_tree::LazyPoseidonTree, protocol::*};\nuse num_bigint::BigInt;\n\n// generate identity\nlet mut secret = *b\"secret\";\nlet id = Identity::from_secret(&mut secret, None);\n\n// Get the first available tree depth. This is controlled by the crate features.\nlet depth = get_supported_depths()[0];\n\n// generate merkle tree\nlet leaf = Field::from(0);\nlet mut tree = LazyPoseidonTree::new(depth, leaf).derived();\ntree = tree.update(0, &id.commitment());\n\nlet merkle_proof = tree.proof(0);\nlet root = tree.root();\n\n// change signal and external_nullifier here\nlet signal_hash = hash_to_field(b\"xxx\");\nlet external_nullifier_hash = hash_to_field(b\"appId\");\n\nlet nullifier_hash = generate_nullifier_hash(&id, external_nullifier_hash);\n\nlet proof = generate_proof(&id, &merkle_proof, external_nullifier_hash, signal_hash).unwrap();\nlet success = verify_proof(root, nullifier_hash, signal_hash, external_nullifier_hash, &proof, depth).unwrap();\n\nassert!(success);\n```\n"
  },
  {
    "path": "crates/semaphore/benches/cascading_merkle_tree.rs",
    "content": "use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion};\nuse semaphore_rs::Field;\nuse semaphore_rs_hasher::Hasher;\nuse semaphore_rs_poseidon::Poseidon;\nuse semaphore_rs_storage::MmapVec;\nuse semaphore_rs_trees::cascading::CascadingMerkleTree;\n\ncriterion_main!(cascading_merkle_tree);\ncriterion_group!(\n    cascading_merkle_tree,\n    bench_cascading_validate,\n    bench_cascading_create_dense_tree,\n    bench_cascading_create_dense_mmap_tree,\n    bench_cascading_restore_dense_mmap_tree,\n    bench_cascading_dense_tree_reads,\n    bench_cascading_dense_mmap_tree_reads,\n    bench_cascading_dense_tree_writes,\n    bench_cascading_dense_mmap_tree_writes,\n    bench_cascading_proof_from_hash\n);\n\nstruct TreeValues<H: Hasher> {\n    depth: usize,\n    empty_value: H::Hash,\n    initial_values: Vec<H::Hash>,\n}\n\nfn bench_cascading_proof_from_hash(criterion: &mut Criterion) {\n    let tree_value = create_values_for_tree(14);\n\n    criterion.bench_function(\"bench_cascading_proof_from_hash\", |b| {\n        let leaf = Field::from(234123412341usize);\n        b.iter_batched_ref(\n            || {\n                let mut tree = CascadingMerkleTree::<Poseidon>::new_with_leaves(\n                    vec![],\n                    tree_value.depth,\n                    &tree_value.empty_value,\n                    &tree_value.initial_values,\n                );\n                tree.set_leaf(1 << 13, leaf);\n                tree\n            },\n            |tree| {\n                let _ = tree.proof_from_hash(leaf);\n            },\n            BatchSize::SmallInput,\n        );\n    });\n}\n\nfn bench_cascading_validate(criterion: &mut Criterion) {\n    let tree_values = [\n        create_values_for_tree(4),\n        create_values_for_tree(10),\n        create_values_for_tree(14),\n    ];\n\n    let mut group = criterion.benchmark_group(\"bench_cascading_validate\");\n\n    for value in tree_values.iter() {\n        let tree = CascadingMerkleTree::<Poseidon>::new_with_leaves(\n            vec![],\n            value.depth,\n            &value.empty_value,\n            &value.initial_values,\n        );\n\n        group.bench_with_input(\n            BenchmarkId::from_parameter(format!(\"validate_{}\", value.depth)),\n            value,\n            |bencher: &mut criterion::Bencher, _| {\n                bencher.iter(|| {\n                    tree.validate().unwrap();\n                });\n            },\n        );\n    }\n    group.finish();\n}\n\nfn bench_cascading_create_dense_tree(criterion: &mut Criterion) {\n    let tree_values = [\n        create_values_for_tree(4),\n        create_values_for_tree(10),\n        create_values_for_tree(14),\n    ];\n\n    let mut group = criterion.benchmark_group(\"bench_cascading_create_dense_tree\");\n\n    for value in tree_values.iter() {\n        group.bench_with_input(\n            BenchmarkId::from_parameter(format!(\"create_dense_tree_depth_{}\", value.depth)),\n            value,\n            |bencher: &mut criterion::Bencher, value| {\n                bencher.iter(|| {\n                    let _tree = CascadingMerkleTree::<Poseidon>::new_with_leaves(\n                        vec![],\n                        value.depth,\n                        &value.empty_value,\n                        &value.initial_values,\n                    );\n                    let _root = _tree.root();\n                });\n            },\n        );\n    }\n    group.finish();\n}\n\nfn bench_cascading_create_dense_mmap_tree(criterion: &mut Criterion) {\n    let tree_values = [\n        create_values_for_tree(4),\n        create_values_for_tree(10),\n        create_values_for_tree(14),\n    ];\n\n    let mut group = criterion.benchmark_group(\"bench_cascading_create_dense_mmap_tree\");\n\n    for value in tree_values.iter() {\n        group.bench_with_input(\n            BenchmarkId::from_parameter(format!(\"create_dense_mmap_tree_depth_{}\", value.depth)),\n            value,\n            |bencher: &mut criterion::Bencher, value| {\n                bencher.iter(|| {\n                    let tempfile = tempfile::tempfile().unwrap();\n                    let storage: MmapVec<_> = unsafe { MmapVec::create(tempfile).unwrap() };\n                    let _tree: CascadingMerkleTree<Poseidon, _> =\n                        CascadingMerkleTree::new_with_leaves(\n                            storage,\n                            value.depth,\n                            &value.empty_value,\n                            &value.initial_values,\n                        );\n                    let _root = _tree.root();\n                });\n            },\n        );\n    }\n    group.finish();\n}\n\nfn bench_cascading_restore_dense_mmap_tree(criterion: &mut Criterion) {\n    let tree_values = vec![\n        create_values_for_tree(4),\n        create_values_for_tree(10),\n        create_values_for_tree(14),\n    ];\n\n    let mut group = criterion.benchmark_group(\"bench_cascading_restore_dense_mmap_tree\");\n\n    (0..3).zip(tree_values).for_each(|(id, value)| {\n        let tempfile = tempfile::NamedTempFile::new().unwrap();\n        let path = tempfile.path();\n        let storage: MmapVec<_> = unsafe { MmapVec::create_from_path(path).unwrap() };\n        {\n            let tree: CascadingMerkleTree<Poseidon, _> = CascadingMerkleTree::new_with_leaves(\n                storage,\n                value.depth,\n                &value.empty_value,\n                &value.initial_values,\n            );\n            let _ = tree.root();\n        }\n\n        group.bench_with_input(\n            BenchmarkId::from_parameter(format!(\"restore_dense_mmap_tree_depth_{}\", value.depth)),\n            &(id, value),\n            |bencher: &mut criterion::Bencher, (_id, value)| {\n                bencher.iter(|| {\n                    let storage = unsafe { MmapVec::restore_from_path(path).unwrap() };\n                    let _tree: CascadingMerkleTree<Poseidon, _> =\n                        CascadingMerkleTree::restore(storage, value.depth, &value.empty_value)\n                            .unwrap();\n                    let _root = _tree.root();\n                });\n            },\n        );\n    });\n    group.finish();\n}\n\nfn bench_cascading_dense_tree_reads(criterion: &mut Criterion) {\n    let tree_value = create_values_for_tree(14);\n\n    let tree = CascadingMerkleTree::<Poseidon>::new_with_leaves(\n        vec![],\n        tree_value.depth,\n        &tree_value.empty_value,\n        &tree_value.initial_values,\n    );\n\n    criterion.bench_function(\"dense tree reads\", |b| {\n        b.iter(|| {\n            // read all leaves, and compare to ones in tree value\n            ((1 << (tree_value.depth - 1))..(1 << tree_value.depth)).for_each(|index| {\n                let _proof = tree.proof(index);\n            })\n        })\n    });\n}\n\nfn bench_cascading_dense_mmap_tree_reads(criterion: &mut Criterion) {\n    let tree_value = create_values_for_tree(14);\n    let file = tempfile::tempfile().unwrap();\n\n    let storage = unsafe { MmapVec::create(file).unwrap() };\n    let tree = CascadingMerkleTree::<Poseidon, _>::new_with_leaves(\n        storage,\n        tree_value.depth,\n        &tree_value.empty_value,\n        &tree_value.initial_values,\n    );\n\n    criterion.bench_function(\"dense mmap tree reads\", |b| {\n        b.iter(|| {\n            // read all leaves, and compare to ones in tree value\n            ((1 << (tree.depth() - 1))..(1 << tree.depth())).for_each(|index| {\n                let _proof = tree.proof(index);\n            })\n        })\n    });\n}\n\nfn bench_cascading_dense_tree_writes(criterion: &mut Criterion) {\n    let tree_value = create_values_for_tree(14);\n\n    let value = Field::from(123_456);\n\n    criterion.bench_function(\"dense tree writes\", |b| {\n        b.iter_batched_ref(\n            || {\n                CascadingMerkleTree::<Poseidon>::new_with_leaves(\n                    vec![],\n                    tree_value.depth,\n                    &tree_value.empty_value,\n                    &tree_value.initial_values,\n                )\n            },\n            |tree| {\n                tree.set_leaf(9000, value);\n            },\n            BatchSize::SmallInput,\n        );\n    });\n}\n\nfn bench_cascading_dense_mmap_tree_writes(criterion: &mut Criterion) {\n    let tree_value = create_values_for_tree(14);\n\n    let value = Field::from(123_456);\n\n    criterion.bench_function(\"dense mmap tree writes\", |b| {\n        b.iter_batched_ref(\n            || {\n                let file = tempfile::tempfile().unwrap();\n                let storage = unsafe { MmapVec::create(file).unwrap() };\n                CascadingMerkleTree::<Poseidon, _>::new_with_leaves(\n                    storage,\n                    tree_value.depth,\n                    &tree_value.empty_value,\n                    &tree_value.initial_values,\n                )\n            },\n            |tree| {\n                tree.set_leaf(9000, value);\n            },\n            BatchSize::SmallInput,\n        );\n    });\n}\n\nfn create_values_for_tree(depth: usize) -> TreeValues<Poseidon> {\n    let empty_value = Field::from(0);\n\n    let initial_values: Vec<ruint::Uint<256, 4>> = (0..(1 << depth)).map(Field::from).collect();\n\n    TreeValues {\n        depth,\n        empty_value,\n        initial_values,\n    }\n}\n"
  },
  {
    "path": "crates/semaphore/benches/lazy_merkle_tree.rs",
    "content": "use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion};\nuse semaphore_rs::poseidon_tree::LazyPoseidonTree;\nuse semaphore_rs::Field;\nuse semaphore_rs_hasher::Hasher;\nuse semaphore_rs_poseidon::Poseidon;\n\ncriterion_main!(lazy_merkle_tree);\ncriterion_group!(\n    lazy_merkle_tree,\n    bench_create_dense_tree,\n    bench_create_dense_mmap_tree,\n    bench_restore_dense_mmap_tree,\n    bench_dense_tree_reads,\n    bench_dense_mmap_tree_reads,\n    bench_dense_tree_writes,\n    bench_dense_mmap_tree_writes,\n);\n\nstruct TreeValues<H: Hasher> {\n    depth: usize,\n    prefix_depth: usize,\n    empty_value: H::Hash,\n    initial_values: Vec<H::Hash>,\n}\n\nfn bench_create_dense_tree(criterion: &mut Criterion) {\n    let tree_values = [\n        create_values_for_tree(4),\n        create_values_for_tree(10),\n        create_values_for_tree(14),\n    ];\n\n    let mut group = criterion.benchmark_group(\"bench_create_dense_tree\");\n\n    for value in tree_values.iter() {\n        group.bench_with_input(\n            BenchmarkId::from_parameter(format!(\"create_dense_tree_depth_{}\", value.depth)),\n            value,\n            |bencher: &mut criterion::Bencher, value| {\n                bencher.iter(|| {\n                    let _tree = LazyPoseidonTree::new_with_dense_prefix_with_initial_values(\n                        value.depth,\n                        value.prefix_depth,\n                        &value.empty_value,\n                        &value.initial_values,\n                    );\n                    let _root = _tree.root();\n                });\n            },\n        );\n    }\n    group.finish();\n}\n\nfn bench_create_dense_mmap_tree(criterion: &mut Criterion) {\n    let tree_values = [\n        create_values_for_tree(4),\n        create_values_for_tree(10),\n        create_values_for_tree(14),\n    ];\n\n    let mut group = criterion.benchmark_group(\"bench_create_dense_mmap_tree\");\n\n    for value in tree_values.iter() {\n        group.bench_with_input(\n            BenchmarkId::from_parameter(format!(\"create_dense_mmap_tree_depth_{}\", value.depth)),\n            value,\n            |bencher: &mut criterion::Bencher, value| {\n                let file = tempfile::NamedTempFile::new().unwrap();\n                let path = file.path().to_str().unwrap();\n                bencher.iter(|| {\n                    let _tree = LazyPoseidonTree::new_mmapped_with_dense_prefix_with_init_values(\n                        value.depth,\n                        value.prefix_depth,\n                        &value.empty_value,\n                        &value.initial_values,\n                        path,\n                    )\n                    .unwrap();\n                    let _root = _tree.root();\n                });\n            },\n        );\n    }\n    group.finish();\n    // remove created mmap file\n    let _ = std::fs::remove_file(\"./testfile\");\n}\n\nfn bench_restore_dense_mmap_tree(criterion: &mut Criterion) {\n    let tree_values = [\n        create_values_for_tree(4),\n        create_values_for_tree(10),\n        create_values_for_tree(14),\n    ];\n\n    let mut group = criterion.benchmark_group(\"bench_restore_dense_mmap_tree\");\n\n    (0..3).zip(tree_values).for_each(|(id, value)| {\n        let file = tempfile::NamedTempFile::new().unwrap();\n        let path = file.path().to_str().unwrap();\n        {\n            let _tree = LazyPoseidonTree::new_mmapped_with_dense_prefix_with_init_values(\n                value.depth,\n                value.prefix_depth,\n                &value.empty_value,\n                &value.initial_values,\n                path,\n            )\n            .unwrap();\n            let _root = _tree.root();\n        }\n\n        group.bench_with_input(\n            BenchmarkId::from_parameter(format!(\"restore_dense_mmap_tree_depth_{}\", value.depth)),\n            &(id, value),\n            |bencher: &mut criterion::Bencher, (_id, value)| {\n                bencher.iter(|| {\n                    let _tree = LazyPoseidonTree::attempt_dense_mmap_restore(\n                        value.depth,\n                        value.depth,\n                        &value.empty_value,\n                        path,\n                    )\n                    .unwrap();\n                    let _root = _tree.root();\n                });\n            },\n        );\n    });\n    group.finish();\n}\n\nfn bench_dense_tree_reads(criterion: &mut Criterion) {\n    let tree_value = create_values_for_tree(14);\n\n    let tree = LazyPoseidonTree::new_with_dense_prefix_with_initial_values(\n        tree_value.depth,\n        tree_value.prefix_depth,\n        &tree_value.empty_value,\n        &tree_value.initial_values,\n    );\n\n    criterion.bench_function(\"dense tree reads\", |b| {\n        b.iter(|| {\n            // read all leaves, and compare to ones in tree value\n            ((1 << (tree_value.depth - 1))..(1 << tree_value.depth)).for_each(|index| {\n                let _proof = tree.proof(index);\n            })\n        })\n    });\n}\n\nfn bench_dense_mmap_tree_reads(criterion: &mut Criterion) {\n    let tree_value = create_values_for_tree(14);\n    let file = tempfile::NamedTempFile::new().unwrap();\n    let path = file.path().to_str().unwrap();\n\n    let tree = LazyPoseidonTree::new_mmapped_with_dense_prefix_with_init_values(\n        tree_value.depth,\n        tree_value.prefix_depth,\n        &tree_value.empty_value,\n        &tree_value.initial_values,\n        path,\n    )\n    .unwrap();\n\n    criterion.bench_function(\"dense mmap tree reads\", |b| {\n        b.iter(|| {\n            // read all leaves, and compare to ones in tree value\n            ((1 << (tree.depth() - 1))..(1 << tree.depth())).for_each(|index| {\n                let _proof = tree.proof(index);\n            })\n        })\n    });\n}\n\nfn bench_dense_tree_writes(criterion: &mut Criterion) {\n    let tree_value = create_values_for_tree(14);\n\n    let value = Field::from(123_456);\n\n    criterion.bench_function(\"dense tree writes\", |b| {\n        b.iter_batched(\n            || {\n                LazyPoseidonTree::new_with_dense_prefix_with_initial_values(\n                    tree_value.depth,\n                    tree_value.prefix_depth,\n                    &tree_value.empty_value,\n                    &tree_value.initial_values,\n                )\n            },\n            |tree| {\n                let _new_tree = tree.update_with_mutation(9000, &value);\n            },\n            BatchSize::SmallInput,\n        );\n    });\n}\n\nfn bench_dense_mmap_tree_writes(criterion: &mut Criterion) {\n    let tree_value = create_values_for_tree(14);\n    let file = tempfile::NamedTempFile::new().unwrap();\n    let path = file.path().to_str().unwrap();\n\n    let value = Field::from(123_456);\n\n    criterion.bench_function(\"dense mmap tree writes\", |b| {\n        b.iter_batched(\n            || {\n                LazyPoseidonTree::new_mmapped_with_dense_prefix_with_init_values(\n                    tree_value.depth,\n                    tree_value.prefix_depth,\n                    &tree_value.empty_value,\n                    &tree_value.initial_values,\n                    path,\n                )\n                .unwrap()\n            },\n            |tree| {\n                let _new_tree = tree.update_with_mutation(9000, &value);\n            },\n            BatchSize::SmallInput,\n        );\n    });\n}\n\nfn create_values_for_tree(depth: usize) -> TreeValues<Poseidon> {\n    let prefix_depth = depth;\n    let empty_value = Field::from(0);\n\n    let initial_values: Vec<ruint::Uint<256, 4>> = (0..(1 << depth)).map(Field::from).collect();\n\n    TreeValues {\n        depth,\n        prefix_depth,\n        empty_value,\n        initial_values,\n    }\n}\n"
  },
  {
    "path": "crates/semaphore/build.rs",
    "content": "use std::fs::{create_dir, create_dir_all, File};\nuse std::path::{absolute, Path, PathBuf};\n\nuse color_eyre::eyre::Result;\n\nextern crate reqwest;\n\nconst SEMAPHORE_FILES_PATH: &str = \"semaphore_files\";\nconst SEMAPHORE_DOWNLOAD_URL: &str = \"https://www.trusted-setup-pse.org/semaphore\";\n\nfn download_and_store_binary(url: &str, path: impl AsRef<Path>) -> Result<()> {\n    let path = path.as_ref();\n\n    let mut resp =\n        reqwest::blocking::get(url).unwrap_or_else(|_| panic!(\"Failed to download file: {url}\"));\n    let mut file =\n        File::create(path).unwrap_or_else(|_| panic!(\"Failed to create file: {}\", path.display()));\n\n    resp.copy_to(&mut file)?;\n    Ok(())\n}\n\nfn create_arkzkey(path: PathBuf) -> Result<PathBuf> {\n    let mut ark_zkey_path = path.clone();\n    ark_zkey_path.set_extension(\"arkzkey\");\n\n    let (original_proving_key, original_constraint_matrices) =\n        semaphore_rs_ark_zkey::read_proving_key_and_matrices_from_zkey(\n            path.to_str().expect(\"Failed to convert path.\"),\n        )?;\n\n    semaphore_rs_ark_zkey::convert_zkey(\n        original_proving_key,\n        original_constraint_matrices,\n        ark_zkey_path.to_str().unwrap(),\n    )?;\n\n    Ok(ark_zkey_path)\n}\n\nfn build_circuit(depth: usize) -> Result<()> {\n    let out_dir = std::env::var(\"OUT_DIR\").expect(\"Missing out dir var\");\n    let base_path = Path::new(&out_dir).join(SEMAPHORE_FILES_PATH);\n\n    if !base_path.exists() {\n        create_dir_all(&base_path)?;\n    }\n\n    let depth_str = depth.to_string();\n    let depth_subfolder = base_path.join(&depth_str);\n    if !Path::new(&depth_subfolder).exists() {\n        create_dir(&depth_subfolder)?;\n    }\n\n    let filename = \"semaphore\";\n    let download_url = format!(\"{SEMAPHORE_DOWNLOAD_URL}/{depth_str}/{filename}.zkey\");\n    let path = Path::new(&depth_subfolder).join(format!(\"{filename}.zkey\"));\n    download_and_store_binary(&download_url, &path)?;\n    create_arkzkey(path)?;\n\n    let ark_zkey_path = Path::new(&depth_subfolder).join(format!(\"{filename}.arkzkey\"));\n\n    // Compute absolute paths\n    let arkzkey_file = absolute(ark_zkey_path)?;\n    let graph_file = absolute(\n        Path::new(\"graphs\")\n            .join(depth.to_string())\n            .join(\"graph.bin\"),\n    )?;\n\n    println!(\"graph_file = {}\", graph_file.display());\n\n    assert!(arkzkey_file.exists());\n    assert!(graph_file.exists());\n\n    // Export generated paths\n    println!(\n        \"cargo:rustc-env=BUILD_RS_ARKZKEY_FILE_{}={}\",\n        depth,\n        arkzkey_file.display()\n    );\n    println!(\n        \"cargo:rustc-env=BUILD_RS_GRAPH_FILE_{}={}\",\n        depth,\n        graph_file.display()\n    );\n\n    Ok(())\n}\n\nfn main() -> Result<()> {\n    // We don't build the circuit for `docs.rs`, as the docs.rs build doesn't have network access.\n    if std::env::var(\"DOCS_RS\").is_ok() {\n        println!(\"building for docs.rs, skipping Semaphore circuit builds\");\n        return Ok(());\n    }\n\n    for depth in semaphore_rs_depth_config::get_supported_depths() {\n        build_circuit(*depth)?;\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "crates/semaphore/examples/abort/main.rs",
    "content": "use color_eyre::Result;\nuse itertools::Itertools;\nuse rand::Rng;\nuse ruint::aliases::U256;\nuse semaphore_rs_hasher::Hasher;\nuse semaphore_rs_poseidon::Poseidon;\nuse semaphore_rs_storage::MmapVec;\nuse semaphore_rs_trees::cascading::CascadingMerkleTree;\nuse semaphore_rs_trees::lazy::LazyMerkleTree;\nuse std::{env, process::Stdio};\n\nstatic FILE_PATH: &str = \"target/debug/examples/abort.mmap\";\nstatic BIN_PATH: &str = \"target/debug/examples/abort\";\nstatic ITERATIONS: usize = 20;\nstatic INITIAL_LEAVES: usize = 10;\n\n/// A test that interupts writes to the mmap merkle trees\n/// to simulate a crash, and to check if restoring the tree\n/// is successful\n///\n/// Run this binary with no arguments to run the tests\n/// `RUSTFLAGS=\"-C panic=abort\" cargo run --example abort`\n#[tokio::main]\nasync fn main() -> Result<()> {\n    let args: Vec<String> = env::args().collect();\n\n    // initialize\n    if args.len() == 1 {\n        run()?;\n    } else if args.len() == 2 && args[1] == \"cascade_restore\" {\n        cascade_restore()?;\n    } else if args.len() == 2 && args[1] == \"cascade_init\" {\n        cascade_init()?;\n    } else if args.len() == 2 && args[1] == \"lazy_restore\" {\n        lazy_restore()?;\n    } else if args.len() == 2 && args[1] == \"lazy_init\" {\n        lazy_init()?;\n    } else {\n        panic!(\"invalid arguments\");\n    }\n\n    Ok(())\n}\n\nfn run() -> Result<()> {\n    let cascade_failures = run_test(\"cascade\")?;\n    let lazy_failures = run_test(\"lazy\")?;\n\n    println!(\"\\nAll Tests Complete!\");\n    println!(\"Cascade failure rate: {cascade_failures}/{ITERATIONS}\");\n    println!(\"Lazy failure rate: {lazy_failures}/{ITERATIONS}\");\n\n    Ok(())\n}\n\nfn run_test(prefix: &str) -> Result<u32> {\n    let mut failures = 0u32;\n    println!(\"Running {prefix} test\");\n    for i in 0..ITERATIONS {\n        println!(\"\\n{prefix} run #{i}\");\n        let output = std::process::Command::new(BIN_PATH)\n            .arg(format!(\"{prefix}_init\"))\n            .stdout(Stdio::piped())\n            .output()?;\n        let stdout = String::from_utf8(output.stdout)?;\n        print!(\"{}\", stdout);\n        let stderr = String::from_utf8(output.stderr)?;\n        print!(\"{}\", stderr);\n\n        let output = std::process::Command::new(BIN_PATH)\n            .arg(format!(\"{prefix}_restore\"))\n            .stdout(Stdio::piped())\n            .output()?;\n        let stdout = String::from_utf8(output.stdout)?;\n        print!(\"{}\", stdout);\n        let stderr = String::from_utf8(output.stderr)?;\n        if !stderr.is_empty() {\n            print!(\"{}\", stderr);\n            failures += 1;\n        }\n    }\n\n    println!(\"\\n{prefix} test complete\");\n    Ok(failures)\n}\n\nfn cascade_init() -> Result<()> {\n    let mmap_vec: MmapVec<<Poseidon as Hasher>::Hash> =\n        unsafe { MmapVec::create_from_path(FILE_PATH)? };\n\n    let leaves = vec![Default::default(); INITIAL_LEAVES];\n\n    let mut tree = CascadingMerkleTree::<Poseidon, _>::new_with_leaves(\n        mmap_vec,\n        30,\n        &Default::default(),\n        &leaves,\n    );\n\n    let _handle = tokio::spawn(async move {\n        for _ in 0..15 {\n            tree.push(U256::from(2)).unwrap();\n        }\n    });\n\n    let mut rng = rand::thread_rng();\n    let millis: u64 = rng.gen_range(0..50);\n    std::thread::sleep(std::time::Duration::from_millis(millis));\n\n    panic!(\"\");\n}\n\nfn cascade_restore() -> Result<()> {\n    let file = std::fs::OpenOptions::new()\n        .read(true)\n        .write(true)\n        .open(FILE_PATH)?;\n\n    let mmap_vec: MmapVec<<Poseidon as Hasher>::Hash> = unsafe { MmapVec::restore(file)? };\n    let tree = CascadingMerkleTree::<Poseidon, _>::restore(mmap_vec, 30, &Default::default())?;\n    println!(\"tree length: {}\", tree.num_leaves());\n    tree.validate()?;\n\n    Ok(())\n}\n\nfn lazy_init() -> Result<()> {\n    let leaves = vec![Default::default(); INITIAL_LEAVES];\n\n    let mut tree = LazyMerkleTree::<Poseidon>::new_mmapped_with_dense_prefix_with_init_values(\n        30,\n        13,\n        &Default::default(),\n        &leaves,\n        FILE_PATH,\n    )?;\n\n    let _handle = std::thread::spawn(move || {\n        for i in INITIAL_LEAVES..(INITIAL_LEAVES + 15) {\n            tree = tree.update_with_mutation(i, &U256::from(2));\n        }\n    });\n\n    let mut rng = rand::thread_rng();\n    let millis: u64 = rng.gen_range(0..50);\n    std::thread::sleep(std::time::Duration::from_millis(millis));\n\n    panic!(\"\");\n}\n\nfn lazy_restore() -> Result<()> {\n    let tree = LazyMerkleTree::<Poseidon>::attempt_dense_mmap_restore(\n        30,\n        13,\n        &Default::default(),\n        FILE_PATH,\n    )?;\n\n    let leaves = tree.leaves().take(20).collect_vec();\n    println!(\"tree length: {leaves:?}\");\n    Ok(())\n}\n"
  },
  {
    "path": "crates/semaphore/src/circuit.rs",
    "content": "#![allow(unused)]\n\nuse ark_bn254::{Bn254, Fr};\nuse ark_groth16::ProvingKey;\nuse ark_relations::r1cs::ConstraintMatrices;\nuse once_cell::sync::Lazy;\nuse semaphore_rs_depth_config::{get_depth_index, get_supported_depth_count};\nuse semaphore_rs_depth_macros::array_for_depths;\n\nconst ZKEY_BYTES: [&[u8]; get_supported_depth_count()] =\n    array_for_depths!(|depth| include_bytes!(env!(concat!(\"BUILD_RS_ARKZKEY_FILE_\", depth))));\n\nconst GRAPH_BYTES: [&[u8]; get_supported_depth_count()] =\n    array_for_depths!(|depth| include_bytes!(env!(concat!(\"BUILD_RS_GRAPH_FILE_\", depth))));\n\nstatic ZKEY: [Lazy<(ProvingKey<Bn254>, ConstraintMatrices<Fr>)>; get_supported_depth_count()] =\n    array_for_depths!(|depth| Lazy::new(|| {\n        semaphore_rs_ark_zkey::read_arkzkey_from_bytes(ZKEY_BYTES[get_depth_index(depth).unwrap()])\n            .expect(\"zkey should be valid\")\n    }));\n\n#[must_use]\npub fn zkey(depth: usize) -> &'static (ProvingKey<Bn254>, ConstraintMatrices<Fr>) {\n    let index = get_depth_index(depth).unwrap_or_else(|| panic!(\"depth {depth} is not supported\"));\n    &ZKEY[index]\n}\n\n#[must_use]\npub fn graph(depth: usize) -> &'static [u8] {\n    let index = get_depth_index(depth).unwrap_or_else(|| panic!(\"depth {depth} is not supported\"));\n\n    GRAPH_BYTES[index]\n}\n"
  },
  {
    "path": "crates/semaphore/src/field.rs",
    "content": "use ruint::{aliases::U256, uint};\nuse semaphore_rs_utils::keccak256;\n\n/// An element of the BN254 scalar field Fr.\n///\n/// Represented as a big-endian byte vector without Montgomery reduction.\n// TODO: Make sure value is always reduced.\npub type Field = U256;\n\n// See <https://docs.rs/ark-bn254/latest/ark_bn254>\npub const MODULUS: Field =\n    uint!(21888242871839275222246405745257275088548364400416034343698204186575808495617_U256);\n\n/// Hash arbitrary data to a field element.\n///\n/// This is used to create `signal_hash` and `external_nullifier_hash`.\n#[must_use]\n#[allow(clippy::module_name_repetitions)]\n#[allow(clippy::missing_panics_doc)]\npub fn hash_to_field(data: &[u8]) -> Field {\n    // Never panics because the target uint is large enough.\n    let n = U256::try_from_be_slice(&keccak256(data)).unwrap();\n    // Shift right one byte to make it fit in the field\n    n >> 8\n}\n"
  },
  {
    "path": "crates/semaphore/src/hash.rs",
    "content": "use core::{\n    fmt::{Debug, Display},\n    str,\n    str::FromStr,\n};\nuse num_bigint::{BigInt, Sign};\nuse ruint::aliases::U256;\nuse semaphore_rs_utils::{bytes_from_hex, bytes_to_hex, deserialize_bytes, serialize_bytes};\nuse serde::{Deserialize, Deserializer, Serialize, Serializer};\n\n/// Container for 256-bit hash values.\n#[derive(Clone, Copy, PartialEq, Eq, Default)]\npub struct Hash(pub [u8; 32]);\n\nimpl Hash {\n    #[must_use]\n    pub const fn from_bytes_be(bytes: [u8; 32]) -> Self {\n        Self(bytes)\n    }\n\n    #[must_use]\n    pub const fn as_bytes_be(&self) -> &[u8; 32] {\n        &self.0\n    }\n}\n\n/// Conversion from U256\nimpl From<&Hash> for U256 {\n    fn from(hash: &Hash) -> Self {\n        Self::from_be_bytes(*hash.as_bytes_be())\n    }\n}\n\n/// Conversion to U256\nimpl From<U256> for Hash {\n    fn from(u256: U256) -> Self {\n        Self::from_bytes_be(u256.to_be_bytes::<32>())\n    }\n}\n\n/// Conversion from vec\nimpl From<Vec<u8>> for Hash {\n    fn from(vec: Vec<u8>) -> Self {\n        let mut bytes = [0_u8; 32];\n        bytes.copy_from_slice(&vec[0..32]);\n        Self::from_bytes_be(bytes)\n    }\n}\n\n/// Conversion to `BigInt`\nimpl From<Hash> for BigInt {\n    fn from(hash: Hash) -> Self {\n        Self::from_bytes_be(Sign::Plus, hash.as_bytes_be())\n    }\n}\n\nimpl From<&Hash> for BigInt {\n    fn from(hash: &Hash) -> Self {\n        Self::from_bytes_be(Sign::Plus, hash.as_bytes_be())\n    }\n}\n\nimpl Debug for Hash {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let hex = bytes_to_hex::<32, 66>(&self.0);\n        let hex_str = str::from_utf8(&hex).expect(\"hex is always valid utf8\");\n        write!(f, \"Field({hex_str})\")\n    }\n}\n\nimpl Display for Hash {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let hex = bytes_to_hex::<32, 66>(&self.0);\n        let hex_str = str::from_utf8(&hex).expect(\"hex is always valid utf8\");\n        write!(f, \"{hex_str}\")\n    }\n}\n\n/// Parse Hash from hex string.\n/// Hex strings can be upper/lower/mixed case and have an optional `0x` prefix\n/// but they must always be exactly 32 bytes.\nimpl FromStr for Hash {\n    type Err = hex::FromHexError;\n\n    fn from_str(s: &str) -> Result<Self, Self::Err> {\n        bytes_from_hex::<32>(s).map(Self)\n    }\n}\n\n/// Serialize hashes into human readable hex strings or byte arrays.\n/// Hex strings are lower case without prefix and always 32 bytes.\nimpl Serialize for Hash {\n    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {\n        serialize_bytes::<32, 66, S>(serializer, &self.0)\n    }\n}\n\n/// Deserialize human readable hex strings or byte arrays into hashes.\n/// Hex strings can be upper/lower/mixed case and have an optional `0x` prefix\n/// but they must always be exactly 32 bytes.\nimpl<'de> Deserialize<'de> for Hash {\n    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {\n        let bytes = deserialize_bytes::<32, _>(deserializer)?;\n        Ok(Self(bytes))\n    }\n}\n\n#[cfg(test)]\npub mod test {\n    use super::*;\n    use hex_literal::hex;\n    use serde_json::{from_str, to_string};\n\n    #[test]\n    fn test_serialize() {\n        let hash = Hash([0; 32]);\n        assert_eq!(\n            to_string(&hash).unwrap(),\n            \"\\\"0x0000000000000000000000000000000000000000000000000000000000000000\\\"\"\n        );\n        let hash = Hash(hex!(\n            \"1c4823575d154474ee3e5ac838d002456a815181437afd14f126da58a9912bbe\"\n        ));\n        assert_eq!(\n            to_string(&hash).unwrap(),\n            \"\\\"0x1c4823575d154474ee3e5ac838d002456a815181437afd14f126da58a9912bbe\\\"\"\n        );\n    }\n\n    #[test]\n    fn test_deserialize() {\n        assert_eq!(\n            from_str::<Hash>(\n                \"\\\"0x1c4823575d154474ee3e5ac838d002456a815181437afd14f126da58a9912bbe\\\"\"\n            )\n            .unwrap(),\n            Hash(hex!(\n                \"1c4823575d154474ee3e5ac838d002456a815181437afd14f126da58a9912bbe\"\n            ))\n        );\n        assert_eq!(\n            from_str::<Hash>(\n                \"\\\"0X1C4823575d154474EE3e5ac838d002456a815181437afd14f126da58a9912bbe\\\"\"\n            )\n            .unwrap(),\n            Hash(hex!(\n                \"1c4823575d154474ee3e5ac838d002456a815181437afd14f126da58a9912bbe\"\n            ))\n        );\n    }\n}\n"
  },
  {
    "path": "crates/semaphore/src/identity.rs",
    "content": "use sha2::{Digest, Sha256};\nuse zeroize::Zeroize;\n\nuse crate::field::MODULUS;\nuse crate::Field;\n\n#[derive(Clone, PartialEq, Eq, Debug)]\npub struct Identity {\n    pub trapdoor: Field,\n    pub nullifier: Field,\n}\n\n/// Implements the private key derivation function from zk-kit.\n///\n/// See <https://github.com/appliedzkp/zk-kit/blob/1ea410456fc2b95877efa7c671bc390ffbfb5d36/packages/identity/src/identity.ts#L58>\nfn derive_field(seed_hex: &[u8; 64], suffix: &[u8]) -> Field {\n    let mut hasher = Sha256::new();\n    hasher.update(seed_hex);\n    hasher.update(suffix);\n    Field::try_from_be_slice(hasher.finalize().as_ref()).unwrap() % MODULUS\n}\n\npub fn seed_hex(seed: &[u8]) -> [u8; 64] {\n    let mut hasher = Sha256::new();\n    hasher.update(seed);\n    let bytes: [u8; 32] = hasher.finalize().into();\n    let mut result = [0_u8; 64];\n    hex::encode_to_slice(bytes, &mut result[..]).expect(\"output buffer is correctly sized\");\n    result\n}\n\nimpl Identity {\n    #[must_use]\n    #[deprecated(since = \"0.2.0\", note = \"please use `from_secret` instead\")]\n    pub fn from_seed(seed: &[u8]) -> Self {\n        let seed_hex = seed_hex(seed);\n        Self {\n            trapdoor: derive_field(&seed_hex, b\"identity_trapdoor\"),\n            nullifier: derive_field(&seed_hex, b\"identity_nullifier\"),\n        }\n    }\n\n    #[must_use]\n    pub fn from_secret(secret: &mut [u8], trapdoor_seed: Option<&[u8]>) -> Self {\n        let mut secret_hex = seed_hex(secret);\n        secret.zeroize();\n\n        Self::from_hashed_secret(&mut secret_hex, trapdoor_seed)\n    }\n\n    #[must_use]\n    pub fn from_hashed_secret(secret_hex: &mut [u8; 64], trapdoor_seed: Option<&[u8]>) -> Self {\n        let identity = Self {\n            trapdoor: derive_field(secret_hex, trapdoor_seed.unwrap_or(b\"identity_trapdoor\")),\n            nullifier: derive_field(secret_hex, b\"identity_nullifier\"),\n        };\n        secret_hex.zeroize();\n        identity\n    }\n\n    #[must_use]\n    pub fn secret_hash(&self) -> Field {\n        semaphore_rs_poseidon::poseidon::hash2(self.nullifier, self.trapdoor)\n    }\n\n    #[must_use]\n    pub fn commitment(&self) -> Field {\n        semaphore_rs_poseidon::poseidon::hash1(self.secret_hash())\n    }\n}\n"
  },
  {
    "path": "crates/semaphore/src/lib.rs",
    "content": "#![doc = include_str!(\"../README.md\")]\n\nmod circuit;\nmod field;\npub mod hash;\npub mod identity;\npub mod packed_proof;\npub mod poseidon_tree;\npub mod protocol;\n\npub use semaphore_rs_depth_config::get_supported_depths;\n\n// Export types\npub use crate::field::{hash_to_field, Field, MODULUS};\n\n#[allow(dead_code)]\n#[cfg(test)]\nmod test {\n    use std::thread::spawn;\n\n    use semaphore_rs_depth_macros::test_all_depths;\n\n    use crate::identity::Identity;\n    use crate::poseidon_tree::LazyPoseidonTree;\n    use crate::protocol::{generate_nullifier_hash, generate_proof, verify_proof};\n    use crate::{hash_to_field, protocol, Field};\n\n    #[test]\n    fn test_field_serde() {\n        let value = Field::from(0x1234_5678);\n        let serialized = serde_json::to_value(value).unwrap();\n        let deserialized: Field = serde_json::from_value(serialized).unwrap();\n        assert_eq!(value, deserialized);\n    }\n\n    fn test_end_to_end(\n        identity: &mut [u8],\n        external_nullifier: &[u8],\n        signal: &[u8],\n        depth: usize,\n    ) {\n        let leaf = Field::from(0);\n\n        // generate identity\n        let id = Identity::from_secret(identity, None);\n\n        // generate merkle tree\n        let mut tree = LazyPoseidonTree::new(depth, leaf).derived();\n        tree = tree.update(0, &id.commitment());\n\n        let merkle_proof = tree.proof(0);\n        let root = tree.root();\n\n        let signal_hash = hash_to_field(signal);\n        let external_nullifier_hash = hash_to_field(external_nullifier);\n        let nullifier_hash = generate_nullifier_hash(&id, external_nullifier_hash);\n\n        let proof =\n            generate_proof(&id, &merkle_proof, external_nullifier_hash, signal_hash).unwrap();\n\n        for _ in 0..5 {\n            let success = verify_proof(\n                root,\n                nullifier_hash,\n                signal_hash,\n                external_nullifier_hash,\n                &proof,\n                depth,\n            )\n            .unwrap();\n            assert!(success);\n        }\n    }\n\n    #[test_all_depths]\n    fn test_auth_flow(depth: usize) {\n        let mut secret = *b\"oh so secret\";\n        let id = Identity::from_secret(&mut secret[..], None);\n        let signal_hash = hash_to_field(b\"signal\");\n        let external_nullifier_hash = hash_to_field(b\"appId\");\n        let nullifier_hash = generate_nullifier_hash(&id, external_nullifier_hash);\n        let id_commitment = id.commitment();\n\n        let proof = protocol::authentication::generate_proof(\n            depth,\n            &id,\n            external_nullifier_hash,\n            signal_hash,\n        )\n        .unwrap();\n\n        let success = protocol::authentication::verify_proof(\n            depth,\n            id_commitment,\n            nullifier_hash,\n            signal_hash,\n            external_nullifier_hash,\n            &proof,\n        )\n        .unwrap();\n        assert!(success);\n    }\n\n    #[test_all_depths]\n    fn test_single_impl(depth: usize) {\n        // Note that rust will still run tests in parallel\n        let mut hello = *b\"hello\";\n        test_end_to_end(&mut hello, b\"appId\", b\"xxx\", depth);\n    }\n\n    #[test_all_depths]\n    fn test_parallel_impl(depth: usize) {\n        // Note that this does not guarantee a concurrency issue will be detected.\n        // For that we need much more sophisticated static analysis tooling like\n        // loom. See <https://github.com/tokio-rs/loom>\n        let mut a_id = *b\"hello\";\n        let mut b_id = *b\"secret\";\n        let a = spawn(move || test_end_to_end(&mut a_id, b\"appId\", b\"xxx\", depth));\n        let b = spawn(move || test_end_to_end(&mut b_id, b\"test\", b\"signal\", depth));\n        a.join().unwrap();\n        b.join().unwrap();\n    }\n}\n"
  },
  {
    "path": "crates/semaphore/src/packed_proof.rs",
    "content": "// Re-export for backwards compatibility\npub use semaphore_rs_proof::packing::PackedProof;\n"
  },
  {
    "path": "crates/semaphore/src/poseidon_tree.rs",
    "content": "use semaphore_rs_hasher::Hasher;\nuse semaphore_rs_poseidon::Poseidon;\nuse semaphore_rs_trees::imt::MerkleTree;\n#[cfg(not(target_arch = \"wasm32\"))]\nuse semaphore_rs_trees::lazy::LazyMerkleTree;\n\npub type PoseidonTree = MerkleTree<Poseidon>;\n#[cfg(not(target_arch = \"wasm32\"))]\npub type LazyPoseidonTree = LazyMerkleTree<Poseidon>;\npub type Branch = semaphore_rs_trees::Branch<<Poseidon as Hasher>::Hash>;\npub type Proof = semaphore_rs_trees::InclusionProof<Poseidon>;\n"
  },
  {
    "path": "crates/semaphore/src/protocol/authentication.rs",
    "content": "use crate::{\n    identity::Identity,\n    protocol::{Proof, ProofError},\n    Field,\n};\nuse semaphore_rs_poseidon::poseidon::hash2;\nuse semaphore_rs_trees::{Branch, InclusionProof};\n\nfn empty_hashes(depth: usize) -> Vec<Field> {\n    let mut empty = Vec::with_capacity(depth);\n    let mut hash = Field::from(0);\n\n    for _ in 0..depth {\n        empty.push(hash);\n        hash = hash2(hash, hash);\n    }\n\n    empty\n}\n\nfn authentication_merkle_proof(depth: usize) -> InclusionProof<semaphore_rs_poseidon::Poseidon> {\n    InclusionProof(empty_hashes(depth).into_iter().map(Branch::Left).collect())\n}\n\nfn authentication_root(depth: usize, id_commitment: Field) -> Field {\n    empty_hashes(depth).into_iter().fold(id_commitment, hash2)\n}\n\npub fn generate_proof(\n    depth: usize,\n    identity: &Identity,\n    ext_nullifier_hash: Field,\n    signal_hash: Field,\n) -> Result<Proof, ProofError> {\n    let merkle_proof = authentication_merkle_proof(depth);\n    super::generate_proof(identity, &merkle_proof, ext_nullifier_hash, signal_hash)\n}\n\npub fn verify_proof(\n    depth: usize,\n    id_commitment: Field,\n    nullifier_hash: Field,\n    signal_hash: Field,\n    ext_nullifier_hash: Field,\n    proof: &Proof,\n) -> Result<bool, ProofError> {\n    let root = authentication_root(depth, id_commitment);\n    super::verify_proof(\n        root,\n        nullifier_hash,\n        signal_hash,\n        ext_nullifier_hash,\n        proof,\n        depth,\n    )\n}\n\n#[cfg(test)]\nmod tests {\n    use semaphore_rs_depth_macros::test_all_depths;\n\n    use crate::{hash_to_field, identity::Identity, protocol::generate_nullifier_hash};\n\n    use super::*;\n\n    /// Validates the generate/verify round-trip for the authentication API.\n    #[test_all_depths]\n    fn test_round_trip(depth: usize) {\n        let mut secret = *b\"test secret seed\";\n        let id = Identity::from_secret(&mut secret, None);\n\n        let signal_hash = hash_to_field(b\"signal\");\n        let external_nullifier_hash = hash_to_field(b\"app_id\");\n        let nullifier_hash = generate_nullifier_hash(&id, external_nullifier_hash);\n\n        let proof = generate_proof(depth, &id, external_nullifier_hash, signal_hash)\n            .expect(\"proof generation should succeed\");\n\n        let valid = verify_proof(\n            depth,\n            id.commitment(),\n            nullifier_hash,\n            signal_hash,\n            external_nullifier_hash,\n            &proof,\n        )\n        .expect(\"proof verification should succeed\");\n\n        assert!(valid);\n    }\n}\n"
  },
  {
    "path": "crates/semaphore/src/protocol/mod.rs",
    "content": "use std::collections::HashMap;\n\nuse ark_bn254::Fr;\nuse ark_ff::PrimeField;\nuse ark_groth16::{prepare_verifying_key, Groth16};\nuse ark_relations::r1cs::SynthesisError;\nuse ark_std::UniformRand;\nuse color_eyre::Result;\nuse once_cell::sync::Lazy;\nuse rand::{thread_rng, Rng};\nuse semaphore_rs_ark_circom::ethereum::AffineError;\nuse semaphore_rs_ark_circom::CircomReduction;\nuse semaphore_rs_depth_config::{get_depth_index, get_supported_depth_count};\nuse semaphore_rs_depth_macros::array_for_depths;\nuse semaphore_rs_poseidon::Poseidon;\nuse semaphore_rs_trees::{Branch, InclusionProof};\nuse semaphore_rs_witness::Graph;\nuse thiserror::Error;\n\nuse crate::circuit::zkey;\nuse crate::identity::Identity;\nuse crate::Field;\n\npub use semaphore_rs_proof::compression;\npub use semaphore_rs_proof::Proof;\n\npub mod authentication;\n\nstatic WITHESS_GRAPH: [Lazy<Graph>; get_supported_depth_count()] = array_for_depths!(|depth| {\n    Lazy::new(|| {\n        semaphore_rs_witness::init_graph(crate::circuit::graph(depth))\n            .expect(\"Failed to initialize Graph\")\n    })\n});\n\n/// Preloads the ZKEY in memory to skip the lazy loading at first verification\npub fn warmup_for_verification(tree_depth: usize) {\n    let _zkey = zkey(tree_depth);\n}\n\n/// Helper to merkle proof into a bigint vector\n/// TODO: we should create a From trait for this\nfn merkle_proof_to_vec(proof: &InclusionProof<Poseidon>) -> Vec<Field> {\n    proof\n        .0\n        .iter()\n        .map(|x| match x {\n            Branch::Left(value) | Branch::Right(value) => *value,\n        })\n        .collect()\n}\n\n/// Generates the nullifier hash\n#[must_use]\npub fn generate_nullifier_hash(identity: &Identity, external_nullifier: Field) -> Field {\n    semaphore_rs_poseidon::poseidon::hash2(external_nullifier, identity.nullifier)\n}\n\n#[derive(Error, Debug)]\npub enum ProofError {\n    #[error(\"Error reading circuit key: {0}\")]\n    CircuitKeyError(#[from] std::io::Error),\n    #[error(\"Error producing witness: {0}\")]\n    WitnessError(color_eyre::Report),\n    #[error(\"Error producing proof: {0}\")]\n    SynthesisError(#[from] SynthesisError),\n    #[error(\"Error converting public input: {0}\")]\n    ToFieldError(#[from] ruint::ToFieldError),\n    #[error(transparent)]\n    G1AffineError(#[from] AffineError),\n}\n\n/// Generates a semaphore proof\n///\n/// # Errors\n///\n/// Returns a [`ProofError`] if proving fails.\npub fn generate_proof(\n    identity: &Identity,\n    merkle_proof: &InclusionProof<Poseidon>,\n    external_nullifier_hash: Field,\n    signal_hash: Field,\n) -> Result<Proof, ProofError> {\n    generate_proof_rng(\n        identity,\n        merkle_proof,\n        external_nullifier_hash,\n        signal_hash,\n        &mut thread_rng(),\n    )\n}\n\n/// Generates a semaphore proof from entropy\n///\n/// # Errors\n///\n/// Returns a [`ProofError`] if proving fails.\npub fn generate_proof_rng(\n    identity: &Identity,\n    merkle_proof: &InclusionProof<Poseidon>,\n    external_nullifier_hash: Field,\n    signal_hash: Field,\n    rng: &mut impl Rng,\n) -> Result<Proof, ProofError> {\n    generate_proof_rs(\n        identity,\n        merkle_proof,\n        external_nullifier_hash,\n        signal_hash,\n        ark_bn254::Fr::rand(rng),\n        ark_bn254::Fr::rand(rng),\n    )\n}\n\nfn generate_proof_rs(\n    identity: &Identity,\n    merkle_proof: &InclusionProof<Poseidon>,\n    external_nullifier_hash: Field,\n    signal_hash: Field,\n    r: ark_bn254::Fr,\n    s: ark_bn254::Fr,\n) -> Result<Proof, ProofError> {\n    let depth = merkle_proof.0.len();\n    let full_assignment =\n        generate_witness(identity, merkle_proof, external_nullifier_hash, signal_hash);\n\n    let zkey = zkey(depth);\n    let ark_proof = Groth16::<_, CircomReduction>::create_proof_with_reduction_and_matrices(\n        &zkey.0,\n        r,\n        s,\n        &zkey.1,\n        zkey.1.num_instance_variables,\n        zkey.1.num_constraints,\n        full_assignment.as_slice(),\n    )?;\n    let proof = ark_proof.into();\n\n    Ok(proof)\n}\n\npub fn generate_witness(\n    identity: &Identity,\n    merkle_proof: &InclusionProof<Poseidon>,\n    external_nullifier_hash: Field,\n    signal_hash: Field,\n) -> Vec<Fr> {\n    let depth = merkle_proof.0.len();\n    let inputs = HashMap::from([\n        (\"identityNullifier\".to_owned(), vec![identity.nullifier]),\n        (\"identityTrapdoor\".to_owned(), vec![identity.trapdoor]),\n        (\"treePathIndices\".to_owned(), path_index(merkle_proof)),\n        (\"treeSiblings\".to_owned(), merkle_proof_to_vec(merkle_proof)),\n        (\n            \"externalNullifier\".to_owned(),\n            vec![external_nullifier_hash],\n        ),\n        (\"signalHash\".to_owned(), vec![signal_hash]),\n    ]);\n\n    let graph = &WITHESS_GRAPH\n        [get_depth_index(depth).unwrap_or_else(|| panic!(\"Depth {depth} not supported\"))];\n\n    let witness = semaphore_rs_witness::calculate_witness(inputs, graph).unwrap();\n    witness\n        .into_iter()\n        .map(|x| Fr::from_bigint(x.into()).expect(\"Couldn't cast U256 to BigInteger\"))\n        .collect::<Vec<_>>()\n}\n\n/// Compute path index\n#[must_use]\npub fn path_index(proof: &InclusionProof<Poseidon>) -> Vec<Field> {\n    proof\n        .0\n        .iter()\n        .map(|branch| match branch {\n            Branch::Left(_) => Field::from(0),\n            Branch::Right(_) => Field::from(1),\n        })\n        .collect()\n}\n\n/// Verifies a given semaphore proof\n///\n/// # Errors\n///\n/// Returns a [`ProofError`] if verifying fails. Verification failure does not\n/// necessarily mean the proof is incorrect.\npub fn verify_proof(\n    root: Field,\n    nullifier_hash: Field,\n    signal_hash: Field,\n    external_nullifier_hash: Field,\n    proof: &Proof,\n    tree_depth: usize,\n) -> Result<bool, ProofError> {\n    let zkey = zkey(tree_depth);\n    let pvk = prepare_verifying_key(&zkey.0.vk);\n\n    let public_inputs = [root, nullifier_hash, signal_hash, external_nullifier_hash]\n        .iter()\n        .map(ark_bn254::Fr::try_from)\n        .collect::<Result<Vec<_>, _>>()?;\n\n    let ark_proof = (*proof).try_into()?;\n    let result = Groth16::<_, CircomReduction>::verify_proof(&pvk, &ark_proof, &public_inputs[..])?;\n    Ok(result)\n}\n\n#[cfg(test)]\n#[allow(dead_code)]\nmod test {\n    use ark_bn254::Config;\n    use ark_ec::bn::Bn;\n    use ark_groth16::Proof as ArkProof;\n    use rand::SeedableRng as _;\n    use rand_chacha::ChaChaRng;\n    use semaphore_rs_depth_macros::test_all_depths;\n    use serde_json::json;\n\n    use super::*;\n    use crate::hash_to_field;\n    use crate::poseidon_tree::LazyPoseidonTree;\n\n    fn arb_proof(seed: u64, depth: usize) -> Proof {\n        // Deterministic randomness for testing\n        let mut rng = ChaChaRng::seed_from_u64(seed);\n\n        // generate identity\n        let mut seed: [u8; 16] = rng.gen();\n        let id = Identity::from_secret(seed.as_mut(), None);\n\n        // generate merkle tree\n        let leaf = Field::from(0);\n        let mut tree = LazyPoseidonTree::new(depth, leaf).derived();\n        tree = tree.update(0, &id.commitment());\n\n        let merkle_proof = tree.proof(0);\n\n        let external_nullifier: [u8; 16] = rng.gen();\n        let external_nullifier_hash = hash_to_field(&external_nullifier);\n\n        let signal: [u8; 16] = rng.gen();\n        let signal_hash = hash_to_field(&signal);\n\n        generate_proof_rng(\n            &id,\n            &merkle_proof,\n            external_nullifier_hash,\n            signal_hash,\n            &mut rng,\n        )\n        .unwrap()\n    }\n\n    #[test_all_depths]\n    fn test_proof_cast_roundtrip(depth: usize) {\n        let proof = arb_proof(123, depth);\n        let ark_proof: ArkProof<Bn<Config>> = proof.try_into().unwrap();\n        let result: Proof = ark_proof.into();\n        assert_eq!(proof, result);\n    }\n\n    #[test_all_depths]\n    fn test_proof_serialize(depth: usize) {\n        let proof = arb_proof(456, depth);\n        let json = serde_json::to_value(proof).unwrap();\n        let valid_values = match depth {\n            16 => json!([\n                [\n                    \"0xe4267974945a50a541e90a399ed9211752216a3e4e1cefab1f0bcd8925ea56e\",\n                    \"0xdd9ada36c50d3f1bf75abe5c5ad7d0a29355b74fc3f604aa108b8886a6ac7f8\"\n                ],\n                [\n                    [\n                        \"0x1621577ad2f90fe2e7ec6f675751693515c3b7e91ee228f1db47fe3aba7c0450\",\n                        \"0x2b07bc915b377f8c7126c2d46636632cdbcb426b446a06edf3320939ee4e1911\"\n                    ],\n                    [\n                        \"0xf40e93e057c7521720448b3d443eac36ff48705312181c41bd78981923be41a\",\n                        \"0x9ce138011687b44a08b979a85b3b122e7335254a02d4fbae7b38b57653c7eb0\"\n                    ]\n                ],\n                [\n                    \"0x295b30c0c025a2b176de1220acdb5f95119a8938689d73076f02bb6d01601fbb\",\n                    \"0xc71250468b955584be8769b047f79614df1176a7a64683f14c27889d47e614\"\n                ]\n            ]),\n            20 => json!([\n                [\n                    \"0x2296e314c88daf893769f4ed0cad8a7f584b39db6ebd4bba230591b5d78f48b3\",\n                    \"0x2e5d33bf993b8e4aba7c06ee82ff7dd674857b491c46f53eda4365ecbf3e5fde\"\n                ],\n                [\n                    [\n                        \"0x277c239fa1cf9e8a7ca65ef09371bee470aad7936583a0b48e60f6a76f17a97c\",\n                        \"0x2b21c607eff04f704e546451dcd27c5f090639074a54b45e345337e09d0ab3d0\"\n                    ],\n                    [\n                        \"0x73fde4daa004ecb853159e54b98cdd204e7874008f91581601881c968607451\",\n                        \"0x171ee4d007b9286d91b581f6d38902e5befc3876b96c71bc178b5f5e8dbf1e40\"\n                    ]\n                ],\n                [\n                    \"0x25afbb8fef95d8481e9e49b4a94848473794447d032fdde2cd73a0d6318b6c3c\",\n                    \"0x2a24e19699e2d8495357cf9b65fb215cebbcda2817b1627758a330e57db5c4b9\"\n                ]\n            ]),\n            30 => json!([\n                [\n                    \"0x19ded61ab5c58fdb12367526c6bc04b9186d0980c4b6fd48a44093e80f9b4206\",\n                    \"0x2e619a034be10e9aab294f1c77a480378e84782c8519449aef0c8f6952382bda\"\n                ],\n                [\n                    [\n                        \"0x2202954c0cdb43dc240d56c3a60d125dbc676f8d97bfeac5987500eb0ff4b9a1\",\n                        \"0x35f5b9d8bfba1341fe9fabef6f46d242e1b22c4006ed3ae3f240f0409b20799\"\n                    ],\n                    [\n                        \"0x13ef645aeaffda30d38c1df68d79d9682d3d002a388e5672fe9b9c7f3224acd7\",\n                        \"0x10a45a9a99cfaf9aef84ab40c5fdad411e800e24471f24ec76addb74b9e041af\"\n                    ]\n                ],\n                [\n                    \"0x1f72d009494e8694cf608c54131e7d565625d59e4637ea77cbf2620c719e8c77\",\n                    \"0x19ee17159b599f6f4b2294d4fb29760d2dc1b58adc0519ce546ad274928f6bc4\"\n                ]\n            ]),\n            _ => panic!(\"unexpected depth: {}\", depth),\n        };\n        assert_eq!(json, valid_values);\n    }\n}\n"
  },
  {
    "path": "crates/semaphore/src/util.rs",
    "content": ""
  },
  {
    "path": "crates/semaphore-depth-config/Cargo.toml",
    "content": "[package]\nname = \"semaphore-rs-depth-config\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nauthors.workspace = true\ndescription.workspace = true\nkeywords.workspace = true\ncategories.workspace = true\n\n[features]\ndepth_16 = []\ndepth_20 = []\ndepth_30 = []\n"
  },
  {
    "path": "crates/semaphore-depth-config/src/lib.rs",
    "content": "#![allow(unused)]\n\npub const fn get_supported_depth_count() -> usize {\n    let mut res = 0;\n    #[cfg(feature = \"depth_16\")]\n    {\n        res += 1;\n    }\n    #[cfg(feature = \"depth_20\")]\n    {\n        res += 1;\n    }\n    #[cfg(feature = \"depth_30\")]\n    {\n        res += 1;\n    }\n    res\n}\n\n#[allow(unused_assignments)]\nconst fn gen_supported_depths() -> [usize; get_supported_depth_count()] {\n    let mut res = [0; get_supported_depth_count()];\n    let mut i = 0;\n    #[cfg(feature = \"depth_16\")]\n    {\n        res[i] = 16;\n        i += 1;\n    }\n    #[cfg(feature = \"depth_20\")]\n    {\n        res[i] = 20;\n        i += 1;\n    }\n    #[cfg(feature = \"depth_30\")]\n    {\n        res[i] = 30;\n        i += 1;\n    }\n    res\n}\n\nstatic SUPPORTED_DEPTHS: [usize; get_supported_depth_count()] = gen_supported_depths();\n\npub fn get_supported_depths() -> &'static [usize] {\n    &SUPPORTED_DEPTHS\n}\n\n#[allow(unused_assignments)]\npub const fn get_depth_index(depth: usize) -> Option<usize> {\n    let mut i = 0;\n\n    #[cfg(feature = \"depth_16\")]\n    {\n        if depth == 16 {\n            return Some(i);\n        }\n        i += 1;\n    }\n    #[cfg(feature = \"depth_20\")]\n    {\n        if depth == 20 {\n            return Some(i);\n        }\n        i += 1;\n    }\n    #[cfg(feature = \"depth_30\")]\n    {\n        if depth == 30 {\n            return Some(i);\n        }\n        i += 1;\n    }\n    None\n}\n"
  },
  {
    "path": "crates/semaphore-depth-macros/Cargo.toml",
    "content": "[package]\nname = \"semaphore-rs-depth-macros\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nauthors.workspace = true\ndescription.workspace = true\nkeywords.workspace = true\ncategories.workspace = true\n\n[features]\ndepth_16 = [\"semaphore-rs-depth-config/depth_16\"]\ndepth_20 = [\"semaphore-rs-depth-config/depth_20\"]\ndepth_30 = [\"semaphore-rs-depth-config/depth_30\"]\n\n[lib]\nproc-macro = true\n\n[dependencies]\nsemaphore-rs-depth-config.workspace = true\nitertools ={ workspace = true }\nsyn.workspace = true\nproc-macro2.workspace = true\nquote.workspace = true\n"
  },
  {
    "path": "crates/semaphore-depth-macros/src/lib.rs",
    "content": "use proc_macro::TokenStream;\nuse quote::{format_ident, quote};\nuse semaphore_rs_depth_config::get_supported_depths;\nuse syn::{\n    parse::{Parse, ParseStream},\n    parse_macro_input, parse_quote,\n    visit_mut::VisitMut,\n    Ident, Token,\n};\n\n/// Multi-depth test generator\n///\n/// This macro is used to generate a test for each supported depth.\n/// It expects to annotate a function with a single argument, and will generate\n/// test cases delegating to that function for each supported depth.\n///\n/// For example,\n/// ```\n/// use semaphore_rs_depth_macros::test_all_depths;\n/// #[test_all_depths]\n/// fn test_depth_non_zero(depth: usize) {\n///     assert!(depth > 0);\n/// }\n/// ```\n/// with `depth_16` and `depth_30` features active will generate the following\n/// code:\n/// ```no_run\n/// fn test_depth_non_zero(depth: usize) {\n///     assert!(depth > 0);\n/// }\n///\n/// #[test]\n/// fn test_depth_non_zero_depth_16() {\n///     test_depth_non_zero(16);\n/// }\n///\n/// #[test]\n/// fn test_depth_non_zero_depth_30() {\n///     test_depth_non_zero(30);\n/// }\n/// ```\n#[proc_macro_attribute]\npub fn test_all_depths(_attr: TokenStream, item: TokenStream) -> TokenStream {\n    let fun = parse_macro_input!(item as syn::ItemFn);\n    let fun_name = &fun.sig.ident;\n\n    let original_fun = quote! { #fun };\n    let mut result = TokenStream::from(original_fun);\n\n    for depth in get_supported_depths() {\n        let fun_name_versioned = format_ident!(\"{}_depth_{}\", fun_name, depth);\n        let tokens = quote! {\n            #[test]\n            fn #fun_name_versioned() {\n                #fun_name(#depth);\n            }\n        };\n        result.extend(TokenStream::from(tokens));\n    }\n    result\n}\n\n#[derive(Debug)]\nstruct ArrayForDepthsInput {\n    replaced_ident: Ident,\n    expr: syn::Expr,\n}\n\n#[derive(Debug)]\nstruct MacroArgs {\n    args: Vec<syn::Expr>,\n}\n\nimpl Parse for MacroArgs {\n    fn parse(input: ParseStream) -> syn::Result<Self> {\n        let mut args = Vec::new();\n        while !input.is_empty() {\n            args.push(input.parse::<syn::Expr>()?);\n            if input.is_empty() {\n                break;\n            }\n            input.parse::<Token![,]>()?;\n        }\n        Ok(MacroArgs { args })\n    }\n}\n\nimpl MacroArgs {\n    fn tokens(&self) -> proc_macro2::TokenStream {\n        let args = &self.args;\n        quote! { #(#args),* }\n    }\n}\n\nstruct IdentReplacer(Ident, syn::Expr);\n\nimpl VisitMut for IdentReplacer {\n    fn visit_expr_mut(&mut self, expr: &mut syn::Expr) {\n        match expr {\n            syn::Expr::Path(ident) => {\n                if ident.path.is_ident(&self.0) {\n                    *expr = self.1.clone();\n                }\n            }\n            syn::Expr::Macro(mcr) => {\n                let Ok(mut args) = mcr.mac.parse_body::<MacroArgs>() else {\n                    return;\n                };\n                for arg in &mut args.args {\n                    self.visit_expr_mut(arg);\n                }\n                mcr.mac.tokens = args.tokens();\n            }\n            _ => syn::visit_mut::visit_expr_mut(self, expr),\n        }\n    }\n}\n\nimpl Parse for ArrayForDepthsInput {\n    fn parse(input: ParseStream) -> syn::Result<Self> {\n        input.parse::<Token![|]>()?;\n        let replaced_ident = input.parse::<Ident>()?;\n        input.parse::<Token![|]>()?;\n        let expr = input.parse::<syn::Expr>()?;\n        Ok(ArrayForDepthsInput {\n            replaced_ident,\n            expr,\n        })\n    }\n}\n\n/// Macro to generate code for multiple depths.\n///\n/// Generates an array of expressions, where the given identifier is replaced\n/// with each supported depth. The argument must use closure syntax, but this\n/// is pure syntactic, the closure expression gets unrolled statically.\n///\n/// This macro also descends into other macros, as long as they use standard\n/// Rust syntax for arguments. Any non-standard macros will be ignored in the\n/// expansion of this.\n///\n/// For example, `array_for_depths!(|depth| depth + 5)`, with only `depth_16`\n/// and `depth_30` supported, will generate `[16 + 5, 30 + 5]`, and\n/// `array_for_depths!(|depth| concat!(\"foo\", depth))` will generate\n/// `[concat!(\"foo\", 16), concat!(\"foo\", 30)]`.\n#[proc_macro]\npub fn array_for_depths(input: TokenStream) -> TokenStream {\n    let input = parse_macro_input!(input as ArrayForDepthsInput);\n    let items = get_supported_depths()\n        .iter()\n        .map(|depth| {\n            let mut replacer = IdentReplacer(input.replaced_ident.clone(), parse_quote!(#depth));\n            let mut expr = input.expr.clone();\n            replacer.visit_expr_mut(&mut expr);\n            expr\n        })\n        .collect::<Vec<_>>();\n    let array = quote! { [#(#items),*] };\n    array.into()\n}\n"
  },
  {
    "path": "crates/storage/Cargo.toml",
    "content": "[package]\nname = \"semaphore-rs-storage\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nauthors.workspace = true\ndescription.workspace = true\nkeywords.workspace = true\ncategories.workspace = true\n\n[dependencies]\nbytemuck.workspace = true\ncolor-eyre.workspace = true\n\n[target.'cfg(not(target_arch = \"wasm32\"))'.dependencies]\nmmap-rs.workspace = true\ntempfile.workspace = true\n"
  },
  {
    "path": "crates/storage/src/lib.rs",
    "content": "use std::ops::{Deref, DerefMut};\n\npub trait GenericStorage<T>:\n    Deref<Target = [T]> + DerefMut<Target = [T]> + Extend<T> + Send + Sync\n{\n    fn push(&mut self, value: T);\n\n    fn extend_from_slice(&mut self, slice: &[T]);\n\n    fn clear(&mut self);\n}\n\nimpl<T: Send + Sync + Copy> GenericStorage<T> for Vec<T> {\n    fn push(&mut self, value: T) {\n        self.push(value);\n    }\n\n    fn extend_from_slice(&mut self, slice: &[T]) {\n        Vec::extend_from_slice(self, slice);\n    }\n\n    fn clear(&mut self) {\n        self.clear();\n    }\n}\n\n#[cfg(not(target_arch = \"wasm32\"))]\nmod native;\n\n#[cfg(not(target_arch = \"wasm32\"))]\npub use native::MmapVec;\n"
  },
  {
    "path": "crates/storage/src/native/mmap_vec.rs",
    "content": "use std::fs::{File, OpenOptions};\nuse std::ops::{Deref, DerefMut};\nuse std::path::Path;\n\nuse bytemuck::Pod;\nuse color_eyre::eyre::{ensure, Context};\nuse mmap_rs::{MmapFlags, MmapMut, MmapOptions};\n\nconst META_SIZE: usize = std::mem::size_of::<usize>();\n\npub struct MmapVec<T> {\n    // This must be Option to properly uphold aliasing access safety guarantees\n    // Look at the `resize` method for more details\n    mmap: Option<MmapMut>,\n    file: File,\n    capacity: usize,\n    phantom: std::marker::PhantomData<T>,\n}\n\n// Public API\nimpl<T: Pod> MmapVec<T> {\n    /// Creates a new MmapVec from a file path.\n    /// Any existing data in the file will be truncated.\n    ///\n    /// # Safety\n    /// Same requirements as `create`\n    pub unsafe fn create_from_path(file_path: impl AsRef<Path>) -> color_eyre::Result<Self> {\n        let file = OpenOptions::new()\n            .read(true)\n            .write(true)\n            .create(true)\n            .truncate(true)\n            .open(file_path)?;\n\n        Self::create(file)\n    }\n\n    /// Creates a new MmapVec from a file.\n    /// Any existing data in the file will be truncated.\n    ///\n    /// # Safety\n    /// This method requires that the safety requirements of [`mmap_rs::MmapOptions::with_file`](https://docs.rs/mmap-rs/0.6.1/mmap_rs/struct.MmapOptions.html#method.with_file) are upheld.\n    ///\n    /// Notably this means that there can exist no other mutable mappings to the\n    /// same file in this process or any other\n    pub unsafe fn create(file: File) -> color_eyre::Result<Self> {\n        file.set_len(0)?;\n        file.set_len(META_SIZE as u64)\n            .context(\"Failed to resize underlying file\")?;\n\n        let mut s = Self::restore(file)?;\n\n        s.set_storage_len(0);\n\n        Ok(s)\n    }\n\n    /// Restores an MmapVec from a file path.\n    ///\n    /// # Safety\n    /// Same requirements as `restore`\n    pub unsafe fn restore_from_path(file_path: impl AsRef<Path>) -> color_eyre::Result<Self> {\n        let file = OpenOptions::new()\n            .read(true)\n            .write(true)\n            .create(true)\n            .truncate(false)\n            .open(file_path)?;\n\n        Self::restore(file)\n    }\n\n    /// Restores an MmapVec from a file. This should not panic.\n    ///\n    /// # Safety\n    /// This method requires that the safety requirements of [`mmap_rs::MmapOptions::with_file`](https://docs.rs/mmap-rs/0.6.1/mmap_rs/struct.MmapOptions.html#method.with_file) are upheld.\n    ///\n    /// Additioanally the caller must ensure that the file contains valid data.\n    ///\n    /// Notably this means that there can exist no other mutable mappings to the\n    /// same file in this process or any other\n    pub unsafe fn restore(file: File) -> color_eyre::Result<Self> {\n        assert!(std::mem::size_of::<T>() != 0);\n\n        let mut byte_len = file.metadata()?.len() as usize;\n\n        if byte_len < META_SIZE {\n            file.set_len(0)?;\n\n            file.set_len(META_SIZE as u64)?;\n\n            byte_len = META_SIZE;\n        }\n\n        let data_len = byte_len.saturating_sub(META_SIZE);\n        ensure!(\n            data_len.is_multiple_of(std::mem::size_of::<T>()),\n            \"data must be divisible by size of T\"\n        );\n\n        let capacity = data_len / std::mem::size_of::<T>();\n\n        let mmap = MmapOptions::new(byte_len)?\n            .with_file(&file, 0)\n            .with_flags(MmapFlags::SHARED)\n            .map_mut()?;\n\n        let s = Self {\n            mmap: Some(mmap),\n            file,\n            capacity,\n            phantom: std::marker::PhantomData,\n        };\n\n        let len = s.storage_len();\n        ensure!(len <= capacity, \"len must be lower than capacity\");\n\n        Ok(s)\n    }\n\n    pub fn clear(&mut self) {\n        self.set_storage_len(0);\n    }\n\n    pub fn push(&mut self, v: T) {\n        let len = self.storage_len();\n        let capacity = self.capacity;\n        let new_len = len + 1;\n\n        if new_len > capacity {\n            self.resize(new_len.next_power_of_two());\n        }\n\n        self.capacity_slice_mut()[len] = v;\n        self.set_storage_len(new_len);\n    }\n\n    pub fn extend_from_slice(&mut self, slice: &[T]) {\n        let len = self.storage_len();\n        let capacity = self.capacity;\n        let new_len = len + slice.len();\n\n        if new_len >= capacity {\n            self.resize(new_len.next_power_of_two());\n        }\n\n        self.capacity_slice_mut()[len..(new_len)].copy_from_slice(slice);\n        self.set_storage_len(new_len);\n    }\n\n    pub fn resize(&mut self, new_capacity: usize) {\n        let new_file_len = META_SIZE + new_capacity * std::mem::size_of::<T>();\n\n        self.file\n            .set_len(new_file_len as u64)\n            .expect(\"Failed to resize underlying file\");\n\n        // # Safety\n        // MmapMut requires that no other instance of MmapMut exists that has access\n        // to the same file.\n        //\n        // In order to uphold that we must first drop the current MmapMut instance.\n        //\n        // MmapMut also requires that the caller must ensure that no other process\n        // has access to the same file at the same time.\n        // This requirement is upheld at the instantiation of MmapVec and must hold true\n        // for its entire lifetime. Therefore it must be upheld here as well.\n        unsafe {\n            self.mmap = None;\n            self.mmap = Some(\n                MmapOptions::new(new_file_len)\n                    .expect(\"cannot create memory map\")\n                    .with_file(&self.file, 0)\n                    .with_flags(MmapFlags::SHARED)\n                    .map_mut()\n                    .expect(\"cannot build memory map\"),\n            );\n        }\n\n        self.capacity = new_capacity;\n    }\n\n    fn set_storage_len(&mut self, new_len: usize) {\n        let slice: &mut [usize] =\n            bytemuck::cast_slice_mut(&mut self.mmap.as_mut().unwrap()[..META_SIZE]);\n        slice[0] = new_len;\n    }\n\n    fn storage_len(&self) -> usize {\n        bytemuck::cast_slice(&self.mmap.as_ref().unwrap()[..META_SIZE])[0]\n    }\n\n    fn capacity_slice(&self) -> &[T] {\n        bytemuck::cast_slice(&self.mmap.as_ref().unwrap().as_slice()[META_SIZE..])\n    }\n\n    fn capacity_slice_mut(&mut self) -> &mut [T] {\n        bytemuck::cast_slice_mut(&mut self.mmap.as_mut().unwrap().as_mut_slice()[META_SIZE..])\n    }\n}\n\nimpl<T> Extend<T> for MmapVec<T>\nwhere\n    T: Pod,\n{\n    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {\n        for item in iter {\n            self.push(item);\n        }\n    }\n}\n\nimpl<T> Deref for MmapVec<T>\nwhere\n    T: Pod,\n{\n    type Target = [T];\n\n    fn deref(&self) -> &Self::Target {\n        &self.capacity_slice()[..self.storage_len()]\n    }\n}\n\nimpl<T> DerefMut for MmapVec<T>\nwhere\n    T: Pod,\n{\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        let len = self.storage_len();\n        &mut self.capacity_slice_mut()[..len]\n    }\n}\n\nimpl<T> std::fmt::Debug for MmapVec<T>\nwhere\n    T: Pod + std::fmt::Debug,\n{\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        let contents: &[T] = self.deref();\n\n        f.debug_struct(\"MmapVec\")\n            .field(\"contents\", &contents)\n            .field(\"capacity\", &self.capacity)\n            .finish()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n\n    use super::*;\n\n    #[test]\n    #[allow(clippy::manual_bits)]\n    fn test_capacity_push() {\n        let f = tempfile::NamedTempFile::new().unwrap();\n        let file_path = f.path().to_owned();\n\n        let mut storage: MmapVec<u32> = unsafe { MmapVec::create(f.reopen().unwrap()).unwrap() };\n        assert_eq!(storage.capacity, 0);\n        assert_eq!(\n            std::fs::metadata(&file_path).unwrap().len(),\n            META_SIZE as u64\n        );\n\n        storage.push(0);\n        assert_eq!(storage.capacity, 1);\n        assert_eq!(\n            std::fs::metadata(&file_path).unwrap().len() as usize,\n            std::mem::size_of::<u32>() + META_SIZE\n        );\n\n        storage.push(0);\n        assert_eq!(storage.capacity, 2);\n        assert_eq!(\n            std::fs::metadata(&file_path).unwrap().len() as usize,\n            std::mem::size_of::<u32>() * 2 + META_SIZE\n        );\n\n        storage.push(0);\n        assert_eq!(storage.capacity, 4);\n        assert_eq!(\n            std::fs::metadata(&file_path).unwrap().len() as usize,\n            std::mem::size_of::<u32>() * 4 + META_SIZE\n        );\n\n        storage.push(0);\n        assert_eq!(storage.capacity, 4);\n        assert_eq!(\n            std::fs::metadata(&file_path).unwrap().len() as usize,\n            std::mem::size_of::<u32>() * 4 + META_SIZE\n        );\n\n        storage.push(0);\n        assert_eq!(storage.capacity, 8);\n        assert_eq!(\n            std::fs::metadata(&file_path).unwrap().len() as usize,\n            std::mem::size_of::<u32>() * 8 + META_SIZE\n        );\n    }\n\n    #[test]\n    #[allow(clippy::manual_bits)]\n    fn test_capacity_extend() {\n        let f = tempfile::NamedTempFile::new().unwrap();\n        let file_path = f.path().to_owned();\n\n        let mut storage: MmapVec<u32> = unsafe { MmapVec::create(f.reopen().unwrap()).unwrap() };\n        assert_eq!(storage.capacity, 0);\n        assert_eq!(\n            std::fs::metadata(&file_path).unwrap().len(),\n            META_SIZE as u64\n        );\n\n        storage.extend_from_slice(&[0, 0]);\n        assert_eq!(storage.capacity, 2);\n        assert_eq!(\n            std::fs::metadata(&file_path).unwrap().len() as usize,\n            std::mem::size_of::<u32>() * 2 + META_SIZE\n        );\n\n        storage.extend_from_slice(&[0, 0, 0]);\n        assert_eq!(storage.capacity, 8);\n        assert_eq!(\n            std::fs::metadata(&file_path).unwrap().len() as usize,\n            std::mem::size_of::<u32>() * 8 + META_SIZE\n        );\n\n        storage.extend_from_slice(&[0]);\n        assert_eq!(storage.capacity, 8);\n        assert_eq!(\n            std::fs::metadata(&file_path).unwrap().len() as usize,\n            std::mem::size_of::<u32>() * 8 + META_SIZE\n        );\n    }\n\n    #[test]\n    #[allow(clippy::manual_bits)]\n    fn test_capacity_create() {\n        let f = tempfile::NamedTempFile::new().unwrap();\n        let file_path = f.path().to_owned();\n\n        let mut storage: MmapVec<u32> = unsafe { MmapVec::create(f.reopen().unwrap()).unwrap() };\n        assert_eq!(storage.capacity, 0);\n        assert_eq!(\n            std::fs::metadata(&file_path).unwrap().len() as usize,\n            META_SIZE\n        );\n\n        storage.extend_from_slice(&[0, 0, 0, 0, 0]);\n        assert_eq!(storage.capacity, 8);\n        assert_eq!(\n            std::fs::metadata(&file_path).unwrap().len() as usize,\n            std::mem::size_of::<u32>() * 8 + META_SIZE\n        );\n\n        let storage: MmapVec<u32> = unsafe { MmapVec::create(f.reopen().unwrap()).unwrap() };\n        assert_eq!(storage.capacity, 0);\n        assert_eq!(\n            std::fs::metadata(&file_path).unwrap().len() as usize,\n            META_SIZE\n        );\n    }\n\n    #[test]\n    #[allow(clippy::manual_bits)]\n    fn test_capacity_create_from_path() {\n        let f = tempfile::NamedTempFile::new().unwrap();\n        let file_path = f.path().to_owned();\n\n        let mut storage: MmapVec<u32> = unsafe { MmapVec::create(f.reopen().unwrap()).unwrap() };\n        assert_eq!(storage.capacity, 0);\n        assert_eq!(\n            std::fs::metadata(&file_path).unwrap().len() as usize,\n            META_SIZE\n        );\n\n        storage.extend_from_slice(&[0, 0, 0, 0, 0]);\n        assert_eq!(storage.capacity, 8);\n        assert_eq!(\n            std::fs::metadata(&file_path).unwrap().len() as usize,\n            std::mem::size_of::<u32>() * 8 + META_SIZE\n        );\n\n        let storage: MmapVec<u32> = unsafe { MmapVec::create_from_path(&file_path).unwrap() };\n        assert_eq!(storage.capacity, 0);\n        assert_eq!(\n            std::fs::metadata(&file_path).unwrap().len() as usize,\n            META_SIZE\n        );\n    }\n\n    #[test]\n    #[allow(clippy::manual_bits)]\n    fn test_capacity_restore() {\n        let f = tempfile::NamedTempFile::new().unwrap();\n        let file_path = f.path().to_owned();\n\n        let mut storage: MmapVec<u32> = unsafe { MmapVec::create(f.reopen().unwrap()).unwrap() };\n        assert_eq!(storage.capacity, 0);\n        assert_eq!(\n            std::fs::metadata(&file_path).unwrap().len() as usize,\n            META_SIZE\n        );\n\n        storage.extend_from_slice(&[0, 0, 0, 0, 0]);\n        assert_eq!(storage.capacity, 8);\n        assert_eq!(\n            std::fs::metadata(&file_path).unwrap().len() as usize,\n            std::mem::size_of::<u32>() * 8 + META_SIZE\n        );\n\n        let storage: MmapVec<u32> = unsafe { MmapVec::restore(f.reopen().unwrap()).unwrap() };\n        assert_eq!(storage.capacity, 8);\n        assert_eq!(\n            std::fs::metadata(&file_path).unwrap().len() as usize,\n            std::mem::size_of::<u32>() * 8 + META_SIZE\n        );\n    }\n\n    #[test]\n    #[allow(clippy::manual_bits)]\n    fn test_capacity_restore_from_path() {\n        let f = tempfile::NamedTempFile::new().unwrap();\n        let file_path = f.path().to_owned();\n\n        let mut storage: MmapVec<u32> = unsafe { MmapVec::create(f.reopen().unwrap()).unwrap() };\n        assert_eq!(storage.capacity, 0);\n        assert_eq!(\n            std::fs::metadata(&file_path).unwrap().len() as usize,\n            META_SIZE\n        );\n\n        storage.extend_from_slice(&[0, 0, 0, 0, 0]);\n        assert_eq!(storage.capacity, 8);\n        assert_eq!(\n            std::fs::metadata(&file_path).unwrap().len() as usize,\n            std::mem::size_of::<u32>() * 8 + META_SIZE\n        );\n\n        let storage: MmapVec<u32> = unsafe { MmapVec::restore_from_path(&file_path).unwrap() };\n        assert_eq!(storage.capacity, 8);\n        assert_eq!(\n            std::fs::metadata(&file_path).unwrap().len() as usize,\n            std::mem::size_of::<u32>() * 8 + META_SIZE\n        );\n    }\n\n    #[test]\n    fn test_mmap_vec() {\n        let f = tempfile::tempfile().unwrap();\n\n        let mut storage: MmapVec<u32> = unsafe { MmapVec::create(f.try_clone().unwrap()).unwrap() };\n\n        println!(\"{storage:?}\");\n        storage.resize(2);\n        println!(\"{storage:?}\");\n\n        storage.push(u32::MAX);\n        println!(\"{storage:?}\");\n        storage.push(2);\n        println!(\"{storage:?}\");\n\n        storage.resize(4);\n        println!(\"{storage:?}\");\n\n        storage.push(42);\n        println!(\"{storage:?}\");\n        storage.push(4);\n        println!(\"{storage:?}\");\n\n        assert_eq!(storage.len(), 4);\n\n        println!(\"{storage:?}\");\n        assert_eq!(storage[0], u32::MAX);\n        assert_eq!(storage[1], 2);\n        assert_eq!(storage[2], 42);\n        assert_eq!(storage[3], 4);\n\n        drop(storage);\n        let restored: MmapVec<u32> = unsafe { MmapVec::restore(f).unwrap() };\n\n        assert_eq!(restored.len(), 4);\n\n        assert_eq!(restored[0], u32::MAX);\n        assert_eq!(restored[1], 2);\n        assert_eq!(restored[2], 42);\n        assert_eq!(restored[3], 4);\n    }\n}\n"
  },
  {
    "path": "crates/storage/src/native/mod.rs",
    "content": "mod mmap_vec;\n\nuse bytemuck::Pod;\n\npub use mmap_vec::MmapVec;\n\nimpl<T: Send + Sync + Pod> super::GenericStorage<T> for MmapVec<T> {\n    fn push(&mut self, value: T) {\n        self.push(value);\n    }\n\n    fn extend_from_slice(&mut self, slice: &[T]) {\n        self.extend_from_slice(slice);\n    }\n\n    fn clear(&mut self) {\n        self.clear();\n    }\n}\n"
  },
  {
    "path": "crates/trees/Cargo.toml",
    "content": "[package]\nname = \"semaphore-rs-trees\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nauthors.workspace = true\ndescription.workspace = true\nkeywords.workspace = true\ncategories.workspace = true\n\n[dependencies]\n# Internal\nsemaphore-rs-hasher.workspace = true\nsemaphore-rs-storage.workspace = true\nsemaphore-rs-ark-circom.workspace = true\n\n# 3rd Party\nbytemuck.workspace = true\ncolor-eyre.workspace = true\nderive-where.workspace = true\nhex.workspace = true\nhex-literal.workspace = true\nitertools.workspace = true\nonce_cell.workspace = true\nrayon.workspace = true\nruint.workspace = true\nserde.workspace = true\nthiserror.workspace = true\ntiny-keccak = { workspace = true, features = [\"sha3\"] }\n\n# Ark\nark-bn254.workspace = true\nark-ec.workspace = true\nark-ff.workspace = true\nark-groth16.workspace = true\nark-relations.workspace = true\nark-std.workspace = true\n\n[dev-dependencies]\nsemaphore-rs-poseidon.workspace = true\nsemaphore-rs-keccak.workspace = true\n\nrand.workspace = true\nserial_test.workspace = true\ntempfile.workspace = true\ntest-case.workspace = true\n\n[target.'cfg(not(target_arch = \"wasm32\"))'.dependencies]\nmmap-rs.workspace = true\n\n[target.'cfg(target_arch = \"wasm32\")'.dependencies]\n# Enable the JS backend for getrandom (pulled in transitively via rand) so\n# that this crate compiles for wasm32-unknown-unknown.\ngetrandom.workspace = true\n"
  },
  {
    "path": "crates/trees/src/cascading/mod.rs",
    "content": "use std::fmt::Debug;\n\nuse bytemuck::Pod;\nuse color_eyre::eyre::{ensure, Result};\nuse derive_where::derive_where;\nuse semaphore_rs_hasher::Hasher;\n\nuse crate::proof::{Branch, InclusionProof};\n\nmod storage_ops;\n\nuse self::storage_ops::{sparse_fill_partial_subtree, StorageOps};\n\n/// A dynamically growable array represented merkle tree.\n///\n/// The left most branch of the tree consists of progressively increasing powers\n/// of two. The right child of each power of two looks like a traditionally\n/// indexed binary tree offset by its parent.\n///\n/// The underlying storage is a 1-indexed dynamically growable array that is\n/// always a power of two in length. The tree is built succesively from the\n/// bottom left to the top right.\n///\n/// The zeroth index of the underlying storage is used to store the number of\n/// leaves in the tree. Because of this, the Hash used must be able to be cast\n/// as a usize. If this is not possible, the code will panic at runtime.\n///\n/// ```markdown\n///           8\n///     4            9\n///  2     5     10     11\n/// 1  3  6  7  12 13 14 15\n///\n/// Leaves are 0 indexed\n/// 0  1  2  3  4  5  6  7\n/// ```\n#[derive_where(Clone; <H as Hasher>::Hash: Clone, S: Clone)]\n#[derive_where(PartialEq; <H as Hasher>::Hash: PartialEq, S: PartialEq)]\n#[derive_where(Eq; <H as Hasher>::Hash: Eq, S: Eq)]\n#[derive_where(Debug; <H as Hasher>::Hash: Debug, S: Debug)]\npub struct CascadingMerkleTree<H, S = Vec<<H as Hasher>::Hash>>\nwhere\n    H: Hasher,\n{\n    depth: usize,\n    root: H::Hash,\n    empty_value: H::Hash,\n    sparse_column: Vec<H::Hash>,\n    storage: S,\n    _marker: std::marker::PhantomData<H>,\n}\nimpl<H, S> CascadingMerkleTree<H, S>\nwhere\n    H: Hasher,\n    <H as Hasher>::Hash: Copy + Pod + Eq + Send + Sync,\n    <H as Hasher>::Hash: Debug,\n    S: StorageOps<H>,\n{\n    /// Use to open a previously initialized tree\n    pub fn restore(\n        storage: S,\n        depth: usize,\n        empty_value: &H::Hash,\n    ) -> Result<CascadingMerkleTree<H, S>> {\n        let tree = Self::restore_unchecked(storage, depth, empty_value)?;\n\n        tree.validate()?;\n\n        Ok(tree)\n    }\n\n    /// Restores a tree from the provided storage\n    ///\n    /// Invalid storage will result in unpredictable behavior\n    pub fn restore_unchecked(\n        storage: S,\n        depth: usize,\n        empty_value: &H::Hash,\n    ) -> Result<CascadingMerkleTree<H, S>> {\n        let len = storage.len();\n\n        storage.validate_const()?;\n\n        ensure!(depth > 0, \"Tree depth must be greater than 0\");\n        ensure!(\n            len <= 2usize.checked_pow(depth as u32 + 1).unwrap(),\n            \"Storage length must be less than or equal to 2^(depth + 1)\"\n        );\n\n        let sparse_column = Self::sparse_column(depth, empty_value);\n\n        let mut tree = CascadingMerkleTree {\n            depth,\n            root: *empty_value,\n            empty_value: *empty_value,\n            sparse_column,\n            storage,\n            _marker: std::marker::PhantomData,\n        };\n\n        tree.recompute_root();\n\n        let num_leaves = tree.num_leaves();\n        ensure!(\n            num_leaves <= len >> 1,\n            \"Number of leaves ({num_leaves}) must be less than or equal to half the storage \\\n             length ({len})\"\n        );\n\n        Ok(tree)\n    }\n\n    /// Create and initialize a tree in the provided storage\n    ///\n    /// initializes an empty tree\n    #[must_use]\n    pub fn new(storage: S, depth: usize, empty_value: &H::Hash) -> CascadingMerkleTree<H, S> {\n        Self::new_with_leaves(storage, depth, empty_value, &[])\n    }\n\n    /// Create and initialize a tree in the provided storage\n    #[must_use]\n    pub fn new_with_leaves(\n        mut storage: S,\n        depth: usize,\n        empty_value: &H::Hash,\n        leaves: &[H::Hash],\n    ) -> CascadingMerkleTree<H, S> {\n        assert!(depth > 0, \"Tree depth must be greater than 0\");\n        assert!(\n            leaves.len() <= (1 << depth),\n            \"Number of leaves must be less than or equal to the capacity of the tree\"\n        );\n\n        let sparse_column = Self::sparse_column(depth, empty_value);\n        storage.populate_with_leaves(&sparse_column, empty_value, leaves);\n\n        let mut tree = CascadingMerkleTree {\n            depth,\n            root: *empty_value,\n            empty_value: *empty_value,\n            sparse_column,\n            storage,\n            _marker: std::marker::PhantomData,\n        };\n\n        tree.recompute_root();\n        tree\n    }\n\n    /// Returns the depth of the tree.\n    #[must_use]\n    pub const fn depth(&self) -> usize {\n        self.depth\n    }\n\n    /// Returns the empty value of the tree.\n    #[must_use]\n    pub const fn empty_value(&self) -> H::Hash {\n        self.empty_value\n    }\n\n    /// Returns the root of the tree.\n    #[must_use]\n    pub const fn root(&self) -> H::Hash {\n        self.root\n    }\n\n    /// Returns the the total number of leaves that have been inserted into the\n    /// tree. It's important to note that this is not the same as total\n    /// capacity of leaves. Leaves that have manually been set to empty\n    /// values are not considered.\n    #[must_use]\n    pub fn num_leaves(&self) -> usize {\n        self.storage.num_leaves()\n    }\n\n    /// Sets the value at the given index.\n    ///\n    /// If the leaf index is greater than the current number of leaves, the tree is extended with\n    /// empty values up to the given leaf index.\n    pub fn set_leaf(&mut self, leaf: usize, value: H::Hash) {\n        let num_leaves = self.storage.num_leaves();\n        if leaf < num_leaves {\n            let index = storage_ops::index_from_leaf(leaf);\n            self.storage[index] = value;\n            self.storage.propagate_up(index);\n            self.recompute_root();\n        } else {\n            let num_zeros = leaf - num_leaves;\n            let mut new = Vec::with_capacity(num_zeros + 1);\n            new.resize(num_zeros, self.empty_value);\n            new.push(value);\n            self.extend_from_slice(&new);\n        }\n    }\n\n    /// Pushes a new leaf to the tree, increasing the number of leaves by one.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the number of leaves exceeds the capacity of the tree.\n    pub fn push(&mut self, leaf: H::Hash) -> Result<()> {\n        let num_leaves = self.num_leaves();\n        ensure!(\n            num_leaves < (1 << self.depth),\n            \"Cannot push more leaves than the capacity of the tree\"\n        );\n        let index = storage_ops::index_from_leaf(num_leaves);\n        let storage_len = self.storage.len();\n\n        // If the index is out of bounds, we need to reallocate the storage\n        // we must always have 2^n leaves for any n\n        if index >= storage_len {\n            debug_assert!(storage_len.is_power_of_two());\n            self.storage\n                .extend(std::iter::repeat_n(self.empty_value, storage_len));\n            let subtree = &mut self.storage[storage_len..(storage_len << 1)];\n            sparse_fill_partial_subtree::<H>(subtree, &self.sparse_column, 0..(storage_len >> 1));\n        }\n\n        self.storage[index] = leaf;\n        self.storage.increment_num_leaves(1);\n        self.storage.propagate_up(index);\n        self.recompute_root();\n\n        Ok(())\n    }\n\n    /// Returns the Merkle proof for the given leaf.\n    ///\n    /// # TODO:\n    /// Currently the branch which connects the storage tip to the root\n    /// is not stored persistenetly. Repeated requests for proofs in between\n    /// tree updates result in recomputing the same hashes when this could be\n    /// avoided.\n    ///\n    /// # Panics\n    ///\n    /// Panics if the leaf index is not less than the current\n    /// number of leaves.\n    #[must_use]\n    pub fn proof(&self, leaf: usize) -> InclusionProof<H> {\n        assert!(leaf < self.num_leaves(), \"Leaf index out of bounds\");\n        let mut proof = Vec::with_capacity(self.depth);\n        let storage_depth = storage_ops::subtree_depth(&self.storage);\n\n        let mut index = storage_ops::index_from_leaf(leaf);\n        for _ in 0..storage_depth {\n            match storage_ops::sibling(index) {\n                Branch::Left(sibling_index) => {\n                    proof.push(Branch::Left(self.storage[sibling_index]));\n                }\n                Branch::Right(sibling_index) => {\n                    proof.push(Branch::Right(self.storage[sibling_index]));\n                }\n            }\n            index = storage_ops::parent(index);\n        }\n\n        let remainder = self.sparse_column[storage_depth..(self.sparse_column.len() - 1)]\n            .iter()\n            .map(|&val| Branch::Left(val));\n        proof.extend(remainder);\n\n        InclusionProof(proof)\n    }\n\n    /// Returns the Merkle proof for the given leaf hash.\n    /// Leaves are scanned from right to left.\n    /// This is a slow operation and `proof` should be used when possible.\n    #[must_use]\n    pub fn proof_from_hash(&self, leaf: H::Hash) -> Option<InclusionProof<H>> {\n        let leaf = self.get_leaf_from_hash(leaf)?;\n        Some(self.proof(leaf))\n    }\n\n    /// Verifies the given proof for the given value.\n    #[must_use]\n    pub fn verify(&self, value: H::Hash, proof: &InclusionProof<H>) -> bool {\n        proof.root(value) == self.root()\n    }\n\n    /// Returns the node hash at the given index.\n    ///\n    /// # Panics\n    ///\n    /// Panics if either the depth or offset is out of bounds.\n    #[must_use]\n    pub fn get_node(&self, depth: usize, offset: usize) -> H::Hash {\n        let height = self.depth - depth;\n        let index = storage_ops::index_height_offset(height, offset);\n        match self.storage.get(index) {\n            Some(hash) => *hash,\n            None => {\n                if offset == 0 {\n                    self.compute_from_storage_tip(depth)\n                } else {\n                    self.sparse_column[height]\n                }\n            }\n        }\n    }\n\n    /// Returns the hash at the given leaf index.\n    #[must_use]\n    pub fn get_leaf(&self, leaf: usize) -> H::Hash {\n        let index = storage_ops::index_from_leaf(leaf);\n        self.storage.get(index).copied().unwrap_or(self.empty_value)\n    }\n\n    /// Returns the leaf index for the given leaf hash.\n    #[must_use]\n    pub fn get_leaf_from_hash(&self, hash: H::Hash) -> Option<usize> {\n        let num_leaves = self.num_leaves();\n        if num_leaves == 0 {\n            return None;\n        }\n\n        let mut end = storage_ops::index_from_leaf(num_leaves - 1) + 1; // 4\n        let prev_pow = end.next_power_of_two() >> 1;\n        let mut start = prev_pow + (prev_pow >> 1);\n\n        loop {\n            match (start..end).rev().find(|&i| self.storage[i] == hash) {\n                Some(index) => {\n                    return Some(storage_ops::leaf_from_index(index));\n                }\n                None => {\n                    if start == 1 {\n                        return None;\n                    }\n                    start /= 2;\n                    end = (start + 1).next_power_of_two();\n                }\n            }\n        }\n    }\n\n    /// Returns an iterator over all leaf hashes.\n    pub fn leaves(&self) -> impl Iterator<Item = H::Hash> + '_ {\n        self.storage.leaves()\n    }\n\n    /// Returns the `sparse_column` for the given depth and empty_value.\n    /// This columns represents empty values sequentially hashed together up to\n    /// the top of the tree.\n    /// Index 0 represents the bottom layer of the tree.\n    #[must_use]\n    fn sparse_column(depth: usize, empty_value: &H::Hash) -> Vec<H::Hash> {\n        (0..depth + 1)\n            .scan(*empty_value, |state, _| {\n                let val = *state;\n                *state = H::hash_node(&val, &val);\n                Some(val)\n            })\n            .collect()\n    }\n\n    /// Returns the root of the tree.\n    /// Hashes are recomputed from the storage tip.\n    fn recompute_root(&mut self) -> H::Hash {\n        let hash = self.compute_from_storage_tip(0);\n        self.root = hash;\n        hash\n    }\n\n    /// Recomputes hashess from the storage tip up to the given depth.\n    /// The hash returned is the hash of the left most branch of the tree.\n    fn compute_from_storage_tip(&self, depth: usize) -> H::Hash {\n        let storage_root = self.storage.storage_root();\n        let storage_depth = self.storage.storage_depth();\n        let mut hash = storage_root;\n        for i in storage_depth..(self.depth - depth) {\n            hash = H::hash_node(&hash, &self.sparse_column[i]);\n        }\n        hash\n    }\n\n    /// Validates all elements of the storage, ensuring that they\n    /// correspond to a valid tree.\n    pub fn validate(&self) -> Result<()> {\n        debug_assert_eq!(\n            self.root,\n            self.compute_from_storage_tip(0),\n            \"Root hash does not match recomputed root hash\"\n        );\n        self.storage.validate(&self.empty_value)\n    }\n\n    /// Extends the tree with the given leaves in parallel.\n    ///\n    /// ```markdown\n    /// subtree_power = ilog2(8) = 3\n    ///\n    ///           8    (subtree)\n    ///      4      [     9     ]\n    ///   2     5   [  10    11 ]\n    /// 1  3  6  7  [12 13 14 15]\n    ///  ```\n    pub fn extend_from_slice(&mut self, leaves: &[H::Hash]) {\n        if leaves.is_empty() {\n            return;\n        }\n        let num_new_leaves = leaves.len();\n        let storage_len = self.storage.len();\n        let current_leaves = self.num_leaves();\n        let total_leaves = current_leaves + num_new_leaves;\n        assert!(\n            total_leaves <= (1 << self.depth),\n            \"Cannot extend more leaves than the capacity of the tree\"\n        );\n        let new_last_leaf_index = storage_ops::index_from_leaf(total_leaves - 1);\n\n        // If the index is out of bounds, we need to resize the storage\n        // we must always have 2^n leaves for any n\n        if new_last_leaf_index >= storage_len {\n            let next_power_of_two = new_last_leaf_index.next_power_of_two();\n            let diff = next_power_of_two - storage_len;\n\n            self.storage\n                .extend(std::iter::repeat_n(self.empty_value, diff));\n        }\n\n        // Represense the power of the first subtree that has been modified\n        let first_subtree_power = ((current_leaves + 1).next_power_of_two()).ilog2();\n        // Represense the power of the last subtree that has been modified\n        let last_subtree_power = ((total_leaves).next_power_of_two()).ilog2();\n\n        let mut remaining_leaves = leaves;\n\n        // We iterate over subsequently larger subtrees which have been\n        // modified by the new leaves.\n        for subtree_power in first_subtree_power..=last_subtree_power {\n            // We have a special case for subtree_power = 0\n            // because the subtree is completely empty.\n            // This represents the very bottom left of the tree.\n            // parent_index represents the index of the parent node of the subtree.\n            // It is the power of two on the left most branch of the tree.\n            let parent_index = if subtree_power == 0 {\n                let (leaf_slice, remaining) = remaining_leaves.split_at(1);\n                remaining_leaves = remaining;\n                self.storage[1] = leaf_slice[0];\n                continue;\n            } else {\n                1 << subtree_power\n            };\n\n            // The slice of the storage that contains the subtree\n            let subtree_slice = &mut self.storage[parent_index..(parent_index << 1)];\n            let (_depth, width) = storage_ops::subtree_depth_width(subtree_slice);\n\n            // leaf_start represents the leaf index of the subtree where we should begin\n            // inserting the new leaves.\n            let leaf_start = if subtree_power == first_subtree_power {\n                current_leaves - ((current_leaves + 1).next_power_of_two() >> 1)\n            } else {\n                0\n            };\n\n            // The number of leaves to be inserted into this subtree.\n            let leaves_to_take = (width - leaf_start).min(remaining_leaves.len());\n            let (leaf_slice, remaining) = remaining_leaves.split_at(leaves_to_take);\n            remaining_leaves = remaining;\n\n            // Extend the subtree with the new leaves beginning at leaf_start\n            let root = if leaf_start == 0 {\n                storage_ops::init_subtree_with_leaves::<H>(\n                    subtree_slice,\n                    &self.sparse_column,\n                    leaf_slice,\n                )\n            } else {\n                storage_ops::extend_subtree_with_leaves::<H>(subtree_slice, leaf_start, leaf_slice)\n            };\n\n            // sibling_hash represents the hash of the sibling of the tip of this subtree.\n            let sibling_hash = self.storage[1 << (subtree_power - 1)];\n\n            // Update the parent node of the tip of this subtree.\n            self.storage[parent_index] = H::hash_node(&sibling_hash, &root);\n        }\n\n        // Update the number of leaves in the tree.\n        self.storage.set_num_leaves(total_leaves);\n        self.recompute_root();\n    }\n}\n\n#[cfg(test)]\nmod tests {\n\n    use rand::{thread_rng, Rng};\n    use semaphore_rs_hasher::Hasher;\n    use semaphore_rs_keccak::keccak::Keccak256;\n    use semaphore_rs_storage::{GenericStorage, MmapVec};\n    use serial_test::serial;\n\n    use super::*;\n\n    #[derive(Debug, Clone, PartialEq, Eq)]\n    pub struct TestHasher;\n    impl Hasher for TestHasher {\n        type Hash = usize;\n\n        fn hash_node(left: &Self::Hash, right: &Self::Hash) -> Self::Hash {\n            left + right\n        }\n    }\n\n    pub fn debug_tree<H, S>(tree: &CascadingMerkleTree<H, S>)\n    where\n        H: Hasher,\n        <H as Hasher>::Hash: Debug + Copy,\n        S: GenericStorage<H::Hash> + std::fmt::Debug,\n    {\n        println!(\"{tree:?}\");\n        debug_storage::<H, S>(&tree.storage);\n    }\n\n    pub fn debug_storage<H, S>(storage: &S)\n    where\n        H: Hasher,\n        <H as Hasher>::Hash: Debug + Copy,\n        S: std::ops::Deref<Target = [<H as Hasher>::Hash]> + std::fmt::Debug,\n    {\n        let storage_depth = storage.len().ilog2();\n        let storage_len = storage.len();\n        let root_index = storage_len >> 1;\n        let mut previous = vec![root_index];\n        println!(\"{:?}\", vec![storage[root_index]]);\n        for _ in 1..storage_depth {\n            let next = previous\n                .iter()\n                .flat_map(|&i| storage_ops::children(i))\n                .collect::<Vec<_>>();\n            previous = next.iter().flat_map(|&(l, r)| [l, r]).collect();\n            let row = previous.iter().map(|&i| storage[i]).collect::<Vec<_>>();\n            println!(\"{row:?}\");\n        }\n    }\n\n    #[test]\n    fn test_index_from_leaf() {\n        let mut leaf_indeces = Vec::new();\n        for i in 0..16 {\n            leaf_indeces.push(storage_ops::index_from_leaf(i));\n        }\n        let expected_leaves = vec![1, 3, 6, 7, 12, 13, 14, 15, 24, 25, 26, 27, 28, 29, 30, 31];\n        assert_eq!(leaf_indeces, expected_leaves);\n        println!(\"Leaf indeces: {:?}\", leaf_indeces);\n    }\n\n    #[test]\n    fn test_index_height_offset() {\n        let expected = vec![\n            ((0, 0), 1),\n            ((0, 1), 3),\n            ((0, 2), 6),\n            ((0, 3), 7),\n            ((0, 4), 12),\n            ((0, 5), 13),\n            ((0, 6), 14),\n            ((0, 7), 15),\n            ((1, 0), 2),\n            ((1, 1), 5),\n            ((1, 2), 10),\n            ((1, 3), 11),\n            ((2, 0), 4),\n            ((2, 1), 9),\n            ((3, 0), 8),\n        ];\n        for ((height, offset), result) in expected {\n            println!(\n                \"Height: {}, Offset: {}, expected: {}\",\n                height, offset, result\n            );\n            assert_eq!(storage_ops::index_height_offset(height, offset), result);\n        }\n    }\n\n    #[test]\n    fn test_parent() {\n        let mut parents = Vec::new();\n        for i in 1..16 {\n            parents.push((i, storage_ops::parent(i)));\n        }\n        let expected_parents = vec![\n            (1, 2),\n            (2, 4),\n            (3, 2),\n            (4, 8),\n            (5, 4),\n            (6, 5),\n            (7, 5),\n            (8, 16),\n            (9, 8),\n            (10, 9),\n            (11, 9),\n            (12, 10),\n            (13, 10),\n            (14, 11),\n            (15, 11),\n        ];\n        assert_eq!(parents, expected_parents);\n        println!(\"Parents: {:?}\", parents);\n    }\n\n    #[test]\n    fn test_sibling() {\n        let mut siblings = Vec::new();\n        for i in 1..16 {\n            siblings.push((i, storage_ops::sibling(i)));\n        }\n        use Branch::*;\n        let expected_siblings = vec![\n            (1, Left(3)),\n            (2, Left(5)),\n            (3, Right(1)),\n            (4, Left(9)),\n            (5, Right(2)),\n            (6, Left(7)),\n            (7, Right(6)),\n            (8, Left(17)),\n            (9, Right(4)),\n            (10, Left(11)),\n            (11, Right(10)),\n            (12, Left(13)),\n            (13, Right(12)),\n            (14, Left(15)),\n            (15, Right(14)),\n        ];\n        assert_eq!(siblings, expected_siblings);\n        println!(\"Siblings: {:?}\", siblings);\n    }\n\n    #[test]\n    fn test_children() {\n        let mut children = Vec::new();\n        for i in 1..16 {\n            children.push((i, storage_ops::children(i)));\n        }\n        let expected_siblings = vec![\n            (1, None),\n            (2, Some((1, 3))),\n            (3, None),\n            (4, Some((2, 5))),\n            (5, Some((6, 7))),\n            (6, None),\n            (7, None),\n            (8, Some((4, 9))),\n            (9, Some((10, 11))),\n            (10, Some((12, 13))),\n            (11, Some((14, 15))),\n            (12, None),\n            (13, None),\n            (14, None),\n            (15, None),\n        ];\n        assert_eq!(children, expected_siblings);\n        println!(\"Siblings: {:?}\", children);\n    }\n\n    #[test]\n    fn test_invalid_storage() {\n        let _ = CascadingMerkleTree::<TestHasher>::restore_unchecked(vec![2, 1, 1, 1, 1], 1, &0)\n            .expect_err(\"invalid storage len\");\n        let _ = CascadingMerkleTree::<TestHasher>::restore_unchecked(vec![3, 1, 1, 1], 1, &0)\n            .expect_err(\"invalid num leaves\");\n        let _ =\n            CascadingMerkleTree::<TestHasher>::restore_unchecked(vec![3, 1, 1, 1, 1, 1, 1], 1, &0)\n                .expect_err(\"len too long for depth\");\n    }\n\n    #[should_panic]\n    #[test]\n    fn test_hash_too_small() {\n        #[derive(Debug, Clone, PartialEq, Eq)]\n        struct InvalidHasher;\n        impl Hasher for InvalidHasher {\n            type Hash = u32;\n\n            fn hash_node(left: &Self::Hash, right: &Self::Hash) -> Self::Hash {\n                left + right\n            }\n        }\n\n        let _ = CascadingMerkleTree::<InvalidHasher>::new_with_leaves(vec![], 1, &0, &[]);\n    }\n\n    #[test]\n    fn test_min_sized_tree() {\n        let num_leaves = 1;\n        let leaves = vec![1; num_leaves];\n        let empty = 0;\n        let tree = CascadingMerkleTree::<TestHasher>::new_with_leaves(vec![], 1, &empty, &leaves);\n        tree.validate().unwrap();\n        debug_tree(&tree);\n    }\n\n    #[test]\n    fn test_set_leaf() {\n        let num_leaves = 4;\n        let leaves = vec![1; num_leaves];\n        let empty = 0;\n        let mut tree =\n            CascadingMerkleTree::<TestHasher>::new_with_leaves(vec![], 2, &empty, &leaves);\n        tree.set_leaf(3, 2);\n        tree.validate().unwrap();\n\n        let expected =\n            CascadingMerkleTree::<TestHasher>::new_with_leaves(vec![], 2, &empty, &[1, 1, 1, 2]);\n        assert_eq!(tree, expected);\n    }\n\n    #[test]\n    fn test_set_leaf_extend() {\n        let num_leaves = 2;\n        let leaves = vec![1; num_leaves];\n        let empty = 0;\n        let mut tree =\n            CascadingMerkleTree::<TestHasher>::new_with_leaves(vec![], 2, &empty, &leaves);\n        tree.set_leaf(3, 2);\n        tree.validate().unwrap();\n\n        let expected =\n            CascadingMerkleTree::<TestHasher>::new_with_leaves(vec![], 2, &empty, &[1, 1, 0, 2]);\n        assert_eq!(tree, expected);\n    }\n\n    #[should_panic]\n    #[test]\n    fn test_zero_depth_tree() {\n        let num_leaves = 1;\n        let leaves = vec![1; num_leaves];\n        let empty = 0;\n        let tree = CascadingMerkleTree::<TestHasher>::new_with_leaves(vec![], 0, &empty, &leaves);\n        debug_tree(&tree);\n    }\n\n    #[test]\n    fn test_odd_leaves() {\n        let num_leaves = 5;\n        let leaves = vec![1; num_leaves];\n        let tree = CascadingMerkleTree::<TestHasher>::new_with_leaves(vec![], 10, &0, &leaves);\n        let expected = CascadingMerkleTree::<TestHasher> {\n            depth: 10,\n            root: 5,\n            empty_value: 0,\n            sparse_column: vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n            storage: vec![5, 1, 2, 1, 4, 2, 1, 1, 5, 1, 1, 0, 1, 0, 0, 0],\n            _marker: std::marker::PhantomData,\n        };\n        debug_tree(&tree);\n        tree.validate().unwrap();\n        assert_eq!(tree, expected);\n    }\n\n    #[test]\n    fn test_even_leaves() {\n        let num_leaves = 1 << 3;\n        let leaves = vec![1; num_leaves];\n        let empty = 0;\n        let tree = CascadingMerkleTree::<TestHasher>::new_with_leaves(vec![], 10, &empty, &leaves);\n        let expected = CascadingMerkleTree::<TestHasher> {\n            depth: 10,\n            root: 8,\n            empty_value: 0,\n            sparse_column: vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n            storage: vec![8, 1, 2, 1, 4, 2, 1, 1, 8, 4, 2, 2, 1, 1, 1, 1],\n            _marker: std::marker::PhantomData,\n        };\n        debug_tree(&tree);\n        tree.validate().unwrap();\n        assert_eq!(tree, expected);\n    }\n\n    #[test]\n    fn test_no_leaves() {\n        let leaves = vec![];\n        let empty = 0;\n        let tree = CascadingMerkleTree::<TestHasher>::new_with_leaves(vec![], 10, &empty, &leaves);\n        let expected = CascadingMerkleTree::<TestHasher> {\n            depth: 10,\n            root: 0,\n            empty_value: 0,\n            sparse_column: vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n            storage: vec![0, 0],\n            _marker: std::marker::PhantomData,\n        };\n        debug_tree(&tree);\n        tree.validate().unwrap();\n        assert_eq!(tree, expected);\n    }\n\n    #[test]\n    fn test_sparse_column() {\n        let leaves = vec![];\n        let empty = 1;\n        let tree = CascadingMerkleTree::<TestHasher>::new_with_leaves(vec![], 10, &empty, &leaves);\n        let expected = CascadingMerkleTree::<TestHasher> {\n            depth: 10,\n            root: 1024,\n            empty_value: 1,\n            sparse_column: vec![1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024],\n            storage: vec![0, 1],\n            _marker: std::marker::PhantomData,\n        };\n        debug_tree(&tree);\n        tree.validate().unwrap();\n        assert_eq!(tree, expected);\n    }\n\n    #[test]\n    fn test_compute_root() {\n        let num_leaves = 1 << 3;\n        let leaves = vec![0; num_leaves];\n        let empty = 1;\n        let tree = CascadingMerkleTree::<TestHasher>::new_with_leaves(vec![], 4, &empty, &leaves);\n        let expected = CascadingMerkleTree::<TestHasher> {\n            depth: 4,\n            root: 8,\n            empty_value: 1,\n            sparse_column: vec![1, 2, 4, 8, 16],\n            storage: vec![8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n            _marker: std::marker::PhantomData,\n        };\n        debug_tree(&tree);\n        tree.validate().unwrap();\n        assert_eq!(tree, expected);\n    }\n\n    #[test]\n    fn test_get_node() {\n        let num_leaves = 3;\n        let leaves = vec![3; num_leaves];\n        let empty = 1;\n        let tree = CascadingMerkleTree::<TestHasher>::new_with_leaves(vec![], 3, &empty, &leaves);\n        debug_tree(&tree);\n        tree.validate().unwrap();\n        let expected = vec![\n            ((3, 0), 3),\n            ((3, 1), 3),\n            ((3, 2), 3),\n            ((3, 3), 1),\n            ((3, 4), 1),\n            ((3, 5), 1),\n            ((3, 6), 1),\n            ((3, 7), 1),\n            ((2, 0), 6),\n            ((2, 1), 4),\n            ((2, 2), 2),\n            ((2, 3), 2),\n            ((1, 0), 10),\n            ((1, 1), 4),\n            ((0, 0), 14),\n        ];\n        for ((depth, offset), result) in expected {\n            println!(\"Depth: {}, Offset: {}, expected: {}\", depth, offset, result);\n            assert_eq!(tree.get_node(depth, offset), result);\n        }\n    }\n\n    #[test]\n    fn test_get_leaf_from_hash() {\n        let empty = 0;\n        let mut tree = CascadingMerkleTree::<TestHasher>::new_with_leaves(vec![], 10, &empty, &[]);\n        tree.validate().unwrap();\n        for i in 1..=64 {\n            tree.push(i).unwrap();\n            tree.validate().unwrap();\n            let first = tree.get_leaf_from_hash(1).unwrap();\n            let this = tree.get_leaf_from_hash(i).unwrap();\n            assert_eq!(first, 0);\n            assert_eq!(this, i - 1);\n        }\n        assert!(tree.get_leaf_from_hash(65).is_none());\n    }\n\n    #[test]\n    fn test_row_indices() {\n        let num_leaves = 12;\n        let leaves = vec![3; num_leaves];\n        let empty = 1;\n        let tree = CascadingMerkleTree::<TestHasher>::new_with_leaves(vec![], 4, &empty, &leaves);\n        tree.validate().unwrap();\n        debug_tree(&tree);\n        let expected = vec![\n            (\n                0,\n                vec![\n                    1usize, 3, 6, 7, 12, 13, 14, 15, 24, 25, 26, 27, 28, 29, 30, 31,\n                ],\n            ),\n            (1, vec![2, 5, 10, 11, 20, 21, 22, 23]),\n            (2, vec![4, 9, 18, 19]),\n            (3, vec![8, 17]),\n            (4, vec![16]),\n        ];\n        for (height, result) in expected {\n            println!(\"Height: {}, expected: {:?}\", height, result);\n            assert_eq!(\n                <Vec<usize> as StorageOps<TestHasher>>::row_indices(&tree.storage, height)\n                    .collect::<Vec<usize>>(),\n                result\n            );\n        }\n    }\n\n    #[test]\n    fn test_row() {\n        let leaves = vec![1, 2, 3, 4, 5, 6];\n        let empty = 0;\n        let tree = CascadingMerkleTree::<TestHasher>::new_with_leaves(vec![], 20, &empty, &leaves);\n        tree.validate().unwrap();\n        debug_tree(&tree);\n        let expected = vec![\n            (0, vec![1, 2, 3, 4, 5, 6, 0, 0]),\n            (1, vec![3, 7, 11, 0]),\n            (2, vec![10, 11]),\n            (3, vec![21]),\n        ];\n        for (height, result) in expected {\n            println!(\"Height: {}, expected: {:?}\", height, result);\n            assert_eq!(\n                <Vec<usize> as StorageOps<TestHasher>>::row(&tree.storage, height)\n                    .collect::<Vec<usize>>(),\n                result\n            );\n            // assert_eq!(tree.storage.row_indices(height).collect(), result);\n        }\n    }\n\n    #[test]\n    fn test_proof_from_hash() {\n        let leaves = vec![1, 2, 3, 4, 5, 6];\n        let empty = 1;\n        let tree = CascadingMerkleTree::<TestHasher>::new_with_leaves(vec![], 4, &empty, &leaves);\n        debug_tree(&tree);\n        tree.validate().unwrap();\n        let expected = vec![\n            (\n                1,\n                vec![\n                    Branch::Left(2),\n                    Branch::Left(7),\n                    Branch::Left(13),\n                    Branch::Left(8),\n                ],\n            ),\n            (\n                2,\n                vec![\n                    Branch::Right(1),\n                    Branch::Left(7),\n                    Branch::Left(13),\n                    Branch::Left(8),\n                ],\n            ),\n            (\n                3,\n                vec![\n                    Branch::Left(4),\n                    Branch::Right(3),\n                    Branch::Left(13),\n                    Branch::Left(8),\n                ],\n            ),\n            (\n                4,\n                vec![\n                    Branch::Right(3),\n                    Branch::Right(3),\n                    Branch::Left(13),\n                    Branch::Left(8),\n                ],\n            ),\n            (\n                5,\n                vec![\n                    Branch::Left(6),\n                    Branch::Left(2),\n                    Branch::Right(10),\n                    Branch::Left(8),\n                ],\n            ),\n            (\n                6,\n                vec![\n                    Branch::Right(5),\n                    Branch::Left(2),\n                    Branch::Right(10),\n                    Branch::Left(8),\n                ],\n            ),\n        ];\n        for (leaf, expected_proof) in expected {\n            let proof = tree.proof_from_hash(leaf).unwrap();\n            assert_eq!(proof.0, expected_proof);\n            assert!(tree.verify(leaf, &proof));\n        }\n    }\n\n    #[test]\n    fn test_leaves() {\n        let mut tree = CascadingMerkleTree::<TestHasher>::new(vec![], 22, &0);\n        debug_tree(&tree);\n        tree.validate().unwrap();\n        let expected: Vec<usize> = vec![];\n        assert_eq!(tree.leaves().collect::<Vec<_>>(), expected);\n\n        tree.push(1).unwrap();\n        debug_tree(&tree);\n        tree.validate().unwrap();\n        assert_eq!(tree.leaves().collect::<Vec<_>>(), vec![1]);\n\n        tree.push(1).unwrap();\n        debug_tree(&tree);\n        tree.validate().unwrap();\n        assert_eq!(tree.leaves().collect::<Vec<_>>(), vec![1, 1]);\n\n        tree.push(1).unwrap();\n        debug_tree(&tree);\n        tree.validate().unwrap();\n        assert_eq!(tree.leaves().collect::<Vec<_>>(), vec![1, 1, 1]);\n\n        tree.push(1).unwrap();\n        debug_tree(&tree);\n        tree.validate().unwrap();\n        assert_eq!(tree.leaves().collect::<Vec<_>>(), vec![1, 1, 1, 1]);\n    }\n\n    type Hash = <Keccak256 as Hasher>::Hash;\n\n    #[test]\n    fn test_extend_from_slice_keccak() -> color_eyre::Result<()> {\n        let leaves = (0..1 << 5)\n            .map(|n: u64| {\n                let b = n.to_be_bytes();\n                let mut hash = [0; 32];\n\n                hash[..8].copy_from_slice(&b);\n\n                hash\n            })\n            .collect::<Vec<_>>();\n\n        // Create expected tree\n        let expected_tree =\n            CascadingMerkleTree::<Keccak256>::new_with_leaves(vec![], 10, &[0; 32], &leaves);\n\n        let mut tree = CascadingMerkleTree::<Keccak256>::new(vec![], 10, &[0; 32]);\n        tree.extend_from_slice(&leaves);\n\n        assert_eq!(\n            tree.leaves().collect::<Vec<Hash>>(),\n            expected_tree.leaves().collect::<Vec<Hash>>()\n        );\n\n        assert_eq!(tree.root(), expected_tree.root());\n        Ok(())\n    }\n\n    #[test]\n    fn test_push_beyond_depth() {\n        let mut tree = CascadingMerkleTree::<TestHasher>::new(vec![], 2, &1);\n        for _ in 0..4 {\n            tree.push(2).unwrap();\n        }\n        let _ = tree.push(2).unwrap_err();\n    }\n\n    #[test]\n    #[should_panic]\n    fn test_init_beyond_depth() {\n        let _tree = CascadingMerkleTree::<TestHasher>::new_with_leaves(vec![], 2, &1, &[2; 5]);\n    }\n\n    #[test]\n    #[should_panic]\n    fn test_extend_beyond_depth() {\n        let mut tree = CascadingMerkleTree::<TestHasher>::new(vec![], 2, &1);\n        tree.extend_from_slice(&[2; 4]);\n        tree.extend_from_slice(&[2; 1]);\n    }\n\n    #[test]\n    fn test_push() {\n        let mut tree = CascadingMerkleTree::<TestHasher>::new(vec![], 30, &1);\n        let mut vec = vec![];\n        for _ in 0..300 {\n            tree.push(2).unwrap();\n            vec.push(2);\n            debug_tree(&tree);\n            tree.validate().unwrap();\n            assert_eq!(tree.leaves().collect::<Vec<usize>>(), vec);\n        }\n    }\n\n    #[test]\n    fn test_extend_from_slice() {\n        for increment in 1..20 {\n            let mut tree = CascadingMerkleTree::<TestHasher>::new(vec![], 30, &1);\n            let mut vec = vec![];\n            for _ in 0..20 {\n                tree.extend_from_slice(&vec![2; increment]);\n                vec.extend_from_slice(&vec![2; increment]);\n                debug_tree(&tree);\n                tree.validate().unwrap();\n                assert_eq!(tree.leaves().collect::<Vec<usize>>(), vec);\n            }\n        }\n    }\n\n    #[test]\n    fn test_extend_from_slice_2() {\n        for increment in 1..20 {\n            let mut tree = CascadingMerkleTree::<Keccak256>::new(vec![], 30, &[0; 32]);\n            let mut vec = vec![];\n            for _ in 0..20 {\n                let slice = (0..increment)\n                    .map(|_| {\n                        let mut hash = [0; 32];\n\n                        let mut rng = thread_rng();\n                        rng.fill(&mut hash);\n\n                        hash\n                    })\n                    .collect::<Vec<_>>();\n                tree.extend_from_slice(&slice);\n                vec.extend_from_slice(&slice);\n                tree.validate().unwrap();\n                assert_eq!(tree.leaves().collect::<Vec<_>>(), vec);\n            }\n        }\n    }\n\n    #[test]\n    fn test_vec_realloc_speed() {\n        let empty = 0;\n        let leaves = vec![1; 1 << 20];\n        let mut tree =\n            CascadingMerkleTree::<TestHasher, Vec<_>>::new_with_leaves(vec![], 30, &empty, &leaves);\n        let start = std::time::Instant::now();\n        tree.push(1).unwrap();\n        let elapsed = start.elapsed();\n        println!(\n            \"Leaf index: {}, Time: {:?}ms\",\n            tree.num_leaves(),\n            elapsed.as_millis()\n        );\n    }\n\n    #[test]\n    #[serial]\n    fn test_mmap_realloc_speed() {\n        let empty = 0;\n        let leaves = vec![1; 1 << 20];\n\n        println!(\"Create tempfile\");\n        let tempfile = tempfile::tempfile().unwrap();\n        println!(\"Init mmap\");\n        let mmap_vec: MmapVec<_> = unsafe { MmapVec::restore(tempfile).unwrap() };\n\n        println!(\"Init tree\");\n        let mut tree = CascadingMerkleTree::<TestHasher, MmapVec<_>>::new_with_leaves(\n            mmap_vec, 30, &empty, &leaves,\n        );\n\n        println!(\"test push\");\n        let start = std::time::Instant::now();\n        tree.push(1).unwrap();\n        let elapsed = start.elapsed();\n        println!(\n            \"Leaf index: {}, Time: {:?}ms\",\n            tree.num_leaves(),\n            elapsed.as_millis()\n        );\n    }\n\n    #[test]\n    #[serial]\n    fn test_restore_from_cache() -> color_eyre::Result<()> {\n        let mut rng = rand::thread_rng();\n\n        let leaves: Vec<Hash> = (0..1 << 2)\n            .map(|_| {\n                let mut hash = [0; 32];\n                rng.fill(&mut hash);\n                hash\n            })\n            .collect::<Vec<Hash>>();\n\n        // Create a new tmp file for mmap storage\n        let tempfile = tempfile::NamedTempFile::new()?;\n        let file_path = tempfile.path().to_owned();\n\n        // Initialize the expected tree\n        let mmap_vec: MmapVec<_> = unsafe { MmapVec::restore(tempfile.reopen()?).unwrap() };\n        let expected_tree = CascadingMerkleTree::<Keccak256, MmapVec<_>>::new_with_leaves(\n            mmap_vec, 3, &[0; 32], &leaves,\n        );\n\n        let expected_root = expected_tree.root();\n        let expected_leaves = expected_tree.leaves().collect::<Vec<Hash>>();\n\n        drop(expected_tree);\n\n        // Restore the tree\n        let mmap_vec: MmapVec<_> = unsafe { MmapVec::restore_from_path(file_path).unwrap() };\n        let tree = CascadingMerkleTree::<Keccak256, MmapVec<_>>::restore(mmap_vec, 3, &[0; 32])?;\n\n        // Assert that the root and the leaves are as expected\n        assert_eq!(tree.root(), expected_root);\n        assert_eq!(tree.leaves().collect::<Vec<Hash>>(), expected_leaves);\n\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "crates/trees/src/cascading/storage_ops.rs",
    "content": "use std::ops::{Deref, DerefMut, Range};\n\nuse bytemuck::Pod;\nuse color_eyre::eyre::{bail, ensure};\nuse color_eyre::Result;\nuse rayon::prelude::*;\nuse semaphore_rs_hasher::Hasher;\nuse semaphore_rs_storage::GenericStorage;\n\nuse crate::proof::Branch;\n\npub trait StorageOps<H>:\n    GenericStorage<H::Hash>\n    + Deref<Target = [H::Hash]>\n    + DerefMut<Target = [H::Hash]>\n    + Send\n    + Sync\n    + Sized\nwhere\n    H: Hasher,\n    <H as Hasher>::Hash: Copy + Pod + Eq + Send + Sync,\n{\n    /// Clears the current storage and initializes it with the given leaves.\n    fn populate_with_leaves(\n        &mut self,\n        sparse_column: &[H::Hash],\n        empty_value: &H::Hash,\n        leaves: &[H::Hash],\n    ) {\n        let num_leaves = leaves.len();\n        let base_len = num_leaves.next_power_of_two();\n        let storage_size = base_len << 1;\n        self.clear();\n        self.extend(std::iter::repeat_n(*empty_value, storage_size));\n        let depth = base_len.ilog2();\n\n        // We iterate over subsequently larger subtrees\n        let mut sibling_hash = *leaves.first().unwrap_or(empty_value);\n        self[1] = sibling_hash;\n        for subtree_power in 1..(depth + 1) {\n            let parent_index = 1 << subtree_power;\n            let subtree_slice = &mut self[parent_index..(parent_index << 1)];\n            let leaf_start = parent_index >> 1;\n            let leaf_end = parent_index.min(num_leaves);\n            let leaf_slice = &leaves[leaf_start..leaf_end];\n            let root = init_subtree_with_leaves::<H>(subtree_slice, sparse_column, leaf_slice);\n            let hash = H::hash_node(&sibling_hash, &root);\n            self[parent_index] = hash;\n            sibling_hash = hash;\n        }\n\n        self.set_num_leaves(num_leaves);\n    }\n\n    /// Returns an iterator over all leaves including those that have noe been\n    /// set.\n    fn leaves(&self) -> impl Iterator<Item = H::Hash> + '_ {\n        self.row_indices(0)\n            .take(self.num_leaves())\n            .map(move |i| self[i])\n    }\n\n    fn row_indices(&self, height: usize) -> impl Iterator<Item = usize> + Send + '_ {\n        let storage_height = (self.len().ilog2() - 1) as usize;\n        let width = if height > storage_height {\n            0\n        } else {\n            let height_diff = storage_height - height;\n            1 << height_diff\n        };\n        row_indices(height).take(width)\n    }\n\n    fn row(&self, height: usize) -> impl Iterator<Item = H::Hash> + Send + '_ {\n        self.row_indices(height).map(move |i| self[i])\n    }\n\n    /// Returns the root hash of the growable storage, not the top level root.\n    fn storage_root(&self) -> H::Hash {\n        self[self.len() >> 1]\n    }\n\n    /// Returns the depth of growable storage, not the top level root.\n    fn storage_depth(&self) -> usize {\n        subtree_depth(self)\n    }\n\n    /// Sets the number of leaves.\n    fn set_num_leaves(&mut self, amount: usize) {\n        let leaf_counter: &mut [usize] = bytemuck::cast_slice_mut(&mut self[0..1]);\n        leaf_counter[0] = amount;\n    }\n\n    fn num_leaves(&self) -> usize {\n        bytemuck::cast_slice(&self[0..1])[0]\n    }\n\n    /// Increments the number of leaves.\n    fn increment_num_leaves(&mut self, amount: usize) {\n        let leaf_counter: &mut [usize] = bytemuck::cast_slice_mut(&mut self[0..1]);\n        leaf_counter[0] += amount;\n    }\n\n    /// Propagates new hashes up the top of the subtree.\n    fn propagate_up(&mut self, mut index: usize) -> Option<()> {\n        loop {\n            let (left, right) = match sibling(index) {\n                Branch::Left(sibling) => (index, sibling),\n                Branch::Right(sibling) => (sibling, index),\n            };\n            let left_hash = self.get(left)?;\n            let right_hash = self.get(right)?;\n            let parent_index = parent(index);\n            self[parent_index] = H::hash_node(left_hash, right_hash);\n            index = parent_index;\n        }\n    }\n\n    /// Performs partial constant time validation of the storage.\n    fn validate_const(&self) -> Result<()> {\n        let len = self.len();\n\n        ensure!(\n            len.is_power_of_two(),\n            \"Storage length ({len}) must be a power of 2\"\n        );\n        ensure!(len > 1, \"Storage length ({len}) must be greater than 1\");\n\n        Ok(())\n    }\n    /// Validates all elements of the storage, ensuring that they\n    /// correspond to a valid tree.\n    fn validate(&self, empty_value: &H::Hash) -> Result<()> {\n        self.validate_const()?;\n        let len = self.len();\n        let width = len >> 1;\n        let depth = width.ilog2() as usize;\n\n        let num_leaves = self.num_leaves();\n        let first_empty = index_from_leaf(num_leaves);\n\n        if first_empty < len {\n            self[first_empty..].par_iter().try_for_each(|hash| {\n                if hash != empty_value {\n                    bail!(\"Storage contains non-empty values past the last leaf\");\n                }\n                Ok(())\n            })?;\n        }\n\n        for height in 0..=depth {\n            let row = self.row(height);\n            let parents = self.row(height + 1);\n            let row_couple = itertools::Itertools::tuples(row);\n\n            parents\n                .zip(row_couple)\n                .par_bridge()\n                .try_for_each(|(parent, (left, right))| {\n                    let expected = H::hash_node(&left, &right);\n                    if parent != expected {\n                        bail!(\"Invalid hash\");\n                    }\n                    Ok(())\n                })?;\n        }\n\n        Ok(())\n    }\n}\n\nimpl<H, S> StorageOps<H> for S\nwhere\n    H: Hasher,\n    S: GenericStorage<H::Hash>,\n    <H as Hasher>::Hash: Copy + Pod + Eq + Send + Sync,\n{\n}\n\n/// Assumes that slice len is a power of 2\n#[inline]\npub fn subtree_depth<H>(storage_slice: &[H]) -> usize {\n    let len = storage_slice.len();\n\n    debug_assert!(len.is_power_of_two());\n    debug_assert!(len > 1);\n\n    (len >> 1).ilog2() as usize\n}\n\npub fn sibling(i: usize) -> Branch<usize> {\n    let next_pow = i.next_power_of_two();\n    if i == next_pow {\n        return Branch::Left((i << 1) + 1);\n    }\n    let prev_pow = next_pow >> 1;\n    if i - 1 == prev_pow {\n        return Branch::Right(prev_pow >> 1);\n    }\n    if i & 1 == 0 {\n        // even\n        Branch::Left(i + 1)\n    } else {\n        // odd\n        Branch::Right(i - 1)\n    }\n}\n\npub fn parent(i: usize) -> usize {\n    if i.is_power_of_two() {\n        return i << 1;\n    }\n    let prev_pow = i.next_power_of_two() >> 1;\n    let shifted = i - prev_pow;\n    let shifted_parent = shifted >> 1;\n    shifted_parent + prev_pow\n}\n\n// leaves are 0 indexed\npub fn index_from_leaf(leaf: usize) -> usize {\n    leaf + (leaf + 1).next_power_of_two()\n}\n\npub fn leaf_from_index(index: usize) -> usize {\n    let next = (index + 1).next_power_of_two();\n    let prev = next >> 1;\n    index - prev\n}\n\npub fn index_height_offset(height: usize, offset: usize) -> usize {\n    if offset == 0 {\n        return 1 << height;\n    }\n    let leaf = offset * (1 << height);\n    let subtree_size = (leaf + 1).next_power_of_two();\n    let offset_node = leaf >> height;\n    offset_node + subtree_size\n}\n\n#[cfg(test)]\npub fn children(i: usize) -> Option<(usize, usize)> {\n    let next_pow = i.next_power_of_two();\n    if i == next_pow {\n        if i == 1 {\n            return None;\n        }\n        let left = i >> 1;\n        let right = i + 1;\n        return Some((left, right));\n    }\n    let prev_pow = next_pow >> 1;\n    let half = prev_pow >> 1;\n\n    let offset = i - prev_pow;\n    if offset >= half {\n        return None;\n    }\n\n    let offset_left = offset * 2;\n    let offset_right = offset_left + 1;\n\n    Some((prev_pow + offset_left, prev_pow + offset_right))\n}\n\n/// Initialize a subtree with the given leaves in parallel.\n///\n/// O(n) time complexity\n///\n/// Subtrees are 1 indexed and directly attached to the left most branch\n/// of the main tree.\n///\n/// This function assumes that storage is already initialized with empty\n/// values and is the correct length for the subtree.\n/// If 'leaves' is not long enough, the remaining leaves will be left empty\n///\n/// storage.len() must be a power of 2 and greater than or equal to 2\n/// storage is 1 indexed\n///\n/// ```markdown\n///           8    (subtree)\n///      4      [     9     ]\n///   2     5   [  10    11 ]\n/// 1  3  6  7  [12 13 14 15]\n///  ```\npub fn init_subtree_with_leaves<H>(\n    subtree: &mut [H::Hash],\n    sparse_column: &[H::Hash],\n    leaves: &[H::Hash],\n) -> H::Hash\nwhere\n    H: Hasher,\n    <H as Hasher>::Hash: Copy + Pod + Eq + Send + Sync,\n{\n    let (_depth, width) = subtree_depth_width(subtree);\n\n    // Set the leaves\n    subtree[(width)..(width + leaves.len())]\n        .par_iter_mut()\n        .zip(leaves.par_iter())\n        .for_each(|(val, leaf)| {\n            *val = *leaf;\n        });\n\n    // For empty values to the right of the newly set leaves\n    // we can prapogate the sparse column up the tree\n    // in O(log(n)) hashes\n    sparse_fill_partial_subtree::<H>(subtree, sparse_column, leaves.len()..width);\n\n    // For newly set leaves we can prapogate the hashes up the tree\n    // in O(n) hashes\n    propagate_partial_subtree::<H>(subtree, 0..leaves.len());\n\n    subtree[1]\n}\n\n/// Extend leaves onto a preexisting subtree. This method assumes that the\n/// sparse column has already been applied to all rows\n///\n/// O(n) time complexity\n///\n/// Subtrees are 1 indexed and directly attached to the left most branch\n/// of the main tree.\n///\n/// This function assumes that storage is already initialized with empty\n/// values and is the correct length for the subtree.\n/// If 'leaves' is not long enough, the remaining leaves will be left empty\n///\n/// storage.len() must be a power of 2 and greater than or equal to 2\n/// storage is 1 indexed\n///\n/// ```markdown\n///           8    (subtree)\n///      4      [     9     ]\n///   2     5   [  10    11 ]\n/// 1  3  6  7  [12 13 14 15]\n///  ```\npub fn extend_subtree_with_leaves<H>(\n    subtree: &mut [H::Hash],\n    start: usize,\n    leaves: &[H::Hash],\n) -> H::Hash\nwhere\n    H: Hasher,\n    <H as Hasher>::Hash: Copy + Pod + Eq + Send + Sync,\n{\n    let (_depth, width) = subtree_depth_width(subtree);\n\n    // Set the leaves\n    subtree[(width + start)..(width + start + leaves.len())]\n        .par_iter_mut()\n        .zip(leaves.par_iter())\n        .for_each(|(val, leaf)| {\n            *val = *leaf;\n        });\n\n    // For newly set leaves we can propagate the hashes up the tree\n    // in O(n) hashes\n    propagate_partial_subtree::<H>(subtree, start..start + leaves.len());\n\n    subtree[1]\n}\n\n/// Propagate hashes up a subtree with leaves within a given range.\n///\n/// O(n) time complexity\n///\n/// Subtrees are 1 indexed and directly attached to the left most branch\n/// of the main tree.\n///\n/// This function assumes that the tree is in a valid state except for the\n/// newly added leaves.\n///\n/// storage.len() must be a power of 2 and greater than or equal to 2\n/// storage is 1 indexed\n///\n/// ```markdown\n///           8    (subtree)\n///      4      [     9     ]\n///   2     5   [  10    11 ]\n/// 1  3  6  7  [12 13 14 15]\n///  ```\npub fn propagate_partial_subtree<H>(subtree: &mut [H::Hash], mut range: Range<usize>) -> H::Hash\nwhere\n    H: Hasher,\n    <H as Hasher>::Hash: Copy + Pod + Eq + Send + Sync,\n{\n    let depth = subtree_depth(subtree);\n\n    // Iterate over mutable layers of the tree\n    for current_depth in (1..=depth).rev() {\n        if range.is_empty() {\n            break;\n        }\n\n        // Split the subtree into relavent layers\n        let (top, child_layer) = subtree.split_at_mut(1 << current_depth);\n        let parent_layer = &mut top[(1 << (current_depth - 1))..];\n\n        // Update the range to match the new parent layer\n        range.start /= 2;\n        range.end = ((range.end - 1) / 2) + 1;\n\n        parent_layer[range.clone()]\n            .par_iter_mut()\n            .enumerate()\n            .for_each(|(i, value)| {\n                let i = i + range.start;\n                let left = &child_layer[2 * i];\n                let right = &child_layer[2 * i + 1];\n                *value = H::hash_node(left, right);\n            });\n    }\n\n    subtree[1]\n}\n\n/// Propagates empty hashes up the tree within a given range.\n///\n/// O(log(n)) time complexity\n///\n/// Subtrees are 1 indexed and directly attached to the left most branch\n/// of the main tree.\n///\n/// This function will overwrite any  existing or dependent values withing the\n/// range. It assumes that the base layer has already been initialized with\n/// empty values.\n///\n/// storage.len() must be a power of 2 and greater than or equal to 2\n/// storage is 1 indexed\n///\n/// ```markdown\n///           8    (subtree)\n///      4      [     9     ]\n///   2     5   [  10    11 ]\n/// 1  3  6  7  [12 13 14 15]\n///  ```\npub fn sparse_fill_partial_subtree<H>(\n    subtree: &mut [H::Hash],\n    sparse_column: &[H::Hash],\n    mut range: Range<usize>,\n) -> H::Hash\nwhere\n    H: Hasher,\n    <H as Hasher>::Hash: Copy + Pod + Eq + Send + Sync,\n{\n    let depth = subtree_depth(subtree);\n\n    // Iterate over mutable layers of the tree\n    for current_depth in (1..=depth).rev() {\n        if range.is_empty() {\n            break;\n        }\n\n        // Split the subtree into relavent layers\n        let (top, _child_layer) = subtree.split_at_mut(1 << current_depth);\n        let parent_layer = &mut top[(1 << (current_depth - 1))..];\n\n        // Update the range to match the new parent layer\n        range.start /= 2;\n        range.end = ((range.end - 1) / 2) + 1;\n\n        parent_layer[range.clone()].par_iter_mut().for_each(|i| {\n            *i = sparse_column[depth + 1 - current_depth];\n        });\n    }\n\n    subtree[1]\n}\n\nfn row_indices(height: usize) -> impl Iterator<Item = usize> + Send {\n    let first = 1 << height;\n    let iter_1 = first..(first + 1);\n\n    let next = (first << 1) + 1;\n\n    let iter_2 = (0..).scan(next, |next, i| {\n        let slice_len = 1 << i;\n        let res = *next..(*next + slice_len);\n        *next *= 2;\n        Some(res)\n    });\n\n    std::iter::once(iter_1).chain(iter_2).flatten()\n}\n\n/// Assumes that slice len is a power of 2\n#[inline]\npub fn subtree_depth_width<H>(storage_slice: &[H]) -> (usize, usize) {\n    let len = storage_slice.len();\n\n    debug_assert!(len.is_power_of_two());\n    debug_assert!(len > 1);\n\n    let width = len >> 1;\n    let depth = width.ilog2() as usize;\n\n    (depth, width)\n}\n\n#[cfg(test)]\nmod tests {\n    use semaphore_rs_keccak::keccak::Keccak256;\n    use semaphore_rs_storage::MmapVec;\n\n    use super::super::tests::TestHasher;\n    use super::*;\n\n    fn test_is_storage_ops<S>(_s: &S)\n    where\n        S: StorageOps<Keccak256>,\n    {\n    }\n\n    // A compile time test to verify that MmapVec is StorageOps\n    #[allow(unused)]\n    fn test_mmap_vec_is_storage_ops(s: MmapVec<<Keccak256 as Hasher>::Hash>) {\n        test_is_storage_ops(&s);\n    }\n\n    #[test]\n    fn test_sparse_fill_partial_subtree() {\n        let mut storage = vec![1; 16];\n        let sparse_column = vec![1, 2, 4, 8, 16];\n        sparse_fill_partial_subtree::<TestHasher>(&mut storage, &sparse_column, 4..8);\n        let expected = vec![1, 8, 1, 4, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1];\n        assert_eq!(storage, expected);\n    }\n\n    #[test]\n    fn test_propagate_partial_subtree_noop_on_empty_range() {\n        let mut subtree = vec![0usize; 4]; // depth=1 subtree\n        let before = subtree.clone();\n        propagate_partial_subtree::<TestHasher>(&mut subtree, 0..0);\n        assert_eq!(subtree, before);\n    }\n\n    #[test]\n    fn test_sparse_fill_partial_subtree_noop_on_empty_range() {\n        let mut subtree = vec![0usize; 4];\n        let sparse_column = vec![0, 0, 0];\n        let before = subtree.clone();\n        sparse_fill_partial_subtree::<TestHasher>(&mut subtree, &sparse_column, 0..0);\n        assert_eq!(subtree, before);\n    }\n}\n"
  },
  {
    "path": "crates/trees/src/imt/mod.rs",
    "content": "//! Implements basic binary Merkle trees\n\nuse std::fmt::Debug;\nuse std::iter::{once, repeat_n, successors};\n\nuse bytemuck::Pod;\nuse derive_where::derive_where;\nuse semaphore_rs_hasher::Hasher;\n\nuse crate::proof::{Branch, InclusionProof};\n\n/// Merkle tree with all leaf and intermediate hashes stored\n#[derive_where(Clone; <H as Hasher>::Hash: Clone)]\n#[derive_where(PartialEq; <H as Hasher>::Hash: PartialEq)]\n#[derive_where(Eq; <H as Hasher>::Hash: Eq)]\n#[derive_where(Debug; <H as Hasher>::Hash: Debug)]\npub struct MerkleTree<H>\nwhere\n    H: Hasher,\n{\n    /// Depth of the tree, # of layers including leaf layer\n    depth: usize,\n\n    /// Hash value of empty subtrees of given depth, starting at leaf level\n    empty: Vec<H::Hash>,\n\n    /// Hash values of tree nodes and leaves, breadth first order\n    nodes: Vec<H::Hash>,\n}\n\n/// For a given node index, return the parent node index\n/// Returns None if there is no parent (root node)\nconst fn parent(index: usize) -> Option<usize> {\n    if index <= 1 {\n        None\n    } else {\n        Some(index >> 1)\n    }\n}\n\n/// For a given node index, return index of the first (left) child.\nconst fn left_child(index: usize) -> usize {\n    index << 1\n}\n\nconst fn depth(index: usize) -> usize {\n    // `n.next_power_of_two()` will return `n` iff `n` is a power of two.\n    // The extra offset corrects this.\n    if index <= 1 {\n        return 0;\n    }\n\n    index.ilog2() as usize\n}\n\nimpl<H> MerkleTree<H>\nwhere\n    H: Hasher,\n    <H as Hasher>::Hash: Clone + Copy + Pod + Eq + Debug,\n{\n    /// Creates a new `MerkleTree`\n    /// * `depth` - The depth of the tree, including the root. This is 1 greater\n    ///   than the `treeLevels` argument to the Semaphore contract.\n    pub fn new(depth: usize, initial_leaf: H::Hash) -> Self {\n        // Compute empty node values, leaf to root\n        let empty = successors(Some(initial_leaf), |prev| Some(H::hash_node(prev, prev)))\n            .take(depth + 1)\n            .collect::<Vec<_>>();\n\n        // Compute node values\n        let first_node = std::iter::once(initial_leaf);\n        let nodes = empty\n            .iter()\n            .rev()\n            .enumerate()\n            .flat_map(|(depth, hash)| repeat_n(hash, 1 << depth))\n            .cloned();\n\n        let nodes = first_node.chain(nodes).collect();\n\n        Self {\n            depth,\n            empty,\n            nodes,\n        }\n    }\n\n    #[must_use]\n    pub fn num_leaves(&self) -> usize {\n        1 << self.depth\n    }\n\n    #[must_use]\n    pub fn root(&self) -> H::Hash {\n        self.nodes[1]\n    }\n\n    pub fn set(&mut self, leaf: usize, hash: H::Hash) {\n        self.set_range(leaf, once(hash));\n    }\n\n    pub fn set_range<I: IntoIterator<Item = H::Hash>>(&mut self, start: usize, hashes: I) {\n        let index = self.num_leaves() + start;\n\n        let mut count = 0;\n        // TODO: Error/panic when hashes is longer than available leafs\n        for (leaf, hash) in self.nodes[index..].iter_mut().zip(hashes) {\n            *leaf = hash;\n            count += 1;\n        }\n\n        if count != 0 {\n            self.update_nodes(index, index + (count - 1));\n        }\n    }\n\n    fn update_nodes(&mut self, start: usize, end: usize) {\n        debug_assert_eq!(depth(start), depth(end));\n        if let (Some(start), Some(end)) = (parent(start), parent(end)) {\n            for parent in start..=end {\n                let child = left_child(parent);\n                self.nodes[parent] = H::hash_node(&self.nodes[child], &self.nodes[child + 1]);\n            }\n            self.update_nodes(start, end);\n        }\n    }\n\n    #[must_use]\n    pub fn proof(&self, leaf: usize) -> Option<InclusionProof<H>> {\n        if leaf >= self.num_leaves() {\n            return None;\n        }\n        let mut index = self.num_leaves() + leaf;\n        let mut path = Vec::with_capacity(self.depth);\n        while let Some(parent) = parent(index) {\n            // Add proof for node at index to parent\n            path.push(match index & 1 {\n                1 => Branch::Right(self.nodes[index - 1]),\n                0 => Branch::Left(self.nodes[index + 1]),\n                _ => unreachable!(),\n            });\n            index = parent;\n        }\n        Some(InclusionProof(path))\n    }\n\n    #[must_use]\n    pub fn verify(&self, hash: H::Hash, proof: &InclusionProof<H>) -> bool {\n        proof.root(hash) == self.root()\n    }\n\n    #[must_use]\n    pub fn leaves(&self) -> &[H::Hash] {\n        &self.nodes[(self.num_leaves() - 1)..]\n    }\n}\n\nimpl<H: Hasher> InclusionProof<H> {\n    /// Compute the leaf index for this proof\n    #[must_use]\n    pub fn leaf_index(&self) -> usize {\n        self.0.iter().rev().fold(0, |index, branch| match branch {\n            Branch::Left(_) => index << 1,\n            Branch::Right(_) => (index << 1) + 1,\n        })\n    }\n\n    /// Compute the Merkle root given a leaf hash\n    #[must_use]\n    pub fn root(&self, hash: H::Hash) -> H::Hash {\n        self.0.iter().fold(hash, |hash, branch| match branch {\n            Branch::Left(sibling) => H::hash_node(&hash, sibling),\n            Branch::Right(sibling) => H::hash_node(sibling, &hash),\n        })\n    }\n}\n\n#[cfg(test)]\npub mod test {\n    use hex_literal::hex;\n    use ruint::aliases::U256;\n    use semaphore_rs_keccak::keccak::Keccak256;\n    use semaphore_rs_poseidon::Poseidon;\n    use test_case::test_case;\n\n    use super::*;\n\n    #[test_case(0 => None)]\n    #[test_case(1 => None)]\n    #[test_case(2 => Some(1))]\n    #[test_case(3 => Some(1))]\n    #[test_case(4 => Some(2))]\n    #[test_case(5 => Some(2))]\n    #[test_case(6 => Some(3))]\n    #[test_case(27 => Some(13))]\n    fn parent_of(index: usize) -> Option<usize> {\n        parent(index)\n    }\n\n    #[test_case(0 => 0 ; \"Nonsense case\")]\n    #[test_case(1 => 2)]\n    #[test_case(2 => 4)]\n    #[test_case(3 => 6)]\n    fn left_child_of(index: usize) -> usize {\n        left_child(index)\n    }\n\n    #[test_case(0 => 0)]\n    #[test_case(1 => 0)]\n    #[test_case(2 => 1)]\n    #[test_case(3 => 1)]\n    #[test_case(6 => 2)]\n    fn depth_of(index: usize) -> usize {\n        depth(index)\n    }\n\n    #[test_case(2 => hex!(\"b4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30\"))]\n    fn empty_keccak(depth: usize) -> [u8; 32] {\n        let tree = MerkleTree::<Keccak256>::new(depth, [0; 32]);\n\n        tree.root()\n    }\n\n    #[test]\n    fn simple_poseidon() {\n        let mut tree = MerkleTree::<Poseidon>::new(10, U256::ZERO);\n\n        let expected_root = ruint::uint!(\n            12413880268183407374852357075976609371175688755676981206018884971008854919922_U256\n        );\n        assert_eq!(tree.root(), expected_root);\n\n        tree.set(0, ruint::uint!(1_U256));\n\n        let expected_root = ruint::uint!(\n            467068234150758165281816522946040748310650451788100792957402532717155514893_U256\n        );\n        assert_eq!(tree.root(), expected_root);\n    }\n}\n"
  },
  {
    "path": "crates/trees/src/lazy/mod.rs",
    "content": "use std::fs::OpenOptions;\nuse std::io::Write;\nuse std::iter::{once, repeat_n, successors};\nuse std::ops::{Deref, DerefMut};\nuse std::path::PathBuf;\nuse std::str::FromStr;\nuse std::sync::{Arc, Mutex};\n\nuse mmap_rs::{MmapFlags, MmapMut, MmapOptions};\nuse rayon::prelude::*;\nuse semaphore_rs_hasher::{Hash, Hasher};\nuse thiserror::Error;\n\nuse crate::{Branch, InclusionProof};\n\npub trait VersionMarker {}\n#[derive(Debug)]\npub struct Canonical;\nimpl VersionMarker for Canonical {}\n#[derive(Debug)]\npub struct Derived;\nimpl VersionMarker for Derived {}\n\n/// A storage-optimized merkle tree.\n///\n/// It has a certain linear-buffer represented\n/// prefix subtree and the rest of the tree is represented using lazy,\n/// pointer-based structures. This makes it possible to hold even large trees in\n/// memory, assuming only a relatively small subset is ever modified.\n///\n/// It exposes an immutable API, so that multiple versions can be kept in memory\n/// while reusing as much structure as possible.\n///\n/// The update method also allows the specification of a mutability hint, which\n/// can be used to vastly improve storage characteristics, but also requires the\n/// caller to ensure certain additional invariants hold. See\n/// [`LazyMerkleTree::update_with_mutation`] for details.\npub struct LazyMerkleTree<H: Hasher, V: VersionMarker = Derived> {\n    tree: AnyTree<H>,\n    _version: V,\n}\n\nimpl<H, Version> LazyMerkleTree<H, Version>\nwhere\n    H: Hasher,\n    <H as Hasher>::Hash: Hash,\n    Version: VersionMarker,\n{\n    /// Creates a new, fully lazy (without any dense prefix) tree.\n    #[must_use]\n    pub fn new(depth: usize, empty_value: H::Hash) -> LazyMerkleTree<H, Canonical> {\n        LazyMerkleTree {\n            tree: AnyTree::new(depth, empty_value),\n            _version: Canonical,\n        }\n    }\n\n    /// Creates a new tree with a dense prefix of the given depth.\n    #[must_use]\n    pub fn new_with_dense_prefix(\n        depth: usize,\n        prefix_depth: usize,\n        empty_value: &H::Hash,\n    ) -> LazyMerkleTree<H, Canonical> {\n        LazyMerkleTree {\n            tree: AnyTree::new_with_dense_prefix(depth, prefix_depth, empty_value),\n            _version: Canonical,\n        }\n    }\n\n    /// Creates a new tree with a dense prefix of the given depth, and with\n    /// initial leaves populated from the given slice.\n    #[must_use]\n    pub fn new_with_dense_prefix_with_initial_values(\n        depth: usize,\n        prefix_depth: usize,\n        empty_value: &H::Hash,\n        initial_values: &[H::Hash],\n    ) -> LazyMerkleTree<H, Canonical> {\n        LazyMerkleTree {\n            tree: AnyTree::new_with_dense_prefix_with_initial_values(\n                depth,\n                prefix_depth,\n                empty_value,\n                initial_values,\n            ),\n            _version: Canonical,\n        }\n    }\n\n    /// Creates a new memory mapped file specified by path and creates a tree\n    /// with dense prefix of the given depth with initial values\n    pub fn new_mmapped_with_dense_prefix_with_init_values(\n        depth: usize,\n        prefix_depth: usize,\n        empty_value: &H::Hash,\n        initial_values: &[H::Hash],\n        file_path: &str,\n    ) -> Result<LazyMerkleTree<H, Canonical>, DenseMMapError> {\n        Ok(LazyMerkleTree {\n            tree: AnyTree::new_mmapped_with_dense_prefix_with_init_values(\n                depth,\n                prefix_depth,\n                empty_value,\n                initial_values,\n                file_path,\n            )?,\n            _version: Canonical,\n        })\n    }\n\n    /// Attempts to restore previous tree state from memory mapped file\n    ///\n    /// # Errors\n    /// - dense mmap tree restore failed\n    pub fn attempt_dense_mmap_restore(\n        depth: usize,\n        prefix_depth: usize,\n        empty_leaf: &H::Hash,\n        file_path: &str,\n    ) -> Result<LazyMerkleTree<H, Canonical>, DenseMMapError> {\n        Ok(LazyMerkleTree {\n            tree: AnyTree::try_restore_dense_mmap_tree_state(\n                depth,\n                prefix_depth,\n                empty_leaf,\n                file_path,\n            )?,\n            _version: Canonical,\n        })\n    }\n\n    /// Returns the depth of the tree.\n    #[must_use]\n    pub const fn depth(&self) -> usize {\n        self.tree.depth()\n    }\n\n    /// Returns the root of the tree.\n    #[must_use]\n    pub fn root(&self) -> H::Hash {\n        self.tree.root()\n    }\n\n    /// Sets the value at the given index to the given value. This is fully\n    /// immutable, returning a new tree and leaving the old one unchanged.\n    /// Reuses as much memory as possible, allocating only `depth` nodes.\n    #[must_use]\n    pub fn update(&self, index: usize, value: &H::Hash) -> LazyMerkleTree<H, Derived> {\n        LazyMerkleTree {\n            tree: self\n                .tree\n                .update_with_mutation_condition(index, value, false),\n            _version: Derived,\n        }\n    }\n\n    /// Returns the Merkle proof for the given index.\n    #[must_use]\n    pub fn proof(&self, index: usize) -> InclusionProof<H> {\n        self.tree.proof(index)\n    }\n\n    /// Verifies the given proof for the given value.\n    #[must_use]\n    pub fn verify(&self, value: H::Hash, proof: &InclusionProof<H>) -> bool {\n        proof.root(value) == self.root()\n    }\n\n    /// Returns the value at the given index.\n    #[must_use]\n    pub fn get_leaf(&self, index: usize) -> H::Hash {\n        self.tree.get_leaf(index)\n    }\n\n    /// Returns an iterator over all leaves.\n    pub fn leaves(&self) -> impl Iterator<Item = H::Hash> + '_ {\n        // TODO this could be made faster by a custom iterator\n        (0..(1 << self.depth())).map(|i| self.get_leaf(i))\n    }\n}\n\nimpl<H> LazyMerkleTree<H, Canonical>\nwhere\n    H: Hasher,\n    <H as Hasher>::Hash: Hash,\n{\n    /// Sets the value at the given index to the given value. This is a mutable\n    /// operation, that will modify any dense subtrees in place.\n    ///\n    /// This has potential consequences for the soundness of the whole\n    /// structure:\n    /// it has the potential to invalidate some trees that share nodes with\n    /// this one, so if many versions are kept at once, special care must be\n    /// taken when calling this. The only trees that are guaranteed to still be\n    /// valid after this operation, are those that already specify the same\n    /// value at the given index. For example, if a linear history of updates is\n    /// kept in memory, this operation is a good way to \"flatten\" updates into\n    /// the oldest kept version.\n    ///\n    /// This operation is useful for storage optimizations, as it avoids\n    /// allocating any new memory in dense subtrees.\n    #[must_use]\n    pub fn update_with_mutation(self, index: usize, value: &H::Hash) -> Self {\n        Self {\n            tree: self.tree.update_with_mutation_condition(index, value, true),\n            _version: Canonical,\n        }\n    }\n\n    /// Gives a `Derived` version of this tree. Useful for initializing\n    /// versioned trees.\n    #[must_use]\n    pub fn derived(&self) -> LazyMerkleTree<H, Derived> {\n        LazyMerkleTree {\n            tree: self.tree.clone(),\n            _version: Derived,\n        }\n    }\n}\n\nimpl<H> Clone for LazyMerkleTree<H, Derived>\nwhere\n    H: Hasher,\n    <H as Hasher>::Hash: Hash,\n{\n    fn clone(&self) -> Self {\n        Self {\n            tree: self.tree.clone(),\n            _version: Derived,\n        }\n    }\n}\n\nenum AnyTree<H: Hasher> {\n    Empty(EmptyTree<H>),\n    Sparse(SparseTree<H>),\n    Dense(DenseTree<H>),\n    DenseMMap(DenseMMapTree<H>),\n}\n\nimpl<H> AnyTree<H>\nwhere\n    H: Hasher,\n    <H as Hasher>::Hash: Hash,\n{\n    fn new(depth: usize, empty_value: H::Hash) -> Self {\n        Self::Empty(EmptyTree::new(depth, empty_value))\n    }\n\n    fn new_with_dense_prefix_with_initial_values(\n        depth: usize,\n        prefix_depth: usize,\n        empty_value: &H::Hash,\n        initial_values: &[H::Hash],\n    ) -> Self {\n        assert!(depth >= prefix_depth);\n        let dense = DenseTree::new_with_values(initial_values, empty_value, prefix_depth);\n        let mut result: Self = dense.into();\n        let mut current_depth = prefix_depth;\n        while current_depth < depth {\n            result =\n                SparseTree::new(result, EmptyTree::new(current_depth, *empty_value).into()).into();\n            current_depth += 1;\n        }\n        result\n    }\n\n    fn new_with_dense_prefix(depth: usize, prefix_depth: usize, empty_value: &H::Hash) -> Self {\n        assert!(depth >= prefix_depth);\n        let mut result: Self = EmptyTree::new(prefix_depth, *empty_value)\n            .alloc_dense()\n            .into();\n        let mut current_depth = prefix_depth;\n        while current_depth < depth {\n            result =\n                SparseTree::new(result, EmptyTree::new(current_depth, *empty_value).into()).into();\n            current_depth += 1;\n        }\n        result\n    }\n\n    fn new_mmapped_with_dense_prefix_with_init_values(\n        depth: usize,\n        prefix_depth: usize,\n        empty_value: &H::Hash,\n        initial_values: &[H::Hash],\n        file_path: &str,\n    ) -> Result<Self, DenseMMapError> {\n        assert!(depth >= prefix_depth);\n        let dense =\n            DenseMMapTree::new_with_values(initial_values, empty_value, prefix_depth, file_path)?;\n        let mut result: Self = dense.into();\n        let mut current_depth = prefix_depth;\n        while current_depth < depth {\n            result =\n                SparseTree::new(result, EmptyTree::new(current_depth, *empty_value).into()).into();\n            current_depth += 1;\n        }\n        Ok(result)\n    }\n\n    fn try_restore_dense_mmap_tree_state(\n        depth: usize,\n        prefix_depth: usize,\n        empty_leaf: &H::Hash,\n        file_path: &str,\n    ) -> Result<Self, DenseMMapError> {\n        let dense_mmap = DenseMMapTree::attempt_restore(empty_leaf, prefix_depth, file_path)?;\n\n        let mut result: Self = dense_mmap.into();\n\n        let mut current_depth = prefix_depth;\n        while current_depth < depth {\n            result =\n                SparseTree::new(result, EmptyTree::new(current_depth, *empty_leaf).into()).into();\n            current_depth += 1;\n        }\n\n        Ok(result)\n    }\n\n    const fn depth(&self) -> usize {\n        match self {\n            Self::Empty(tree) => tree.depth,\n            Self::Sparse(tree) => tree.depth,\n            Self::Dense(tree) => tree.depth,\n            Self::DenseMMap(tree) => tree.depth,\n        }\n    }\n\n    fn root(&self) -> H::Hash {\n        match self {\n            Self::Empty(tree) => tree.root(),\n            Self::Sparse(tree) => tree.root(),\n            Self::Dense(tree) => tree.root(),\n            Self::DenseMMap(tree) => tree.root(),\n        }\n    }\n\n    fn proof(&self, index: usize) -> InclusionProof<H> {\n        assert!(index < (1 << self.depth()));\n        let mut path = Vec::with_capacity(self.depth());\n        match self {\n            Self::Empty(tree) => tree.write_proof(index, &mut path),\n            Self::Sparse(tree) => tree.write_proof(index, &mut path),\n            Self::Dense(tree) => tree.write_proof(index, &mut path),\n            Self::DenseMMap(tree) => tree.write_proof(index, &mut path),\n        }\n        path.reverse();\n        InclusionProof(path)\n    }\n\n    fn write_proof(&self, index: usize, path: &mut Vec<Branch<H::Hash>>) {\n        match self {\n            Self::Empty(tree) => tree.write_proof(index, path),\n            Self::Sparse(tree) => tree.write_proof(index, path),\n            Self::Dense(tree) => tree.write_proof(index, path),\n            Self::DenseMMap(tree) => tree.write_proof(index, path),\n        }\n    }\n\n    fn update_with_mutation_condition(\n        &self,\n        index: usize,\n        value: &H::Hash,\n        is_mutation_allowed: bool,\n    ) -> Self {\n        match self {\n            Self::Empty(tree) => tree\n                .update_with_mutation_condition(index, value, is_mutation_allowed)\n                .into(),\n            Self::Sparse(tree) => tree\n                .update_with_mutation_condition(index, value, is_mutation_allowed)\n                .into(),\n            Self::Dense(tree) => {\n                tree.update_with_mutation_condition(index, value, is_mutation_allowed)\n            }\n            Self::DenseMMap(tree) => {\n                tree.update_with_mutation_condition(index, value, is_mutation_allowed)\n            }\n        }\n    }\n\n    fn get_leaf(&self, index: usize) -> H::Hash {\n        match self {\n            Self::Empty(tree) => tree.get_leaf(),\n            Self::Sparse(tree) => tree.get_leaf(index),\n            Self::Dense(tree) => tree.get_leaf(index),\n            Self::DenseMMap(tree) => tree.get_leaf(index),\n        }\n    }\n}\n\nimpl<H> Clone for AnyTree<H>\nwhere\n    H: Hasher,\n    <H as Hasher>::Hash: Hash,\n{\n    fn clone(&self) -> Self {\n        match self {\n            Self::Empty(t) => t.clone().into(),\n            Self::Sparse(t) => t.clone().into(),\n            Self::Dense(t) => t.clone().into(),\n            Self::DenseMMap(t) => t.clone().into(),\n        }\n    }\n}\n\nimpl<H: Hasher> From<EmptyTree<H>> for AnyTree<H> {\n    fn from(tree: EmptyTree<H>) -> Self {\n        Self::Empty(tree)\n    }\n}\n\nimpl<H: Hasher> From<SparseTree<H>> for AnyTree<H> {\n    fn from(tree: SparseTree<H>) -> Self {\n        Self::Sparse(tree)\n    }\n}\n\nimpl<H: Hasher> From<DenseTree<H>> for AnyTree<H> {\n    fn from(tree: DenseTree<H>) -> Self {\n        Self::Dense(tree)\n    }\n}\n\nimpl<H: Hasher> From<DenseMMapTree<H>> for AnyTree<H> {\n    fn from(tree: DenseMMapTree<H>) -> Self {\n        Self::DenseMMap(tree)\n    }\n}\n\nstruct EmptyTree<H: Hasher> {\n    depth: usize,\n    empty_tree_values: Arc<Vec<H::Hash>>,\n}\n\nimpl<H: Hasher> Clone for EmptyTree<H> {\n    fn clone(&self) -> Self {\n        Self {\n            depth: self.depth,\n            empty_tree_values: self.empty_tree_values.clone(),\n        }\n    }\n}\n\nimpl<H> EmptyTree<H>\nwhere\n    H: Hasher,\n    <H as Hasher>::Hash: Hash,\n{\n    #[must_use]\n    fn new(depth: usize, empty_value: H::Hash) -> Self {\n        let empty_tree_values = {\n            let values = successors(Some(empty_value), |value| Some(H::hash_node(value, value)))\n                .take(depth + 1)\n                .collect();\n            Arc::new(values)\n        };\n        Self {\n            depth,\n            empty_tree_values,\n        }\n    }\n\n    fn write_proof(&self, index: usize, path: &mut Vec<Branch<H::Hash>>) {\n        for depth in (1..=self.depth).rev() {\n            let val = self.empty_tree_values[depth - 1];\n            let branch = if get_turn_at_depth(index, depth) == Turn::Left {\n                Branch::Left(val)\n            } else {\n                Branch::Right(val)\n            };\n            path.push(branch);\n        }\n    }\n\n    #[must_use]\n    fn update_with_mutation_condition(\n        &self,\n        index: usize,\n        value: &H::Hash,\n        is_mutation_allowed: bool,\n    ) -> SparseTree<H> {\n        self.alloc_sparse()\n            .update_with_mutation_condition(index, value, is_mutation_allowed)\n    }\n\n    #[must_use]\n    fn alloc_sparse(&self) -> SparseTree<H> {\n        if self.depth == 0 {\n            SparseTree::new_leaf(self.root())\n        } else {\n            let next_child: Self = Self {\n                depth: self.depth - 1,\n                empty_tree_values: self.empty_tree_values.clone(),\n            };\n            SparseTree::new(next_child.clone().into(), next_child.into())\n        }\n    }\n\n    #[must_use]\n    fn alloc_dense(&self) -> DenseTree<H> {\n        let values = self\n            .empty_tree_values\n            .iter()\n            .rev()\n            .enumerate()\n            .flat_map(|(depth, value)| repeat_n(value, 1 << depth));\n        let padded_values = once(&self.empty_tree_values[0])\n            .chain(values)\n            .cloned()\n            .collect();\n        DenseTree {\n            depth: self.depth,\n            root_index: 1,\n            storage: Arc::new(Mutex::new(padded_values)),\n        }\n    }\n\n    #[must_use]\n    fn root(&self) -> H::Hash {\n        self.empty_tree_values[self.depth]\n    }\n\n    fn get_leaf(&self) -> H::Hash {\n        self.empty_tree_values[0]\n    }\n}\n\nstruct Children<H: Hasher> {\n    left: Arc<AnyTree<H>>,\n    right: Arc<AnyTree<H>>,\n}\n\nimpl<H: Hasher> Clone for Children<H> {\n    fn clone(&self) -> Self {\n        Self {\n            left: self.left.clone(),\n            right: self.right.clone(),\n        }\n    }\n}\n\nstruct SparseTree<H: Hasher> {\n    depth: usize,\n    root: H::Hash,\n    children: Option<Children<H>>,\n}\n\n#[derive(Debug, PartialEq, Eq)]\nenum Turn {\n    Left,\n    Right,\n}\n\nconst fn get_turn_at_depth(index: usize, depth: usize) -> Turn {\n    if index & (1 << (depth - 1)) == 0 {\n        Turn::Left\n    } else {\n        Turn::Right\n    }\n}\n\nconst fn clear_turn_at_depth(index: usize, depth: usize) -> usize {\n    index & !(1 << (depth - 1))\n}\n\nimpl<H> From<Children<H>> for SparseTree<H>\nwhere\n    H: Hasher,\n    <H as Hasher>::Hash: Hash,\n{\n    fn from(children: Children<H>) -> Self {\n        assert_eq!(children.left.depth(), children.right.depth());\n        let (depth, root) = {\n            let left = children.left.clone();\n            let right = children.right.clone();\n            let depth = left.depth() + 1;\n            let root = H::hash_node(&left.root(), &right.root());\n            (depth, root)\n        };\n        Self {\n            depth,\n            root,\n            children: Some(children),\n        }\n    }\n}\n\nimpl<H> Clone for SparseTree<H>\nwhere\n    H: Hasher,\n    <H as Hasher>::Hash: Hash,\n{\n    fn clone(&self) -> Self {\n        Self {\n            depth: self.depth,\n            root: self.root,\n            children: self.children.clone(),\n        }\n    }\n}\n\nimpl<H> SparseTree<H>\nwhere\n    H: Hasher,\n    <H as Hasher>::Hash: Hash,\n{\n    fn new(left: AnyTree<H>, right: AnyTree<H>) -> Self {\n        assert_eq!(left.depth(), right.depth());\n        let children = Children {\n            left: Arc::new(left),\n            right: Arc::new(right),\n        };\n        children.into()\n    }\n\n    const fn new_leaf(value: H::Hash) -> Self {\n        Self {\n            depth: 0,\n            root: value,\n            children: None,\n        }\n    }\n\n    fn write_proof(&self, index: usize, path: &mut Vec<Branch<H::Hash>>) {\n        if let Some(children) = &self.children {\n            let next_index = clear_turn_at_depth(index, self.depth);\n            if get_turn_at_depth(index, self.depth) == Turn::Left {\n                path.push(Branch::Left(children.right.root()));\n                children.left.write_proof(next_index, path);\n            } else {\n                path.push(Branch::Right(children.left.root()));\n                children.right.write_proof(next_index, path);\n            }\n        }\n    }\n\n    #[must_use]\n    fn update_with_mutation_condition(\n        &self,\n        index: usize,\n        value: &H::Hash,\n        is_mutation_allowed: bool,\n    ) -> Self {\n        let Some(children) = &self.children else {\n            // no children – this is a leaf\n            return Self::new_leaf(*value);\n        };\n\n        let next_index = clear_turn_at_depth(index, self.depth);\n        let children = if get_turn_at_depth(index, self.depth) == Turn::Left {\n            let left = &children.left;\n            let new_left =\n                left.update_with_mutation_condition(next_index, value, is_mutation_allowed);\n            Children {\n                left: Arc::new(new_left),\n                right: children.right.clone(),\n            }\n        } else {\n            let right = &children.right;\n            let new_right =\n                right.update_with_mutation_condition(next_index, value, is_mutation_allowed);\n            Children {\n                left: children.left.clone(),\n                right: Arc::new(new_right),\n            }\n        };\n\n        children.into()\n    }\n\n    fn root(&self) -> H::Hash {\n        self.root\n    }\n\n    fn get_leaf(&self, index: usize) -> H::Hash {\n        self.children.as_ref().map_or_else(\n            || self.root,\n            |children| {\n                let next_index = clear_turn_at_depth(index, self.depth);\n                if get_turn_at_depth(index, self.depth) == Turn::Left {\n                    children.left.get_leaf(next_index)\n                } else {\n                    children.right.get_leaf(next_index)\n                }\n            },\n        )\n    }\n}\n\n#[derive(Debug)]\nstruct DenseTree<H: Hasher> {\n    depth: usize,\n    root_index: usize,\n    storage: Arc<Mutex<Vec<H::Hash>>>,\n}\n\nimpl<H: Hasher> Clone for DenseTree<H> {\n    fn clone(&self) -> Self {\n        Self {\n            depth: self.depth,\n            root_index: self.root_index,\n            storage: self.storage.clone(),\n        }\n    }\n}\n\nimpl<H> DenseTree<H>\nwhere\n    H: Hasher,\n    <H as Hasher>::Hash: Hash,\n{\n    fn vec_from_values(values: &[H::Hash], empty_value: &H::Hash, depth: usize) -> Vec<H::Hash> {\n        let leaf_count = 1 << depth;\n        let storage_size = 1 << (depth + 1);\n        let mut storage = Vec::with_capacity(storage_size);\n\n        let empties = repeat_n(empty_value, leaf_count);\n        storage.extend(empties);\n        storage.extend_from_slice(values);\n        if values.len() < leaf_count {\n            let empties = repeat_n(empty_value, leaf_count - values.len());\n            storage.extend(empties);\n        }\n\n        // We iterate over mutable layers of the tree\n        for current_depth in (1..=depth).rev() {\n            let (top, child_layer) = storage.split_at_mut(1 << current_depth);\n            let parent_layer = &mut top[(1 << (current_depth - 1))..];\n\n            parent_layer\n                .par_iter_mut()\n                .enumerate()\n                .for_each(|(i, value)| {\n                    let left = &child_layer[2 * i];\n                    let right = &child_layer[2 * i + 1];\n                    *value = H::hash_node(left, right);\n                });\n        }\n\n        storage\n    }\n\n    fn new_with_values(values: &[H::Hash], empty_value: &H::Hash, depth: usize) -> Self {\n        let storage = Self::vec_from_values(values, empty_value, depth);\n\n        Self {\n            depth,\n            root_index: 1,\n            storage: Arc::new(Mutex::new(storage)),\n        }\n    }\n\n    fn with_ref<F, R>(&self, fun: F) -> R\n    where\n        F: FnOnce(DenseTreeRef<H>) -> R,\n    {\n        let guard = self.storage.lock().expect(\"lock poisoned, terminating\");\n        let r = DenseTreeRef {\n            depth: self.depth,\n            root_index: self.root_index,\n            storage: &guard,\n            locked_storage: &self.storage,\n        };\n        fun(r)\n    }\n\n    fn write_proof(&self, index: usize, path: &mut Vec<Branch<H::Hash>>) {\n        self.with_ref(|r| r.write_proof(index, path));\n    }\n\n    fn get_leaf(&self, index: usize) -> H::Hash {\n        self.with_ref(|r| {\n            let leaf_index_in_dense_tree = index + (self.root_index << self.depth);\n            r.storage[leaf_index_in_dense_tree]\n        })\n    }\n\n    fn update_with_mutation_condition(\n        &self,\n        index: usize,\n        value: &H::Hash,\n        is_mutation_allowed: bool,\n    ) -> AnyTree<H> {\n        if is_mutation_allowed {\n            self.update_with_mutation(index, value);\n            self.clone().into()\n        } else {\n            self.with_ref(|r| r.update(index, value)).into()\n        }\n    }\n\n    fn update_with_mutation(&self, index: usize, value: &H::Hash) {\n        let mut storage = self.storage.lock().expect(\"lock poisoned, terminating\");\n        let leaf_index_in_dense_tree = index + (self.root_index << self.depth);\n        storage[leaf_index_in_dense_tree] = *value;\n        let mut current = leaf_index_in_dense_tree / 2;\n        while current > 0 {\n            let left = &storage[2 * current];\n            let right = &storage[2 * current + 1];\n            storage[current] = H::hash_node(left, right);\n            current /= 2;\n        }\n    }\n\n    fn root(&self) -> H::Hash {\n        self.storage.lock().unwrap()[self.root_index]\n    }\n}\n\nstruct DenseTreeRef<'a, H: Hasher> {\n    depth: usize,\n    root_index: usize,\n    storage: &'a Vec<H::Hash>,\n    locked_storage: &'a Arc<Mutex<Vec<H::Hash>>>,\n}\n\nimpl<H: Hasher> From<DenseTreeRef<'_, H>> for DenseTree<H> {\n    fn from(value: DenseTreeRef<H>) -> Self {\n        Self {\n            depth: value.depth,\n            root_index: value.root_index,\n            storage: value.locked_storage.clone(),\n        }\n    }\n}\n\nimpl<H: Hasher> From<DenseTreeRef<'_, H>> for AnyTree<H> {\n    fn from(value: DenseTreeRef<H>) -> Self {\n        Self::Dense(value.into())\n    }\n}\n\nimpl<H> DenseTreeRef<'_, H>\nwhere\n    H: Hasher,\n    <H as Hasher>::Hash: Hash,\n{\n    fn root(&self) -> H::Hash {\n        self.storage[self.root_index]\n    }\n\n    const fn left(&self) -> DenseTreeRef<'_, H> {\n        Self {\n            depth: self.depth - 1,\n            root_index: 2 * self.root_index,\n            storage: self.storage,\n            locked_storage: self.locked_storage,\n        }\n    }\n\n    const fn right(&self) -> DenseTreeRef<'_, H> {\n        Self {\n            depth: self.depth - 1,\n            root_index: 2 * self.root_index + 1,\n            storage: self.storage,\n            locked_storage: self.locked_storage,\n        }\n    }\n\n    fn write_proof(&self, index: usize, path: &mut Vec<Branch<H::Hash>>) {\n        if self.depth == 0 {\n            return;\n        }\n        let next_index = clear_turn_at_depth(index, self.depth);\n        if get_turn_at_depth(index, self.depth) == Turn::Left {\n            path.push(Branch::Left(self.right().root()));\n            self.left().write_proof(next_index, path);\n        } else {\n            path.push(Branch::Right(self.left().root()));\n            self.right().write_proof(next_index, path);\n        }\n    }\n\n    fn update(&self, index: usize, hash: &H::Hash) -> SparseTree<H> {\n        if self.depth == 0 {\n            return SparseTree::new_leaf(*hash);\n        }\n        let next_index = clear_turn_at_depth(index, self.depth);\n        if get_turn_at_depth(index, self.depth) == Turn::Left {\n            let left = self.left();\n            let new_left = left.update(next_index, hash);\n            let right = self.right();\n            let new_root = H::hash_node(&new_left.root(), &right.root());\n            SparseTree {\n                children: Some(Children {\n                    left: Arc::new(new_left.into()),\n                    right: Arc::new(self.right().into()),\n                }),\n                root: new_root,\n                depth: self.depth,\n            }\n        } else {\n            let right = self.right();\n            let new_right = right.update(next_index, hash);\n            let left = self.left();\n            let new_root = H::hash_node(&left.root(), &new_right.root());\n            SparseTree {\n                children: Some(Children {\n                    left: Arc::new(self.left().into()),\n                    right: Arc::new(new_right.into()),\n                }),\n                root: new_root,\n                depth: self.depth,\n            }\n        }\n    }\n}\n\nstruct DenseMMapTree<H: Hasher> {\n    depth: usize,\n    root_index: usize,\n    storage: Arc<Mutex<MmapMutWrapper<H>>>,\n}\n\nimpl<H: Hasher> Clone for DenseMMapTree<H> {\n    fn clone(&self) -> Self {\n        Self {\n            depth: self.depth,\n            root_index: self.root_index,\n            storage: self.storage.clone(),\n        }\n    }\n}\n\nimpl<H> DenseMMapTree<H>\nwhere\n    H: Hasher,\n    <H as Hasher>::Hash: Hash,\n{\n    /// Creates a new `DenseMMapTree` with initial values and depth\n    ///\n    /// # Errors\n    ///\n    /// - returns Err if path buf failed to be created with provided string\n    /// - returns Err if mmap creation fails\n    fn new_with_values(\n        values: &[H::Hash],\n        empty_value: &H::Hash,\n        depth: usize,\n        mmap_file_path: &str,\n    ) -> Result<Self, DenseMMapError> {\n        let path_buf = match PathBuf::from_str(mmap_file_path) {\n            Ok(pb) => pb,\n            Err(_e) => return Err(DenseMMapError::FailedToCreatePathBuf),\n        };\n\n        let storage = DenseTree::<H>::vec_from_values(values, empty_value, depth);\n\n        let mmap = MmapMutWrapper::new_from_storage(path_buf, &storage)?;\n\n        Ok(Self {\n            depth,\n            root_index: 1,\n            storage: Arc::new(Mutex::new(mmap)),\n        })\n    }\n\n    /// Given the file path and tree depth,\n    /// it attempts to restore the memory map\n    ///\n    /// # Errors\n    ///\n    /// - returns Err if path buf creation fails\n    /// - Derives errors from `MmapMutWrapper`\n    ///\n    /// # Panics\n    ///\n    /// - mutex lock is poisoned\n    fn attempt_restore(\n        empty_leaf: &H::Hash,\n        depth: usize,\n        mmap_file_path: &str,\n    ) -> Result<Self, DenseMMapError> {\n        let path_buf = match PathBuf::from_str(mmap_file_path) {\n            Ok(pb) => pb,\n            Err(_e) => return Err(DenseMMapError::FailedToCreatePathBuf),\n        };\n\n        let mmap = MmapMutWrapper::attempt_restore(empty_leaf, depth, path_buf)?;\n\n        Ok(Self {\n            depth,\n            root_index: 1,\n            storage: Arc::new(Mutex::new(mmap)),\n        })\n    }\n\n    fn with_ref<F, R>(&self, fun: F) -> R\n    where\n        F: FnOnce(DenseTreeMMapRef<H>) -> R,\n    {\n        let guard = self.storage.lock().expect(\"lock poisoned, terminating\");\n        let r = DenseTreeMMapRef {\n            depth: self.depth,\n            root_index: self.root_index,\n            storage: &guard,\n            locked_storage: &self.storage,\n        };\n        fun(r)\n    }\n\n    fn write_proof(&self, index: usize, path: &mut Vec<Branch<H::Hash>>) {\n        self.with_ref(|r| r.write_proof(index, path));\n    }\n\n    fn get_leaf(&self, index: usize) -> H::Hash {\n        self.with_ref(|r| {\n            let leaf_index_in_dense_tree = index + (self.root_index << self.depth);\n            r.storage[leaf_index_in_dense_tree]\n        })\n    }\n\n    fn update_with_mutation_condition(\n        &self,\n        index: usize,\n        value: &H::Hash,\n        is_mutation_allowed: bool,\n    ) -> AnyTree<H> {\n        if is_mutation_allowed {\n            self.update_with_mutation(index, value);\n            self.clone().into()\n        } else {\n            self.with_ref(|r| r.update(index, value)).into()\n        }\n    }\n\n    fn update_with_mutation(&self, index: usize, value: &H::Hash) {\n        let mut storage = self.storage.lock().expect(\"lock poisoned, terminating\");\n        let leaf_index_in_dense_tree = index + (self.root_index << self.depth);\n        storage[leaf_index_in_dense_tree] = *value;\n        let mut current = leaf_index_in_dense_tree / 2;\n        while current > 0 {\n            let left = &storage[2 * current];\n            let right = &storage[2 * current + 1];\n            storage[current] = H::hash_node(left, right);\n            current /= 2;\n        }\n    }\n\n    fn root(&self) -> H::Hash {\n        self.storage.lock().expect(\"lock poisoned\")[self.root_index]\n    }\n}\n\nstruct DenseTreeMMapRef<'a, H: Hasher> {\n    depth: usize,\n    root_index: usize,\n    storage: &'a MmapMutWrapper<H>,\n    locked_storage: &'a Arc<Mutex<MmapMutWrapper<H>>>,\n}\n\nimpl<H: Hasher> From<DenseTreeMMapRef<'_, H>> for DenseMMapTree<H> {\n    fn from(value: DenseTreeMMapRef<H>) -> Self {\n        Self {\n            depth: value.depth,\n            root_index: value.root_index,\n            storage: value.locked_storage.clone(),\n        }\n    }\n}\n\nimpl<H: Hasher> From<DenseTreeMMapRef<'_, H>> for AnyTree<H> {\n    fn from(value: DenseTreeMMapRef<H>) -> Self {\n        Self::DenseMMap(value.into())\n    }\n}\n\nimpl<H> DenseTreeMMapRef<'_, H>\nwhere\n    H: Hasher,\n    <H as Hasher>::Hash: Hash,\n{\n    fn root(&self) -> H::Hash {\n        self.storage[self.root_index]\n    }\n\n    const fn left(&self) -> DenseTreeMMapRef<'_, H> {\n        Self {\n            depth: self.depth - 1,\n            root_index: 2 * self.root_index,\n            storage: self.storage,\n            locked_storage: self.locked_storage,\n        }\n    }\n\n    const fn right(&self) -> DenseTreeMMapRef<'_, H> {\n        Self {\n            depth: self.depth - 1,\n            root_index: 2 * self.root_index + 1,\n            storage: self.storage,\n            locked_storage: self.locked_storage,\n        }\n    }\n\n    fn write_proof(&self, index: usize, path: &mut Vec<Branch<H::Hash>>) {\n        if self.depth == 0 {\n            return;\n        }\n        let next_index = clear_turn_at_depth(index, self.depth);\n        if get_turn_at_depth(index, self.depth) == Turn::Left {\n            path.push(Branch::Left(self.right().root()));\n            self.left().write_proof(next_index, path);\n        } else {\n            path.push(Branch::Right(self.left().root()));\n            self.right().write_proof(next_index, path);\n        }\n    }\n\n    fn update(&self, index: usize, hash: &H::Hash) -> SparseTree<H> {\n        if self.depth == 0 {\n            return SparseTree::new_leaf(*hash);\n        }\n        let next_index = clear_turn_at_depth(index, self.depth);\n        if get_turn_at_depth(index, self.depth) == Turn::Left {\n            let left = self.left();\n            let new_left = left.update(next_index, hash);\n            let right = self.right();\n            let new_root = H::hash_node(&new_left.root(), &right.root());\n            SparseTree {\n                children: Some(Children {\n                    left: Arc::new(new_left.into()),\n                    right: Arc::new(self.right().into()),\n                }),\n                root: new_root,\n                depth: self.depth,\n            }\n        } else {\n            let right = self.right();\n            let new_right = right.update(next_index, hash);\n            let left = self.left();\n            let new_root = H::hash_node(&left.root(), &new_right.root());\n            SparseTree {\n                children: Some(Children {\n                    left: Arc::new(self.left().into()),\n                    right: Arc::new(new_right.into()),\n                }),\n                root: new_root,\n                depth: self.depth,\n            }\n        }\n    }\n}\n\npub struct MmapMutWrapper<H: Hasher> {\n    mmap: MmapMut,\n    phantom: std::marker::PhantomData<H>,\n}\n\nimpl<H> MmapMutWrapper<H>\nwhere\n    H: Hasher,\n    <H as Hasher>::Hash: Hash,\n{\n    /// Creates a new memory map backed with file with provided size\n    /// and fills the entire map with initial value\n    ///\n    /// # Errors\n    ///\n    /// - returns Err if file creation has failed\n    /// - returns Err if bytes couldn't be written to file\n    ///\n    /// # Panics\n    ///\n    /// - empty hash value serialization failed\n    /// - file size cannot be set\n    /// - file is too large, possible truncation can occur\n    /// - cannot build memory map\n    pub fn new_from_storage(\n        file_path: PathBuf,\n        storage: &[H::Hash],\n    ) -> Result<Self, DenseMMapError> {\n        // Safety: potential uninitialized padding from `H::Hash` is safe to use if\n        // we're casting back to the same type.\n        let buf = bytemuck::cast_slice(storage);\n        let buf_len = buf.len();\n\n        let mut file = match OpenOptions::new()\n            .read(true)\n            .write(true)\n            .create(true)\n            .truncate(true)\n            .open(file_path)\n        {\n            Ok(file) => file,\n            Err(_e) => return Err(DenseMMapError::FileCreationFailed),\n        };\n\n        file.set_len(buf_len as u64).expect(\"cannot set file size\");\n        if file.write_all(buf).is_err() {\n            return Err(DenseMMapError::FileCannotWriteBytes);\n        }\n\n        let mmap = unsafe {\n            MmapOptions::new(usize::try_from(buf_len as u64).expect(\"file size truncated\"))\n                .expect(\"cannot create memory map\")\n                .with_file(&file, 0)\n                .with_flags(MmapFlags::SHARED)\n                .map_mut()\n                .expect(\"cannot build memory map\")\n        };\n\n        Ok(Self {\n            mmap,\n            phantom: std::marker::PhantomData,\n        })\n    }\n\n    /// Given the file path and tree depth,\n    /// it attempts to restore the memory map\n    ///\n    /// # Errors\n    ///\n    /// - returns Err if file doesn't exist\n    /// - returns Err if file size doesn't match the expected tree size\n    ///\n    /// # Panics\n    ///\n    /// - cannot get file metadata to check for file length\n    /// - truncated file size when attempting to build memory map\n    /// - cannot build memory map\n    pub fn attempt_restore(\n        empty_leaf: &H::Hash,\n        depth: usize,\n        file_path: PathBuf,\n    ) -> Result<Self, DenseMMapError> {\n        let file = match OpenOptions::new().read(true).write(true).open(file_path) {\n            Ok(file) => file,\n            Err(_e) => return Err(DenseMMapError::FileDoesntExist),\n        };\n\n        let size_of_empty_leaf = std::mem::size_of_val(empty_leaf);\n        let expected_file_size = (1 << (depth + 1)) * size_of_empty_leaf as u64;\n\n        if expected_file_size != file.metadata().expect(\"cannot get file metadata\").len() {\n            return Err(DenseMMapError::FileSizeShouldMatchTree);\n        }\n\n        let mmap = unsafe {\n            MmapOptions::new(\n                usize::try_from(expected_file_size).expect(\"expected file size truncated\"),\n            )\n            .expect(\"cannot create memory map\")\n            .with_file(&file, 0)\n            .with_flags(MmapFlags::SHARED)\n            .map_mut()\n            .expect(\"cannot build memory map\")\n        };\n\n        Ok(Self {\n            mmap,\n            phantom: std::marker::PhantomData,\n        })\n    }\n}\n\nimpl<H> Deref for MmapMutWrapper<H>\nwhere\n    H: Hasher,\n    <H as Hasher>::Hash: Hash,\n{\n    type Target = [H::Hash];\n\n    fn deref(&self) -> &Self::Target {\n        bytemuck::cast_slice(self.mmap.as_slice())\n    }\n}\n\nimpl<H> DerefMut for MmapMutWrapper<H>\nwhere\n    H: Hasher,\n    <H as Hasher>::Hash: Hash,\n{\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        bytemuck::cast_slice_mut(self.mmap.as_mut_slice())\n    }\n}\n\n#[derive(Error, Debug)]\npub enum DenseMMapError {\n    #[error(\"file size should match expected tree size\")]\n    FileSizeShouldMatchTree,\n    #[error(\"file doesn't exist\")]\n    FileDoesntExist,\n    #[error(\"failed to create a file\")]\n    FileCreationFailed,\n    #[error(\"cannot write bytes to file\")]\n    FileCannotWriteBytes,\n    #[error(\"failed to create pathbuf\")]\n    FailedToCreatePathBuf,\n}\n\n#[cfg(test)]\nmod tests {\n    use hex_literal::hex;\n    use semaphore_rs_hasher::Hasher;\n    use semaphore_rs_keccak::keccak::Keccak256;\n\n    use super::*;\n\n    struct TestHasher;\n\n    impl Hasher for TestHasher {\n        type Hash = u64;\n\n        fn hash_node(left: &Self::Hash, right: &Self::Hash) -> Self::Hash {\n            left + 2 * right + 1\n        }\n    }\n\n    #[test]\n    fn test_updates_in_sparse() {\n        let tree_1 = LazyMerkleTree::<TestHasher>::new(2, 0);\n        assert_eq!(tree_1.root(), 4);\n        let tree_2 = tree_1.update(0, &1);\n        assert_eq!(tree_1.root(), 4);\n        assert_eq!(tree_2.root(), 5);\n        let tree_3 = tree_2.update(2, &2);\n        assert_eq!(tree_1.root(), 4);\n        assert_eq!(tree_2.root(), 5);\n        assert_eq!(tree_3.root(), 9);\n    }\n\n    #[test]\n    fn test_updates_in_dense() {\n        let tree_1 = LazyMerkleTree::<TestHasher>::new_with_dense_prefix(2, 2, &0);\n        assert_eq!(tree_1.root(), 4);\n        let tree_2 = tree_1.update(0, &1);\n        assert_eq!(tree_1.root(), 4);\n        assert_eq!(tree_2.root(), 5);\n        let tree_3 = tree_2.update(2, &2);\n        assert_eq!(tree_1.root(), 4);\n        assert_eq!(tree_2.root(), 5);\n        assert_eq!(tree_3.root(), 9);\n    }\n\n    #[test]\n    fn test_mutable_updates_in_dense() {\n        let tree = LazyMerkleTree::<Keccak256>::new_with_dense_prefix(2, 2, &[0; 32]);\n        let original_tree = LazyMerkleTree {\n            tree: tree.tree.clone(),\n            _version: Derived,\n        };\n        assert_eq!(\n            original_tree.root(),\n            hex!(\"b4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30\")\n        );\n        let tree = tree.update_with_mutation(\n            0,\n            &hex!(\"0000000000000000000000000000000000000000000000000000000000000001\"),\n        );\n        assert_eq!(\n            original_tree.root(),\n            hex!(\"c1ba1812ff680ce84c1d5b4f1087eeb08147a4d510f3496b2849df3a73f5af95\")\n        );\n        let tree = tree.update_with_mutation(\n            1,\n            &hex!(\"0000000000000000000000000000000000000000000000000000000000000002\"),\n        );\n        assert_eq!(\n            original_tree.root(),\n            hex!(\"893760ec5b5bee236f29e85aef64f17139c3c1b7ff24ce64eb6315fca0f2485b\")\n        );\n        let tree = tree.update_with_mutation(\n            2,\n            &hex!(\"0000000000000000000000000000000000000000000000000000000000000003\"),\n        );\n        assert_eq!(\n            original_tree.root(),\n            hex!(\"222ff5e0b5877792c2bc1670e2ccd0c2c97cd7bb1672a57d598db05092d3d72c\")\n        );\n        let _tree = tree.update_with_mutation(\n            3,\n            &hex!(\"0000000000000000000000000000000000000000000000000000000000000004\"),\n        );\n        assert_eq!(\n            original_tree.root(),\n            hex!(\"a9bb8c3f1f12e9aa903a50c47f314b57610a3ab32f2d463293f58836def38d36\")\n        );\n    }\n\n    #[test]\n    fn test_mutable_updates_in_dense_with_dense_prefix() {\n        let h0 = [0; 32];\n        let h1 = hex!(\"0000000000000000000000000000000000000000000000000000000000000001\");\n        let h2 = hex!(\"0000000000000000000000000000000000000000000000000000000000000002\");\n        let h3 = hex!(\"0000000000000000000000000000000000000000000000000000000000000003\");\n        let h4 = hex!(\"0000000000000000000000000000000000000000000000000000000000000004\");\n        let tree = LazyMerkleTree::<Keccak256>::new_with_dense_prefix(2, 1, &[0; 32]);\n        let original_tree = LazyMerkleTree {\n            tree: tree.tree.clone(),\n            _version: Derived,\n        };\n        assert_eq!(\n            tree.root(),\n            hex!(\"b4c11951957c6f8f642c4af61cd6b24640fec6dc7fc607ee8206a99e92410d30\")\n        );\n        let t1 = tree.update_with_mutation(0, &h1);\n        assert_eq!(\n            t1.root(),\n            hex!(\"c1ba1812ff680ce84c1d5b4f1087eeb08147a4d510f3496b2849df3a73f5af95\")\n        );\n        let t2 = t1.update_with_mutation(1, &h2);\n        assert_eq!(\n            t2.root(),\n            hex!(\"893760ec5b5bee236f29e85aef64f17139c3c1b7ff24ce64eb6315fca0f2485b\")\n        );\n        let t3 = t2.update_with_mutation(2, &h3);\n        assert_eq!(\n            t3.root(),\n            hex!(\"222ff5e0b5877792c2bc1670e2ccd0c2c97cd7bb1672a57d598db05092d3d72c\")\n        );\n        let t4 = t3.update_with_mutation(3, &h4);\n        assert_eq!(\n            t4.root(),\n            hex!(\"a9bb8c3f1f12e9aa903a50c47f314b57610a3ab32f2d463293f58836def38d36\")\n        );\n        // first two leaves are in the dense subtree, the rest is sparse, therefore only\n        // first 2 get updated inplace.\n        assert_eq!(\n            original_tree.leaves().collect::<Vec<_>>(),\n            vec![h1, h2, h0, h0]\n        );\n        // all leaves are updated in the properly tracked tree\n        assert_eq!(t4.leaves().collect::<Vec<_>>(), vec![h1, h2, h3, h4]);\n    }\n\n    #[test]\n    fn test_proof() {\n        let tree = LazyMerkleTree::<Keccak256>::new_with_dense_prefix(2, 1, &[0; 32]);\n        let tree = tree.update_with_mutation(\n            0,\n            &hex!(\"0000000000000000000000000000000000000000000000000000000000000001\"),\n        );\n        let tree = tree.update_with_mutation(\n            1,\n            &hex!(\"0000000000000000000000000000000000000000000000000000000000000002\"),\n        );\n        let tree = tree.update_with_mutation(\n            2,\n            &hex!(\"0000000000000000000000000000000000000000000000000000000000000003\"),\n        );\n        let tree = tree.update_with_mutation(\n            3,\n            &hex!(\"0000000000000000000000000000000000000000000000000000000000000004\"),\n        );\n\n        let proof = tree.proof(2);\n        assert_eq!(proof.leaf_index(), 2);\n        assert!(tree.verify(\n            hex!(\"0000000000000000000000000000000000000000000000000000000000000003\"),\n            &proof\n        ));\n        assert!(!tree.verify(\n            hex!(\"0000000000000000000000000000000000000000000000000000000000000001\"),\n            &proof\n        ));\n    }\n\n    #[test]\n    fn test_giant_tree_with_initial_vals() {\n        let h0 = [0; 32];\n        let h1 = hex!(\"0000000000000000000000000000000000000000000000000000000000000001\");\n        let h2 = hex!(\"0000000000000000000000000000000000000000000000000000000000000002\");\n        let h3 = hex!(\"0000000000000000000000000000000000000000000000000000000000000003\");\n        let h4 = hex!(\"0000000000000000000000000000000000000000000000000000000000000004\");\n        let updates: Vec<(usize, _)> = vec![(0, h1), (1, h2), (2, h3), (3, h4)];\n        let mut from_empty =\n            LazyMerkleTree::<Keccak256>::new_with_dense_prefix(63, 10, &h0).derived();\n        for (ix, hash) in &updates {\n            from_empty = from_empty.update(*ix, hash);\n        }\n        let from_initial_vals =\n            LazyMerkleTree::<Keccak256>::new_with_dense_prefix_with_initial_values(\n                63,\n                10,\n                &h0,\n                &[h1, h2, h3, h4],\n            )\n            .derived();\n        assert_eq!(from_empty.root(), from_initial_vals.root());\n    }\n\n    #[test]\n    fn test_giant_trees() {\n        let h0 = [0; 32];\n        let h1 = hex!(\"0000000000000000000000000000000000000000000000000000000000000001\");\n        let h2 = hex!(\"0000000000000000000000000000000000000000000000000000000000000002\");\n        let h3 = hex!(\"0000000000000000000000000000000000000000000000000000000000000003\");\n        let h4 = hex!(\"0000000000000000000000000000000000000000000000000000000000000004\");\n        let updates: Vec<(usize, _)> = vec![\n            (1, h1),\n            (2, h2),\n            (1_000_000_000, h3),\n            (1_000_000_000_000, h4),\n        ];\n        let mut tree = LazyMerkleTree::<Keccak256>::new_with_dense_prefix(63, 10, &h0).derived();\n        for (ix, hash) in &updates {\n            tree = tree.update(*ix, hash);\n        }\n        for (ix, hash) in &updates {\n            let proof = tree.proof(*ix);\n            assert_eq!(proof.root(*hash), tree.root());\n        }\n        let first_three_leaves = tree.leaves().take(3).collect::<Vec<_>>();\n        assert_eq!(first_three_leaves, vec![h0, h1, h2]);\n\n        let mut tree = LazyMerkleTree::<Keccak256>::new_with_dense_prefix(63, 10, &h0);\n        let original_tree = tree.derived();\n        for (ix, hash) in &updates {\n            tree = tree.update_with_mutation(*ix, hash);\n        }\n        for (ix, hash) in &updates {\n            let proof = tree.proof(*ix);\n            assert_eq!(proof.root(*hash), tree.root());\n        }\n        let first_three_leaves = original_tree.leaves().take(3).collect::<Vec<_>>();\n        assert_eq!(first_three_leaves, vec![h0, h1, h2]);\n        let first_three_leaves = tree.leaves().take(3).collect::<Vec<_>>();\n        assert_eq!(first_three_leaves, vec![h0, h1, h2]);\n    }\n\n    #[test]\n    fn test_dense_mmap_tree() {\n        let h0 = [0; 32];\n        let h1 = hex!(\"0000000000000000000000000000000000000000000000000000000000000001\");\n        let h2 = hex!(\"0000000000000000000000000000000000000000000000000000000000000002\");\n        let h3 = hex!(\"0000000000000000000000000000000000000000000000000000000000000003\");\n        let h4 = hex!(\"0000000000000000000000000000000000000000000000000000000000000004\");\n        let h5 = hex!(\"0000000000000000000000000000000000000000000000000000000000000005\");\n        let h6 = hex!(\"0000000000000000000000000000000000000000000000000000000000000006\");\n        let h7 = hex!(\"0000000000000000000000000000000000000000000000000000000000000007\");\n        let h8 = hex!(\"0000000000000000000000000000000000000000000000000000000000000008\");\n\n        let initial_values = vec![h1, h2, h3, h4, h5, h6, h7, h8];\n\n        let tree: LazyMerkleTree<Keccak256, Canonical> =\n            LazyMerkleTree::<Keccak256>::new_mmapped_with_dense_prefix_with_init_values(\n                3,\n                3,\n                &h0,\n                &initial_values,\n                \"./testfile\",\n            )\n            .unwrap();\n        let tree_leaves = tree.leaves().collect::<Vec<_>>();\n\n        assert_eq!(tree_leaves, initial_values);\n\n        let proof_h1 = tree.proof(0);\n        assert!(tree.verify(h1, &proof_h1));\n\n        let proof_h2 = tree.proof(1);\n        assert!(tree.verify(h2, &proof_h2));\n\n        // drop a tree, the mmap file should still be there\n        drop(tree);\n\n        let tree: LazyMerkleTree<Keccak256, Canonical> =\n            LazyMerkleTree::<Keccak256>::attempt_dense_mmap_restore(3, 3, &h0, \"./testfile\")\n                .unwrap();\n\n        // repeat asserts again\n        let tree_leaves = tree.leaves().collect::<Vec<_>>();\n\n        assert_eq!(tree_leaves, initial_values);\n\n        let proof_h1 = tree.proof(0);\n        assert!(tree.verify(h1, &proof_h1));\n\n        let proof_h2 = tree.proof(1);\n        assert!(tree.verify(h2, &proof_h2));\n\n        // remove mmap file at the end\n        std::fs::remove_file(\"./testfile\").unwrap();\n    }\n}\n"
  },
  {
    "path": "crates/trees/src/lib.rs",
    "content": "pub mod cascading;\npub mod imt;\n#[cfg(not(target_arch = \"wasm32\"))]\npub mod lazy;\npub mod proof;\n\npub use proof::{Branch, InclusionProof};\n"
  },
  {
    "path": "crates/trees/src/proof.rs",
    "content": "use std::fmt::Debug;\n\nuse derive_where::derive_where;\nuse semaphore_rs_hasher::Hasher;\nuse serde::{Deserialize, Serialize};\n\n/// Merkle proof path, bottom to top.\n#[derive_where(Clone; <H as Hasher>::Hash: Clone)]\n#[derive_where(PartialEq; <H as Hasher>::Hash: PartialEq)]\n#[derive_where(Eq; <H as Hasher>::Hash: Eq)]\n#[derive_where(Debug; <H as Hasher>::Hash: Debug)]\npub struct InclusionProof<H>(pub Vec<Branch<H::Hash>>)\nwhere\n    H: Hasher;\n\n/// Element of a Merkle proof\n#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]\npub enum Branch<T> {\n    /// Left branch taken, value is the right sibling hash.\n    Left(T),\n\n    /// Right branch taken, value is the left sibling hash.\n    Right(T),\n}\n\nimpl<H> Serialize for InclusionProof<H>\nwhere\n    H: Hasher,\n    H::Hash: Serialize,\n{\n    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>\n    where\n        S: serde::Serializer,\n    {\n        self.0.serialize(serializer)\n    }\n}\n\nimpl<'de, H> Deserialize<'de> for InclusionProof<H>\nwhere\n    H: Hasher,\n    H::Hash: Deserialize<'de>,\n{\n    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>\n    where\n        D: serde::Deserializer<'de>,\n    {\n        let branches = Vec::deserialize(deserializer)?;\n        Ok(InclusionProof(branches))\n    }\n}\n\nimpl<T> Branch<T> {\n    /// Get the inner value\n    #[must_use]\n    pub fn into_inner(self) -> T {\n        match self {\n            Self::Left(sibling) => sibling,\n            Self::Right(sibling) => sibling,\n        }\n    }\n}\n\nimpl<T: Debug> Debug for Branch<T> {\n    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n        match self {\n            Self::Left(arg0) => f.debug_tuple(\"Left\").field(arg0).finish(),\n            Self::Right(arg0) => f.debug_tuple(\"Right\").field(arg0).finish(),\n        }\n    }\n}\n"
  },
  {
    "path": "crates/trees/tests/equivalent.rs",
    "content": "use rand::{thread_rng, Rng};\nuse ruint::aliases::U256;\nuse semaphore_rs_poseidon::Poseidon;\nuse semaphore_rs_trees::cascading::CascadingMerkleTree;\nuse semaphore_rs_trees::imt::MerkleTree;\nuse semaphore_rs_trees::lazy::{Canonical, LazyMerkleTree};\n\nconst DEPTH: usize = 20;\nconst DENSE_PREFIX: usize = 16;\n\nconst NUM_LEAVES: usize = 100;\n\ntype HashType = Poseidon;\nconst EMPTY_VALUE: U256 = U256::ZERO;\n\n#[test]\nfn equivalent() {\n    let mut lazy: LazyMerkleTree<HashType, Canonical> =\n        LazyMerkleTree::<HashType, Canonical>::new_with_dense_prefix(\n            DEPTH,\n            DENSE_PREFIX,\n            &EMPTY_VALUE,\n        );\n    let mut lazy_derived = lazy.derived();\n    let mut imt: MerkleTree<HashType> = MerkleTree::new(DEPTH, EMPTY_VALUE);\n    let mut cascading: CascadingMerkleTree<HashType> =\n        CascadingMerkleTree::new(vec![], DEPTH, &EMPTY_VALUE);\n\n    assert_eq!(lazy.root(), cascading.root());\n    assert_eq!(lazy.root(), imt.root());\n\n    let mut rng = thread_rng();\n\n    let random_leaves = (0..NUM_LEAVES)\n        .map(|_| {\n            let mut limbs = [0u64; 4];\n            for limb in limbs.iter_mut() {\n                *limb = rng.gen();\n            }\n            // zero last to fit in field\n            limbs[3] &= 0x0FFFFFFFFFFFFFFF;\n\n            U256::from_limbs(limbs)\n        })\n        .collect::<Vec<_>>();\n\n    for (i, leaf) in random_leaves.iter().enumerate() {\n        lazy_derived = lazy_derived.update(i, leaf);\n        imt.set(i, *leaf);\n        cascading.push(*leaf).unwrap();\n    }\n\n    // Lazy & IMT both return the total (i.e. max) number of leaves\n    assert_eq!(lazy.leaves().count(), lazy_derived.leaves().count());\n    assert_eq!(lazy.leaves().count(), imt.num_leaves());\n\n    // Cascading returns the current number of leaves\n    assert_eq!(cascading.num_leaves(), NUM_LEAVES);\n\n    assert_eq!(lazy_derived.root(), cascading.root());\n    assert_eq!(lazy_derived.root(), imt.root());\n\n    // Mutably update the canonical lazy tree\n    for (i, leaf) in random_leaves.iter().enumerate() {\n        lazy = lazy.update_with_mutation(i, leaf);\n    }\n\n    assert_eq!(lazy.root(), cascading.root());\n\n    for (i, leaf) in random_leaves.iter().enumerate() {\n        let cascading_proof = cascading.proof(i);\n        let lazy_proof = lazy.proof(i);\n        let imt_proof = imt.proof(i).unwrap();\n\n        assert_eq!(cascading_proof, lazy_proof);\n        assert_eq!(cascading_proof, imt_proof);\n\n        assert!(cascading.verify(*leaf, &cascading_proof));\n        assert!(lazy.verify(*leaf, &cascading_proof));\n        assert!(imt.verify(*leaf, &cascading_proof));\n    }\n}\n"
  },
  {
    "path": "crates/utils/Cargo.toml",
    "content": "[package]\nname = \"semaphore-rs-utils\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nauthors.workspace = true\ndescription.workspace = true\nkeywords.workspace = true\ncategories.workspace = true\n\n[dependencies]\nserde.workspace = true\ntiny-keccak.workspace = true\nhex.workspace = true\n\n[dev-dependencies]\nserde_json.workspace = true\nbincode.workspace = true\n\n"
  },
  {
    "path": "crates/utils/src/lib.rs",
    "content": "use core::{\n    fmt::{Formatter, Result as FmtResult},\n    str,\n};\nuse serde::{\n    de::{Error as DeError, Visitor},\n    Deserializer, Serializer,\n};\nuse tiny_keccak::{Hasher as _, Keccak};\n\npub fn keccak256(bytes: &[u8]) -> [u8; 32] {\n    let mut output = [0; 32];\n    let mut hasher = Keccak::v256();\n    hasher.update(bytes);\n    hasher.finalize(&mut output);\n    output\n}\n\npub fn bytes_to_hex<const N: usize, const M: usize>(bytes: &[u8; N]) -> [u8; M] {\n    // TODO: Replace `M` with a const expression once it's stable.\n    debug_assert_eq!(M, 2 * N + 2);\n    let mut result = [0u8; M];\n    result[0] = b'0';\n    result[1] = b'x';\n    hex::encode_to_slice(&bytes[..], &mut result[2..]).expect(\"the buffer is correctly sized\");\n    result\n}\n\n/// Helper to serialize byte arrays\npub fn serialize_bytes<const N: usize, const M: usize, S: Serializer>(\n    serializer: S,\n    bytes: &[u8; N],\n) -> Result<S::Ok, S::Error> {\n    // TODO: Replace `M` with a const expression once it's stable.\n    debug_assert_eq!(M, 2 * N + 2);\n    if serializer.is_human_readable() {\n        // Write as a 0x prefixed lower-case hex string\n        let buffer = bytes_to_hex::<N, M>(bytes);\n        let string = str::from_utf8(&buffer).expect(\"the buffer is valid UTF-8\");\n        serializer.serialize_str(string)\n    } else {\n        // Write as bytes directly\n        serializer.serialize_bytes(&bytes[..])\n    }\n}\n\n/// Helper to deserialize byte arrays from hex strings\n///\n/// TODO: How does it handle strings that are to short?\npub fn bytes_from_hex<const N: usize>(s: &str) -> Result<[u8; N], hex::FromHexError> {\n    let str = trim_hex_prefix(s);\n    let mut result = [0_u8; N];\n    hex::decode_to_slice(str, &mut result)?;\n    Ok(result)\n}\n\n/// Helper function to remove  optionally `0x` prefix from hex strings.\nfn trim_hex_prefix(str: &str) -> &str {\n    str.trim_start_matches(\"0x\").trim_start_matches(\"0X\")\n}\n\n/// Helper to deserialize byte arrays.\npub fn deserialize_bytes<'de, const N: usize, D: Deserializer<'de>>(\n    deserializer: D,\n) -> Result<[u8; N], D::Error> {\n    if deserializer.is_human_readable() {\n        struct StrVisitor<const N: usize>;\n        impl<const N: usize> Visitor<'_> for StrVisitor<N> {\n            type Value = [u8; N];\n\n            fn expecting(&self, formatter: &mut Formatter) -> FmtResult {\n                write!(formatter, \"a {N} byte hex string\")\n            }\n\n            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>\n            where\n                E: DeError,\n            {\n                bytes_from_hex(value).map_err(|e| E::custom(format!(\"Error in hex: {e}\")))\n            }\n        }\n        deserializer.deserialize_str(StrVisitor)\n    } else {\n        struct ByteVisitor<const N: usize>;\n        impl<const N: usize> Visitor<'_> for ByteVisitor<N> {\n            type Value = [u8; N];\n\n            fn expecting(&self, formatter: &mut Formatter) -> FmtResult {\n                write!(formatter, \"{N} bytes of binary data\")\n            }\n\n            fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>\n            where\n                E: DeError,\n            {\n                if value.len() != N {\n                    return Err(E::invalid_length(value.len(), &self));\n                }\n                let mut result = [0_u8; N];\n                result.copy_from_slice(value);\n                Ok(result)\n            }\n        }\n        deserializer.deserialize_bytes(ByteVisitor)\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    #[test]\n    fn test_serialize_bytes_hex() {\n        let bytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];\n        let mut ser = serde_json::Serializer::new(Vec::new());\n        serialize_bytes::<16, 34, _>(&mut ser, &bytes).unwrap();\n        let json = ser.into_inner();\n        assert_eq!(json, b\"\\\"0x0102030405060708090a0b0c0d0e0f10\\\"\");\n    }\n\n    #[test]\n    fn test_serialize_bytes_bin() {\n        let bytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];\n        let mut bin: Vec<u8> = Vec::new();\n        {\n            let mut ser = bincode::Serializer::new(&mut bin, bincode::options());\n            serialize_bytes::<16, 34, _>(&mut ser, &bytes).unwrap();\n        }\n        // Bincode appears to prefix with a length.\n        assert_eq!(\n            bin,\n            [16, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]\n        );\n    }\n}\n"
  },
  {
    "path": "cspell.json",
    "content": "{\n    \"version\": \"0.2\",\n    \"ignorePaths\": [],\n    \"dictionaryDefinitions\": [],\n    \"dictionaries\": [],\n    \"words\": [\n        \"biguint\",\n        \"chacha\",\n        \"circom\",\n        \"groth\",\n        \"hasher\",\n        \"keccak\",\n        \"merkle\",\n        \"mmaped\",\n        \"modpow\",\n        \"mulmod\",\n        \"proptest\",\n        \"Repr\",\n        \"Seedable\",\n        \"snarkfiles\",\n        \"thiserror\",\n        \"zkey\"\n    ],\n    \"ignoreWords\": [],\n    \"import\": []\n}\n"
  },
  {
    "path": "mit-license.md",
    "content": "# The MIT License (MIT)\n\nCopyright © 2021-2023 Worldcoin Foundation\n\nPermission 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:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\n**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.**\n"
  },
  {
    "path": "publish_all.sh",
    "content": "#!/usr/bin/env bash\n\nset -e\n\ncargo publish -p semaphore-rs-utils\ncargo publish -p semaphore-rs-ark-circom\ncargo publish -p semaphore-rs-proof\ncargo publish -p semaphore-rs-ark-zkey\ncargo publish -p semaphore-rs-hasher\ncargo publish -p semaphore-rs-poseidon\ncargo publish -p semaphore-rs-keccak\ncargo publish -p semaphore-rs-storage\ncargo publish -p semaphore-rs-trees\ncargo publish -p semaphore-rs-depth-config\ncargo publish -p semaphore-rs-depth-macros\ncargo publish -p semaphore-rs-witness\ncargo publish -p semaphore-rs\n\n"
  },
  {
    "path": "release-plz.toml",
    "content": "[workspace]\n# Automatically create GitHub releases for each package.\n# This creates tags like: semaphore-rs-v0.5.1, semaphore-rs-hasher-v0.5.1, etc.\ngit_release_enable = true\ngit_release_type = \"auto\"\npr_labels = [\"release\"]\nrelease = false # only process specifically defined packages\npublish = false # only process specifically defined packages\nchangelog_update = false\n\n# Publish only specific packages to crates.io.\n# semaphore-rs-js is excluded — it carries its own independent version (0.3.0\n# vs workspace 0.5.0) and is managed separately.\n# release-plz resolves publish order automatically from the Cargo dependency graph.\n\n# semaphore-rs is the \"primary\" package that owns the root CHANGELOG.md.\n# changelog_include pulls in changes from every other published crate so that\n# we get a single, aggregated changelog at the repo root.  All other packages\n# have changelog_update = false to avoid overwriting the same file.\n[[package]]\nname = \"semaphore-rs\"\nrelease = true\npublish = true\nchangelog_update = true\nchangelog_path = \"CHANGELOG.md\"\nchangelog_include = [\n  \"semaphore-rs-utils\",\n  \"semaphore-rs-ark-circom\",\n  \"semaphore-rs-proof\",\n  \"semaphore-rs-ark-zkey\",\n  \"semaphore-rs-hasher\",\n  \"semaphore-rs-poseidon\",\n  \"semaphore-rs-keccak\",\n  \"semaphore-rs-storage\",\n  \"semaphore-rs-trees\",\n  \"semaphore-rs-depth-config\",\n  \"semaphore-rs-depth-macros\",\n  \"semaphore-rs-witness\",\n]\n\n[[package]]\nname = \"semaphore-rs-utils\"\nrelease = true\npublish = true\nchangelog_update = false\n\n[[package]]\nname = \"semaphore-rs-ark-circom\"\nrelease = true\npublish = true\nchangelog_update = false\n\n[[package]]\nname = \"semaphore-rs-proof\"\nrelease = true\npublish = true\nchangelog_update = false\n\n[[package]]\nname = \"semaphore-rs-ark-zkey\"\nrelease = true\npublish = true\nchangelog_update = false\n\n[[package]]\nname = \"semaphore-rs-hasher\"\nrelease = true\npublish = true\nchangelog_update = false\n\n[[package]]\nname = \"semaphore-rs-poseidon\"\nrelease = true\npublish = true\nchangelog_update = false\n\n[[package]]\nname = \"semaphore-rs-keccak\"\nrelease = true\npublish = true\nchangelog_update = false\n\n[[package]]\nname = \"semaphore-rs-storage\"\nrelease = true\npublish = true\nchangelog_update = false\n\n[[package]]\nname = \"semaphore-rs-trees\"\nrelease = true\npublish = true\nchangelog_update = false\n\n[[package]]\nname = \"semaphore-rs-depth-config\"\nrelease = true\npublish = true\nchangelog_update = false\n\n[[package]]\nname = \"semaphore-rs-depth-macros\"\nrelease = true\npublish = true\nchangelog_update = false\n\n[[package]]\nname = \"semaphore-rs-witness\"\nrelease = true\npublish = true\nchangelog_update = false\n"
  },
  {
    "path": "supply-chain/audits.toml",
    "content": "\n# cargo-vet audits file\n\n[audits]\n\n[[trusted.aho-corasick]]\ncriteria = \"safe-to-deploy\"\nuser-id = 189 # Andrew Gallant (BurntSushi)\nstart = \"2019-03-28\"\nend = \"2024-06-15\"\n\n[[trusted.byteorder]]\ncriteria = \"safe-to-deploy\"\nuser-id = 189 # Andrew Gallant (BurntSushi)\nstart = \"2019-06-09\"\nend = \"2024-06-15\"\n\n[[trusted.csv]]\ncriteria = \"safe-to-deploy\"\nuser-id = 189 # Andrew Gallant (BurntSushi)\nstart = \"2019-04-05\"\nend = \"2024-06-15\"\n\n[[trusted.csv-core]]\ncriteria = \"safe-to-deploy\"\nuser-id = 189 # Andrew Gallant (BurntSushi)\nstart = \"2019-06-26\"\nend = \"2024-06-15\"\n\n[[trusted.memchr]]\ncriteria = \"safe-to-deploy\"\nuser-id = 189 # Andrew Gallant (BurntSushi)\nstart = \"2019-07-07\"\nend = \"2024-06-15\"\n\n[[trusted.proc-macro2]]\ncriteria = \"safe-to-deploy\"\nuser-id = 3618 # David Tolnay (dtolnay)\nstart = \"2019-04-23\"\nend = \"2024-06-14\"\n\n[[trusted.regex]]\ncriteria = \"safe-to-deploy\"\nuser-id = 189 # Andrew Gallant (BurntSushi)\nstart = \"2019-02-27\"\nend = \"2024-06-15\"\n\n[[trusted.regex-automata]]\ncriteria = \"safe-to-deploy\"\nuser-id = 189 # Andrew Gallant (BurntSushi)\nstart = \"2019-02-25\"\nend = \"2024-06-15\"\n\n[[trusted.regex-syntax]]\ncriteria = \"safe-to-deploy\"\nuser-id = 189 # Andrew Gallant (BurntSushi)\nstart = \"2019-03-30\"\nend = \"2024-06-15\"\n\n[[trusted.same-file]]\ncriteria = \"safe-to-deploy\"\nuser-id = 189 # Andrew Gallant (BurntSushi)\nstart = \"2019-07-16\"\nend = \"2024-06-15\"\n\n[[trusted.serde]]\ncriteria = \"safe-to-deploy\"\nuser-id = 3618 # David Tolnay (dtolnay)\nstart = \"2019-03-01\"\nend = \"2024-06-14\"\n\n[[trusted.serde_derive]]\ncriteria = \"safe-to-deploy\"\nuser-id = 3618 # David Tolnay (dtolnay)\nstart = \"2019-03-01\"\nend = \"2024-06-14\"\n\n[[trusted.ucd-trie]]\ncriteria = \"safe-to-deploy\"\nuser-id = 189 # Andrew Gallant (BurntSushi)\nstart = \"2019-07-21\"\nend = \"2024-06-15\"\n\n[[trusted.walkdir]]\ncriteria = \"safe-to-deploy\"\nuser-id = 189 # Andrew Gallant (BurntSushi)\nstart = \"2019-06-09\"\nend = \"2024-06-15\"\n\n[[trusted.winapi-util]]\ncriteria = \"safe-to-deploy\"\nuser-id = 189 # Andrew Gallant (BurntSushi)\nstart = \"2020-01-11\"\nend = \"2024-06-15\"\n"
  },
  {
    "path": "supply-chain/config.toml",
    "content": "\n# cargo-vet config file\n\n[cargo-vet]\nversion = \"0.9\"\n\n[imports.bytecodealliance]\nurl = \"https://raw.githubusercontent.com/bytecodealliance/wasmtime/main/supply-chain/audits.toml\"\n\n[imports.embark]\nurl = \"https://raw.githubusercontent.com/EmbarkStudios/rust-ecosystem/main/audits.toml\"\n\n[imports.google]\nurl = \"https://raw.githubusercontent.com/google/supply-chain/main/audits.toml\"\n\n[imports.isrg]\nurl = \"https://raw.githubusercontent.com/divviup/libprio-rs/main/supply-chain/audits.toml\"\n\n[imports.mozilla]\nurl = \"https://raw.githubusercontent.com/mozilla/supply-chain/main/audits.toml\"\n\n[policy.ark-groth16]\naudit-as-crates-io = true\n\n[policy.semaphore]\naudit-as-crates-io = false\n\n[[exemptions.addr2line]]\nversion = \"0.19.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ahash]]\nversion = \"0.7.6\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.alloy-rlp]]\nversion = \"0.3.4\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ark-bn254]]\nversion = \"0.3.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ark-crypto-primitives]]\nversion = \"0.3.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ark-ec]]\nversion = \"0.3.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ark-ff]]\nversion = \"0.3.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ark-ff]]\nversion = \"0.4.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ark-ff-asm]]\nversion = \"0.3.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ark-ff-asm]]\nversion = \"0.4.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ark-ff-macros]]\nversion = \"0.3.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ark-ff-macros]]\nversion = \"0.4.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ark-groth16]]\nversion = \"0.3.0@git:765817f77a6e14964c6f264d565b18676b11bd59\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ark-poly]]\nversion = \"0.3.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ark-relations]]\nversion = \"0.3.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ark-serialize]]\nversion = \"0.3.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ark-serialize]]\nversion = \"0.4.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ark-serialize-derive]]\nversion = \"0.3.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ark-snark]]\nversion = \"0.3.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ark-std]]\nversion = \"0.3.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ark-std]]\nversion = \"0.4.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.arrayvec]]\nversion = \"0.7.4\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.auto_impl]]\nversion = \"1.2.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.backtrace]]\nversion = \"0.3.71\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.base16ct]]\nversion = \"0.2.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.base64]]\nversion = \"0.21.7\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.base64ct]]\nversion = \"1.6.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.bincode]]\nversion = \"1.3.3\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.bitflags]]\nversion = \"1.3.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.bitvec]]\nversion = \"1.0.1\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.blake2]]\nversion = \"0.9.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.block-buffer]]\nversion = \"0.10.4\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.byte-slice-cast]]\nversion = \"1.2.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.bytecheck]]\nversion = \"0.6.12\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.bytecheck_derive]]\nversion = \"0.6.12\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.bytes]]\nversion = \"1.6.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.cast]]\nversion = \"0.3.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.cc]]\nversion = \"1.0.90\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.chrono]]\nversion = \"0.4.37\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ciborium]]\nversion = \"0.2.2\"\ncriteria = \"safe-to-run\"\n\n[[exemptions.ciborium-io]]\nversion = \"0.2.2\"\ncriteria = \"safe-to-run\"\n\n[[exemptions.ciborium-ll]]\nversion = \"0.2.2\"\ncriteria = \"safe-to-run\"\n\n[[exemptions.clap]]\nversion = \"2.34.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.color-eyre]]\nversion = \"0.5.11\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.color-eyre]]\nversion = \"0.6.3\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.color-spantrace]]\nversion = \"0.1.6\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.color-spantrace]]\nversion = \"0.2.1\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.combine]]\nversion = \"4.6.6\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.const-hex]]\nversion = \"1.11.3\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.const-oid]]\nversion = \"0.9.6\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.core-foundation]]\nversion = \"0.9.4\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.corosensei]]\nversion = \"0.1.4\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.cpufeatures]]\nversion = \"0.2.12\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.crc32fast]]\nversion = \"1.4.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.criterion]]\nversion = \"0.3.6\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.criterion-plot]]\nversion = \"0.4.5\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.crossbeam-deque]]\nversion = \"0.8.5\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.crossbeam-epoch]]\nversion = \"0.9.18\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.crossbeam-utils]]\nversion = \"0.8.8\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.crypto-bigint]]\nversion = \"0.5.5\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.crypto-mac]]\nversion = \"0.8.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.darling]]\nversion = \"0.20.8\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.darling_core]]\nversion = \"0.20.8\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.darling_macro]]\nversion = \"0.20.8\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.dashmap]]\nversion = \"5.5.3\"\ncriteria = \"safe-to-run\"\n\n[[exemptions.der]]\nversion = \"0.7.9\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.derivative]]\nversion = \"2.2.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.digest]]\nversion = \"0.9.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ecdsa]]\nversion = \"0.16.9\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.either]]\nversion = \"1.10.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.elliptic-curve]]\nversion = \"0.13.8\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.enum-as-inner]]\nversion = \"0.6.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.enum-iterator]]\nversion = \"0.7.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.enum-iterator-derive]]\nversion = \"0.7.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.enumset]]\nversion = \"1.1.3\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.enumset_derive]]\nversion = \"0.8.1\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.errno]]\nversion = \"0.3.8\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ethabi]]\nversion = \"18.0.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ethbloom]]\nversion = \"0.13.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ethereum-types]]\nversion = \"0.14.1\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.eyre]]\nversion = \"0.6.12\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.fallible-iterator]]\nversion = \"0.2.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.fastrand]]\nversion = \"2.0.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.fastrlp]]\nversion = \"0.3.1\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ff]]\nversion = \"0.13.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.fixed-hash]]\nversion = \"0.8.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.funty]]\nversion = \"2.0.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.futures]]\nversion = \"0.3.30\"\ncriteria = \"safe-to-run\"\n\n[[exemptions.futures-channel]]\nversion = \"0.3.30\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.futures-core]]\nversion = \"0.3.30\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.futures-executor]]\nversion = \"0.3.30\"\ncriteria = \"safe-to-run\"\n\n[[exemptions.futures-io]]\nversion = \"0.3.30\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.futures-sink]]\nversion = \"0.3.30\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.futures-task]]\nversion = \"0.3.30\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.futures-util]]\nversion = \"0.3.30\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.generic-array]]\nversion = \"0.14.7\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.getrandom]]\nversion = \"0.2.9\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.gimli]]\nversion = \"0.26.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.gimli]]\nversion = \"0.28.1\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.group]]\nversion = \"0.13.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.h2]]\nversion = \"0.3.26\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.half]]\nversion = \"1.8.3\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.half]]\nversion = \"2.4.1\"\ncriteria = \"safe-to-run\"\n\n[[exemptions.hashbrown]]\nversion = \"0.11.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.hashbrown]]\nversion = \"0.14.3\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.hermit-abi]]\nversion = \"0.1.19\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.hermit-abi]]\nversion = \"0.3.9\"\ncriteria = \"safe-to-run\"\n\n[[exemptions.hex-literal]]\nversion = \"0.4.1\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.home]]\nversion = \"0.5.9\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.http]]\nversion = \"0.2.12\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.http-body]]\nversion = \"0.4.6\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.httparse]]\nversion = \"1.8.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.hyper]]\nversion = \"0.14.28\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.hyper-tls]]\nversion = \"0.5.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.impl-codec]]\nversion = \"0.6.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.impl-rlp]]\nversion = \"0.3.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.impl-serde]]\nversion = \"0.4.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.impl-trait-for-tuples]]\nversion = \"0.2.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.indenter]]\nversion = \"0.3.3\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.indexmap]]\nversion = \"1.9.3\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.indexmap]]\nversion = \"2.2.6\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ipnet]]\nversion = \"2.9.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.is-terminal]]\nversion = \"0.4.12\"\ncriteria = \"safe-to-run\"\n\n[[exemptions.itertools]]\nversion = \"0.10.5\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.js-sys]]\nversion = \"0.3.69\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.k256]]\nversion = \"0.13.3\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.keccak]]\nversion = \"0.1.5\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.libc]]\nversion = \"0.2.146\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.libloading]]\nversion = \"0.7.4\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.libm]]\nversion = \"0.2.8\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.linux-raw-sys]]\nversion = \"0.4.13\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.lock_api]]\nversion = \"0.4.11\"\ncriteria = \"safe-to-run\"\n\n[[exemptions.log]]\nversion = \"0.4.21\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.loupe]]\nversion = \"0.1.3\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.loupe-derive]]\nversion = \"0.1.3\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.mach]]\nversion = \"0.3.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.memmap2]]\nversion = \"0.5.10\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.memoffset]]\nversion = \"0.6.5\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.mime]]\nversion = \"0.3.17\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.miniz_oxide]]\nversion = \"0.7.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.mio]]\nversion = \"0.8.11\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.mmap-rs]]\nversion = \"0.6.1\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.more-asserts]]\nversion = \"0.2.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.nix]]\nversion = \"0.26.4\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.num]]\nversion = \"0.4.1\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.num-complex]]\nversion = \"0.4.5\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.num_enum]]\nversion = \"0.7.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.num_enum_derive]]\nversion = \"0.7.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.object]]\nversion = \"0.28.4\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.object]]\nversion = \"0.32.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.once_cell]]\nversion = \"1.17.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.oorandom]]\nversion = \"11.1.3\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.opaque-debug]]\nversion = \"0.3.1\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.open-fastrlp]]\nversion = \"0.1.4\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.open-fastrlp-derive]]\nversion = \"0.1.1\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.owo-colors]]\nversion = \"1.3.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.owo-colors]]\nversion = \"3.5.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.parity-scale-codec]]\nversion = \"3.6.9\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.parity-scale-codec-derive]]\nversion = \"3.6.9\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.parking_lot_core]]\nversion = \"0.9.9\"\ncriteria = \"safe-to-run\"\n\n[[exemptions.paste]]\nversion = \"1.0.14\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.pest]]\nversion = \"2.7.9\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.pin-project-lite]]\nversion = \"0.2.14\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.pkcs8]]\nversion = \"0.10.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.pkg-config]]\nversion = \"0.3.30\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.plotters]]\nversion = \"0.3.5\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.plotters-backend]]\nversion = \"0.3.5\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.plotters-svg]]\nversion = \"0.3.5\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ppv-lite86]]\nversion = \"0.2.17\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.primitive-types]]\nversion = \"0.12.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.proc-macro-crate]]\nversion = \"1.3.1\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.proc-macro-crate]]\nversion = \"2.0.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.proc-macro-crate]]\nversion = \"3.1.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.proc-macro-error]]\nversion = \"1.0.4\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.proptest]]\nversion = \"1.4.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ptr_meta]]\nversion = \"0.1.4\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ptr_meta_derive]]\nversion = \"0.1.4\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.quick-error]]\nversion = \"1.2.3\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.radium]]\nversion = \"0.7.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.rand]]\nversion = \"0.8.5\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.rand_xorshift]]\nversion = \"0.3.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.redox_syscall]]\nversion = \"0.4.1\"\ncriteria = \"safe-to-run\"\n\n[[exemptions.regalloc]]\nversion = \"0.0.34\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.region]]\nversion = \"3.0.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.rend]]\nversion = \"0.4.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.reqwest]]\nversion = \"0.11.27\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.rfc6979]]\nversion = \"0.4.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.rkyv]]\nversion = \"0.7.44\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.rkyv_derive]]\nversion = \"0.7.44\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.rlp]]\nversion = \"0.5.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.rlp-derive]]\nversion = \"0.1.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ruint]]\nversion = \"1.12.1\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ruint-macro]]\nversion = \"1.2.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.rustc-demangle]]\nversion = \"0.1.23\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.rustc-hex]]\nversion = \"2.1.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.rustc_version]]\nversion = \"0.3.3\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.rustc_version]]\nversion = \"0.4.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.rustix]]\nversion = \"0.38.32\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.rustls-pemfile]]\nversion = \"1.0.4\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.rusty-fork]]\nversion = \"0.3.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.ryu]]\nversion = \"1.0.17\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.scale-info]]\nversion = \"2.11.1\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.scale-info-derive]]\nversion = \"2.11.1\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.schannel]]\nversion = \"0.1.23\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.scopeguard]]\nversion = \"1.2.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.seahash]]\nversion = \"4.1.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.sec1]]\nversion = \"0.7.3\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.security-framework]]\nversion = \"2.10.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.security-framework-sys]]\nversion = \"2.10.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.semver]]\nversion = \"0.11.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.semver]]\nversion = \"1.0.22\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.semver-parser]]\nversion = \"0.10.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.serde_bytes]]\nversion = \"0.11.14\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.serde_json]]\nversion = \"1.0.115\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.serde_urlencoded]]\nversion = \"0.7.1\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.serial_test]]\nversion = \"3.0.0\"\ncriteria = \"safe-to-run\"\n\n[[exemptions.serial_test_derive]]\nversion = \"3.0.0\"\ncriteria = \"safe-to-run\"\n\n[[exemptions.sha2]]\nversion = \"0.10.8\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.sharded-slab]]\nversion = \"0.1.7\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.signature]]\nversion = \"2.2.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.simdutf8]]\nversion = \"0.1.4\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.slab]]\nversion = \"0.4.9\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.smallvec]]\nversion = \"1.13.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.socket2]]\nversion = \"0.5.6\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.spki]]\nversion = \"0.7.3\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.stable_deref_trait]]\nversion = \"1.2.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.strum]]\nversion = \"0.26.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.strum_macros]]\nversion = \"0.26.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.syn]]\nversion = \"1.0.109\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.syn]]\nversion = \"2.0.58\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.sync_wrapper]]\nversion = \"0.1.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.sysctl]]\nversion = \"0.5.5\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.system-configuration]]\nversion = \"0.5.1\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.system-configuration-sys]]\nversion = \"0.5.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.target-lexicon]]\nversion = \"0.12.14\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.tempfile]]\nversion = \"3.10.1\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.textwrap]]\nversion = \"0.11.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.thiserror]]\nversion = \"1.0.58\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.thiserror-impl]]\nversion = \"1.0.58\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.thread_local]]\nversion = \"1.1.8\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.tiny-keccak]]\nversion = \"2.0.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.tinytemplate]]\nversion = \"1.2.1\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.tinyvec_macros]]\nversion = \"0.1.1\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.tokio]]\nversion = \"1.37.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.tokio-util]]\nversion = \"0.7.10\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.toml_datetime]]\nversion = \"0.6.5\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.toml_edit]]\nversion = \"0.19.15\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.toml_edit]]\nversion = \"0.20.7\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.toml_edit]]\nversion = \"0.21.1\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.tower-service]]\nversion = \"0.3.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.tracing]]\nversion = \"0.1.40\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.tracing-attributes]]\nversion = \"0.1.27\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.tracing-core]]\nversion = \"0.1.32\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.tracing-error]]\nversion = \"0.1.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.tracing-error]]\nversion = \"0.2.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.tracing-log]]\nversion = \"0.2.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.tracing-subscriber]]\nversion = \"0.2.25\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.tracing-subscriber]]\nversion = \"0.3.18\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.tracing-test]]\nversion = \"0.2.4\"\ncriteria = \"safe-to-run\"\n\n[[exemptions.tracing-test-macro]]\nversion = \"0.2.4\"\ncriteria = \"safe-to-run\"\n\n[[exemptions.try-lock]]\nversion = \"0.2.5\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.typenum]]\nversion = \"1.17.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.uint]]\nversion = \"0.9.5\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.unarray]]\nversion = \"0.1.4\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.uuid]]\nversion = \"1.8.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.wait-timeout]]\nversion = \"0.2.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.want]]\nversion = \"0.3.1\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.wasi]]\nversion = \"0.11.0+wasi-snapshot-preview1\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.wasm-bindgen]]\nversion = \"0.2.92\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.wasm-bindgen-backend]]\nversion = \"0.2.92\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.wasm-bindgen-futures]]\nversion = \"0.4.42\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.wasm-bindgen-macro]]\nversion = \"0.2.92\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.wasm-bindgen-macro-support]]\nversion = \"0.2.92\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.wasm-bindgen-shared]]\nversion = \"0.2.92\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.wasm-encoder]]\nversion = \"0.202.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.wasmparser]]\nversion = \"0.83.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.wast]]\nversion = \"202.0.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.wat]]\nversion = \"1.202.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.web-sys]]\nversion = \"0.3.69\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.which]]\nversion = \"4.4.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.widestring]]\nversion = \"1.0.2\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.winapi]]\nversion = \"0.3.9\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.winapi-i686-pc-windows-gnu]]\nversion = \"0.4.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.winapi-x86_64-pc-windows-gnu]]\nversion = \"0.4.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.windows]]\nversion = \"0.48.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.windows-sys]]\nversion = \"0.33.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.windows-sys]]\nversion = \"0.48.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.windows-sys]]\nversion = \"0.52.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.windows-targets]]\nversion = \"0.48.5\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.windows-targets]]\nversion = \"0.52.4\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.windows_aarch64_gnullvm]]\nversion = \"0.48.5\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.windows_aarch64_gnullvm]]\nversion = \"0.52.4\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.windows_aarch64_msvc]]\nversion = \"0.33.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.windows_aarch64_msvc]]\nversion = \"0.48.5\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.windows_aarch64_msvc]]\nversion = \"0.52.4\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.windows_i686_gnu]]\nversion = \"0.33.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.windows_i686_gnu]]\nversion = \"0.48.5\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.windows_i686_gnu]]\nversion = \"0.52.4\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.windows_i686_msvc]]\nversion = \"0.33.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.windows_i686_msvc]]\nversion = \"0.48.5\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.windows_i686_msvc]]\nversion = \"0.52.4\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.windows_x86_64_gnu]]\nversion = \"0.33.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.windows_x86_64_gnu]]\nversion = \"0.48.5\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.windows_x86_64_gnu]]\nversion = \"0.52.4\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.windows_x86_64_gnullvm]]\nversion = \"0.48.5\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.windows_x86_64_gnullvm]]\nversion = \"0.52.4\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.windows_x86_64_msvc]]\nversion = \"0.33.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.windows_x86_64_msvc]]\nversion = \"0.48.5\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.windows_x86_64_msvc]]\nversion = \"0.52.4\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.winnow]]\nversion = \"0.5.40\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.winreg]]\nversion = \"0.50.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.wyz]]\nversion = \"0.5.1\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.zeroize]]\nversion = \"1.7.0\"\ncriteria = \"safe-to-deploy\"\n\n[[exemptions.zeroize_derive]]\nversion = \"1.4.2\"\ncriteria = \"safe-to-deploy\"\n"
  }
]