Skip to content

Commit

Permalink
Test vectors & benchmark for PCS (microsoft#222)
Browse files Browse the repository at this point in the history
* test(ipa): proof verified random seed

test(ipa): proof verified

test(ipa): switch from keccak to grumpkin transcript

test(ipa): wip test IPA prove & verify

chore: added Jetbrain config to gitignore

* test(ipa): using generic fn over mlkzg, ipa & zm

- Implemented test_fail_bad_proof that generates a test case from a seed to try and verify a non valid proof.
- Implement both test_fail_bad_proof & test_from_seed for IPA over Grumpkin, MLKZG over Bn256 and ZM over Bn256

* ci(ipa): fixed xclippy

* feat(ipa): wip bench pcs

* feat(ipa): bench proof generation and verification

- Created a benchmark for proof generation and verification for IPA over Grumpkin, MLKZG over Bn and ZM over Bn
- multilinear.rs to public for benchmark purposes
- Refactored generic test methods per @adr1anh comment

* feat(ipa): proper polynomial sizes for test & sample size to 10

* feat(ipa): benchmark groups

* ci(ipa): fix xclippy

* test(ipa): added more polynomial sizes

* test(ipa): renamed test method

* refactor(ipa): revamp based on review

* refactor(ipa): Flat -> Auto bench

* refactor(ipa): random_with_eval inline

* ci(ipa): fix xclippy
  • Loading branch information
tchataigner authored and huitseeker committed Jan 26, 2024
1 parent 931f32a commit d41e56b
Show file tree
Hide file tree
Showing 12 changed files with 490 additions and 72 deletions.
7 changes: 7 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,15 @@ harness = false
name = "sha256"
harness = false


[[bench]]
name = "pcs"
harness = false
required-features = ["bench"]

[features]
default = []
bench = []
asm = ["halo2curves/asm"]
# Compiles in portable mode, w/o ISA extensions => binary can be executed on all systems.
portable = ["pasta-msm/portable"]
Expand Down
2 changes: 1 addition & 1 deletion benches/compressed-snark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type SS2 = nova_snark::spartan::ppsnark::RelaxedR1CSSNARK<E2, EE2>;
type C1 = NonTrivialCircuit<<E1 as Engine>::Scalar>;
type C2 = TrivialCircuit<<E2 as Engine>::Scalar>;

// To run these benchmarks, first download `criterion` with `cargo install cargo install cargo-criterion`.
// To run these benchmarks, first download `criterion` with `cargo install cargo-criterion`.
// Then `cargo criterion --bench compressed-snark`. The results are located in `target/criterion/data/<name-of-benchmark>`.
// For flamegraphs, run `cargo criterion --bench compressed-snark --features flamegraph -- --profile-time <secs>`.
// The results are located in `target/criterion/profile/<name-of-benchmark>`.
Expand Down
199 changes: 199 additions & 0 deletions benches/pcs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
use criterion::{criterion_group, criterion_main, Bencher, BenchmarkId, Criterion, SamplingMode};
use ff::Field;
use halo2curves::bn256::Bn256;
use nova_snark::provider::{
hyperkzg::EvaluationEngine as MLEvaluationEngine,
ipa_pc::EvaluationEngine as IPAEvaluationEngine, non_hiding_zeromorph::ZMPCS, Bn256Engine,
Bn256EngineKZG, Bn256EngineZM,
};
use nova_snark::spartan::polys::multilinear::MultilinearPolynomial;
use nova_snark::traits::{
commitment::CommitmentEngineTrait, evaluation::EvaluationEngineTrait, Engine,
TranscriptEngineTrait,
};
use rand::rngs::StdRng;
use rand_core::{CryptoRng, RngCore, SeedableRng};
use std::any::type_name;
use std::time::Duration;

// To run these benchmarks, first download `criterion` with `cargo install cargo-criterion`.
// Then `cargo criterion --bench pcs`.
// For flamegraphs, run `cargo criterion --bench pcs --features flamegraph -- --profile-time <secs>`.
// The results are located in `target/criterion/profile/<name-of-benchmark>`.
cfg_if::cfg_if! {
if #[cfg(feature = "flamegraph")] {
criterion_group! {
name = pcs;
config = Criterion::default().warm_up_time(Duration::from_millis(3000)).with_profiler(pprof::criterion::PProfProfiler::new(100, pprof::criterion::Output::Flamegraph(None)));
targets = bench_pcs
}
} else {
criterion_group! {
name = pcs;
config = Criterion::default().warm_up_time(Duration::from_millis(3000));
targets = bench_pcs
}
}
}

criterion_main!(pcs);

const NUM_VARS_TEST_VECTOR: [usize; 6] = [10, 12, 14, 16, 18, 20];

struct BenchAssests<E: Engine, EE: EvaluationEngineTrait<E>> {
poly: MultilinearPolynomial<<E as Engine>::Scalar>,
point: Vec<<E as Engine>::Scalar>,
eval: <E as Engine>::Scalar,
ck: <<E as Engine>::CE as CommitmentEngineTrait<E>>::CommitmentKey,
commitment: <<E as Engine>::CE as CommitmentEngineTrait<E>>::Commitment,
prover_key: <EE as EvaluationEngineTrait<E>>::ProverKey,
verifier_key: <EE as EvaluationEngineTrait<E>>::VerifierKey,
proof: Option<<EE as EvaluationEngineTrait<E>>::EvaluationArgument>,
}

/// Returns a random polynomial, a point and calculate its evaluation.
pub fn random_poly_with_eval<E: Engine, R: RngCore + CryptoRng>(
num_vars: usize,
mut rng: &mut R,
) -> (
MultilinearPolynomial<<E as Engine>::Scalar>,
Vec<<E as Engine>::Scalar>,
<E as Engine>::Scalar,
) {
// Generate random polynomial and point.
let poly = MultilinearPolynomial::random(num_vars, &mut rng);
let point = (0..num_vars)
.map(|_| <E as Engine>::Scalar::random(&mut rng))
.collect::<Vec<_>>();

// Calculation evaluation of point over polynomial.
let eval = MultilinearPolynomial::evaluate_with(poly.evaluations(), &point);

(poly, point, eval)
}

impl<E: Engine, EE: EvaluationEngineTrait<E>> BenchAssests<E, EE> {
pub(crate) fn from_num_vars<R: CryptoRng + RngCore>(num_vars: usize, rng: &mut R) -> Self {
let (poly, point, eval) = random_poly_with_eval::<E, R>(num_vars, rng);

// Mock commitment key.
let ck = E::CE::setup(b"test", 1 << num_vars);
// Commits to the provided vector using the provided generators.
let commitment = E::CE::commit(&ck, poly.evaluations());

let (prover_key, verifier_key) = EE::setup(&ck);

// Generate proof so that we can bench verification.
let proof = EE::prove(
&ck,
&prover_key,
&mut E::TE::new(b"TestEval"),
&commitment,
poly.evaluations(),
&point,
&eval,
)
.unwrap();

Self {
poly,
point,
eval,
ck,
commitment,
prover_key,
verifier_key,
proof: Some(proof),
}
}
}

// Macro to generate benchmark code for multiple evaluation engine types
macro_rules! benchmark_all_engines {
($criterion:expr, $test_vector:expr, $proving_fn:expr, $verifying_fn:expr, $( ($assets:ident, $eval_engine:ty) ),*) => {
for num_vars in $test_vector.iter() {
let mut rng = rand::rngs::StdRng::seed_from_u64(*num_vars as u64);

$(
let $assets: BenchAssests<_, $eval_engine> = BenchAssests::from_num_vars::<StdRng>(*num_vars, &mut rng);
)*

// Proving group
let mut proving_group = $criterion.benchmark_group(format!("PCS-Proving {}", num_vars));
proving_group
.sampling_mode(SamplingMode::Auto)
.sample_size(10);

$(
proving_group.bench_with_input(BenchmarkId::new(type_name::<$eval_engine>(), num_vars), &num_vars, |b, _| {
$proving_fn(b, &$assets);
});
)*

proving_group.finish();

// Verifying group
let mut verifying_group = $criterion.benchmark_group(format!("PCS-Verifying {}", num_vars));
verifying_group
.sampling_mode(SamplingMode::Auto)
.sample_size(10);

$(
verifying_group.bench_with_input(BenchmarkId::new(type_name::<$eval_engine>(), num_vars), &num_vars, |b, _| {
$verifying_fn(b, &$assets);
});
)*

verifying_group.finish();
}
};
}

fn bench_pcs(c: &mut Criterion) {
benchmark_all_engines!(
c,
NUM_VARS_TEST_VECTOR,
bench_pcs_proving_internal,
bench_pcs_verifying_internal,
(ipa_assets, IPAEvaluationEngine<Bn256Engine>),
(mlkzg_assets, MLEvaluationEngine<Bn256, Bn256EngineKZG>),
(zm_assets, ZMPCS<Bn256, Bn256EngineZM>)
);
}

fn bench_pcs_proving_internal<E: Engine, EE: EvaluationEngineTrait<E>>(
b: &mut Bencher<'_>,
bench_assets: &BenchAssests<E, EE>,
) {
// Bench generate proof.
b.iter(|| {
EE::prove(
&bench_assets.ck,
&bench_assets.prover_key,
&mut E::TE::new(b"TestEval"),
&bench_assets.commitment,
bench_assets.poly.evaluations(),
&bench_assets.point,
&bench_assets.eval,
)
.unwrap();
});
}

fn bench_pcs_verifying_internal<E: Engine, EE: EvaluationEngineTrait<E>>(
b: &mut Bencher<'_>,
bench_assets: &BenchAssests<E, EE>,
) {
// Bench verify proof.
b.iter(|| {
EE::verify(
&bench_assets.verifier_key,
&mut E::TE::new(b"TestEval"),
&bench_assets.commitment,
&bench_assets.point,
&bench_assets.eval,
bench_assets.proof.as_ref().unwrap(),
)
.unwrap();
});
}
2 changes: 1 addition & 1 deletion benches/recursive-snark.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type E2 = VestaEngine;
type C1 = NonTrivialCircuit<<E1 as Engine>::Scalar>;
type C2 = TrivialCircuit<<E2 as Engine>::Scalar>;

// To run these benchmarks, first download `criterion` with `cargo install cargo install cargo-criterion`.
// To run these benchmarks, first download `criterion` with `cargo install cargo-criterion`.
// Then `cargo criterion --bench recursive-snark`. The results are located in `target/criterion/data/<name-of-benchmark>`.
// For flamegraphs, run `cargo criterion --bench recursive-snark --features flamegraph -- --profile-time <secs>`.
// The results are located in `target/criterion/profile/<name-of-benchmark>`.
Expand Down
55 changes: 4 additions & 51 deletions src/provider/hyperkzg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -429,12 +429,11 @@ where
#[cfg(test)]
mod tests {
use super::*;
use crate::provider::util::test_utils::prove_verify_from_num_vars;
use crate::{
provider::keccak::Keccak256Transcript, spartan::polys::multilinear::MultilinearPolynomial,
traits::commitment::CommitmentTrait, CommitmentKey,
provider::keccak::Keccak256Transcript, traits::commitment::CommitmentTrait, CommitmentKey,
};
use bincode::Options;
use rand::SeedableRng;

type E = halo2curves::bn256::Bn256;
type NE = crate::provider::Bn256EngineKZG;
Expand Down Expand Up @@ -558,54 +557,8 @@ mod tests {
#[test]
fn test_mlkzg_more() {
// test the mlkzg prover and verifier with random instances (derived from a seed)
for ell in [4, 5, 6] {
let mut rng = rand::rngs::StdRng::seed_from_u64(ell as u64);

let n = 1 << ell; // n = 2^ell

let poly = (0..n).map(|_| Fr::random(&mut rng)).collect::<Vec<_>>();
let point = (0..ell).map(|_| Fr::random(&mut rng)).collect::<Vec<_>>();
let eval = MultilinearPolynomial::evaluate_with(&poly, &point);

let ck: CommitmentKey<NE> =
<KZGCommitmentEngine<E> as CommitmentEngineTrait<NE>>::setup(b"test", n);
let (pk, vk): (KZGProverKey<E>, KZGVerifierKey<E>) = EvaluationEngine::<E, NE>::setup(&ck);

// make a commitment
let C = <KZGCommitmentEngine<E> as CommitmentEngineTrait<NE>>::commit(&ck, &poly);

// prove an evaluation
let mut prover_transcript = Keccak256Transcript::<NE>::new(b"TestEval");
let proof: EvaluationArgument<E> = EvaluationEngine::<E, NE>::prove(
&ck,
&pk,
&mut prover_transcript,
&C,
&poly,
&point,
&eval,
)
.unwrap();

// verify the evaluation
let mut verifier_tr = Keccak256Transcript::<NE>::new(b"TestEval");
assert!(
EvaluationEngine::<E, NE>::verify(&vk, &mut verifier_tr, &C, &point, &eval, &proof).is_ok()
);

// Change the proof and expect verification to fail
let mut bad_proof = proof.clone();
bad_proof.comms[0] = (bad_proof.comms[0] + bad_proof.comms[1]).to_affine();
let mut verifier_tr2 = Keccak256Transcript::<NE>::new(b"TestEval");
assert!(EvaluationEngine::<E, NE>::verify(
&vk,
&mut verifier_tr2,
&C,
&point,
&eval,
&bad_proof
)
.is_err());
for num_vars in [4, 5, 6] {
prove_verify_from_num_vars::<_, EvaluationEngine<E, NE>>(num_vars);
}
}
}
14 changes: 14 additions & 0 deletions src/provider/ipa_pc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,3 +407,17 @@ where
}
}
}

#[cfg(test)]
mod test {
use crate::provider::ipa_pc::EvaluationEngine;
use crate::provider::util::test_utils::prove_verify_from_num_vars;
use crate::provider::GrumpkinEngine;

#[test]
fn test_multiple_polynomial_size() {
for num_vars in [4, 5, 6] {
prove_verify_from_num_vars::<_, EvaluationEngine<GrumpkinEngine>>(num_vars);
}
}
}
2 changes: 1 addition & 1 deletion src/provider/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub(crate) mod traits;
// a non-hiding variant of {kzg, zeromorph}
pub(crate) mod kzg_commitment;
pub(crate) mod non_hiding_kzg;
mod util;
pub(crate) mod util;

// crate-private modules
mod keccak;
Expand Down
36 changes: 22 additions & 14 deletions src/provider/non_hiding_zeromorph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -438,19 +438,19 @@ fn eval_and_quotient_scalars<F: Field>(y: F, x: F, z: F, point: &[F]) -> (F, (Ve
// = [- (y^i * x^(2^num_vars - d_i - 1) + z * (x^(2^i) * Φ_(n-i-1)(x^(2^(i+1))) - u_i * Φ_(n-i)(x^(2^i)))), i ∈ [0, num_vars-1]]
#[allow(clippy::disallowed_methods)]
let q_scalars = iter::successors(Some(F::ONE), |acc| Some(*acc * y)).take(num_vars)
.zip_eq(offsets_of_x)
// length: num_vars + 1
.zip(squares_of_x)
// length: num_vars + 1
.zip(&vs)
.zip_eq(&vs[1..])
.zip_eq(point.iter().rev()) // assume variables come in BE form
.map(
|(((((power_of_y, offset_of_x), square_of_x), v_i), v_j), u_i)| {
(-(power_of_y * offset_of_x), -(z * (square_of_x * v_j - *u_i * v_i)))
},
)
.unzip();
.zip_eq(offsets_of_x)
// length: num_vars + 1
.zip(squares_of_x)
// length: num_vars + 1
.zip(&vs)
.zip_eq(&vs[1..])
.zip_eq(point.iter().rev()) // assume variables come in BE form
.map(
|(((((power_of_y, offset_of_x), square_of_x), v_i), v_j), u_i)| {
(-(power_of_y * offset_of_x), -(z * (square_of_x * v_j - *u_i * v_i)))
},
)
.unzip();

// -vs[0] * z = -z * (x^(2^num_vars) - 1) / (x - 1) = -z Φ_n(x)
(-vs[0] * z, q_scalars)
Expand Down Expand Up @@ -530,7 +530,8 @@ mod test {
batched_lifted_degree_quotient, eval_and_quotient_scalars, trim, ZMEvaluation, ZMPCS,
},
traits::DlogGroup,
Bn256Engine,
util::test_utils::prove_verify_from_num_vars,
Bn256Engine, Bn256EngineZM,
},
spartan::polys::multilinear::MultilinearPolynomial,
traits::{Engine as NovaEngine, Group, TranscriptEngineTrait, TranscriptReprTrait},
Expand Down Expand Up @@ -593,6 +594,13 @@ mod test {
commit_open_verify_with::<Bn256, Bn256Engine>();
}

#[test]
fn test_multiple_polynomial_size() {
for num_vars in [4, 5, 6] {
prove_verify_from_num_vars::<_, ZMPCS<Bn256, Bn256EngineZM>>(num_vars);
}
}

#[test]
fn test_quotients() {
// Define size parameters
Expand Down
Loading

0 comments on commit d41e56b

Please sign in to comment.