Showing preview only (430K chars total). Download the full file or copy to clipboard to get everything.
Repository: worldcoin/semaphore-rs
Branch: main
Commit: 00df132aaa76
Files: 93
Total size: 403.0 KB
Directory structure:
gitextract_5m5mxrb4/
├── .github/
│ └── workflows/
│ ├── build-and-test.yml
│ ├── release-crates.yml
│ └── relyance-sci.yml
├── .gitignore
├── CHANGELOG.md
├── Cargo.toml
├── crates/
│ ├── ark-circom/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ ├── circom.rs
│ │ │ ├── ethereum.rs
│ │ │ ├── lib.rs
│ │ │ └── zkey.rs
│ │ └── test-vectors/
│ │ ├── test.zkey
│ │ └── verification_key.json
│ ├── ark-zkey/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ └── src/
│ │ ├── lib.rs
│ │ └── semaphore.16.arkzkey
│ ├── circom-witness-rs/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── build.rs
│ │ ├── include/
│ │ │ └── witness.h
│ │ ├── script/
│ │ │ └── replace.sh
│ │ └── src/
│ │ ├── field.rs
│ │ ├── generate.rs
│ │ ├── graph.rs
│ │ └── lib.rs
│ ├── hasher/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── js/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ ├── build.mjs
│ │ ├── example/
│ │ │ ├── .gitignore
│ │ │ ├── index.mjs
│ │ │ ├── index.ts
│ │ │ └── package.json
│ │ └── src/
│ │ └── lib.rs
│ ├── keccak/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── keccak.rs
│ │ ├── lib.rs
│ │ └── sha3.rs
│ ├── poseidon/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── constants.rs
│ │ ├── lib.rs
│ │ └── poseidon.rs
│ ├── proof/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── ark.rs
│ │ ├── compression.rs
│ │ ├── lib.rs
│ │ └── packing.rs
│ ├── semaphore/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ ├── benches/
│ │ │ ├── cascading_merkle_tree.rs
│ │ │ └── lazy_merkle_tree.rs
│ │ ├── build.rs
│ │ ├── examples/
│ │ │ └── abort/
│ │ │ └── main.rs
│ │ └── src/
│ │ ├── circuit.rs
│ │ ├── field.rs
│ │ ├── hash.rs
│ │ ├── identity.rs
│ │ ├── lib.rs
│ │ ├── packed_proof.rs
│ │ ├── poseidon_tree.rs
│ │ ├── protocol/
│ │ │ ├── authentication.rs
│ │ │ └── mod.rs
│ │ └── util.rs
│ ├── semaphore-depth-config/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── semaphore-depth-macros/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── storage/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs
│ │ └── native/
│ │ ├── mmap_vec.rs
│ │ └── mod.rs
│ ├── trees/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ ├── cascading/
│ │ │ │ ├── mod.rs
│ │ │ │ └── storage_ops.rs
│ │ │ ├── imt/
│ │ │ │ └── mod.rs
│ │ │ ├── lazy/
│ │ │ │ └── mod.rs
│ │ │ ├── lib.rs
│ │ │ └── proof.rs
│ │ └── tests/
│ │ └── equivalent.rs
│ └── utils/
│ ├── Cargo.toml
│ └── src/
│ └── lib.rs
├── cspell.json
├── mit-license.md
├── publish_all.sh
├── release-plz.toml
└── supply-chain/
├── audits.toml
└── config.toml
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/build-and-test.yml
================================================
name: Build and Test
on: push
env:
RUST_VERSION: 1.94.0
jobs:
dependabot-dependency-review:
runs-on: ubuntu-latest
steps:
- name: "Checkout Repository"
uses: actions/checkout@v4
- name: "Dependency Review"
uses: actions/dependency-review-action@v4
with:
base-ref: ${{ inputs.base-ref || github.event.pull_request.base.sha || 'main' }}
head-ref: ${{ inputs.head-ref || github.event.pull_request.head.sha || github.ref }}
test:
name: Test
runs-on: ubuntu-latest
env:
RUSTFLAGS: "-D warnings"
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up Rust
run: |
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 }}
- name: Cache Cargo registry
uses: actions/cache@v4
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.toml') }}
restore-keys: |
${{ runner.os }}-cargo-registry-
- name: Cache Cargo index
uses: actions/cache@v4
with:
path: ~/.cargo/git
key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.toml') }}
restore-keys: |
${{ runner.os }}-cargo-index-
- name: Cache Cargo build
uses: actions/cache@v4
with:
path: target
key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.toml') }}
restore-keys: |
${{ runner.os }}-cargo-build-
- name: Check
run: cargo check --workspace --tests --benches --features depth_16,depth_20,depth_30
- name: Check Formatting
run: cargo fmt --all -- --check
- name: Run Clippy
run: cargo clippy --all-targets --features depth_16,depth_20,depth_30
- name: Run test
run: cargo test --workspace --features depth_16,depth_20,depth_30
wasm:
name: WASM Check
runs-on: ubuntu-latest
env:
RUSTFLAGS: "-D warnings"
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up Rust
run: |
rustup update ${{ env.RUST_VERSION }} && rustup default ${{ env.RUST_VERSION }}
rustup target add wasm32-unknown-unknown
- name: Cache Cargo registry
uses: actions/cache@v4
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-wasm-cargo-registry-${{ hashFiles('**/Cargo.toml') }}
restore-keys: |
${{ runner.os }}-wasm-cargo-registry-
- name: Cache Cargo index
uses: actions/cache@v4
with:
path: ~/.cargo/git
key: ${{ runner.os }}-wasm-cargo-index-${{ hashFiles('**/Cargo.toml') }}
restore-keys: |
${{ runner.os }}-wasm-cargo-index-
- name: Cache Cargo build
uses: actions/cache@v4
with:
path: target
key: ${{ runner.os }}-wasm-cargo-build-${{ hashFiles('**/Cargo.toml') }}
restore-keys: |
${{ runner.os }}-wasm-cargo-build-
- name: Check (storage)
run: cargo check --target wasm32-unknown-unknown -p semaphore-rs-storage
- name: Check (trees)
run: cargo check --target wasm32-unknown-unknown -p semaphore-rs-trees
- name: Check (semaphore-rs)
run: cargo check --target wasm32-unknown-unknown -p semaphore-rs --features depth_16
# vet:
# name: Vet Dependencies
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@master
# - name: Install Rust
# uses: actions-rs/toolchain@v1
# with:
# profile: minimal
# toolchain: ${{ env.RUST_VERSION }}
# override: true
# - uses: actions-rs/cargo@v1
# with:
# command: build
# - uses: actions/cache@v3
# with:
# path: |
# ~/.cargo/registry/index/
# ~/.cargo/registry/cache/
# ~/.cargo/git/db/
# target/
# key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
# - name: Install cargo-vet
# run: cargo install cargo-vet --version ${{ env.CARGO_VET_VERSION }} --git ${{ env.CARGO_VET_REPO }}
# - name: Prune (If some import got updated)
# run: cargo vet prune
# - name: Invoke cargo-vet
# run: cargo vet
================================================
FILE: .github/workflows/release-crates.yml
================================================
name: Release Crates
on:
push:
branches:
- main
workflow_dispatch:
# Prevent concurrent release runs — release-plz is not safe to run in parallel
# against the same repo state. Keep in-progress runs; don't cancel them.
concurrency:
group: release-plz
cancel-in-progress: false
jobs:
release-crates:
name: Release Crates
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
id-token: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Run release-plz
uses: release-plz/action@1efcf74dfcd6e500990dad806e286899ae384064 # v0.5.119 (https://github.com/release-plz/action/releases/tag/v0.5.119)
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: .github/workflows/relyance-sci.yml
================================================
name: Relyance SCI Scan
on:
schedule:
- cron: "30 0 * * *"
workflow_dispatch:
permissions:
contents: read
jobs:
execute-relyance-sci:
name: Relyance SCI Job
runs-on:
group: arc-public-large-amd64-runner
permissions:
contents: read
steps:
- name: Run Relyance SCI
uses: worldcoin/gh-actions-public/relyance@main
# More information: https://github.com/worldcoin/gh-actions-public/tree/main/relyance
with:
secrets-dpp-sci-key: ${{ secrets.DPP_SCI_KEY }}
================================================
FILE: .gitignore
================================================
/target
*.profraw
snarkfiles_tmp
semaphore_files
.idea
lcov.info
*.DS_Store
Cargo.lock
================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.5.3](https://github.com/worldcoin/semaphore-rs/compare/semaphore-rs-v0.5.2...semaphore-rs-v0.5.3) - 2026-03-20
### Other
- Optimize auth proofs and restore depth test macros ([#137](https://github.com/worldcoin/semaphore-rs/pull/137))
## [0.5.2](https://github.com/worldcoin/semaphore-rs/compare/semaphore-rs-v0.5.1...semaphore-rs-v0.5.2) - 2026-03-20
### Other
- update Cargo.toml dependencies
## [0.5.1](https://github.com/worldcoin/semaphore-rs/compare/semaphore-rs-v0.5.0...semaphore-rs-v0.5.1) - 2026-03-16
### Added
- gate mmap-rs and lazy trees for WASM compatibility ([#131](https://github.com/worldcoin/semaphore-rs/pull/131))
- cascade improvements ([#130](https://github.com/worldcoin/semaphore-rs/pull/130))
- check for pushing past tree depth
- check for empty range
### Other
- satisfy clippy
================================================
FILE: Cargo.toml
================================================
[workspace]
members = ["crates/*"]
resolver = "2"
[workspace.package]
version = "0.5.3"
edition = "2021"
homepage = "https://github.com/worldcoin/semaphore-rs"
license = "MIT"
repository = "https://github.com/worldcoin/semaphore-rs"
authors = [
"Remco Bloemen <remco@worldcoin.org>",
"Philipp Sippl <philipp@worldcoin.org>",
]
description = "Rust support library for Semaphore"
keywords = ["worldcoin", "protocol", "signup"]
categories = ["cryptography"]
[workspace.dependencies]
# Internal
semaphore-rs-utils = { version = "0.5.3", path = "crates/utils" }
semaphore-rs-ark-circom = { version = "0.5.3", path = "crates/ark-circom" }
semaphore-rs-ark-zkey = { version = "0.5.3", path = "crates/ark-zkey" }
semaphore-rs-proof = { version = "0.5.3", path = "crates/proof", default-features = false }
semaphore-rs-poseidon = { version = "0.5.3", path = "crates/poseidon" }
semaphore-rs-hasher = { version = "0.5.3", path = "crates/hasher" }
semaphore-rs-keccak = { version = "0.5.3", path = "crates/keccak" }
semaphore-rs-trees = { version = "0.5.3", path = "crates/trees" }
semaphore-rs-storage = { version = "0.5.3", path = "crates/storage" }
semaphore-rs-depth-config = { version = "0.5.3", path = "crates/semaphore-depth-config" }
semaphore-rs-depth-macros = { version = "0.5.3", path = "crates/semaphore-depth-macros" }
semaphore-rs-witness = { version = "0.5.3", path = "crates/circom-witness-rs" }
# 3rd Party
alloy-core = { version = "1.0", default-features = false, features = [
"sol-types",
] }
bincode = "1.3.3"
bytemuck = "1.18"
byteorder = "1"
color-eyre = "0.6"
criterion = { version = "0.5", features = ["async_tokio", "html_reports"] }
cxx = "1"
cxx-build = "1"
derive-where = "1.6.0"
hex = "0.4.0"
hex-literal = "0.4"
itertools = "0.13"
lazy_static = "1"
mmap-rs = "0.6.1"
num-bigint = { version = "0.4", default-features = false, features = ["rand"] }
num-traits = "0.2.19"
once_cell = "1.8"
postcard = "1"
proptest = "1.0"
rand = { version = "0.8.4", features = ["small_rng"] }
rand_chacha = "0.3.1"
rayon = "1.5.1"
reqwest = { version = "0.12", default-features = false, features = [
"blocking",
"rustls-tls",
] }
ruint = { version = "1.12.3", features = [
"rand",
"bytemuck",
"serde",
"num-bigint",
"ark-ff-05",
] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.79"
serial_test = "3"
sha2 = "0.10.1"
test-case = "3.3.1"
tempfile = "3.0"
thiserror = "1.0.0"
tiny-keccak = { version = "2.0.2", features = ["sha3", "keccak"] }
tokio = "1"
tracing-test = "0.2"
zeroize = "1.6.0"
memmap2 = "0.9"
flame = "0.2"
flamer = "0.5"
# WASM
wasm-bindgen = "0.2"
js-sys = "0.3"
# Required if we're compiling to WASM
getrandom = { version = "0.2.15", features = ["js"] }
wasm-bindgen-test = "0.3"
# Proc macros
syn = { version = "2.0.9", features = ["full", "visit-mut", "extra-traits"] }
proc-macro2 = "1.0.53"
quote = "1.0.26"
# Ark
ark-bn254 = { version = "0.5.0" }
ark-ec = { version = "0.5.0", default-features = false, features = [
"parallel",
] }
ark-ff = { version = "0.5.0", default-features = false, features = [
"parallel",
"asm",
] }
ark-groth16 = { version = "0.5.0", features = ["parallel"] }
ark-relations = { version = "0.5.0", default-features = false }
ark-std = { version = "0.5.0", default-features = false, features = [
"parallel",
] }
ark-serialize = { version = "0.5.0", features = ["derive"] }
ark-poly = { version = "0.5.0" }
ark-crypto-primitives = { version = "0.5.0" }
[profile.release]
codegen-units = 1
lto = true
panic = "abort"
opt-level = 3
# Compilation profile for any non-workspace member.
# Dependencies are optimized, even in a dev build. This improves dev performance
# while having neglible impact on incremental build times.
[profile.dev.package."*"]
opt-level = 3
================================================
FILE: crates/ark-circom/Cargo.toml
================================================
[package]
name = "semaphore-rs-ark-circom"
version.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
authors.workspace = true
description.workspace = true
keywords.workspace = true
categories.workspace = true
[dependencies]
ark-ff.workspace = true
ark-relations.workspace = true
ark-serialize.workspace = true
ark-std.workspace = true
ark-bn254.workspace = true
ark-groth16.workspace = true
ark-poly.workspace = true
ark-crypto-primitives.workspace = true
ruint.workspace = true
serde_json.workspace = true
byteorder.workspace = true
num-traits.workspace = true
num-bigint.workspace = true
thiserror.workspace = true
[features]
# This features does nothing but the ark_std::cfg_into_iter expectes it
parallel = []
================================================
FILE: crates/ark-circom/src/circom.rs
================================================
use ark_ff::PrimeField;
use ark_groth16::r1cs_to_qap::{evaluate_constraint, LibsnarkReduction, R1CSToQAP};
use ark_poly::EvaluationDomain;
use ark_relations::r1cs::{ConstraintMatrices, ConstraintSystemRef, SynthesisError};
use ark_std::{cfg_into_iter, cfg_iter, cfg_iter_mut, vec};
/// Implements the witness map used by snarkjs. The arkworks witness map calculates the
/// coefficients of H through computing (AB-C)/Z in the evaluation domain and going back to the
/// coefficients domain. snarkjs instead precomputes the Lagrange form of the powers of tau bases
/// in a domain twice as large and the witness map is computed as the odd coefficients of (AB-C)
/// in that domain. This serves as HZ when computing the C proof element.
pub struct CircomReduction;
impl R1CSToQAP for CircomReduction {
#[allow(clippy::type_complexity)]
fn instance_map_with_evaluation<F: PrimeField, D: EvaluationDomain<F>>(
cs: ConstraintSystemRef<F>,
t: &F,
) -> Result<(Vec<F>, Vec<F>, Vec<F>, F, usize, usize), SynthesisError> {
LibsnarkReduction::instance_map_with_evaluation::<F, D>(cs, t)
}
fn witness_map_from_matrices<F: PrimeField, D: EvaluationDomain<F>>(
matrices: &ConstraintMatrices<F>,
num_inputs: usize,
num_constraints: usize,
full_assignment: &[F],
) -> Result<Vec<F>, SynthesisError> {
let zero = F::zero();
let domain =
D::new(num_constraints + num_inputs).ok_or(SynthesisError::PolynomialDegreeTooLarge)?;
let domain_size = domain.size();
let mut a = vec![zero; domain_size];
let mut b = vec![zero; domain_size];
cfg_iter_mut!(a[..num_constraints])
.zip(cfg_iter_mut!(b[..num_constraints]))
.zip(cfg_iter!(&matrices.a))
.zip(cfg_iter!(&matrices.b))
.for_each(|(((a, b), at_i), bt_i)| {
*a = evaluate_constraint(at_i, full_assignment);
*b = evaluate_constraint(bt_i, full_assignment);
});
{
let start = num_constraints;
let end = start + num_inputs;
a[start..end].clone_from_slice(&full_assignment[..num_inputs]);
}
let mut c = vec![zero; domain_size];
cfg_iter_mut!(c[..num_constraints])
.zip(&a)
.zip(&b)
.for_each(|((c_i, &a), &b)| {
*c_i = a * b;
});
domain.ifft_in_place(&mut a);
domain.ifft_in_place(&mut b);
let root_of_unity = {
let domain_size_double = 2 * domain_size;
let domain_double =
D::new(domain_size_double).ok_or(SynthesisError::PolynomialDegreeTooLarge)?;
domain_double.element(1)
};
D::distribute_powers_and_mul_by_const(&mut a, root_of_unity, F::one());
D::distribute_powers_and_mul_by_const(&mut b, root_of_unity, F::one());
domain.fft_in_place(&mut a);
domain.fft_in_place(&mut b);
let mut ab = domain.mul_polynomials_in_evaluation_domain(&a, &b);
drop(a);
drop(b);
domain.ifft_in_place(&mut c);
D::distribute_powers_and_mul_by_const(&mut c, root_of_unity, F::one());
domain.fft_in_place(&mut c);
cfg_iter_mut!(ab)
.zip(c)
.for_each(|(ab_i, c_i)| *ab_i -= &c_i);
Ok(ab)
}
fn h_query_scalars<F: PrimeField, D: EvaluationDomain<F>>(
max_power: usize,
t: F,
_: F,
delta_inverse: F,
) -> Result<Vec<F>, SynthesisError> {
// the usual H query has domain-1 powers. Z has domain powers. So HZ has 2*domain-1 powers.
let mut scalars = cfg_into_iter!(0..2 * max_power + 1)
.map(|i| delta_inverse * t.pow([i as u64]))
.collect::<Vec<_>>();
let domain_size = scalars.len();
let domain = D::new(domain_size).ok_or(SynthesisError::PolynomialDegreeTooLarge)?;
// generate the lagrange coefficients
domain.ifft_in_place(&mut scalars);
Ok(cfg_into_iter!(scalars).skip(1).step_by(2).collect())
}
}
================================================
FILE: crates/ark-circom/src/ethereum.rs
================================================
//! Helpers for converting Arkworks types to U256-tuples as expected by the
//! Solidity Groth16 Verifier smart contracts
use ark_ff::{BigInteger, PrimeField};
use num_traits::Zero;
use ark_bn254::{Bn254, Fq, Fq2, Fr, G1Affine, G2Affine};
use ark_serialize::CanonicalDeserialize;
use ruint::aliases::U256;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum AffineError {
#[error("point is not on curve")]
NotOnCurve,
#[error("point is not in correct subgroup")]
NotInCorrectSubgroup,
}
pub struct Inputs(pub Vec<U256>);
impl From<&[Fr]> for Inputs {
fn from(src: &[Fr]) -> Self {
let els = src.iter().map(|point| point_to_u256(*point)).collect();
Self(els)
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct G1 {
pub x: U256,
pub y: U256,
}
impl TryFrom<G1> for G1Affine {
type Error = AffineError;
fn try_from(value: G1) -> Result<Self, Self::Error> {
let x: Fq = u256_to_point(value.x);
let y: Fq = u256_to_point(value.y);
if x.is_zero() && y.is_zero() {
Ok(G1Affine::identity())
} else {
let point = G1Affine {
x,
y,
infinity: false,
};
if !point.is_on_curve() {
return Err(AffineError::NotOnCurve);
}
if !point.is_in_correct_subgroup_assuming_on_curve() {
return Err(AffineError::NotInCorrectSubgroup);
}
Ok(point)
}
}
}
type G1Tup = (U256, U256);
impl G1 {
pub fn as_tuple(&self) -> (U256, U256) {
(self.x, self.y)
}
}
impl From<&G1Affine> for G1 {
fn from(p: &G1Affine) -> Self {
Self {
x: point_to_u256(p.x),
y: point_to_u256(p.y),
}
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct G2 {
pub x: [U256; 2],
pub y: [U256; 2],
}
impl TryFrom<G2> for G2Affine {
type Error = AffineError;
fn try_from(src: G2) -> Result<G2Affine, AffineError> {
let c0 = u256_to_point(src.x[0]);
let c1 = u256_to_point(src.x[1]);
let x = Fq2::new(c0, c1);
let c0 = u256_to_point(src.y[0]);
let c1 = u256_to_point(src.y[1]);
let y = Fq2::new(c0, c1);
if x.is_zero() && y.is_zero() {
Ok(G2Affine::identity())
} else {
let point = G2Affine {
x,
y,
infinity: false,
};
if !point.is_on_curve() {
return Err(AffineError::NotOnCurve);
}
if !point.is_in_correct_subgroup_assuming_on_curve() {
return Err(AffineError::NotInCorrectSubgroup);
}
Ok(point)
}
}
}
type G2Tup = ([U256; 2], [U256; 2]);
impl G2 {
// NB: Serialize the c1 limb first.
pub fn as_tuple(&self) -> G2Tup {
([self.x[1], self.x[0]], [self.y[1], self.y[0]])
}
}
impl From<&G2Affine> for G2 {
fn from(p: &G2Affine) -> Self {
Self {
x: [point_to_u256(p.x.c0), point_to_u256(p.x.c1)],
y: [point_to_u256(p.y.c0), point_to_u256(p.y.c1)],
}
}
}
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Proof {
pub a: G1,
pub b: G2,
pub c: G1,
}
impl Proof {
pub fn as_tuple(&self) -> (G1Tup, G2Tup, G1Tup) {
(self.a.as_tuple(), self.b.as_tuple(), self.c.as_tuple())
}
}
impl From<ark_groth16::Proof<Bn254>> for Proof {
fn from(proof: ark_groth16::Proof<Bn254>) -> Self {
Self {
a: G1::from(&proof.a),
b: G2::from(&proof.b),
c: G1::from(&proof.c),
}
}
}
impl TryFrom<Proof> for ark_groth16::Proof<Bn254> {
type Error = AffineError;
fn try_from(src: Proof) -> Result<ark_groth16::Proof<Bn254>, AffineError> {
Ok(ark_groth16::Proof {
a: src.a.try_into()?,
b: src.b.try_into()?,
c: src.c.try_into()?,
})
}
}
#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct VerifyingKey {
pub alpha1: G1,
pub beta2: G2,
pub gamma2: G2,
pub delta2: G2,
pub ic: Vec<G1>,
}
impl VerifyingKey {
pub fn as_tuple(&self) -> (G1Tup, G2Tup, G2Tup, G2Tup, Vec<G1Tup>) {
(
self.alpha1.as_tuple(),
self.beta2.as_tuple(),
self.gamma2.as_tuple(),
self.delta2.as_tuple(),
self.ic.iter().map(|i| i.as_tuple()).collect(),
)
}
}
impl From<ark_groth16::VerifyingKey<Bn254>> for VerifyingKey {
fn from(vk: ark_groth16::VerifyingKey<Bn254>) -> Self {
Self {
alpha1: G1::from(&vk.alpha_g1),
beta2: G2::from(&vk.beta_g2),
gamma2: G2::from(&vk.gamma_g2),
delta2: G2::from(&vk.delta_g2),
ic: vk.gamma_abc_g1.iter().map(G1::from).collect(),
}
}
}
impl TryFrom<VerifyingKey> for ark_groth16::VerifyingKey<Bn254> {
type Error = AffineError;
fn try_from(src: VerifyingKey) -> Result<ark_groth16::VerifyingKey<Bn254>, AffineError> {
Ok(ark_groth16::VerifyingKey {
alpha_g1: src.alpha1.try_into()?,
beta_g2: src.beta2.try_into()?,
gamma_g2: src.gamma2.try_into()?,
delta_g2: src.delta2.try_into()?,
gamma_abc_g1: src
.ic
.into_iter()
.map(TryInto::try_into)
.collect::<Result<_, _>>()?,
})
}
}
// Helper for converting a PrimeField to its U256 representation for Ethereum compatibility
fn u256_to_point<F: PrimeField>(point: U256) -> F {
let buf: [u8; 32] = point.to_le_bytes();
let bigint = F::BigInt::deserialize_uncompressed(&buf[..]).expect("always works");
F::from_bigint(bigint).expect("always works")
}
// Helper for converting a PrimeField to its U256 representation for Ethereum compatibility
// (U256 reads data as big endian)
fn point_to_u256<F: PrimeField>(point: F) -> U256 {
let point = point.into_bigint();
let point_bytes = point.to_bytes_be();
U256::try_from_be_slice(&point_bytes[..]).expect("always works")
}
#[cfg(test)]
mod tests {
use super::*;
use ark_bn254::Fq;
use ark_std::UniformRand;
fn fq() -> Fq {
Fq::from(2)
}
fn fr() -> Fr {
Fr::from(2)
}
fn g1() -> G1Affine {
let rng = &mut ark_std::test_rng();
G1Affine::rand(rng)
}
fn g2() -> G2Affine {
let rng = &mut ark_std::test_rng();
G2Affine::rand(rng)
}
#[test]
fn convert_fq() {
let el = fq();
let el2 = point_to_u256(el);
let el3: Fq = u256_to_point(el2);
let el4 = point_to_u256(el3);
assert_eq!(el, el3);
assert_eq!(el2, el4);
}
#[test]
fn convert_fr() {
let el = fr();
let el2 = point_to_u256(el);
let el3: Fr = u256_to_point(el2);
let el4 = point_to_u256(el3);
assert_eq!(el, el3);
assert_eq!(el2, el4);
}
#[test]
fn convert_g1() {
let el = g1();
let el2 = G1::from(&el);
let el3: G1Affine = el2.try_into().unwrap();
let el4 = G1::from(&el3);
assert_eq!(el, el3);
assert_eq!(el2, el4);
}
#[test]
fn convert_g2() {
let el = g2();
let el2 = G2::from(&el);
let el3: G2Affine = el2.try_into().unwrap();
let el4 = G2::from(&el3);
assert_eq!(el, el3);
assert_eq!(el2, el4);
}
#[test]
fn convert_vk() {
let vk = ark_groth16::VerifyingKey::<Bn254> {
alpha_g1: g1(),
beta_g2: g2(),
gamma_g2: g2(),
delta_g2: g2(),
gamma_abc_g1: vec![g1(), g1(), g1()],
};
let vk_ethers = VerifyingKey::from(vk.clone());
let ark_vk: ark_groth16::VerifyingKey<Bn254> = vk_ethers.try_into().unwrap();
assert_eq!(ark_vk, vk);
}
#[test]
fn convert_proof() {
let p = ark_groth16::Proof::<Bn254> {
a: g1(),
b: g2(),
c: g1(),
};
let p2 = Proof::from(p.clone());
let p3 = ark_groth16::Proof::try_from(p2).unwrap();
assert_eq!(p, p3);
}
}
================================================
FILE: crates/ark-circom/src/lib.rs
================================================
pub mod circom;
pub mod ethereum;
pub mod zkey;
pub use circom::CircomReduction;
pub use zkey::read_zkey;
================================================
FILE: crates/ark-circom/src/zkey.rs
================================================
use ark_ff::{BigInteger256, PrimeField};
use ark_relations::r1cs::ConstraintMatrices;
use ark_serialize::{CanonicalDeserialize, SerializationError};
use ark_std::log2;
use byteorder::{LittleEndian, ReadBytesExt};
use std::{
collections::HashMap,
io::{Read, Seek, SeekFrom},
};
use ark_bn254::{Bn254, Fq, Fq2, Fr, G1Affine, G2Affine};
use ark_groth16::{ProvingKey, VerifyingKey};
use num_traits::Zero;
type IoResult<T> = Result<T, SerializationError>;
#[derive(Clone, Debug)]
struct Section {
position: u64,
#[allow(dead_code)]
size: usize,
}
/// Reads a SnarkJS ZKey file into an Arkworks ProvingKey.
pub fn read_zkey<R: Read + Seek>(
reader: &mut R,
) -> IoResult<(ProvingKey<Bn254>, ConstraintMatrices<Fr>)> {
let mut binfile = BinFile::new(reader)?;
let proving_key = binfile.proving_key()?;
let matrices = binfile.matrices()?;
Ok((proving_key, matrices))
}
#[derive(Debug)]
struct BinFile<'a, R> {
#[allow(dead_code)]
ftype: String,
#[allow(dead_code)]
version: u32,
sections: HashMap<u32, Vec<Section>>,
reader: &'a mut R,
}
impl<'a, R: Read + Seek> BinFile<'a, R> {
fn new(reader: &'a mut R) -> IoResult<Self> {
let mut magic = [0u8; 4];
reader.read_exact(&mut magic)?;
let version = reader.read_u32::<LittleEndian>()?;
let num_sections = reader.read_u32::<LittleEndian>()?;
let mut sections = HashMap::new();
for _ in 0..num_sections {
let section_id = reader.read_u32::<LittleEndian>()?;
let section_length = reader.read_u64::<LittleEndian>()?;
let section = sections.entry(section_id).or_insert_with(Vec::new);
section.push(Section {
position: reader.stream_position()?,
size: section_length as usize,
});
reader.seek(SeekFrom::Current(section_length as i64))?;
}
Ok(Self {
ftype: std::str::from_utf8(&magic[..]).unwrap().to_string(),
version,
sections,
reader,
})
}
fn proving_key(&mut self) -> IoResult<ProvingKey<Bn254>> {
let header = self.groth_header()?;
let ic = self.ic(header.n_public)?;
let a_query = self.a_query(header.n_vars)?;
let b_g1_query = self.b_g1_query(header.n_vars)?;
let b_g2_query = self.b_g2_query(header.n_vars)?;
let l_query = self.l_query(header.n_vars - header.n_public - 1)?;
let h_query = self.h_query(header.domain_size as usize)?;
let vk = VerifyingKey::<Bn254> {
alpha_g1: header.verifying_key.alpha_g1,
beta_g2: header.verifying_key.beta_g2,
gamma_g2: header.verifying_key.gamma_g2,
delta_g2: header.verifying_key.delta_g2,
gamma_abc_g1: ic,
};
let pk = ProvingKey::<Bn254> {
vk,
beta_g1: header.verifying_key.beta_g1,
delta_g1: header.verifying_key.delta_g1,
a_query,
b_g1_query,
b_g2_query,
h_query,
l_query,
};
Ok(pk)
}
fn get_section(&self, id: u32) -> Section {
self.sections.get(&id).unwrap()[0].clone()
}
fn groth_header(&mut self) -> IoResult<HeaderGroth> {
let section = self.get_section(2);
let header = HeaderGroth::new(&mut self.reader, §ion)?;
Ok(header)
}
fn ic(&mut self, n_public: usize) -> IoResult<Vec<G1Affine>> {
// the range is non-inclusive so we do +1 to get all inputs
self.g1_section(n_public + 1, 3)
}
/// Returns the [`ConstraintMatrices`] corresponding to the zkey
pub fn matrices(&mut self) -> IoResult<ConstraintMatrices<Fr>> {
let header = self.groth_header()?;
let section = self.get_section(4);
self.reader.seek(SeekFrom::Start(section.position))?;
let num_coeffs: u32 = self.reader.read_u32::<LittleEndian>()?;
// insantiate AB
let mut matrices = vec![vec![vec![]; header.domain_size as usize]; 2];
let mut max_constraint_index = 0;
for _ in 0..num_coeffs {
let matrix: u32 = self.reader.read_u32::<LittleEndian>()?;
let constraint: u32 = self.reader.read_u32::<LittleEndian>()?;
let signal: u32 = self.reader.read_u32::<LittleEndian>()?;
let value: Fr = deserialize_field_fr(&mut self.reader)?;
max_constraint_index = std::cmp::max(max_constraint_index, constraint);
matrices[matrix as usize][constraint as usize].push((value, signal as usize));
}
let num_constraints = max_constraint_index as usize - header.n_public;
// Remove the public input constraints, Arkworks adds them later
matrices.iter_mut().for_each(|m| {
m.truncate(num_constraints);
});
// This is taken from Arkworks' to_matrices() function
let a = matrices[0].clone();
let b = matrices[1].clone();
let a_num_non_zero: usize = a.iter().map(|lc| lc.len()).sum();
let b_num_non_zero: usize = b.iter().map(|lc| lc.len()).sum();
let matrices = ConstraintMatrices {
num_instance_variables: header.n_public + 1,
num_witness_variables: header.n_vars - header.n_public,
num_constraints,
a_num_non_zero,
b_num_non_zero,
c_num_non_zero: 0,
a,
b,
c: vec![],
};
Ok(matrices)
}
fn a_query(&mut self, n_vars: usize) -> IoResult<Vec<G1Affine>> {
self.g1_section(n_vars, 5)
}
fn b_g1_query(&mut self, n_vars: usize) -> IoResult<Vec<G1Affine>> {
self.g1_section(n_vars, 6)
}
fn b_g2_query(&mut self, n_vars: usize) -> IoResult<Vec<G2Affine>> {
self.g2_section(n_vars, 7)
}
fn l_query(&mut self, n_vars: usize) -> IoResult<Vec<G1Affine>> {
self.g1_section(n_vars, 8)
}
fn h_query(&mut self, n_vars: usize) -> IoResult<Vec<G1Affine>> {
self.g1_section(n_vars, 9)
}
fn g1_section(&mut self, num: usize, section_id: usize) -> IoResult<Vec<G1Affine>> {
let section = self.get_section(section_id as u32);
self.reader.seek(SeekFrom::Start(section.position))?;
deserialize_g1_vec(self.reader, num as u32)
}
fn g2_section(&mut self, num: usize, section_id: usize) -> IoResult<Vec<G2Affine>> {
let section = self.get_section(section_id as u32);
self.reader.seek(SeekFrom::Start(section.position))?;
deserialize_g2_vec(self.reader, num as u32)
}
}
#[derive(Default, Clone, Debug, CanonicalDeserialize)]
pub struct ZVerifyingKey {
alpha_g1: G1Affine,
beta_g1: G1Affine,
beta_g2: G2Affine,
gamma_g2: G2Affine,
delta_g1: G1Affine,
delta_g2: G2Affine,
}
impl ZVerifyingKey {
fn new<R: Read>(reader: &mut R) -> IoResult<Self> {
let alpha_g1 = deserialize_g1(reader)?;
let beta_g1 = deserialize_g1(reader)?;
let beta_g2 = deserialize_g2(reader)?;
let gamma_g2 = deserialize_g2(reader)?;
let delta_g1 = deserialize_g1(reader)?;
let delta_g2 = deserialize_g2(reader)?;
Ok(Self {
alpha_g1,
beta_g1,
beta_g2,
gamma_g2,
delta_g1,
delta_g2,
})
}
}
#[derive(Clone, Debug)]
struct HeaderGroth {
#[allow(dead_code)]
n8q: u32,
#[allow(dead_code)]
q: BigInteger256,
#[allow(dead_code)]
n8r: u32,
#[allow(dead_code)]
r: BigInteger256,
n_vars: usize,
n_public: usize,
domain_size: u32,
#[allow(dead_code)]
power: u32,
verifying_key: ZVerifyingKey,
}
impl HeaderGroth {
fn new<R: Read + Seek>(reader: &mut R, section: &Section) -> IoResult<Self> {
reader.seek(SeekFrom::Start(section.position))?;
Self::read(reader)
}
fn read<R: Read>(mut reader: &mut R) -> IoResult<Self> {
// TODO: Impl From<u32> in Arkworks
let n8q: u32 = u32::deserialize_uncompressed(&mut reader)?;
// group order r of Bn254
let q = BigInteger256::deserialize_uncompressed(&mut reader)?;
let n8r: u32 = u32::deserialize_uncompressed(&mut reader)?;
// Prime field modulus
let r = BigInteger256::deserialize_uncompressed(&mut reader)?;
let n_vars = u32::deserialize_uncompressed(&mut reader)? as usize;
let n_public = u32::deserialize_uncompressed(&mut reader)? as usize;
let domain_size: u32 = u32::deserialize_uncompressed(&mut reader)?;
let power = log2(domain_size as usize);
let verifying_key = ZVerifyingKey::new(&mut reader)?;
Ok(Self {
n8q,
q,
n8r,
r,
n_vars,
n_public,
domain_size,
power,
verifying_key,
})
}
}
// need to divide by R, since snarkjs outputs the zkey with coefficients
// multiplieid by R^2
fn deserialize_field_fr<R: Read>(reader: &mut R) -> IoResult<Fr> {
let bigint = BigInteger256::deserialize_uncompressed(reader)?;
Ok(Fr::new_unchecked(Fr::new_unchecked(bigint).into_bigint()))
}
// skips the multiplication by R because Circom points are already in Montgomery form
fn deserialize_field<R: Read>(reader: &mut R) -> IoResult<Fq> {
let bigint = BigInteger256::deserialize_uncompressed(reader)?;
// if you use Fq::new it multiplies by R
Ok(Fq::new_unchecked(bigint))
}
pub fn deserialize_field2<R: Read>(reader: &mut R) -> IoResult<Fq2> {
let c0 = deserialize_field(reader)?;
let c1 = deserialize_field(reader)?;
Ok(Fq2::new(c0, c1))
}
fn deserialize_g1<R: Read>(reader: &mut R) -> IoResult<G1Affine> {
let x = deserialize_field(reader)?;
let y = deserialize_field(reader)?;
let infinity = x.is_zero() && y.is_zero();
if infinity {
Ok(G1Affine::identity())
} else {
Ok(G1Affine::new(x, y))
}
}
fn deserialize_g2<R: Read>(reader: &mut R) -> IoResult<G2Affine> {
let f1 = deserialize_field2(reader)?;
let f2 = deserialize_field2(reader)?;
let infinity = f1.is_zero() && f2.is_zero();
if infinity {
Ok(G2Affine::identity())
} else {
Ok(G2Affine::new(f1, f2))
}
}
fn deserialize_g1_vec<R: Read>(reader: &mut R, n_vars: u32) -> IoResult<Vec<G1Affine>> {
(0..n_vars).map(|_| deserialize_g1(reader)).collect()
}
fn deserialize_g2_vec<R: Read>(reader: &mut R, n_vars: u32) -> IoResult<Vec<G2Affine>> {
(0..n_vars).map(|_| deserialize_g2(reader)).collect()
}
#[cfg(test)]
mod tests {
use super::*;
use ark_bn254::{G1Projective, G2Projective};
use num_bigint::BigUint;
use serde_json::Value;
use std::fs::File;
use num_traits::{One, Zero};
use std::str::FromStr;
use std::convert::TryFrom;
fn fq_from_str(s: &str) -> Fq {
BigInteger256::try_from(BigUint::from_str(s).unwrap())
.unwrap()
.into()
}
// Circom snarkjs code:
// console.log(curve.G1.F.one)
fn fq_buf() -> Vec<u8> {
vec![
157, 13, 143, 197, 141, 67, 93, 211, 61, 11, 199, 245, 40, 235, 120, 10, 44, 70, 121,
120, 111, 163, 110, 102, 47, 223, 7, 154, 193, 119, 10, 14,
]
}
// Circom snarkjs code:
// const buff = new Uint8Array(curve.G1.F.n8*2);
// curve.G1.toRprLEM(buff, 0, curve.G1.one);
// console.dir( buff, { 'maxArrayLength': null })
fn g1_buf() -> Vec<u8> {
vec![
157, 13, 143, 197, 141, 67, 93, 211, 61, 11, 199, 245, 40, 235, 120, 10, 44, 70, 121,
120, 111, 163, 110, 102, 47, 223, 7, 154, 193, 119, 10, 14, 58, 27, 30, 139, 27, 135,
186, 166, 123, 22, 142, 235, 81, 214, 241, 20, 88, 140, 242, 240, 222, 70, 221, 204,
94, 190, 15, 52, 131, 239, 20, 28,
]
}
// Circom snarkjs code:
// const buff = new Uint8Array(curve.G2.F.n8*2);
// curve.G2.toRprLEM(buff, 0, curve.G2.one);
// console.dir( buff, { 'maxArrayLength': null })
fn g2_buf() -> Vec<u8> {
vec![
38, 32, 188, 2, 209, 181, 131, 142, 114, 1, 123, 73, 53, 25, 235, 220, 223, 26, 129,
151, 71, 38, 184, 251, 59, 80, 150, 175, 65, 56, 87, 25, 64, 97, 76, 168, 125, 115,
180, 175, 196, 216, 2, 88, 90, 221, 67, 96, 134, 47, 160, 82, 252, 80, 233, 9, 107,
123, 234, 58, 131, 240, 254, 20, 246, 233, 107, 136, 157, 250, 157, 97, 120, 155, 158,
245, 151, 210, 127, 254, 254, 125, 27, 35, 98, 26, 158, 255, 6, 66, 158, 174, 235, 126,
253, 40, 238, 86, 24, 199, 86, 91, 9, 100, 187, 60, 125, 50, 34, 249, 87, 220, 118, 16,
53, 51, 190, 53, 249, 85, 130, 100, 253, 147, 230, 160, 164, 13,
]
}
// Circom logs in Projective coordinates: console.log(curve.G1.one)
fn g1_one() -> G1Affine {
let x = Fq::one();
let y = Fq::one() + Fq::one();
let z = Fq::one();
G1Affine::from(G1Projective::new(x, y, z))
}
// Circom logs in Projective coordinates: console.log(curve.G2.one)
fn g2_one() -> G2Affine {
let x = Fq2::new(
fq_from_str(
"10857046999023057135944570762232829481370756359578518086990519993285655852781",
),
fq_from_str(
"11559732032986387107991004021392285783925812861821192530917403151452391805634",
),
);
let y = Fq2::new(
fq_from_str(
"8495653923123431417604973247489272438418190587263600148770280649306958101930",
),
fq_from_str(
"4082367875863433681332203403145435568316851327593401208105741076214120093531",
),
);
let z = Fq2::new(Fq::one(), Fq::zero());
G2Affine::from(G2Projective::new(x, y, z))
}
#[test]
fn can_deser_fq() {
let buf = fq_buf();
let fq = deserialize_field(&mut &buf[..]).unwrap();
assert_eq!(fq, Fq::one());
}
#[test]
fn can_deser_g1() {
let buf = g1_buf();
assert_eq!(buf.len(), 64);
let g1 = deserialize_g1(&mut &buf[..]).unwrap();
let expected = g1_one();
assert_eq!(g1, expected);
}
#[test]
fn can_deser_g1_vec() {
let n_vars = 10;
let buf = vec![g1_buf(); n_vars]
.iter()
.flatten()
.cloned()
.collect::<Vec<_>>();
let expected = vec![g1_one(); n_vars];
let de = deserialize_g1_vec(&mut &buf[..], n_vars as u32).unwrap();
assert_eq!(expected, de);
}
#[test]
fn can_deser_g2() {
let buf = g2_buf();
assert_eq!(buf.len(), 128);
let g2 = deserialize_g2(&mut &buf[..]).unwrap();
let expected = g2_one();
assert_eq!(g2, expected);
}
#[test]
fn can_deser_g2_vec() {
let n_vars = 10;
let buf = vec![g2_buf(); n_vars]
.iter()
.flatten()
.cloned()
.collect::<Vec<_>>();
let expected = vec![g2_one(); n_vars];
let de = deserialize_g2_vec(&mut &buf[..], n_vars as u32).unwrap();
assert_eq!(expected, de);
}
#[test]
fn header() {
// `circom --r1cs` using the below file:
//
// template Multiplier() {
// signal private input a;
// signal private input b;
// signal output c;
//
// c <== a*b;
// }
//
// component main = Multiplier();
//
// Then:
// `snarkjs zkey new circuit.r1cs powersOfTau28_hez_final_10.ptau test.zkey`
let path = "./test-vectors/test.zkey";
let mut file = File::open(path).unwrap();
let mut binfile = BinFile::new(&mut file).unwrap();
let header = binfile.groth_header().unwrap();
assert_eq!(header.n_vars, 4);
assert_eq!(header.n_public, 1);
assert_eq!(header.domain_size, 4);
assert_eq!(header.power, 2);
}
#[test]
fn deser_key() {
let path = "./test-vectors/test.zkey";
let mut file = File::open(path).unwrap();
let (params, _matrices) = read_zkey(&mut file).unwrap();
// Check IC
let expected = vec![
deserialize_g1(
&mut &[
11, 205, 205, 176, 2, 105, 129, 243, 153, 58, 137, 89, 61, 95, 99, 161, 133,
201, 153, 192, 119, 19, 113, 136, 43, 105, 47, 206, 166, 55, 81, 22, 154, 77,
58, 119, 28, 230, 160, 206, 134, 98, 4, 115, 112, 184, 46, 117, 61, 180, 103,
138, 141, 202, 110, 252, 199, 252, 141, 211, 5, 46, 244, 10,
][..],
)
.unwrap(),
deserialize_g1(
&mut &[
118, 135, 198, 156, 63, 190, 210, 98, 194, 59, 169, 168, 204, 168, 76, 208,
109, 170, 24, 193, 57, 31, 184, 88, 234, 218, 118, 58, 107, 129, 90, 36, 230,
98, 62, 243, 3, 55, 68, 227, 117, 64, 188, 81, 81, 247, 161, 68, 68, 210, 142,
191, 174, 43, 110, 194, 253, 128, 217, 4, 54, 196, 111, 43,
][..],
)
.unwrap(),
];
assert_eq!(expected, params.vk.gamma_abc_g1);
// Check A Query
let expected = vec![
deserialize_g1(
&mut &[
240, 165, 110, 187, 72, 39, 218, 59, 128, 85, 50, 174, 229, 1, 86, 58, 125,
244, 145, 205, 248, 253, 120, 2, 165, 140, 154, 55, 220, 253, 14, 19, 212, 106,
59, 19, 125, 198, 202, 4, 59, 74, 14, 62, 20, 248, 219, 47, 234, 205, 54, 183,
33, 119, 165, 84, 46, 75, 39, 17, 229, 42, 192, 2,
][..],
)
.unwrap(),
deserialize_g1(
&mut &[
93, 53, 177, 82, 50, 5, 123, 116, 91, 35, 14, 196, 43, 180, 54, 15, 88, 144,
197, 105, 57, 167, 54, 5, 188, 109, 17, 89, 9, 223, 80, 1, 39, 193, 211, 168,
203, 119, 169, 105, 17, 156, 53, 106, 11, 102, 44, 92, 123, 220, 158, 240, 97,
253, 30, 121, 4, 236, 171, 23, 100, 34, 133, 11,
][..],
)
.unwrap(),
deserialize_g1(
&mut &[
177, 47, 21, 237, 244, 73, 76, 98, 80, 10, 10, 142, 80, 145, 40, 254, 100, 214,
103, 33, 38, 84, 238, 248, 252, 181, 75, 32, 109, 16, 93, 23, 135, 157, 206,
122, 107, 105, 202, 164, 197, 124, 242, 100, 70, 108, 9, 180, 224, 102, 250,
149, 130, 14, 133, 185, 132, 189, 193, 230, 180, 143, 156, 30,
][..],
)
.unwrap(),
deserialize_g1(
&mut &[
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,
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,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
][..],
)
.unwrap(),
];
assert_eq!(expected, params.a_query);
// B G1 Query
let expected = vec![
deserialize_g1(
&mut &[
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,
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,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
][..],
)
.unwrap(),
deserialize_g1(
&mut &[
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,
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,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
][..],
)
.unwrap(),
deserialize_g1(
&mut &[
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,
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,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
][..],
)
.unwrap(),
deserialize_g1(
&mut &[
177, 47, 21, 237, 244, 73, 76, 98, 80, 10, 10, 142, 80, 145, 40, 254, 100, 214,
103, 33, 38, 84, 238, 248, 252, 181, 75, 32, 109, 16, 93, 23, 192, 95, 174, 93,
171, 34, 86, 151, 199, 77, 127, 3, 75, 254, 119, 227, 124, 241, 134, 235, 51,
55, 203, 254, 164, 226, 111, 250, 189, 190, 199, 17,
][..],
)
.unwrap(),
];
assert_eq!(expected, params.b_g1_query);
// B G2 Query
let expected = vec![
deserialize_g2(
&mut &[
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,
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,
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,
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,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
][..],
)
.unwrap(),
deserialize_g2(
&mut &[
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,
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,
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,
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,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
][..],
)
.unwrap(),
deserialize_g2(
&mut &[
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,
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,
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,
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,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
][..],
)
.unwrap(),
deserialize_g2(
&mut &[
240, 25, 157, 232, 164, 49, 152, 204, 244, 190, 178, 178, 29, 133, 205, 175,
172, 28, 12, 123, 139, 202, 196, 13, 67, 165, 204, 42, 74, 40, 6, 36, 112, 104,
61, 67, 107, 112, 72, 41, 213, 210, 249, 75, 89, 144, 144, 34, 177, 228, 18,
70, 80, 195, 124, 82, 40, 122, 91, 21, 198, 100, 154, 1, 16, 235, 41, 4, 176,
106, 9, 113, 141, 251, 100, 233, 188, 128, 194, 173, 0, 100, 206, 110, 53, 223,
163, 47, 166, 235, 25, 12, 151, 238, 45, 0, 78, 210, 56, 53, 57, 212, 67, 189,
253, 132, 62, 62, 116, 20, 235, 15, 245, 113, 30, 182, 33, 127, 203, 231, 124,
149, 74, 223, 39, 190, 217, 41,
][..],
)
.unwrap(),
];
assert_eq!(expected, params.b_g2_query);
// Check L Query
let expected = vec![
deserialize_g1(
&mut &[
146, 142, 29, 235, 9, 162, 84, 255, 6, 119, 86, 214, 154, 18, 12, 190, 202, 19,
168, 45, 29, 76, 174, 130, 6, 59, 146, 15, 229, 82, 81, 40, 50, 25, 124, 247,
129, 12, 147, 35, 108, 119, 178, 116, 238, 145, 33, 184, 74, 201, 128, 41, 151,
6, 60, 84, 156, 225, 200, 14, 240, 171, 128, 20,
][..],
)
.unwrap(),
deserialize_g1(
&mut &[
26, 32, 112, 226, 161, 84, 188, 236, 141, 226, 119, 169, 235, 218, 253, 176,
157, 184, 108, 243, 73, 122, 239, 217, 39, 190, 239, 105, 147, 190, 80, 47,
211, 68, 155, 212, 139, 173, 229, 160, 123, 117, 243, 110, 162, 188, 217, 206,
102, 19, 36, 189, 87, 183, 113, 8, 164, 133, 43, 142, 138, 109, 66, 33,
][..],
)
.unwrap(),
];
assert_eq!(expected, params.l_query);
// Check H Query
let expected = vec![
deserialize_g1(
&mut &[
21, 76, 104, 34, 28, 236, 135, 204, 218, 16, 160, 115, 185, 44, 19, 62, 43, 24,
57, 99, 207, 105, 10, 139, 195, 60, 17, 57, 85, 244, 167, 10, 166, 166, 165,
55, 38, 75, 116, 116, 182, 87, 217, 112, 28, 237, 239, 123, 231, 180, 122, 109,
77, 116, 88, 67, 102, 48, 80, 214, 137, 47, 94, 30,
][..],
)
.unwrap(),
deserialize_g1(
&mut &[
144, 175, 205, 119, 119, 192, 11, 10, 148, 224, 87, 161, 157, 231, 101, 208,
55, 15, 13, 16, 24, 59, 9, 22, 63, 215, 255, 30, 77, 188, 71, 37, 84, 227, 59,
29, 159, 116, 101, 93, 212, 220, 159, 141, 204, 107, 131, 87, 174, 149, 175,
72, 199, 109, 64, 109, 180, 150, 160, 249, 246, 33, 212, 29,
][..],
)
.unwrap(),
deserialize_g1(
&mut &[
129, 169, 52, 179, 66, 88, 123, 199, 222, 69, 24, 17, 219, 235, 118, 195, 156,
210, 14, 21, 76, 155, 178, 210, 223, 4, 233, 5, 8, 18, 156, 24, 82, 68, 183,
186, 7, 126, 2, 201, 207, 207, 74, 45, 44, 199, 16, 165, 25, 65, 157, 199, 90,
159, 12, 150, 250, 17, 177, 193, 244, 93, 230, 41,
][..],
)
.unwrap(),
deserialize_g1(
&mut &[
207, 61, 229, 214, 21, 61, 103, 165, 93, 145, 54, 138, 143, 214, 5, 83, 183,
22, 174, 87, 108, 59, 99, 96, 19, 20, 25, 139, 114, 238, 198, 40, 182, 88, 1,
255, 206, 132, 156, 165, 178, 171, 0, 226, 179, 30, 192, 4, 79, 198, 69, 43,
145, 133, 116, 86, 36, 144, 190, 119, 79, 241, 76, 16,
][..],
)
.unwrap(),
];
assert_eq!(expected, params.h_query);
}
#[test]
fn deser_vk() {
let path = "./test-vectors/test.zkey";
let mut file = File::open(path).unwrap();
let (params, _matrices) = read_zkey(&mut file).unwrap();
let json = std::fs::read_to_string("./test-vectors/verification_key.json").unwrap();
let json: Value = serde_json::from_str(&json).unwrap();
assert_eq!(json_to_g1(&json, "vk_alpha_1"), params.vk.alpha_g1);
assert_eq!(json_to_g2(&json, "vk_beta_2"), params.vk.beta_g2);
assert_eq!(json_to_g2(&json, "vk_gamma_2"), params.vk.gamma_g2);
assert_eq!(json_to_g2(&json, "vk_delta_2"), params.vk.delta_g2);
assert_eq!(json_to_g1_vec(&json, "IC"), params.vk.gamma_abc_g1);
}
fn json_to_g1(json: &Value, key: &str) -> G1Affine {
let els: Vec<String> = json
.get(key)
.unwrap()
.as_array()
.unwrap()
.iter()
.map(|i| i.as_str().unwrap().to_string())
.collect();
G1Affine::from(G1Projective::new(
fq_from_str(&els[0]),
fq_from_str(&els[1]),
fq_from_str(&els[2]),
))
}
fn json_to_g1_vec(json: &Value, key: &str) -> Vec<G1Affine> {
let els: Vec<Vec<String>> = json
.get(key)
.unwrap()
.as_array()
.unwrap()
.iter()
.map(|i| {
i.as_array()
.unwrap()
.iter()
.map(|x| x.as_str().unwrap().to_string())
.collect::<Vec<String>>()
})
.collect();
els.iter()
.map(|coords| {
G1Affine::from(G1Projective::new(
fq_from_str(&coords[0]),
fq_from_str(&coords[1]),
fq_from_str(&coords[2]),
))
})
.collect()
}
fn json_to_g2(json: &Value, key: &str) -> G2Affine {
let els: Vec<Vec<String>> = json
.get(key)
.unwrap()
.as_array()
.unwrap()
.iter()
.map(|i| {
i.as_array()
.unwrap()
.iter()
.map(|x| x.as_str().unwrap().to_string())
.collect::<Vec<String>>()
})
.collect();
let x = Fq2::new(fq_from_str(&els[0][0]), fq_from_str(&els[0][1]));
let y = Fq2::new(fq_from_str(&els[1][0]), fq_from_str(&els[1][1]));
let z = Fq2::new(fq_from_str(&els[2][0]), fq_from_str(&els[2][1]));
G2Affine::from(G2Projective::new(x, y, z))
}
}
================================================
FILE: crates/ark-circom/test-vectors/verification_key.json
================================================
{
"protocol": "groth16",
"curve": "bn128",
"nPublic": 1,
"vk_alpha_1": [
"20491192805390485299153009773594534940189261866228447918068658471970481763042",
"9383485363053290200918347156157836566562967994039712273449902621266178545958",
"1"
],
"vk_beta_2": [
[
"6375614351688725206403948262868962793625744043794305715222011528459656738731",
"4252822878758300859123897981450591353533073413197771768651442665752259397132"
],
[
"10505242626370262277552901082094356697409835680220590971873171140371331206856",
"21847035105528745403288232691147584728191162732299865338377159692350059136679"
],
[
"1",
"0"
]
],
"vk_gamma_2": [
[
"10857046999023057135944570762232829481370756359578518086990519993285655852781",
"11559732032986387107991004021392285783925812861821192530917403151452391805634"
],
[
"8495653923123431417604973247489272438418190587263600148770280649306958101930",
"4082367875863433681332203403145435568316851327593401208105741076214120093531"
],
[
"1",
"0"
]
],
"vk_delta_2": [
[
"10857046999023057135944570762232829481370756359578518086990519993285655852781",
"11559732032986387107991004021392285783925812861821192530917403151452391805634"
],
[
"8495653923123431417604973247489272438418190587263600148770280649306958101930",
"4082367875863433681332203403145435568316851327593401208105741076214120093531"
],
[
"1",
"0"
]
],
"vk_alphabeta_12": [
[
[
"2029413683389138792403550203267699914886160938906632433982220835551125967885",
"21072700047562757817161031222997517981543347628379360635925549008442030252106"
],
[
"5940354580057074848093997050200682056184807770593307860589430076672439820312",
"12156638873931618554171829126792193045421052652279363021382169897324752428276"
],
[
"7898200236362823042373859371574133993780991612861777490112507062703164551277",
"7074218545237549455313236346927434013100842096812539264420499035217050630853"
]
],
[
[
"7077479683546002997211712695946002074877511277312570035766170199895071832130",
"10093483419865920389913245021038182291233451549023025229112148274109565435465"
],
[
"4595479056700221319381530156280926371456704509942304414423590385166031118820",
"19831328484489333784475432780421641293929726139240675179672856274388269393268"
],
[
"11934129596455521040620786944827826205713621633706285934057045369193958244500",
"8037395052364110730298837004334506829870972346962140206007064471173334027475"
]
]
],
"IC": [
[
"6819801395408938350212900248749732364821477541620635511814266536599629892365",
"9092252330033992554755034971584864587974280972948086568597554018278609861372",
"1"
],
[
"17882351432929302592725330552407222299541667716607588771282887857165175611387",
"18907419617206324833977586007131055763810739835484972981819026406579664278293",
"1"
]
]
}
================================================
FILE: crates/ark-zkey/.gitignore
================================================
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
================================================
FILE: crates/ark-zkey/Cargo.toml
================================================
[package]
name = "semaphore-rs-ark-zkey"
version.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
authors.workspace = true
description.workspace = true
keywords.workspace = true
categories.workspace = true
[dependencies]
semaphore-rs-ark-circom.workspace = true
color-eyre.workspace = true
memmap2.workspace = true
ark-serialize.workspace = true
ark-bn254.workspace = true
ark-groth16.workspace = true
ark-relations.workspace = true
ark-ff.workspace = true
ark-ec.workspace = true
================================================
FILE: crates/ark-zkey/README.md
================================================
# ark-zkey
Library to read `zkey` faster by serializing to `arkworks` friendly format.
See https://github.com/oskarth/mopro/issues/25 for context.
## To generate arkzkey
Hacky, but the way we generate `arkzkey` now is by running the corresponding test.
Note that we also neeed to change the const `ZKEY_BYTES` above.
E.g.:
```
cargo test multiplier2 --release -- --nocapture
cargo test keccak256 --release -- --nocapture
cargo test rsa --release -- --nocapture
```
Will take corresponding `zkey` and put `arkzkey`` in same folder.
## Multiplier
NOTE: Need to change const ZKEY here
`cargo test multiplier2 --release -- --nocapture`
```
running 1 test
[build] Processing zkey data...
[build] Time to process zkey data: 3.513041ms
[build] Serializing proving key and constraint matrices
[build] Time to serialize proving key and constraint matrices: 42ns
[build] Writing arkzkey to: ../mopro-core/examples/circom/multiplier2/target/multiplier2_final.arkzkey
[build] Time to write arkzkey: 1.884875ms
Reading arkzkey from: ../mopro-core/examples/circom/multiplier2/target/multiplier2_final.arkzkey
Time to open arkzkey file: 18.084µs
Time to mmap arkzkey: 8.542µs
Time to deserialize proving key: 305.75µs
Time to deserialize matrices: 5µs
Time to read arkzkey: 348.083µs
test tests::test_multiplier2_serialization_deserialization ... ok
```
Naive test: `cargo test naive --release -- --nocapture` (with right zkey constant).
**Result: `350µs` vs naive `3.3ms`**
## Keccak
NOTE: Need to change const ZKEY here
`cargo test keccak256 --release -- --nocapture`
```
[build] Processing zkey data...
test tests::test_keccak256_serialization_deserialization has been running for over 60 seconds
[build]Time to process zkey data: 158.753181958s
[build] Serializing proving key and constraint matrices
[build] Time to serialize proving key and constraint matrices: 42ns
[build] Writing arkzkey to: ../mopro-core/examples/circom/keccak256/target/keccak256_256_test_final.arkzkey
[build] Time to write arkzkey: 16.204274125s
Reading arkzkey from: ../mopro-core/examples/circom/keccak256/target/keccak256_256_test_final.arkzkey
Time to open arkzkey file: 51.75µs
Time to mmap arkzkey: 17.25µs
Time to deserialize proving key: 18.323550083s
Time to deserialize matrices: 46.935792ms
Time to read arkzkey: 18.3730695s
test tests::test_keccak256_serialization_deserialization ... ok
```
Vs naive:
`[build] Time to process zkey data: 158.753181958s`
**Result: 18s vs 158s**
================================================
FILE: crates/ark-zkey/src/lib.rs
================================================
use std::fs::File;
use std::io::BufReader;
use std::path::PathBuf;
use ark_bn254::{Bn254, Fr};
use ark_ff::Field;
use ark_groth16::ProvingKey;
use ark_relations::r1cs::ConstraintMatrices;
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use color_eyre::eyre::{Result, WrapErr};
use semaphore_rs_ark_circom::read_zkey;
#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, PartialEq)]
pub struct SerializableProvingKey(pub ProvingKey<Bn254>);
#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, PartialEq)]
pub struct SerializableMatrix<F: Field> {
pub data: Vec<Vec<(F, usize)>>,
}
#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, PartialEq)]
pub struct SerializableConstraintMatrices<F: Field> {
pub num_instance_variables: usize,
pub num_witness_variables: usize,
pub num_constraints: usize,
pub a_num_non_zero: usize,
pub b_num_non_zero: usize,
pub c_num_non_zero: usize,
pub a: SerializableMatrix<F>,
pub b: SerializableMatrix<F>,
pub c: SerializableMatrix<F>,
}
// TODO: Return ProvingKey<Bn254>, ConstraintMatrices<Fr>?
pub fn read_arkzkey_from_bytes(
arkzkey_bytes: &[u8],
) -> Result<(ProvingKey<Bn254>, ConstraintMatrices<Fr>)> {
let mut cursor = std::io::Cursor::new(arkzkey_bytes);
let serialized_proving_key =
SerializableProvingKey::deserialize_compressed_unchecked(&mut cursor)
.wrap_err("Failed to deserialize proving key")?;
let serialized_constraint_matrices =
SerializableConstraintMatrices::deserialize_compressed_unchecked(&mut cursor)
.wrap_err("Failed to deserialize constraint matrices")?;
// Get on right form for API
let proving_key: ProvingKey<Bn254> = serialized_proving_key.0;
let constraint_matrices: ConstraintMatrices<Fr> = ConstraintMatrices {
num_instance_variables: serialized_constraint_matrices.num_instance_variables,
num_witness_variables: serialized_constraint_matrices.num_witness_variables,
num_constraints: serialized_constraint_matrices.num_constraints,
a_num_non_zero: serialized_constraint_matrices.a_num_non_zero,
b_num_non_zero: serialized_constraint_matrices.b_num_non_zero,
c_num_non_zero: serialized_constraint_matrices.c_num_non_zero,
a: serialized_constraint_matrices.a.data,
b: serialized_constraint_matrices.b.data,
c: serialized_constraint_matrices.c.data,
};
Ok((proving_key, constraint_matrices))
}
pub fn read_proving_key_and_matrices_from_zkey(
zkey_path: &str,
) -> Result<(SerializableProvingKey, SerializableConstraintMatrices<Fr>)> {
let zkey_file_path = PathBuf::from(zkey_path);
let zkey_file = File::open(zkey_file_path).wrap_err("Failed to open zkey file")?;
let mut buf_reader = BufReader::new(zkey_file);
let (proving_key, matrices) =
read_zkey(&mut buf_reader).wrap_err("Failed to read zkey file")?;
let serializable_proving_key = SerializableProvingKey(proving_key);
let serializable_constrain_matrices = SerializableConstraintMatrices {
num_instance_variables: matrices.num_instance_variables,
num_witness_variables: matrices.num_witness_variables,
num_constraints: matrices.num_constraints,
a_num_non_zero: matrices.a_num_non_zero,
b_num_non_zero: matrices.b_num_non_zero,
c_num_non_zero: matrices.c_num_non_zero,
a: SerializableMatrix { data: matrices.a },
b: SerializableMatrix { data: matrices.b },
c: SerializableMatrix { data: matrices.c },
};
Ok((serializable_proving_key, serializable_constrain_matrices))
}
pub fn convert_zkey(
proving_key: SerializableProvingKey,
constraint_matrices: SerializableConstraintMatrices<Fr>,
arkzkey_path: &str,
) -> Result<()> {
let arkzkey_file_path = PathBuf::from(arkzkey_path);
let mut file = File::create(&arkzkey_file_path)
.wrap_err("Failed to create serialized proving key file")?;
proving_key
.serialize_compressed(&mut file)
.wrap_err("Failed to serialize proving key")?;
constraint_matrices
.serialize_compressed(&mut file)
.wrap_err("Failed to serialize constraint matrices")?;
Ok(())
}
#[cfg(test)]
mod tests {
use std::time::Instant;
use super::*;
#[test]
fn test_read_arkzkey_from_bytes() -> Result<()> {
const ARKZKEY_BYTES: &[u8] = include_bytes!("./semaphore.16.arkzkey");
println!("Reading arkzkey from bytes (keccak)");
let now = Instant::now();
let (_deserialized_proving_key, _deserialized_constraint_matrices) =
read_arkzkey_from_bytes(ARKZKEY_BYTES)?;
println!("Time to read arkzkey: {:?}", now.elapsed());
Ok(())
}
}
================================================
FILE: crates/circom-witness-rs/.gitignore
================================================
/target
*_cpp
*.new
.DS_Store
circuit.cc
constants.dat
.idea
================================================
FILE: crates/circom-witness-rs/Cargo.toml
================================================
[package]
name = "semaphore-rs-witness"
version.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
authors.workspace = true
description.workspace = true
keywords.workspace = true
categories.workspace = true
[dependencies]
ark-bn254 = { workspace = true, features = ["std"] }
ark-ff = { workspace = true, features = ["std"] }
ark-serialize.workspace = true
byteorder.workspace = true
color-eyre.workspace = true
hex.workspace = true
postcard = { workspace = true, features = ["use-std"] }
rand.workspace = true
ruint.workspace = true
serde.workspace = true
serde_json.workspace = true
[build-dependencies]
cxx-build.workspace = true
[features]
build-witness = []
================================================
FILE: crates/circom-witness-rs/LICENSE
================================================
MIT License
Copyright (c) 2023 Philipp Sippl
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: crates/circom-witness-rs/README.md
================================================
# 🏎️ circom-witness-rs
## Description
This 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).
`circom-witness-rs` comes with two modes:
1. Generate the static execution graph required for the witness generation at build time (`--features=build-witness`).
2. Generate the witness elements at runtime from serialized graph.
In 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.
## Usage
See this [example project](https://github.com/philsippl/semaphore-witness-example) for Semaphore with more details on building.
See `semaphore-rs` for an [example at runtime](https://github.com/worldcoin/semaphore-rs/blob/62f556bdc1a2a25021dcccc97af4dfa522ab5789/src/protocol/mod.rs#L161-L163).
All 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.
## Benchmarks
### [semaphore-rs](https://github.com/worldcoin/semaphore-rs/tree/main)
**TLDR: For semaphore circuit (depth 30) `circom-witness-rs` is ~25x faster than wasm and ~10x faster than native c++ version.**
```
cargo bench --bench=criterion --features=bench,depth_30
```
With `circom-witness-rs`:q
```
witness_30 time: [993.84 µs 996.62 µs 999.42 µs]
```
With wasm witness generator from [`circom-compat`](https://github.com/arkworks-rs/circom-compat/blob/master/src/witness/witness_calculator.rs):
```
witness_30 time: [24.630 ms 24.693 ms 24.759 ms]
```
With native c++ witness generator from circom: `9.640ms`
As 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`).
## Unimplemented features
There 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.
https://github.com/philsippl/circom-witness-rs/blob/e889cedde49a8929812b825aede55d9668118302/src/generate.rs#L61-L89
================================================
FILE: crates/circom-witness-rs/build.rs
================================================
use std::{env, fs, path::Path, process::Command};
fn main() {
if cfg!(feature = "build-witness") {
let witness_cpp = env::var("WITNESS_CPP").unwrap();
let circuit_file = Path::new(&witness_cpp);
let circuit_name = circuit_file.file_stem().unwrap().to_str().unwrap();
let status = Command::new("circom")
.args([
fs::canonicalize(circuit_file).unwrap().to_str().unwrap(),
"--c",
])
.status()
.unwrap();
assert!(status.success());
let cpp = Path::new("./")
.join(circuit_name.to_owned() + "_cpp")
.join(circuit_name.to_owned() + ".cpp");
println!("cargo:warning=\"{}\"", cpp.to_str().unwrap());
let status = Command::new("./script/replace.sh")
.arg(cpp.to_str().unwrap())
.status()
.unwrap();
assert!(status.success());
cxx_build::bridge("src/generate.rs")
.file("src/circuit.cc")
.flag_if_supported("-std=c++14")
.flag_if_supported("-w")
.flag_if_supported("-d")
.flag_if_supported("-g")
.compile("witness");
println!("cargo:rerun-if-changed=src/main.rs");
println!("cargo:rerun-if-changed=src/circuit.cc");
println!("cargo:rerun-if-changed=include/circuit.h");
}
}
================================================
FILE: crates/circom-witness-rs/include/witness.h
================================================
#pragma once
#include "rust/cxx.h"
#include <memory>
typedef unsigned long long u64;
typedef uint32_t u32;
typedef uint8_t u8;
struct Circom_CalcWit;
void run(Circom_CalcWit *buf);
uint get_size_of_io_map();
uint get_total_signal_no();
uint get_main_input_signal_no();
uint get_main_input_signal_start();
uint get_number_of_components();
uint get_size_of_constants();
uint get_size_of_input_hashmap();
uint get_size_of_witness();
================================================
FILE: crates/circom-witness-rs/script/replace.sh
================================================
#!/bin/sh
# Check for input file
if [ "$#" -ne 1 ]; then
echo "Usage: $0 <filename>"
exit 1
fi
filename=$(basename "$1" .cpp)
# Add header
cat <<EOT > "$filename.new"
#include "witness/include/witness.h"
#include "witness/src/generate.rs.h"
/// We need this accessor since cxx doesn't support hashmaps yet
class IOSignalInfoAccessor {
private:
Circom_CalcWit *calcWitContext;
public:
explicit IOSignalInfoAccessor(Circom_CalcWit *calcWit)
: calcWitContext(calcWit) {}
auto operator[](size_t index) const -> decltype(auto) {
return (calcWitContext
->templateInsId2IOSignalInfoList)[index % get_size_of_input_hashmap()];
}
};
typedef void (*Circom_TemplateFunction)(uint __cIdx, Circom_CalcWit* __ctx);
//////////////////////////////////////////////////////////////////
/// Generated code from circom compiler below
//////////////////////////////////////////////////////////////////
EOT
# Replace a few things we can't do in cxx
sed -e 's/FrElement\* signalValues/rust::Vec<FrElement> \&signalValues/g' \
-e 's/std::string/rust::string/g' \
-e 's/ctx->templateInsId2IOSignalInfo/IOSignalInfoAccessor(ctx)/g' \
-e 's/u32\* mySubcomponents/rust::Vec<u32> mySubcomponents/g' \
-e 's/FrElement\* circuitConstants/rust::Vec<FrElement> \&circuitConstants/g' \
-e 's/rust::string\* listOfTemplateMessages/rust::Vec<rust::string> \&listOfTemplateMessages/g' \
-e 's/FrElement expaux\[\([0-9]*\)\];/rust::Vec<FrElement> expaux = create_vec(\1);/g' \
-e 's/FrElement lvar\[\([0-9]*\)\];/rust::Vec<FrElement> lvar = create_vec(\1);/g' \
-e 's/FrElement lvarcall\[\([0-9]*\)\];/rust::Vec<FrElement> lvarcall = create_vec(\1);/g' \
-e 's/PFrElement aux_dest/FrElement \*aux_dest/g' \
-e 's/subcomponents = new uint\[\([0-9]*\)\];/subcomponents = create_vec_u32(\1);/g' \
-e '/trace/d' \
-e 's/\(ctx,\)\(lvarcall,\)\(myId,\)/\1\&\2\3/g' \
-e '/^#include/d' \
-e '/assert/d' \
-e '/mySubcomponentsParallel/d' \
-e 's/FrElement lvarcall\[\([0-9]*\)\];/rust::Vec<FrElement> lvarcall = create_vec(\1);/g' \
-e 's/,FrElement\* lvar,/,rust::Vec<FrElement>\& lvar,/g' \
-e 's/ctx,\&lvarcall,myId,/ctx,lvarcall,myId,/g' \
-e '/delete \[\][^;]*;/d' -e 'N;/\ndelete/!P;D' \
-e '/^#include/d' "$1" >> "$filename.new"
sed -E -e 's/"([^"]+)"\+ctx->generate_position_array\(([^)]+)\)/generate_position_array("\1", \2)/g' \
-e 's/subcomponents = new uint\[([0-9]+)\]\{0\};/subcomponents = create_vec_u32(\1);/g' \
-e 's/^uint aux_dimensions\[([0-9]+)\] = \{([^}]+)\};$/rust::Vec<uint> aux_dimensions = rust::Vec<uint32_t>{\2};/' "$filename.new" > "src/circuit.cc"
cp "$(echo $filename)_cpp/$filename.dat" src/constants.dat
================================================
FILE: crates/circom-witness-rs/src/field.rs
================================================
#![allow(unused, non_snake_case)]
use crate::graph::{Node, Operation};
use ruint::{aliases::U256, uint};
use std::{ptr, sync::Mutex};
pub const M: U256 =
uint!(21888242871839275222246405745257275088548364400416034343698204186575808495617_U256);
pub const INV: u64 = 14042775128853446655;
pub const R: U256 = uint!(0x0e0a77c19a07df2f666ea36f7879462e36fc76959f60cd29ac96341c4ffffffb_U256);
static NODES: Mutex<Vec<Node>> = Mutex::new(Vec::new());
static VALUES: Mutex<Vec<U256>> = Mutex::new(Vec::new());
static CONSTANT: Mutex<Vec<bool>> = Mutex::new(Vec::new());
#[derive(Debug, Default, Clone, Copy)]
pub struct FrElement(pub usize);
pub fn print_eval() {
let nodes = NODES.lock().unwrap();
let values = VALUES.lock().unwrap();
let constant = CONSTANT.lock().unwrap();
let mut constants = 0_usize;
for (i, node) in nodes.iter().enumerate() {
print!("{}: {:?}", i, node);
if constant[i] {
constants += 1;
println!(" = {}", values[i]);
} else {
println!();
}
}
eprintln!(
"{} nodes of which {} constant and {} dynamic",
nodes.len(),
constants,
nodes.len() - constants
);
}
pub fn get_graph() -> Vec<Node> {
NODES.lock().unwrap().clone()
}
pub fn get_values() -> Vec<U256> {
VALUES.lock().unwrap().clone()
}
pub fn undefined() -> FrElement {
FrElement(usize::MAX)
}
pub fn constant(c: U256) -> FrElement {
let mut nodes = NODES.lock().unwrap();
let mut values = VALUES.lock().unwrap();
let mut constant = CONSTANT.lock().unwrap();
assert_eq!(nodes.len(), values.len());
assert_eq!(nodes.len(), constant.len());
nodes.push(Node::Constant(c));
values.push(c);
constant.push(true);
FrElement(nodes.len() - 1)
}
pub fn input(i: usize, value: U256) -> FrElement {
let mut nodes = NODES.lock().unwrap();
let mut values = VALUES.lock().unwrap();
let mut constant = CONSTANT.lock().unwrap();
assert_eq!(nodes.len(), values.len());
assert_eq!(nodes.len(), constant.len());
nodes.push(Node::Input(i));
values.push(value);
constant.push(false);
FrElement(nodes.len() - 1)
}
fn binop(op: Operation, to: *mut FrElement, a: *const FrElement, b: *const FrElement) {
let mut nodes = NODES.lock().unwrap();
let mut values = VALUES.lock().unwrap();
let mut constant = CONSTANT.lock().unwrap();
assert_eq!(nodes.len(), values.len());
assert_eq!(nodes.len(), constant.len());
let (a, b, to) = unsafe { ((*a).0, (*b).0, &mut (*to).0) };
assert!(a < nodes.len());
assert!(b < nodes.len());
nodes.push(Node::Op(op, a, b));
*to = nodes.len() - 1;
let (va, vb) = (values[a], values[b]);
values.push(op.eval(va, vb));
let (ca, cb) = (constant[a], constant[b]);
constant.push(ca && cb);
}
pub fn Fr_mul(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {
binop(Operation::Mul, to, a, b);
}
#[allow(warnings)]
pub unsafe fn Fr_add(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {
binop(Operation::Add, to, a, b);
}
#[allow(warnings)]
pub unsafe fn Fr_sub(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {
binop(Operation::Sub, to, a, b);
}
#[allow(warnings)]
pub fn Fr_copy(to: *mut FrElement, a: *const FrElement) {
unsafe {
*to = *a;
}
}
#[allow(warnings)]
pub fn Fr_copyn(to: *mut FrElement, a: *const FrElement, n: usize) {
unsafe {
ptr::copy_nonoverlapping(a, to, n);
}
}
/// Create a vector of FrElement with length `len`.
/// Needed because the default constructor of opaque type is not implemented.
pub fn create_vec(len: usize) -> Vec<FrElement> {
vec![FrElement(usize::MAX); len]
}
pub fn create_vec_u32(len: usize) -> Vec<u32> {
vec![0; len]
}
pub fn generate_position_array(
prefix: String,
dimensions: Vec<u32>,
size_dimensions: u32,
index: u32,
) -> String {
let mut positions: String = prefix;
let mut index = index;
for i in 0..size_dimensions {
let last_pos = index % dimensions[size_dimensions as usize - 1 - i as usize];
index /= dimensions[size_dimensions as usize - 1 - i as usize];
let new_pos = format!("[{}]", last_pos);
positions = new_pos + &positions;
}
positions
}
pub unsafe fn Fr_toInt(a: *const FrElement) -> u64 {
let nodes = NODES.lock().unwrap();
let values = VALUES.lock().unwrap();
let constant = CONSTANT.lock().unwrap();
assert_eq!(nodes.len(), values.len());
assert_eq!(nodes.len(), constant.len());
let a = unsafe { (*a).0 };
assert!(a < nodes.len());
assert!(constant[a]);
values[a].try_into().unwrap()
}
pub unsafe fn print(a: *const FrElement) {
println!("DEBUG>> {:?}", (*a).0);
}
pub fn Fr_isTrue(a: *mut FrElement) -> bool {
let nodes = NODES.lock().unwrap();
let values = VALUES.lock().unwrap();
let constant = CONSTANT.lock().unwrap();
assert_eq!(nodes.len(), values.len());
assert_eq!(nodes.len(), constant.len());
let a = unsafe { (*a).0 };
assert!(a < nodes.len());
assert!(constant[a]);
values[a] != U256::ZERO
}
pub unsafe fn Fr_eq(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {
binop(Operation::Eq, to, a, b);
}
pub unsafe fn Fr_neq(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {
binop(Operation::Neq, to, a, b);
}
pub unsafe fn Fr_lt(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {
binop(Operation::Lt, to, a, b);
}
pub unsafe fn Fr_gt(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {
binop(Operation::Gt, to, a, b);
}
pub unsafe fn Fr_leq(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {
binop(Operation::Leq, to, a, b);
}
pub unsafe fn Fr_geq(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {
binop(Operation::Geq, to, a, b);
}
pub unsafe fn Fr_lor(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {
binop(Operation::Lor, to, a, b);
}
pub unsafe fn Fr_shl(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {
binop(Operation::Shl, to, a, b);
}
pub unsafe fn Fr_shr(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {
binop(Operation::Shr, to, a, b);
}
pub unsafe fn Fr_band(to: *mut FrElement, a: *const FrElement, b: *const FrElement) {
binop(Operation::Band, to, a, b);
}
================================================
FILE: crates/circom-witness-rs/src/generate.rs
================================================
#![allow(non_snake_case)]
use crate::field::{self, *};
use crate::graph::{self, Node};
use crate::HashSignalInfo;
use byteorder::{LittleEndian, ReadBytesExt};
use ffi::InputOutputList;
use ruint::{aliases::U256, uint};
use serde::{Deserialize, Serialize};
use std::{io::Read, time::Instant};
#[cxx::bridge]
mod ffi {
#[derive(Debug, Default, Clone)]
pub struct InputOutputList {
pub defs: Vec<IODef>,
}
#[derive(Debug, Clone, Default)]
pub struct IODef {
pub code: usize,
pub offset: usize,
pub lengths: Vec<usize>,
}
#[derive(Debug, Default, Clone)]
struct Circom_Component {
templateId: u64,
signalStart: u64,
inputCounter: u64,
templateName: String,
componentName: String,
idFather: u64,
subcomponents: Vec<u32>,
outputIsSet: Vec<bool>,
}
#[derive(Debug)]
struct Circom_CalcWit {
signalValues: Vec<FrElement>,
componentMemory: Vec<Circom_Component>,
circuitConstants: Vec<FrElement>,
templateInsId2IOSignalInfoList: Vec<InputOutputList>,
listOfTemplateMessages: Vec<String>,
}
// Rust types and signatures exposed to C++.
extern "Rust" {
type FrElement;
fn create_vec(len: usize) -> Vec<FrElement>;
fn create_vec_u32(len: usize) -> Vec<u32>;
fn generate_position_array(
prefix: String,
dimensions: Vec<u32>,
size_dimensions: u32,
index: u32,
) -> String;
// Field operations
unsafe fn Fr_mul(to: *mut FrElement, a: *const FrElement, b: *const FrElement);
unsafe fn Fr_add(to: *mut FrElement, a: *const FrElement, b: *const FrElement);
unsafe fn Fr_sub(to: *mut FrElement, a: *const FrElement, b: *const FrElement);
unsafe fn Fr_copy(to: *mut FrElement, a: *const FrElement);
unsafe fn Fr_copyn(to: *mut FrElement, a: *const FrElement, n: usize);
// unsafe fn Fr_neg(to: *mut FrElement, a: *const FrElement);
// unsafe fn Fr_inv(to: *mut FrElement, a: *const FrElement);
// unsafe fn Fr_div(to: *mut FrElement, a: *const FrElement, b: *const FrElement);
// unsafe fn Fr_square(to: *mut FrElement, a: *const FrElement);
unsafe fn Fr_shl(to: *mut FrElement, a: *const FrElement, b: *const FrElement);
unsafe fn Fr_shr(to: *mut FrElement, a: *const FrElement, b: *const FrElement);
unsafe fn Fr_band(to: *mut FrElement, a: *const FrElement, b: *const FrElement);
// fn Fr_bor(to: &mut FrElement, a: &FrElement, b: &FrElement);
// fn Fr_bxor(to: &mut FrElement, a: &FrElement, b: &FrElement);
// fn Fr_bnot(to: &mut FrElement, a: &FrElement);
unsafe fn Fr_eq(to: *mut FrElement, a: *const FrElement, b: *const FrElement);
unsafe fn Fr_neq(to: *mut FrElement, a: *const FrElement, b: *const FrElement);
unsafe fn Fr_lt(to: *mut FrElement, a: *const FrElement, b: *const FrElement);
unsafe fn Fr_gt(to: *mut FrElement, a: *const FrElement, b: *const FrElement);
unsafe fn Fr_leq(to: *mut FrElement, a: *const FrElement, b: *const FrElement);
unsafe fn Fr_geq(to: *mut FrElement, a: *const FrElement, b: *const FrElement);
unsafe fn Fr_isTrue(a: *mut FrElement) -> bool;
// fn Fr_fromBool(to: &mut FrElement, a: bool);
unsafe fn Fr_toInt(a: *mut FrElement) -> u64;
unsafe fn Fr_lor(to: *mut FrElement, a: *const FrElement, b: *const FrElement);
unsafe fn print(a: *mut FrElement);
// fn Fr_pow(to: &mut FrElement, a: &FrElement, b: &FrElement);
// fn Fr_idiv(to: &mut FrElement, a: &FrElement, b: &FrElement);
}
// C++ types and signatures exposed to Rust.
unsafe extern "C++" {
include!("witness/include/witness.h");
unsafe fn run(ctx: *mut Circom_CalcWit);
fn get_size_of_io_map() -> u32;
fn get_total_signal_no() -> u32;
fn get_main_input_signal_no() -> u32;
fn get_main_input_signal_start() -> u32;
fn get_number_of_components() -> u32;
fn get_size_of_constants() -> u32;
fn get_size_of_input_hashmap() -> u32;
fn get_size_of_witness() -> u32;
}
}
const DAT_BYTES: &[u8] = include_bytes!("constants.dat");
pub fn get_input_hash_map() -> Vec<HashSignalInfo> {
let mut bytes = &DAT_BYTES[..(ffi::get_size_of_input_hashmap() as usize) * 24];
let mut input_hash_map =
vec![HashSignalInfo::default(); ffi::get_size_of_input_hashmap() as usize];
for i in 0..ffi::get_size_of_input_hashmap() as usize {
let hash = bytes.read_u64::<LittleEndian>().unwrap();
let signalid = bytes.read_u64::<LittleEndian>().unwrap();
let signalsize = bytes.read_u64::<LittleEndian>().unwrap();
input_hash_map[i] = HashSignalInfo {
hash,
signalid,
signalsize,
};
}
input_hash_map
}
pub fn get_witness_to_signal() -> Vec<usize> {
let mut bytes = &DAT_BYTES[(ffi::get_size_of_input_hashmap() as usize) * 24
..(ffi::get_size_of_input_hashmap() as usize) * 24
+ (ffi::get_size_of_witness() as usize) * 8];
let mut signal_list = Vec::with_capacity(ffi::get_size_of_witness() as usize);
for i in 0..ffi::get_size_of_witness() as usize {
signal_list.push(bytes.read_u64::<LittleEndian>().unwrap() as usize);
}
signal_list
}
pub fn get_constants() -> Vec<FrElement> {
if ffi::get_size_of_constants() == 0 {
return vec![];
}
// skip the first part
let mut bytes = &DAT_BYTES[(ffi::get_size_of_input_hashmap() as usize) * 24
+ (ffi::get_size_of_witness() as usize) * 8..];
let mut constants = vec![field::constant(U256::from(0)); ffi::get_size_of_constants() as usize];
for i in 0..ffi::get_size_of_constants() as usize {
let sv = bytes.read_i32::<LittleEndian>().unwrap() as i32;
let typ = bytes.read_u32::<LittleEndian>().unwrap() as u32;
let mut buf = [0; 32];
bytes.read_exact(&mut buf);
if typ & 0x80000000 == 0 {
constants[i] = field::constant(U256::from(sv));
} else {
constants[i] =
field::constant(U256::from_le_bytes(buf).mul_redc(uint!(1_U256), M, INV));
}
}
return constants;
}
pub fn get_iosignals() -> Vec<InputOutputList> {
if ffi::get_size_of_io_map() == 0 {
return vec![];
}
// skip the first part
let mut bytes = &DAT_BYTES[(ffi::get_size_of_input_hashmap() as usize) * 24
+ (ffi::get_size_of_witness() as usize) * 8
+ (ffi::get_size_of_constants() as usize * 40)..];
let io_size = ffi::get_size_of_io_map() as usize;
let hashmap_size = ffi::get_size_of_input_hashmap() as usize;
let mut indices = vec![0usize; io_size];
let mut map: Vec<InputOutputList> = vec![InputOutputList::default(); hashmap_size];
(0..io_size).for_each(|i| {
let t32 = bytes.read_u32::<LittleEndian>().unwrap() as usize;
indices[i] = t32;
});
(0..io_size).for_each(|i| {
let l32 = bytes.read_u32::<LittleEndian>().unwrap() as usize;
let mut io_list: InputOutputList = InputOutputList { defs: vec![] };
(0..l32).for_each(|_j| {
let offset = bytes.read_u32::<LittleEndian>().unwrap() as usize;
let len = bytes.read_u32::<LittleEndian>().unwrap() as usize + 1;
let mut lengths = vec![0usize; len];
(1..len).for_each(|k| {
lengths[k] = bytes.read_u32::<LittleEndian>().unwrap() as usize;
});
io_list.defs.push(ffi::IODef {
code: 0,
offset,
lengths,
});
});
map[indices[i] % hashmap_size] = io_list;
});
map
}
/// Run cpp witness generator and optimize graph
pub fn build_witness() -> color_color_eyre::Result<()> {
let mut signal_values = vec![field::undefined(); ffi::get_total_signal_no() as usize];
signal_values[0] = field::constant(uint!(1_U256));
let total_input_len =
(ffi::get_main_input_signal_no() + ffi::get_main_input_signal_start()) as usize;
for i in 0..total_input_len {
signal_values[i + 1] = field::input(i + 1, uint!(0_U256));
}
let mut ctx = ffi::Circom_CalcWit {
signalValues: signal_values,
componentMemory: vec![
ffi::Circom_Component::default();
ffi::get_number_of_components() as usize
],
circuitConstants: get_constants(),
templateInsId2IOSignalInfoList: get_iosignals(),
listOfTemplateMessages: vec![],
};
// measure time
let now = Instant::now();
unsafe {
ffi::run(&mut ctx as *mut _);
}
eprintln!("Calculation took: {:?}", now.elapsed());
let signal_values = get_witness_to_signal();
let mut signals = signal_values
.into_iter()
.map(|i| ctx.signalValues[i].0)
.collect::<Vec<_>>();
let mut nodes = field::get_graph();
eprintln!("Graph with {} nodes", nodes.len());
// Optimize graph
graph::optimize(&mut nodes, &mut signals);
// Store graph to file.
let input_map = get_input_hash_map();
let bytes = postcard::to_stdvec(&(&nodes, &signals, &input_map)).unwrap();
eprintln!("Graph size: {} bytes", bytes.len());
std::fs::write("graph.bin", bytes).unwrap();
// Evaluate the graph.
let input_len = (ffi::get_main_input_signal_no() + ffi::get_main_input_signal_start()) as usize; // TODO: fetch from file
let mut inputs = vec![U256::from(0); input_len];
inputs[0] = U256::from(1);
for i in 1..nodes.len() {
if let Node::Input(j) = nodes[i] {
inputs[j] = get_values()[i];
} else {
break;
}
}
let now = Instant::now();
for _ in 0..10 {
_ = graph::evaluate(&nodes, &inputs, &signals);
}
eprintln!("Calculation took: {:?}", now.elapsed() / 10);
// Print graph
// for (i, node) in nodes.iter().enumerate() {
// println!("node[{}] = {:?}", i, node);
// }
// for (i, j) in signals.iter().enumerate() {
// println!("signal[{}] = node[{}]", i, j);
// }
Ok(())
}
================================================
FILE: crates/circom-witness-rs/src/graph.rs
================================================
use std::{
collections::HashMap,
ops::{Shl, Shr},
};
use crate::field::M;
use ark_bn254::Fr;
use ark_ff::PrimeField;
use rand::Rng;
use ruint::aliases::U256;
use serde::{Deserialize, Serialize};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Compress, Validate};
fn ark_se<S, A: CanonicalSerialize>(a: &A, s: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut bytes = vec![];
a.serialize_with_mode(&mut bytes, Compress::Yes)
.map_err(serde::ser::Error::custom)?;
s.serialize_bytes(&bytes)
}
fn ark_de<'de, D, A: CanonicalDeserialize>(data: D) -> Result<A, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let s: Vec<u8> = serde::de::Deserialize::deserialize(data)?;
let a = A::deserialize_with_mode(s.as_slice(), Compress::Yes, Validate::Yes);
a.map_err(serde::de::Error::custom)
}
#[derive(Hash, PartialEq, Eq, Debug, Clone, Copy, Serialize, Deserialize)]
pub enum Operation {
Mul,
MMul,
Add,
Sub,
Eq,
Neq,
Lt,
Gt,
Leq,
Geq,
Lor,
Shl,
Shr,
Band,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Node {
Input(usize),
Constant(U256),
#[serde(serialize_with = "ark_se", deserialize_with = "ark_de")]
MontConstant(Fr),
Op(Operation, usize, usize),
}
impl Operation {
pub fn eval(&self, a: U256, b: U256) -> U256 {
use Operation::*;
match self {
Add => a.add_mod(b, M),
Sub => a.add_mod(M - b, M),
Mul => a.mul_mod(b, M),
Eq => U256::from(a == b),
Neq => U256::from(a != b),
Lt => U256::from(a < b),
Gt => U256::from(a > b),
Leq => U256::from(a <= b),
Geq => U256::from(a >= b),
Lor => U256::from(a != U256::ZERO || b != U256::ZERO),
Shl => compute_shl_uint(a, b),
Shr => compute_shr_uint(a, b),
Band => a.bitand(b),
_ => unimplemented!("operator {:?} not implemented", self),
}
}
pub fn eval_fr(&self, a: Fr, b: Fr) -> Fr {
use Operation::*;
match self {
Add => a + b,
Sub => a - b,
Mul => a * b,
_ => unimplemented!("operator {:?} not implemented for Montgomery", self),
}
}
}
fn compute_shl_uint(a: U256, b: U256) -> U256 {
debug_assert!(b.lt(&U256::from(256)));
let ls_limb = b.as_limbs()[0];
a.shl(ls_limb as usize)
}
fn compute_shr_uint(a: U256, b: U256) -> U256 {
debug_assert!(b.lt(&U256::from(256)));
let ls_limb = b.as_limbs()[0];
a.shr(ls_limb as usize)
}
/// All references must be backwards.
fn assert_valid(nodes: &[Node]) {
for (i, &node) in nodes.iter().enumerate() {
if let Node::Op(_, a, b) = node {
assert!(a < i);
assert!(b < i);
}
}
}
pub fn optimize(nodes: &mut Vec<Node>, outputs: &mut [usize]) {
tree_shake(nodes, outputs);
propagate(nodes);
value_numbering(nodes, outputs);
constants(nodes);
tree_shake(nodes, outputs);
montgomery_form(nodes);
}
#[allow(clippy::unnecessary_fallible_conversions)] // Prevents the false positive on line 143
pub fn evaluate(nodes: &[Node], inputs: &[U256], outputs: &[usize]) -> Vec<U256> {
// assert_valid(nodes);
// Evaluate the graph.
let mut values = Vec::with_capacity(nodes.len());
for &node in nodes.iter() {
let value = match node {
Node::Constant(c) => Fr::new(c.into()),
Node::MontConstant(c) => c,
Node::Input(i) => Fr::new(inputs[i].into()),
Node::Op(op, a, b) => op.eval_fr(values[a], values[b]),
};
values.push(value);
}
// Convert from Montgomery form and return the outputs.
let mut out = vec![U256::ZERO; outputs.len()];
for i in 0..outputs.len() {
out[i] = U256::try_from(values[outputs[i]].into_bigint()).unwrap();
}
out
}
/// Constant propagation
pub fn propagate(nodes: &mut [Node]) {
assert_valid(nodes);
let mut constants = 0_usize;
for i in 0..nodes.len() {
if let Node::Op(op, a, b) = nodes[i] {
if let (Node::Constant(va), Node::Constant(vb)) = (nodes[a], nodes[b]) {
nodes[i] = Node::Constant(op.eval(va, vb));
constants += 1;
} else if a == b {
// Not constant but equal
use Operation::*;
if let Some(c) = match op {
Eq | Leq | Geq => Some(true),
Neq | Lt | Gt => Some(false),
_ => None,
} {
nodes[i] = Node::Constant(U256::from(c));
constants += 1;
}
}
}
}
eprintln!("Propagated {constants} constants");
}
/// Remove unused nodes
pub fn tree_shake(nodes: &mut Vec<Node>, outputs: &mut [usize]) {
assert_valid(nodes);
// Mark all nodes that are used.
let mut used = vec![false; nodes.len()];
for &i in outputs.iter() {
used[i] = true;
}
// Work backwards from end as all references are backwards.
for i in (0..nodes.len()).rev() {
if used[i] {
if let Node::Op(_, a, b) = nodes[i] {
used[a] = true;
used[b] = true;
}
}
}
// Remove unused nodes
let n = nodes.len();
let mut retain = used.iter();
nodes.retain(|_| *retain.next().unwrap());
let removed = n - nodes.len();
// Renumber references.
let mut renumber = vec![None; n];
let mut index = 0;
for (i, &used) in used.iter().enumerate() {
if used {
renumber[i] = Some(index);
index += 1;
}
}
assert_eq!(index, nodes.len());
for (&used, renumber) in used.iter().zip(renumber.iter()) {
assert_eq!(used, renumber.is_some());
}
// Renumber references.
for node in nodes.iter_mut() {
if let Node::Op(_, a, b) = node {
*a = renumber[*a].unwrap();
*b = renumber[*b].unwrap();
}
}
for output in outputs.iter_mut() {
*output = renumber[*output].unwrap();
}
eprintln!("Removed {removed} unused nodes");
}
/// Randomly evaluate the graph
fn random_eval(nodes: &mut [Node]) -> Vec<U256> {
let mut rng = rand::thread_rng();
let mut values = Vec::with_capacity(nodes.len());
let mut inputs = HashMap::new();
let mut prfs = HashMap::new();
for node in nodes.iter() {
use Operation::*;
let value = match node {
// Constants evaluate to themselves
Node::Constant(c) => *c,
Node::MontConstant(_c) => unimplemented!("should not be used"),
// Algebraic Ops are evaluated directly
// Since the field is large, by Swartz-Zippel if
// two values are the same then they are likely algebraically equal.
Node::Op(op @ (Add | Sub | Mul), a, b) => op.eval(values[*a], values[*b]),
// Input and non-algebraic ops are random functions
// TODO: https://github.com/recmo/uint/issues/95 and use .gen_range(..M)
Node::Input(i) => *inputs.entry(*i).or_insert_with(|| rng.gen::<U256>() % M),
Node::Op(op, a, b) => *prfs
.entry((*op, values[*a], values[*b]))
.or_insert_with(|| rng.gen::<U256>() % M),
};
values.push(value);
}
values
}
/// Value numbering
pub fn value_numbering(nodes: &mut [Node], outputs: &mut [usize]) {
assert_valid(nodes);
// Evaluate the graph in random field elements.
let values = random_eval(nodes);
// Find all nodes with the same value.
let mut value_map = HashMap::new();
for (i, &value) in values.iter().enumerate() {
value_map.entry(value).or_insert_with(Vec::new).push(i);
}
// For nodes that are the same, pick the first index.
let mut renumber = Vec::with_capacity(nodes.len());
for value in values {
renumber.push(value_map[&value][0]);
}
// Renumber references.
for node in nodes.iter_mut() {
if let Node::Op(_, a, b) = node {
*a = renumber[*a];
*b = renumber[*b];
}
}
for output in outputs.iter_mut() {
*output = renumber[*output];
}
eprintln!("Global value numbering applied");
}
/// Probabilistic constant determination
pub fn constants(nodes: &mut [Node]) {
assert_valid(nodes);
// Evaluate the graph in random field elements.
let values_a = random_eval(nodes);
let values_b = random_eval(nodes);
// Find all nodes with the same value.
let mut constants = 0;
for i in 0..nodes.len() {
if let Node::Constant(_) = nodes[i] {
continue;
}
if values_a[i] == values_b[i] {
nodes[i] = Node::Constant(values_a[i]);
constants += 1;
}
}
eprintln!("Found {} constants", constants);
}
/// Convert to Montgomery form
pub fn montgomery_form(nodes: &mut [Node]) {
for node in nodes.iter_mut() {
use Node::*;
use Operation::*;
match node {
Constant(c) => *node = MontConstant(Fr::new((*c).into())),
MontConstant(..) => (),
Input(..) => (),
Op(Add | Sub | Mul, ..) => (),
Op(..) => unimplemented!("Operators Montgomery form"),
}
}
eprintln!("Converted to Montgomery form");
}
================================================
FILE: crates/circom-witness-rs/src/lib.rs
================================================
mod field;
pub mod graph;
#[cfg(feature = "build-witness")]
pub mod generate;
use std::collections::HashMap;
use ruint::aliases::U256;
use serde::{Deserialize, Serialize};
use crate::graph::Node;
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct HashSignalInfo {
pub hash: u64,
pub signalid: u64,
pub signalsize: u64,
}
pub struct Graph {
pub nodes: Vec<Node>,
pub signals: Vec<usize>,
pub input_mapping: Vec<HashSignalInfo>,
}
fn fnv1a(s: &str) -> u64 {
let mut hash: u64 = 0xCBF29CE484222325;
for c in s.bytes() {
hash ^= c as u64;
hash = hash.wrapping_mul(0x100000001B3);
}
hash
}
/// Loads the graph from bytes
pub fn init_graph(graph_bytes: &[u8]) -> color_eyre::Result<Graph> {
let (nodes, signals, input_mapping): (Vec<Node>, Vec<usize>, Vec<HashSignalInfo>) =
postcard::from_bytes(graph_bytes)?;
Ok(Graph {
nodes,
signals,
input_mapping,
})
}
/// Calculates the number of needed inputs
pub fn get_inputs_size(graph: &Graph) -> usize {
let mut start = false;
let mut max_index = 0usize;
for &node in graph.nodes.iter() {
if let Node::Input(i) = node {
if i > max_index {
max_index = i;
}
start = true
} else if start {
break;
}
}
max_index + 1
}
/// Allocates inputs vec with position 0 set to 1
pub fn get_inputs_buffer(size: usize) -> Vec<U256> {
let mut inputs = vec![U256::ZERO; size];
inputs[0] = U256::from(1);
inputs
}
/// Calculates the position of the given signal in the inputs buffer
pub fn get_input_mapping(input_list: &Vec<String>, graph: &Graph) -> HashMap<String, usize> {
let mut input_mapping = HashMap::new();
for key in input_list {
let h = fnv1a(key);
let pos = graph
.input_mapping
.iter()
.position(|x| x.hash == h)
.unwrap();
let si = (graph.input_mapping[pos].signalid) as usize;
input_mapping.insert(key.to_string(), si);
}
input_mapping
}
/// Sets all provided inputs given the mapping and inputs buffer
pub fn populate_inputs(
input_list: &HashMap<String, Vec<U256>>,
input_mapping: &HashMap<String, usize>,
input_buffer: &mut [U256],
) {
for (key, value) in input_list {
let start = input_mapping[key];
let end = start + value.len();
input_buffer[start..end].copy_from_slice(value);
}
}
/// Calculate witness based on serialized graph and inputs
pub fn calculate_witness(
input_list: HashMap<String, Vec<U256>>,
graph: &Graph,
) -> color_eyre::Result<Vec<U256>> {
let mut inputs_buffer = get_inputs_buffer(get_inputs_size(graph));
let input_mapping = get_input_mapping(&input_list.keys().cloned().collect(), graph);
populate_inputs(&input_list, &input_mapping, &mut inputs_buffer);
Ok(graph::evaluate(
&graph.nodes,
&inputs_buffer,
&graph.signals,
))
}
================================================
FILE: crates/hasher/Cargo.toml
================================================
[package]
name = "semaphore-rs-hasher"
version.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
authors.workspace = true
description.workspace = true
keywords.workspace = true
categories.workspace = true
[dependencies]
bytemuck.workspace = true
================================================
FILE: crates/hasher/src/lib.rs
================================================
use bytemuck::Pod;
/// Hash types, values and algorithms for a Merkle tree
pub trait Hasher {
/// Type of the leaf and node hashes
type Hash;
/// Compute the hash of an intermediate node
fn hash_node(left: &Self::Hash, right: &Self::Hash) -> Self::Hash;
}
/// A marker trait that indicates some useful properties of a hash type
///
/// It's not strictly necessary, but for many implementations it's a useful set of constraints
pub trait Hash: Pod + Eq + Send + Sync {}
impl<T> Hash for T where T: Pod + Eq + Send + Sync {}
================================================
FILE: crates/js/.gitignore
================================================
/target
**/*.rs.bk
Cargo.lock
bin/
pkg/
pkg-nodejs/
pkg-bundler/
pkg-web/
wasm-pack.log
================================================
FILE: crates/js/Cargo.toml
================================================
[package]
name = "semaphore-rs-js"
version = "0.3.0"
edition.workspace = true
publish = false # independently versioned; not part of the workspace release cycle
homepage.workspace = true
license.workspace = true
repository.workspace = true
authors.workspace = true
description.workspace = true
keywords.workspace = true
categories.workspace = true
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
semaphore-rs-proof = { workspace = true, default-features = false }
ruint.workspace = true
hex.workspace = true
wasm-bindgen.workspace = true
js-sys.workspace = true
getrandom.workspace = true
[dev-dependencies]
wasm-bindgen-test.workspace = true
================================================
FILE: crates/js/README.md
================================================
# WASM bindings for semaphore-rs related functionality
This crate exposes semaphore-rs functionality to WASM. Currently it only exposes proof compression.
## Building & publishing
wasm-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.
The `build_and_publish.sh` script handles all of that.
To 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.
To only check the build output run `DRY_RUN=1 ./build_and_publish.sh`.
## Example
Refer to `example/index.mjs` or `example/index.ts` for usage
================================================
FILE: crates/js/build.mjs
================================================
#!/usr/bin/env node
// build.mjs
// This script performs the following steps:
// 1. Uses `cargo metadata` to extract package metadata.
// 2. Constructs a detailed package.json using that metadata.
// 3. Invokes `cargo build --target wasm32-unknown-unknown` for the local package.
// 4. Locates the wasm artifact (by searching upward for the target directory),
// and runs wasm-bindgen (with --target web, --omit-imports, --omit-default-module-path)
// outputting the JS bindings into a pkg directory.
// 5. Generates an inline loader script that embeds the wasm as a base64 string.
// 6. Publishes the package (unless the `--dry-run` CLI argument is present).
import fs from 'fs';
import path from 'path';
import { execSync } from 'child_process';
const isDryRun = process.argv.includes('--dry-run');
async function main() {
// 1. Extract package metadata using cargo metadata.
console.log("Extracting package metadata via cargo metadata...");
const metadataJson = execSync('cargo metadata --no-deps --format-version 1', { encoding: 'utf-8' });
const metadata = JSON.parse(metadataJson);
const manifestPath = path.resolve('Cargo.toml');
const pkgMeta = metadata.packages.find(pkg => path.resolve(pkg.manifest_path) === manifestPath);
if (!pkgMeta) {
throw new Error("Could not find package metadata for the current Cargo.toml");
}
const pkgName = pkgMeta.name;
const pkgVersion = pkgMeta.version;
const pkgDescription = pkgMeta.description || "";
const pkgLicense = pkgMeta.license || "";
const pkgHomepage = pkgMeta.homepage || "";
const pkgRepository = pkgMeta.repository || "";
const pkgKeywords = pkgMeta.keywords || [];
// We'll use authors as collaborators.
const pkgCollaborators = pkgMeta.authors || [];
// Convert crate name to snake_case for file naming (dashes become underscores).
const pkgBaseName = pkgName.replace(/-/g, '_');
// wasm-bindgen will output files named like `<pkgBaseName>.js` and `<pkgBaseName>_bg.wasm`
const wasmBindgenJs = `${pkgBaseName}.js`;
const wasmBindgenWasm = `${pkgBaseName}_bg.wasm`;
const wasmBindgenDts = `${pkgBaseName}.d.ts`;
// 2. Build the Rust crate for the wasm target.
console.log("Building the Rust project with cargo...");
execSync('cargo build --target wasm32-unknown-unknown', { stdio: 'inherit' });
// 3. Locate the target directory by searching upward from the current directory.
let targetDir = null;
let currentDir = process.cwd();
while (currentDir !== path.parse(currentDir).root) {
const potentialTarget = path.join(currentDir, 'target');
if (fs.existsSync(potentialTarget)) {
targetDir = potentialTarget;
break;
}
currentDir = path.dirname(currentDir);
}
if (!targetDir) {
throw new Error("Could not locate the target directory");
}
// Assume a debug build; the wasm artifact should be at:
// target/wasm32-unknown-unknown/debug/<pkgBaseName>.wasm
const wasmArtifactPath = path.join(targetDir, 'wasm32-unknown-unknown', 'debug', `${pkgBaseName}.wasm`);
if (!fs.existsSync(wasmArtifactPath)) {
throw new Error(`Wasm artifact not found at ${wasmArtifactPath}`);
}
// 4. Run wasm-bindgen on the artifact.
console.log("Running wasm-bindgen...");
const pkgDir = path.resolve('pkg');
if (!fs.existsSync(pkgDir)) {
fs.mkdirSync(pkgDir);
}
const wasmBindgenCmd = `wasm-bindgen ${wasmArtifactPath} --out-dir ${pkgDir} --target web --omit-imports --omit-default-module-path`;
execSync(wasmBindgenCmd, { stdio: 'inherit' });
// 5. Construct the inline loader.
const wasmOutputPath = path.join(pkgDir, wasmBindgenWasm);
if (!fs.existsSync(wasmOutputPath)) {
throw new Error(`Wasm file not found in pkg directory: ${wasmOutputPath}`);
}
const wasmBuffer = fs.readFileSync(wasmOutputPath);
const wasmBase64 = wasmBuffer.toString('base64');
const inlineLoaderContent = `
// This file is auto-generated by build.mjs.
// It inlines the wasm module as a base64 string and loads it synchronously.
import { initSync } from './${wasmBindgenJs}';
const base64Wasm = "${wasmBase64}";
// Convert a base64 string to a Uint8Array.
function base64ToUint8Array(base64) {
if (typeof atob === 'function') {
const binaryString = atob(base64);
const len = binaryString.length;
const bytes = new Uint8Array(len);
for (let i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes;
} else if (typeof Buffer === 'function') {
return new Uint8Array(Buffer.from(base64, 'base64'));
} else {
throw new Error('No base64 decoder available');
}
}
const wasmBytes = base64ToUint8Array(base64Wasm);
// Initialize the generated bindings with the inlined wasm instance.
initSync({ module: wasmBytes });
export * from './${wasmBindgenJs}';
`.trim();
const inlineLoaderPath = path.join(pkgDir, 'index.js');
fs.writeFileSync(inlineLoaderPath, inlineLoaderContent);
console.log(`Generated inline loader: ${inlineLoaderPath}`);
// 6. Construct a fleshed-out package.json.
const packageJson = {
name: pkgName,
type: "module",
collaborators: pkgCollaborators,
description: pkgDescription,
version: pkgVersion,
license: pkgLicense,
repository: pkgRepository ? { type: "git", url: pkgRepository } : undefined,
files: [
// With inlined wasm, ship the loader and generated JS bindings.
"index.js",
wasmBindgenJs,
wasmBindgenDts
],
main: "index.js",
homepage: pkgHomepage,
types: wasmBindgenDts,
sideEffects: [
"./snippets/*"
],
keywords: pkgKeywords
};
// Remove any keys that are undefined.
Object.keys(packageJson).forEach(key => {
if (packageJson[key] === undefined) {
delete packageJson[key];
}
});
const pkgJsonPath = path.join(pkgDir, 'package.json');
fs.writeFileSync(pkgJsonPath, JSON.stringify(packageJson, null, 2));
console.log(`Updated package.json: ${pkgJsonPath}`);
// 7. Publish the package unless --dry-run is provided.
if (!isDryRun) {
console.log("Publishing package...");
execSync('npm publish', { stdio: 'inherit', cwd: pkgDir });
} else {
console.log("--dry-run flag present, skipping npm publish.");
}
console.log("Build complete: Wasm module inlined and package.json regenerated.");
}
main().catch(err => {
console.error(err);
process.exit(1);
});
================================================
FILE: crates/js/example/.gitignore
================================================
node_modules/
================================================
FILE: crates/js/example/index.mjs
================================================
import { compressProof, decompressProof } from "semaphore-rs-js";
const proof = [
"0x2d77679b613036865f4518894c80691cf65338fe7834fe3dd5f98c4f0f5a9e6d",
"0x24018e845edf74d69528a63eed053296a397df13a1d08873e2b2d673837b31c3",
"0x099d39b2cbca524b5916ac97dbc4afc1b8a5f59d65ba583fc49ec2677226e926",
"0x0da5812d7b4e0beb22d25c194431674396aec70751873edb9ac8c933ba1f0f2e",
"0x0723caca23efb9aa44db59ead0eeb28c2efb9c766d9a3f994ed047179e37b347",
"0x02166d9fc2d4cf446b120e5663880e0927825aa36a02b896ac0f3a5ef6e0239b",
"0x287fb1d0415a734ba76df9eb50ca6758bb806272f8fe40e3adbad3a850c05167",
"0x1240cf8aa43cf4ea4a2d8dffac653a6467cefd0f19e129cffad85299d6705444",
];
const compressed = compressProof(proof);
const decompressed = decompressProof(compressed);
for (let i = 0; i < 8; i++) {
if (proof[i] !== decompressed[i]) {
console.log("Proof not equal after decompression");
}
}
================================================
FILE: crates/js/example/index.ts
================================================
import { compressProof, decompressProof } from "semaphore-rs-js";
const proof: [string, string, string, string, string, string, string, string] =
[
"0x2d77679b613036865f4518894c80691cf65338fe7834fe3dd5f98c4f0f5a9e6d",
"0x24018e845edf74d69528a63eed053296a397df13a1d08873e2b2d673837b31c3",
"0x099d39b2cbca524b5916ac97dbc4afc1b8a5f59d65ba583fc49ec2677226e926",
"0x0da5812d7b4e0beb22d25c194431674396aec70751873edb9ac8c933ba1f0f2e",
"0x0723caca23efb9aa44db59ead0eeb28c2efb9c766d9a3f994ed047179e37b347",
"0x02166d9fc2d4cf446b120e5663880e0927825aa36a02b896ac0f3a5ef6e0239b",
"0x287fb1d0415a734ba76df9eb50ca6758bb806272f8fe40e3adbad3a850c05167",
"0x1240cf8aa43cf4ea4a2d8dffac653a6467cefd0f19e129cffad85299d6705444",
];
const compressed = compressProof(proof);
const decompressed = decompressProof(compressed);
for (let i = 0; i < 8; i++) {
if (proof[i] !== decompressed[i]) {
console.log("Proof not equal after decompression");
}
}
================================================
FILE: crates/js/example/package.json
================================================
{
"name": "testing",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"semaphore-rs-js": "0.3.0"
}
}
================================================
FILE: crates/js/src/lib.rs
================================================
use js_sys::Array;
use ruint::aliases::U256;
use semaphore_rs_proof::{compression::CompressedProof, Proof};
use wasm_bindgen::prelude::*;
/// Compresses a Groth16 proof
#[wasm_bindgen(
js_name = "compressProof",
unchecked_return_type = "[string, string, string, string]",
return_description = "An array of 4 0x prefixed, hex encoded strings representing a compressed proof"
)]
pub fn compress_proof(
#[wasm_bindgen(
unchecked_param_type = "[string, string, string, string, string, string, string, string]",
param_description = "An array of 8 hex encoded strings (with optional 0x prefixes) that represent an uncompressed proof"
)]
proof: Array,
) -> Result<Array, JsError> {
let proof: Vec<String> = proof
.iter()
.map(|v| v.as_string().unwrap_or_default())
.collect();
let proof = from_vec(proof)?;
let proof = Proof::from_flat(proof);
let proof = semaphore_rs_proof::compression::compress_proof(proof)
.ok_or_else(|| JsError::new("Failed to compress proof"))?
.flatten();
Ok(to_js_array(proof))
}
/// Decompresses a Groth16 proof
#[wasm_bindgen(
js_name = "decompressProof",
unchecked_return_type = "[string, string, string, string, string, string, string, string]",
return_description = "An array of 8 0x prefixed, hex encoded strings representing an uncompressed proof"
)]
pub fn decompress_proof(
#[wasm_bindgen(
js_name = "compressedProof",
unchecked_param_type = "[string, string, string, string]",
param_description = "An array of 4 hex encoded strings (with optional 0x prefixes) that represent a compressed proof"
)]
compressed_proof: Array,
) -> Result<Array, JsError> {
let compressed_proof: Vec<String> = compressed_proof
.iter()
.map(|v| v.as_string().unwrap_or_default())
.collect();
let proof = from_vec(compressed_proof)?;
let proof = CompressedProof::from_flat(proof);
let proof = semaphore_rs_proof::compression::decompress_proof(proof)
.ok_or_else(|| JsError::new("Failed to decompress proof"))?;
let proof = proof.flatten();
Ok(to_js_array(proof))
}
fn from_vec<const N: usize>(proof: Vec<String>) -> Result<[U256; N], JsError> {
if proof.len() != N {
return Err(JsError::new(&format!("Proof length must be {N}")));
}
let proof: Vec<U256> = proof
.into_iter()
.map(|s| {
U256::from_str_radix(s.trim_start_matches("0x"), 16)
.map_err(|err| JsError::new(&err.to_string()))
})
.collect::<Result<_, _>>()?;
let proof: [U256; N] = proof.try_into().unwrap();
Ok(proof)
}
fn to_js_array<const N: usize>(arr: [U256; N]) -> Array {
let js_array = Array::new();
arr.iter().take(N).for_each(|v| {
js_array.push(&JsValue::from_str(&format!("{:#066x}", v)));
});
js_array
}
================================================
FILE: crates/keccak/Cargo.toml
================================================
[package]
name = "semaphore-rs-keccak"
version.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
authors.workspace = true
description.workspace = true
keywords.workspace = true
categories.workspace = true
[dependencies]
semaphore-rs-hasher.workspace = true
tiny-keccak = { workspace = true, features = ["keccak"] }
[features]
default = ["sha3"]
sha3 = ["tiny-keccak/sha3"]
================================================
FILE: crates/keccak/src/keccak.rs
================================================
use semaphore_rs_hasher::Hasher;
use tiny_keccak::{Hasher as _, Keccak};
pub struct Keccak256;
impl Hasher for Keccak256 {
type Hash = [u8; 32];
fn hash_node(left: &Self::Hash, right: &Self::Hash) -> Self::Hash {
let mut keccak = Keccak::v256();
let mut output = [0; 32];
keccak.update(left);
keccak.update(right);
keccak.finalize(&mut output);
output
}
}
================================================
FILE: crates/keccak/src/lib.rs
================================================
pub mod keccak;
#[cfg(feature = "sha3")]
pub mod sha3;
================================================
FILE: crates/keccak/src/sha3.rs
================================================
use semaphore_rs_hasher::Hasher;
use tiny_keccak::{Hasher as _, Sha3};
pub struct Sha3_256;
impl Hasher for Sha3_256 {
type Hash = [u8; 32];
fn hash_node(left: &Self::Hash, right: &Self::Hash) -> Self::Hash {
let mut sha3_hasher = Sha3::v256();
sha3_hasher.update(left);
sha3_hasher.update(right);
let mut out = [0u8; 32];
sha3_hasher.finalize(&mut out);
out
}
}
================================================
FILE: crates/poseidon/Cargo.toml
================================================
[package]
name = "semaphore-rs-poseidon"
version.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
authors.workspace = true
description.workspace = true
keywords.workspace = true
categories.workspace = true
[dependencies]
semaphore-rs-hasher.workspace = true
ark-bn254.workspace = true
ark-ff.workspace = true
once_cell.workspace = true
ruint.workspace = true
================================================
FILE: crates/poseidon/src/constants.rs
================================================
use ruint::aliases::U256;
use ruint::uint;
uint! {
pub const M1: [[U256; 2]; 2] = [
[
0x066f6f85d6f68a85ec10345351a23a3aaf07f38af8c952a7bceca70bd2af7ad5_U256,
0x2b9d4b4110c9ae997782e1509b1d0fdb20a7c02bbd8bea7305462b9f8125b1e8_U256,
], [
0x0cc57cdbb08507d62bf67a4493cc262fb6c09d557013fff1f573f431221f8ff9_U256,
0x1274e649a32ed355a31a6ed69724e1adade857e86eb5c3a121bcd147943203c8_U256,
],
];
pub const C1: [[U256; 2]; 64] = [
[
0x09c46e9ec68e9bd4fe1faaba294cba38a71aa177534cdd1b6c7dc0dbd0abd7a7_U256,
0x0c0356530896eec42a97ed937f3135cfc5142b3ae405b8343c1d83ffa604cb81_U256,
], [
0x1e28a1d935698ad1142e51182bb54cf4a00ea5aabd6268bd317ea977cc154a30_U256,
0x27af2d831a9d2748080965db30e298e40e5757c3e008db964cf9e2b12b91251f_U256,
], [
0x1e6f11ce60fc8f513a6a3cfe16ae175a41291462f214cd0879aaf43545b74e03_U256,
0x2a67384d3bbd5e438541819cb681f0be04462ed14c3613d8f719206268d142d3_U256,
], [
0x0b66fdf356093a611609f8e12fbfecf0b985e381f025188936408f5d5c9f45d0_U256,
0x012ee3ec1e78d470830c61093c2ade370b26c83cc5cebeeddaa6852dbdb09e21_U256,
], [
0x0252ba5f6760bfbdfd88f67f8175e3fd6cd1c431b099b6bb2d108e7b445bb1b9_U256,
0x179474cceca5ff676c6bec3cef54296354391a8935ff71d6ef5aeaad7ca932f1_U256,
], [
0x2c24261379a51bfa9228ff4a503fd4ed9c1f974a264969b37e1a2589bbed2b91_U256,
0x1cc1d7b62692e63eac2f288bd0695b43c2f63f5001fc0fc553e66c0551801b05_U256,
], [
0x255059301aada98bb2ed55f852979e9600784dbf17fbacd05d9eff5fd9c91b56_U256,
0x28437be3ac1cb2e479e1f5c0eccd32b3aea24234970a8193b11c29ce7e59efd9_U256,
], [
0x28216a442f2e1f711ca4fa6b53766eb118548da8fb4f78d4338762c37f5f2043_U256,
0x2c1f47cd17fa5adf1f39f4e7056dd03feee1efce03094581131f2377323482c9_U256,
], [
0x07abad02b7a5ebc48632bcc9356ceb7dd9dafca276638a63646b8566a621afc9_U256,
0x0230264601ffdf29275b33ffaab51dfe9429f90880a69cd137da0c4d15f96c3c_U256,
], [
0x1bc973054e51d905a0f168656497ca40a864414557ee289e717e5d66899aa0a9_U256,
0x2e1c22f964435008206c3157e86341edd249aff5c2d8421f2a6b22288f0a67fc_U256,
], [
0x1224f38df67c5378121c1d5f461bbc509e8ea1598e46c9f7a70452bc2bba86b8_U256,
0x02e4e69d8ba59e519280b4bd9ed0068fd7bfe8cd9dfeda1969d2989186cde20e_U256,
], [
0x1f1eccc34aaba0137f5df81fc04ff3ee4f19ee364e653f076d47e9735d98018e_U256,
0x1672ad3d709a353974266c3039a9a7311424448032cd1819eacb8a4d4284f582_U256,
], [
0x283e3fdc2c6e420c56f44af5192b4ae9cda6961f284d24991d2ed602df8c8fc7_U256,
0x1c2a3d120c550ecfd0db0957170fa013683751f8fdff59d6614fbd69ff394bcc_U256,
], [
0x216f84877aac6172f7897a7323456efe143a9a43773ea6f296cb6b8177653fbd_U256,
0x2c0d272becf2a75764ba7e8e3e28d12bceaa47ea61ca59a411a1f51552f94788_U256,
], [
0x16e34299865c0e28484ee7a74c454e9f170a5480abe0508fcb4a6c3d89546f43_U256,
0x175ceba599e96f5b375a232a6fb9cc71772047765802290f48cd939755488fc5_U256,
], [
0x0c7594440dc48c16fead9e1758b028066aa410bfbc354f54d8c5ffbb44a1ee32_U256,
0x1a3c29bc39f21bb5c466db7d7eb6fd8f760e20013ccf912c92479882d919fd8d_U256,
], [
0x0ccfdd906f3426e5c0986ea049b253400855d349074f5a6695c8eeabcd22e68f_U256,
0x14f6bc81d9f186f62bdb475ce6c9411866a7a8a3fd065b3ce0e699b67dd9e796_U256,
], [
0x0962b82789fb3d129702ca70b2f6c5aacc099810c9c495c888edeb7386b97052_U256,
0x1a880af7074d18b3bf20c79de25127bc13284ab01ef02575afef0c8f6a31a86d_U256,
], [
0x10cba18419a6a332cd5e77f0211c154b20af2924fc20ff3f4c3012bb7ae9311b_U256,
0x057e62a9a8f89b3ebdc76ba63a9eaca8fa27b7319cae3406756a2849f302f10d_U256,
], [
0x287c971de91dc0abd44adf5384b4988cb961303bbf65cff5afa0413b44280cee_U256,
0x21df3388af1687bbb3bca9da0cca908f1e562bc46d4aba4e6f7f7960e306891d_U256,
], [
0x1be5c887d25bce703e25cc974d0934cd789df8f70b498fd83eff8b560e1682b3_U256,
0x268da36f76e568fb68117175cea2cd0dd2cb5d42fda5acea48d59c2706a0d5c1_U256,
], [
0x0e17ab091f6eae50c609beaf5510ececc5d8bb74135ebd05bd06460cc26a5ed6_U256,
0x04d727e728ffa0a67aee535ab074a43091ef62d8cf83d270040f5caa1f62af40_U256,
], [
0x0ddbd7bf9c29341581b549762bc022ed33702ac10f1bfd862b15417d7e39ca6e_U256,
0x2790eb3351621752768162e82989c6c234f5b0d1d3af9b588a29c49c8789654b_U256,
], [
0x1e457c601a63b73e4471950193d8a570395f3d9ab8b2fd0984b764206142f9e9_U256,
0x21ae64301dca9625638d6ab2bbe7135ffa90ecd0c43ff91fc4c686fc46e091b0_U256,
], [
0x0379f63c8ce3468d4da293166f494928854be9e3432e09555858534eed8d350b_U256,
0x002d56420359d0266a744a080809e054ca0e4921a46686ac8c9f58a324c35049_U256,
], [
0x123158e5965b5d9b1d68b3cd32e10bbeda8d62459e21f4090fc2c5af963515a6_U256,
0x0be29fc40847a941661d14bbf6cbe0420fbb2b6f52836d4e60c80eb49cad9ec1_U256,
], [
0x1ac96991dec2bb0557716142015a453c36db9d859cad5f9a233802f24fdf4c1a_U256,
0x1596443f763dbcc25f4964fc61d23b3e5e12c9fa97f18a9251ca3355bcb0627e_U256,
], [
0x12e0bcd3654bdfa76b2861d4ec3aeae0f1857d9f17e715aed6d049eae3ba3212_U256,
0x0fc92b4f1bbea82b9ea73d4af9af2a50ceabac7f37154b1904e6c76c7cf964ba_U256,
], [
0x1f9c0b1610446442d6f2e592a8013f40b14f7c7722236f4f9c7e965233872762_U256,
0x0ebd74244ae72675f8cde06157a782f4050d914da38b4c058d159f643dbbf4d3_U256,
], [
0x2cb7f0ed39e16e9f69a9fafd4ab951c03b0671e97346ee397a839839dccfc6d1_U256,
0x1a9d6e2ecff022cc5605443ee41bab20ce761d0514ce526690c72bca7352d9bf_U256,
], [
0x2a115439607f335a5ea83c3bc44a9331d0c13326a9a7ba3087da182d648ec72f_U256,
0x23f9b6529b5d040d15b8fa7aee3e3410e738b56305cd44f29535c115c5a4c060_U256,
], [
0x05872c16db0f72a2249ac6ba484bb9c3a3ce97c16d58b68b260eb939f0e6e8a7_U256,
0x1300bdee08bb7824ca20fb80118075f40219b6151d55b5c52b624a7cdeddf6a7_U256,
], [
0x19b9b63d2f108e17e63817863a8f6c288d7ad29916d98cb1072e4e7b7d52b376_U256,
0x015bee1357e3c015b5bda237668522f613d1c88726b5ec4224a20128481b4f7f_U256,
], [
0x2953736e94bb6b9f1b9707a4f1615e4efe1e1ce4bab218cbea92c785b128ffd1_U256,
0x0b069353ba091618862f806180c0385f851b98d372b45f544ce7266ed6608dfc_U256,
], [
0x304f74d461ccc13115e4e0bcfb93817e55aeb7eb9306b64e4f588ac97d81f429_U256,
0x15bbf146ce9bca09e8a33f5e77dfe4f5aad2a164a4617a4cb8ee5415cde913fc_U256,
], [
0x0ab4dfe0c2742cde44901031487964ed9b8f4b850405c10ca9ff23859572c8c6_U256,
0x0e32db320a044e3197f45f7649a19675ef5eedfea546dea9251de39f9639779a_U256,
], [
0x0a1756aa1f378ca4b27635a78b6888e66797733a82774896a3078efa516da016_U256,
0x044c4a33b10f693447fd17177f952ef895e61d328f85efa94254d6a2a25d93ef_U256,
], [
0x2ed3611b725b8a70be655b537f66f700fe0879d79a496891d37b07b5466c4b8b_U256,
0x1f9ba4e8bab7ce42c8ecc3d722aa2e0eadfdeb9cfdd347b5d8339ea7120858aa_U256,
], [
0x1b233043052e8c288f7ee907a84e518aa38e82ac4502066db74056f865c5d3da_U256,
0x2431e1cc164bb8d074031ab72bd55b4c902053bfc0f14db0ca2f97b020875954_U256,
], [
0x082f934c91f5aac330cd6953a0a7db45a13e322097583319a791f273965801fd_U256,
0x2b9a0a223e7538b0a34be074315542a3c77245e2ae7cbe999ad6bb930c48997c_U256,
], [
0x0e1cd91edd2cfa2cceb85483b887a9be8164163e75a8a00eb0b589cc70214e7d_U256,
0x2e1eac0f2bfdfd63c951f61477e3698999774f19854d00f588d324601cebe2f9_U256,
], [
0x0cbfa95f37fb74060c76158e769d6d157345784d8efdb33c23d748115b500b83_U256,
0x08f05b3be923ed44d65ad49d8a61e9a676d991e3a77513d9980c232dfa4a4f84_U256,
], [
0x22719e2a070bcd0852bf8e21984d0443e7284925dc0758a325a2dd510c047ef6_U256,
0x041f596a9ee1cb2bc060f7fcc3a1ab4c7bdbf036119982c0f41f62b2f26830c0_U256,
], [
0x233fd35de1be520a87628eb06f6b1d4c021be1c2d0dc464a19fcdd0986b10f89_U256,
0x0524b46d1aa87a5e4325e0a423ebc810d31e078aa1b4707eefcb453c61c9c267_U256,
], [
0x2c34f424c81e5716ce47fcac894b85824227bb954b0f3199cc4486237c515211_U256,
0x0b5f2a4b63387819207effc2b5541fb72dd2025b5457cc97f33010327de4915e_U256,
], [
0x22207856082ccc54c5b72fe439d2cfd6c17435d2f57af6ceaefac41fe05c659f_U256,
0x24d57a8bf5da63fe4e24159b7f8950b5cdfb210194caf79f27854048ce2c8171_U256,
], [
0x0afab181fdd5e0583b371d75bd693f98374ad7097bb01a8573919bb23b79396e_U256,
0x2dba9b108f208772998a52efac7cbd5676c0057194c16c0bf16290d62b1128ee_U256,
], [
0x26349b66edb8b16f56f881c788f53f83cbb83de0bd592b255aff13e6bce420b3_U256,
0x25af7ce0e5e10357685e95f92339753ad81a56d28ecc193b235288a3e6f137db_U256,
], [
0x25b4ce7bd2294390c094d6a55edd68b970eed7aae88b2bff1f7c0187fe35011f_U256,
0x22c543f10f6c89ec387e53f1908a88e5de9cef28ebdf30b18cb9d54c1e02b631_U256,
], [
0x0236f93e7789c4724fc7908a9f191e1e425e906a919d7a34df668e74882f87a9_U256,
0x29350b401166ca010e7d27e37d05da99652bdae114eb01659cb497af980c4b52_U256,
], [
0x0eed787d65820d3f6bd31bbab547f75a65edb75d844ebb89ee1260916652363f_U256,
0x07cc1170f13b46f2036a753f520b3291fdcd0e99bd94297d1906f656f4de6fad_U256,
], [
0x22b939233b1d7205f49bcf613a3d30b1908786d7f9f5d10c2059435689e8acea_U256,
0x01451762a0aab81c8aad1dc8bc33e870740f083a5aa85438add650ace60ae5a6_U256,
], [
0x23506bb5d8727d4461fabf1025d46d1fe32eaa61dec7da57e704fec0892fce89_U256,
0x2e484c44e838aea0bac06ae3f71bdd092a3709531e1efea97f8bd68907355522_U256,
], [
0x0f4bc7d07ebafd64379e78c50bd2e42baf4a594545cedc2545418da26835b54c_U256,
0x1f4d3c8f6583e9e5fa76637862faaee851582388725df460e620996d50d8e74e_U256,
], [
0x093514e0c70711f82660d07be0e4a988fae02abc7b681d9153eb9bcb48fe7389_U256,
0x1adab0c8e2b3bad346699a2b5f3bc03643ee83ece47228f24a58e0a347e153d8_U256,
], [
0x1672b1726057d99dd14709ebb474641a378c1b94b8072bac1a22dbef9e80dad2_U256,
0x1dfd53d4576af2e38f44f53fdcab468cc5d8e2fae0acc4ee30d47b239b479c14_U256,
], [
0x0c6888a10b75b0f3a70a36263a37e17fe6d77d640f6fc3debc7f207753205c60_U256,
0x1addb933a65be77092b34a7e77d12fe8611a61e00ee6848b85091ecca9d1e508_U256,
], [
0x00d7540dcd268a845c10ae18d1de933cf638ff5425f0afff7935628e299d1791_U256,
0x140c0e42687e9ead01b2827a5664ca9c26fedde4acd99db1d316939d20b82c0e_U256,
], [
0x2f0c3a115d4317d191ba89b8d13d1806c20a0f9b24f8c5edc091e2ae56565984_U256,
0x0c4ee778ff7c14553006ed220cf9c81008a0cff670b22b82d8c538a1dc958c61_U256,
], [
0x1704f2766d46f82c3693f00440ccc3609424ed26c0acc66227c3d7485de74c69_U256,
0x2f2d19cc3ea5d78ea7a02c1b51d244abf0769c9f8544e40239b66fe9009c3cfa_U256,
], [
0x1ae03853b75fcaba5053f112e2a8e8dcdd7ee6cb9cfed9c7d6c766a806fc6629_U256,
0x0971aabf795241df51d131d0fa61aa5f3556921b2d6f014e4e41a86ddaf056d5_U256,
], [
0x1408c316e6014e1a91d4cf6b6e0de73eda624f8380df1c875f5c29f7bfe2f646_U256,
0x1667f3fe2edbe850248abe42b543093b6c89f1f773ef285341691f39822ef5bd_U256,
], [
0x13bf7c5d0d2c4376a48b0a03557cdf915b81718409e5c133424c69576500fe37_U256,
0x07620a6dfb0b6cec3016adf3d3533c24024b95347856b79719bc0ba743a62c2c_U256,
], [
0x1574c7ef0c43545f36a8ca08bdbdd8b075d2959e2f322b731675de3e1982b4d0_U256,
0x269e4b5b7a2eb21afd567970a717ceec5bd4184571c254fdc06e03a7ff8378f0_U256,
],
];
pub const M: [[U256; 3]; 3] = [
[
0x109b7f411ba0e4c9b2b70caf5c36a7b194be7c11ad24378bfedb68592ba8118b_U256,
0x16ed41e13bb9c0c66ae119424fddbcbc9314dc9fdbdeea55d6c64543dc4903e0_U256,
0x2b90bba00fca0589f617e7dcbfe82e0df706ab640ceb247b791a93b74e36736d_U256,
], [
0x2969f27eed31a480b9c36c764379dbca2cc8fdd1415c3dded62940bcde0bd771_U256,
0x2e2419f9ec02ec394c9871c832963dc1b89d743c8c7b964029b2311687b1fe23_U256,
0x101071f0032379b697315876690f053d148d4e109f5fb065c8aacc55a0f89bfa_U256,
], [
0x143021ec686a3f330d5f9e654638065ce6cd79e28c5b3753326244ee65a1b1a7_U256,
0x176cc029695ad02582a70eff08a6fd99d057e12e58e7d7b6b16cdfabc8ee2911_U256,
0x19a3fc0a56702bf417ba7fee3802593fa644470307043f7773279cd71d25d5e0_U256,
],
];
pub const C: [[U256; 3]; 65] = [
[
0x0ee9a592ba9a9518d05986d656f40c2114c4993c11bb29938d21d47304cd8e6e_U256,
0x00f1445235f2148c5986587169fc1bcd887b08d4d00868df5696fff40956e864_U256,
0x08dff3487e8ac99e1f29a058d0fa80b930c728730b7ab36ce879f3890ecf73f5_U256,
], [
0x2f27be690fdaee46c3ce28f7532b13c856c35342c84bda6e20966310fadc01d0_U256,
0x2b2ae1acf68b7b8d2416bebf3d4f6234b763fe04b8043ee48b8327bebca16cf2_U256,
0x0319d062072bef7ecca5eac06f97d4d55952c175ab6b03eae64b44c7dbf11cfa_U256,
], [
0x28813dcaebaeaa828a376df87af4a63bc8b7bf27ad49c6298ef7b387bf28526d_U256,
0x2727673b2ccbc903f181bf38e1c1d40d2033865200c352bc150928adddf9cb78_U256,
0x234ec45ca27727c2e74abd2b2a1494cd6efbd43e340587d6b8fb9e31e65cc632_U256,
], [
0x15b52534031ae18f7f862cb2cf7cf760ab10a8150a337b1ccd99ff6e8797d428_U256,
0x0dc8fad6d9e4b35f5ed9a3d186b79ce38e0e8a8d1b58b132d701d4eecf68d1f6_U256,
0x1bcd95ffc211fbca600f705fad3fb567ea4eb378f62e1fec97805518a47e4d9c_U256,
], [
0x10520b0ab721cadfe9eff81b016fc34dc76da36c2578937817cb978d069de559_U256,
0x1f6d48149b8e7f7d9b257d8ed5fbbaf42932498075fed0ace88a9eb81f5627f6_U256,
0x1d9655f652309014d29e00ef35a2089bfff8dc1c816f0dc9ca34bdb5460c8705_U256,
], [
0x04df5a56ff95bcafb051f7b1cd43a99ba731ff67e47032058fe3d4185697cc7d_U256,
0x0672d995f8fff640151b3d290cedaf148690a10a8c8424a7f6ec282b6e4be828_U256,
0x099952b414884454b21200d7ffafdd5f0c9a9dcc06f2708e9fc1d8209b5c75b9_U256,
], [
0x052cba2255dfd00c7c483143ba8d469448e43586a9b4cd9183fd0e843a6b9fa6_U256,
0x0b8badee690adb8eb0bd74712b7999af82de55707251ad7716077cb93c464ddc_U256,
0x119b1590f13307af5a1ee651020c07c749c15d60683a8050b963d0a8e4b2bdd1_U256,
], [
0x03150b7cd6d5d17b2529d36be0f67b832c4acfc884ef4ee5ce15be0bfb4a8d09_U256,
0x2cc6182c5e14546e3cf1951f173912355374efb83d80898abe69cb317c9ea565_U256,
0x005032551e6378c450cfe129a404b3764218cadedac14e2b92d2cd73111bf0f9_U256,
], [
0x233237e3289baa34bb147e972ebcb9516469c399fcc069fb88f9da2cc28276b5_U256,
0x05c8f4f4ebd4a6e3c980d31674bfbe6323037f21b34ae5a4e80c2d4c24d60280_U256,
0x0a7b1db13042d396ba05d818a319f25252bcf35ef3aeed91ee1f09b2590fc65b_U256,
], [
0x2a73b71f9b210cf5b14296572c9d32dbf156e2b086ff47dc5df542365a404ec0_U256,
0x1ac9b0417abcc9a1935107e9ffc91dc3ec18f2c4dbe7f22976a760bb5c50c460_U256,
0x12c0339ae08374823fabb076707ef479269f3e4d6cb104349015ee046dc93fc0_U256,
], [
0x0b7475b102a165ad7f5b18db4e1e704f52900aa3253baac68246682e56e9a28e_U256,
0x037c2849e191ca3edb1c5e49f6e8b8917c843e379366f2ea32ab3aa88d7f8448_U256,
0x05a6811f8556f014e92674661e217e9bd5206c5c93a07dc145fdb176a716346f_U256,
], [
0x29a795e7d98028946e947b75d54e9f044076e87a7b2883b47b675ef5f38bd66e_U256,
0x20439a0c84b322eb45a3857afc18f5826e8c7382c8a1585c507be199981fd22f_U256,
0x2e0ba8d94d9ecf4a94ec2050c7371ff1bb50f27799a84b6d4a2a6f2a0982c887_U256,
], [
0x143fd115ce08fb27ca38eb7cce822b4517822cd2109048d2e6d0ddcca17d71c8_U256,
0x0c64cbecb1c734b857968dbbdcf813cdf8611659323dbcbfc84323623be9caf1_U256,
0x028a305847c683f646fca925c163ff5ae74f348d62c2b670f1426cef9403da53_U256,
], [
0x2e4ef510ff0b6fda5fa940ab4c4380f26a6bcb64d89427b824d6755b5db9e30c_U256,
0x0081c95bc43384e663d79270c956ce3b8925b4f6d033b078b96384f50579400e_U256,
0x2ed5f0c91cbd9749187e2fade687e05ee2491b349c039a0bba8a9f4023a0bb38_U256,
], [
0x30509991f88da3504bbf374ed5aae2f03448a22c76234c8c990f01f33a735206_U256,
0x1c3f20fd55409a53221b7c4d49a356b9f0a1119fb2067b41a7529094424ec6ad_U256,
0x10b4e7f3ab5df003049514459b6e18eec46bb2213e8e131e170887b47ddcb96c_U256,
], [
0x2a1982979c3ff7f43ddd543d891c2abddd80f804c077d775039aa3502e43adef_U256,
0x1c74ee64f15e1db6feddbead56d6d55dba431ebc396c9af95cad0f1315bd5c91_U256,
0x07533ec850ba7f98eab9303cace01b4b9e4f2e8b82708cfa9c2fe45a0ae146a0_U256,
], [
0x21576b438e500449a151e4eeaf17b154285c68f42d42c1808a11abf3764c0750_U256,
0x2f17c0559b8fe79608ad5ca193d62f10bce8384c815f0906743d6930836d4a9e_U256,
0x2d477e3862d07708a79e8aae946170bc9775a4201318474ae665b0b1b7e2730e_U256,
], [
0x162f5243967064c390e095577984f291afba2266c38f5abcd89be0f5b2747eab_U256,
0x2b4cb233ede9ba48264ecd2c8ae50d1ad7a8596a87f29f8a7777a70092393311_U256,
0x2c8fbcb2dd8573dc1dbaf8f4622854776db2eece6d85c4cf4254e7c35e03b07a_U256,
], [
0x1d6f347725e4816af2ff453f0cd56b199e1b61e9f601e9ade5e88db870949da9_U256,
0x204b0c397f4ebe71ebc2d8b3df5b913df9e6ac02b68d31324cd49af5c4565529_U256,
0x0c4cb9dc3c4fd8174f1149b3c63c3c2f9ecb827cd7dc25534ff8fb75bc79c502_U256,
], [
0x174ad61a1448c899a25416474f4930301e5c49475279e0639a616ddc45bc7b54_U256,
0x1a96177bcf4d8d89f759df4ec2f3cde2eaaa28c177cc0fa13a9816d49a38d2ef_U256,
0x066d04b24331d71cd0ef8054bc60c4ff05202c126a233c1a8242ace360b8a30a_U256,
], [
0x2a4c4fc6ec0b0cf52195782871c6dd3b381cc65f72e02ad527037a62aa1bd804_U256,
0x13ab2d136ccf37d447e9f2e14a7cedc95e727f8446f6d9d7e55afc01219fd649_U256,
0x1121552fca26061619d24d843dc82769c1b04fcec26f55194c2e3e869acc6a9a_U256,
], [
0x00ef653322b13d6c889bc81715c37d77a6cd267d595c4a8909a5546c7c97cff1_U256,
0x0e25483e45a665208b261d8ba74051e6400c776d652595d9845aca35d8a397d3_U256,
0x29f536dcb9dd7682245264659e15d88e395ac3d4dde92d8c46448db979eeba89_U256,
], [
0x2a56ef9f2c53febadfda33575dbdbd885a124e2780bbea170e456baace0fa5be_U256,
0x1c8361c78eb5cf5decfb7a2d17b5c409f2ae2999a46762e8ee416240a8cb9af1_U256,
0x151aff5f38b20a0fc0473089aaf0206b83e8e68a764507bfd3d0ab4be74319c5_U256,
], [
0x04c6187e41ed881dc1b239c88f7f9d43a9f52fc8c8b6cdd1e76e47615b51f100_U256,
0x13b37bd80f4d27fb10d84331f6fb6d534b81c61ed15776449e801b7ddc9c2967_U256,
0x01a5c536273c2d9df578bfbd32c17b7a2ce3664c2a52032c9321ceb1c4e8a8e4_U256,
], [
0x2ab3561834ca73835ad05f5d7acb950b4a9a2c666b9726da832239065b7c3b02_U256,
0x1d4d8ec291e720db200fe6d686c0d613acaf6af4e95d3bf69f7ed516a597b646_U256,
0x041294d2cc484d228f5784fe7919fd2bb925351240a04b711514c9c80b65af1d_U256,
], [
0x154ac98e01708c611c4fa715991f004898f57939d126e392042971dd90e81fc6_U256,
0x0b339d8acca7d4f83eedd84093aef51050b3684c88f8b0b04524563bc6ea4da4_U256,
0x0955e49e6610c94254a4f84cfbab344598f0e71eaff4a7dd81ed95b50839c82e_U256,
], [
0x06746a6156eba54426b9e22206f15abca9a6f41e6f535c6f3525401ea0654626_U256,
0x0f18f5a0ecd1423c496f3820c549c27838e5790e2bd0a196ac917c7ff32077fb_U256,
0x04f6eeca1751f7308ac59eff5beb261e4bb563583ede7bc92a738223d6f76e13_U256,
], [
0x2b56973364c4c4f5c1a3ec4da3cdce038811eb116fb3e45bc1768d26fc0b3758_U256,
0x123769dd49d5b054dcd76b89804b1bcb8e1392b385716a5d83feb65d437f29ef_U256,
0x2147b424fc48c80a88ee52b91169aacea989f6446471150994257b2fb01c63e9_U256,
], [
0x0fdc1f58548b85701a6c5505ea332a29647e6f34ad4243c2ea54ad897cebe54d_U256,
0x12373a8251fea004df68abcf0f7786d4bceff28c5dbbe0c3944f685cc0a0b1f2_U256,
0x21e4f4ea5f35f85bad7ea52ff742c9e8a642756b6af44203dd8a1f35c1a90035_U256,
], [
0x16243916d69d2ca3dfb4722224d4c462b57366492f45e90d8a81934f1bc3b147_U256,
0x1efbe46dd7a578b4f66f9adbc88b4378abc21566e1a0453ca13a4159cac04ac2_U256,
0x07ea5e8537cf5dd08886020e23a7f387d468d5525be66f853b672cc96a88969a_U256,
], [
0x05a8c4f9968b8aa3b7b478a30f9a5b63650f19a75e7ce11ca9fe16c0b76c00bc_U256,
0x20f057712cc21654fbfe59bd345e8dac3f7818c701b9c7882d9d57b72a32e83f_U256,
0x04a12ededa9dfd689672f8c67fee31636dcd8e88d01d49019bd90b33eb33db69_U256,
], [
0x27e88d8c15f37dcee44f1e5425a51decbd136ce5091a6767e49ec9544ccd101a_U256,
0x2feed17b84285ed9b8a5c8c5e95a41f66e096619a7703223176c41ee433de4d1_U256,
0x1ed7cc76edf45c7c404241420f729cf394e5942911312a0d6972b8bd53aff2b8_U256,
], [
0x15742e99b9bfa323157ff8c586f5660eac6783476144cdcadf2874be45466b1a_U256,
0x1aac285387f65e82c895fc6887ddf40577107454c6ec0317284f033f27d0c785_U256,
0x25851c3c845d4790f9ddadbdb6057357832e2e7a49775f71ec75a96554d67c77_U256,
], [
0x15a5821565cc2ec2ce78457db197edf353b7ebba2c5523370ddccc3d9f146a67_U256,
0x2411d57a4813b9980efa7e31a1db5966dcf64f36044277502f15485f28c71727_U256,
0x002e6f8d6520cd4713e335b8c0b6d2e647e9a98e12f4cd2558828b5ef6cb4c9b_U256,
], [
0x2ff7bc8f4380cde997da00b616b0fcd1af8f0e91e2fe1ed7398834609e0315d2_U256,
0x00b9831b948525595ee02724471bcd182e9521f6b7bb68f1e93be4febb0d3cbe_U256,
0x0a2f53768b8ebf6a86913b0e57c04e011ca408648a4743a87d77adbf0c9c3512_U256,
], [
0x00248156142fd0373a479f91ff239e960f599ff7e94be69b7f2a290305e1198d_U256,
0x171d5620b87bfb1328cf8c02ab3f0c9a397196aa6a542c2350eb512a2b2bcda9_U256,
0x170a4f55536f7dc970087c7c10d6fad760c952172dd54dd99d1045e4ec34a808_U256,
], [
0x29aba33f799fe66c2ef3134aea04336ecc37e38c1cd211ba482eca17e2dbfae1_U256,
0x1e9bc179a4fdd758fdd1bb1945088d47e70d114a03f6a0e8b5ba650369e64973_U256,
0x1dd269799b660fad58f7f4892dfb0b5afeaad869a9c4b44f9c9e1c43bdaf8f09_U256,
], [
0x22cdbc8b70117ad1401181d02e15459e7ccd426fe869c7c95d1dd2cb0f24af38_U256,
0x0ef042e454771c533a9f57a55c503fcefd3150f52ed94a7cd5ba93b9c7dacefd_U256,
0x11609e06ad6c8fe2f287f3036037e8851318e8b08a0359a03b304ffca62e8284_U256,
], [
0x1166d9e554616dba9e753eea427c17b7fecd58c076dfe42708b08f5b783aa9af_U256,
0x2de52989431a859593413026354413db177fbf4cd2ac0b56f855a888357ee466_U256,
0x3006eb4ffc7a85819a6da492f3a8ac1df51aee5b17b8e89d74bf01cf5f71e9ad_U256,
], [
0x2af41fbb61ba8a80fdcf6fff9e3f6f422993fe8f0a4639f962344c8225145086_U256,
0x119e684de476155fe5a6b41a8ebc85db8718ab27889e85e781b214bace4827c3_U256,
0x1835b786e2e8925e188bea59ae363537b51248c23828f047cff784b97b3fd800_U256,
], [
0x28201a34c594dfa34d794996c6433a20d152bac2a7905c926c40e285ab32eeb6_U256,
0x083efd7a27d1751094e80fefaf78b000864c82eb571187724a761f88c22cc4e7_U256,
0x0b6f88a3577199526158e61ceea27be811c16df7774dd8519e079564f61fd13b_U256,
], [
0x0ec868e6d15e51d9644f66e1d6471a94589511ca00d29e1014390e6ee4254f5b_U256,
0x2af33e3f866771271ac0c9b3ed2e1142ecd3e74b939cd40d00d937ab84c98591_U256,
0x0b520211f904b5e7d09b5d961c6ace7734568c547dd6858b364ce5e47951f178_U256,
], [
0x0b2d722d0919a1aad8db58f10062a92ea0c56ac4270e822cca228620188a1d40_U256,
0x1f790d4d7f8cf094d980ceb37c2453e957b54a9991ca38bbe0061d1ed6e562d4_U256,
0x0171eb95dfbf7d1eaea97cd385f780150885c16235a2a6a8da92ceb01e504233_U256,
], [
0x0c2d0e3b5fd57549329bf6885da66b9b790b40defd2c8650762305381b168873_U256,
0x1162fb28689c27154e5a8228b4e72b377cbcafa589e283c35d3803054407a18d_U256,
0x2f1459b65dee441b64ad386a91e8310f282c5a92a89e19921623ef8249711bc0_U256,
], [
0x1e6ff3216b688c3d996d74367d5cd4c1bc489d46754eb712c243f70d1b53cfbb_U256,
0x01ca8be73832b8d0681487d27d157802d741a6f36cdc2a0576881f9326478875_U256,
0x1f7735706ffe9fc586f976d5bdf223dc680286080b10cea00b9b5de315f9650e_U256,
], [
0x2522b60f4ea3307640a0c2dce041fba921ac10a3d5f096ef4745ca838285f019_U256,
0x23f0bee001b1029d5255075ddc957f833418cad4f52b6c3f8ce16c235572575b_U256,
0x2bc1ae8b8ddbb81fcaac2d44555ed5685d142633e9df905f66d9401093082d59_U256,
], [
0x0f9406b8296564a37304507b8dba3ed162371273a07b1fc98011fcd6ad72205f_U256,
0x2360a8eb0cc7defa67b72998de90714e17e75b174a52ee4acb126c8cd995f0a8_U256,
0x15871a5cddead976804c803cbaef255eb4815a5e96df8b006dcbbc2767f88948_U256,
], [
0x193a56766998ee9e0a8652dd2f3b1da0362f4f54f72379544f957ccdeefb420f_U256,
0x2a394a43934f86982f9be56ff4fab1703b2e63c8ad334834e4309805e777ae0f_U256,
0x1859954cfeb8695f3e8b635dcb345192892cd11223443ba7b4166e8876c0d142_U256,
], [
0x04e1181763050e58013444dbcb99f1902b11bc25d90bbdca408d3819f4fed32b_U256,
0x0fdb253dee83869d40c335ea64de8c5bb10eb82db08b5e8b1f5e5552bfd05f23_U256,
0x058cbe8a9a5027bdaa4efb623adead6275f08686f1c08984a9d7c5bae9b4f1c0_U256,
], [
0x1382edce9971e186497eadb1aeb1f52b23b4b83bef023ab0d15228b4cceca59a_U256,
0x03464990f045c6ee0819ca51fd11b0be7f61b8eb99f14b77e1e6634601d9e8b5_U256,
0x23f7bfc8720dc296fff33b41f98ff83c6fcab4605db2eb5aaa5bc137aeb70a58_U256,
], [
0x0a59a158e3eec2117e6e94e7f0e9decf18c3ffd5e1531a9219636158bbaf62f2_U256,
0x06ec54c80381c052b58bf23b312ffd3ce2c4eba065420af8f4c23ed0075fd07b_U256,
0x118872dc832e0eb5476b56648e867ec8b09340f7a7bcb1b4962f0ff9ed1f9d01_U256,
], [
0x13d69fa127d834165ad5c7cba7ad59ed52e0b0f0e42d7fea95e1906b520921b1_U256,
0x169a177f63ea681270b1c6877a73d21bde143942fb71dc55fd8a49f19f10c77b_U256,
0x04ef51591c6ead97ef42f287adce40d93abeb032b922f66ffb7e9a5a7450544d_U256,
], [
0x256e175a1dc079390ecd7ca703fb2e3b19ec61805d4f03ced5f45ee6dd0f69ec_U256,
0x30102d28636abd5fe5f2af412ff6004f75cc360d3205dd2da002813d3e2ceeb2_U256,
0x10998e42dfcd3bbf1c0714bc73eb1bf40443a3fa99bef4a31fd31be182fcc792_U256,
], [
0x193edd8e9fcf3d7625fa7d24b598a1d89f3362eaf4d582efecad76f879e36860_U256,
0x18168afd34f2d915d0368ce80b7b3347d1c7a561ce611425f2664d7aa51f0b5d_U256,
0x29383c01ebd3b6ab0c017656ebe658b6a328ec77bc33626e29e2e95b33ea6111_U256,
], [
0x10646d2f2603de39a1f4ae5e7771a64a702db6e86fb76ab600bf573f9010c711_U256,
0x0beb5e07d1b27145f575f1395a55bf132f90c25b40da7b3864d0242dcb1117fb_U256,
0x16d685252078c133dc0d3ecad62b5c8830f95bb2e54b59abdffbf018d96fa336_U256,
], [
0x0a6abd1d833938f33c74154e0404b4b40a555bbbec21ddfafd672dd62047f01a_U256,
0x1a679f5d36eb7b5c8ea12a4c2dedc8feb12dffeec450317270a6f19b34cf1860_U256,
0x0980fb233bd456c23974d50e0ebfde4726a423eada4e8f6ffbc7592e3f1b93d6_U256,
], [
0x161b42232e61b84cbf1810af93a38fc0cece3d5628c9282003ebacb5c312c72b_U256,
0x0ada10a90c7f0520950f7d47a60d5e6a493f09787f1564e5d09203db47de1a0b_U256,
0x1a730d372310ba82320345a29ac4238ed3f07a8a2b4e121bb50ddb9af407f451_U256,
], [
0x2c8120f268ef054f817064c369dda7ea908377feaba5c4dffbda10ef58e8c556_U256,
0x1c7c8824f758753fa57c00789c684217b930e95313bcb73e6e7b8649a4968f70_U256,
0x2cd9ed31f5f8691c8e39e4077a74faa0f400ad8b491eb3f7b47b27fa3fd1cf77_U256,
], [
0x23ff4f9d46813457cf60d92f57618399a5e022ac321ca550854ae23918a22eea_U256,
0x09945a5d147a4f66ceece6405dddd9d0af5a2c5103529407dff1ea58f180426d_U256,
0x188d9c528025d4c2b67660c6b771b90f7c7da6eaa29d3f268a6dd223ec6fc630_U256,
], [
0x3050e37996596b7f81f68311431d8734dba7d926d3633595e0c0d8ddf4f0f47f_U256,
0x15af1169396830a91600ca8102c35c426ceae5461e3f95d89d829518d30afd78_U256,
0x1da6d09885432ea9a06d9f37f873d985dae933e351466b2904284da3320d8acc_U256,
], [
0x2796ea90d269af29f5f8acf33921124e4e4fad3dbe658945e546ee411ddaa9cb_U256,
0x202d7dd1da0f6b4b0325c8b3307742f01e15612ec8e9304a7cb0319e01d32d60_U256,
0x096d6790d05bb759156a952ba263d672a2d7f9c788f4c831a29dace4c0f8be5f_U256,
], [
0x054efa1f65b0fce283808965275d877b438da23ce5b13e1963798cb1447d25a4_U256,
0x1b162f83d917e93edb3308c29802deb9d8aa690113b2e14864ccf6e18e4165f1_U256,
0x21e5241e12564dd6fd9f1cdd2a0de39eedfefc1466cc568ec5ceb745a0506edc_U256,
], [
0x1cfb5662e8cf5ac9226a80ee17b36abecb73ab5f87e161927b4349e10e4bdf08_U256,
0x0f21177e302a771bbae6d8d1ecb373b62c99af346220ac0129c53f666eb24100_U256,
0x1671522374606992affb0dd7f71b12bec4236aede6290546bcef7e1f515c2320_U256,
], [
0x0fa3ec5b9488259c2eb4cf24501bfad9be2ec9e42c5cc8ccd419d2a692cad870_U256,
0x193c0e04e0bd298357cb266c1506080ed36edce85c648cc085e8c57b1ab54bba_U256,
0x102adf8ef74735a27e9128306dcbc3c99f6f7291cd406578ce14ea2adaba68f8_U256,
], [
0x0fe0af7858e49859e2a54d6f1ad945b1316aa24bfbdd23ae40a6d0cb70c3eab1_U256,
0x216f6717bbc7dedb08536a2220843f4e2da5f1daa9ebdefde8a5ea7344798d22_U256,
0x1da55cc900f0d21f4a3e694391918a1b3c23b2ac773c6b3ef88e2e4228325161_U256,
],
];
}
================================================
FILE: crates/poseidon/src/lib.rs
================================================
use ruint::aliases::U256;
use semaphore_rs_hasher::Hasher;
pub mod constants;
pub mod poseidon;
pub struct Poseidon;
impl Hasher for Poseidon {
type Hash = U256;
fn hash_node(left: &Self::Hash, right: &Self::Hash) -> Self::Hash {
poseidon::hash2(*left, *right)
}
}
================================================
FILE: crates/poseidon/src/poseidon.rs
================================================
use ark_bn254::Fr;
use ark_ff::{Field, Zero};
use once_cell::sync::Lazy;
use ruint::aliases::U256;
use crate::constants;
static M1: Lazy<[[Fr; 2]; 2]> = Lazy::new(|| {
constants::M1
.iter()
.map(|row| {
row.iter()
.map(Fr::try_from)
.collect::<Result<Vec<Fr>, _>>()
.unwrap()
.try_into()
.unwrap()
})
.collect::<Vec<[Fr; 2]>>()
.try_into()
.unwrap()
});
static C1: Lazy<[[Fr; 2]; 64]> = Lazy::new(|| {
constants::C1
.iter()
.map(|row| {
row.iter()
.map(Fr::try_from)
.collect::<Result<Vec<Fr>, _>>()
.unwrap()
.try_into()
.unwrap()
})
.collect::<Vec<[Fr; 2]>>()
.try_into()
.unwrap()
});
static M: Lazy<[[Fr; 3]; 3]> = Lazy::new(|| {
constants::M
.iter()
.map(|row| {
row.iter()
.map(Fr::try_from)
.collect::<Result<Vec<Fr>, _>>()
.unwrap()
.try_into()
.unwrap()
})
.collect::<Vec<[Fr; 3]>>()
.try_into()
.unwrap()
});
static C: Lazy<[[Fr; 3]; 65]> = Lazy::new(|| {
constants::C
.iter()
.map(|row| {
row.iter()
.map(Fr::try_from)
.collect::<Result<Vec<Fr>, _>>()
.unwrap()
.try_into()
.unwrap()
})
.collect::<Vec<[Fr; 3]>>()
.try_into()
.unwrap()
});
/// Compute the one-value Poseidon hash function.
///
/// # Panics
///
/// Panics if `input` is not a valid field element.
#[must_use]
pub fn hash1(value: U256) -> U256 {
let value = value.try_into().unwrap();
let mut state = [Fr::zero(), value];
for i in 0..64 {
// Add round constants
state[0] += C1[i][0];
state[1] += C1[i][1];
// SubWords, S-Box: Exponentiate
state[0] = state[0].pow([5]);
if !(4..60).contains(&i) {
state[1] = state[1].pow([5]);
}
// MixLayer: Multiply by maximum distance separable matrix
state = [
M1[0][0] * state[0] + M1[0][1] * state[1],
M1[1][0] * state[0] + M1[1][1] * state[1],
];
}
state[0].into()
}
/// Compute the two-value Poseidon hash function.
///
/// # Panics
///
/// Panics if `left`, `right` are not a valid field element.
#[must_use]
pub fn hash2(left: U256, right: U256) -> U256 {
let left = left.try_into().unwrap();
let right = right.try_into().unwrap();
let mut state = [Fr::zero(), left, right];
for i in 0..65 {
// Add round constants
state[0] += C[i][0];
state[1] += C[i][1];
state[2] += C[i][2];
// SubWords, S-Box: Exponentiate
state[0] = state[0].pow([5]);
if !(4..61).contains(&i) {
state[1] = state[1].pow([5]);
state[2] = state[2].pow([5]);
}
// MixLayer: Multiply by maximum distance separable matrix
state = [
M[0][0] * state[0] + M[0][1] * state[1] + M[0][2] * state[2],
M[1][0] * state[0] + M[1][1] * state[1] + M[1][2] * state[2],
M[2][0] * state[0] + M[2][1] * state[1] + M[2][2] * state[2],
];
}
state[0].into()
}
#[cfg(test)]
mod tests {
use ruint::uint;
use super::*;
#[test]
fn test_hash1() {
uint! {
assert_eq!(hash1(0_U256), 0x2a09a9fd93c590c26b91effbb2499f07e8f7aa12e2b4940a3aed2411cb65e11c_U256);
}
}
#[test]
fn test_hash2() {
uint! {
assert_eq!(hash2(0_U256, 0_U256), 0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864_U256);
assert_eq!(hash2(31213_U256, 132_U256), 0x303f59cd0831b5633bcda50514521b33776b5d4280eb5868ba1dbbe2e4d76ab5_U256);
}
}
}
================================================
FILE: crates/proof/Cargo.toml
================================================
[package]
name = "semaphore-rs-proof"
version.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
authors.workspace = true
description.workspace = true
keywords.workspace = true
categories.workspace = true
[dependencies]
semaphore-rs-utils.workspace = true
semaphore-rs-ark-circom = { workspace = true, optional = true }
ruint.workspace = true
serde.workspace = true
serde_json.workspace = true
ark-ec = { workspace = true, optional = true }
ark-groth16 = { workspace = true, optional = true }
ark-bn254 = { workspace = true, optional = true }
alloy-core = { workspace = true }
lazy_static.workspace = true
getrandom.workspace = true
hex.workspace = true
[features]
default = ["ark"]
ark = ["dep:semaphore-rs-ark-circom", "dep:ark-ec", "dep:ark-groth16", "dep:ark-bn254"]
================================================
FILE: crates/proof/src/ark.rs
================================================
use super::Proof;
use ark_bn254::Config;
use ark_ec::bn::Bn;
use ark_groth16::Proof as ArkProof;
use semaphore_rs_ark_circom::ethereum::AffineError;
impl From<ArkProof<Bn<Config>>> for Proof {
fn from(proof: ArkProof<Bn<Config>>) -> Self {
let proof = semaphore_rs_ark_circom::ethereum::Proof::from(proof);
let (a, b, c) = proof.as_tuple();
Self(a, b, c)
}
}
impl TryFrom<Proof> for ArkProof<Bn<Config>> {
type Error = AffineError;
fn try_from(proof: Proof) -> Result<Self, AffineError> {
let eth_proof = semaphore_rs_ark_circom::ethereum::Proof {
a: semaphore_rs_ark_circom::ethereum::G1 {
x: proof.0 .0,
y: proof.0 .1,
},
#[rustfmt::skip] // Rustfmt inserts some confusing spaces
b: semaphore_rs_ark_circom::ethereum::G2 {
// The order of coefficients is flipped.
x: [proof.1.0[1], proof.1.0[0]],
y: [proof.1.1[1], proof.1.1[0]],
},
c: semaphore_rs_ark_circom::ethereum::G1 {
x: proof.2 .0,
y: proof.2 .1,
},
};
// This conversion can fail if points are not on the curve.
eth_proof.try_into()
}
}
================================================
FILE: crates/proof/src/compression.rs
================================================
//! Groth16 proof compression
//!
//! Ported from https://github.com/worldcoin/world-id-state-bridge/blob/main/src/SemaphoreVerifier.sol
//!
//! Based upon work in https://xn--2-umb.com/23/bn254-compression/
use ruint::aliases::U256;
use ruint::uint;
use serde::{Deserialize, Serialize};
use super::{Proof, G1, G2};
use lazy_static::lazy_static;
/// Base field Fp order P
pub const P: U256 =
uint! { 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47_U256 };
// A helper for a frequently used constants
pub const ONE: U256 = uint! { 1_U256 };
pub const TWO: U256 = uint! { 2_U256 };
pub const THREE: U256 = uint! { 3_U256 };
pub const FOUR: U256 = uint! { 4_U256 };
lazy_static! {
/// Exponent for the square root in Fp
pub static ref EXP_SQRT_FP: U256 = (P + ONE) / FOUR;
/// Exponent for the inverse in Fp
pub static ref EXP_INVERSE_FP: U256 = P - TWO;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct CompressedProof(pub U256, pub (U256, U256), pub U256);
impl CompressedProof {
pub const fn from_flat(flat: [U256; 4]) -> Self {
let [a, b0, b1, c] = flat;
Self(a, (b0, b1), c)
}
pub const fn flatten(self) -> [U256; 4] {
let Self(a, (b0, b1), c) = self;
[a, b0, b1, c]
}
}
pub fn compress_proof(proof: Proof) -> Option<CompressedProof> {
let Proof(g1a, g2, g1b) = proof;
// NOTE: Order of real and imaginary parts in the proof data is flipped
let ([x0, x1], [y0, y1]) = g2;
let g2 = ([x1, x0], [y1, y0]);
let a = compress_g1(g1a)?;
// NOTE: G2 compressed repr is flipped
let (c0, c1) = compress_g2(g2)?;
let c = (c1, c0);
let b = compress_g1(g1b)?;
Some(CompressedProof(a, c, b))
}
pub fn decompress_proof(compressed: CompressedProof) -> Option<Proof> {
let CompressedProof(a, c, b) = compressed;
let g1a = decompress_g1(a)?;
// NOTE: G2 compressed repr is flipped
let (c1, c0) = c;
let c = (c0, c1);
let g2 = decompress_g2(c)?;
let g1b = decompress_g1(b)?;
// Unswap
let ([x1, x0], [y1, y0]) = g2;
let g2 = ([x0, x1], [y0, y1]);
Some(Proof(g1a, g2, g1b))
}
pub fn compress_g1((x, y): G1) -> Option<U256> {
if x >= P || y >= P {
return None; // Point not in field
}
if x == U256::ZERO && y == U256::ZERO {
return Some(U256::ZERO); // Point at infinity
}
let y_pos = sqrt_fp(x.pow_mod(THREE, P).add_mod(THREE, P))?;
if y == y_pos {
Some(x << 1)
} else if y == neg_fp(y_pos) {
Some(x << 1 | ONE)
} else {
None
}
}
pub fn decompress_g1(c: U256) -> Option<G1> {
if c == U256::ZERO {
return Some((U256::ZERO, U256::ZERO)); // Point at infinity
}
let negate = c & ONE == ONE;
let x: U256 = c >> 1;
if x >= P {
return None;
}
let y2 = x.pow_mod(THREE, P).add_mod(THREE, P);
let mut y = sqrt_fp(y2)?;
if negate {
y = neg_fp(y);
}
Some((x, y))
}
/// Compresses the
pub fn compress_g2(([x0, x1], [y0, y1]): G2) -> Option<(U256, U256)> {
if x0 >= P || x1 >= P || y0 >= P || y1 >= P {
return None; // Point not in field
}
if (x0 | x1 | y0 | y1) == U256::ZERO {
return Some((U256::ZERO, U256::ZERO)); // Point at infinity
}
// Compute y^2
let n3ab = x0.mul_mod(x1, P).mul_mod(P - THREE, P);
let a_3 = x0.pow_mod(THREE, P);
let b_3 = x1.pow_mod(THREE, P);
let y0_pos = U256::from(27)
.mul_mod(U256::from(82).inv_mod(P).unwrap(), P)
.add_mod(a_3.add_mod(n3ab.mul_mod(x1, P), P), P);
let y1_pos = neg_fp(
THREE
.mul_mod(U256::from(82).inv_mod(P).unwrap(), P)
.add_mod(b_3.add_mod(n3ab.mul_mod(x0, P), P), P),
);
// Determine hint bit
let d = sqrt_fp(
y0_pos
.mul_mod(y0_pos, P)
.add_mod(y1_pos.mul_mod(y1_pos, P), P),
)?;
let hint = !is_square_fp(y0_pos.add_mod(d, P).mul_mod(TWO.inv_mod(P).unwrap(), P));
// Recover y
let (new_y0_pos, new_y1_pos) = sqrt_fp2(y0_pos, y1_pos, hint)?;
let hint = if hint { TWO } else { U256::ZERO };
if y0 == new_y0_pos && y1 == new_y1_pos {
Some(((x0 << 2) | hint, x1))
} else if y0 == neg_fp(new_y0_pos) && y1 == neg_fp(new_y1_pos) {
Some(((x0 << 2) | hint | ONE, x1))
} else {
None
}
}
pub fn decompress_g2((c0, c1): (U256, U256)) -> Option<G2> {
if c0 == U256::ZERO && c1 == U256::ZERO {
return Some(([U256::ZERO, U256::ZERO], [U256::ZERO, U256::ZERO])); // Point at infinity
}
let negate = c0 & ONE == ONE;
let hint = c0 & TWO == TWO;
let x0: U256 = c0 >> 2;
let x1 = c1;
if x0 >= P || x1 >= P {
return None;
}
let n3ab = x0.mul_mod(x1, P).mul_mod(P - THREE, P);
let a_3 = x0.pow_mod(THREE, P);
let b_3 = x1.pow_mod(THREE, P);
let y0 = U256::from(27)
.mul_mod(U256::from(82).inv_mod(P)?, P)
.add_mod(a_3.add_mod(n3ab.mul_mod(x1, P), P), P);
let y1 = neg_fp(
THREE
.mul_mod(U256::from(82).inv_mod(P)?, P)
.add_mod(b_3.add_mod(n3ab.mul_mod(x0, P), P), P),
);
let (mut y0, mut y1) = sqrt_fp2(y0, y1, hint)?;
if negate {
y0 = neg_fp(y0);
y1 = neg_fp(y1);
}
Some(([x0, x1], [y0, y1]))
}
fn sqrt_fp(a: U256) -> Option<U256> {
let x = a.pow_mod(*EXP_SQRT_FP, P);
if x.mul_mod(x, P) == a {
Some(x)
} else {
None
}
}
fn sqrt_fp2(a0: U256, a1: U256, hint: bool) -> Option<(U256, U256)> {
let mut d = sqrt_fp(a0.pow_mod(TWO, P).add_mod(a1.pow_mod(TWO, P), P))?;
if hint {
d = neg_fp(d);
}
let frac_1_2 = ONE.mul_mod(TWO.inv_mod(P)?, P);
let x0 = sqrt_fp(a0.add_mod(d, P).mul_mod(frac_1_2, P))?;
let x1 = a1.mul_mod(invert_fp(x0.mul_mod(TWO, P))?, P);
if a0 != x0.pow_mod(TWO, P).add_mod(neg_fp(x1.pow_mod(TWO, P)), P)
|| a1 != TWO.mul_mod(x0.mul_mod(x1, P), P)
{
return None;
}
Some((x0, x1))
}
fn is_square_fp(a: U256) -> bool {
let x = a.pow_mod(*EXP_SQRT_FP, P);
x.mul_mod(x, P) == a
}
/// Inversion in Fp
///
/// Returns a number x such that a * x = 1 in Fp
/// Returns None if the inverse does not exist
fn invert_fp(a: U256) -> Option<U256> {
let x = a.pow_mod(*EXP_INVERSE_FP, P);
if a.mul_mod(x, P) != ONE {
return None;
}
Some(x)
}
fn neg_fp(a: U256) -> U256 {
P.wrapping_sub(a % P) % P
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn inversion() {
let v = uint! { 4598362786468342265918458423096940256393720972438048893356218087518821823203_U256 };
let inverted = invert_fp(v).unwrap();
let exp_inverted = uint! { 4182222526301715069940346543278816173622053692765626450942898397518664864041_U256 };
assert_eq!(exp_inverted, inverted);
}
#[test]
fn square_root_fp() {
let v = uint! { 14471043194638943579446425262583282548539507047061604313953794288955195726209_U256 };
let exp_sqrt = uint! { 13741342543520938546471415319044405232187715299443307089577869276344592329757_U256 };
let sqrt = sqrt_fp(v).unwrap();
assert_eq!(exp_sqrt, sqrt);
}
#[test]
fn square_root_fp_2() {
let (a, b) = sqrt_fp2(uint!{17473058728477435457299093362519578563618705081729024467362715416915525458528_U256}, uint!{17683468329848516541101685027677188007795188556813329975791177956431310972350_U256}, false).unwrap();
let exp_a = uint! {10193706077588260514783319931179623845729747565730309463634080055351233087269_U256};
let exp_b = uint! {2911435556167431587172450261242327574185987927358833959334220021362478804490_U256};
assert_eq!(exp_a, a);
assert_eq!(exp_b, b);
}
// The literal values below are taken from the proof in the following tx: https://etherscan.io/tx/0x53309842294be8c2b9fd694c4e86a5ab031c0d58750978fb3d6f60de16eaa897
// Raw proof data is:
// 20565048055856194013099208963146657799256893353279242520150547463020687826541
// 16286013012747852737396822706018267259565592188907848191354824303311847109059
// 4348608846293503080802796983494208797681981448804902149317789801083784587558
// 6172488348732750834133346196464201580503416389945891763609808290085997580078
// 3229429189805934086496276224876305383924675874777054942516982958483565949767
// 944252930093106871283598150477854448876343937304805759422971930315581301659
// 18318130744212307125672524358864792312717149086464333958791498157127232409959
// 8256141885907329266852096557308020923997215847794048916749940281741155521604
//
// Note that for the G2 compression test the order of real and imaginary is flipped
//
// The expected compressed data is generated with the SemaphoreVerifier implementation
// in world-id-state-bridge using chisel.
//
// Unfortunately the `compress_g1` and `compress_g2` methods are set to `internal` so
// the approach is a little hacky, but steps to regenerate these values are as follows:
// 1. Change `internal` to `public` in `SemaphoreVerifier.sol`
// 2. Start `chisel`
// 3. Execute the following in chisel repl
// ```
// > import {SemaphoreVerifier} from "src/SemaphoreVerifier.sol";
// > SemaphoreVerifier ve = new SemaphoreVerifier();
// ```
// 4. Now you can generate the expected data fixtures using e.g.
// ```
// > ve.compress_g1(0x19ded61ab5c58fdb12367526c6bc04b9186d0980c4b6fd48a44093e80f9b4206, 0x2e619a034be10e9aab294f1c77a480378e84782c8519449aef0c8f6952382bda)
// ```
// Note that for some reason chisel doesn't handle multiple return values that well, so you
// might have to pattern match the return types, e.g.
// ```
// > (uint256 a, uint256 b) = ve.compress_g2(...);
// > a;
// Type: uint256
// ├ Hex: 0x1dd212f101a320736a9662cac57929556777fad3e7882b022d4ba3261cf14db6
// ├ Hex (full word): 0x1dd212f101a320736a9662cac57929556777fad3e7882b022d4ba3261cf14db6
// └ Decimal: 13488241221471993734368286196608381596836013455766665997449768358320614231478
// ```
#[test]
fn proof_compression() {
let flat_proof: [U256; 8] = uint! { [
20565048055856194013099208963146657799256893353279242520150547463020687826541_U256,
16286013012747852737396822706018267259565592188907848191354824303311847109059_U256,
4348608846293503080802796983494208797681981448804902149317789801083784587558_U256,
6172488348732750834133346196464201580503416389945891763609808290085997580078_U256,
3229429189805934086496276224876305383924675874777054942516982958483565949767_U256,
944252930093106871283598150477854448876343937304805759422971930315581301659_U256,
18318130744212307125672524358864792312717149086464333958791498157127232409959_U256,
8256141885907329266852096557308020923997215847794048916749940281741155521604_U256,
]};
let proof = Proof::from_flat(flat_proof);
let compressed = compress_proof(proof).unwrap();
let exp_flat_compressed: [U256; 4] = uint! {[
41130096111712388026198417926293315598513786706558485040301094926041375653083_U256,
4348608846293503080802796983494208797681981448804902149317789801083784587558_U256,
24689953394931003336533384785856806322013665559783567054439233160343990320315_U256,
36636261488424614251345048717729584625434298172928667917582996314254464819918_U256,
]};
assert_eq!(exp_flat_compressed, compressed.flatten());
let decompressed = decompress_proof(compressed).unwrap();
assert_eq!(proof, decompressed);
}
#[test]
fn g1_compression() {
let point: G1 = uint! {
(
0x19ded61ab5c58fdb12367526c6bc04b9186d0980c4b6fd48a44093e80f9b4206_U256,
0x2e619a034be10e9aab294f1c77a480378e84782c8519449aef0c8f6952382bda_U256
)
};
let exp_compressed =
uint! { 0x33bdac356b8b1fb6246cea4d8d78097230da1301896dfa91488127d01f36840c_U256 };
let compressed = compress_g1(point).unwrap();
assert_eq!(exp_compressed, compressed);
let decompressed = decompress_g1(compressed).unwrap();
assert_eq!(point, decompressed);
}
#[test]
fn g2_compression() {
let point: G2 = uint! {
(
[
0x077484BC4068C81CDAA598B2B15E4A5559DDFEB4F9E20AC08B52E8C9873C536D_U256,
0x25E744163329AABFB40086C09E0B54D09DFBD302CE975E71150133E46E75F0AA_U256,
],
[
0x20AF3E3AFED950A86937F4319100B19A1141FF59DA42B9670CFA57E5D83BE618_U256,
0x089C901AA5603652F8CC748F04907233C63A75302244D67FF974B05AF09948D2_U256,
]
)
};
let compressed = compress_g2(point).unwrap();
let exp_compressed = uint! { (0x1dd212f101a320736a9662cac57929556777fad3e7882b022d4ba3261cf14db6_U256, 0x25e744163329aabfb40086c09e0b54d09dfbd302ce975e71150133e46e75f0aa_U256) };
assert_eq!(exp_compressed, compressed);
let decompressed = decompress_g2(compressed).unwrap();
assert_eq!(point, decompressed);
}
#[test]
fn deser() {
let s = r#"["0x1",["0x2","0x3"],"0x4"]"#;
let deserialized: CompressedProof = serde_json::from_str(s).unwrap();
let reserialized = serde_json::to_string(&deserialized).unwrap();
assert_eq!(s, reserialized);
}
}
================================================
FILE: crates/proof/src/lib.rs
================================================
use ruint::aliases::U256;
use serde::{Deserialize, Serialize};
#[cfg(feature = "ark")]
mod ark;
pub mod compression;
pub mod packing;
// Matches the private G1Tup type in ark-circom.
pub type G1 = (U256, U256);
// Matches the private G2Tup type in ark-circom.
pub type G2 = ([U256; 2], [U256; 2]);
/// Wrap a proof object so we have serde support
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Proof(pub G1, pub G2, pub G1);
impl Proof {
pub const fn from_flat(flat: [U256; 8]) -> Self {
let [x0, x1, x2, x3, x4, x5, x6, x7] = flat;
Self((x0, x1), ([x2, x3], [x4, x5]), (x6, x7))
}
pub const fn flatten(self) -> [U256; 8] {
let Self((a0, a1), ([bx0, bx1], [by0, by1]), (c0, c1)) = self;
[a0, a1, bx0, bx1, by0, by1, c0, c1]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deser() {
let s = r#"[["0x1","0x2"],[["0x3","0x4"],["0x5","0x6"]],["0x7","0x8"]]"#;
let deserialized: Proof = serde_json::from_str(s).unwrap();
let reserialized = serde_json::to_string(&deserialized).unwrap();
assert_eq!(s, reserialized);
}
}
================================================
FILE: crates/proof/src/packing.rs
================================================
use std::{
fmt::Display,
str::{from_utf8, FromStr},
};
use alloy_core::sol_types::{
sol_data::{FixedArray, Uint},
SolType, SolValue,
};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::Proof;
use semaphore_rs_utils::{bytes_from_hex, bytes_to_hex, deserialize_bytes, serialize_bytes};
/// A packed proof is a representation of the ZKP in a single attribute (as
/// opposed to array of arrays) which is easier to transport
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PackedProof(pub [u8; 256]);
impl From<Proof> for PackedProof {
fn from(proof: Proof) -> Self {
let flat_proof = [
proof.0 .0,
proof.0 .1,
proof.1 .0[0],
proof.1 .0[1],
proof.1 .1[0],
proof.1 .1[1],
proof.2 .0,
proof.2 .1,
];
let bytes = flat_proof.abi_encode();
let mut encoded = [0u8; 256];
encoded.copy_from_slice(&bytes[..256]);
Self(encoded)
}
}
impl From<PackedProof> for Proof {
fn from(proof: PackedProof) -> Self {
let decoded = FixedArray::<Uint<256>, 8>::abi_decode(&proof.0).unwrap();
let a = (decoded[0], decoded[1]);
let b = ([decoded[2], decoded[3]], [decoded[4], decoded[5]]);
let c = (decoded[6], decoded[7]);
Self(a, b, c)
}
}
impl Display for PackedProof {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let hex = bytes_to_hex::<256, 514>(&self.0);
write!(
f,
"{}",
from_utf8(&hex).expect("failed to convert to string")
)
}
}
impl FromStr for PackedProof {
type Err = hex::FromHexError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
bytes_from_hex::<256>(s).map(Self)
}
}
impl Serialize for PackedProof {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serialize_bytes::<256, 514, S>(serializer, &self.0)
}
}
impl<'de> Deserialize<'de> for PackedProof {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let bytes = deserialize_bytes::<256, _>(deserializer)?;
Ok(Self(bytes))
}
}
#[cfg(test)]
pub mod test {
use super::*;
use ruint::aliases::U256;
#[test]
fn test_serializing_proof_into_packed_proof() {
let proof = Proof(
(U256::from(1), U256::from(2)),
(
[U256::from(3), U256::from(4)],
[U256::from(5), U256::from(6)],
),
(U256::from(7), U256::from(8)),
);
let packed_proof = PackedProof::from(proof);
assert_eq!(packed_proof.to_string(), "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008");
let proof2 = Proof::from(packed_proof);
assert_eq!(proof, proof2);
}
#[test]
fn test_parse_from_string() {
let packed_proof_str = "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008";
let packed_proof = PackedProof::from_str(packed_proof_str).unwrap();
let expected_proof = Proof(
(U256::from(1), U256::from(2)),
(
[U256::from(3), U256::from(4)],
[U256::from(5), U256::from(6)],
),
(U256::from(7), U256::from(8)),
);
let proof: Proof = packed_proof.into();
assert_eq!(proof, expected_proof);
}
#[test]
fn test_parse_from_string_without_prefix() {
// note the lack of 0x prefix
let packed_proof_str = "00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008";
let packed_proof = PackedProof::from_str(packed_proof_str).unwrap();
let expected_proof = Proof(
(U256::from(5), U256::from(6)),
(
[U256::from(3), U256::from(4)],
[U256::from(5), U256::from(6)],
),
(U256::from(7), U256::from(8)),
);
let proof: Proof = packed_proof.into();
assert_eq!(proof, expected_proof);
}
#[test]
fn test_serialize_proof_to_json() {
let packed_proof_str = "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008";
let packed_proof = PackedProof::from_str(packed_proof_str).unwrap();
let proof: Proof = packed_proof.into();
let serialized = serde_json::to_value(proof).unwrap();
assert_eq!(
serialized,
serde_json::json!([
["0x1", "0x2"],
[["0x3", "0x4"], ["0x5", "0x6"]],
["0x7", "0x8"]
])
);
}
#[test]
fn test_serialize_proof_to_json_real_numbers() {
let packed_proof_str = "0x15c1fc6907219676890dfe147ee6f10b580c7881dddacb1567b3bcbfc513a54d233afda3efff43a7631990d2e79470abcbae3ccad4b920476e64745bfe97bb0a0c8c7d7434c382d590d601d951c29c8463d555867db70f9e84f7741c81c2e1e6241d2ddf1c9e6670a24109a0e9c915cd6e07d0248a384dd38d3c91e9b0419f5f0b23c5467a06eff56cc2c246ada1e7d5705afc4dc8b43fd5a6972c679a2019c5091ed6522f7924d3674d08966a008f947f9aa016a4100bb12f911326f3e1befd0acdf5a5996e00933206cbec48f3bbdcee2a4ca75f8db911c00001e5a05474872446d6f1c1506837392a30fdc73d66fd89f4e1b1a5d14b93e2ad0c5f7b777520";
let packed_proof = PackedProof::from_str(packed_proof_str).unwrap();
let proof: Proof = packed_proof.into();
let serialized = serde_json::to_value(proof).unwrap();
assert_eq!(
serialized,
serde_json::json!([
[
"0x15c1fc6907219676890dfe147ee6f10b580c7881dddacb1567b3bcbfc513a54d",
"0x233afda3efff43a7631990d2e79470abcbae3ccad4b920476e64745bfe97bb0a"
],
[
[
"0xc8c7d7434c382d590d601d951c29c8463d555867db70f9e84f7741c81c2e1e6",
"0x241d2ddf1c9e6670a24109a0e9c915cd6e07d0248a384dd38d3c91e9b0419f5f"
],
[
"0xb23c5467a06eff56cc2c246ada1e7d5705afc4dc8b43fd5a6972c679a2019c5",
"0x91ed6522f7924d3674d08966a008f947f9aa016a4100bb12f911326f3e1befd"
]
],
[
"0xacdf5a5996e00933206cbec48f3bbdcee2a4ca75f8db911c00001e5a0547487",
"0x2446d6f1c1506837392a30fdc73d66fd89f4e1b1a5d14b93e2ad0c5f7b777520"
]
])
);
}
#[test]
fn test_deserialize_proof_from_json() {
let proof_str = "[
[
\"0x15c1fc6907219676890dfe147ee6f10b580c7881dddacb1567b3bcbfc513a54d\",
\"0x233afda3efff43a7631990d2e79470abcbae3ccad4b920476e64745bfe97bb0a\"
],
[
[
\"0xc8c7d7434c382d590d601d951c29c8463d555867db70f9e84f7741c81c2e1e6\",
\"0x241d2ddf1c9e6670a24109a0e9c915cd6e07d0248a384dd38d3c91e9b0419f5f\"
],
[
\"0xb23c5467a06eff56cc2c246ada1e7d5705afc4dc8b43fd5a6972c679a2019c5\",
\"0x91ed6522f7924d3674d08966a008f947f9aa016a4100bb12f911326f3e1befd\"
]
],
[
\"0xacdf5a5996e00933206cbec48f3bbdcee2a4ca75f8db911c00001e5a0547487\",
\"0x2446d6f1c1506837392a30fdc73d66fd89f4e1b1a5d14b93e2ad0c5f7b777520\"
]
]";
let proof = serde_json::from_str::<Proof>(proof_str).unwrap();
let packed_proof = PackedProof::from(proof);
let expected_proof = "0x15c1fc6907219676890dfe147ee6f10b580c7881dddacb1567b3bcbfc513a54d233afda3efff43a7631990d2e79470abcbae3ccad4b920476e64745bfe97bb0a0c8c7d7434c382d590d601d951c29c8463d555867db70f9e84f7741c81c2e1e6241d2ddf1c9e6670a24109a0e9c915cd6e07d0248a384dd38d3c91e9b0419f5f0b23c5467a06eff56cc2c246ada1e7d5705afc4dc8b43fd5a6972c679a2019c5091ed6522f7924d3674d08966a008f947f9aa016a4100bb12f911326f3e1befd0acdf5a5996e00933206cbec48f3bbdcee2a4ca75f8db911c00001e5a05474872446d6f1c1506837392a30fdc73d66fd89f4e1b1a5d14b93e2ad0c5f7b777520";
assert_eq!(packed_proof.to_string(), expected_proof);
}
#[test]
fn test_invalid_parsing() {
// note this is only 7 numbers
let packed_proof_str = "0x0000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000007";
PackedProof::from_str(packed_proof_str).expect_err("parsing should fail");
// not a valid number
let packed_proof_str = "0000000000000000p000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008";
PackedProof::from_str(packed_proof_str).expect_err("parsing should fail");
// completely invalid
let packed_proof_str = "0x0";
PackedProof::from_str(packed_proof_str).expect_err("parsing should fail");
}
}
================================================
FILE: crates/semaphore/Cargo.toml
================================================
[package]
name = "semaphore-rs"
version.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
authors.workspace = true
description.workspace = true
keywords.workspace = true
categories.workspace = true
[dependencies]
# Internal
semaphore-rs-utils.workspace = true
semaphore-rs-ark-zkey.workspace = true
semaphore-rs-ark-circom.workspace = true
semaphore-rs-proof = { workspace = true, features = ["ark"] }
semaphore-rs-poseidon.workspace = true
semaphore-rs-hasher.workspace = true
semaphore-rs-keccak.workspace = true
semaphore-rs-trees.workspace = true
semaphore-rs-storage.workspace = true
semaphore-rs-depth-config.workspace = true
semaphore-rs-depth-macros.workspace = true
semaphore-rs-witness.workspace = true
# 3rd Party
bincode.workspace = true
bytemuck.workspace = true
color-eyre.workspace = true
hex.workspace = true
hex-literal.workspace = true
itertools.workspace = true
lazy_static.workspace = true
num-bigint.workspace = true
once_cell.workspace = true
rand.workspace = true
rayon.workspace = true
ruint.workspace = true
serde.workspace = true
sha2.workspace = true
thiserror.workspace = true
tiny-keccak.workspace = true
zeroize.workspace = true
# Ark
ark-bn254.workspace = true
ark-ec.workspace = true
ark-ff.workspace = true
ark-groth16.workspace = true
ark-relations.workspace = true
ark-std.workspace = true
[dev-dependencies]
serial_test.workspace = true
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
criterion.workspace = true
bincode.workspace = true
proptest.workspace = true
rand_chacha.workspace = true
serde_json.workspace = true
tempfile.workspace = true
tiny-keccak.workspace = true
tracing-test.workspace = true
[build-dependencies]
semaphore-rs-ark-zkey.workspace = true
color-eyre.workspace = true
reqwest.workspace = true
semaphore-rs-depth-config.workspace = true
[[bench]]
name = "cascading_merkle_tree"
harness = false
[[bench]]
name = "lazy_merkle_tree"
harness = false
[features]
default = []
depth_16 = [
"semaphore-rs-depth-config/depth_16",
"semaphore-rs-depth-macros/depth_16",
]
depth_20 = [
"semaphore-rs-depth-config/depth_20",
"semaphore-rs-depth-macros/depth_20",
]
depth_30 = [
"semaphore-rs-depth-config/depth_30",
"semaphore-rs-depth-macros/depth_30",
]
================================================
FILE: crates/semaphore/README.md
================================================
# 🦀 semaphore-rs
Rust 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.
## Usage
Add this line to your `cargo.toml`:
```toml
semaphore = { git = "https://github.com/worldcoin/semaphore-rs" }
```
## Building semaphore circuits
1. Check out submodule (if not done before already): `git submodule update --init --recursive`
1. Install semaphore dependencies `cd semaphore && npm install`
1. Compile circuits `npm exec ts-node ./scripts/compile-circuits.ts`
1. You'll find the `zkey` and `wasm` file in `semaphore/build/snark`
## Example
Example as in `src/lib.rs`, run with `cargo test`.
```rust,no_run
use semaphore_rs::{get_supported_depths, hash_to_field, Field, identity::Identity,
poseidon_tree::LazyPoseidonTree, protocol::*};
use num_bigint::BigInt;
// generate identity
let mut secret = *b"secret";
let id = Identity::from_secret(&mut secret, None);
// Get the first available tree depth. This is controlled by the crate features.
let depth = get_supported_depths()[0];
// generate merkle tree
let leaf = Field::from(0);
let mut tree = LazyPoseidonTree::new(depth, leaf).derived();
tree = tree.update(0, &id.commitment());
let merkle_proof = tree.proof(0);
let root = tree.root();
// change signal and external_nullifier here
let signal_hash = hash_to_field(b"xxx");
let external_nullifier_hash = hash_to_field(b"appId");
let nullifier_hash = generate_nullifier_hash(&id, external_nullifier_hash);
let proof = generate_proof(&id, &merkle_proof, external_nullifier_hash, signal_hash).unwrap();
let success = verify_proof(root, nullifier_hash, signal_hash, external_nullifier_hash, &proof, depth).unwrap();
assert!(success);
```
================================================
FILE: crates/semaphore/benches/cascading_merkle_tree.rs
================================================
use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion};
use semaphore_rs::Field;
use semaphore_rs_hasher::Hasher;
use semaphore_rs_poseidon::Poseidon;
use semaphore_rs_storage::MmapVec;
use semaphore_rs_trees::cascading::CascadingMerkleTree;
criterion_main!(cascading_merkle_tree);
criterion_group!(
cascading_merkle_tree,
bench_cascading_validate,
bench_cascading_create_dense_tree,
bench_cascading_create_dense_mmap_tree,
bench_cascading_restore_dense_mmap_tree,
bench_cascading_dense_tree_reads,
bench_cascading_dense_mmap_tree_reads,
bench_cascading_dense_tree_writes,
bench_cascading_dense_mmap_tree_writes,
bench_cascading_proof_from_hash
);
struct TreeValues<H: Hasher> {
depth: usize,
empty_value: H::Hash,
initial_values: Vec<H::Hash>,
}
fn bench_cascading_proof_from_hash(criterion: &mut Criterion) {
let tree_value = create_values_for_tree(14);
criterion.bench_function("bench_cascading_proof_from_hash", |b| {
let leaf = Field::from(234123412341usize);
b.iter_batched_ref(
|| {
let mut tree = CascadingMerkleTree::<Poseidon>::new_with_leaves(
vec![],
tree_value.depth,
&tree_value.empty_value,
&tree_value.initial_values,
);
tree.set_leaf(1 << 13, leaf);
tree
},
|tree| {
let _ = tree.proof_from_hash(leaf);
},
BatchSize::SmallInput,
);
});
}
fn bench_cascading_validate(criterion: &mut Criterion) {
let tree_values = [
create_values_for_tree(4),
create_values_for_tree(10),
create_values_for_tree(14),
];
let mut group = criterion.benchmark_group("bench_cascading_validate");
for value in tree_values.iter() {
let tree = CascadingMerkleTree::<Poseidon>::new_with_leaves(
vec![],
value.depth,
&value.empty_value,
&value.initial_values,
);
group.bench_with_input(
BenchmarkId::from_parameter(format!("validate_{}", value.depth)),
value,
|bencher: &mut criterion::Bencher, _| {
bencher.iter(|| {
tree.validate().unwrap();
});
},
);
}
group.finish();
}
fn bench_cascading_create_dense_tree(criterion: &mut Criterion) {
let tree_values = [
create_values_for_tree(4),
create_values_for_tree(10),
create_values_for_tree(14),
];
let mut group = criterion.benchmark_group("bench_cascading_create_dense_tree");
for value in tree_values.iter() {
group.bench_with_input(
BenchmarkId::from_parameter(format!("create_dense_tree_depth_{}", value.depth)),
value,
|bencher: &mut criterion::Bencher, value| {
bencher.iter(|| {
let _tree = CascadingMerkleTree::<Poseidon>::new_with_leaves(
vec![],
value.depth,
&value.empty_value,
&value.initial_values,
);
let _root = _tree.root();
});
},
);
}
group.finish();
}
fn bench_cascading_create_dense_mmap_tree(criterion: &mut Criterion) {
let tree_values = [
create_values_for_tree(4),
create_values_for_tree(10),
create_values_for_tree(14),
];
let mut group = criterion.benchmark_group("bench_cascading_create_dense_mmap_tree");
for value in tree_values.iter() {
group.bench_with_input(
BenchmarkId::from_parameter(format!("create_dense_mmap_tree_depth_{}", value.depth)),
value,
|bencher: &mut criterion::Bencher, value| {
bencher.iter(|| {
let tempfile = tempfile::tempfile().unwrap();
let storage: MmapVec<_> = unsafe { MmapVec::create(tempfile).unwrap() };
let _tree: CascadingMerkleTree<Poseidon, _> =
CascadingMerkleTree::new_with_leaves(
storage,
value.depth,
&value.empty_value,
&value.initial_values,
);
let _root = _tree.root();
});
},
);
}
group.finish();
}
fn bench_cascading_restore_dense_mmap_tree(criterion: &mut Criterion) {
let tree_values = vec![
create_values_for_tree(4),
create_values_for_tree(10),
create_values_for_tree(14),
];
let mut group = criterion.benchmark_group("bench_cascading_restore_dense_mmap_tree");
(0..3).zip(tree_values).for_each(|(id, value)| {
let tempfile = tempfile::NamedTempFile::new().unwrap();
let path = tempfile.path();
let storage: MmapVec<_> = unsafe { MmapVec::create_from_path(path).unwrap() };
{
let tree: CascadingMerkleTree<Poseidon, _> = CascadingMerkleTree::new_with_leaves(
storage,
value.depth,
&value.empty_value,
&value.initial_values,
);
let _ = tree.root();
}
group.bench_with_input(
BenchmarkId::from_parameter(format!("restore_dense_mmap_tree_depth_{}", value.depth)),
&(id, value),
|bencher: &mut criterion::Bencher, (_id, value)| {
bencher.iter(|| {
let storage = unsafe { MmapVec::restore_from_path(path).unwrap() };
let _tree: CascadingMerkleTree<Poseidon, _> =
CascadingMerkleTree::restore(storage, value.depth, &value.empty_value)
.unwrap();
let _root = _tree.root();
});
},
);
});
group.finish();
}
fn bench_cascading_dense_tree_reads(criterion: &mut Criterion) {
let tree_value = create_values_for_tree(14);
let tree = CascadingMerkleTree::<Poseidon>::new_with_leaves(
vec![],
tree_value.depth,
&tree_value.empty_value,
&tree_value.initial_values,
);
criterion.bench_function("dense tree reads", |b| {
b.iter(|| {
// read all leaves, and compare to ones in tree value
((1 << (tree_value.depth - 1))..(1 << tree_value.depth)).for_each(|index| {
let _proof = tree.proof(index);
})
})
});
}
fn bench_cascading_dense_mmap_tree_reads(criterion: &mut Criterion) {
let tree_value = create_values_for_tree(14);
let file = tempfile::tempfile().unwrap();
let storage = unsafe { MmapVec::create(file).unwrap() };
let tree = CascadingMerkleTree::<Poseidon, _>::new_with_leaves(
storage,
tree_value.depth,
&tree_value.empty_value,
&tree_value.initial_values,
);
criterion.bench_function("dense mmap tree reads", |b| {
b.iter(|| {
// read all leaves, and compare to ones in tree value
((1 << (tree.depth() - 1))..(1 << tree.depth())).for_each(|index| {
let _proof = tree.proof(index);
})
})
});
}
fn bench_cascading_dense_tree_writes(criterion: &mut Criterion) {
let tree_value = create_values_for_tree(14);
let value = Field::from(123_456);
criterion.bench_functio
gitextract_5m5mxrb4/
├── .github/
│ └── workflows/
│ ├── build-and-test.yml
│ ├── release-crates.yml
│ └── relyance-sci.yml
├── .gitignore
├── CHANGELOG.md
├── Cargo.toml
├── crates/
│ ├── ark-circom/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ ├── circom.rs
│ │ │ ├── ethereum.rs
│ │ │ ├── lib.rs
│ │ │ └── zkey.rs
│ │ └── test-vectors/
│ │ ├── test.zkey
│ │ └── verification_key.json
│ ├── ark-zkey/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ └── src/
│ │ ├── lib.rs
│ │ └── semaphore.16.arkzkey
│ ├── circom-witness-rs/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── LICENSE
│ │ ├── README.md
│ │ ├── build.rs
│ │ ├── include/
│ │ │ └── witness.h
│ │ ├── script/
│ │ │ └── replace.sh
│ │ └── src/
│ │ ├── field.rs
│ │ ├── generate.rs
│ │ ├── graph.rs
│ │ └── lib.rs
│ ├── hasher/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── js/
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ ├── build.mjs
│ │ ├── example/
│ │ │ ├── .gitignore
│ │ │ ├── index.mjs
│ │ │ ├── index.ts
│ │ │ └── package.json
│ │ └── src/
│ │ └── lib.rs
│ ├── keccak/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── keccak.rs
│ │ ├── lib.rs
│ │ └── sha3.rs
│ ├── poseidon/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── constants.rs
│ │ ├── lib.rs
│ │ └── poseidon.rs
│ ├── proof/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── ark.rs
│ │ ├── compression.rs
│ │ ├── lib.rs
│ │ └── packing.rs
│ ├── semaphore/
│ │ ├── Cargo.toml
│ │ ├── README.md
│ │ ├── benches/
│ │ │ ├── cascading_merkle_tree.rs
│ │ │ └── lazy_merkle_tree.rs
│ │ ├── build.rs
│ │ ├── examples/
│ │ │ └── abort/
│ │ │ └── main.rs
│ │ └── src/
│ │ ├── circuit.rs
│ │ ├── field.rs
│ │ ├── hash.rs
│ │ ├── identity.rs
│ │ ├── lib.rs
│ │ ├── packed_proof.rs
│ │ ├── poseidon_tree.rs
│ │ ├── protocol/
│ │ │ ├── authentication.rs
│ │ │ └── mod.rs
│ │ └── util.rs
│ ├── semaphore-depth-config/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── semaphore-depth-macros/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ └── lib.rs
│ ├── storage/
│ │ ├── Cargo.toml
│ │ └── src/
│ │ ├── lib.rs
│ │ └── native/
│ │ ├── mmap_vec.rs
│ │ └── mod.rs
│ ├── trees/
│ │ ├── Cargo.toml
│ │ ├── src/
│ │ │ ├── cascading/
│ │ │ │ ├── mod.rs
│ │ │ │ └── storage_ops.rs
│ │ │ ├── imt/
│ │ │ │ └── mod.rs
│ │ │ ├── lazy/
│ │ │ │ └── mod.rs
│ │ │ ├── lib.rs
│ │ │ └── proof.rs
│ │ └── tests/
│ │ └── equivalent.rs
│ └── utils/
│ ├── Cargo.toml
│ └── src/
│ └── lib.rs
├── cspell.json
├── mit-license.md
├── publish_all.sh
├── release-plz.toml
└── supply-chain/
├── audits.toml
└── config.toml
SYMBOL INDEX (654 symbols across 45 files)
FILE: crates/ark-circom/src/circom.rs
type CircomReduction (line 12) | pub struct CircomReduction;
method instance_map_with_evaluation (line 16) | fn instance_map_with_evaluation<F: PrimeField, D: EvaluationDomain<F>>(
method witness_map_from_matrices (line 23) | fn witness_map_from_matrices<F: PrimeField, D: EvaluationDomain<F>>(
method h_query_scalars (line 90) | fn h_query_scalars<F: PrimeField, D: EvaluationDomain<F>>(
FILE: crates/ark-circom/src/ethereum.rs
type AffineError (line 12) | pub enum AffineError {
type Inputs (line 19) | pub struct Inputs(pub Vec<U256>);
method from (line 22) | fn from(src: &[Fr]) -> Self {
type G1 (line 30) | pub struct G1 {
method as_tuple (line 63) | pub fn as_tuple(&self) -> (U256, U256) {
method from (line 69) | fn from(p: &G1Affine) -> Self {
type Error (line 36) | type Error = AffineError;
method try_from (line 38) | fn try_from(value: G1) -> Result<Self, Self::Error> {
type G1Tup (line 60) | type G1Tup = (U256, U256);
type G2 (line 78) | pub struct G2 {
method as_tuple (line 118) | pub fn as_tuple(&self) -> G2Tup {
method from (line 124) | fn from(p: &G2Affine) -> Self {
type Error (line 84) | type Error = AffineError;
method try_from (line 86) | fn try_from(src: G2) -> Result<G2Affine, AffineError> {
type G2Tup (line 114) | type G2Tup = ([U256; 2], [U256; 2]);
type Proof (line 133) | pub struct Proof {
method as_tuple (line 140) | pub fn as_tuple(&self) -> (G1Tup, G2Tup, G1Tup) {
method from (line 146) | fn from(proof: ark_groth16::Proof<Bn254>) -> Self {
type Error (line 156) | type Error = AffineError;
function try_from (line 158) | fn try_from(src: Proof) -> Result<ark_groth16::Proof<Bn254>, AffineError> {
type VerifyingKey (line 168) | pub struct VerifyingKey {
method as_tuple (line 177) | pub fn as_tuple(&self) -> (G1Tup, G2Tup, G2Tup, G2Tup, Vec<G1Tup>) {
method from (line 189) | fn from(vk: ark_groth16::VerifyingKey<Bn254>) -> Self {
type Error (line 201) | type Error = AffineError;
function try_from (line 203) | fn try_from(src: VerifyingKey) -> Result<ark_groth16::VerifyingKey<Bn254...
function u256_to_point (line 219) | fn u256_to_point<F: PrimeField>(point: U256) -> F {
function point_to_u256 (line 227) | fn point_to_u256<F: PrimeField>(point: F) -> U256 {
function fq (line 239) | fn fq() -> Fq {
function fr (line 243) | fn fr() -> Fr {
function g1 (line 247) | fn g1() -> G1Affine {
function g2 (line 252) | fn g2() -> G2Affine {
function convert_fq (line 258) | fn convert_fq() {
function convert_fr (line 268) | fn convert_fr() {
function convert_g1 (line 278) | fn convert_g1() {
function convert_g2 (line 288) | fn convert_g2() {
function convert_vk (line 298) | fn convert_vk() {
function convert_proof (line 312) | fn convert_proof() {
FILE: crates/ark-circom/src/zkey.rs
type IoResult (line 16) | type IoResult<T> = Result<T, SerializationError>;
type Section (line 19) | struct Section {
function read_zkey (line 26) | pub fn read_zkey<R: Read + Seek>(
type BinFile (line 36) | struct BinFile<'a, R> {
function new (line 46) | fn new(reader: &'a mut R) -> IoResult<Self> {
function proving_key (line 76) | fn proving_key(&mut self) -> IoResult<ProvingKey<Bn254>> {
function get_section (line 108) | fn get_section(&self, id: u32) -> Section {
function groth_header (line 112) | fn groth_header(&mut self) -> IoResult<HeaderGroth> {
function ic (line 118) | fn ic(&mut self, n_public: usize) -> IoResult<Vec<G1Affine>> {
function matrices (line 124) | pub fn matrices(&mut self) -> IoResult<ConstraintMatrices<Fr>> {
function a_query (line 171) | fn a_query(&mut self, n_vars: usize) -> IoResult<Vec<G1Affine>> {
function b_g1_query (line 175) | fn b_g1_query(&mut self, n_vars: usize) -> IoResult<Vec<G1Affine>> {
function b_g2_query (line 179) | fn b_g2_query(&mut self, n_vars: usize) -> IoResult<Vec<G2Affine>> {
function l_query (line 183) | fn l_query(&mut self, n_vars: usize) -> IoResult<Vec<G1Affine>> {
function h_query (line 187) | fn h_query(&mut self, n_vars: usize) -> IoResult<Vec<G1Affine>> {
function g1_section (line 191) | fn g1_section(&mut self, num: usize, section_id: usize) -> IoResult<Vec<...
function g2_section (line 197) | fn g2_section(&mut self, num: usize, section_id: usize) -> IoResult<Vec<...
type ZVerifyingKey (line 205) | pub struct ZVerifyingKey {
method new (line 215) | fn new<R: Read>(reader: &mut R) -> IoResult<Self> {
type HeaderGroth (line 235) | struct HeaderGroth {
method new (line 256) | fn new<R: Read + Seek>(reader: &mut R, section: &Section) -> IoResult<...
method read (line 261) | fn read<R: Read>(mut reader: &mut R) -> IoResult<Self> {
function deserialize_field_fr (line 295) | fn deserialize_field_fr<R: Read>(reader: &mut R) -> IoResult<Fr> {
function deserialize_field (line 301) | fn deserialize_field<R: Read>(reader: &mut R) -> IoResult<Fq> {
function deserialize_field2 (line 307) | pub fn deserialize_field2<R: Read>(reader: &mut R) -> IoResult<Fq2> {
function deserialize_g1 (line 313) | fn deserialize_g1<R: Read>(reader: &mut R) -> IoResult<G1Affine> {
function deserialize_g2 (line 324) | fn deserialize_g2<R: Read>(reader: &mut R) -> IoResult<G2Affine> {
function deserialize_g1_vec (line 335) | fn deserialize_g1_vec<R: Read>(reader: &mut R, n_vars: u32) -> IoResult<...
function deserialize_g2_vec (line 339) | fn deserialize_g2_vec<R: Read>(reader: &mut R, n_vars: u32) -> IoResult<...
function fq_from_str (line 356) | fn fq_from_str(s: &str) -> Fq {
function fq_buf (line 364) | fn fq_buf() -> Vec<u8> {
function g1_buf (line 375) | fn g1_buf() -> Vec<u8> {
function g2_buf (line 388) | fn g2_buf() -> Vec<u8> {
function g1_one (line 401) | fn g1_one() -> G1Affine {
function g2_one (line 409) | fn g2_one() -> G2Affine {
function can_deser_fq (line 432) | fn can_deser_fq() {
function can_deser_g1 (line 439) | fn can_deser_g1() {
function can_deser_g1_vec (line 448) | fn can_deser_g1_vec() {
function can_deser_g2 (line 462) | fn can_deser_g2() {
function can_deser_g2_vec (line 472) | fn can_deser_g2_vec() {
function header (line 486) | fn header() {
function deser_key (line 512) | fn deser_key() {
function deser_vk (line 732) | fn deser_vk() {
function json_to_g1 (line 747) | fn json_to_g1(json: &Value, key: &str) -> G1Affine {
function json_to_g1_vec (line 763) | fn json_to_g1_vec(json: &Value, key: &str) -> Vec<G1Affine> {
function json_to_g2 (line 790) | fn json_to_g2(json: &Value, key: &str) -> G2Affine {
FILE: crates/ark-zkey/src/lib.rs
type SerializableProvingKey (line 14) | pub struct SerializableProvingKey(pub ProvingKey<Bn254>);
type SerializableMatrix (line 17) | pub struct SerializableMatrix<F: Field> {
type SerializableConstraintMatrices (line 22) | pub struct SerializableConstraintMatrices<F: Field> {
function read_arkzkey_from_bytes (line 35) | pub fn read_arkzkey_from_bytes(
function read_proving_key_and_matrices_from_zkey (line 65) | pub fn read_proving_key_and_matrices_from_zkey(
function convert_zkey (line 92) | pub fn convert_zkey(
function test_read_arkzkey_from_bytes (line 120) | fn test_read_arkzkey_from_bytes() -> Result<()> {
FILE: crates/circom-witness-rs/build.rs
function main (line 3) | fn main() {
FILE: crates/circom-witness-rs/include/witness.h
type u64 (line 5) | typedef unsigned long long u64;
type u32 (line 6) | typedef uint32_t u32;
type u8 (line 7) | typedef uint8_t u8;
type Circom_CalcWit (line 9) | struct Circom_CalcWit
FILE: crates/circom-witness-rs/src/field.rs
constant M (line 7) | pub const M: U256 =
constant INV (line 10) | pub const INV: u64 = 14042775128853446655;
constant R (line 12) | pub const R: U256 = uint!(0x0e0a77c19a07df2f666ea36f7879462e36fc76959f60...
type FrElement (line 19) | pub struct FrElement(pub usize);
function print_eval (line 21) | pub fn print_eval() {
function get_graph (line 44) | pub fn get_graph() -> Vec<Node> {
function get_values (line 48) | pub fn get_values() -> Vec<U256> {
function undefined (line 52) | pub fn undefined() -> FrElement {
function constant (line 56) | pub fn constant(c: U256) -> FrElement {
function input (line 70) | pub fn input(i: usize, value: U256) -> FrElement {
function binop (line 84) | fn binop(op: Operation, to: *mut FrElement, a: *const FrElement, b: *con...
function Fr_mul (line 104) | pub fn Fr_mul(to: *mut FrElement, a: *const FrElement, b: *const FrEleme...
function Fr_add (line 109) | pub unsafe fn Fr_add(to: *mut FrElement, a: *const FrElement, b: *const ...
function Fr_sub (line 114) | pub unsafe fn Fr_sub(to: *mut FrElement, a: *const FrElement, b: *const ...
function Fr_copy (line 119) | pub fn Fr_copy(to: *mut FrElement, a: *const FrElement) {
function Fr_copyn (line 126) | pub fn Fr_copyn(to: *mut FrElement, a: *const FrElement, n: usize) {
function create_vec (line 134) | pub fn create_vec(len: usize) -> Vec<FrElement> {
function create_vec_u32 (line 138) | pub fn create_vec_u32(len: usize) -> Vec<u32> {
function generate_position_array (line 142) | pub fn generate_position_array(
function Fr_toInt (line 159) | pub unsafe fn Fr_toInt(a: *const FrElement) -> u64 {
function print (line 172) | pub unsafe fn print(a: *const FrElement) {
function Fr_isTrue (line 176) | pub fn Fr_isTrue(a: *mut FrElement) -> bool {
function Fr_eq (line 189) | pub unsafe fn Fr_eq(to: *mut FrElement, a: *const FrElement, b: *const F...
function Fr_neq (line 193) | pub unsafe fn Fr_neq(to: *mut FrElement, a: *const FrElement, b: *const ...
function Fr_lt (line 197) | pub unsafe fn Fr_lt(to: *mut FrElement, a: *const FrElement, b: *const F...
function Fr_gt (line 201) | pub unsafe fn Fr_gt(to: *mut FrElement, a: *const FrElement, b: *const F...
function Fr_leq (line 205) | pub unsafe fn Fr_leq(to: *mut FrElement, a: *const FrElement, b: *const ...
function Fr_geq (line 209) | pub unsafe fn Fr_geq(to: *mut FrElement, a: *const FrElement, b: *const ...
function Fr_lor (line 213) | pub unsafe fn Fr_lor(to: *mut FrElement, a: *const FrElement, b: *const ...
function Fr_shl (line 217) | pub unsafe fn Fr_shl(to: *mut FrElement, a: *const FrElement, b: *const ...
function Fr_shr (line 221) | pub unsafe fn Fr_shr(to: *mut FrElement, a: *const FrElement, b: *const ...
function Fr_band (line 225) | pub unsafe fn Fr_band(to: *mut FrElement, a: *const FrElement, b: *const...
FILE: crates/circom-witness-rs/src/generate.rs
type InputOutputList (line 16) | pub struct InputOutputList {
type IODef (line 21) | pub struct IODef {
type Circom_Component (line 28) | struct Circom_Component {
type Circom_CalcWit (line 40) | struct Circom_CalcWit {
function create_vec (line 52) | fn create_vec(len: usize) -> Vec<FrElement>;
function create_vec_u32 (line 53) | fn create_vec_u32(len: usize) -> Vec<u32>;
function generate_position_array (line 54) | fn generate_position_array(
function Fr_mul (line 62) | unsafe fn Fr_mul(to: *mut FrElement, a: *const FrElement, b: *const FrEl...
function Fr_add (line 63) | unsafe fn Fr_add(to: *mut FrElement, a: *const FrElement, b: *const FrEl...
function Fr_sub (line 64) | unsafe fn Fr_sub(to: *mut FrElement, a: *const FrElement, b: *const FrEl...
function Fr_copy (line 65) | unsafe fn Fr_copy(to: *mut FrElement, a: *const FrElement);
function Fr_copyn (line 66) | unsafe fn Fr_copyn(to: *mut FrElement, a: *const FrElement, n: usize);
function Fr_shl (line 71) | unsafe fn Fr_shl(to: *mut FrElement, a: *const FrElement, b: *const FrEl...
function Fr_shr (line 72) | unsafe fn Fr_shr(to: *mut FrElement, a: *const FrElement, b: *const FrEl...
function Fr_band (line 73) | unsafe fn Fr_band(to: *mut FrElement, a: *const FrElement, b: *const FrE...
function Fr_eq (line 77) | unsafe fn Fr_eq(to: *mut FrElement, a: *const FrElement, b: *const FrEle...
function Fr_neq (line 78) | unsafe fn Fr_neq(to: *mut FrElement, a: *const FrElement, b: *const FrEl...
function Fr_lt (line 79) | unsafe fn Fr_lt(to: *mut FrElement, a: *const FrElement, b: *const FrEle...
function Fr_gt (line 80) | unsafe fn Fr_gt(to: *mut FrElement, a: *const FrElement, b: *const FrEle...
function Fr_leq (line 81) | unsafe fn Fr_leq(to: *mut FrElement, a: *const FrElement, b: *const FrEl...
function Fr_geq (line 82) | unsafe fn Fr_geq(to: *mut FrElement, a: *const FrElement, b: *const FrEl...
function Fr_isTrue (line 83) | unsafe fn Fr_isTrue(a: *mut FrElement) -> bool;
function Fr_toInt (line 85) | unsafe fn Fr_toInt(a: *mut FrElement) -> u64;
function Fr_lor (line 86) | unsafe fn Fr_lor(to: *mut FrElement, a: *const FrElement, b: *const FrEl...
function print (line 87) | unsafe fn print(a: *mut FrElement);
function run (line 96) | unsafe fn run(ctx: *mut Circom_CalcWit);
function get_size_of_io_map (line 97) | fn get_size_of_io_map() -> u32;
function get_total_signal_no (line 98) | fn get_total_signal_no() -> u32;
function get_main_input_signal_no (line 99) | fn get_main_input_signal_no() -> u32;
function get_main_input_signal_start (line 100) | fn get_main_input_signal_start() -> u32;
function get_number_of_components (line 101) | fn get_number_of_components() -> u32;
function get_size_of_constants (line 102) | fn get_size_of_constants() -> u32;
function get_size_of_input_hashmap (line 103) | fn get_size_of_input_hashmap() -> u32;
function get_size_of_witness (line 104) | fn get_size_of_witness() -> u32;
constant DAT_BYTES (line 108) | const DAT_BYTES: &[u8] = include_bytes!("constants.dat");
function get_input_hash_map (line 110) | pub fn get_input_hash_map() -> Vec<HashSignalInfo> {
function get_witness_to_signal (line 127) | pub fn get_witness_to_signal() -> Vec<usize> {
function get_constants (line 138) | pub fn get_constants() -> Vec<FrElement> {
function get_iosignals (line 165) | pub fn get_iosignals() -> Vec<InputOutputList> {
function build_witness (line 210) | pub fn build_witness() -> color_color_eyre::Result<()> {
FILE: crates/circom-witness-rs/src/graph.rs
function ark_se (line 15) | fn ark_se<S, A: CanonicalSerialize>(a: &A, s: S) -> Result<S::Ok, S::Error>
function ark_de (line 25) | fn ark_de<'de, D, A: CanonicalDeserialize>(data: D) -> Result<A, D::Error>
type Operation (line 35) | pub enum Operation {
method eval (line 62) | pub fn eval(&self, a: U256, b: U256) -> U256 {
method eval_fr (line 82) | pub fn eval_fr(&self, a: Fr, b: Fr) -> Fr {
type Node (line 53) | pub enum Node {
function compute_shl_uint (line 93) | fn compute_shl_uint(a: U256, b: U256) -> U256 {
function compute_shr_uint (line 99) | fn compute_shr_uint(a: U256, b: U256) -> U256 {
function assert_valid (line 106) | fn assert_valid(nodes: &[Node]) {
function optimize (line 115) | pub fn optimize(nodes: &mut Vec<Node>, outputs: &mut [usize]) {
function evaluate (line 125) | pub fn evaluate(nodes: &[Node], inputs: &[U256], outputs: &[usize]) -> V...
function propagate (line 150) | pub fn propagate(nodes: &mut [Node]) {
function tree_shake (line 177) | pub fn tree_shake(nodes: &mut Vec<Node>, outputs: &mut [usize]) {
function random_eval (line 231) | fn random_eval(nodes: &mut [Node]) -> Vec<U256> {
function value_numbering (line 262) | pub fn value_numbering(nodes: &mut [Node], outputs: &mut [usize]) {
function constants (line 295) | pub fn constants(nodes: &mut [Node]) {
function montgomery_form (line 317) | pub fn montgomery_form(nodes: &mut [Node]) {
FILE: crates/circom-witness-rs/src/lib.rs
type HashSignalInfo (line 15) | pub struct HashSignalInfo {
type Graph (line 21) | pub struct Graph {
function fnv1a (line 27) | fn fnv1a(s: &str) -> u64 {
function init_graph (line 37) | pub fn init_graph(graph_bytes: &[u8]) -> color_eyre::Result<Graph> {
function get_inputs_size (line 49) | pub fn get_inputs_size(graph: &Graph) -> usize {
function get_inputs_buffer (line 66) | pub fn get_inputs_buffer(size: usize) -> Vec<U256> {
function get_input_mapping (line 73) | pub fn get_input_mapping(input_list: &Vec<String>, graph: &Graph) -> Has...
function populate_inputs (line 89) | pub fn populate_inputs(
function calculate_witness (line 102) | pub fn calculate_witness(
FILE: crates/hasher/src/lib.rs
type Hasher (line 4) | pub trait Hasher {
method hash_node (line 9) | fn hash_node(left: &Self::Hash, right: &Self::Hash) -> Self::Hash;
type Hash (line 15) | pub trait Hash: Pod + Eq + Send + Sync {}
FILE: crates/js/build.mjs
function main (line 19) | async function main() {
FILE: crates/js/src/lib.rs
function compress_proof (line 12) | pub fn compress_proof(
function decompress_proof (line 40) | pub fn decompress_proof(
function from_vec (line 63) | fn from_vec<const N: usize>(proof: Vec<String>) -> Result<[U256; N], JsE...
function to_js_array (line 81) | fn to_js_array<const N: usize>(arr: [U256; N]) -> Array {
FILE: crates/keccak/src/keccak.rs
type Keccak256 (line 3) | pub struct Keccak256;
type Hash (line 6) | type Hash = [u8; 32];
method hash_node (line 8) | fn hash_node(left: &Self::Hash, right: &Self::Hash) -> Self::Hash {
FILE: crates/keccak/src/sha3.rs
type Sha3_256 (line 4) | pub struct Sha3_256;
type Hash (line 7) | type Hash = [u8; 32];
method hash_node (line 9) | fn hash_node(left: &Self::Hash, right: &Self::Hash) -> Self::Hash {
FILE: crates/poseidon/src/lib.rs
type Poseidon (line 7) | pub struct Poseidon;
type Hash (line 10) | type Hash = U256;
method hash_node (line 12) | fn hash_node(left: &Self::Hash, right: &Self::Hash) -> Self::Hash {
FILE: crates/poseidon/src/poseidon.rs
function hash1 (line 78) | pub fn hash1(value: U256) -> U256 {
function hash2 (line 108) | pub fn hash2(left: U256, right: U256) -> U256 {
function test_hash1 (line 143) | fn test_hash1() {
function test_hash2 (line 151) | fn test_hash2() {
FILE: crates/proof/src/ark.rs
method from (line 8) | fn from(proof: ArkProof<Bn<Config>>) -> Self {
type Error (line 16) | type Error = AffineError;
function try_from (line 18) | fn try_from(proof: Proof) -> Result<Self, AffineError> {
FILE: crates/proof/src/compression.rs
constant P (line 15) | pub const P: U256 =
constant ONE (line 19) | pub const ONE: U256 = uint! { 1_U256 };
constant TWO (line 20) | pub const TWO: U256 = uint! { 2_U256 };
constant THREE (line 21) | pub const THREE: U256 = uint! { 3_U256 };
constant FOUR (line 22) | pub const FOUR: U256 = uint! { 4_U256 };
type CompressedProof (line 33) | pub struct CompressedProof(pub U256, pub (U256, U256), pub U256);
method from_flat (line 36) | pub const fn from_flat(flat: [U256; 4]) -> Self {
method flatten (line 42) | pub const fn flatten(self) -> [U256; 4] {
function compress_proof (line 48) | pub fn compress_proof(proof: Proof) -> Option<CompressedProof> {
function decompress_proof (line 65) | pub fn decompress_proof(compressed: CompressedProof) -> Option<Proof> {
function compress_g1 (line 84) | pub fn compress_g1((x, y): G1) -> Option<U256> {
function decompress_g1 (line 101) | pub fn decompress_g1(c: U256) -> Option<G1> {
function compress_g2 (line 122) | pub fn compress_g2(([x0, x1], [y0, y1]): G2) -> Option<(U256, U256)> {
function decompress_g2 (line 166) | pub fn decompress_g2((c0, c1): (U256, U256)) -> Option<G2> {
function sqrt_fp (line 203) | fn sqrt_fp(a: U256) -> Option<U256> {
function sqrt_fp2 (line 212) | fn sqrt_fp2(a0: U256, a1: U256, hint: bool) -> Option<(U256, U256)> {
function is_square_fp (line 232) | fn is_square_fp(a: U256) -> bool {
function invert_fp (line 241) | fn invert_fp(a: U256) -> Option<U256> {
function neg_fp (line 251) | fn neg_fp(a: U256) -> U256 {
function inversion (line 260) | fn inversion() {
function square_root_fp (line 269) | fn square_root_fp() {
function square_root_fp_2 (line 278) | fn square_root_fp_2() {
function proof_compression (line 329) | fn proof_compression() {
function g1_compression (line 358) | fn g1_compression() {
function g2_compression (line 376) | fn g2_compression() {
function deser (line 400) | fn deser() {
FILE: crates/proof/src/lib.rs
type G1 (line 12) | pub type G1 = (U256, U256);
type G2 (line 15) | pub type G2 = ([U256; 2], [U256; 2]);
type Proof (line 19) | pub struct Proof(pub G1, pub G2, pub G1);
method from_flat (line 22) | pub const fn from_flat(flat: [U256; 8]) -> Self {
method flatten (line 27) | pub const fn flatten(self) -> [U256; 8] {
function deser (line 38) | fn deser() {
FILE: crates/proof/src/packing.rs
type PackedProof (line 18) | pub struct PackedProof(pub [u8; 256]);
method from (line 21) | fn from(proof: Proof) -> Self {
method deserialize (line 78) | fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, ...
method from (line 41) | fn from(proof: PackedProof) -> Self {
method fmt (line 53) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type Err (line 64) | type Err = hex::FromHexError;
method from_str (line 66) | fn from_str(s: &str) -> Result<Self, Self::Err> {
method serialize (line 72) | fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Er...
function test_serializing_proof_into_packed_proof (line 90) | fn test_serializing_proof_into_packed_proof() {
function test_parse_from_string (line 110) | fn test_parse_from_string() {
function test_parse_from_string_without_prefix (line 130) | fn test_parse_from_string_without_prefix() {
function test_serialize_proof_to_json (line 151) | fn test_serialize_proof_to_json() {
function test_serialize_proof_to_json_real_numbers (line 170) | fn test_serialize_proof_to_json_real_numbers() {
function test_deserialize_proof_from_json (line 204) | fn test_deserialize_proof_from_json() {
function test_invalid_parsing (line 236) | fn test_invalid_parsing() {
FILE: crates/semaphore-depth-config/src/lib.rs
function get_supported_depth_count (line 3) | pub const fn get_supported_depth_count() -> usize {
function gen_supported_depths (line 21) | const fn gen_supported_depths() -> [usize; get_supported_depth_count()] {
function get_supported_depths (line 44) | pub fn get_supported_depths() -> &'static [usize] {
function get_depth_index (line 49) | pub const fn get_depth_index(depth: usize) -> Option<usize> {
FILE: crates/semaphore-depth-macros/src/lib.rs
function test_all_depths (line 43) | pub fn test_all_depths(_attr: TokenStream, item: TokenStream) -> TokenSt...
type ArrayForDepthsInput (line 64) | struct ArrayForDepthsInput {
type MacroArgs (line 70) | struct MacroArgs {
method tokens (line 89) | fn tokens(&self) -> proc_macro2::TokenStream {
method parse (line 75) | fn parse(input: ParseStream) -> syn::Result<Self> {
type IdentReplacer (line 95) | struct IdentReplacer(Ident, syn::Expr);
method visit_expr_mut (line 98) | fn visit_expr_mut(&mut self, expr: &mut syn::Expr) {
method parse (line 120) | fn parse(input: ParseStream) -> syn::Result<Self> {
function array_for_depths (line 147) | pub fn array_for_depths(input: TokenStream) -> TokenStream {
FILE: crates/semaphore/benches/cascading_merkle_tree.rs
type TreeValues (line 22) | struct TreeValues<H: Hasher> {
function bench_cascading_proof_from_hash (line 28) | fn bench_cascading_proof_from_hash(criterion: &mut Criterion) {
function bench_cascading_validate (line 52) | fn bench_cascading_validate(criterion: &mut Criterion) {
function bench_cascading_create_dense_tree (line 82) | fn bench_cascading_create_dense_tree(criterion: &mut Criterion) {
function bench_cascading_create_dense_mmap_tree (line 111) | fn bench_cascading_create_dense_mmap_tree(criterion: &mut Criterion) {
function bench_cascading_restore_dense_mmap_tree (line 143) | fn bench_cascading_restore_dense_mmap_tree(criterion: &mut Criterion) {
function bench_cascading_dense_tree_reads (line 183) | fn bench_cascading_dense_tree_reads(criterion: &mut Criterion) {
function bench_cascading_dense_mmap_tree_reads (line 203) | fn bench_cascading_dense_mmap_tree_reads(criterion: &mut Criterion) {
function bench_cascading_dense_tree_writes (line 225) | fn bench_cascading_dense_tree_writes(criterion: &mut Criterion) {
function bench_cascading_dense_mmap_tree_writes (line 248) | fn bench_cascading_dense_mmap_tree_writes(criterion: &mut Criterion) {
function create_values_for_tree (line 273) | fn create_values_for_tree(depth: usize) -> TreeValues<Poseidon> {
FILE: crates/semaphore/benches/lazy_merkle_tree.rs
type TreeValues (line 19) | struct TreeValues<H: Hasher> {
function bench_create_dense_tree (line 26) | fn bench_create_dense_tree(criterion: &mut Criterion) {
function bench_create_dense_mmap_tree (line 55) | fn bench_create_dense_mmap_tree(criterion: &mut Criterion) {
function bench_restore_dense_mmap_tree (line 90) | fn bench_restore_dense_mmap_tree(criterion: &mut Criterion) {
function bench_dense_tree_reads (line 134) | fn bench_dense_tree_reads(criterion: &mut Criterion) {
function bench_dense_mmap_tree_reads (line 154) | fn bench_dense_mmap_tree_reads(criterion: &mut Criterion) {
function bench_dense_tree_writes (line 178) | fn bench_dense_tree_writes(criterion: &mut Criterion) {
function bench_dense_mmap_tree_writes (line 201) | fn bench_dense_mmap_tree_writes(criterion: &mut Criterion) {
function create_values_for_tree (line 228) | fn create_values_for_tree(depth: usize) -> TreeValues<Poseidon> {
FILE: crates/semaphore/build.rs
constant SEMAPHORE_FILES_PATH (line 8) | const SEMAPHORE_FILES_PATH: &str = "semaphore_files";
constant SEMAPHORE_DOWNLOAD_URL (line 9) | const SEMAPHORE_DOWNLOAD_URL: &str = "https://www.trusted-setup-pse.org/...
function download_and_store_binary (line 11) | fn download_and_store_binary(url: &str, path: impl AsRef<Path>) -> Resul...
function create_arkzkey (line 23) | fn create_arkzkey(path: PathBuf) -> Result<PathBuf> {
function build_circuit (line 41) | fn build_circuit(depth: usize) -> Result<()> {
function main (line 91) | fn main() -> Result<()> {
FILE: crates/semaphore/examples/abort/main.rs
function main (line 24) | async fn main() -> Result<()> {
function run (line 45) | fn run() -> Result<()> {
function run_test (line 56) | fn run_test(prefix: &str) -> Result<u32> {
function cascade_init (line 87) | fn cascade_init() -> Result<()> {
function cascade_restore (line 113) | fn cascade_restore() -> Result<()> {
function lazy_init (line 127) | fn lazy_init() -> Result<()> {
function lazy_restore (line 151) | fn lazy_restore() -> Result<()> {
FILE: crates/semaphore/src/circuit.rs
constant ZKEY_BYTES (line 10) | const ZKEY_BYTES: [&[u8]; get_supported_depth_count()] =
constant GRAPH_BYTES (line 13) | const GRAPH_BYTES: [&[u8]; get_supported_depth_count()] =
function zkey (line 23) | pub fn zkey(depth: usize) -> &'static (ProvingKey<Bn254>, ConstraintMatr...
function graph (line 29) | pub fn graph(depth: usize) -> &'static [u8] {
FILE: crates/semaphore/src/field.rs
type Field (line 8) | pub type Field = U256;
constant MODULUS (line 11) | pub const MODULUS: Field =
function hash_to_field (line 20) | pub fn hash_to_field(data: &[u8]) -> Field {
FILE: crates/semaphore/src/hash.rs
type Hash (line 13) | pub struct Hash(pub [u8; 32]);
method from_bytes_be (line 17) | pub const fn from_bytes_be(bytes: [u8; 32]) -> Self {
method as_bytes_be (line 22) | pub const fn as_bytes_be(&self) -> &[u8; 32] {
method from (line 36) | fn from(u256: U256) -> Self {
method from (line 43) | fn from(vec: Vec<u8>) -> Self {
method deserialize (line 102) | fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, ...
method from (line 29) | fn from(hash: &Hash) -> Self {
method from (line 52) | fn from(hash: Hash) -> Self {
method from (line 58) | fn from(hash: &Hash) -> Self {
method fmt (line 64) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
method fmt (line 72) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
type Err (line 83) | type Err = hex::FromHexError;
method from_str (line 85) | fn from_str(s: &str) -> Result<Self, Self::Err> {
method serialize (line 93) | fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Er...
function test_serialize (line 115) | fn test_serialize() {
function test_deserialize (line 131) | fn test_deserialize() {
FILE: crates/semaphore/src/identity.rs
type Identity (line 8) | pub struct Identity {
method from_seed (line 35) | pub fn from_seed(seed: &[u8]) -> Self {
method from_secret (line 44) | pub fn from_secret(secret: &mut [u8], trapdoor_seed: Option<&[u8]>) ->...
method from_hashed_secret (line 52) | pub fn from_hashed_secret(secret_hex: &mut [u8; 64], trapdoor_seed: Op...
method secret_hash (line 62) | pub fn secret_hash(&self) -> Field {
method commitment (line 67) | pub fn commitment(&self) -> Field {
function derive_field (line 16) | fn derive_field(seed_hex: &[u8; 64], suffix: &[u8]) -> Field {
function seed_hex (line 23) | pub fn seed_hex(seed: &[u8]) -> [u8; 64] {
FILE: crates/semaphore/src/lib.rs
function test_field_serde (line 29) | fn test_field_serde() {
function test_end_to_end (line 36) | fn test_end_to_end(
function test_auth_flow (line 76) | fn test_auth_flow(depth: usize) {
function test_single_impl (line 105) | fn test_single_impl(depth: usize) {
function test_parallel_impl (line 112) | fn test_parallel_impl(depth: usize) {
FILE: crates/semaphore/src/poseidon_tree.rs
type PoseidonTree (line 7) | pub type PoseidonTree = MerkleTree<Poseidon>;
type LazyPoseidonTree (line 9) | pub type LazyPoseidonTree = LazyMerkleTree<Poseidon>;
type Branch (line 10) | pub type Branch = semaphore_rs_trees::Branch<<Poseidon as Hasher>::Hash>;
type Proof (line 11) | pub type Proof = semaphore_rs_trees::InclusionProof<Poseidon>;
FILE: crates/semaphore/src/protocol/authentication.rs
function empty_hashes (line 9) | fn empty_hashes(depth: usize) -> Vec<Field> {
function authentication_merkle_proof (line 21) | fn authentication_merkle_proof(depth: usize) -> InclusionProof<semaphore...
function authentication_root (line 25) | fn authentication_root(depth: usize, id_commitment: Field) -> Field {
function generate_proof (line 29) | pub fn generate_proof(
function verify_proof (line 39) | pub fn verify_proof(
function test_round_trip (line 68) | fn test_round_trip(depth: usize) {
FILE: crates/semaphore/src/protocol/mod.rs
function warmup_for_verification (line 37) | pub fn warmup_for_verification(tree_depth: usize) {
function merkle_proof_to_vec (line 43) | fn merkle_proof_to_vec(proof: &InclusionProof<Poseidon>) -> Vec<Field> {
function generate_nullifier_hash (line 55) | pub fn generate_nullifier_hash(identity: &Identity, external_nullifier: ...
type ProofError (line 60) | pub enum ProofError {
function generate_proof (line 78) | pub fn generate_proof(
function generate_proof_rng (line 98) | pub fn generate_proof_rng(
function generate_proof_rs (line 115) | fn generate_proof_rs(
function generate_witness (line 142) | pub fn generate_witness(
function path_index (line 173) | pub fn path_index(proof: &InclusionProof<Poseidon>) -> Vec<Field> {
function verify_proof (line 190) | pub fn verify_proof(
function arb_proof (line 226) | fn arb_proof(seed: u64, depth: usize) -> Proof {
function test_proof_cast_roundtrip (line 258) | fn test_proof_cast_roundtrip(depth: usize) {
function test_proof_serialize (line 266) | fn test_proof_serialize(depth: usize) {
FILE: crates/storage/src/lib.rs
type GenericStorage (line 3) | pub trait GenericStorage<T>:
method push (line 6) | fn push(&mut self, value: T);
method extend_from_slice (line 8) | fn extend_from_slice(&mut self, slice: &[T]);
method clear (line 10) | fn clear(&mut self);
function push (line 14) | fn push(&mut self, value: T) {
function extend_from_slice (line 18) | fn extend_from_slice(&mut self, slice: &[T]) {
function clear (line 22) | fn clear(&mut self) {
FILE: crates/storage/src/native/mmap_vec.rs
constant META_SIZE (line 9) | const META_SIZE: usize = std::mem::size_of::<usize>();
type MmapVec (line 11) | pub struct MmapVec<T> {
function create_from_path (line 27) | pub unsafe fn create_from_path(file_path: impl AsRef<Path>) -> color_eyr...
function create (line 46) | pub unsafe fn create(file: File) -> color_eyre::Result<Self> {
function restore_from_path (line 62) | pub unsafe fn restore_from_path(file_path: impl AsRef<Path>) -> color_ey...
function restore (line 82) | pub unsafe fn restore(file: File) -> color_eyre::Result<Self> {
function clear (line 121) | pub fn clear(&mut self) {
function push (line 125) | pub fn push(&mut self, v: T) {
function extend_from_slice (line 138) | pub fn extend_from_slice(&mut self, slice: &[T]) {
function resize (line 151) | pub fn resize(&mut self, new_capacity: usize) {
function set_storage_len (line 183) | fn set_storage_len(&mut self, new_len: usize) {
function storage_len (line 189) | fn storage_len(&self) -> usize {
function capacity_slice (line 193) | fn capacity_slice(&self) -> &[T] {
function capacity_slice_mut (line 197) | fn capacity_slice_mut(&mut self) -> &mut [T] {
function extend (line 206) | fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
type Target (line 217) | type Target = [T];
method deref (line 219) | fn deref(&self) -> &Self::Target {
method deref_mut (line 228) | fn deref_mut(&mut self) -> &mut Self::Target {
function fmt (line 238) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
function test_capacity_push (line 255) | fn test_capacity_push() {
function test_capacity_extend (line 304) | fn test_capacity_extend() {
function test_capacity_create (line 339) | fn test_capacity_create() {
function test_capacity_create_from_path (line 367) | fn test_capacity_create_from_path() {
function test_capacity_restore (line 395) | fn test_capacity_restore() {
function test_capacity_restore_from_path (line 423) | fn test_capacity_restore_from_path() {
function test_mmap_vec (line 450) | fn test_mmap_vec() {
FILE: crates/storage/src/native/mod.rs
function push (line 8) | fn push(&mut self, value: T) {
function extend_from_slice (line 12) | fn extend_from_slice(&mut self, slice: &[T]) {
function clear (line 16) | fn clear(&mut self) {
FILE: crates/trees/src/cascading/mod.rs
type CascadingMerkleTree (line 41) | pub struct CascadingMerkleTree<H, S = Vec<<H as Hasher>::Hash>>
function restore (line 60) | pub fn restore(
function restore_unchecked (line 75) | pub fn restore_unchecked(
function new (line 117) | pub fn new(storage: S, depth: usize, empty_value: &H::Hash) -> Cascading...
function new_with_leaves (line 123) | pub fn new_with_leaves(
function depth (line 153) | pub const fn depth(&self) -> usize {
function empty_value (line 159) | pub const fn empty_value(&self) -> H::Hash {
function root (line 165) | pub const fn root(&self) -> H::Hash {
function num_leaves (line 174) | pub fn num_leaves(&self) -> usize {
function set_leaf (line 182) | pub fn set_leaf(&mut self, leaf: usize, value: H::Hash) {
function push (line 203) | pub fn push(&mut self, leaf: H::Hash) -> Result<()> {
function proof (line 243) | pub fn proof(&self, leaf: usize) -> InclusionProof<H> {
function proof_from_hash (line 273) | pub fn proof_from_hash(&self, leaf: H::Hash) -> Option<InclusionProof<H>> {
function verify (line 280) | pub fn verify(&self, value: H::Hash, proof: &InclusionProof<H>) -> bool {
function get_node (line 290) | pub fn get_node(&self, depth: usize, offset: usize) -> H::Hash {
function get_leaf (line 307) | pub fn get_leaf(&self, leaf: usize) -> H::Hash {
function get_leaf_from_hash (line 314) | pub fn get_leaf_from_hash(&self, hash: H::Hash) -> Option<usize> {
function leaves (line 341) | pub fn leaves(&self) -> impl Iterator<Item = H::Hash> + '_ {
function sparse_column (line 350) | fn sparse_column(depth: usize, empty_value: &H::Hash) -> Vec<H::Hash> {
function recompute_root (line 362) | fn recompute_root(&mut self) -> H::Hash {
function compute_from_storage_tip (line 370) | fn compute_from_storage_tip(&self, depth: usize) -> H::Hash {
function validate (line 382) | pub fn validate(&self) -> Result<()> {
function extend_from_slice (line 401) | pub fn extend_from_slice(&mut self, leaves: &[H::Hash]) {
type TestHasher (line 502) | pub struct TestHasher;
type Hash (line 504) | type Hash = usize;
method hash_node (line 506) | fn hash_node(left: &Self::Hash, right: &Self::Hash) -> Self::Hash {
function debug_tree (line 511) | pub fn debug_tree<H, S>(tree: &CascadingMerkleTree<H, S>)
function debug_storage (line 521) | pub fn debug_storage<H, S>(storage: &S)
function test_index_from_leaf (line 544) | fn test_index_from_leaf() {
function test_index_height_offset (line 555) | fn test_index_height_offset() {
function test_parent (line 583) | fn test_parent() {
function test_sibling (line 610) | fn test_sibling() {
function test_children (line 638) | fn test_children() {
function test_invalid_storage (line 665) | fn test_invalid_storage() {
function test_hash_too_small (line 677) | fn test_hash_too_small() {
function test_min_sized_tree (line 692) | fn test_min_sized_tree() {
function test_set_leaf (line 702) | fn test_set_leaf() {
function test_set_leaf_extend (line 717) | fn test_set_leaf_extend() {
function test_zero_depth_tree (line 733) | fn test_zero_depth_tree() {
function test_odd_leaves (line 742) | fn test_odd_leaves() {
function test_even_leaves (line 760) | fn test_even_leaves() {
function test_no_leaves (line 779) | fn test_no_leaves() {
function test_sparse_column (line 797) | fn test_sparse_column() {
function test_compute_root (line 815) | fn test_compute_root() {
function test_get_node (line 834) | fn test_get_node() {
function test_get_leaf_from_hash (line 865) | fn test_get_leaf_from_hash() {
function test_row_indices (line 881) | fn test_row_indices() {
function test_row (line 911) | fn test_row() {
function test_proof_from_hash (line 935) | fn test_proof_from_hash() {
function test_leaves (line 1005) | fn test_leaves() {
type Hash (line 1033) | type Hash = <Keccak256 as Hasher>::Hash;
function test_extend_from_slice_keccak (line 1036) | fn test_extend_from_slice_keccak() -> color_eyre::Result<()> {
function test_push_beyond_depth (line 1065) | fn test_push_beyond_depth() {
function test_init_beyond_depth (line 1075) | fn test_init_beyond_depth() {
function test_extend_beyond_depth (line 1081) | fn test_extend_beyond_depth() {
function test_push (line 1088) | fn test_push() {
function test_extend_from_slice (line 1101) | fn test_extend_from_slice() {
function test_extend_from_slice_2 (line 1116) | fn test_extend_from_slice_2() {
function test_vec_realloc_speed (line 1140) | fn test_vec_realloc_speed() {
function test_mmap_realloc_speed (line 1157) | fn test_mmap_realloc_speed() {
function test_restore_from_cache (line 1184) | fn test_restore_from_cache() -> color_eyre::Result<()> {
FILE: crates/trees/src/cascading/storage_ops.rs
type StorageOps (line 12) | pub trait StorageOps<H>:
method populate_with_leaves (line 24) | fn populate_with_leaves(
method leaves (line 57) | fn leaves(&self) -> impl Iterator<Item = H::Hash> + '_ {
method row_indices (line 63) | fn row_indices(&self, height: usize) -> impl Iterator<Item = usize> + ...
method row (line 74) | fn row(&self, height: usize) -> impl Iterator<Item = H::Hash> + Send +...
method storage_root (line 79) | fn storage_root(&self) -> H::Hash {
method storage_depth (line 84) | fn storage_depth(&self) -> usize {
method set_num_leaves (line 89) | fn set_num_leaves(&mut self, amount: usize) {
method num_leaves (line 94) | fn num_leaves(&self) -> usize {
method increment_num_leaves (line 99) | fn increment_num_leaves(&mut self, amount: usize) {
method propagate_up (line 105) | fn propagate_up(&mut self, mut index: usize) -> Option<()> {
method validate_const (line 120) | fn validate_const(&self) -> Result<()> {
method validate (line 133) | fn validate(&self, empty_value: &H::Hash) -> Result<()> {
function subtree_depth (line 182) | pub fn subtree_depth<H>(storage_slice: &[H]) -> usize {
function sibling (line 191) | pub fn sibling(i: usize) -> Branch<usize> {
function parent (line 209) | pub fn parent(i: usize) -> usize {
function index_from_leaf (line 220) | pub fn index_from_leaf(leaf: usize) -> usize {
function leaf_from_index (line 224) | pub fn leaf_from_index(index: usize) -> usize {
function index_height_offset (line 230) | pub fn index_height_offset(height: usize, offset: usize) -> usize {
function children (line 241) | pub fn children(i: usize) -> Option<(usize, usize)> {
function init_subtree_with_leaves (line 285) | pub fn init_subtree_with_leaves<H>(
function extend_subtree_with_leaves (line 337) | pub fn extend_subtree_with_leaves<H>(
function propagate_partial_subtree (line 382) | pub fn propagate_partial_subtree<H>(subtree: &mut [H::Hash], mut range: ...
function sparse_fill_partial_subtree (line 437) | pub fn sparse_fill_partial_subtree<H>(
function row_indices (line 470) | fn row_indices(height: usize) -> impl Iterator<Item = usize> + Send {
function subtree_depth_width (line 488) | pub fn subtree_depth_width<H>(storage_slice: &[H]) -> (usize, usize) {
function test_is_storage_ops (line 508) | fn test_is_storage_ops<S>(_s: &S)
function test_mmap_vec_is_storage_ops (line 516) | fn test_mmap_vec_is_storage_ops(s: MmapVec<<Keccak256 as Hasher>::Hash>) {
function test_sparse_fill_partial_subtree (line 521) | fn test_sparse_fill_partial_subtree() {
function test_propagate_partial_subtree_noop_on_empty_range (line 530) | fn test_propagate_partial_subtree_noop_on_empty_range() {
function test_sparse_fill_partial_subtree_noop_on_empty_range (line 538) | fn test_sparse_fill_partial_subtree_noop_on_empty_range() {
FILE: crates/trees/src/imt/mod.rs
type MerkleTree (line 17) | pub struct MerkleTree<H>
function parent (line 33) | const fn parent(index: usize) -> Option<usize> {
function left_child (line 42) | const fn left_child(index: usize) -> usize {
function depth (line 46) | const fn depth(index: usize) -> usize {
function new (line 64) | pub fn new(depth: usize, initial_leaf: H::Hash) -> Self {
function num_leaves (line 89) | pub fn num_leaves(&self) -> usize {
function root (line 94) | pub fn root(&self) -> H::Hash {
function set (line 98) | pub fn set(&mut self, leaf: usize, hash: H::Hash) {
function set_range (line 102) | pub fn set_range<I: IntoIterator<Item = H::Hash>>(&mut self, start: usiz...
function update_nodes (line 117) | fn update_nodes(&mut self, start: usize, end: usize) {
function proof (line 129) | pub fn proof(&self, leaf: usize) -> Option<InclusionProof<H>> {
function verify (line 148) | pub fn verify(&self, hash: H::Hash, proof: &InclusionProof<H>) -> bool {
function leaves (line 153) | pub fn leaves(&self) -> &[H::Hash] {
function leaf_index (line 161) | pub fn leaf_index(&self) -> usize {
function root (line 170) | pub fn root(&self, hash: H::Hash) -> H::Hash {
function parent_of (line 196) | fn parent_of(index: usize) -> Option<usize> {
function left_child_of (line 204) | fn left_child_of(index: usize) -> usize {
function depth_of (line 213) | fn depth_of(index: usize) -> usize {
function empty_keccak (line 218) | fn empty_keccak(depth: usize) -> [u8; 32] {
function simple_poseidon (line 225) | fn simple_poseidon() {
FILE: crates/trees/src/lazy/mod.rs
type VersionMarker (line 16) | pub trait VersionMarker {}
type Canonical (line 18) | pub struct Canonical;
type Derived (line 21) | pub struct Derived;
type LazyMerkleTree (line 38) | pub struct LazyMerkleTree<H: Hasher, V: VersionMarker = Derived> {
function new (line 51) | pub fn new(depth: usize, empty_value: H::Hash) -> LazyMerkleTree<H, Cano...
function new_with_dense_prefix (line 60) | pub fn new_with_dense_prefix(
function new_with_dense_prefix_with_initial_values (line 74) | pub fn new_with_dense_prefix_with_initial_values(
function new_mmapped_with_dense_prefix_with_init_values (line 93) | pub fn new_mmapped_with_dense_prefix_with_init_values(
function attempt_dense_mmap_restore (line 116) | pub fn attempt_dense_mmap_restore(
function depth (line 135) | pub const fn depth(&self) -> usize {
function root (line 141) | pub fn root(&self) -> H::Hash {
function update (line 149) | pub fn update(&self, index: usize, value: &H::Hash) -> LazyMerkleTree<H,...
function proof (line 160) | pub fn proof(&self, index: usize) -> InclusionProof<H> {
function verify (line 166) | pub fn verify(&self, value: H::Hash, proof: &InclusionProof<H>) -> bool {
function get_leaf (line 172) | pub fn get_leaf(&self, index: usize) -> H::Hash {
function leaves (line 177) | pub fn leaves(&self) -> impl Iterator<Item = H::Hash> + '_ {
function update_with_mutation (line 204) | pub fn update_with_mutation(self, index: usize, value: &H::Hash) -> Self {
function derived (line 214) | pub fn derived(&self) -> LazyMerkleTree<H, Derived> {
method clone (line 227) | fn clone(&self) -> Self {
type AnyTree (line 235) | enum AnyTree<H: Hasher> {
function new (line 247) | fn new(depth: usize, empty_value: H::Hash) -> Self {
function new_with_dense_prefix_with_initial_values (line 251) | fn new_with_dense_prefix_with_initial_values(
function new_with_dense_prefix (line 269) | fn new_with_dense_prefix(depth: usize, prefix_depth: usize, empty_value:...
function new_mmapped_with_dense_prefix_with_init_values (line 283) | fn new_mmapped_with_dense_prefix_with_init_values(
function try_restore_dense_mmap_tree_state (line 303) | fn try_restore_dense_mmap_tree_state(
function depth (line 323) | const fn depth(&self) -> usize {
function root (line 332) | fn root(&self) -> H::Hash {
function proof (line 341) | fn proof(&self, index: usize) -> InclusionProof<H> {
function write_proof (line 354) | fn write_proof(&self, index: usize, path: &mut Vec<Branch<H::Hash>>) {
function update_with_mutation_condition (line 363) | fn update_with_mutation_condition(
function get_leaf (line 385) | fn get_leaf(&self, index: usize) -> H::Hash {
method clone (line 400) | fn clone(&self) -> Self {
function from (line 411) | fn from(tree: EmptyTree<H>) -> Self {
function from (line 417) | fn from(tree: SparseTree<H>) -> Self {
function from (line 423) | fn from(tree: DenseTree<H>) -> Self {
function from (line 429) | fn from(tree: DenseMMapTree<H>) -> Self {
type EmptyTree (line 434) | struct EmptyTree<H: Hasher> {
method clone (line 440) | fn clone(&self) -> Self {
function new (line 454) | fn new(depth: usize, empty_value: H::Hash) -> Self {
function write_proof (line 467) | fn write_proof(&self, index: usize, path: &mut Vec<Branch<H::Hash>>) {
function update_with_mutation_condition (line 480) | fn update_with_mutation_condition(
function alloc_sparse (line 491) | fn alloc_sparse(&self) -> SparseTree<H> {
function alloc_dense (line 504) | fn alloc_dense(&self) -> DenseTree<H> {
function root (line 523) | fn root(&self) -> H::Hash {
function get_leaf (line 527) | fn get_leaf(&self) -> H::Hash {
type Children (line 532) | struct Children<H: Hasher> {
method clone (line 538) | fn clone(&self) -> Self {
type SparseTree (line 546) | struct SparseTree<H: Hasher> {
type Turn (line 553) | enum Turn {
function get_turn_at_depth (line 558) | const fn get_turn_at_depth(index: usize, depth: usize) -> Turn {
function clear_turn_at_depth (line 566) | const fn clear_turn_at_depth(index: usize, depth: usize) -> usize {
function from (line 575) | fn from(children: Children<H>) -> Self {
method clone (line 597) | fn clone(&self) -> Self {
function new (line 611) | fn new(left: AnyTree<H>, right: AnyTree<H>) -> Self {
function new_leaf (line 620) | const fn new_leaf(value: H::Hash) -> Self {
function write_proof (line 628) | fn write_proof(&self, index: usize, path: &mut Vec<Branch<H::Hash>>) {
function update_with_mutation_condition (line 642) | fn update_with_mutation_condition(
function root (line 675) | fn root(&self) -> H::Hash {
function get_leaf (line 679) | fn get_leaf(&self, index: usize) -> H::Hash {
type DenseTree (line 695) | struct DenseTree<H: Hasher> {
method clone (line 702) | fn clone(&self) -> Self {
function vec_from_values (line 716) | fn vec_from_values(values: &[H::Hash], empty_value: &H::Hash, depth: usi...
function new_with_values (line 747) | fn new_with_values(values: &[H::Hash], empty_value: &H::Hash, depth: usi...
function with_ref (line 757) | fn with_ref<F, R>(&self, fun: F) -> R
function write_proof (line 771) | fn write_proof(&self, index: usize, path: &mut Vec<Branch<H::Hash>>) {
function get_leaf (line 775) | fn get_leaf(&self, index: usize) -> H::Hash {
function update_with_mutation_condition (line 782) | fn update_with_mutation_condition(
function update_with_mutation (line 796) | fn update_with_mutation(&self, index: usize, value: &H::Hash) {
function root (line 809) | fn root(&self) -> H::Hash {
type DenseTreeRef (line 814) | struct DenseTreeRef<'a, H: Hasher> {
function from (line 822) | fn from(value: DenseTreeRef<H>) -> Self {
function from (line 832) | fn from(value: DenseTreeRef<H>) -> Self {
function root (line 842) | fn root(&self) -> H::Hash {
function left (line 846) | const fn left(&self) -> DenseTreeRef<'_, H> {
function right (line 855) | const fn right(&self) -> DenseTreeRef<'_, H> {
function write_proof (line 864) | fn write_proof(&self, index: usize, path: &mut Vec<Branch<H::Hash>>) {
function update (line 878) | fn update(&self, index: usize, hash: &H::Hash) -> SparseTree<H> {
type DenseMMapTree (line 913) | struct DenseMMapTree<H: Hasher> {
method clone (line 920) | fn clone(&self) -> Self {
function new_with_values (line 940) | fn new_with_values(
function attempt_restore (line 973) | fn attempt_restore(
function with_ref (line 992) | fn with_ref<F, R>(&self, fun: F) -> R
function write_proof (line 1006) | fn write_proof(&self, index: usize, path: &mut Vec<Branch<H::Hash>>) {
function get_leaf (line 1010) | fn get_leaf(&self, index: usize) -> H::Hash {
function update_with_mutation_condition (line 1017) | fn update_with_mutation_condition(
function update_with_mutation (line 1031) | fn update_with_mutation(&self, index: usize, value: &H::Hash) {
function root (line 1044) | fn root(&self) -> H::Hash {
type DenseTreeMMapRef (line 1049) | struct DenseTreeMMapRef<'a, H: Hasher> {
function from (line 1057) | fn from(value: DenseTreeMMapRef<H>) -> Self {
function from (line 1067) | fn from(value: DenseTreeMMapRef<H>) -> Self {
function root (line 1077) | fn root(&self) -> H::Hash {
function left (line 1081) | const fn left(&self) -> DenseTreeMMapRef<'_, H> {
function right (line 1090) | const fn right(&self) -> DenseTreeMMapRef<'_, H> {
function write_proof (line 1099) | fn write_proof(&self, index: usize, path: &mut Vec<Branch<H::Hash>>) {
function update (line 1113) | fn update(&self, index: usize, hash: &H::Hash) -> SparseTree<H> {
type MmapMutWrapper (line 1148) | pub struct MmapMutWrapper<H: Hasher> {
function new_from_storage (line 1172) | pub fn new_from_storage(
function attempt_restore (line 1225) | pub fn attempt_restore(
type Target (line 1265) | type Target = [H::Hash];
method deref (line 1267) | fn deref(&self) -> &Self::Target {
method deref_mut (line 1277) | fn deref_mut(&mut self) -> &mut Self::Target {
type DenseMMapError (line 1283) | pub enum DenseMMapError {
type TestHasher (line 1304) | struct TestHasher;
type Hash (line 1307) | type Hash = u64;
method hash_node (line 1309) | fn hash_node(left: &Self::Hash, right: &Self::Hash) -> Self::Hash {
function test_updates_in_sparse (line 1315) | fn test_updates_in_sparse() {
function test_updates_in_dense (line 1328) | fn test_updates_in_dense() {
function test_mutable_updates_in_dense (line 1341) | fn test_mutable_updates_in_dense() {
function test_mutable_updates_in_dense_with_dense_prefix (line 1386) | fn test_mutable_updates_in_dense_with_dense_prefix() {
function test_proof (line 1432) | fn test_proof() {
function test_giant_tree_with_initial_vals (line 1464) | fn test_giant_tree_with_initial_vals() {
function test_giant_trees (line 1488) | fn test_giant_trees() {
function test_dense_mmap_tree (line 1527) | fn test_dense_mmap_tree() {
FILE: crates/trees/src/proof.rs
type InclusionProof (line 12) | pub struct InclusionProof<H>(pub Vec<Branch<H::Hash>>)
type Branch (line 18) | pub enum Branch<T> {
method serialize (line 31) | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
function deserialize (line 44) | fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
function into_inner (line 56) | pub fn into_inner(self) -> T {
method fmt (line 65) | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
FILE: crates/trees/tests/equivalent.rs
constant DEPTH (line 8) | const DEPTH: usize = 20;
constant DENSE_PREFIX (line 9) | const DENSE_PREFIX: usize = 16;
constant NUM_LEAVES (line 11) | const NUM_LEAVES: usize = 100;
type HashType (line 13) | type HashType = Poseidon;
constant EMPTY_VALUE (line 14) | const EMPTY_VALUE: U256 = U256::ZERO;
function equivalent (line 17) | fn equivalent() {
FILE: crates/utils/src/lib.rs
function keccak256 (line 11) | pub fn keccak256(bytes: &[u8]) -> [u8; 32] {
function bytes_to_hex (line 19) | pub fn bytes_to_hex<const N: usize, const M: usize>(bytes: &[u8; N]) -> ...
function serialize_bytes (line 30) | pub fn serialize_bytes<const N: usize, const M: usize, S: Serializer>(
function bytes_from_hex (line 50) | pub fn bytes_from_hex<const N: usize>(s: &str) -> Result<[u8; N], hex::F...
function trim_hex_prefix (line 58) | fn trim_hex_prefix(str: &str) -> &str {
function deserialize_bytes (line 63) | pub fn deserialize_bytes<'de, const N: usize, D: Deserializer<'de>>(
function test_serialize_bytes_hex (line 113) | fn test_serialize_bytes_hex() {
function test_serialize_bytes_bin (line 122) | fn test_serialize_bytes_bin() {
Condensed preview — 93 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (434K chars).
[
{
"path": ".github/workflows/build-and-test.yml",
"chars": 4485,
"preview": "name: Build and Test\non: push\n\nenv:\n RUST_VERSION: 1.94.0\n\njobs:\n dependabot-dependency-review:\n runs-on: ubuntu-la"
},
{
"path": ".github/workflows/release-crates.yml",
"chars": 944,
"preview": "name: Release Crates\n\non:\n push:\n branches:\n - main\n workflow_dispatch:\n\n# Prevent concurrent release runs — r"
},
{
"path": ".github/workflows/relyance-sci.yml",
"chars": 535,
"preview": "name: Relyance SCI Scan\n\non:\n schedule:\n - cron: \"30 0 * * *\"\n workflow_dispatch:\n\npermissions:\n contents: read\n\nj"
},
{
"path": ".gitignore",
"chars": 88,
"preview": "/target\n*.profraw\nsnarkfiles_tmp\nsemaphore_files\n.idea\nlcov.info\n*.DS_Store\n\nCargo.lock\n"
},
{
"path": "CHANGELOG.md",
"chars": 1093,
"preview": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Change"
},
{
"path": "Cargo.toml",
"chars": 3766,
"preview": "[workspace]\nmembers = [\"crates/*\"]\nresolver = \"2\"\n\n[workspace.package]\nversion = \"0.5.3\"\nedition = \"2021\"\nhomepage = \"ht"
},
{
"path": "crates/ark-circom/Cargo.toml",
"chars": 789,
"preview": "[package]\nname = \"semaphore-rs-ark-circom\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nl"
},
{
"path": "crates/ark-circom/src/circom.rs",
"chars": 4117,
"preview": "use ark_ff::PrimeField;\nuse ark_groth16::r1cs_to_qap::{evaluate_constraint, LibsnarkReduction, R1CSToQAP};\nuse ark_poly:"
},
{
"path": "crates/ark-circom/src/ethereum.rs",
"chars": 8359,
"preview": "//! Helpers for converting Arkworks types to U256-tuples as expected by the\n//! Solidity Groth16 Verifier smart contract"
},
{
"path": "crates/ark-circom/src/lib.rs",
"chars": 107,
"preview": "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",
"chars": 29585,
"preview": "use ark_ff::{BigInteger256, PrimeField};\nuse ark_relations::r1cs::ConstraintMatrices;\nuse ark_serialize::{CanonicalDeser"
},
{
"path": "crates/ark-circom/test-vectors/verification_key.json",
"chars": 2925,
"preview": "{\n \"protocol\": \"groth16\",\n \"curve\": \"bn128\",\n \"nPublic\": 1,\n \"vk_alpha_1\": [\n \"2049119280539048529915300977359453494018"
},
{
"path": "crates/ark-zkey/.gitignore",
"chars": 414,
"preview": "# Generated by Cargo\n# will have compiled files and executables\ndebug/\ntarget/\n\n# Remove Cargo.lock from gitignore if cr"
},
{
"path": "crates/ark-zkey/Cargo.toml",
"chars": 556,
"preview": "[package]\nname = \"semaphore-rs-ark-zkey\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nlic"
},
{
"path": "crates/ark-zkey/README.md",
"chars": 2478,
"preview": "# ark-zkey\n\nLibrary to read `zkey` faster by serializing to `arkworks` friendly format.\n\nSee https://github.com/oskarth/"
},
{
"path": "crates/ark-zkey/src/lib.rs",
"chars": 4788,
"preview": "use std::fs::File;\nuse std::io::BufReader;\nuse std::path::PathBuf;\n\nuse ark_bn254::{Bn254, Fr};\nuse ark_ff::Field;\nuse a"
},
{
"path": "crates/circom-witness-rs/.gitignore",
"chars": 61,
"preview": "/target\n*_cpp\n*.new\n.DS_Store\ncircuit.cc\nconstants.dat\n.idea\n"
},
{
"path": "crates/circom-witness-rs/Cargo.toml",
"chars": 736,
"preview": "[package]\nname = \"semaphore-rs-witness\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nlice"
},
{
"path": "crates/circom-witness-rs/LICENSE",
"chars": 1070,
"preview": "MIT License\n\nCopyright (c) 2023 Philipp Sippl\n\nPermission is hereby granted, free of charge, to any person obtaining a c"
},
{
"path": "crates/circom-witness-rs/README.md",
"chars": 3172,
"preview": "# 🏎️ circom-witness-rs\n\n## Description\n\nThis crate provides a fast witness generator for Circom circuits, serving as a d"
},
{
"path": "crates/circom-witness-rs/build.rs",
"chars": 1395,
"preview": "use std::{env, fs, path::Path, process::Command};\n\nfn main() {\n if cfg!(feature = \"build-witness\") {\n let witn"
},
{
"path": "crates/circom-witness-rs/include/witness.h",
"chars": 433,
"preview": "#pragma once\n#include \"rust/cxx.h\"\n#include <memory>\n\ntypedef unsigned long long u64;\ntypedef uint32_t u32;\ntypedef uint"
},
{
"path": "crates/circom-witness-rs/script/replace.sh",
"chars": 2734,
"preview": "#!/bin/sh\n\n# Check for input file\nif [ \"$#\" -ne 1 ]; then\n echo \"Usage: $0 <filename>\"\n exit 1\nfi\n\nfilename=$(base"
},
{
"path": "crates/circom-witness-rs/src/field.rs",
"chars": 6428,
"preview": "#![allow(unused, non_snake_case)]\n\nuse crate::graph::{Node, Operation};\nuse ruint::{aliases::U256, uint};\nuse std::{ptr,"
},
{
"path": "crates/circom-witness-rs/src/generate.rs",
"chars": 10299,
"preview": "#![allow(non_snake_case)]\n\nuse crate::field::{self, *};\nuse crate::graph::{self, Node};\nuse crate::HashSignalInfo;\nuse b"
},
{
"path": "crates/circom-witness-rs/src/graph.rs",
"chars": 9549,
"preview": "use std::{\n collections::HashMap,\n ops::{Shl, Shr},\n};\n\nuse crate::field::M;\nuse ark_bn254::Fr;\nuse ark_ff::PrimeF"
},
{
"path": "crates/circom-witness-rs/src/lib.rs",
"chars": 3033,
"preview": "mod field;\npub mod graph;\n\n#[cfg(feature = \"build-witness\")]\npub mod generate;\n\nuse std::collections::HashMap;\n\nuse ruin"
},
{
"path": "crates/hasher/Cargo.toml",
"chars": 318,
"preview": "[package]\nname = \"semaphore-rs-hasher\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nlicen"
},
{
"path": "crates/hasher/src/lib.rs",
"chars": 543,
"preview": "use bytemuck::Pod;\n\n/// Hash types, values and algorithms for a Merkle tree\npub trait Hasher {\n /// Type of the leaf "
},
{
"path": "crates/js/.gitignore",
"chars": 88,
"preview": "/target\n**/*.rs.bk\nCargo.lock\nbin/\npkg/\npkg-nodejs/\npkg-bundler/\npkg-web/\nwasm-pack.log\n"
},
{
"path": "crates/js/Cargo.toml",
"chars": 652,
"preview": "[package]\nname = \"semaphore-rs-js\"\nversion = \"0.3.0\"\nedition.workspace = true\npublish = false # independently versioned;"
},
{
"path": "crates/js/README.md",
"chars": 673,
"preview": "# WASM bindings for semaphore-rs related functionality\n\nThis crate exposes semaphore-rs functionality to WASM. Currently"
},
{
"path": "crates/js/build.mjs",
"chars": 6380,
"preview": "#!/usr/bin/env node\n// build.mjs\n// This script performs the following steps:\n// 1. Uses `cargo metadata` to extract pac"
},
{
"path": "crates/js/example/.gitignore",
"chars": 14,
"preview": "node_modules/\n"
},
{
"path": "crates/js/example/index.mjs",
"chars": 885,
"preview": "import { compressProof, decompressProof } from \"semaphore-rs-js\";\n\nconst proof = [\n \"0x2d77679b613036865f4518894c80691c"
},
{
"path": "crates/js/example/index.ts",
"chars": 971,
"preview": "import { compressProof, decompressProof } from \"semaphore-rs-js\";\n\nconst proof: [string, string, string, string, string,"
},
{
"path": "crates/js/example/package.json",
"chars": 259,
"preview": "{\n \"name\": \"testing\",\n \"version\": \"1.0.0\",\n \"main\": \"index.js\",\n \"scripts\": {\n \"test\": \"echo \\\"Error: no test spe"
},
{
"path": "crates/js/src/lib.rs",
"chars": 2911,
"preview": "use js_sys::Array;\nuse ruint::aliases::U256;\nuse semaphore_rs_proof::{compression::CompressedProof, Proof};\nuse wasm_bin"
},
{
"path": "crates/keccak/Cargo.toml",
"chars": 446,
"preview": "[package]\nname = \"semaphore-rs-keccak\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nlicen"
},
{
"path": "crates/keccak/src/keccak.rs",
"chars": 420,
"preview": "use semaphore_rs_hasher::Hasher;\nuse tiny_keccak::{Hasher as _, Keccak};\npub struct Keccak256;\n\nimpl Hasher for Keccak25"
},
{
"path": "crates/keccak/src/lib.rs",
"chars": 56,
"preview": "pub mod keccak;\n\n#[cfg(feature = \"sha3\")]\npub mod sha3;\n"
},
{
"path": "crates/keccak/src/sha3.rs",
"chars": 429,
"preview": "use semaphore_rs_hasher::Hasher;\nuse tiny_keccak::{Hasher as _, Sha3};\n\npub struct Sha3_256;\n\nimpl Hasher for Sha3_256 {"
},
{
"path": "crates/poseidon/Cargo.toml",
"chars": 432,
"preview": "[package]\nname = \"semaphore-rs-poseidon\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nlic"
},
{
"path": "crates/poseidon/src/constants.rs",
"chars": 28641,
"preview": "use ruint::aliases::U256;\nuse ruint::uint;\n\nuint! {\n\npub const M1: [[U256; 2]; 2] = [\n [\n 0x066f6f85d6f68a85ec"
},
{
"path": "crates/poseidon/src/lib.rs",
"chars": 289,
"preview": "use ruint::aliases::U256;\nuse semaphore_rs_hasher::Hasher;\n\npub mod constants;\npub mod poseidon;\n\npub struct Poseidon;\n\n"
},
{
"path": "crates/poseidon/src/poseidon.rs",
"chars": 3984,
"preview": "use ark_bn254::Fr;\nuse ark_ff::{Field, Zero};\nuse once_cell::sync::Lazy;\nuse ruint::aliases::U256;\n\nuse crate::constants"
},
{
"path": "crates/proof/Cargo.toml",
"chars": 844,
"preview": "[package]\nname = \"semaphore-rs-proof\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nlicens"
},
{
"path": "crates/proof/src/ark.rs",
"chars": 1274,
"preview": "use super::Proof;\nuse ark_bn254::Config;\nuse ark_ec::bn::Bn;\nuse ark_groth16::Proof as ArkProof;\nuse semaphore_rs_ark_ci"
},
{
"path": "crates/proof/src/compression.rs",
"chars": 13664,
"preview": "//! Groth16 proof compression\n//!\n//! Ported from https://github.com/worldcoin/world-id-state-bridge/blob/main/src/Semap"
},
{
"path": "crates/proof/src/lib.rs",
"chars": 1162,
"preview": "use ruint::aliases::U256;\nuse serde::{Deserialize, Serialize};\n\n#[cfg(feature = \"ark\")]\nmod ark;\n\npub mod compression;\n\n"
},
{
"path": "crates/proof/src/packing.rs",
"chars": 11513,
"preview": "use std::{\n fmt::Display,\n str::{from_utf8, FromStr},\n};\n\nuse alloy_core::sol_types::{\n sol_data::{FixedArray, "
},
{
"path": "crates/semaphore/Cargo.toml",
"chars": 2329,
"preview": "[package]\nname = \"semaphore-rs\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nlicense.work"
},
{
"path": "crates/semaphore/README.md",
"chars": 1965,
"preview": "# 🦀 semaphore-rs\n\nRust support library for using [semaphore](https://github.com/appliedzkp/semaphore). It's mostly a Rus"
},
{
"path": "crates/semaphore/benches/cascading_merkle_tree.rs",
"chars": 9151,
"preview": "use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion};\nuse semaphore_rs::Field;\nuse semaph"
},
{
"path": "crates/semaphore/benches/lazy_merkle_tree.rs",
"chars": 7603,
"preview": "use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion};\nuse semaphore_rs::poseidon_tree::La"
},
{
"path": "crates/semaphore/build.rs",
"chars": 3052,
"preview": "use std::fs::{create_dir, create_dir_all, File};\nuse std::path::{absolute, Path, PathBuf};\n\nuse color_eyre::eyre::Result"
},
{
"path": "crates/semaphore/examples/abort/main.rs",
"chars": 4671,
"preview": "use color_eyre::Result;\nuse itertools::Itertools;\nuse rand::Rng;\nuse ruint::aliases::U256;\nuse semaphore_rs_hasher::Hash"
},
{
"path": "crates/semaphore/src/circuit.rs",
"chars": 1275,
"preview": "#![allow(unused)]\n\nuse ark_bn254::{Bn254, Fr};\nuse ark_groth16::ProvingKey;\nuse ark_relations::r1cs::ConstraintMatrices;"
},
{
"path": "crates/semaphore/src/field.rs",
"chars": 886,
"preview": "use ruint::{aliases::U256, uint};\nuse semaphore_rs_utils::keccak256;\n\n/// An element of the BN254 scalar field Fr.\n///\n/"
},
{
"path": "crates/semaphore/src/hash.rs",
"chars": 4284,
"preview": "use core::{\n fmt::{Debug, Display},\n str,\n str::FromStr,\n};\nuse num_bigint::{BigInt, Sign};\nuse ruint::aliases:"
},
{
"path": "crates/semaphore/src/identity.rs",
"chars": 2198,
"preview": "use sha2::{Digest, Sha256};\nuse zeroize::Zeroize;\n\nuse crate::field::MODULUS;\nuse crate::Field;\n\n#[derive(Clone, Partial"
},
{
"path": "crates/semaphore/src/lib.rs",
"chars": 3732,
"preview": "#![doc = include_str!(\"../README.md\")]\n\nmod circuit;\nmod field;\npub mod hash;\npub mod identity;\npub mod packed_proof;\npu"
},
{
"path": "crates/semaphore/src/packed_proof.rs",
"chars": 91,
"preview": "// Re-export for backwards compatibility\npub use semaphore_rs_proof::packing::PackedProof;\n"
},
{
"path": "crates/semaphore/src/poseidon_tree.rs",
"chars": 467,
"preview": "use semaphore_rs_hasher::Hasher;\nuse semaphore_rs_poseidon::Poseidon;\nuse semaphore_rs_trees::imt::MerkleTree;\n#[cfg(not"
},
{
"path": "crates/semaphore/src/protocol/authentication.rs",
"chars": 2501,
"preview": "use crate::{\n identity::Identity,\n protocol::{Proof, ProofError},\n Field,\n};\nuse semaphore_rs_poseidon::poseido"
},
{
"path": "crates/semaphore/src/protocol/mod.rs",
"chars": 10921,
"preview": "use std::collections::HashMap;\n\nuse ark_bn254::Fr;\nuse ark_ff::PrimeField;\nuse ark_groth16::{prepare_verifying_key, Grot"
},
{
"path": "crates/semaphore/src/util.rs",
"chars": 0,
"preview": ""
},
{
"path": "crates/semaphore-depth-config/Cargo.toml",
"chars": 336,
"preview": "[package]\nname = \"semaphore-rs-depth-config\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true"
},
{
"path": "crates/semaphore-depth-config/src/lib.rs",
"chars": 1383,
"preview": "#![allow(unused)]\n\npub const fn get_supported_depth_count() -> usize {\n let mut res = 0;\n #[cfg(feature = \"depth_1"
},
{
"path": "crates/semaphore-depth-macros/Cargo.toml",
"chars": 633,
"preview": "[package]\nname = \"semaphore-rs-depth-macros\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true"
},
{
"path": "crates/semaphore-depth-macros/src/lib.rs",
"chars": 4717,
"preview": "use proc_macro::TokenStream;\nuse quote::{format_ident, quote};\nuse semaphore_rs_depth_config::get_supported_depths;\nuse "
},
{
"path": "crates/storage/Cargo.toml",
"chars": 456,
"preview": "[package]\nname = \"semaphore-rs-storage\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nlice"
},
{
"path": "crates/storage/src/lib.rs",
"chars": 651,
"preview": "use std::ops::{Deref, DerefMut};\n\npub trait GenericStorage<T>:\n Deref<Target = [T]> + DerefMut<Target = [T]> + Extend"
},
{
"path": "crates/storage/src/native/mmap_vec.rs",
"chars": 14995,
"preview": "use std::fs::{File, OpenOptions};\nuse std::ops::{Deref, DerefMut};\nuse std::path::Path;\n\nuse bytemuck::Pod;\nuse color_ey"
},
{
"path": "crates/storage/src/native/mod.rs",
"chars": 353,
"preview": "mod mmap_vec;\n\nuse bytemuck::Pod;\n\npub use mmap_vec::MmapVec;\n\nimpl<T: Send + Sync + Pod> super::GenericStorage<T> for M"
},
{
"path": "crates/trees/Cargo.toml",
"chars": 1432,
"preview": "[package]\nname = \"semaphore-rs-trees\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nlicens"
},
{
"path": "crates/trees/src/cascading/mod.rs",
"chars": 39685,
"preview": "use std::fmt::Debug;\n\nuse bytemuck::Pod;\nuse color_eyre::eyre::{ensure, Result};\nuse derive_where::derive_where;\nuse sem"
},
{
"path": "crates/trees/src/cascading/storage_ops.rs",
"chars": 15888,
"preview": "use std::ops::{Deref, DerefMut, Range};\n\nuse bytemuck::Pod;\nuse color_eyre::eyre::{bail, ensure};\nuse color_eyre::Result"
},
{
"path": "crates/trees/src/imt/mod.rs",
"chars": 6862,
"preview": "//! Implements basic binary Merkle trees\n\nuse std::fmt::Debug;\nuse std::iter::{once, repeat_n, successors};\n\nuse bytemuc"
},
{
"path": "crates/trees/src/lazy/mod.rs",
"chars": 49368,
"preview": "use std::fs::OpenOptions;\nuse std::io::Write;\nuse std::iter::{once, repeat_n, successors};\nuse std::ops::{Deref, DerefMu"
},
{
"path": "crates/trees/src/lib.rs",
"chars": 139,
"preview": "pub mod cascading;\npub mod imt;\n#[cfg(not(target_arch = \"wasm32\"))]\npub mod lazy;\npub mod proof;\n\npub use proof::{Branch"
},
{
"path": "crates/trees/src/proof.rs",
"chars": 1815,
"preview": "use std::fmt::Debug;\n\nuse derive_where::derive_where;\nuse semaphore_rs_hasher::Hasher;\nuse serde::{Deserialize, Serializ"
},
{
"path": "crates/trees/tests/equivalent.rs",
"chars": 2621,
"preview": "use rand::{thread_rng, Rng};\nuse ruint::aliases::U256;\nuse semaphore_rs_poseidon::Poseidon;\nuse semaphore_rs_trees::casc"
},
{
"path": "crates/utils/Cargo.toml",
"chars": 438,
"preview": "[package]\nname = \"semaphore-rs-utils\"\nversion.workspace = true\nedition.workspace = true\nhomepage.workspace = true\nlicens"
},
{
"path": "crates/utils/src/lib.rs",
"chars": 4392,
"preview": "use core::{\n fmt::{Formatter, Result as FmtResult},\n str,\n};\nuse serde::{\n de::{Error as DeError, Visitor},\n "
},
{
"path": "cspell.json",
"chars": 462,
"preview": "{\n \"version\": \"0.2\",\n \"ignorePaths\": [],\n \"dictionaryDefinitions\": [],\n \"dictionaries\": [],\n \"words\": [\n "
},
{
"path": "mit-license.md",
"chars": 1096,
"preview": "# The MIT License (MIT)\n\nCopyright © 2021-2023 Worldcoin Foundation\n\nPermission is hereby granted, free of charge, to an"
},
{
"path": "publish_all.sh",
"chars": 523,
"preview": "#!/usr/bin/env bash\n\nset -e\n\ncargo publish -p semaphore-rs-utils\ncargo publish -p semaphore-rs-ark-circom\ncargo publish "
},
{
"path": "release-plz.toml",
"chars": 2575,
"preview": "[workspace]\n# Automatically create GitHub releases for each package.\n# This creates tags like: semaphore-rs-v0.5.1, sema"
},
{
"path": "supply-chain/audits.toml",
"chars": 2045,
"preview": "\n# cargo-vet audits file\n\n[audits]\n\n[[trusted.aho-corasick]]\ncriteria = \"safe-to-deploy\"\nuser-id = 189 # Andrew Gallant "
},
{
"path": "supply-chain/config.toml",
"chars": 23935,
"preview": "\n# cargo-vet config file\n\n[cargo-vet]\nversion = \"0.9\"\n\n[imports.bytecodealliance]\nurl = \"https://raw.githubusercontent.c"
}
]
// ... and 2 more files (download for full content)
About this extraction
This page contains the full source code of the worldcoin/semaphore-rs GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 93 files (403.0 KB), approximately 126.2k tokens, and a symbol index with 654 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.