arnaucube
arnaucube/NovaPublic Self-hosted repository
Nova/benches/recursive-snark.rs
benches/recursive-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  PublicParams, RecursiveSNARK,
 13};
 14use std::time::Duration;
 15
 16type G1 = pasta_curves::pallas::Point;
 17type G2 = pasta_curves::vesta::Point;
 18type C1 = NonTrivialTestCircuit<<G1 as Group>::Scalar>;
 19type C2 = TrivialTestCircuit<<G2 as Group>::Scalar>;
 20
 21criterion_group! {
 22name = recursive_snark;
 23config = Criterion::default().warm_up_time(Duration::from_millis(3000));
 24targets = bench_recursive_snark
 25}
 26
 27criterion_main!(recursive_snark);
 28
 29fn bench_recursive_snark(c: &mut Criterion) {
 30  // we vary the number of constraints in the step circuit
 31  for &log_num_cons_in_step_circuit in [0, 14, 15, 16, 17, 18, 19, 20].iter() {
 32    let num_cons = 1 << log_num_cons_in_step_circuit;
 33
 34    let mut group = c.benchmark_group(format!("RecursiveSNARK-StepCircuitSize-{}", num_cons));
 35    group.sample_size(10);
 36
 37    // Produce public parameters
 38    let pp = PublicParams::<G1, G2, C1, C2>::setup(
 39      NonTrivialTestCircuit::new(num_cons),
 40      TrivialTestCircuit::default(),
 41    );
 42
 43    // Bench time to produce a recursive SNARK;
 44    // we execute a certain number of warm-up steps since executing
 45    // the first step is cheaper than other steps owing to the presence of
 46    // a lot of zeros in the satisfying assignment
 47    let num_warmup_steps = 10;
 48    let mut recursive_snark: Option<RecursiveSNARK<G1, G2, C1, C2>> = None;
 49
 50    for i in 0..num_warmup_steps {
 51      let res = RecursiveSNARK::prove_step(
 52        &pp,
 53        recursive_snark,
 54        NonTrivialTestCircuit::new(num_cons),
 55        TrivialTestCircuit::default(),
 56        <G1 as Group>::Scalar::one(),
 57        <G2 as Group>::Scalar::one(),
 58      );
 59      assert!(res.is_ok());
 60      let recursive_snark_unwrapped = res.unwrap();
 61
 62      // verify the recursive snark at each step of recursion
 63      let res = recursive_snark_unwrapped.verify(
 64        &pp,
 65        i + 1,
 66        <G1 as Group>::Scalar::one(),
 67        <G2 as Group>::Scalar::one(),
 68      );
 69      assert!(res.is_ok());
 70
 71      // set the running variable for the next iteration
 72      recursive_snark = Some(recursive_snark_unwrapped);
 73    }
 74
 75    group.bench_function("Prove", |b| {
 76      b.iter(|| {
 77        // produce a recursive SNARK for a step of the recursion
 78        assert!(RecursiveSNARK::prove_step(
 79          black_box(&pp),
 80          black_box(recursive_snark.clone()),
 81          black_box(NonTrivialTestCircuit::new(num_cons)),
 82          black_box(TrivialTestCircuit::default()),
 83          black_box(<G1 as Group>::Scalar::one()),
 84          black_box(<G2 as Group>::Scalar::one()),
 85        )
 86        .is_ok());
 87      })
 88    });
 89
 90    let recursive_snark = recursive_snark.unwrap();
 91
 92    // Benchmark the verification time
 93    group.bench_function("Verify", |b| {
 94      b.iter(|| {
 95        assert!(black_box(&recursive_snark)
 96          .verify(
 97            black_box(&pp),
 98            black_box(num_warmup_steps),
 99            black_box(<G1 as Group>::Scalar::one()),
100            black_box(<G2 as Group>::Scalar::one()),
101          )
102          .is_ok());
103      });
104    });
105    group.finish();
106  }
107}
108
109#[derive(Clone, Debug, Default)]
110struct NonTrivialTestCircuit<F: PrimeField> {
111  num_cons: usize,
112  _p: PhantomData<F>,
113}
114
115impl<F> NonTrivialTestCircuit<F>
116where
117  F: PrimeField,
118{
119  pub fn new(num_cons: usize) -> Self {
120    Self {
121      num_cons,
122      _p: Default::default(),
123    }
124  }
125}
126impl<F> StepCircuit<F> for NonTrivialTestCircuit<F>
127where
128  F: PrimeField,
129{
130  fn synthesize<CS: ConstraintSystem<F>>(
131    &self,
132    cs: &mut CS,
133    z: AllocatedNum<F>,
134  ) -> Result<AllocatedNum<F>, SynthesisError> {
135    // Consider a an equation: `x^2 = y`, where `x` and `y` are respectively the input and output.
136    let mut x = z;
137    let mut y = x.clone();
138    for i in 0..self.num_cons {
139      y = x.square(cs.namespace(|| format!("x_sq_{}", i)))?;
140      x = y.clone();
141    }
142    Ok(y)
143  }
144
145  fn compute(&self, z: &F) -> F {
146    let mut x = *z;
147    let mut y = x;
148    for _i in 0..self.num_cons {
149      y = x * x;
150      x = y;
151    }
152    y
153  }
154}