Nova/src/lib.rs
src/lib.rsRaw
1//! This library implements Nova, a high-speed recursive SNARK.
2#![deny(
3 warnings,
4 unused,
5 future_incompatible,
6 nonstandard_style,
7 rust_2018_idioms,
8 missing_docs
9)]
10#![allow(non_snake_case)]
11#![allow(clippy::type_complexity)]
12#![forbid(unsafe_code)]
13
14// private modules
15mod bellperson;
16mod circuit;
17mod constants;
18mod nifs;
19mod r1cs;
20
21// public modules
22pub mod errors;
23pub mod gadgets;
24pub mod provider;
25pub mod spartan;
26pub mod traits;
27
28use crate::bellperson::{
29 r1cs::{NovaShape, NovaWitness},
30 shape_cs::ShapeCS,
31 solver::SatisfyingAssignment,
32};
33use ::bellperson::{Circuit, ConstraintSystem};
34use circuit::{NovaAugmentedCircuit, NovaAugmentedCircuitInputs, NovaAugmentedCircuitParams};
35use constants::{BN_LIMB_WIDTH, BN_N_LIMBS, NUM_FE_WITHOUT_IO_FOR_CRHF, NUM_HASH_BITS};
36use core::marker::PhantomData;
37use errors::NovaError;
38use ff::Field;
39use gadgets::utils::scalar_as_base;
40use nifs::NIFS;
41use r1cs::{R1CSInstance, R1CSShape, R1CSWitness, RelaxedR1CSInstance, RelaxedR1CSWitness};
42use serde::{Deserialize, Serialize};
43use traits::{
44 circuit::StepCircuit,
45 commitment::{CommitmentEngineTrait, CommitmentTrait},
46 snark::RelaxedR1CSSNARKTrait,
47 AbsorbInROTrait, Group, ROConstants, ROConstantsCircuit, ROConstantsTrait, ROTrait,
48};
49
50/// A type that holds public parameters of Nova
51#[derive(Serialize, Deserialize)]
52#[serde(bound = "")]
53pub struct PublicParams<G1, G2, C1, C2>
54where
55 G1: Group<Base = <G2 as Group>::Scalar>,
56 G2: Group<Base = <G1 as Group>::Scalar>,
57 C1: StepCircuit<G1::Scalar>,
58 C2: StepCircuit<G2::Scalar>,
59{
60 F_arity_primary: usize,
61 F_arity_secondary: usize,
62 ro_consts_primary: ROConstants<G1>,
63 ro_consts_circuit_primary: ROConstantsCircuit<G2>,
64 ck_primary: CommitmentKey<G1>,
65 r1cs_shape_primary: R1CSShape<G1>,
66 ro_consts_secondary: ROConstants<G2>,
67 ro_consts_circuit_secondary: ROConstantsCircuit<G1>,
68 ck_secondary: CommitmentKey<G2>,
69 r1cs_shape_secondary: R1CSShape<G2>,
70 augmented_circuit_params_primary: NovaAugmentedCircuitParams,
71 augmented_circuit_params_secondary: NovaAugmentedCircuitParams,
72 _p_c1: PhantomData<C1>,
73 _p_c2: PhantomData<C2>,
74}
75
76impl<G1, G2, C1, C2> PublicParams<G1, G2, C1, C2>
77where
78 G1: Group<Base = <G2 as Group>::Scalar>,
79 G2: Group<Base = <G1 as Group>::Scalar>,
80 C1: StepCircuit<G1::Scalar>,
81 C2: StepCircuit<G2::Scalar>,
82{
83 /// Create a new `PublicParams`
84 pub fn setup(c_primary: C1, c_secondary: C2) -> Self {
85 let augmented_circuit_params_primary =
86 NovaAugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, true);
87 let augmented_circuit_params_secondary =
88 NovaAugmentedCircuitParams::new(BN_LIMB_WIDTH, BN_N_LIMBS, false);
89
90 let ro_consts_primary: ROConstants<G1> = ROConstants::<G1>::new();
91 let ro_consts_secondary: ROConstants<G2> = ROConstants::<G2>::new();
92
93 let F_arity_primary = c_primary.arity();
94 let F_arity_secondary = c_secondary.arity();
95
96 // ro_consts_circuit_primary are parameterized by G2 because the type alias uses G2::Base = G1::Scalar
97 let ro_consts_circuit_primary: ROConstantsCircuit<G2> = ROConstantsCircuit::<G2>::new();
98 let ro_consts_circuit_secondary: ROConstantsCircuit<G1> = ROConstantsCircuit::<G1>::new();
99
100 // Initialize ck for the primary
101 let circuit_primary: NovaAugmentedCircuit<G2, C1> = NovaAugmentedCircuit::new(
102 augmented_circuit_params_primary.clone(),
103 None,
104 c_primary,
105 ro_consts_circuit_primary.clone(),
106 );
107 let mut cs: ShapeCS<G1> = ShapeCS::new();
108 let _ = circuit_primary.synthesize(&mut cs);
109 let (r1cs_shape_primary, ck_primary) = cs.r1cs_shape();
110
111 // Initialize ck for the secondary
112 let circuit_secondary: NovaAugmentedCircuit<G1, C2> = NovaAugmentedCircuit::new(
113 augmented_circuit_params_secondary.clone(),
114 None,
115 c_secondary,
116 ro_consts_circuit_secondary.clone(),
117 );
118 let mut cs: ShapeCS<G2> = ShapeCS::new();
119 let _ = circuit_secondary.synthesize(&mut cs);
120 let (r1cs_shape_secondary, ck_secondary) = cs.r1cs_shape();
121
122 Self {
123 F_arity_primary,
124 F_arity_secondary,
125 ro_consts_primary,
126 ro_consts_circuit_primary,
127 ck_primary,
128 r1cs_shape_primary,
129 ro_consts_secondary,
130 ro_consts_circuit_secondary,
131 ck_secondary,
132 r1cs_shape_secondary,
133 augmented_circuit_params_primary,
134 augmented_circuit_params_secondary,
135 _p_c1: Default::default(),
136 _p_c2: Default::default(),
137 }
138 }
139
140 /// Returns the number of constraints in the primary and secondary circuits
141 pub fn num_constraints(&self) -> (usize, usize) {
142 (
143 self.r1cs_shape_primary.num_cons,
144 self.r1cs_shape_secondary.num_cons,
145 )
146 }
147
148 /// Returns the number of variables in the primary and secondary circuits
149 pub fn num_variables(&self) -> (usize, usize) {
150 (
151 self.r1cs_shape_primary.num_vars,
152 self.r1cs_shape_secondary.num_vars,
153 )
154 }
155}
156
157/// A SNARK that proves the correct execution of an incremental computation
158#[derive(Clone, Debug, Serialize, Deserialize)]
159#[serde(bound = "")]
160pub struct RecursiveSNARK<G1, G2, C1, C2>
161where
162 G1: Group<Base = <G2 as Group>::Scalar>,
163 G2: Group<Base = <G1 as Group>::Scalar>,
164 C1: StepCircuit<G1::Scalar>,
165 C2: StepCircuit<G2::Scalar>,
166{
167 r_W_primary: RelaxedR1CSWitness<G1>,
168 r_U_primary: RelaxedR1CSInstance<G1>,
169 l_w_primary: R1CSWitness<G1>,
170 l_u_primary: R1CSInstance<G1>,
171 r_W_secondary: RelaxedR1CSWitness<G2>,
172 r_U_secondary: RelaxedR1CSInstance<G2>,
173 l_w_secondary: R1CSWitness<G2>,
174 l_u_secondary: R1CSInstance<G2>,
175 i: usize,
176 zi_primary: Vec<G1::Scalar>,
177 zi_secondary: Vec<G2::Scalar>,
178 _p_c1: PhantomData<C1>,
179 _p_c2: PhantomData<C2>,
180}
181
182impl<G1, G2, C1, C2> RecursiveSNARK<G1, G2, C1, C2>
183where
184 G1: Group<Base = <G2 as Group>::Scalar>,
185 G2: Group<Base = <G1 as Group>::Scalar>,
186 C1: StepCircuit<G1::Scalar>,
187 C2: StepCircuit<G2::Scalar>,
188{
189 /// Create a new `RecursiveSNARK` (or updates the provided `RecursiveSNARK`)
190 /// by executing a step of the incremental computation
191 pub fn prove_step(
192 pp: &PublicParams<G1, G2, C1, C2>,
193 recursive_snark: Option<Self>,
194 c_primary: C1,
195 c_secondary: C2,
196 z0_primary: Vec<G1::Scalar>,
197 z0_secondary: Vec<G2::Scalar>,
198 ) -> Result<Self, NovaError> {
199 if z0_primary.len() != pp.F_arity_primary || z0_secondary.len() != pp.F_arity_secondary {
200 return Err(NovaError::InvalidInitialInputLength);
201 }
202
203 match recursive_snark {
204 None => {
205 // base case for the primary
206 let mut cs_primary: SatisfyingAssignment<G1> = SatisfyingAssignment::new();
207 let inputs_primary: NovaAugmentedCircuitInputs<G2> = NovaAugmentedCircuitInputs::new(
208 pp.r1cs_shape_secondary.get_digest(),
209 G1::Scalar::zero(),
210 z0_primary.clone(),
211 None,
212 None,
213 None,
214 None,
215 );
216
217 let circuit_primary: NovaAugmentedCircuit<G2, C1> = NovaAugmentedCircuit::new(
218 pp.augmented_circuit_params_primary.clone(),
219 Some(inputs_primary),
220 c_primary.clone(),
221 pp.ro_consts_circuit_primary.clone(),
222 );
223 let _ = circuit_primary.synthesize(&mut cs_primary);
224 let (u_primary, w_primary) = cs_primary
225 .r1cs_instance_and_witness(&pp.r1cs_shape_primary, &pp.ck_primary)
226 .map_err(|_e| NovaError::UnSat)?;
227
228 // base case for the secondary
229 let mut cs_secondary: SatisfyingAssignment<G2> = SatisfyingAssignment::new();
230 let inputs_secondary: NovaAugmentedCircuitInputs<G1> = NovaAugmentedCircuitInputs::new(
231 pp.r1cs_shape_primary.get_digest(),
232 G2::Scalar::zero(),
233 z0_secondary.clone(),
234 None,
235 None,
236 Some(u_primary.clone()),
237 None,
238 );
239 let circuit_secondary: NovaAugmentedCircuit<G1, C2> = NovaAugmentedCircuit::new(
240 pp.augmented_circuit_params_secondary.clone(),
241 Some(inputs_secondary),
242 c_secondary.clone(),
243 pp.ro_consts_circuit_secondary.clone(),
244 );
245 let _ = circuit_secondary.synthesize(&mut cs_secondary);
246 let (u_secondary, w_secondary) = cs_secondary
247 .r1cs_instance_and_witness(&pp.r1cs_shape_secondary, &pp.ck_secondary)
248 .map_err(|_e| NovaError::UnSat)?;
249
250 // IVC proof for the primary circuit
251 let l_w_primary = w_primary;
252 let l_u_primary = u_primary;
253 let r_W_primary =
254 RelaxedR1CSWitness::from_r1cs_witness(&pp.r1cs_shape_primary, &l_w_primary);
255 let r_U_primary = RelaxedR1CSInstance::from_r1cs_instance(
256 &pp.ck_primary,
257 &pp.r1cs_shape_primary,
258 &l_u_primary,
259 );
260
261 // IVC proof of the secondary circuit
262 let l_w_secondary = w_secondary;
263 let l_u_secondary = u_secondary;
264 let r_W_secondary = RelaxedR1CSWitness::<G2>::default(&pp.r1cs_shape_secondary);
265 let r_U_secondary =
266 RelaxedR1CSInstance::<G2>::default(&pp.ck_secondary, &pp.r1cs_shape_secondary);
267
268 // Outputs of the two circuits thus far
269 let zi_primary = c_primary.output(&z0_primary);
270 let zi_secondary = c_secondary.output(&z0_secondary);
271
272 if zi_primary.len() != pp.F_arity_primary || zi_secondary.len() != pp.F_arity_secondary {
273 return Err(NovaError::InvalidStepOutputLength);
274 }
275
276 Ok(Self {
277 r_W_primary,
278 r_U_primary,
279 l_w_primary,
280 l_u_primary,
281 r_W_secondary,
282 r_U_secondary,
283 l_w_secondary,
284 l_u_secondary,
285 i: 1_usize,
286 zi_primary,
287 zi_secondary,
288 _p_c1: Default::default(),
289 _p_c2: Default::default(),
290 })
291 }
292 Some(r_snark) => {
293 // fold the secondary circuit's instance
294 let (nifs_secondary, (r_U_secondary, r_W_secondary)) = NIFS::prove(
295 &pp.ck_secondary,
296 &pp.ro_consts_secondary,
297 &pp.r1cs_shape_secondary,
298 &r_snark.r_U_secondary,
299 &r_snark.r_W_secondary,
300 &r_snark.l_u_secondary,
301 &r_snark.l_w_secondary,
302 )?;
303
304 let mut cs_primary: SatisfyingAssignment<G1> = SatisfyingAssignment::new();
305 let inputs_primary: NovaAugmentedCircuitInputs<G2> = NovaAugmentedCircuitInputs::new(
306 pp.r1cs_shape_secondary.get_digest(),
307 G1::Scalar::from(r_snark.i as u64),
308 z0_primary,
309 Some(r_snark.zi_primary.clone()),
310 Some(r_snark.r_U_secondary.clone()),
311 Some(r_snark.l_u_secondary.clone()),
312 Some(Commitment::<G2>::decompress(&nifs_secondary.comm_T)?),
313 );
314
315 let circuit_primary: NovaAugmentedCircuit<G2, C1> = NovaAugmentedCircuit::new(
316 pp.augmented_circuit_params_primary.clone(),
317 Some(inputs_primary),
318 c_primary.clone(),
319 pp.ro_consts_circuit_primary.clone(),
320 );
321 let _ = circuit_primary.synthesize(&mut cs_primary);
322
323 let (l_u_primary, l_w_primary) = cs_primary
324 .r1cs_instance_and_witness(&pp.r1cs_shape_primary, &pp.ck_primary)
325 .map_err(|_e| NovaError::UnSat)?;
326
327 // fold the primary circuit's instance
328 let (nifs_primary, (r_U_primary, r_W_primary)) = NIFS::prove(
329 &pp.ck_primary,
330 &pp.ro_consts_primary,
331 &pp.r1cs_shape_primary,
332 &r_snark.r_U_primary,
333 &r_snark.r_W_primary,
334 &l_u_primary,
335 &l_w_primary,
336 )?;
337
338 let mut cs_secondary: SatisfyingAssignment<G2> = SatisfyingAssignment::new();
339 let inputs_secondary: NovaAugmentedCircuitInputs<G1> = NovaAugmentedCircuitInputs::new(
340 pp.r1cs_shape_primary.get_digest(),
341 G2::Scalar::from(r_snark.i as u64),
342 z0_secondary,
343 Some(r_snark.zi_secondary.clone()),
344 Some(r_snark.r_U_primary.clone()),
345 Some(l_u_primary.clone()),
346 Some(Commitment::<G1>::decompress(&nifs_primary.comm_T)?),
347 );
348
349 let circuit_secondary: NovaAugmentedCircuit<G1, C2> = NovaAugmentedCircuit::new(
350 pp.augmented_circuit_params_secondary.clone(),
351 Some(inputs_secondary),
352 c_secondary.clone(),
353 pp.ro_consts_circuit_secondary.clone(),
354 );
355 let _ = circuit_secondary.synthesize(&mut cs_secondary);
356
357 let (l_u_secondary, l_w_secondary) = cs_secondary
358 .r1cs_instance_and_witness(&pp.r1cs_shape_secondary, &pp.ck_secondary)
359 .map_err(|_e| NovaError::UnSat)?;
360
361 // update the running instances and witnesses
362 let zi_primary = c_primary.output(&r_snark.zi_primary);
363 let zi_secondary = c_secondary.output(&r_snark.zi_secondary);
364
365 Ok(Self {
366 r_W_primary,
367 r_U_primary,
368 l_w_primary,
369 l_u_primary,
370 r_W_secondary,
371 r_U_secondary,
372 l_w_secondary,
373 l_u_secondary,
374 i: r_snark.i + 1,
375 zi_primary,
376 zi_secondary,
377 _p_c1: Default::default(),
378 _p_c2: Default::default(),
379 })
380 }
381 }
382 }
383
384 /// Verify the correctness of the `RecursiveSNARK`
385 pub fn verify(
386 &self,
387 pp: &PublicParams<G1, G2, C1, C2>,
388 num_steps: usize,
389 z0_primary: Vec<G1::Scalar>,
390 z0_secondary: Vec<G2::Scalar>,
391 ) -> Result<(Vec<G1::Scalar>, Vec<G2::Scalar>), NovaError> {
392 // number of steps cannot be zero
393 if num_steps == 0 {
394 return Err(NovaError::ProofVerifyError);
395 }
396
397 // check if the provided proof has executed num_steps
398 if self.i != num_steps {
399 return Err(NovaError::ProofVerifyError);
400 }
401
402 // check if the (relaxed) R1CS instances have two public outputs
403 if self.l_u_primary.X.len() != 2
404 || self.l_u_secondary.X.len() != 2
405 || self.r_U_primary.X.len() != 2
406 || self.r_U_secondary.X.len() != 2
407 {
408 return Err(NovaError::ProofVerifyError);
409 }
410
411 // check if the output hashes in R1CS instances point to the right running instances
412 let (hash_primary, hash_secondary) = {
413 let mut hasher = <G2 as Group>::RO::new(
414 pp.ro_consts_secondary.clone(),
415 NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * pp.F_arity_primary,
416 );
417 hasher.absorb(scalar_as_base::<G2>(pp.r1cs_shape_secondary.get_digest()));
418 hasher.absorb(G1::Scalar::from(num_steps as u64));
419 for e in &z0_primary {
420 hasher.absorb(*e);
421 }
422 for e in &self.zi_primary {
423 hasher.absorb(*e);
424 }
425 self.r_U_secondary.absorb_in_ro(&mut hasher);
426
427 let mut hasher2 = <G1 as Group>::RO::new(
428 pp.ro_consts_primary.clone(),
429 NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * pp.F_arity_secondary,
430 );
431 hasher2.absorb(scalar_as_base::<G1>(pp.r1cs_shape_primary.get_digest()));
432 hasher2.absorb(G2::Scalar::from(num_steps as u64));
433 for e in &z0_secondary {
434 hasher2.absorb(*e);
435 }
436 for e in &self.zi_secondary {
437 hasher2.absorb(*e);
438 }
439 self.r_U_primary.absorb_in_ro(&mut hasher2);
440
441 (
442 hasher.squeeze(NUM_HASH_BITS),
443 hasher2.squeeze(NUM_HASH_BITS),
444 )
445 };
446
447 if hash_primary != scalar_as_base::<G1>(self.l_u_primary.X[1])
448 || hash_secondary != scalar_as_base::<G2>(self.l_u_secondary.X[1])
449 {
450 return Err(NovaError::ProofVerifyError);
451 }
452
453 // check the satisfiability of the provided instances
454 let ((res_r_primary, res_l_primary), (res_r_secondary, res_l_secondary)) = rayon::join(
455 || {
456 rayon::join(
457 || {
458 pp.r1cs_shape_primary.is_sat_relaxed(
459 &pp.ck_primary,
460 &self.r_U_primary,
461 &self.r_W_primary,
462 )
463 },
464 || {
465 pp.r1cs_shape_primary
466 .is_sat(&pp.ck_primary, &self.l_u_primary, &self.l_w_primary)
467 },
468 )
469 },
470 || {
471 rayon::join(
472 || {
473 pp.r1cs_shape_secondary.is_sat_relaxed(
474 &pp.ck_secondary,
475 &self.r_U_secondary,
476 &self.r_W_secondary,
477 )
478 },
479 || {
480 pp.r1cs_shape_secondary.is_sat(
481 &pp.ck_secondary,
482 &self.l_u_secondary,
483 &self.l_w_secondary,
484 )
485 },
486 )
487 },
488 );
489
490 // check the returned res objects
491 res_r_primary?;
492 res_l_primary?;
493 res_r_secondary?;
494 res_l_secondary?;
495
496 Ok((self.zi_primary.clone(), self.zi_secondary.clone()))
497 }
498}
499
500/// A type that holds the prover key for `CompressedSNARK`
501#[derive(Clone, Debug, Serialize, Deserialize)]
502#[serde(bound = "")]
503pub struct ProverKey<G1, G2, C1, C2, S1, S2>
504where
505 G1: Group<Base = <G2 as Group>::Scalar>,
506 G2: Group<Base = <G1 as Group>::Scalar>,
507 C1: StepCircuit<G1::Scalar>,
508 C2: StepCircuit<G2::Scalar>,
509 S1: RelaxedR1CSSNARKTrait<G1>,
510 S2: RelaxedR1CSSNARKTrait<G2>,
511{
512 pk_primary: S1::ProverKey,
513 pk_secondary: S2::ProverKey,
514 _p_c1: PhantomData<C1>,
515 _p_c2: PhantomData<C2>,
516}
517
518/// A type that holds the verifier key for `CompressedSNARK`
519#[derive(Clone, Serialize, Deserialize)]
520#[serde(bound = "")]
521pub struct VerifierKey<G1, G2, C1, C2, S1, S2>
522where
523 G1: Group<Base = <G2 as Group>::Scalar>,
524 G2: Group<Base = <G1 as Group>::Scalar>,
525 C1: StepCircuit<G1::Scalar>,
526 C2: StepCircuit<G2::Scalar>,
527 S1: RelaxedR1CSSNARKTrait<G1>,
528 S2: RelaxedR1CSSNARKTrait<G2>,
529{
530 F_arity_primary: usize,
531 F_arity_secondary: usize,
532 ro_consts_primary: ROConstants<G1>,
533 ro_consts_secondary: ROConstants<G2>,
534 r1cs_shape_primary_digest: G1::Scalar,
535 r1cs_shape_secondary_digest: G2::Scalar,
536 vk_primary: S1::VerifierKey,
537 vk_secondary: S2::VerifierKey,
538 _p_c1: PhantomData<C1>,
539 _p_c2: PhantomData<C2>,
540}
541
542/// A SNARK that proves the knowledge of a valid `RecursiveSNARK`
543#[derive(Clone, Serialize, Deserialize)]
544#[serde(bound = "")]
545pub struct CompressedSNARK<G1, G2, C1, C2, S1, S2>
546where
547 G1: Group<Base = <G2 as Group>::Scalar>,
548 G2: Group<Base = <G1 as Group>::Scalar>,
549 C1: StepCircuit<G1::Scalar>,
550 C2: StepCircuit<G2::Scalar>,
551 S1: RelaxedR1CSSNARKTrait<G1>,
552 S2: RelaxedR1CSSNARKTrait<G2>,
553{
554 r_U_primary: RelaxedR1CSInstance<G1>,
555 l_u_primary: R1CSInstance<G1>,
556 nifs_primary: NIFS<G1>,
557 f_W_snark_primary: S1,
558
559 r_U_secondary: RelaxedR1CSInstance<G2>,
560 l_u_secondary: R1CSInstance<G2>,
561 nifs_secondary: NIFS<G2>,
562 f_W_snark_secondary: S2,
563
564 zn_primary: Vec<G1::Scalar>,
565 zn_secondary: Vec<G2::Scalar>,
566
567 _p_c1: PhantomData<C1>,
568 _p_c2: PhantomData<C2>,
569}
570
571impl<G1, G2, C1, C2, S1, S2> CompressedSNARK<G1, G2, C1, C2, S1, S2>
572where
573 G1: Group<Base = <G2 as Group>::Scalar>,
574 G2: Group<Base = <G1 as Group>::Scalar>,
575 C1: StepCircuit<G1::Scalar>,
576 C2: StepCircuit<G2::Scalar>,
577 S1: RelaxedR1CSSNARKTrait<G1>,
578 S2: RelaxedR1CSSNARKTrait<G2>,
579{
580 /// Creates prover and verifier keys for `CompressedSNARK`
581 pub fn setup(
582 pp: &PublicParams<G1, G2, C1, C2>,
583 ) -> Result<
584 (
585 ProverKey<G1, G2, C1, C2, S1, S2>,
586 VerifierKey<G1, G2, C1, C2, S1, S2>,
587 ),
588 NovaError,
589 > {
590 let (pk_primary, vk_primary) = S1::setup(&pp.ck_primary, &pp.r1cs_shape_primary)?;
591 let (pk_secondary, vk_secondary) = S2::setup(&pp.ck_secondary, &pp.r1cs_shape_secondary)?;
592
593 let pk = ProverKey {
594 pk_primary,
595 pk_secondary,
596 _p_c1: Default::default(),
597 _p_c2: Default::default(),
598 };
599
600 let vk = VerifierKey {
601 F_arity_primary: pp.F_arity_primary,
602 F_arity_secondary: pp.F_arity_secondary,
603 ro_consts_primary: pp.ro_consts_primary.clone(),
604 ro_consts_secondary: pp.ro_consts_secondary.clone(),
605 r1cs_shape_primary_digest: pp.r1cs_shape_primary.get_digest(),
606 r1cs_shape_secondary_digest: pp.r1cs_shape_secondary.get_digest(),
607 vk_primary,
608 vk_secondary,
609 _p_c1: Default::default(),
610 _p_c2: Default::default(),
611 };
612
613 Ok((pk, vk))
614 }
615
616 /// Create a new `CompressedSNARK`
617 pub fn prove(
618 pp: &PublicParams<G1, G2, C1, C2>,
619 pk: &ProverKey<G1, G2, C1, C2, S1, S2>,
620 recursive_snark: &RecursiveSNARK<G1, G2, C1, C2>,
621 ) -> Result<Self, NovaError> {
622 let (res_primary, res_secondary) = rayon::join(
623 // fold the primary circuit's instance
624 || {
625 NIFS::prove(
626 &pp.ck_primary,
627 &pp.ro_consts_primary,
628 &pp.r1cs_shape_primary,
629 &recursive_snark.r_U_primary,
630 &recursive_snark.r_W_primary,
631 &recursive_snark.l_u_primary,
632 &recursive_snark.l_w_primary,
633 )
634 },
635 || {
636 // fold the secondary circuit's instance
637 NIFS::prove(
638 &pp.ck_secondary,
639 &pp.ro_consts_secondary,
640 &pp.r1cs_shape_secondary,
641 &recursive_snark.r_U_secondary,
642 &recursive_snark.r_W_secondary,
643 &recursive_snark.l_u_secondary,
644 &recursive_snark.l_w_secondary,
645 )
646 },
647 );
648
649 let (nifs_primary, (f_U_primary, f_W_primary)) = res_primary?;
650 let (nifs_secondary, (f_U_secondary, f_W_secondary)) = res_secondary?;
651
652 // create SNARKs proving the knowledge of f_W_primary and f_W_secondary
653 let (f_W_snark_primary, f_W_snark_secondary) = rayon::join(
654 || S1::prove(&pp.ck_primary, &pk.pk_primary, &f_U_primary, &f_W_primary),
655 || {
656 S2::prove(
657 &pp.ck_secondary,
658 &pk.pk_secondary,
659 &f_U_secondary,
660 &f_W_secondary,
661 )
662 },
663 );
664
665 Ok(Self {
666 r_U_primary: recursive_snark.r_U_primary.clone(),
667 l_u_primary: recursive_snark.l_u_primary.clone(),
668 nifs_primary,
669 f_W_snark_primary: f_W_snark_primary?,
670
671 r_U_secondary: recursive_snark.r_U_secondary.clone(),
672 l_u_secondary: recursive_snark.l_u_secondary.clone(),
673 nifs_secondary,
674 f_W_snark_secondary: f_W_snark_secondary?,
675
676 zn_primary: recursive_snark.zi_primary.clone(),
677 zn_secondary: recursive_snark.zi_secondary.clone(),
678
679 _p_c1: Default::default(),
680 _p_c2: Default::default(),
681 })
682 }
683
684 /// Verify the correctness of the `CompressedSNARK`
685 pub fn verify(
686 &self,
687 vk: &VerifierKey<G1, G2, C1, C2, S1, S2>,
688 num_steps: usize,
689 z0_primary: Vec<G1::Scalar>,
690 z0_secondary: Vec<G2::Scalar>,
691 ) -> Result<(Vec<G1::Scalar>, Vec<G2::Scalar>), NovaError> {
692 // number of steps cannot be zero
693 if num_steps == 0 {
694 return Err(NovaError::ProofVerifyError);
695 }
696
697 // check if the (relaxed) R1CS instances have two public outputs
698 if self.l_u_primary.X.len() != 2
699 || self.l_u_secondary.X.len() != 2
700 || self.r_U_primary.X.len() != 2
701 || self.r_U_secondary.X.len() != 2
702 {
703 return Err(NovaError::ProofVerifyError);
704 }
705
706 // check if the output hashes in R1CS instances point to the right running instances
707 let (hash_primary, hash_secondary) = {
708 let mut hasher = <G2 as Group>::RO::new(
709 vk.ro_consts_secondary.clone(),
710 NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * vk.F_arity_primary,
711 );
712 hasher.absorb(scalar_as_base::<G2>(vk.r1cs_shape_secondary_digest));
713 hasher.absorb(G1::Scalar::from(num_steps as u64));
714 for e in z0_primary {
715 hasher.absorb(e);
716 }
717 for e in &self.zn_primary {
718 hasher.absorb(*e);
719 }
720 self.r_U_secondary.absorb_in_ro(&mut hasher);
721
722 let mut hasher2 = <G1 as Group>::RO::new(
723 vk.ro_consts_primary.clone(),
724 NUM_FE_WITHOUT_IO_FOR_CRHF + 2 * vk.F_arity_secondary,
725 );
726 hasher2.absorb(scalar_as_base::<G1>(vk.r1cs_shape_primary_digest));
727 hasher2.absorb(G2::Scalar::from(num_steps as u64));
728 for e in z0_secondary {
729 hasher2.absorb(e);
730 }
731 for e in &self.zn_secondary {
732 hasher2.absorb(*e);
733 }
734 self.r_U_primary.absorb_in_ro(&mut hasher2);
735
736 (
737 hasher.squeeze(NUM_HASH_BITS),
738 hasher2.squeeze(NUM_HASH_BITS),
739 )
740 };
741
742 if hash_primary != scalar_as_base::<G1>(self.l_u_primary.X[1])
743 || hash_secondary != scalar_as_base::<G2>(self.l_u_secondary.X[1])
744 {
745 return Err(NovaError::ProofVerifyError);
746 }
747
748 // fold the running instance and last instance to get a folded instance
749 let f_U_primary = self.nifs_primary.verify(
750 &vk.ro_consts_primary,
751 &vk.r1cs_shape_primary_digest,
752 &self.r_U_primary,
753 &self.l_u_primary,
754 )?;
755 let f_U_secondary = self.nifs_secondary.verify(
756 &vk.ro_consts_secondary,
757 &vk.r1cs_shape_secondary_digest,
758 &self.r_U_secondary,
759 &self.l_u_secondary,
760 )?;
761
762 // check the satisfiability of the folded instances using SNARKs proving the knowledge of their satisfying witnesses
763 let (res_primary, res_secondary) = rayon::join(
764 || self.f_W_snark_primary.verify(&vk.vk_primary, &f_U_primary),
765 || {
766 self
767 .f_W_snark_secondary
768 .verify(&vk.vk_secondary, &f_U_secondary)
769 },
770 );
771
772 res_primary?;
773 res_secondary?;
774
775 Ok((self.zn_primary.clone(), self.zn_secondary.clone()))
776 }
777}
778
779type CommitmentKey<G> = <<G as traits::Group>::CE as CommitmentEngineTrait<G>>::CommitmentKey;
780type Commitment<G> = <<G as Group>::CE as CommitmentEngineTrait<G>>::Commitment;
781type CompressedCommitment<G> = <<<G as Group>::CE as CommitmentEngineTrait<G>>::Commitment as CommitmentTrait<G>>::CompressedCommitment;
782type CE<G> = <G as Group>::CE;
783
784#[cfg(test)]
785mod tests {
786 use super::*;
787 type G1 = pasta_curves::pallas::Point;
788 type G2 = pasta_curves::vesta::Point;
789 type EE1 = provider::ipa_pc::EvaluationEngine<G1>;
790 type EE2 = provider::ipa_pc::EvaluationEngine<G2>;
791 type CC1 = spartan::spark::TrivialCompComputationEngine<G1, EE1>;
792 type CC2 = spartan::spark::TrivialCompComputationEngine<G2, EE2>;
793 type S1 = spartan::RelaxedR1CSSNARK<G1, EE1, CC1>;
794 type S2 = spartan::RelaxedR1CSSNARK<G2, EE2, CC2>;
795 use ::bellperson::{gadgets::num::AllocatedNum, ConstraintSystem, SynthesisError};
796 use core::marker::PhantomData;
797 use ff::PrimeField;
798 use traits::circuit::TrivialTestCircuit;
799
800 #[derive(Clone, Debug, Default)]
801 struct CubicCircuit<F: PrimeField> {
802 _p: PhantomData<F>,
803 }
804
805 impl<F> StepCircuit<F> for CubicCircuit<F>
806 where
807 F: PrimeField,
808 {
809 fn arity(&self) -> usize {
810 1
811 }
812
813 fn synthesize<CS: ConstraintSystem<F>>(
814 &self,
815 cs: &mut CS,
816 z: &[AllocatedNum<F>],
817 ) -> Result<Vec<AllocatedNum<F>>, SynthesisError> {
818 // Consider a cubic equation: `x^3 + x + 5 = y`, where `x` and `y` are respectively the input and output.
819 let x = &z[0];
820 let x_sq = x.square(cs.namespace(|| "x_sq"))?;
821 let x_cu = x_sq.mul(cs.namespace(|| "x_cu"), x)?;
822 let y = AllocatedNum::alloc(cs.namespace(|| "y"), || {
823 Ok(x_cu.get_value().unwrap() + x.get_value().unwrap() + F::from(5u64))
824 })?;
825
826 cs.enforce(
827 || "y = x^3 + x + 5",
828 |lc| {
829 lc + x_cu.get_variable()
830 + x.get_variable()
831 + CS::one()
832 + CS::one()
833 + CS::one()
834 + CS::one()
835 + CS::one()
836 },
837 |lc| lc + CS::one(),
838 |lc| lc + y.get_variable(),
839 );
840
841 Ok(vec![y])
842 }
843
844 fn output(&self, z: &[F]) -> Vec<F> {
845 vec![z[0] * z[0] * z[0] + z[0] + F::from(5u64)]
846 }
847 }
848
849 #[test]
850 fn test_ivc_trivial() {
851 // produce public parameters
852 let pp = PublicParams::<
853 G1,
854 G2,
855 TrivialTestCircuit<<G1 as Group>::Scalar>,
856 TrivialTestCircuit<<G2 as Group>::Scalar>,
857 >::setup(TrivialTestCircuit::default(), TrivialTestCircuit::default());
858
859 let num_steps = 1;
860
861 // produce a recursive SNARK
862 let res = RecursiveSNARK::prove_step(
863 &pp,
864 None,
865 TrivialTestCircuit::default(),
866 TrivialTestCircuit::default(),
867 vec![<G1 as Group>::Scalar::zero()],
868 vec![<G2 as Group>::Scalar::zero()],
869 );
870 assert!(res.is_ok());
871 let recursive_snark = res.unwrap();
872
873 // verify the recursive SNARK
874 let res = recursive_snark.verify(
875 &pp,
876 num_steps,
877 vec![<G1 as Group>::Scalar::zero()],
878 vec![<G2 as Group>::Scalar::zero()],
879 );
880 assert!(res.is_ok());
881 }
882
883 #[test]
884 fn test_ivc_nontrivial() {
885 let circuit_primary = TrivialTestCircuit::default();
886 let circuit_secondary = CubicCircuit::default();
887
888 // produce public parameters
889 let pp = PublicParams::<
890 G1,
891 G2,
892 TrivialTestCircuit<<G1 as Group>::Scalar>,
893 CubicCircuit<<G2 as Group>::Scalar>,
894 >::setup(circuit_primary.clone(), circuit_secondary.clone());
895
896 let num_steps = 3;
897
898 // produce a recursive SNARK
899 let mut recursive_snark: Option<
900 RecursiveSNARK<
901 G1,
902 G2,
903 TrivialTestCircuit<<G1 as Group>::Scalar>,
904 CubicCircuit<<G2 as Group>::Scalar>,
905 >,
906 > = None;
907
908 for i in 0..num_steps {
909 let res = RecursiveSNARK::prove_step(
910 &pp,
911 recursive_snark,
912 circuit_primary.clone(),
913 circuit_secondary.clone(),
914 vec![<G1 as Group>::Scalar::one()],
915 vec![<G2 as Group>::Scalar::zero()],
916 );
917 assert!(res.is_ok());
918 let recursive_snark_unwrapped = res.unwrap();
919
920 // verify the recursive snark at each step of recursion
921 let res = recursive_snark_unwrapped.verify(
922 &pp,
923 i + 1,
924 vec![<G1 as Group>::Scalar::one()],
925 vec![<G2 as Group>::Scalar::zero()],
926 );
927 assert!(res.is_ok());
928
929 // set the running variable for the next iteration
930 recursive_snark = Some(recursive_snark_unwrapped);
931 }
932
933 assert!(recursive_snark.is_some());
934 let recursive_snark = recursive_snark.unwrap();
935
936 // verify the recursive SNARK
937 let res = recursive_snark.verify(
938 &pp,
939 num_steps,
940 vec![<G1 as Group>::Scalar::one()],
941 vec![<G2 as Group>::Scalar::zero()],
942 );
943 assert!(res.is_ok());
944
945 let (zn_primary, zn_secondary) = res.unwrap();
946
947 // sanity: check the claimed output with a direct computation of the same
948 assert_eq!(zn_primary, vec![<G1 as Group>::Scalar::one()]);
949 let mut zn_secondary_direct = vec![<G2 as Group>::Scalar::zero()];
950 for _i in 0..num_steps {
951 zn_secondary_direct = CubicCircuit::default().output(&zn_secondary_direct);
952 }
953 assert_eq!(zn_secondary, zn_secondary_direct);
954 assert_eq!(zn_secondary, vec![<G2 as Group>::Scalar::from(2460515u64)]);
955 }
956
957 #[test]
958 fn test_ivc_nontrivial_with_compression() {
959 let circuit_primary = TrivialTestCircuit::default();
960 let circuit_secondary = CubicCircuit::default();
961
962 // produce public parameters
963 let pp = PublicParams::<
964 G1,
965 G2,
966 TrivialTestCircuit<<G1 as Group>::Scalar>,
967 CubicCircuit<<G2 as Group>::Scalar>,
968 >::setup(circuit_primary.clone(), circuit_secondary.clone());
969
970 let num_steps = 3;
971
972 // produce a recursive SNARK
973 let mut recursive_snark: Option<
974 RecursiveSNARK<
975 G1,
976 G2,
977 TrivialTestCircuit<<G1 as Group>::Scalar>,
978 CubicCircuit<<G2 as Group>::Scalar>,
979 >,
980 > = None;
981
982 for _i in 0..num_steps {
983 let res = RecursiveSNARK::prove_step(
984 &pp,
985 recursive_snark,
986 circuit_primary.clone(),
987 circuit_secondary.clone(),
988 vec![<G1 as Group>::Scalar::one()],
989 vec![<G2 as Group>::Scalar::zero()],
990 );
991 assert!(res.is_ok());
992 recursive_snark = Some(res.unwrap());
993 }
994
995 assert!(recursive_snark.is_some());
996 let recursive_snark = recursive_snark.unwrap();
997
998 // verify the recursive SNARK
999 let res = recursive_snark.verify(
1000 &pp,
1001 num_steps,
1002 vec![<G1 as Group>::Scalar::one()],
1003 vec![<G2 as Group>::Scalar::zero()],
1004 );
1005 assert!(res.is_ok());
1006
1007 let (zn_primary, zn_secondary) = res.unwrap();
1008
1009 // sanity: check the claimed output with a direct computation of the same
1010 assert_eq!(zn_primary, vec![<G1 as Group>::Scalar::one()]);
1011 let mut zn_secondary_direct = vec![<G2 as Group>::Scalar::zero()];
1012 for _i in 0..num_steps {
1013 zn_secondary_direct = CubicCircuit::default().output(&zn_secondary_direct);
1014 }
1015 assert_eq!(zn_secondary, zn_secondary_direct);
1016 assert_eq!(zn_secondary, vec![<G2 as Group>::Scalar::from(2460515u64)]);
1017
1018 // produce the prover and verifier keys for compressed snark
1019 let (pk, vk) = CompressedSNARK::<_, _, _, _, S1, S2>::setup(&pp).unwrap();
1020
1021 // produce a compressed SNARK
1022 let res = CompressedSNARK::<_, _, _, _, S1, S2>::prove(&pp, &pk, &recursive_snark);
1023 assert!(res.is_ok());
1024 let compressed_snark = res.unwrap();
1025
1026 // verify the compressed SNARK
1027 let res = compressed_snark.verify(
1028 &vk,
1029 num_steps,
1030 vec![<G1 as Group>::Scalar::one()],
1031 vec![<G2 as Group>::Scalar::zero()],
1032 );
1033 assert!(res.is_ok());
1034 }
1035
1036 #[test]
1037 fn test_ivc_nontrivial_with_spark_compression() {
1038 let circuit_primary = TrivialTestCircuit::default();
1039 let circuit_secondary = CubicCircuit::default();
1040
1041 // produce public parameters
1042 let pp = PublicParams::<
1043 G1,
1044 G2,
1045 TrivialTestCircuit<<G1 as Group>::Scalar>,
1046 CubicCircuit<<G2 as Group>::Scalar>,
1047 >::setup(circuit_primary.clone(), circuit_secondary.clone());
1048
1049 let num_steps = 3;
1050
1051 // produce a recursive SNARK
1052 let mut recursive_snark: Option<
1053 RecursiveSNARK<
1054 G1,
1055 G2,
1056 TrivialTestCircuit<<G1 as Group>::Scalar>,
1057 CubicCircuit<<G2 as Group>::Scalar>,
1058 >,
1059 > = None;
1060
1061 for _i in 0..num_steps {
1062 let res = RecursiveSNARK::prove_step(
1063 &pp,
1064 recursive_snark,
1065 circuit_primary.clone(),
1066 circuit_secondary.clone(),
1067 vec![<G1 as Group>::Scalar::one()],
1068 vec![<G2 as Group>::Scalar::zero()],
1069 );
1070 assert!(res.is_ok());
1071 recursive_snark = Some(res.unwrap());
1072 }
1073
1074 assert!(recursive_snark.is_some());
1075 let recursive_snark = recursive_snark.unwrap();
1076
1077 // verify the recursive SNARK
1078 let res = recursive_snark.verify(
1079 &pp,
1080 num_steps,
1081 vec![<G1 as Group>::Scalar::one()],
1082 vec![<G2 as Group>::Scalar::zero()],
1083 );
1084 assert!(res.is_ok());
1085
1086 let (zn_primary, zn_secondary) = res.unwrap();
1087
1088 // sanity: check the claimed output with a direct computation of the same
1089 assert_eq!(zn_primary, vec![<G1 as Group>::Scalar::one()]);
1090 let mut zn_secondary_direct = vec![<G2 as Group>::Scalar::zero()];
1091 for _i in 0..num_steps {
1092 zn_secondary_direct = CubicCircuit::default().output(&zn_secondary_direct);
1093 }
1094 assert_eq!(zn_secondary, zn_secondary_direct);
1095 assert_eq!(zn_secondary, vec![<G2 as Group>::Scalar::from(2460515u64)]);
1096
1097 // run the compressed snark with Spark compiler
1098 type CC1Prime = spartan::spark::SparkEngine<G1, EE1>;
1099 type CC2Prime = spartan::spark::SparkEngine<G2, EE2>;
1100 type S1Prime = spartan::RelaxedR1CSSNARK<G1, EE1, CC1Prime>;
1101 type S2Prime = spartan::RelaxedR1CSSNARK<G2, EE2, CC2Prime>;
1102
1103 // produce the prover and verifier keys for compressed snark
1104 let (pk, vk) = CompressedSNARK::<_, _, _, _, S1Prime, S2Prime>::setup(&pp).unwrap();
1105
1106 // produce a compressed SNARK
1107 let res = CompressedSNARK::<_, _, _, _, S1Prime, S2Prime>::prove(&pp, &pk, &recursive_snark);
1108 assert!(res.is_ok());
1109 let compressed_snark = res.unwrap();
1110
1111 // verify the compressed SNARK
1112 let res = compressed_snark.verify(
1113 &vk,
1114 num_steps,
1115 vec![<G1 as Group>::Scalar::one()],
1116 vec![<G2 as Group>::Scalar::zero()],
1117 );
1118 assert!(res.is_ok());
1119 }
1120
1121 #[test]
1122 fn test_ivc_nondet_with_compression() {
1123 // y is a non-deterministic advice representing the fifth root of the input at a step.
1124 #[derive(Clone, Debug)]
1125 struct FifthRootCheckingCircuit<F: PrimeField> {
1126 y: F,
1127 }
1128
1129 impl<F> FifthRootCheckingCircuit<F>
1130 where
1131 F: PrimeField,
1132 {
1133 fn new(num_steps: usize) -> (Vec<F>, Vec<Self>) {
1134 let mut powers = Vec::new();
1135 let rng = &mut rand::rngs::OsRng;
1136 let mut seed = F::random(rng);
1137 for _i in 0..num_steps + 1 {
1138 let mut power = seed;
1139 power = power.square();
1140 power = power.square();
1141 power *= seed;
1142
1143 powers.push(Self { y: power });
1144
1145 seed = power;
1146 }
1147
1148 // reverse the powers to get roots
1149 let roots = powers.into_iter().rev().collect::<Vec<Self>>();
1150 (vec![roots[0].y], roots[1..].to_vec())
1151 }
1152 }
1153
1154 impl<F> StepCircuit<F> for FifthRootCheckingCircuit<F>
1155 where
1156 F: PrimeField,
1157 {
1158 fn arity(&self) -> usize {
1159 1
1160 }
1161
1162 fn synthesize<CS: ConstraintSystem<F>>(
1163 &self,
1164 cs: &mut CS,
1165 z: &[AllocatedNum<F>],
1166 ) -> Result<Vec<AllocatedNum<F>>, SynthesisError> {
1167 let x = &z[0];
1168
1169 // we allocate a variable and set it to the provided non-deterministic advice.
1170 let y = AllocatedNum::alloc(cs.namespace(|| "y"), || Ok(self.y))?;
1171
1172 // We now check if y = x^{1/5} by checking if y^5 = x
1173 let y_sq = y.square(cs.namespace(|| "y_sq"))?;
1174 let y_quad = y_sq.square(cs.namespace(|| "y_quad"))?;
1175 let y_pow_5 = y_quad.mul(cs.namespace(|| "y_fifth"), &y)?;
1176
1177 cs.enforce(
1178 || "y^5 = x",
1179 |lc| lc + y_pow_5.get_variable(),
1180 |lc| lc + CS::one(),
1181 |lc| lc + x.get_variable(),
1182 );
1183
1184 Ok(vec![y])
1185 }
1186
1187 fn output(&self, z: &[F]) -> Vec<F> {
1188 // sanity check
1189 let x = z[0];
1190 let y_pow_5 = {
1191 let y = self.y;
1192 let y_sq = y.square();
1193 let y_quad = y_sq.square();
1194 y_quad * self.y
1195 };
1196 assert_eq!(x, y_pow_5);
1197
1198 // return non-deterministic advice
1199 // as the output of the step
1200 vec![self.y]
1201 }
1202 }
1203
1204 let circuit_primary = FifthRootCheckingCircuit {
1205 y: <G1 as Group>::Scalar::zero(),
1206 };
1207
1208 let circuit_secondary = TrivialTestCircuit::default();
1209
1210 // produce public parameters
1211 let pp = PublicParams::<
1212 G1,
1213 G2,
1214 FifthRootCheckingCircuit<<G1 as Group>::Scalar>,
1215 TrivialTestCircuit<<G2 as Group>::Scalar>,
1216 >::setup(circuit_primary, circuit_secondary.clone());
1217
1218 let num_steps = 3;
1219
1220 // produce non-deterministic advice
1221 let (z0_primary, roots) = FifthRootCheckingCircuit::new(num_steps);
1222 let z0_secondary = vec![<G2 as Group>::Scalar::zero()];
1223
1224 // produce a recursive SNARK
1225 let mut recursive_snark: Option<
1226 RecursiveSNARK<
1227 G1,
1228 G2,
1229 FifthRootCheckingCircuit<<G1 as Group>::Scalar>,
1230 TrivialTestCircuit<<G2 as Group>::Scalar>,
1231 >,
1232 > = None;
1233
1234 for circuit_primary in roots.iter().take(num_steps) {
1235 let res = RecursiveSNARK::prove_step(
1236 &pp,
1237 recursive_snark,
1238 circuit_primary.clone(),
1239 circuit_secondary.clone(),
1240 z0_primary.clone(),
1241 z0_secondary.clone(),
1242 );
1243 assert!(res.is_ok());
1244 recursive_snark = Some(res.unwrap());
1245 }
1246
1247 assert!(recursive_snark.is_some());
1248 let recursive_snark = recursive_snark.unwrap();
1249
1250 // verify the recursive SNARK
1251 let res = recursive_snark.verify(&pp, num_steps, z0_primary.clone(), z0_secondary.clone());
1252 assert!(res.is_ok());
1253
1254 // produce the prover and verifier keys for compressed snark
1255 let (pk, vk) = CompressedSNARK::<_, _, _, _, S1, S2>::setup(&pp).unwrap();
1256
1257 // produce a compressed SNARK
1258 let res = CompressedSNARK::<_, _, _, _, S1, S2>::prove(&pp, &pk, &recursive_snark);
1259 assert!(res.is_ok());
1260 let compressed_snark = res.unwrap();
1261
1262 // verify the compressed SNARK
1263 let res = compressed_snark.verify(&vk, num_steps, z0_primary, z0_secondary);
1264 assert!(res.is_ok());
1265 }
1266
1267 #[test]
1268 fn test_ivc_base() {
1269 // produce public parameters
1270 let pp = PublicParams::<
1271 G1,
1272 G2,
1273 TrivialTestCircuit<<G1 as Group>::Scalar>,
1274 CubicCircuit<<G2 as Group>::Scalar>,
1275 >::setup(TrivialTestCircuit::default(), CubicCircuit::default());
1276
1277 let num_steps = 1;
1278
1279 // produce a recursive SNARK
1280 let res = RecursiveSNARK::prove_step(
1281 &pp,
1282 None,
1283 TrivialTestCircuit::default(),
1284 CubicCircuit::default(),
1285 vec![<G1 as Group>::Scalar::one()],
1286 vec![<G2 as Group>::Scalar::zero()],
1287 );
1288 assert!(res.is_ok());
1289 let recursive_snark = res.unwrap();
1290
1291 // verify the recursive SNARK
1292 let res = recursive_snark.verify(
1293 &pp,
1294 num_steps,
1295 vec![<G1 as Group>::Scalar::one()],
1296 vec![<G2 as Group>::Scalar::zero()],
1297 );
1298 assert!(res.is_ok());
1299
1300 let (zn_primary, zn_secondary) = res.unwrap();
1301
1302 assert_eq!(zn_primary, vec![<G1 as Group>::Scalar::one()]);
1303 assert_eq!(zn_secondary, vec![<G2 as Group>::Scalar::from(5u64)]);
1304 }
1305}