arnaucube
arnaucube/NovaPublic Self-hosted repository
Nova/benches/compressed-snark.rs
benches/compressed-snark.rsRaw
  1#![allow(non_snake_case)]
  2
  3use bellperson::{gadgets::num::AllocatedNum, ConstraintSystem, SynthesisError};
  4use core::marker::PhantomData;
  5use criterion::*;
  6use ff::PrimeField;
  7use nova_snark::{
  8  traits::{
  9    circuit::{StepCircuit, TrivialTestCircuit},
 10    Group,
 11  },
 12  CompressedSNARK, PublicParams, RecursiveSNARK,
 13};
 14use std::time::Duration;
 15
 16type G1 = pasta_curves::pallas::Point;
 17type G2 = pasta_curves::vesta::Point;
 18type EE1 = nova_snark::provider::ipa_pc::EvaluationEngine<G1>;
 19type EE2 = nova_snark::provider::ipa_pc::EvaluationEngine<G2>;
 20// SNARKs without computational commitments
 21type S1 = nova_snark::spartan::snark::RelaxedR1CSSNARK<G1, EE1>;
 22type S2 = nova_snark::spartan::snark::RelaxedR1CSSNARK<G2, EE2>;
 23// SNARKs with computational commitments
 24type SS1 = nova_snark::spartan::ppsnark::RelaxedR1CSSNARK<G1, EE1>;
 25type SS2 = nova_snark::spartan::ppsnark::RelaxedR1CSSNARK<G2, EE2>;
 26type C1 = NonTrivialTestCircuit<<G1 as Group>::Scalar>;
 27type C2 = TrivialTestCircuit<<G2 as Group>::Scalar>;
 28
 29// To run these benchmarks, first download `criterion` with `cargo install cargo install cargo-criterion`.
 30// Then `cargo criterion --bench compressed-snark`. The results are located in `target/criterion/data/<name-of-benchmark>`.
 31// For flamegraphs, run `cargo criterion --bench compressed-snark --features flamegraph -- --profile-time <secs>`.
 32// The results are located in `target/criterion/profile/<name-of-benchmark>`.
 33cfg_if::cfg_if! {
 34  if #[cfg(feature = "flamegraph")] {
 35    criterion_group! {
 36      name = compressed_snark;
 37      config = Criterion::default().warm_up_time(Duration::from_millis(3000)).with_profiler(pprof::criterion::PProfProfiler::new(100, pprof::criterion::Output::Flamegraph(None)));
 38      targets = bench_compressed_snark, bench_compressed_snark_with_computational_commitments
 39    }
 40  } else {
 41    criterion_group! {
 42      name = compressed_snark;
 43      config = Criterion::default().warm_up_time(Duration::from_millis(3000));
 44      targets = bench_compressed_snark, bench_compressed_snark_with_computational_commitments
 45    }
 46  }
 47}
 48
 49criterion_main!(compressed_snark);
 50
 51fn bench_compressed_snark(c: &mut Criterion) {
 52  let num_samples = 10;
 53  let num_cons_verifier_circuit_primary = 9819;
 54  // we vary the number of constraints in the step circuit
 55  for &num_cons_in_augmented_circuit in
 56    [9819, 16384, 32768, 65536, 131072, 262144, 524288, 1048576].iter()
 57  {
 58    // number of constraints in the step circuit
 59    let num_cons = num_cons_in_augmented_circuit - num_cons_verifier_circuit_primary;
 60
 61    let mut group = c.benchmark_group(format!("CompressedSNARK-StepCircuitSize-{num_cons}"));
 62    group.sample_size(num_samples);
 63
 64    let c_primary = NonTrivialTestCircuit::new(num_cons);
 65    let c_secondary = TrivialTestCircuit::default();
 66
 67    // Produce public parameters
 68    let pp = PublicParams::<G1, G2, C1, C2>::setup(c_primary.clone(), c_secondary.clone());
 69
 70    // Produce prover and verifier keys for CompressedSNARK
 71    let (pk, vk) = CompressedSNARK::<_, _, _, _, S1, S2>::setup(&pp).unwrap();
 72
 73    // produce a recursive SNARK
 74    let num_steps = 3;
 75    let mut recursive_snark: RecursiveSNARK<G1, G2, C1, C2> = RecursiveSNARK::new(
 76      &pp,
 77      &c_primary,
 78      &c_secondary,
 79      vec![<G1 as Group>::Scalar::from(2u64)],
 80      vec![<G2 as Group>::Scalar::from(2u64)],
 81    );
 82
 83    for i in 0..num_steps {
 84      let res = recursive_snark.prove_step(
 85        &pp,
 86        &c_primary,
 87        &c_secondary,
 88        vec![<G1 as Group>::Scalar::from(2u64)],
 89        vec![<G2 as Group>::Scalar::from(2u64)],
 90      );
 91      assert!(res.is_ok());
 92
 93      // verify the recursive snark at each step of recursion
 94      let res = recursive_snark.verify(
 95        &pp,
 96        i + 1,
 97        &[<G1 as Group>::Scalar::from(2u64)],
 98        &[<G2 as Group>::Scalar::from(2u64)],
 99      );
100      assert!(res.is_ok());
101    }
102
103    // Bench time to produce a compressed SNARK
104    group.bench_function("Prove", |b| {
105      b.iter(|| {
106        assert!(CompressedSNARK::<_, _, _, _, S1, S2>::prove(
107          black_box(&pp),
108          black_box(&pk),
109          black_box(&recursive_snark)
110        )
111        .is_ok());
112      })
113    });
114    let res = CompressedSNARK::<_, _, _, _, S1, S2>::prove(&pp, &pk, &recursive_snark);
115    assert!(res.is_ok());
116    let compressed_snark = res.unwrap();
117
118    // Benchmark the verification time
119    group.bench_function("Verify", |b| {
120      b.iter(|| {
121        assert!(black_box(&compressed_snark)
122          .verify(
123            black_box(&vk),
124            black_box(num_steps),
125            black_box(vec![<G1 as Group>::Scalar::from(2u64)]),
126            black_box(vec![<G2 as Group>::Scalar::from(2u64)]),
127          )
128          .is_ok());
129      })
130    });
131
132    group.finish();
133  }
134}
135
136fn bench_compressed_snark_with_computational_commitments(c: &mut Criterion) {
137  let num_samples = 10;
138  let num_cons_verifier_circuit_primary = 9819;
139  // we vary the number of constraints in the step circuit
140  for &num_cons_in_augmented_circuit in [9819, 16384, 32768, 65536, 131072, 262144].iter() {
141    // number of constraints in the step circuit
142    let num_cons = num_cons_in_augmented_circuit - num_cons_verifier_circuit_primary;
143
144    let mut group = c.benchmark_group(format!(
145      "CompressedSNARK-Commitments-StepCircuitSize-{num_cons}"
146    ));
147    group
148      .sampling_mode(SamplingMode::Flat)
149      .sample_size(num_samples);
150
151    let c_primary = NonTrivialTestCircuit::new(num_cons);
152    let c_secondary = TrivialTestCircuit::default();
153
154    // Produce public parameters
155    let pp = PublicParams::<G1, G2, C1, C2>::setup(c_primary.clone(), c_secondary.clone());
156
157    // Produce prover and verifier keys for CompressedSNARK
158    let (pk, vk) = CompressedSNARK::<_, _, _, _, SS1, SS2>::setup(&pp).unwrap();
159
160    // produce a recursive SNARK
161    let num_steps = 3;
162    let mut recursive_snark: RecursiveSNARK<G1, G2, C1, C2> = RecursiveSNARK::new(
163      &pp,
164      &c_primary,
165      &c_secondary,
166      vec![<G1 as Group>::Scalar::from(2u64)],
167      vec![<G2 as Group>::Scalar::from(2u64)],
168    );
169
170    for i in 0..num_steps {
171      let res = recursive_snark.prove_step(
172        &pp,
173        &c_primary,
174        &c_secondary,
175        vec![<G1 as Group>::Scalar::from(2u64)],
176        vec![<G2 as Group>::Scalar::from(2u64)],
177      );
178      assert!(res.is_ok());
179
180      // verify the recursive snark at each step of recursion
181      let res = recursive_snark.verify(
182        &pp,
183        i + 1,
184        &[<G1 as Group>::Scalar::from(2u64)],
185        &[<G2 as Group>::Scalar::from(2u64)],
186      );
187      assert!(res.is_ok());
188    }
189
190    // Bench time to produce a compressed SNARK
191    group.bench_function("Prove", |b| {
192      b.iter(|| {
193        assert!(CompressedSNARK::<_, _, _, _, SS1, SS2>::prove(
194          black_box(&pp),
195          black_box(&pk),
196          black_box(&recursive_snark)
197        )
198        .is_ok());
199      })
200    });
201    let res = CompressedSNARK::<_, _, _, _, SS1, SS2>::prove(&pp, &pk, &recursive_snark);
202    assert!(res.is_ok());
203    let compressed_snark = res.unwrap();
204
205    // Benchmark the verification time
206    group.bench_function("Verify", |b| {
207      b.iter(|| {
208        assert!(black_box(&compressed_snark)
209          .verify(
210            black_box(&vk),
211            black_box(num_steps),
212            black_box(vec![<G1 as Group>::Scalar::from(2u64)]),
213            black_box(vec![<G2 as Group>::Scalar::from(2u64)]),
214          )
215          .is_ok());
216      })
217    });
218
219    group.finish();
220  }
221}
222
223#[derive(Clone, Debug, Default)]
224struct NonTrivialTestCircuit<F: PrimeField> {
225  num_cons: usize,
226  _p: PhantomData<F>,
227}
228
229impl<F> NonTrivialTestCircuit<F>
230where
231  F: PrimeField,
232{
233  pub fn new(num_cons: usize) -> Self {
234    Self {
235      num_cons,
236      _p: Default::default(),
237    }
238  }
239}
240impl<F> StepCircuit<F> for NonTrivialTestCircuit<F>
241where
242  F: PrimeField,
243{
244  fn arity(&self) -> usize {
245    1
246  }
247
248  fn synthesize<CS: ConstraintSystem<F>>(
249    &self,
250    cs: &mut CS,
251    z: &[AllocatedNum<F>],
252  ) -> Result<Vec<AllocatedNum<F>>, SynthesisError> {
253    // Consider a an equation: `x^2 = y`, where `x` and `y` are respectively the input and output.
254    let mut x = z[0].clone();
255    let mut y = x.clone();
256    for i in 0..self.num_cons {
257      y = x.square(cs.namespace(|| format!("x_sq_{i}")))?;
258      x = y.clone();
259    }
260    Ok(vec![y])
261  }
262
263  fn output(&self, z: &[F]) -> Vec<F> {
264    let mut x = z[0];
265    let mut y = x;
266    for _i in 0..self.num_cons {
267      y = x * x;
268      x = y;
269    }
270    vec![y]
271  }
272}