diff --git a/.cargo/config.toml b/.cargo/config.toml
new file mode 100644
index 000000000..983e89ba0
--- /dev/null
+++ b/.cargo/config.toml
@@ -0,0 +1,5 @@
+[target.wasm32-unknown-unknown]
+runner = 'wasm-bindgen-test-runner'
+
+[target.wasm32-wasip2]
+runner = 'wasmtime'
diff --git a/.github/scripts/wasm-target-test-build.sh b/.github/scripts/wasm-target-test-build.sh
deleted file mode 100644
index 3c42427cd..000000000
--- a/.github/scripts/wasm-target-test-build.sh
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/bin/sh
-
-GIT_ROOT=$(pwd)
-
-cd /tmp
-
-# create test project
-cargo new foobar
-cd foobar
-
-# set rust-toolchain same as "sonobe"
-cp "${GIT_ROOT}/rust-toolchain" .
-
-# add wasm32-* targets
-rustup target add wasm32-unknown-unknown wasm32-wasip1
-
-# add dependencies
-cargo add --path "${GIT_ROOT}/frontends" --features wasm, parallel
-cargo add --path "${GIT_ROOT}/folding-schemes" --features parallel
-cargo add getrandom --features wasm_js --target wasm32-unknown-unknown
-
-# test build for wasm32-* targets
-cargo build --release --target wasm32-unknown-unknown
-cargo build --release --target wasm32-wasip1
-# Emscripten would require to fetch the `emcc` tooling. Hence we don't build the lib as a dep for it.
-# cargo build --release --target wasm32-unknown-emscripten
-
-# delete test project
-cd ../
-rm -rf foobar
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a4b70d0cc..9ca52f736 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,5 +1,6 @@
name: CI Check
on:
+ workflow_dispatch:
merge_group:
pull_request:
push:
@@ -36,44 +37,45 @@ concurrency:
jobs:
test:
if: github.event.pull_request.draft == false
- name: Test
+ name: Test ${{ matrix.target }} (${{ matrix.features }})
runs-on: ubuntu-latest
strategy:
matrix:
- feature_set: [basic]
include:
- - feature_set: basic
- features: --features default,light-test
+ # x64: both parallel and no-parallel
+ - target: x86_64-unknown-linux-gnu
+ features: parallel
+ args: "--features parallel"
+ - target: x86_64-unknown-linux-gnu
+ features: no-parallel
+ args: ""
+ # wasm: no-parallel only
+ - target: wasm32-unknown-unknown
+ features: no-parallel
+ args: ""
+ - target: wasm32-wasip2
+ features: no-parallel
+ args: ""
steps:
- - uses: actions/checkout@v2
- - uses: actions-rs/toolchain@v1
- - uses: noir-lang/noirup@v0.1.3
- with:
- toolchain: 0.36.0
- - name: Download Circom
- run: |
- mkdir -p $HOME/bin
- curl -sSfL https://github.com/iden3/circom/releases/download/v2.1.6/circom-linux-amd64 -o $HOME/bin/circom
- chmod +x $HOME/bin/circom
- echo "$HOME/bin" >> $GITHUB_PATH
- - name: Download solc
- run: |
- curl -sSfL https://github.com/ethereum/solidity/releases/download/v0.8.4/solc-static-linux -o /usr/local/bin/solc
- chmod +x /usr/local/bin/solc
- - name: Execute compile.sh to generate .r1cs and .wasm from .circom
- run: ./experimental-frontends/src/circom/test_folder/compile.sh
- - name: Execute compile.sh to generate .json from noir
- run: ./experimental-frontends/src/noir/test_folder/compile.sh
- - name: Run tests
- uses: actions-rs/cargo@v1
- with:
- command: test
- args: --release --workspace --no-default-features ${{ matrix.features }}
- - name: Run Doc-tests
- uses: actions-rs/cargo@v1
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
with:
- command: test
- args: --doc
+ targets: ${{ matrix.target }}
+ - uses: Swatinem/rust-cache@v2
+ - name: Install wasm-bindgen-cli
+ if: matrix.target == 'wasm32-unknown-unknown'
+ run: cargo install wasm-bindgen-cli
+ - name: Install wasmtime-cli
+ if: matrix.target == 'wasm32-wasip2'
+ run: cargo install wasmtime-cli
+ - name: Test sonobe-primitives
+ run: cargo test --release -p sonobe-primitives --target ${{ matrix.target }} ${{ matrix.args }}
+ - name: Test sonobe-fs
+ run: cargo test --release -p sonobe-fs --target ${{ matrix.target }} ${{ matrix.args }}
+ - name: Test sonobe-ivc
+ run: cargo test --release -p sonobe-ivc --target ${{ matrix.target }} ${{ matrix.args }}
+ - name: Test documentation examples
+ run: cargo test --doc --target ${{ matrix.target }} ${{ matrix.args }}
build:
if: github.event.pull_request.draft == false
@@ -82,79 +84,21 @@ jobs:
strategy:
matrix:
target:
+ - x86_64-unknown-linux-gnu
- wasm32-unknown-unknown
- - wasm32-wasip1
- # Ignoring until clear usage is required
- # - wasm32-unknown-emscripten
-
- steps:
- - uses: actions/checkout@v3
- - uses: actions-rs/toolchain@v1
- with:
- override: false
- default: true
- - name: Add target
- run: rustup target add ${{ matrix.target }}
- - name: Wasm-compat experimental-frontends build
- uses: actions-rs/cargo@v1
- with:
- command: build
- args: -p experimental-frontends --no-default-features --target ${{ matrix.target }} --features "wasm, parallel"
- - name: Wasm-compat folding-schemes build
- uses: actions-rs/cargo@v1
- with:
- command: build
- args: -p folding-schemes --no-default-features --target ${{ matrix.target }} --features "default,light-test"
- - name: Run wasm-compat script
- run: |
- chmod +x .github/scripts/wasm-target-test-build.sh
- .github/scripts/wasm-target-test-build.sh
- shell: bash
-
- examples:
- if: github.event.pull_request.draft == false
- name: Run examples & examples tests
- runs-on: ubuntu-latest
+ - wasm32-wasip2
steps:
- - uses: actions/checkout@v2
- - uses: actions-rs/toolchain@v1
- - uses: noir-lang/noirup@v0.1.3
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
with:
- toolchain: 0.36.0
- - name: Download Circom
- run: |
- mkdir -p $HOME/bin
- curl -sSfL https://github.com/iden3/circom/releases/download/v2.1.6/circom-linux-amd64 -o $HOME/bin/circom
- chmod +x $HOME/bin/circom
- echo "$HOME/bin" >> $GITHUB_PATH
- - name: Download solc
- run: |
- curl -sSfL https://github.com/ethereum/solidity/releases/download/v0.8.4/solc-static-linux -o /usr/local/bin/solc
- chmod +x /usr/local/bin/solc
- - name: Execute compile.sh to generate .r1cs and .wasm from .circom
- run: ./experimental-frontends/src/circom/test_folder/compile.sh
- - name: Execute compile.sh to generate .json from noir
- run: ./experimental-frontends/src/noir/test_folder/compile.sh
- - name: Run examples tests
- run: cargo test --examples
- - name: Run examples
- run: cargo run --release --example 2>&1 | grep -E '^ ' | xargs -n1 cargo run --release --example
-
- # run the benchmarks with the flag `--no-run` to ensure that they compile,
- # but without executing them.
- bench:
- if: github.event.pull_request.draft == false
- name: Bench compile
- timeout-minutes: 30
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- - uses: actions-rs/toolchain@v1
+ targets: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
- - uses: actions-rs/cargo@v1
- with:
- command: bench
- args: -p folding-schemes --no-run
+ - name: Build sonobe-primitives
+ run: cargo build -p sonobe-primitives --target ${{ matrix.target }}
+ - name: Build sonobe-fs
+ run: cargo build -p sonobe-fs --target ${{ matrix.target }}
+ - name: Build sonobe-ivc
+ run: cargo build -p sonobe-ivc --target ${{ matrix.target }}
fmt:
if: github.event.pull_request.draft == false
@@ -162,41 +106,33 @@ jobs:
timeout-minutes: 30
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
- - uses: actions-rs/toolchain@v1
- - uses: Swatinem/rust-cache@v2
- - run: rustup component add rustfmt
- - uses: actions-rs/cargo@v1
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
with:
- command: fmt
- args: --all --check
+ components: rustfmt
+ - uses: Swatinem/rust-cache@v2
+ - name: Run rustfmt
+ run: cargo fmt --all --check
clippy:
if: github.event.pull_request.draft == false
- name: Clippy lint checks
+ name: Clippy (${{ matrix.target }})
runs-on: ubuntu-latest
strategy:
matrix:
- feature_set: [basic, wasm]
- include:
- - feature_set: basic
- features: --features default
- # We only want to test `experimental-frontends` package with `wasm` feature.
- - feature_set: wasm
- features: -p experimental-frontends --features wasm,parallel --target wasm32-unknown-unknown
+ target:
+ - x86_64-unknown-linux-gnu
+ - wasm32-unknown-unknown
+ - wasm32-wasip2
steps:
- - uses: actions/checkout@v2
- - uses: actions-rs/toolchain@v1
+ - uses: actions/checkout@v4
+ - uses: dtolnay/rust-toolchain@stable
with:
components: clippy
+ targets: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
- - name: Add target
- run: rustup target add wasm32-unknown-unknown
- name: Run clippy
- uses: actions-rs/cargo@v1
- with:
- command: clippy
- args: --no-default-features ${{ matrix.features }} -- -D warnings
+ run: cargo clippy --workspace --all-targets --target ${{ matrix.target }} -- -D warnings
typos:
if: github.event.pull_request.draft == false
diff --git a/.gitignore b/.gitignore
index d3ba383d1..df0c112d5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,18 +1,7 @@
/target
Cargo.lock
-# Circom generated files
-experimental-frontends/src/circom/test_folder/*_js/
*.r1cs
*.sym
-
-# Noir generated files
-experimental-frontends/src/noir/test_folder/*/target/*
-
-# generated contracts data
-solidity-verifiers/generated
-examples/*.sol
-examples/*.calldata
-examples/*.inputs
*.serialized
*/*.serialized
diff --git a/Cargo.toml b/Cargo.toml
index f7f00d21a..d1b8c62ac 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,91 +1,67 @@
[workspace]
members = [
- "folding-schemes",
- "solidity-verifiers",
- "cli",
- "experimental-frontends",
+ "crates/primitives",
+ "crates/fs",
+ "crates/ivc",
]
resolver = "2"
-
-[patch.crates-io]
-# Update ark-groth16 to latest git version
-ark-groth16 = { git = "https://github.com/arkworks-rs/groth16", rev = "b3b4a15" }
-
-# Required dependencies for latest ark-groth16
-ark-ff = { git = "https://github.com/arkworks-rs/algebra" }
-ark-ec = { git = "https://github.com/arkworks-rs/algebra" }
-ark-serialize = { git = "https://github.com/arkworks-rs/algebra" }
-ark-poly = { git = "https://github.com/arkworks-rs/algebra" }
-ark-relations = { git = "https://github.com/arkworks-rs/snark" }
-ark-snark = { git = "https://github.com/arkworks-rs/snark" }
-ark-crypto-primitives = { git = "https://github.com/flyingnobita/crypto-primitives", rev = "f559264" }
-ark-r1cs-std = { git = "https://github.com/flyingnobita/r1cs-std_yelhousni", rev = "b4bab0c" } # "perf/sw-updated" branch
-ark-std = { git = "https://github.com/arkworks-rs/std" }
-ark-poly-commit = { git = "https://github.com/arkworks-rs/poly-commit" }
-
-
-# Curve crates also need git versions
-ark-bn254 = { git = "https://github.com/arkworks-rs/algebra" }
-ark-grumpkin = { git = "https://github.com/arkworks-rs/algebra" }
-ark-pallas = { git = "https://github.com/arkworks-rs/algebra" }
-ark-vesta = { git = "https://github.com/arkworks-rs/algebra" }
-ark-mnt4-298 = { git = "https://github.com/arkworks-rs/algebra" }
-ark-mnt6-298 = { git = "https://github.com/arkworks-rs/algebra" }
-
-[patch."https://github.com/arkworks-rs/circom-compat"]
-ark-circom = { git = "https://github.com/dmpierre/circom-compat", rev = "0dfd773c" }
-
[workspace.package]
-edition = "2021"
+edition = "2024"
license = "MIT"
repository = "https://github.com/privacy-scaling-explorations/sonobe/"
+rust-version = "1.85.1"
[workspace.dependencies]
-acvm = { git = "https://github.com/winderica/noir", rev = "fc9e99", default-features = false } # "arkworks-next" branch
-askama = { version = "0.12.0", default-features = false }
-clap = { version = "4.4" }
-clap-verbosity-flag = { version = "2.1" }
-criterion = { version = "0.5" }
-env_logger = { version = "0.10" }
-getrandom = { version = "0.2" }
-log = { version = "0.4" }
-noname = { git = "https://github.com/dmpierre/noname", rev = "c34f17" }
num-bigint = { version = "0.4.3" }
num-integer = { version = "0.1" }
-pprof = { version = "0.13" }
-serde = { version = "^1.0.0" }
-serde_json = { version = "^1.0.0" }
+num-traits = { version = "0.2" }
sha3 = { version = "0.10" }
-rand = { version = "0.8.5" }
rayon = { version = "1" }
-revm = { version = "19.5.0", default-features = false }
-rust-crypto = { version = "0.2" }
-thiserror = { version = "1.0" }
-tokio = "1.44.1"
-wasmer = { version = "6.1.0-rc.2", default-features = false }
+thiserror = { version = "2.0.16" }
+wasm-bindgen-test = { version = "0.3" }
# Arkworks family
-ark-bn254 = { version = "^0.5.0", default-features = false }
-ark-circom = { git = "https://github.com/arkworks-rs/circom-compat", default-features = false }
-ark-crypto-primitives = { version = "^0.5.0", default-features = false }
-ark-ec = { version = "^0.5.0", default-features = false }
-ark-ff = { version = "^0.5.0", default-features = false }
-ark-groth16 = { version = "^0.5.0" }
-ark-grumpkin = { version = "^0.5.0", default-features = false }
-ark-mnt4-298 = { version = "^0.5.0" }
-ark-mnt6-298 = { version = "^0.5.0" }
-ark-pallas = { version = "^0.5.0" }
-ark-poly = { version = "^0.5.0", default-features = false }
-ark-poly-commit = { version = "^0.5.0" }
-ark-r1cs-std = { version = "^0.5.0", default-features = false }
-ark-relations = { version = "^0.5.0", default-features = false }
-ark-serialize = { version = "^0.5.0" }
-ark-snark = { version = "^0.5.0", default-features = false }
-ark-std = { version = "^0.5.0", default-features = false }
-ark-vesta = { version = "^0.5.0" }
+ark-crypto-primitives = { git = "https://github.com/arkworks-rs/crypto-primitives", default-features = false }
+ark-ec = { git = "https://github.com/arkworks-rs/algebra", default-features = false }
+ark-ff = { git = "https://github.com/arkworks-rs/algebra", default-features = false }
+ark-groth16 = { git = "https://github.com/arkworks-rs/groth16", default-features = false }
+ark-poly = { git = "https://github.com/arkworks-rs/algebra", default-features = false }
+ark-poly-commit = { git = "https://github.com/arkworks-rs/poly-commit", default-features = false }
+ark-r1cs-std = { git = "https://github.com/arkworks-rs/r1cs-std", default-features = false }
+ark-relations = { git = "https://github.com/arkworks-rs/snark", default-features = false }
+ark-serialize = { git = "https://github.com/arkworks-rs/algebra", default-features = false }
+ark-snark = { git = "https://github.com/arkworks-rs/snark", default-features = false }
+ark-std = { git = "https://github.com/arkworks-rs/std", default-features = false }
+
+# Ark curves
+ark-bn254 = { git = "https://github.com/arkworks-rs/algebra", default-features = false }
+ark-grumpkin = { git = "https://github.com/arkworks-rs/algebra", default-features = false }
+ark-pallas = { git = "https://github.com/arkworks-rs/algebra", default-features = false }
+ark-vesta = { git = "https://github.com/arkworks-rs/algebra", default-features = false }
# Local crates
-experimental-frontends = { path = "experimental-frontends" }
-folding-schemes = { path = "folding-schemes" }
-solidity-verifiers = { path = "solidity-verifiers" }
+sonobe-primitives = { path = "crates/primitives", default-features = false }
+sonobe-fs = { path = "crates/fs", default-features = false }
+sonobe-ivc = { path = "crates/ivc", default-features = false }
+
+[patch.crates-io]
+# We depend on git versions of arkworks crates, but some of our dependencies
+# depend on crates.io versions, so we need to override them here to avoid
+# version conflicts.
+ark-ff = { git = "https://github.com/arkworks-rs/algebra" }
+ark-ec = { git = "https://github.com/arkworks-rs/algebra" }
+ark-serialize = { git = "https://github.com/arkworks-rs/algebra" }
+ark-poly = { git = "https://github.com/arkworks-rs/algebra" }
+ark-std = { git = "https://github.com/arkworks-rs/std" }
+ark-crypto-primitives = { git = "https://github.com/winderica/crypto-primitives", rev = "af003fc" }
+ark-r1cs-std = { git = "https://github.com/winderica/r1cs-std", rev = "ae8283a" } # "sw-fix-updated" branch
+
+# Curve crates also need git versions
+ark-bn254 = { git = "https://github.com/arkworks-rs/algebra" }
+
+[patch."https://github.com/arkworks-rs/crypto-primitives"]
+ark-crypto-primitives = { git = "https://github.com/winderica/crypto-primitives", rev = "af003fc" }
+
+[patch."https://github.com/arkworks-rs/r1cs-std"]
+ark-r1cs-std = { git = "https://github.com/winderica/r1cs-std", rev = "ae8283a" } # "sw-fix-updated" branch
\ No newline at end of file
diff --git a/README.md b/README.md
index bb9ee6f10..19ca77b10 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
Experimental folding schemes library implemented jointly by [0xPARC](https://0xparc.org/) and [PSE](https://pse.dev).
-
+
Sonobe is a modular library to fold arithmetic circuit instances in an Incremental Verifiable computation (IVC) style. It features multiple folding schemes and decider setups, allowing users to pick the scheme which best fits their needs.
@@ -67,7 +67,7 @@ Once the IVC iterations are completed, the IVC proof is compressed into the Deci
-
+
Where $w_i$ are the external witnesses used at each iterative step.
@@ -87,14 +87,14 @@ The development flow using Sonobe looks like:
4. Generate the decider verifier
-
+
The folding scheme and decider used can be swapped with a few lines of code (eg. switching from a Decider that uses two Spartan proofs over a cycle of curves, to a Decider that uses a single Groth16 proof over the BN254 to be verified in an Ethereum smart contract).
The [Sonobe docs](https://privacy-scaling-explorations.github.io/sonobe-docs/) contain more details about the usage and design of the library.
-Complete examples can be found at [folding-schemes/examples](https://github.com/privacy-scaling-explorations/sonobe/tree/main/examples)
+Complete examples can be found at [folding-schemes/examples](https://github.com/privacy-scaling-explorations/sonobeAcknowledgments/tree/main/examples)
## License
diff --git a/benches/README.md b/benches/README.md
deleted file mode 100644
index 6f998097e..000000000
--- a/benches/README.md
+++ /dev/null
@@ -1,10 +0,0 @@
-# benchmarks
-*Note: we're starting to benchmark & profile Sonobe, current results are pre-optimizations.*
-
-- Benchmark
- - Run: `cargo bench`
- - To run a specific benchmark, for example Nova's benchmark, run: `cargo bench --bench=nova`
-- Profiling
- - eg. `cargo bench --bench=nova -- --profile-time 3`
-
-
diff --git a/benches/common.rs b/benches/common.rs
deleted file mode 100644
index 163dbbfc6..000000000
--- a/benches/common.rs
+++ /dev/null
@@ -1,53 +0,0 @@
-use criterion::*;
-
-use folding_schemes::{
- frontend::{utils::CustomFCircuit, FCircuit},
- Curve, Error, FoldingScheme,
-};
-
-pub(crate) fn bench_ivc_opt<
- C1: Curve,
- C2: Curve,
- FS: FoldingScheme>,
->(
- c: &mut Criterion,
- name: String,
- n: usize,
- prep_param: FS::PreprocessorParam,
-) -> Result<(), Error> {
- let fcircuit_size = 1 << n; // 2^n
-
- let f_circuit = CustomFCircuit::::new(fcircuit_size)?;
-
- let mut rng = rand::rngs::OsRng;
-
- // prepare the FS prover & verifier params
- let fs_params = FS::preprocess(&mut rng, &prep_param)?;
-
- let z_0 = vec![C1::ScalarField::from(3_u32)];
- let mut fs = FS::init(&fs_params, f_circuit, z_0)?;
-
- // warmup steps
- for _ in 0..5 {
- fs.prove_step(rng, (), None)?;
- }
-
- let mut group = c.benchmark_group(format!(
- "{} - FCircuit: {} (2^{}) constraints",
- name, fcircuit_size, n
- ));
- group.significance_level(0.1).sample_size(10);
- group.bench_function("prove_step", |b| {
- b.iter(|| -> Result<_, _> { black_box(fs.clone()).prove_step(rng, (), None) })
- });
-
- // verify the IVCProof
- let ivc_proof = fs.ivc_proof();
- group.bench_function("verify", |b| {
- b.iter(|| -> Result<_, _> {
- FS::verify(black_box(fs_params.1.clone()), black_box(ivc_proof.clone()))
- })
- });
- group.finish();
- Ok(())
-}
diff --git a/benches/hypernova.rs b/benches/hypernova.rs
deleted file mode 100644
index d3f4421ca..000000000
--- a/benches/hypernova.rs
+++ /dev/null
@@ -1,84 +0,0 @@
-use criterion::*;
-use pprof::criterion::{Output, PProfProfiler};
-
-use ark_bn254::{Fr as bn_Fr, G1Projective as bn_G};
-use ark_grumpkin::Projective as grumpkin_G;
-use ark_pallas::{Fr as pallas_Fr, Projective as pallas_G};
-use ark_vesta::Projective as vesta_G;
-
-use folding_schemes::{
- commitment::pedersen::Pedersen,
- folding::{hypernova::HyperNova, nova::PreprocessorParam},
- frontend::{utils::CustomFCircuit, FCircuit},
- transcript::poseidon::poseidon_canonical_config,
-};
-
-mod common;
-use common::bench_ivc_opt;
-
-fn bench_hypernova_ivc(c: &mut Criterion) {
- let poseidon_config = poseidon_canonical_config::();
-
- // iterate over the powers of n
- for n in [0_usize, 14, 16, 18, 19, 20, 21, 22].iter() {
- let fcircuit_size = 1 << n; // 2^n
- let fcircuit = CustomFCircuit::::new(fcircuit_size).unwrap();
- let prep_param = PreprocessorParam::new(poseidon_config.clone(), fcircuit);
-
- bench_ivc_opt::<
- pallas_G,
- vesta_G,
- HyperNova<
- pallas_G,
- vesta_G,
- CustomFCircuit,
- Pedersen,
- Pedersen,
- 1,
- 1,
- false,
- >,
- >(
- c,
- "HyperNova - Pallas-Vesta curves".to_string(),
- *n,
- prep_param,
- )
- .unwrap();
- }
-
- let poseidon_config = poseidon_canonical_config::();
- for n in [0_usize, 14, 16, 18, 19, 20, 21, 22].iter() {
- let fcircuit_size = 1 << n; // 2^n
- let fcircuit = CustomFCircuit::::new(fcircuit_size).unwrap();
- let prep_param = PreprocessorParam::new(poseidon_config.clone(), fcircuit);
-
- bench_ivc_opt::<
- bn_G,
- grumpkin_G,
- HyperNova<
- bn_G,
- grumpkin_G,
- CustomFCircuit,
- Pedersen,
- Pedersen,
- 1,
- 1,
- false,
- >,
- >(
- c,
- "HyperNova - BN254-Grumpkin curves".to_string(),
- *n,
- prep_param,
- )
- .unwrap();
- }
-}
-
-criterion_group! {
- name = benches;
- config = Criterion::default().with_profiler(PProfProfiler::new(100, Output::Flamegraph(None)));
- targets = bench_hypernova_ivc
-}
-criterion_main!(benches);
diff --git a/benches/nova.rs b/benches/nova.rs
deleted file mode 100644
index eed5419e2..000000000
--- a/benches/nova.rs
+++ /dev/null
@@ -1,75 +0,0 @@
-use criterion::*;
-use pprof::criterion::{Output, PProfProfiler};
-
-use ark_bn254::{Fr as bn_Fr, G1Projective as bn_G};
-use ark_grumpkin::Projective as grumpkin_G;
-use ark_pallas::{Fr as pallas_Fr, Projective as pallas_G};
-use ark_vesta::Projective as vesta_G;
-
-use folding_schemes::{
- commitment::pedersen::Pedersen,
- folding::nova::{Nova, PreprocessorParam},
- frontend::{utils::CustomFCircuit, FCircuit},
- transcript::poseidon::poseidon_canonical_config,
-};
-
-mod common;
-use common::bench_ivc_opt;
-
-fn bench_nova_ivc(c: &mut Criterion) {
- let poseidon_config = poseidon_canonical_config::();
-
- // iterate over the powers of n
- for n in [0_usize, 14, 16, 18, 19, 20, 21, 22].iter() {
- let fcircuit_size = 1 << n; // 2^n
- let fcircuit = CustomFCircuit::::new(fcircuit_size).unwrap();
- let prep_param = PreprocessorParam::new(poseidon_config.clone(), fcircuit);
-
- bench_ivc_opt::<
- pallas_G,
- vesta_G,
- Nova<
- pallas_G,
- vesta_G,
- CustomFCircuit,
- Pedersen,
- Pedersen,
- false,
- >,
- >(c, "Nova - Pallas-Vesta curves".to_string(), *n, prep_param)
- .unwrap();
- }
-
- let poseidon_config = poseidon_canonical_config::();
- for n in [0_usize, 14, 16, 18, 19, 20, 21, 22].iter() {
- let fcircuit_size = 1 << n; // 2^n
- let fcircuit = CustomFCircuit::::new(fcircuit_size).unwrap();
- let prep_param = PreprocessorParam::new(poseidon_config.clone(), fcircuit);
-
- bench_ivc_opt::<
- bn_G,
- grumpkin_G,
- Nova<
- bn_G,
- grumpkin_G,
- CustomFCircuit,
- Pedersen,
- Pedersen,
- false,
- >,
- >(
- c,
- "Nova - BN254-Grumpkin curves".to_string(),
- *n,
- prep_param,
- )
- .unwrap();
- }
-}
-
-criterion_group! {
- name = benches;
- config = Criterion::default().with_profiler(PProfProfiler::new(100, Output::Flamegraph(None)));
- targets = bench_nova_ivc
-}
-criterion_main!(benches);
diff --git a/benches/protogalaxy.rs b/benches/protogalaxy.rs
deleted file mode 100644
index ace36c354..000000000
--- a/benches/protogalaxy.rs
+++ /dev/null
@@ -1,78 +0,0 @@
-use criterion::*;
-use pprof::criterion::{Output, PProfProfiler};
-
-use ark_bn254::{Fr as bn_Fr, G1Projective as bn_G};
-use ark_grumpkin::Projective as grumpkin_G;
-use ark_pallas::{Fr as pallas_Fr, Projective as pallas_G};
-use ark_vesta::Projective as vesta_G;
-
-use folding_schemes::{
- commitment::pedersen::Pedersen,
- folding::protogalaxy::ProtoGalaxy,
- frontend::{utils::CustomFCircuit, FCircuit},
- transcript::poseidon::poseidon_canonical_config,
-};
-
-mod common;
-use common::bench_ivc_opt;
-
-fn bench_protogalaxy_ivc(c: &mut Criterion) {
- let poseidon_config = poseidon_canonical_config::();
-
- // iterate over the powers of n
- for n in [0_usize, 14, 16, 18, 19, 20, 21, 22].iter() {
- let fcircuit_size = 1 << n; // 2^n
- let fcircuit = CustomFCircuit::::new(fcircuit_size).unwrap();
- let prep_param = (poseidon_config.clone(), fcircuit);
-
- bench_ivc_opt::<
- pallas_G,
- vesta_G,
- ProtoGalaxy<
- pallas_G,
- vesta_G,
- CustomFCircuit,
- Pedersen,
- Pedersen,
- >,
- >(
- c,
- "ProtoGalaxy - Pallas-Vesta curves".to_string(),
- *n,
- prep_param,
- )
- .unwrap();
- }
-
- let poseidon_config = poseidon_canonical_config::();
- for n in [0_usize, 14, 16, 18, 19, 20, 21, 22].iter() {
- let fcircuit_size = 1 << n; // 2^n
- let fcircuit = CustomFCircuit::::new(fcircuit_size).unwrap();
- let prep_param = (poseidon_config.clone(), fcircuit);
-
- bench_ivc_opt::<
- bn_G,
- grumpkin_G,
- ProtoGalaxy<
- bn_G,
- grumpkin_G,
- CustomFCircuit,
- Pedersen,
- Pedersen,
- >,
- >(
- c,
- "ProtoGalaxy - BN254-Grumpkin curves".to_string(),
- *n,
- prep_param,
- )
- .unwrap();
- }
-}
-
-criterion_group! {
- name = benches;
- config = Criterion::default().with_profiler(PProfProfiler::new(100, Output::Flamegraph(None)));
- targets = bench_protogalaxy_ivc
-}
-criterion_main!(benches);
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
deleted file mode 100644
index d24b0b054..000000000
--- a/cli/Cargo.toml
+++ /dev/null
@@ -1,17 +0,0 @@
-[package]
-name = "solidity-verifiers-cli"
-version = "0.1.0"
-edition.workspace = true
-license.workspace = true
-repository.workspace = true
-
-[dependencies]
-ark-serialize = { workspace = true }
-solidity-verifiers = { workspace = true }
-clap = { workspace = true, features = ["derive", "string"] }
-clap-verbosity-flag = { workspace = true }
-env_logger = { workspace = true }
-
-[features]
-default = ["parallel"]
-parallel = ["solidity-verifiers/parallel"]
\ No newline at end of file
diff --git a/cli/README.md b/cli/README.md
deleted file mode 100644
index d34b2026b..000000000
--- a/cli/README.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# Solidity Verifiers CLI
-
-Solidity Verifiers CLI is a Command-Line Interface (CLI) tool to generate the Solidity smart contracts that verify proofs of Zero Knowledge cryptographic protocols. This tool is developed by the collaborative efforts of the PSE (Privacy & Scaling Explorations) and 0xPARC teams.
-
-Solidity Verifiers CLI is released under the MIT license, but notice that the Solidity template for the Groth16 verification has GPL-3.0 license, hence the generated Solidity verifiers that use the Groth16 template will have that license too.
-
-## Supported Protocols
-
-Solidity Verifier currently supports the generation of Solidity smart contracts for the verification of proofs in the following Zero Knowledge protocols:
-
-- **Groth16:**
- - Efficient and succinct zero-knowledge proof system.
- - Template credit: [Jordi Baylina - Groth16 Verifier Template](https://github.com/iden3/snarkjs/blob/master/templates/verifier_groth16.sol.ejs)
-
-- **KZG:**
- - Uses the Kate-Zaverucha-Goldberg polynomial commitment scheme.
- - Template credit: [weijiekoh - KZG10 Verifier Contract](https://github.com/weijiekoh/libkzg/blob/master/sol/KZGVerifier.sol)
-
-- **Nova + CycleFold Decider:**
- - Implements the decider circuit verification for the Nova proof system in conjunction with the CycleFold protocol optimization.
- - Template inspiration and setup credit: [Han - revm/Solidity Contract Testing Functions](https://github.com/privacy-scaling-explorations/halo2-solidity-verifier/tree/main)
-
-## Usage
-
-```bash
-solidity-verifiers-cli [OPTIONS] -p -k -o
-```
-
-A real use case (which was used to test the tool itself):
-`solidity-verifiers-cli -p groth16 -k ./solidity-verifiers/assets/G16_test_vk`
-This would generate a Groth16 verifier contract for the given G16 verifier key (which consists of the G16_Vk only) and store this contract in `$pwd`.
-
-### Options:
- -v, --verbose: Increase logging verbosity
- -q, --quiet: Decrease logging verbosity
- -p, --protocol : Selects the protocol for which to generate the Decider circuit Solidity Verifier (possible values: groth16, kzg, nova-cyclefold)
- -o, --out : Sets the output path for all generated artifacts
- -k, --protocol-vk : Sets the input path for the file containing the verifier key required by the protocol chosen such that the verification contract can be generated.
- --pragma : Selects the Solidity compiler version to be set in the Solidity Verifier contract artifact
- -h, --help: Print help (see a summary with '-h')
- -V, --version: Print version
-
-## License
-Solidity Verifier CLI is released under the MIT license, but notice that the Solidity template for the Groth16 verification has GPL-3.0 license, hence the generated Solidity verifiers will have that license too.
-
-## Contributing
-Feel free to explore, use, and contribute to Solidity Verifiers CLI as we strive to enhance privacy and scalability in the blockchain space!
-We welcome contributions to Solidity Verifiers CLI! If you encounter any issues, have feature requests, or want to contribute to the codebase, please check out the GitHub repository and follow the guidelines outlined in the contributing documentation.
diff --git a/cli/src/main.rs b/cli/src/main.rs
deleted file mode 100644
index 29479b58e..000000000
--- a/cli/src/main.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-use ark_serialize::Write;
-use clap::Parser;
-use settings::Cli;
-use std::path::Path;
-use std::{fs, io};
-
-mod settings;
-
-fn create_or_open_then_write>(path: &Path, content: &T) -> Result<(), io::Error> {
- let mut file = fs::OpenOptions::new()
- .create(true)
- .truncate(true)
- .write(true)
- .open(path)?;
- file.write_all(content.as_ref())
-}
-
-fn main() {
- let cli = Cli::parse();
-
- // generate a subscriber with the desired log level
- env_logger::builder()
- .format_timestamp_secs()
- .filter_level(cli.verbosity.log_level_filter())
- .init();
-
- let out_path = cli.out;
-
- // Fetch the exact protocol for which we need to generate the Decider verifier contract.
- let protocol = cli.protocol;
- // Fetch the protocol data passed by the user from the file.
- let protocol_vk = std::fs::read(cli.protocol_vk).unwrap();
-
- // Generate the Solidity Verifier contract for the selected protocol with the given data.
- create_or_open_then_write(
- &out_path,
- &protocol.render(&protocol_vk, cli.pragma).unwrap(),
- )
- .unwrap();
-}
diff --git a/cli/src/settings.rs b/cli/src/settings.rs
deleted file mode 100644
index 158ae1a13..000000000
--- a/cli/src/settings.rs
+++ /dev/null
@@ -1,113 +0,0 @@
-use ark_serialize::SerializationError;
-use clap::{Parser, ValueEnum};
-use solidity_verifiers::{
- Groth16VerifierKey, KZG10VerifierKey, NovaCycleFoldVerifierKey, ProtocolVerifierKey,
-};
-use std::{env, fmt::Display, path::PathBuf};
-
-fn get_default_out_path() -> PathBuf {
- let mut path = env::current_dir().unwrap();
- path.push("verifier.sol");
- path
-}
-
-#[derive(Debug, Copy, Clone, ValueEnum)]
-pub(crate) enum Protocol {
- Groth16,
- Kzg,
- NovaCycleFold,
-}
-
-impl Display for Protocol {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{self:?}")
- }
-}
-
-// Would be nice to link this to the `Template` or `ProtocolVerifierKey` traits.
-// Sadly, this requires Boxing with `dyn` or similar which would complicate the code more than is actually required.
-impl Protocol {
- pub(crate) fn render(
- &self,
- data: &[u8],
- pragma: Option,
- ) -> Result, SerializationError> {
- match self {
- Self::Groth16 => Ok(Groth16VerifierKey::deserialize_protocol_verifier_key(data)?
- .render_as_template(pragma)),
-
- Self::Kzg => Ok(KZG10VerifierKey::deserialize_protocol_verifier_key(data)?
- .render_as_template(pragma)),
- Self::NovaCycleFold => Ok(NovaCycleFoldVerifierKey::deserialize_protocol_verifier_key(
- data,
- )?
- .render_as_template(pragma)),
- }
- }
-}
-
-const ABOUT: &str = "A Command-Line Interface (CLI) tool to generate the Solidity smart contracts that verify proofs of Zero Knowledge cryptographic protocols.
-";
-
-const LONG_ABOUT: &str = "
- _____ ______ ______ ______ ______ ______ ______
-| |__| || |__| || |__| || |__| || |__| || |__| || |__| |
-| () || () || () || () || () || () || () |
-|______||______||______||______||______||______||______|
- ______ ______
-| |__| | ____ _ _ _ _ _ | |__| |
-| () | / ___| ___ | (_) __| (_) |_ _ _ | () |
-|______| \\___ \\ / _ \\| | |/ _` | | __| | | | |______|
- ______ ___) | (_) | | | (_| | | |_| |_| | ______
-| |__| | |____/ \\___/|_|_|\\__,_|_|\\__|\\__, | | |__| |
-| () | __ __ _ __ _ |___/ | () |
-|______| \\ \\ / /__ _ __(_)/ _(_) ___ _ __ |______|
- ______ \\ \\ / / _ \\ '__| | |_| |/ _ \\ '__| ______
-| |__| | \\ V / __/ | | | _| | __/ | | |__| |
-| () | \\_/ \\___|_| |_|_| |_|\\___|_| | () |
-|______| |______|
- ______ ______ ______ ______ ______ ______ ______
-| |__| || |__| || |__| || |__| || |__| || |__| || |__| |
-| () || () || () || () || () || () || () |
-|______||______||______||______||______||______||______|
-
-Welcome to Solidity Verifiers CLI, a Command-Line Interface (CLI) tool designed to simplify the generation of Solidity smart contracts that verify proofs of Zero Knowledge cryptographic protocols. This tool is developed by the collaborative efforts of the PSE (Privacy & Scaling Explorations) and 0xPARC teams.
-
-Solidity Verifiers CLI is released under the MIT license, but notice that the Solidity template for the Groth16 verification has GPL-3.0 license, hence the generated Solidity verifiers that use the Groth16 template will have that license too.
-
-Solidity Verifier currently supports the generation of Solidity smart contracts for the verification of proofs in the following Zero Knowledge protocols:
-
- Groth16:
- Efficient and succinct zero-knowledge proof system.
-
- KZG:
- Uses the Kate-Zaverucha-Goldberg polynomial commitment scheme.
-
- Nova + CycleFold Decider:
- Implements the decider circuit verification for the Nova proof system in conjunction with the CycleFold protocol optimization.
-";
-#[derive(Debug, Parser)]
-#[command(author = "0xPARC & PSE", version, about = ABOUT, long_about = Some(LONG_ABOUT))]
-#[command(propagate_version = true)]
-/// A tool to create Solidity Contracts which act as verifiers for the major Folding Schemes implemented
-/// within the `sonobe` repo.
-pub(crate) struct Cli {
- #[command(flatten)]
- pub verbosity: clap_verbosity_flag::Verbosity,
-
- /// Selects the protocol for which we want to generate the Solidity Verifier contract.
- #[arg(short = 'p', long, value_enum, rename_all = "lower")]
- pub protocol: Protocol,
-
- #[arg(short = 'o', long, default_value=get_default_out_path().into_os_string())]
- /// Sets the output path for all the artifacts generated by the command.
- pub out: PathBuf,
-
- #[arg(short = 'k', long)]
- /// Sets the input path for the file containing the verifier key required by the protocol chosen such that the verification contract can be generated.
- pub protocol_vk: PathBuf,
-
- /// Selects the Solidity compiler version to be set in the Solidity Verifier contract artifact.
- #[arg(long, default_value=None)]
- pub pragma: Option,
-}
diff --git a/crates/fs/Cargo.toml b/crates/fs/Cargo.toml
new file mode 100644
index 000000000..772f4e47d
--- /dev/null
+++ b/crates/fs/Cargo.toml
@@ -0,0 +1,37 @@
+[package]
+name = "sonobe-fs"
+version = "0.1.0"
+edition.workspace = true
+license.workspace = true
+repository.workspace = true
+
+[dependencies]
+ark-crypto-primitives = { workspace = true, features = ["constraints", "sponge", "crh"] }
+ark-ec = { workspace = true }
+ark-ff = { workspace = true, features = ["asm"] }
+ark-poly = { workspace = true }
+ark-r1cs-std = { workspace = true }
+ark-relations = { workspace = true }
+ark-std = { workspace = true, features = ["getrandom"] }
+ark-serialize = { workspace = true }
+num-bigint = { workspace = true, features = ["rand"] }
+thiserror = { workspace = true }
+rayon = { workspace = true }
+
+sonobe-primitives = { workspace = true }
+
+[dev-dependencies]
+ark-bn254 = { workspace = true, features = ["curve", "r1cs"] }
+ark-pallas = { workspace = true, features = ["curve", "r1cs"] }
+
+[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies]
+getrandom = { version = "0.2", features = ["js"] }
+
+[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dev-dependencies]
+wasm-bindgen-test = { workspace = true }
+
+[features]
+default = []
+parallel = [
+ "sonobe-primitives/parallel",
+]
diff --git a/crates/fs/src/definitions/algorithms.rs b/crates/fs/src/definitions/algorithms.rs
new file mode 100644
index 000000000..a4238a038
--- /dev/null
+++ b/crates/fs/src/definitions/algorithms.rs
@@ -0,0 +1,120 @@
+//! Traits that define out-of-circuit widgets for folding scheme algorithms
+//! (preprocessing, key generation, proof generation, proof verification, and
+//! deciding).
+
+use ark_std::{borrow::Borrow, rand::RngCore};
+use sonobe_primitives::{relations::Relation, transcripts::Transcript};
+
+use super::{FoldingSchemeDef, errors::Error, keys::DeciderKey};
+
+/// [`FoldingSchemePreprocessor`] is the trait for folding scheme preprocessor.
+pub trait FoldingSchemePreprocessor: FoldingSchemeDef {
+ /// [`FoldingSchemePreprocessor::preprocess`] defines the preprocessing
+ /// algorithm, which is a randomized algorithm that takes as input the
+ /// config / parameterization `config` of the folding scheme (e.g., size
+ /// bounds of the folding scheme) and outputs the public parameters.
+ ///
+ /// Here, the randomness source is controlled by `rng`.
+ ///
+ /// The security parameter is implicitly specified by the size of underlying
+ /// fields and groups.
+ fn preprocess(config: Self::Config, rng: impl RngCore) -> Result;
+}
+
+/// [`FoldingSchemeKeyGenerator`] is the trait for folding scheme key generator.
+pub trait FoldingSchemeKeyGenerator: FoldingSchemeDef {
+ /// [`FoldingSchemeKeyGenerator::generate_keys`] defines the key generation
+ /// algorithm, which is a deterministic algorithm that takes as input the
+ /// public parameters `pp` and the arithmetization `arith`, and outputs a
+ /// prover key and a verifier key.
+ fn generate_keys(pp: Self::PublicParam, arith: Self::Arith) -> Result;
+}
+
+/// [`FoldingSchemeProver`] is the trait for folding scheme prover.
+pub trait FoldingSchemeProver: FoldingSchemeDef {
+ /// [`FoldingSchemeProver::prove`] defines the proof generation algorithm,
+ /// which is a (probably) randomized algorithm that takes as input the
+ /// prover key `pk`, the transcript `transcript` between the prover and the
+ /// verifier, `M` running witnesses `Ws`, `M` running instances `Us`, `N`
+ /// incoming witnesses `ws`, and `N` incoming instances `us`, and outputs
+ /// the folded witness and instance, the proof, and the challenges.
+ ///
+ /// Here, although the challenges can usually be derived by `transcript` and
+ /// thus do not necessarily need to be returned for verification, we still
+ /// have the prover return them explicitly so that they can be used for the
+ /// construction of CycleFold circuits in our CycleFold-based folding-to-IVC
+ /// compiler without re-deriving them from the transcript.
+ ///
+ /// The prover may further use `rng` as the randomness source, e.g., for
+ /// the hiding/zero-knowledge property.
+ #[allow(non_snake_case, clippy::type_complexity)]
+ fn prove(
+ pk: &::ProverKey,
+ transcript: &mut impl Transcript,
+ Ws: &[impl Borrow; M],
+ Us: &[impl Borrow; M],
+ ws: &[impl Borrow; N],
+ us: &[impl Borrow; N],
+ rng: impl RngCore,
+ ) -> Result<(Self::RW, Self::RU, Self::Proof, Self::Challenge), Error>;
+}
+
+/// [`FoldingSchemeVerifier`] is the trait for folding scheme verifier.
+pub trait FoldingSchemeVerifier: FoldingSchemeDef {
+ /// [`FoldingSchemeVerifier::verify`] defines the proof verification
+ /// algorithm, which is a deterministic algorithm that takes as input the
+ /// verifier key `vk`, the transcript `transcript` between the prover and
+ /// the verifier, `M` running instances `Us`, `N` incoming instances `us`,
+ /// and the proof `proof`, and outputs the folded instance.
+ #[allow(non_snake_case)]
+ fn verify(
+ vk: &::VerifierKey,
+ transcript: &mut impl Transcript,
+ Us: &[impl Borrow; M],
+ us: &[impl Borrow; N],
+ proof: &Self::Proof,
+ ) -> Result;
+}
+
+/// [`FoldingSchemeDecider`] is the trait for folding scheme decider.
+pub trait FoldingSchemeDecider: FoldingSchemeDef {
+ /// [`FoldingSchemeDecider::decide_running`] defines the deciding algorithm
+ /// for running witness-instance pairs, which is a deterministic algorithm
+ /// that takes as input the decider key `dk`, a running witness `W` and a
+ /// running instance `U`, and outputs whether the witness-instance pair
+ /// satisfies the running relation.
+ #[allow(non_snake_case)]
+ fn decide_running(dk: &Self::DeciderKey, W: &Self::RW, U: &Self::RU) -> Result<(), Error> {
+ Relation::::check_relation(dk, W, U)
+ }
+
+ /// [`FoldingSchemeDecider::decide_running`] defines the deciding algorithm
+ /// for incoming witness-instance pairs, which is a deterministic algorithm
+ /// that takes as input the decider key `dk`, an incoming witness `W` and an
+ /// incoming instance `U`, and outputs whether the witness-instance pair
+ /// satisfies the incoming relation.
+ fn decide_incoming(dk: &Self::DeciderKey, w: &Self::IW, u: &Self::IU) -> Result<(), Error> {
+ Relation::::check_relation(dk, w, u)
+ }
+}
+
+impl FoldingSchemeDecider for FS {}
+
+/// [`FoldingSchemeOps`] is a convenience super-trait bundling all algorithms.
+pub trait FoldingSchemeOps:
+ FoldingSchemePreprocessor
+ + FoldingSchemeKeyGenerator
+ + FoldingSchemeProver
+ + FoldingSchemeVerifier
+ + FoldingSchemeDecider
+{
+}
+
+impl FoldingSchemeOps for FS where
+ FS: FoldingSchemePreprocessor
+ + FoldingSchemeKeyGenerator
+ + FoldingSchemeProver
+ + FoldingSchemeVerifier
+ + FoldingSchemeDecider
+{
+}
diff --git a/crates/fs/src/definitions/circuits.rs b/crates/fs/src/definitions/circuits.rs
new file mode 100644
index 000000000..68d1be5f5
--- /dev/null
+++ b/crates/fs/src/definitions/circuits.rs
@@ -0,0 +1,60 @@
+//! Traits that define in-circuit gadgets for folding scheme algorithms, mainly
+//! for proof verification.
+
+use ark_relations::gr1cs::SynthesisError;
+use sonobe_primitives::{commitments::CommitmentDefGadget, transcripts::TranscriptGadget};
+
+use super::{FoldingSchemeDefGadget, algorithms::FoldingSchemeOps};
+
+/// [`FoldingSchemePartialVerifierGadget`] is the partial in-circuit verifier.
+///
+/// For schemes that have circuit-unfriendly parts in their verification, the
+/// implementation can choose to only implement this partial verifier gadget and
+/// use some other techniques for the remaining verification work.
+/// For example, group-based folding schemes can defer the expensive elliptic
+/// curve operations on commitments to an external CycleFold circuit.
+pub trait FoldingSchemePartialVerifierGadget:
+ FoldingSchemeDefGadget>
+{
+ /// [`FoldingSchemePartialVerifierGadget::verify_hinted`] defines the proof
+ /// verification gadget that matches its out-of-circuit widget
+ /// [`crate::FoldingSchemeVerifier::verify`].
+ ///
+ /// The implementation is allowed to create hints for the missing parts of
+ /// the verification that are not performed inside the constraint system,
+ /// and it is unnecessary to constrain these hints inside the circuit.
+ /// However, it is the caller's responsibility to ensure that these hints
+ /// are later verified using other techniques (e.g., CycleFold helper).
+ #[allow(non_snake_case)]
+ fn verify_hinted(
+ vk: &Self::VerifierKey,
+ transcript: &mut impl TranscriptGadget<::ConstraintField>,
+ Us: [&Self::RU; M],
+ us: [&Self::IU; N],
+ proof: &Self::Proof,
+ ) -> Result<(Self::RU, Self::Challenge), SynthesisError>;
+}
+
+/// [`FoldingSchemeFullVerifierGadget`] is the full in-circuit verifier.
+///
+/// Extends [`FoldingSchemePartialVerifierGadget`] by performing everything
+/// required for proof verification inside the constraint system.
+pub trait FoldingSchemeFullVerifierGadget:
+ FoldingSchemePartialVerifierGadget
+{
+ /// [`FoldingSchemeFullVerifierGadget::verify`] defines the proof
+ /// verification gadget that matches its out-of-circuit widget
+ /// [`crate::FoldingSchemeVerifier::verify`].
+ ///
+ /// Unlike [`FoldingSchemePartialVerifierGadget::verify_hinted`], the
+ /// implementation is expected to perform all necessary verification steps
+ /// and constrain all required variables inside the circuit.
+ #[allow(non_snake_case)]
+ fn verify(
+ vk: &Self::VerifierKey,
+ transcript: &mut impl TranscriptGadget<::ConstraintField>,
+ Us: [&Self::RU; M],
+ us: [&Self::IU; N],
+ proof: &Self::Proof,
+ ) -> Result;
+}
diff --git a/crates/fs/src/definitions/errors.rs b/crates/fs/src/definitions/errors.rs
new file mode 100644
index 000000000..d721a88dc
--- /dev/null
+++ b/crates/fs/src/definitions/errors.rs
@@ -0,0 +1,49 @@
+//! Error definitions for folding schemes.
+
+use ark_relations::gr1cs::SynthesisError;
+use sonobe_primitives::{
+ arithmetizations::Error as ArithError, commitments::Error as CommitmentError,
+ sumcheck::Error as SumCheckError,
+};
+use thiserror::Error;
+
+/// [`Error`] enumerates possible errors during folding scheme operations.
+#[derive(Debug, Error)]
+pub enum Error {
+ /// [`Error::ArithError`] indicates an error from the underlying constraint
+ /// system.
+ #[error(transparent)]
+ ArithError(#[from] ArithError),
+ /// [`Error::CommitmentError`] indicates an error from the underlying
+ /// commitment scheme.
+ #[error(transparent)]
+ CommitmentError(#[from] CommitmentError),
+ /// [`Error::SynthesisError`] indicates an error during constraint
+ /// synthesis.
+ #[error(transparent)]
+ SynthesisError(#[from] SynthesisError),
+ /// [`Error::SumCheckError`] indicates an error from the underlying sumcheck
+ /// protocol.
+ #[error(transparent)]
+ SumCheckError(#[from] SumCheckError),
+ /// [`Error::Unsupported`] indicates that a certain use case is not
+ /// supported.
+ #[error("Unsupported use case: {0}")]
+ Unsupported(String),
+ /// [`Error::DomainCreationFailure`] indicates a failure in creating
+ /// evaluation domains.
+ #[error("Failed to create domain")]
+ DomainCreationFailure,
+ /// [`Error::IndivisibleByVanishingPoly`] indicates that a polynomial is
+ /// not divisible by the vanishing polynomial of a certain domain.
+ #[error("Indivisible by vanishing polynomial")]
+ IndivisibleByVanishingPoly,
+ /// [`Error::UnsatisfiedRelation`] indicates that a certain relation is not
+ /// satisfied.
+ #[error("Unsatisfied relation: {0}")]
+ UnsatisfiedRelation(String),
+ /// [`Error::InvalidPublicParameters`] indicates that the provided public
+ /// parameters are invalid.
+ #[error("Invalid public parameters: {0}")]
+ InvalidPublicParameters(String),
+}
diff --git a/crates/fs/src/definitions/instances.rs b/crates/fs/src/definitions/instances.rs
new file mode 100644
index 000000000..cd431c448
--- /dev/null
+++ b/crates/fs/src/definitions/instances.rs
@@ -0,0 +1,114 @@
+//! Traits and abstractions for folding scheme instances.
+
+use ark_r1cs_std::{GR1CSVar, alloc::AllocVar, select::CondSelectGadget};
+use ark_relations::gr1cs::{Namespace, SynthesisError};
+use ark_std::fmt::Debug;
+use sonobe_primitives::{
+ arithmetizations::ArithConfig,
+ commitments::{CommitmentDef, CommitmentDefGadget},
+ traits::Dummy,
+ transcripts::{Absorbable, AbsorbableVar},
+};
+
+use super::utils::TaggedVec;
+
+/// [`FoldingInstance`] defines the operations that a folding scheme's instance
+/// should support.
+pub trait FoldingInstance: Clone + Debug + PartialEq + Eq + Absorbable {
+ /// [`FoldingInstance::N_COMMITMENTS`] defines the number of commitments
+ /// contained in the instance.
+ const N_COMMITMENTS: usize;
+
+ /// [`FoldingInstance::commitments`] returns the commitments contained in
+ /// the instance.
+ // TODO (@winderica): consider the scenario where the instance has multiple
+ // commitments of different types.
+ fn commitments(&self) -> Vec<&CM::Commitment>;
+
+ /// [`FoldingInstance::public_inputs`] returns the reference to the public
+ /// inputs contained in the instance.
+ fn public_inputs(&self) -> &[CM::Scalar];
+
+ /// [`FoldingInstance::public_inputs_mut`] returns the mutable reference to
+ /// the public inputs contained in the instance.
+ fn public_inputs_mut(&mut self) -> &mut [CM::Scalar];
+}
+
+/// [`PlainInstance`] is a vector of field elements that are the statements /
+/// public inputs to a constraint system.
+/// We provide this type for folding schemes that support such simple instances,
+/// enabling compatibility with the definition of accumulation schemes (i.e.,
+/// running x plain -> running).
+///
+/// To distinguish it from the witness vector, we use a tagged vector with tag
+/// `'u'` for it.
+pub type PlainInstance = TaggedVec;
+
+impl Dummy<&A> for PlainInstance {
+ fn dummy(cfg: &A) -> Self {
+ vec![V::default(); cfg.n_public_inputs()].into()
+ }
+}
+
+impl FoldingInstance for PlainInstance {
+ const N_COMMITMENTS: usize = 0;
+
+ fn commitments(&self) -> Vec<&CM::Commitment> {
+ vec![]
+ }
+
+ fn public_inputs(&self) -> &[CM::Scalar] {
+ self
+ }
+
+ fn public_inputs_mut(&mut self) -> &mut [CM::Scalar] {
+ self
+ }
+}
+
+/// [`FoldingInstanceVar`] is the in-circuit variable of [`FoldingInstance`].
+pub trait FoldingInstanceVar:
+ AllocVar
+ + GR1CSVar>
+ + AbsorbableVar
+ + CondSelectGadget
+{
+ /// [`FoldingInstanceVar::commitments`] returns the commitments contained in
+ /// the instance variable.
+ fn commitments(&self) -> Vec<&CM::CommitmentVar>;
+
+ /// [`FoldingInstanceVar::public_inputs`] returns the reference to the
+ /// public inputs contained in the instance variable.
+ fn public_inputs(&self) -> &Vec;
+
+ /// [`FoldingInstanceVar::new_witness_with_public_inputs`] allocates a
+ /// folding instance in the circuit as a witness variable, with the given
+ /// pre-allocated public inputs.
+ fn new_witness_with_public_inputs(
+ cs: impl Into>,
+ u: &Self::Value,
+ x: Vec,
+ ) -> Result;
+}
+
+impl FoldingInstanceVar for PlainInstanceVar {
+ fn commitments(&self) -> Vec<&CM::CommitmentVar> {
+ vec![]
+ }
+
+ fn public_inputs(&self) -> &Vec {
+ self
+ }
+
+ fn new_witness_with_public_inputs(
+ _cs: impl Into>,
+ _u: &Self::Value,
+ x: Vec,
+ ) -> Result {
+ Ok(Self(x))
+ }
+}
+
+/// [`PlainInstanceVar`] is the in-circuit variable of [`PlainInstance`].
+// TODO (@winderica): use a different tag?
+pub type PlainInstanceVar = PlainInstance;
diff --git a/crates/fs/src/definitions/keys.rs b/crates/fs/src/definitions/keys.rs
new file mode 100644
index 000000000..a71d7f0b7
--- /dev/null
+++ b/crates/fs/src/definitions/keys.rs
@@ -0,0 +1,25 @@
+//! Traits and abstractions for folding scheme keys.
+
+use sonobe_primitives::arithmetizations::ArithConfig;
+
+/// [`DeciderKey`] defines the information that a folding scheme's decider key
+/// should include or provide access to.
+pub trait DeciderKey {
+ /// [`DeciderKey::ProverKey`] is the type of the prover key contained in the
+ /// decider key.
+ type ProverKey;
+ /// [`DeciderKey::VerifierKey`] is the type of the verifier key contained in
+ /// the decider key.
+ type VerifierKey;
+ /// [`DeciderKey::ArithConfig`] is the constraint system configuration
+ /// associated with the folding scheme.
+ type ArithConfig: ArithConfig;
+
+ /// [`DeciderKey::to_pk`] returns the reference to the prover key.
+ fn to_pk(&self) -> &Self::ProverKey;
+ /// [`DeciderKey::to_vk`] returns the reference to the verifier key.
+ fn to_vk(&self) -> &Self::VerifierKey;
+ /// [`DeciderKey::to_arith_config`] returns the reference to the constraint
+ /// system configuration.
+ fn to_arith_config(&self) -> &Self::ArithConfig;
+}
diff --git a/crates/fs/src/definitions/mod.rs b/crates/fs/src/definitions/mod.rs
new file mode 100644
index 000000000..38a737027
--- /dev/null
+++ b/crates/fs/src/definitions/mod.rs
@@ -0,0 +1,137 @@
+//! Shared traits for folding schemes, including definitions of related
+//! cryptographic objects and algorithms in and out of circuit.
+
+pub mod algorithms;
+pub mod circuits;
+pub mod errors;
+pub mod instances;
+pub mod keys;
+pub mod utils;
+pub mod variants;
+pub mod witnesses;
+
+use ark_r1cs_std::{GR1CSVar, alloc::AllocVar};
+use sonobe_primitives::{
+ arithmetizations::Arith,
+ circuits::AssignmentsOwned,
+ commitments::{CommitmentDef, CommitmentDefGadget},
+ relations::{Relation, WitnessInstanceSampler},
+ traits::{Dummy, SonobeField},
+};
+
+use self::{
+ errors::Error,
+ instances::{FoldingInstance, FoldingInstanceVar},
+ keys::DeciderKey,
+ witnesses::FoldingWitness,
+};
+
+/// [`FoldingSchemeDef`] provides the core type definitions of a folding scheme.
+///
+/// A folding scheme is a cryptographic primitive that folds multiple instances
+/// of computations into a single instance while preserving the validity of the
+/// computations.
+/// More specifically, a folding scheme in general considers two relations `R1`
+/// and `R2`.
+/// The folding prover folds `M` witness-instance pairs satisfying `R1` and `N`
+/// witness-instance pairs satisfying `R2` into a single witness-instance pair
+/// satisfying `R1`, along with a proof that the folding was done correctly.
+/// The folding verifier folds `M` instances of `R1` and `N` instances of `R2`
+/// into a single instance of `R1` under the help of the proof.
+///
+/// While folding schemes can be applied in various contexts, we primarily focus
+/// on their use in constructing recursive proof systems, and thus we refer to
+/// `R1` as the "running relation" and `R2` as the "incoming relation" in the
+/// codebase.
+/// A witness-instance pair `(W, U)` of type `(RW, RU)` for `R1` is called a
+/// "running" witness-instance pair, while a witness-instance pair `(w, u)` of
+/// type `(IW, IU)` for `R2` is called an "incoming" witness-instance pair.
+///
+/// Different folding schemes support different running and incoming relations,
+/// as well as the number of witness-instance pairs that can be folded at once.
+pub trait FoldingSchemeDef {
+ /// [`FoldingSchemeDef::CM`] is the commitment scheme used by the folding
+ /// scheme.
+ type CM: CommitmentDef;
+ /// [`FoldingSchemeDef::RW`] is the type of running witness.
+ type RW: FoldingWitness + for<'a> Dummy<&'a ::Config>;
+ /// [`FoldingSchemeDef::RU`] is the type of running instance.
+ type RU: FoldingInstance + for<'a> Dummy<&'a ::Config>;
+ /// [`FoldingSchemeDef::IW`] is the type of incoming witness.
+ type IW: FoldingWitness + for<'a> Dummy<&'a ::Config>;
+ /// [`FoldingSchemeDef::IU`] is the type of incoming instance.
+ type IU: FoldingInstance + for<'a> Dummy<&'a ::Config>;
+ /// [`FoldingSchemeDef::TranscriptField`] is the field type used in the
+ /// transcript of the folding scheme.
+ type TranscriptField: SonobeField;
+ /// [`FoldingSchemeDef::Arith`] is the constraint system supported by the
+ /// folding scheme.
+ type Arith: Arith::ArithConfig>;
+ /// [`FoldingSchemeDef::Config`] is the type of configuration required to
+ /// generate the public parameters of the folding scheme.
+ type Config;
+ /// [`FoldingSchemeDef::PublicParam`] is the type of public parameters of
+ /// the folding scheme.
+ type PublicParam;
+ /// [`FoldingSchemeDef::DeciderKey`] is the type of decider key of the
+ /// folding scheme, which is used to determine the satisfiability of a
+ /// witness-instance pair.
+ type DeciderKey: DeciderKey
+ + Clone
+ + Relation
+ + Relation
+ + WitnessInstanceSampler
+ + WitnessInstanceSampler<
+ Self::IW,
+ Self::IU,
+ Source = AssignmentsOwned<::Scalar>,
+ Error = Error,
+ >;
+ /// [`FoldingSchemeDef::Challenge`] is the type of challenge generated
+ /// during the folding process.
+ type Challenge;
+ /// [`FoldingSchemeDef::Proof`] is the type of proof generated by the
+ /// folding prover.
+ type Proof: Clone
+ + for<'a> Dummy<&'a ::Config>;
+}
+
+/// [`FoldingSchemeDefGadget`] specifies the in-circuit associated types for a
+/// folding scheme gadget.
+pub trait FoldingSchemeDefGadget {
+ /// [`FoldingSchemeDefGadget::Widget`] points to the out-of-circuit folding
+ /// scheme widget.
+ type Widget: FoldingSchemeDef;
+
+ /// [`FoldingSchemeDefGadget::CM`] is the commitment scheme gadget.
+ type CM: CommitmentDefGadget::CM>;
+ /// [`FoldingSchemeDefGadget::RU`] is the type of in-circuit running
+ /// instance variable.
+ type RU: FoldingInstanceVar::RU>;
+ /// [`FoldingSchemeDefGadget::IU`] is the type of in-circuit incoming
+ /// instance variable.
+ type IU: FoldingInstanceVar::IU>;
+
+ /// [`FoldingSchemeDefGadget::VerifierKey`] is the type of in-circuit
+ /// verifier key variable.
+ type VerifierKey;
+
+ /// [`FoldingSchemeDefGadget::Challenge`] is the type of in-circuit
+ /// challenge variable.
+ type Challenge: AllocVar<
+ ::Challenge,
+ ::ConstraintField,
+ > + GR1CSVar<
+ ::ConstraintField,
+ Value = ::Challenge,
+ >;
+ /// [`FoldingSchemeDefGadget::Proof`] is the type of in-circuit proof
+ /// variable.
+ type Proof: AllocVar<
+ ::Proof,
+ ::ConstraintField,
+ > + GR1CSVar<
+ ::ConstraintField,
+ Value = ::Proof,
+ >;
+}
diff --git a/crates/fs/src/definitions/utils.rs b/crates/fs/src/definitions/utils.rs
new file mode 100644
index 000000000..f4d27acb8
--- /dev/null
+++ b/crates/fs/src/definitions/utils.rs
@@ -0,0 +1,107 @@
+//! Utility types shared across folding scheme definitions.
+
+use ark_ff::{Field, PrimeField};
+use ark_r1cs_std::{
+ GR1CSVar,
+ alloc::{AllocVar, AllocationMode},
+ fields::fp::FpVar,
+ prelude::Boolean,
+ select::CondSelectGadget,
+};
+use ark_relations::gr1cs::{ConstraintSystemRef, Namespace, SynthesisError};
+use ark_std::{
+ borrow::Borrow,
+ ops::{Deref, DerefMut},
+};
+use sonobe_primitives::transcripts::{Absorbable, AbsorbableVar};
+
+/// [`TaggedVec`] is a wrapper around a vector that additionally carries a
+/// compile-time `char` tag.
+///
+/// This is used to create nominally distinct vector types that are structurally
+/// identical.
+#[derive(Clone, Debug, Default, PartialEq, Eq)]
+pub struct TaggedVec(pub Vec);
+
+impl Deref for TaggedVec {
+ type Target = Vec;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl DerefMut for TaggedVec {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.0
+ }
+}
+
+impl From> for TaggedVec {
+ fn from(v: Vec) -> Self {
+ Self(v)
+ }
+}
+
+impl From> for Vec {
+ fn from(val: TaggedVec) -> Self {
+ val.0
+ }
+}
+
+impl Absorbable for TaggedVec {
+ fn absorb_into(&self, dest: &mut Vec) {
+ self.0.absorb_into(dest)
+ }
+}
+
+impl, const TAG: char> AbsorbableVar for TaggedVec {
+ fn absorb_into(&self, dest: &mut Vec>) -> Result<(), SynthesisError> {
+ self.0.absorb_into(dest)
+ }
+}
+
+impl, Y, const TAG: char> AllocVar, F>
+ for TaggedVec
+{
+ fn new_variable>>(
+ cs: impl Into>,
+ f: impl FnOnce() -> Result,
+ mode: AllocationMode,
+ ) -> Result {
+ let v = f()?;
+ Vec::new_variable(cs, || Ok(&v.borrow()[..]), mode).map(Self)
+ }
+}
+
+impl, const TAG: char> CondSelectGadget
+ for TaggedVec
+{
+ fn conditionally_select(
+ cond: &Boolean,
+ true_value: &Self,
+ false_value: &Self,
+ ) -> Result {
+ if true_value.len() != false_value.len() {
+ return Err(SynthesisError::Unsatisfiable);
+ }
+ true_value
+ .iter()
+ .zip(false_value.iter())
+ .map(|(t, f)| cond.select(t, f))
+ .collect::>()
+ .map(Self)
+ }
+}
+
+impl, const TAG: char> GR1CSVar for TaggedVec {
+ type Value = TaggedVec;
+
+ fn cs(&self) -> ConstraintSystemRef {
+ self.0.cs()
+ }
+
+ fn value(&self) -> Result {
+ self.0.value().map(TaggedVec)
+ }
+}
diff --git a/crates/fs/src/definitions/variants.rs b/crates/fs/src/definitions/variants.rs
new file mode 100644
index 000000000..b3655f37d
--- /dev/null
+++ b/crates/fs/src/definitions/variants.rs
@@ -0,0 +1,68 @@
+//! Traits that define variants of folding schemes based on different underlying
+//! mathematical structures.
+
+use sonobe_primitives::{
+ commitments::{CommitmentDef, GroupBasedCommitment},
+ traits::CF2,
+};
+
+use crate::{
+ FoldingSchemeDef, FoldingSchemeDefGadget, FoldingSchemeFullVerifierGadget, FoldingSchemeOps,
+ FoldingSchemePartialVerifierGadget,
+};
+
+/// [`GroupBasedFoldingSchemePrimaryDef`] defines a folding scheme based on
+/// groups (elliptic curves), whose transcript field is the scalar field of its
+/// group-based commitment scheme.
+pub trait GroupBasedFoldingSchemePrimaryDef:
+ FoldingSchemeDef<
+ CM: GroupBasedCommitment,
+ TranscriptField = <::CM as CommitmentDef>::Scalar,
+ >
+{
+ /// [`GroupBasedFoldingSchemePrimaryDef::Gadget`] is the in-circuit gadget
+ /// that defines the folding scheme.
+ type Gadget: FoldingSchemeDefGadget::Gadget2>;
+}
+
+/// [`GroupBasedFoldingSchemePrimary`] is a convenience trait that combines the
+/// definition [`GroupBasedFoldingSchemePrimaryDef`] and operations
+/// [`FoldingSchemeOps`].
+pub trait GroupBasedFoldingSchemePrimary:
+ GroupBasedFoldingSchemePrimaryDef>
+ + FoldingSchemeOps
+{
+}
+
+impl GroupBasedFoldingSchemePrimary for FS where
+ FS: GroupBasedFoldingSchemePrimaryDef>
+{
+}
+
+/// [`GroupBasedFoldingSchemeSecondaryDef`] defines a folding scheme based on
+/// groups (elliptic curves), whose transcript field is the base field of its
+/// group-based commitment scheme.
+pub trait GroupBasedFoldingSchemeSecondaryDef:
+ FoldingSchemeDef<
+ CM: GroupBasedCommitment,
+ TranscriptField = CF2<<::CM as CommitmentDef>::Commitment>,
+ >
+{
+ /// [`GroupBasedFoldingSchemeSecondaryDef::Gadget`] is the in-circuit gadget
+ /// that defines the folding scheme.
+ type Gadget: FoldingSchemeDefGadget::Gadget1>;
+}
+
+/// [`GroupBasedFoldingSchemeSecondary`] is a convenience trait that combines
+/// the definition [`GroupBasedFoldingSchemeSecondaryDef`] and operations
+/// [`FoldingSchemeOps`].
+pub trait GroupBasedFoldingSchemeSecondary:
+ GroupBasedFoldingSchemeSecondaryDef>
+ + FoldingSchemeOps
+{
+}
+
+impl GroupBasedFoldingSchemeSecondary for FS where
+ FS: GroupBasedFoldingSchemeSecondaryDef>
+{
+}
diff --git a/crates/fs/src/definitions/witnesses.rs b/crates/fs/src/definitions/witnesses.rs
new file mode 100644
index 000000000..95da4b78b
--- /dev/null
+++ b/crates/fs/src/definitions/witnesses.rs
@@ -0,0 +1,65 @@
+//! Traits and abstractions for folding scheme witnesses.
+
+use ark_r1cs_std::{GR1CSVar, alloc::AllocVar};
+use ark_std::fmt::Debug;
+use sonobe_primitives::{
+ arithmetizations::ArithConfig,
+ commitments::{CommitmentDef, CommitmentDefGadget},
+ traits::Dummy,
+};
+
+use super::utils::TaggedVec;
+
+/// [`FoldingWitness`] defines the operations that a folding scheme's witness
+/// should support.
+pub trait FoldingWitness: Debug {
+ /// [`FoldingWitness::N_OPENINGS`] defines the number of openings contained
+ /// in the witness.
+ const N_OPENINGS: usize;
+
+ /// [`FoldingWitness::openings`] returns the reference to all openings
+ /// contained in the witness, where each opening a tuple of the values being
+ /// committed to and the randomness used in the commitment.
+ fn openings(&self) -> Vec<(&[CM::Scalar], &CM::Randomness)>;
+}
+
+/// [`PlainWitness`] is a vector of field elements that are the witnesses to a
+/// constraint system.
+/// We provide this type for folding schemes that support such simple witnesses,
+/// enabling compatibility with the definition of accumulation schemes (i.e.,
+/// running x plain -> running).
+///
+/// To distinguish it from the instance vector, we use a tagged vector with tag
+/// `'w'` for it.
+pub type PlainWitness = TaggedVec;
+
+impl Dummy<&A> for PlainWitness {
+ fn dummy(cfg: &A) -> Self {
+ vec![V::default(); cfg.n_witnesses()].into()
+ }
+}
+
+impl FoldingWitness for PlainWitness {
+ const N_OPENINGS: usize = 0;
+
+ fn openings(&self) -> Vec<(&[CM::Scalar], &CM::Randomness)> {
+ vec![]
+ }
+}
+
+/// [`FoldingWitnessVar`] is the in-circuit variable of [`FoldingWitness`].
+pub trait FoldingWitnessVar:
+ AllocVar
+ + GR1CSVar>
+{
+}
+
+impl FoldingWitnessVar for T where
+ T: AllocVar
+ + GR1CSVar>
+{
+}
+
+/// [`PlainWitnessVar`] is the in-circuit variable of [`PlainWitness`].
+// TODO (@winderica): use a different tag?
+pub type PlainWitnessVar = PlainWitness;
diff --git a/crates/fs/src/lib.rs b/crates/fs/src/lib.rs
new file mode 100644
index 000000000..42c9636df
--- /dev/null
+++ b/crates/fs/src/lib.rs
@@ -0,0 +1,136 @@
+#![warn(missing_docs)]
+
+//! Folding scheme definition and implementations.
+//!
+//! This crate provides the traits for folding schemes, the out-of-circuit
+//! widgets and the in-circuit gadgets of their algorithms, and their associated
+//! structures (such as keys, instances, and witnesses) in [`definitions`].
+//!
+//! Concrete constructions of the following folding schemes are then implemented
+//! as submodules:
+//! - [`Nova`](nova)
+//! - [`HyperNova`](hypernova)
+//! - [`Mova`](mova)
+//! - [`Ova`](ova)
+//! - [`ProtoGalaxy`](protogalaxy)
+//!
+//! Each scheme module mirrors the same directory layout:
+//! - `algorithms/`: Implementations for the following algorithms:
+//! - Preprocessing/Setup: [`FoldingSchemePreprocessor`]
+//! - Key generation: [`FoldingSchemeKeyGenerator`]
+//! - Proof generation: [`FoldingSchemeProver`]
+//! - Proof verification: [`FoldingSchemeVerifier`]
+//! - `circuits/`: In-circuit (partial / full) gadgets, mainly for verification.
+//! - `instances/`: Instance types.
+//! - `witnesses/`: Witness types.
+
+pub mod definitions;
+pub mod nova;
+pub mod ova;
+pub mod protogalaxy;
+
+pub use self::definitions::{
+ FoldingSchemeDef, FoldingSchemeDefGadget,
+ algorithms::{
+ FoldingSchemeDecider, FoldingSchemeKeyGenerator, FoldingSchemeOps,
+ FoldingSchemePreprocessor, FoldingSchemeProver, FoldingSchemeVerifier,
+ },
+ circuits::{FoldingSchemeFullVerifierGadget, FoldingSchemePartialVerifierGadget},
+ errors::Error,
+ instances::{FoldingInstance, FoldingInstanceVar, PlainInstance, PlainInstanceVar},
+ keys::DeciderKey,
+ utils::TaggedVec,
+ variants::{
+ GroupBasedFoldingSchemePrimary, GroupBasedFoldingSchemePrimaryDef,
+ GroupBasedFoldingSchemeSecondary, GroupBasedFoldingSchemeSecondaryDef,
+ },
+ witnesses::{FoldingWitness, FoldingWitnessVar, PlainWitness, PlainWitnessVar},
+};
+
+#[cfg(test)]
+mod tests {
+ use ark_relations::gr1cs::{ConstraintSynthesizer, ConstraintSystem};
+ use ark_std::{error::Error, rand::Rng, sync::Arc};
+ use sonobe_primitives::{
+ circuits::{ArithExtractor, AssignmentsOwned},
+ commitments::CommitmentDef,
+ relations::WitnessInstanceSampler,
+ transcripts::{
+ Transcript,
+ griffin::{GriffinParams, sponge::GriffinSponge},
+ },
+ };
+
+ use super::*;
+
+ #[allow(non_snake_case)]
+ pub fn test_folding_scheme, const M: usize, const N: usize>(
+ config: FS::Config,
+ circuit: impl ConstraintSynthesizer<::Scalar>,
+ assignments_vec: Vec::Scalar>>,
+ mut rng: impl Rng,
+ ) -> Result<(), Box>
+ where
+ FS::Arith: From::Scalar>>,
+ {
+ let pp = FS::preprocess(config, &mut rng)?;
+
+ let cs = ArithExtractor::new();
+ cs.execute_synthesizer(circuit)?;
+ let arith = cs.arith()?;
+ let dk = FS::generate_keys(pp, arith)?;
+ let pk = dk.to_pk();
+ let vk = dk.to_vk();
+
+ let mut Ws = vec![];
+ let mut Us = vec![];
+ for _ in 0..M {
+ let (W, U) = WitnessInstanceSampler::::sample(&dk, (), &mut rng)?;
+ FS::decide_running(&dk, &W, &U)?;
+ Ws.push(W);
+ Us.push(U);
+ }
+ let mut Ws = Ws.try_into().unwrap();
+ let mut Us = Us.try_into().unwrap();
+
+ let config = Arc::new(GriffinParams::new(16, 5, 9));
+
+ let mut transcript_p = GriffinSponge::new(&config);
+ let mut transcript_v = GriffinSponge::new(&config);
+
+ for assignments in assignments_vec {
+ let mut ws = vec![];
+ let mut us = vec![];
+ for _ in 0..N {
+ let (w, u) = WitnessInstanceSampler::::sample(
+ &dk,
+ assignments.clone(),
+ &mut rng,
+ )?;
+ FS::decide_incoming(&dk, &w, &u)?;
+ ws.push(w);
+ us.push(u);
+ }
+ let ws = ws.try_into().unwrap();
+ let us = us.try_into().unwrap();
+
+ let (WW, UU, pi, _) = FS::prove(pk, &mut transcript_p, &Ws, &Us, &ws, &us, &mut rng)?;
+ FS::decide_running(&dk, &WW, &UU)?;
+ assert_eq!(FS::verify(vk, &mut transcript_v, &Us, &us, &pi)?, UU);
+
+ for i in 0..M {
+ let (W, U) = WitnessInstanceSampler::::sample(&dk, (), &mut rng)?;
+ FS::decide_running(&dk, &W, &U)?;
+ Ws[i] = W;
+ Us[i] = U;
+ }
+ if M != 0 {
+ let idx = rng.gen_range(0..M);
+ Ws[idx] = WW;
+ Us[idx] = UU;
+ }
+ }
+
+ Ok(())
+ }
+}
diff --git a/crates/fs/src/nova/algorithms/key_generator.rs b/crates/fs/src/nova/algorithms/key_generator.rs
new file mode 100644
index 000000000..89104470e
--- /dev/null
+++ b/crates/fs/src/nova/algorithms/key_generator.rs
@@ -0,0 +1,45 @@
+//! Key generation for Nova.
+
+use ark_std::sync::Arc;
+use sonobe_primitives::{
+ arithmetizations::{Arith, ArithConfig},
+ commitments::{CommitmentKey, GroupBasedCommitment},
+ traits::SonobeField,
+};
+
+use crate::{
+ Error, FoldingSchemeKeyGenerator,
+ nova::{AbstractNova, AbstractNova2},
+};
+
+impl
+ FoldingSchemeKeyGenerator for AbstractNova
+{
+ fn generate_keys(ck: Self::PublicParam, r1cs: Self::Arith) -> Result {
+ let ck = Arc::new(ck);
+ let r1cs = Arc::new(r1cs);
+ let cfg = r1cs.config();
+ if ck.max_scalars_len() < cfg.n_constraints().max(cfg.n_witnesses()) {
+ return Err(Error::InvalidPublicParameters(
+ "The commitment key is too short for the R1CS instance".into(),
+ ));
+ }
+ Ok(Self::DeciderKey { arith: r1cs, ck })
+ }
+}
+
+impl
+ FoldingSchemeKeyGenerator for AbstractNova2
+{
+ fn generate_keys(ck: Self::PublicParam, r1cs: Self::Arith) -> Result {
+ let ck = Arc::new(ck);
+ let r1cs = Arc::new(r1cs);
+ let cfg = r1cs.config();
+ if ck.max_scalars_len() < cfg.n_constraints().max(cfg.n_witnesses()) {
+ return Err(Error::InvalidPublicParameters(
+ "The commitment key is too short for the R1CS instance".into(),
+ ));
+ }
+ Ok(Self::DeciderKey { arith: r1cs, ck })
+ }
+}
diff --git a/crates/fs/src/nova/algorithms/mod.rs b/crates/fs/src/nova/algorithms/mod.rs
new file mode 100644
index 000000000..263d3cd42
--- /dev/null
+++ b/crates/fs/src/nova/algorithms/mod.rs
@@ -0,0 +1,6 @@
+//! Implementations folding scheme algorithms for Nova.
+
+pub mod key_generator;
+pub mod preprocessor;
+pub mod prover;
+pub mod verifier;
diff --git a/crates/fs/src/nova/algorithms/preprocessor.rs b/crates/fs/src/nova/algorithms/preprocessor.rs
new file mode 100644
index 000000000..316fbbec6
--- /dev/null
+++ b/crates/fs/src/nova/algorithms/preprocessor.rs
@@ -0,0 +1,27 @@
+//! Preprocessing for Nova.
+
+use ark_std::rand::RngCore;
+use sonobe_primitives::{commitments::GroupBasedCommitment, traits::SonobeField};
+
+use crate::{
+ Error, FoldingSchemePreprocessor,
+ nova::{AbstractNova, AbstractNova2},
+};
+
+impl
+ FoldingSchemePreprocessor for AbstractNova
+{
+ fn preprocess(ck_len: usize, mut rng: impl RngCore) -> Result {
+ let ck = CM::generate_key(ck_len, &mut rng)?;
+ Ok(ck)
+ }
+}
+
+impl
+ FoldingSchemePreprocessor for AbstractNova2
+{
+ fn preprocess(ck_len: usize, mut rng: impl RngCore) -> Result {
+ let ck = CM::generate_key(ck_len, &mut rng)?;
+ Ok(ck)
+ }
+}
diff --git a/crates/fs/src/nova/algorithms/prover.rs b/crates/fs/src/nova/algorithms/prover.rs
new file mode 100644
index 000000000..de6ec8485
--- /dev/null
+++ b/crates/fs/src/nova/algorithms/prover.rs
@@ -0,0 +1,208 @@
+//! Proof generation for Nova.
+
+use ark_ff::One;
+use ark_std::{borrow::Borrow, cfg_into_iter, cfg_iter, ops::Mul, rand::RngCore};
+#[cfg(feature = "parallel")]
+use rayon::prelude::*;
+use sonobe_primitives::{
+ algebra::ops::bits::FromBits, circuits::AssignmentsOwned, commitments::GroupBasedCommitment,
+ traits::SonobeField, transcripts::Transcript,
+};
+
+use crate::{
+ Error, FoldingSchemeProver,
+ nova::{AbstractNova, AbstractNova2, NovaKey},
+};
+
+impl
+ FoldingSchemeProver<1, 1> for AbstractNova
+{
+ #[allow(non_snake_case)]
+ fn prove(
+ pk: &NovaKey,
+ transcript: &mut impl Transcript,
+ Ws: &[impl Borrow; 1],
+ Us: &[impl Borrow; 1],
+ ws: &[impl Borrow; 1],
+ us: &[impl Borrow; 1],
+ rng: impl RngCore,
+ ) -> Result<(Self::RW, Self::RU, Self::Proof<1, 1>, Self::Challenge), Error> {
+ let (W, U) = (Ws[0].borrow(), Us[0].borrow());
+ let (w, u) = (ws[0].borrow(), us[0].borrow());
+
+ // Compute the cross term `T` by following the optimized approach in
+ // [Mova](https://eprint.iacr.org/2024/1220.pdf)'s section 5.2.
+ let v = pk.arith.evaluate_at(AssignmentsOwned::from((
+ U.u + CM::Scalar::one(),
+ cfg_iter!(U.x).zip(&u.x).map(|(a, b)| *a + b).collect(),
+ cfg_iter!(W.w).zip(&w.w).map(|(a, b)| *a + b).collect(),
+ )))?;
+ let t = cfg_into_iter!(v)
+ .zip(&W.e)
+ .map(|(a, b)| a - b)
+ .collect::>();
+
+ let (cm_t, r_t) = CM::commit(&pk.ck, &t, rng)?;
+
+ let rho_bits = {
+ transcript.add(&U);
+ transcript.add(&u);
+ transcript.add(&cm_t);
+ transcript.challenge_bits(CHALLENGE_BITS)
+ };
+ let rho = CM::Scalar::from_bits_le(&rho_bits);
+
+ Ok((
+ Self::RW {
+ e: cfg_iter!(W.e).zip(&t).map(|(a, b)| rho * b + a).collect(),
+ r_e: W.r_e + r_t * rho,
+ w: cfg_iter!(W.w).zip(&w.w).map(|(a, b)| rho * b + a).collect(),
+ r_w: W.r_w + w.r_w * rho,
+ },
+ Self::RU {
+ cm_e: U.cm_e + cm_t.mul(rho),
+ u: U.u + rho,
+ cm_w: U.cm_w + u.cm_w.mul(rho),
+ x: cfg_iter!(U.x).zip(&u.x).map(|(a, b)| rho * b + a).collect(),
+ },
+ cm_t,
+ rho_bits.try_into().unwrap(),
+ ))
+ }
+}
+
+impl
+ FoldingSchemeProver<2, 0> for AbstractNova
+{
+ #[allow(non_snake_case)]
+ fn prove(
+ pk: &NovaKey,
+ transcript: &mut impl Transcript,
+ [W1, W2]: &[impl Borrow; 2],
+ [U1, U2]: &[impl Borrow; 2],
+ _: &[impl Borrow; 0],
+ _: &[impl Borrow; 0],
+ rng: impl RngCore,
+ ) -> Result<(Self::RW, Self::RU, Self::Proof<2, 0>, Self::Challenge), Error> {
+ let (W1, U1) = (W1.borrow(), U1.borrow());
+ let (W2, U2) = (W2.borrow(), U2.borrow());
+
+ // Compute the cross term `T` by following the optimized approach in
+ // [Mova](https://eprint.iacr.org/2024/1220.pdf)'s section 5.2.
+ let v = pk.arith.evaluate_at(AssignmentsOwned::from((
+ U1.u + U2.u,
+ cfg_iter!(U1.x).zip(&U2.x).map(|(a, b)| *a + b).collect(),
+ cfg_iter!(W1.w).zip(&W2.w).map(|(a, b)| *a + b).collect(),
+ )))?;
+ let t = cfg_into_iter!(v)
+ .zip(&W1.e)
+ .zip(&W2.e)
+ .map(|((a, b), c)| a - b - c)
+ .collect::>();
+
+ let (cm_t, r_t) = CM::commit(&pk.ck, &t, rng)?;
+
+ let rho_bits = {
+ transcript.add(&U1);
+ transcript.add(&U2);
+ transcript.add(&cm_t);
+ transcript.challenge_bits(CHALLENGE_BITS)
+ };
+ let rho = CM::Scalar::from_bits_le(&rho_bits);
+ let rho_squared = rho * rho;
+
+ Ok((
+ Self::RW {
+ e: cfg_iter!(W1.e)
+ .zip(&t)
+ .zip(&W2.e)
+ .map(|((a, b), c)| rho_squared * c + rho * b + a)
+ .collect(),
+ r_e: W1.r_e + r_t * rho + W2.r_e * rho_squared,
+ w: cfg_iter!(W1.w)
+ .zip(&W2.w)
+ .map(|(a, b)| rho * b + a)
+ .collect(),
+ r_w: W1.r_w + W2.r_w * rho,
+ },
+ Self::RU {
+ cm_e: U1.cm_e + cm_t.mul(rho) + U2.cm_e.mul(rho_squared),
+ u: U1.u + rho * U2.u,
+ cm_w: U1.cm_w + U2.cm_w.mul(rho),
+ x: cfg_iter!(U1.x)
+ .zip(&U2.x)
+ .map(|(a, b)| rho * b + a)
+ .collect(),
+ },
+ cm_t,
+ rho_bits.try_into().unwrap(),
+ ))
+ }
+}
+
+impl
+ FoldingSchemeProver<1, 1> for AbstractNova2
+{
+ #[allow(non_snake_case)]
+ fn prove(
+ pk: &NovaKey,
+ transcript: &mut impl Transcript,
+ Ws: &[impl Borrow