Skip to content

Commit

Permalink
Nova Forward ports (easy) (#329)
Browse files Browse the repository at this point in the history
* update README; update version (#303)

* Miscellaneous improvements (details in commit messages) (#308)

* move KZG engine to provider module; update paths everywhere

* add a sparse matrix entry only if the coefficient is non-zero; update digests

* move test code to place where it is used

* move asm to default and add a note

* update constants

* update link

* simplify test_pp_digest

* chore: update expect tests

* Improvements to error handling and naming (#309)

* rename and introduce checks about length

* introduce a test about public IO

* chore: remove needless annotation

---------

Co-authored-by: Srinath Setty <srinath@microsoft.com>
  • Loading branch information
huitseeker and srinathsetty authored Feb 20, 2024
1 parent 9cdccb3 commit 07d9b1e
Show file tree
Hide file tree
Showing 24 changed files with 201 additions and 121 deletions.
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ rayon-scan = "0.1.0"
grumpkin-msm = { git = "https://github.com/lurk-lab/grumpkin-msm", branch = "dev" }

[target.'cfg(target_arch = "wasm32")'.dependencies]
# see https://github.com/rust-random/rand/pull/948
getrandom = { version = "0.2.0", default-features = false, features = ["js"] }

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
Expand Down
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ A distinctive aspect of Nova is that it is the simplest recursive proof system i
## Details of the library
This repository provides `nova-snark,` a Rust library implementation of Nova over a cycle of elliptic curves. Our code supports three curve cycles: (1) Pallas/Vesta, (2) BN254/Grumpkin, and (3) secp/secq.

At its core, Nova relies on a commitment scheme for vectors. Compressing IVC proofs using Spartan relies on interpreting commitments to vectors as commitments to multilinear polynomials and prove evaluations of committed polynomials. Our code implements two commitment schemes and evaluation arguments:
At its core, Nova relies on a commitment scheme for vectors. Compressing IVC proofs using Spartan relies on interpreting commitments to vectors as commitments to multilinear polynomials and prove evaluations of committed polynomials. Our code implements three commitment schemes and evaluation arguments:
1. Pedersen commitments with IPA-based evaluation argument (supported on all three curve cycles), and
2. Multilinear KZG commitments and evaluation argument (supported on curves with pairings e.g., BN254).
2. HyperKZG commitments and evaluation argument (supported on curves with pairings e.g., BN254).
3. KZG commitments with a [Zeromorph](https://eprint.iacr.org/2023/917) evaluation argument (supported on curves equipped with a pairing).

For more details on using multilinear KZG, please see the test `test_ivc_nontrivial_with_compression`. The multilinear KZG instantiation requires a universal trusted setup (the so-called "powers of tau"). In the `setup` method in `src/provider/mlkzg.rs`, one can load group elements produced in an existing KZG trusted setup (that was created for other proof systems based on univariate polynomials such as Plonk or variants), but the library does not currently do so (please see [this](https://github.com/microsoft/Nova/issues/270) issue).
For more details on using HyperKZG, please see the test `test_ivc_nontrivial_with_compression`. The HyperKZG instantiation requires a universal trusted setup (the so-called "powers of tau"). In the `setup` method in `src/provider/hyperkzg.rs`, one can load group elements produced in an existing KZG trusted setup (that was created for other proof systems based on univariate polynomials such as Plonk or variants), but the library does not currently do so (please see [this](https://github.com/microsoft/Nova/issues/270) issue).

We also implement a SNARK, based on [Spartan](https://eprint.iacr.org/2019/550.pdf), to compress IVC proofs produced by Nova. There are two variants, one that does *not* use any preprocessing and another that uses preprocessing of circuits to ensure that the verifier's run time does not depend on the size of the step circuit.

Expand All @@ -36,12 +37,14 @@ A front-end is a tool to take a high-level program and turn it into an intermedi
In the future, we plan to support [Noir](https://noir-lang.org/), a Rust-like DSL and a compiler to transform those programs into an IR. See [this](https://github.com/microsoft/Nova/issues/275) GitHub issue for details.

## Tests and examples
By default, we enable the `asm` feature of an underlying library (which boosts performance by up to 50\%). If the library fails to build or run, one can pass `--no-default-features` to `cargo` commands noted below.

To run tests (we recommend the release mode to drastically shorten run times):
```text
cargo test --release
```

To run example:
To run an example:
```text
cargo run --release --example minroot
```
Expand Down
3 changes: 2 additions & 1 deletion benches/compressed-snark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ fn bench_compressed_snark_internal<S1: RelaxedR1CSSNARKTrait<E1>, S2: RelaxedR1C
let c_secondary = TrivialCircuit::default();

// Produce public parameters
let pp = PublicParams::<E1>::setup(&c_primary, &c_secondary, &*S1::ck_floor(), &*S2::ck_floor());
let pp = PublicParams::<E1>::setup(&c_primary, &c_secondary, &*S1::ck_floor(), &*S2::ck_floor())
.unwrap();

// Produce prover and verifier keys for CompressedSNARK
let (pk, vk) = CompressedSNARK::<_, S1, S2>::setup(&pp).unwrap();
Expand Down
5 changes: 3 additions & 2 deletions benches/pcs.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use arecibo::provider::Bn256EngineIPA;
use arecibo::provider::{
hyperkzg::EvaluationEngine as MLEvaluationEngine,
ipa_pc::EvaluationEngine as IPAEvaluationEngine, non_hiding_zeromorph::ZMPCS,
shplonk::EvaluationEngine as Shplonk, Bn256Engine, Bn256EngineKZG, Bn256EngineZM,
shplonk::EvaluationEngine as Shplonk, Bn256EngineKZG, Bn256EngineZM,
};
use arecibo::spartan::polys::multilinear::MultilinearPolynomial;
use arecibo::traits::{
Expand Down Expand Up @@ -157,7 +158,7 @@ fn bench_pcs(c: &mut Criterion) {
NUM_VARS_TEST_VECTOR,
bench_pcs_proving_internal,
bench_pcs_verifying_internal,
(ipa_assets, IPAEvaluationEngine<Bn256Engine>),
(ipa_assets, IPAEvaluationEngine<Bn256EngineIPA>),
(hyperkzg_assets, MLEvaluationEngine<Bn256, Bn256EngineKZG>),
(zm_assets, ZMPCS<Bn256, Bn256EngineZM>),
(shplonk_assets, Shplonk<Bn256, Bn256EngineKZG>)
Expand Down
3 changes: 2 additions & 1 deletion benches/recursive-snark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ fn bench_recursive_snark(c: &mut Criterion) {
&c_secondary,
&*default_ck_hint(),
&*default_ck_hint(),
);
)
.unwrap();

// Bench time to produce a recursive SNARK;
// we execute a certain number of warm-up steps since executing
Expand Down
3 changes: 2 additions & 1 deletion benches/sha256.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ fn bench_recursive_snark(c: &mut Criterion) {
&ttc,
&*default_ck_hint(),
&*default_ck_hint(),
);
)
.unwrap();

let circuit_secondary = TrivialCircuit::default();
let z0_primary = vec![<E1 as Engine>::Scalar::from(2u64)];
Expand Down
5 changes: 3 additions & 2 deletions examples/and.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,8 @@ fn main() {
&circuit_secondary,
&*S1::ck_floor(),
&*S2::ck_floor(),
);
)
.unwrap();
println!("PublicParams::setup, took {:?} ", start.elapsed());

println!(
Expand Down Expand Up @@ -290,7 +291,7 @@ fn main() {
assert!(res.is_ok());

// produce a compressed SNARK
println!("Generating a CompressedSNARK using Spartan with multilinear KZG...");
println!("Generating a CompressedSNARK using Spartan with HyperKZG...");
let (pk, vk) = CompressedSNARK::<_, S1, S2>::setup(&pp).unwrap();

let start = Instant::now();
Expand Down
5 changes: 3 additions & 2 deletions examples/minroot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,8 @@ fn main() {
&circuit_secondary,
&*S1::ck_floor(),
&*S2::ck_floor(),
);
)
.unwrap();
println!("PublicParams::setup, took {:?} ", start.elapsed());
#[cfg(feature = "abomonate")]
let pp = {
Expand Down Expand Up @@ -334,7 +335,7 @@ fn main() {
assert!(res.is_ok());

// produce a compressed SNARK
println!("Generating a CompressedSNARK using Spartan with multilinear KZG...");
println!("Generating a CompressedSNARK using Spartan with HyperKZG...");
let (pk, vk) = CompressedSNARK::<_, S1, S2>::setup(&pp).unwrap();

let start = Instant::now();
Expand Down
4 changes: 2 additions & 2 deletions src/bellpepper/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ mod tests {
shape_cs::ShapeCS,
solver::SatisfyingAssignment,
},
provider::{Bn256Engine, PallasEngine, Secp256k1Engine},
provider::{Bn256EngineKZG, PallasEngine, Secp256k1Engine},
traits::{snark::default_ck_hint, Engine},
};
use bellpepper_core::{num::AllocatedNum, ConstraintSystem};
Expand Down Expand Up @@ -59,7 +59,7 @@ mod tests {
#[test]
fn test_alloc_bit() {
test_alloc_bit_with::<PallasEngine>();
test_alloc_bit_with::<Bn256Engine>();
test_alloc_bit_with::<Bn256EngineKZG>();
test_alloc_bit_with::<Secp256k1Engine>();
}
}
25 changes: 14 additions & 11 deletions src/bellpepper/r1cs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,17 +117,20 @@ fn add_constraint<S: PrimeField>(
assert_eq!(n + 1, C.indptr.len(), "C: invalid shape");

let add_constraint_component = |index: Index, coeff: &S, M: &mut SparseMatrix<S>| {
match index {
Index::Input(idx) => {
// Inputs come last, with input 0, representing 'one',
// at position num_vars within the witness vector.
let idx = idx + num_vars;
M.data.push(*coeff);
M.indices.push(idx);
}
Index::Aux(idx) => {
M.data.push(*coeff);
M.indices.push(idx);
// we add constraints to the matrix only if the associated coefficient is non-zero
if *coeff != S::ZERO {
match index {
Index::Input(idx) => {
// Inputs come last, with input 0, representing 'one',
// at position num_vars within the witness vector.
let idx = idx + num_vars;
M.data.push(*coeff);
M.indices.push(idx);
}
Index::Aux(idx) => {
M.data.push(*coeff);
M.indices.push(idx);
}
}
}
};
Expand Down
10 changes: 5 additions & 5 deletions src/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ mod tests {
constants::{BN_LIMB_WIDTH, BN_N_LIMBS},
gadgets::utils::scalar_as_base,
provider::{
poseidon::PoseidonConstantsCircuit, Bn256Engine, GrumpkinEngine, PallasEngine,
poseidon::PoseidonConstantsCircuit, Bn256EngineKZG, GrumpkinEngine, PallasEngine,
Secp256k1Engine, Secq256k1Engine, VestaEngine,
},
traits::{circuit::TrivialCircuit, snark::default_ck_hint, CurveCycleEquipped, Dual},
Expand Down Expand Up @@ -468,13 +468,13 @@ mod tests {
}

#[test]
fn test_recursive_circuit_grumpkin() {
fn test_recursive_circuit_bn256_grumpkin() {
let params1 = NovaAugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, true);
let params2 = NovaAugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, false);
let ro_consts1: ROConstantsCircuit<GrumpkinEngine> = PoseidonConstantsCircuit::default();
let ro_consts2: ROConstantsCircuit<Bn256Engine> = PoseidonConstantsCircuit::default();
let ro_consts2: ROConstantsCircuit<Bn256EngineKZG> = PoseidonConstantsCircuit::default();

test_recursive_circuit_with::<Bn256Engine>(
test_recursive_circuit_with::<Bn256EngineKZG>(
&params1,
&params2,
ro_consts1,
Expand All @@ -485,7 +485,7 @@ mod tests {
}

#[test]
fn test_recursive_circuit_secp() {
fn test_recursive_circuit_secpq() {
let params1 = NovaAugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, true);
let params2 = NovaAugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, false);
let ro_consts1: ROConstantsCircuit<Secq256k1Engine> = PoseidonConstantsCircuit::default();
Expand Down
13 changes: 7 additions & 6 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ pub enum NovaError {
/// returned if the supplied row or col in (row,col,val) tuple is out of range
#[error("InvalidIndex")]
InvalidIndex,
/// returned if the supplied input is not even-sized
#[error("OddInputLength")]
OddInputLength,
/// returned if the step circuit calls inputize or alloc_io in its synthesize method
/// instead of passing output with the return value
#[error("InvalidStepCircuitIO")]
InvalidStepCircuitIO,
/// returned if the supplied input is not of the right length
#[error("InvalidInputLength")]
InvalidInputLength,
Expand Down Expand Up @@ -74,9 +75,9 @@ pub enum NovaError {
/// Errors specific to the Polynomial commitment scheme
#[derive(Debug, Eq, PartialEq, Error)]
pub enum PCSError {
/// returned when an invalid inner product argument is provided
#[error("InvalidIPA")]
InvalidIPA,
/// returned when an invalid PCS evaluation argument is provided
#[error("InvalidPCS")]
InvalidPCS,
/// returned when there is a Zeromorph error
#[error("ZMError")]
ZMError,
Expand Down
17 changes: 9 additions & 8 deletions src/gadgets/ecc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -785,7 +785,8 @@ mod tests {
provider::{
bn256_grumpkin::{bn256, grumpkin},
secp_secq::{secp256k1, secq256k1},
Bn256Engine, GrumpkinEngine, PallasEngine, Secp256k1Engine, Secq256k1Engine, VestaEngine,
Bn256EngineIPA, Bn256EngineKZG, GrumpkinEngine, PallasEngine, Secp256k1Engine,
Secq256k1Engine, VestaEngine,
},
traits::{snark::default_ck_hint, Engine},
};
Expand Down Expand Up @@ -925,7 +926,7 @@ mod tests {
test_ecc_ops_with::<pallas::Affine, <PallasEngine as Engine>::GE>();
test_ecc_ops_with::<vesta::Affine, <VestaEngine as Engine>::GE>();

test_ecc_ops_with::<bn256::Affine, <Bn256Engine as Engine>::GE>();
test_ecc_ops_with::<bn256::Affine, <Bn256EngineIPA as Engine>::GE>();
test_ecc_ops_with::<grumpkin::Affine, <GrumpkinEngine as Engine>::GE>();

test_ecc_ops_with::<secp256k1::Affine, <Secp256k1Engine as Engine>::GE>();
Expand Down Expand Up @@ -1012,8 +1013,8 @@ mod tests {
test_ecc_circuit_ops_with::<PallasEngine, VestaEngine>(&expect!["2704"], &expect!["2692"]);
test_ecc_circuit_ops_with::<VestaEngine, PallasEngine>(&expect!["2704"], &expect!["2692"]);

test_ecc_circuit_ops_with::<Bn256Engine, GrumpkinEngine>(&expect!["2738"], &expect!["2724"]);
test_ecc_circuit_ops_with::<GrumpkinEngine, Bn256Engine>(&expect!["2738"], &expect!["2724"]);
test_ecc_circuit_ops_with::<Bn256EngineIPA, GrumpkinEngine>(&expect!["2738"], &expect!["2724"]);
test_ecc_circuit_ops_with::<GrumpkinEngine, Bn256EngineIPA>(&expect!["2738"], &expect!["2724"]);

test_ecc_circuit_ops_with::<Secp256k1Engine, Secq256k1Engine>(
&expect!["2670"],
Expand Down Expand Up @@ -1075,8 +1076,8 @@ mod tests {
test_ecc_circuit_add_equal_with::<PallasEngine, VestaEngine>();
test_ecc_circuit_add_equal_with::<VestaEngine, PallasEngine>();

test_ecc_circuit_add_equal_with::<Bn256Engine, GrumpkinEngine>();
test_ecc_circuit_add_equal_with::<GrumpkinEngine, Bn256Engine>();
test_ecc_circuit_add_equal_with::<Bn256EngineKZG, GrumpkinEngine>();
test_ecc_circuit_add_equal_with::<GrumpkinEngine, Bn256EngineKZG>();

test_ecc_circuit_add_equal_with::<Secp256k1Engine, Secq256k1Engine>();
test_ecc_circuit_add_equal_with::<Secq256k1Engine, Secp256k1Engine>();
Expand Down Expand Up @@ -1135,11 +1136,11 @@ mod tests {
test_ecc_circuit_add_negation_with::<PallasEngine, VestaEngine>(&expect!["39"], &expect!["34"]);
test_ecc_circuit_add_negation_with::<VestaEngine, PallasEngine>(&expect!["39"], &expect!["34"]);

test_ecc_circuit_add_negation_with::<Bn256Engine, GrumpkinEngine>(
test_ecc_circuit_add_negation_with::<Bn256EngineIPA, GrumpkinEngine>(
&expect!["39"],
&expect!["34"],
);
test_ecc_circuit_add_negation_with::<GrumpkinEngine, Bn256Engine>(
test_ecc_circuit_add_negation_with::<GrumpkinEngine, Bn256EngineIPA>(
&expect!["39"],
&expect!["34"],
);
Expand Down
Loading

1 comment on commit 07d9b1e

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmarks

Table of Contents

Overview

This benchmark report shows the Arecibo GPU benchmarks.
NVIDIA L4
Intel(R) Xeon(R) CPU @ 2.20GHz
32 vCPUs
125 GB RAM
Workflow run: https://github.com/lurk-lab/arecibo/actions/runs/7974488290

Benchmark Results

RecursiveSNARK-NIVC-2

ref=9cdccb3 ref=07d9b1e
Prove-NumCons-6540 44.63 ms (✅ 1.00x) 44.99 ms (✅ 1.01x slower)
Verify-NumCons-6540 34.39 ms (✅ 1.00x) 34.96 ms (✅ 1.02x slower)
Prove-NumCons-1028888 322.45 ms (✅ 1.00x) 319.19 ms (✅ 1.01x faster)
Verify-NumCons-1028888 249.84 ms (✅ 1.00x) 249.49 ms (✅ 1.00x faster)

CompressedSNARK-NIVC-Commitments-2

ref=9cdccb3 ref=07d9b1e
Prove-NumCons-6540 10.63 s (✅ 1.00x) 10.84 s (✅ 1.02x slower)
Verify-NumCons-6540 50.94 ms (✅ 1.00x) 51.68 ms (✅ 1.01x slower)
Prove-NumCons-1028888 51.95 s (✅ 1.00x) 53.85 s (✅ 1.04x slower)
Verify-NumCons-1028888 51.34 ms (✅ 1.00x) 52.17 ms (✅ 1.02x slower)

Made with criterion-table

Please sign in to comment.