-
Notifications
You must be signed in to change notification settings - Fork 207
/
Copy pathcompressed-snark.rs
223 lines (199 loc) · 6.66 KB
/
compressed-snark.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
#![allow(non_snake_case)]
use core::marker::PhantomData;
use criterion::{measurement::WallTime, *};
use ff::PrimeField;
use nova_snark::{
frontend::{num::AllocatedNum, ConstraintSystem, SynthesisError},
nova::{CompressedSNARK, PublicParams, RecursiveSNARK},
provider::{Bn256EngineKZG, GrumpkinEngine},
traits::{
circuit::{StepCircuit, TrivialCircuit},
snark::RelaxedR1CSSNARKTrait,
Engine,
},
};
use std::time::Duration;
type E1 = Bn256EngineKZG;
type E2 = GrumpkinEngine;
type EE1 = nova_snark::provider::hyperkzg::EvaluationEngine<E1>;
type EE2 = nova_snark::provider::ipa_pc::EvaluationEngine<E2>;
// SNARKs without computational commitments
type S1 = nova_snark::spartan::snark::RelaxedR1CSSNARK<E1, EE1>;
type S2 = nova_snark::spartan::snark::RelaxedR1CSSNARK<E2, EE2>;
// SNARKs with computational commitments for the primary curve
type SS1 = nova_snark::spartan::ppsnark::RelaxedR1CSSNARK<E1, EE1>;
type SS2 = nova_snark::spartan::snark::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`.
// 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>`.
cfg_if::cfg_if! {
if #[cfg(feature = "flamegraph")] {
criterion_group! {
name = compressed_snark;
config = Criterion::default().warm_up_time(Duration::from_millis(3000)).with_profiler(pprof2::criterion::PProfProfiler::new(100, pprof2::criterion::Output::Flamegraph(None)));
targets = bench_compressed_snark, bench_compressed_snark_with_computational_commitments
}
} else {
criterion_group! {
name = compressed_snark;
config = Criterion::default().warm_up_time(Duration::from_millis(3000));
targets = bench_compressed_snark, bench_compressed_snark_with_computational_commitments,
}
}
}
criterion_main!(compressed_snark);
// This should match the value for the primary in test_recursive_circuit_bn256_grumpkin
const NUM_CONS_VERIFIER_CIRCUIT_PRIMARY: usize = 9985;
const NUM_SAMPLES: usize = 10;
/// Benchmarks the compressed SNARK at a provided number of constraints
///
/// Parameters
/// - `group``: the criterion benchmark group
/// - `num_cons`: the number of constraints in the step circuit
fn bench_compressed_snark_internal<S1: RelaxedR1CSSNARKTrait<E1>, S2: RelaxedR1CSSNARKTrait<E2>>(
group: &mut BenchmarkGroup<'_, WallTime>,
num_cons: usize,
) {
let c_primary = NonTrivialCircuit::new(num_cons);
let c_secondary = TrivialCircuit::default();
// Produce public parameters
let pp = PublicParams::<E1, E2, C1, C2>::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();
// produce a recursive SNARK
let num_steps = 3;
let mut recursive_snark: RecursiveSNARK<E1, E2, C1, C2> = RecursiveSNARK::new(
&pp,
&c_primary,
&c_secondary,
&[<E1 as Engine>::Scalar::from(2u64)],
&[<E2 as Engine>::Scalar::from(2u64)],
)
.unwrap();
for i in 0..num_steps {
let res = recursive_snark.prove_step(&pp, &c_primary, &c_secondary);
assert!(res.is_ok());
// verify the recursive snark at each step of recursion
let res = recursive_snark.verify(
&pp,
i + 1,
&[<E1 as Engine>::Scalar::from(2u64)],
&[<E2 as Engine>::Scalar::from(2u64)],
);
assert!(res.is_ok());
}
// Bench time to produce a compressed SNARK
group.bench_function("Prove", |b| {
b.iter(|| {
assert!(CompressedSNARK::<_, _, _, _, S1, S2>::prove(
black_box(&pp),
black_box(&pk),
black_box(&recursive_snark),
)
.is_ok());
})
});
let res = CompressedSNARK::<_, _, _, _, S1, S2>::prove(&pp, &pk, &recursive_snark);
assert!(res.is_ok());
let compressed_snark = res.unwrap();
// Benchmark the verification time
group.bench_function("Verify", |b| {
b.iter(|| {
assert!(black_box(&compressed_snark)
.verify(
black_box(&vk),
black_box(num_steps),
black_box(&[<E1 as Engine>::Scalar::from(2u64)]),
black_box(&[<E2 as Engine>::Scalar::from(2u64)]),
)
.is_ok());
})
});
}
fn bench_compressed_snark(c: &mut Criterion) {
// we vary the number of constraints in the step circuit
for &num_cons_in_augmented_circuit in [
NUM_CONS_VERIFIER_CIRCUIT_PRIMARY,
16384,
32768,
65536,
131072,
262144,
524288,
1048576,
]
.iter()
{
// number of constraints in the step circuit
let num_cons = num_cons_in_augmented_circuit - NUM_CONS_VERIFIER_CIRCUIT_PRIMARY;
let mut group = c.benchmark_group(format!("CompressedSNARK-StepCircuitSize-{num_cons}"));
group.sample_size(NUM_SAMPLES);
bench_compressed_snark_internal::<S1, S2>(&mut group, num_cons);
group.finish();
}
}
fn bench_compressed_snark_with_computational_commitments(c: &mut Criterion) {
// we vary the number of constraints in the step circuit
for &num_cons_in_augmented_circuit in [
NUM_CONS_VERIFIER_CIRCUIT_PRIMARY,
16384,
32768,
65536,
131072,
262144,
]
.iter()
{
// number of constraints in the step circuit
let num_cons = num_cons_in_augmented_circuit - NUM_CONS_VERIFIER_CIRCUIT_PRIMARY;
let mut group = c.benchmark_group(format!(
"CompressedSNARK-Commitments-StepCircuitSize-{num_cons}"
));
group
.sampling_mode(SamplingMode::Flat)
.sample_size(NUM_SAMPLES);
bench_compressed_snark_internal::<SS1, SS2>(&mut group, num_cons);
group.finish();
}
}
#[derive(Clone, Debug, Default)]
struct NonTrivialCircuit<F: PrimeField> {
num_cons: usize,
_p: PhantomData<F>,
}
impl<F: PrimeField> NonTrivialCircuit<F> {
pub fn new(num_cons: usize) -> Self {
Self {
num_cons,
_p: PhantomData,
}
}
}
impl<F: PrimeField> StepCircuit<F> for NonTrivialCircuit<F> {
fn arity(&self) -> usize {
1
}
fn synthesize<CS: ConstraintSystem<F>>(
&self,
cs: &mut CS,
z: &[AllocatedNum<F>],
) -> Result<Vec<AllocatedNum<F>>, SynthesisError> {
// Consider an equation: `x^2 = y`, where `x` and `y` are respectively the input and output.
let mut x = z[0].clone();
let mut y = x.clone();
for i in 0..self.num_cons {
y = x.square(cs.namespace(|| format!("x_sq_{i}")))?;
x = y.clone();
}
Ok(vec![y])
}
}