ark-r1cs-std/crypto-primitives/src/nizk/groth16/constraints.rs
crypto-primitives/src/nizk/groth16/constraints.rsRaw
1use crate::{
2 nizk::{groth16::Groth16, NIZKVerifierGadget},
3 Vec,
4};
5use algebra_core::{AffineCurve, Field, PairingEngine, ToConstraintField};
6use r1cs_core::{ConstraintSynthesizer, ConstraintSystem, SynthesisError};
7use r1cs_std::prelude::*;
8
9use core::{borrow::Borrow, marker::PhantomData};
10use groth16::{Proof, VerifyingKey};
11
12#[derive(Derivative)]
13#[derivative(Clone(bound = "P::G1Gadget: Clone, P::G2Gadget: Clone"))]
14pub struct ProofGadget<
15 PairingE: PairingEngine,
16 ConstraintF: Field,
17 P: PairingGadget<PairingE, ConstraintF>,
18> {
19 pub a: P::G1Gadget,
20 pub b: P::G2Gadget,
21 pub c: P::G1Gadget,
22}
23
24#[derive(Derivative)]
25#[derivative(Clone(
26 bound = "P::G1Gadget: Clone, P::GTGadget: Clone, P::G1PreparedGadget: Clone, \
27 P::G2PreparedGadget: Clone, "
28))]
29pub struct VerifyingKeyGadget<
30 PairingE: PairingEngine,
31 ConstraintF: Field,
32 P: PairingGadget<PairingE, ConstraintF>,
33> {
34 pub alpha_g1: P::G1Gadget,
35 pub beta_g2: P::G2Gadget,
36 pub gamma_g2: P::G2Gadget,
37 pub delta_g2: P::G2Gadget,
38 pub gamma_abc_g1: Vec<P::G1Gadget>,
39}
40
41impl<PairingE: PairingEngine, ConstraintF: Field, P: PairingGadget<PairingE, ConstraintF>>
42 VerifyingKeyGadget<PairingE, ConstraintF, P>
43{
44 pub fn prepare<CS: ConstraintSystem<ConstraintF>>(
45 &self,
46 mut cs: CS,
47 ) -> Result<PreparedVerifyingKeyGadget<PairingE, ConstraintF, P>, SynthesisError> {
48 let mut cs = cs.ns(|| "Preparing verifying key");
49 let alpha_g1_pc = P::prepare_g1(&mut cs.ns(|| "Prepare alpha_g1"), &self.alpha_g1)?;
50 let beta_g2_pc = P::prepare_g2(&mut cs.ns(|| "Prepare beta_g2"), &self.beta_g2)?;
51
52 let alpha_g1_beta_g2 = P::pairing(
53 &mut cs.ns(|| "Precompute e(alpha_g1, beta_g2)"),
54 alpha_g1_pc,
55 beta_g2_pc,
56 )?;
57
58 let gamma_g2_neg = self.gamma_g2.negate(&mut cs.ns(|| "Negate gamma_g2"))?;
59 let gamma_g2_neg_pc = P::prepare_g2(&mut cs.ns(|| "Prepare gamma_g2_neg"), &gamma_g2_neg)?;
60
61 let delta_g2_neg = self.delta_g2.negate(&mut cs.ns(|| "Negate delta_g2"))?;
62 let delta_g2_neg_pc = P::prepare_g2(&mut cs.ns(|| "Prepare delta_g2_neg"), &delta_g2_neg)?;
63
64 Ok(PreparedVerifyingKeyGadget {
65 alpha_g1_beta_g2,
66 gamma_g2_neg_pc,
67 delta_g2_neg_pc,
68 gamma_abc_g1: self.gamma_abc_g1.clone(),
69 })
70 }
71}
72
73#[derive(Derivative)]
74#[derivative(Clone(
75 bound = "P::G1Gadget: Clone, P::GTGadget: Clone, P::G1PreparedGadget: Clone, \
76 P::G2PreparedGadget: Clone, "
77))]
78pub struct PreparedVerifyingKeyGadget<
79 PairingE: PairingEngine,
80 ConstraintF: Field,
81 P: PairingGadget<PairingE, ConstraintF>,
82> {
83 pub alpha_g1_beta_g2: P::GTGadget,
84 pub gamma_g2_neg_pc: P::G2PreparedGadget,
85 pub delta_g2_neg_pc: P::G2PreparedGadget,
86 pub gamma_abc_g1: Vec<P::G1Gadget>,
87}
88
89pub struct Groth16VerifierGadget<PairingE, ConstraintF, P>
90where
91 PairingE: PairingEngine,
92 ConstraintF: Field,
93 P: PairingGadget<PairingE, ConstraintF>,
94{
95 _pairing_engine: PhantomData<PairingE>,
96 _engine: PhantomData<ConstraintF>,
97 _pairing_gadget: PhantomData<P>,
98}
99
100impl<PairingE, ConstraintF, P, C, V> NIZKVerifierGadget<Groth16<PairingE, C, V>, ConstraintF>
101 for Groth16VerifierGadget<PairingE, ConstraintF, P>
102where
103 PairingE: PairingEngine,
104 ConstraintF: Field,
105 C: ConstraintSynthesizer<PairingE::Fr>,
106 V: ToConstraintField<PairingE::Fr>,
107 P: PairingGadget<PairingE, ConstraintF>,
108{
109 type VerificationKeyGadget = VerifyingKeyGadget<PairingE, ConstraintF, P>;
110 type ProofGadget = ProofGadget<PairingE, ConstraintF, P>;
111
112 fn check_verify<'a, CS, I, T>(
113 cs: CS,
114 vk: &Self::VerificationKeyGadget,
115 public_inputs: I,
116 proof: &Self::ProofGadget,
117 ) -> Result<(), SynthesisError>
118 where
119 CS: ConstraintSystem<ConstraintF>,
120 I: Iterator<Item = &'a T>,
121 T: 'a + ToBitsGadget<ConstraintF> + ?Sized,
122 {
123 <Self as NIZKVerifierGadget<Groth16<PairingE, C, V>, ConstraintF>>::conditional_check_verify(
124 cs,
125 vk,
126 public_inputs,
127 proof,
128 &Boolean::constant(true),
129 )
130 }
131
132 fn conditional_check_verify<'a, CS, I, T>(
133 mut cs: CS,
134 vk: &Self::VerificationKeyGadget,
135 mut public_inputs: I,
136 proof: &Self::ProofGadget,
137 condition: &Boolean,
138 ) -> Result<(), SynthesisError>
139 where
140 CS: ConstraintSystem<ConstraintF>,
141 I: Iterator<Item = &'a T>,
142 T: 'a + ToBitsGadget<ConstraintF> + ?Sized,
143 {
144 let pvk = vk.prepare(&mut cs.ns(|| "Prepare vk"))?;
145
146 let g_ic = {
147 let mut cs = cs.ns(|| "Process input");
148 let mut g_ic = pvk.gamma_abc_g1[0].clone();
149 let mut input_len = 1;
150 for (i, (input, b)) in public_inputs
151 .by_ref()
152 .zip(pvk.gamma_abc_g1.iter().skip(1))
153 .enumerate()
154 {
155 let input_bits = input.to_bits(cs.ns(|| format!("Input {}", i)))?;
156 g_ic = b.mul_bits(cs.ns(|| format!("Mul {}", i)), &g_ic, input_bits.iter())?;
157 input_len += 1;
158 }
159 // Check that the input and the query in the verification are of the
160 // same length.
161 assert!(input_len == pvk.gamma_abc_g1.len() && public_inputs.next().is_none());
162 g_ic
163 };
164
165 let test_exp = {
166 let proof_a_prep = P::prepare_g1(cs.ns(|| "Prepare proof a"), &proof.a)?;
167 let proof_b_prep = P::prepare_g2(cs.ns(|| "Prepare proof b"), &proof.b)?;
168 let proof_c_prep = P::prepare_g1(cs.ns(|| "Prepare proof c"), &proof.c)?;
169
170 let g_ic_prep = P::prepare_g1(cs.ns(|| "Prepare g_ic"), &g_ic)?;
171
172 P::miller_loop(
173 cs.ns(|| "Miller loop 1"),
174 &[proof_a_prep, g_ic_prep, proof_c_prep],
175 &[
176 proof_b_prep,
177 pvk.gamma_g2_neg_pc.clone(),
178 pvk.delta_g2_neg_pc.clone(),
179 ],
180 )?
181 };
182
183 let test = P::final_exponentiation(cs.ns(|| "Final Exp"), &test_exp).unwrap();
184
185 test.conditional_enforce_equal(cs.ns(|| "Test 1"), &pvk.alpha_g1_beta_g2, condition)?;
186 Ok(())
187 }
188}
189
190impl<PairingE, ConstraintF, P> AllocGadget<VerifyingKey<PairingE>, ConstraintF>
191 for VerifyingKeyGadget<PairingE, ConstraintF, P>
192where
193 PairingE: PairingEngine,
194 ConstraintF: Field,
195 P: PairingGadget<PairingE, ConstraintF>,
196{
197 #[inline]
198 fn alloc_constant<T, CS: ConstraintSystem<ConstraintF>>(
199 mut cs: CS,
200 val: T,
201 ) -> Result<Self, SynthesisError>
202 where
203 T: Borrow<VerifyingKey<PairingE>>,
204 {
205 let VerifyingKey {
206 alpha_g1,
207 beta_g2,
208 gamma_g2,
209 delta_g2,
210 gamma_abc_g1,
211 } = val.borrow().clone();
212 let alpha_g1 =
213 P::G1Gadget::alloc_constant(cs.ns(|| "alpha_g1"), alpha_g1.into_projective())?;
214 let beta_g2 = P::G2Gadget::alloc_constant(cs.ns(|| "beta_g2"), beta_g2.into_projective())?;
215 let gamma_g2 =
216 P::G2Gadget::alloc_constant(cs.ns(|| "gamma_g2"), gamma_g2.into_projective())?;
217 let delta_g2 =
218 P::G2Gadget::alloc_constant(cs.ns(|| "delta_g2"), delta_g2.into_projective())?;
219
220 let gamma_abc_g1 = gamma_abc_g1
221 .into_iter()
222 .enumerate()
223 .map(|(i, gamma_abc_i)| {
224 P::G1Gadget::alloc_constant(
225 cs.ns(|| format!("gamma_abc_{}", i)),
226 gamma_abc_i.into_projective(),
227 )
228 })
229 .collect::<Vec<_>>()
230 .into_iter()
231 .collect::<Result<_, _>>()?;
232 Ok(Self {
233 alpha_g1,
234 beta_g2,
235 gamma_g2,
236 delta_g2,
237 gamma_abc_g1,
238 })
239 }
240
241 #[inline]
242 fn alloc<FN, T, CS: ConstraintSystem<ConstraintF>>(
243 mut cs: CS,
244 value_gen: FN,
245 ) -> Result<Self, SynthesisError>
246 where
247 FN: FnOnce() -> Result<T, SynthesisError>,
248 T: Borrow<VerifyingKey<PairingE>>,
249 {
250 value_gen().and_then(|vk| {
251 let VerifyingKey {
252 alpha_g1,
253 beta_g2,
254 gamma_g2,
255 delta_g2,
256 gamma_abc_g1,
257 } = vk.borrow().clone();
258 let alpha_g1 =
259 P::G1Gadget::alloc(cs.ns(|| "alpha_g1"), || Ok(alpha_g1.into_projective()))?;
260 let beta_g2 =
261 P::G2Gadget::alloc(cs.ns(|| "beta_g2"), || Ok(beta_g2.into_projective()))?;
262 let gamma_g2 =
263 P::G2Gadget::alloc(cs.ns(|| "gamma_g2"), || Ok(gamma_g2.into_projective()))?;
264 let delta_g2 =
265 P::G2Gadget::alloc(cs.ns(|| "delta_g2"), || Ok(delta_g2.into_projective()))?;
266
267 let gamma_abc_g1 = gamma_abc_g1
268 .into_iter()
269 .enumerate()
270 .map(|(i, gamma_abc_i)| {
271 P::G1Gadget::alloc(cs.ns(|| format!("gamma_abc_{}", i)), || {
272 Ok(gamma_abc_i.into_projective())
273 })
274 })
275 .collect::<Vec<_>>()
276 .into_iter()
277 .collect::<Result<_, _>>()?;
278 Ok(Self {
279 alpha_g1,
280 beta_g2,
281 gamma_g2,
282 delta_g2,
283 gamma_abc_g1,
284 })
285 })
286 }
287
288 #[inline]
289 fn alloc_input<FN, T, CS: ConstraintSystem<ConstraintF>>(
290 mut cs: CS,
291 value_gen: FN,
292 ) -> Result<Self, SynthesisError>
293 where
294 FN: FnOnce() -> Result<T, SynthesisError>,
295 T: Borrow<VerifyingKey<PairingE>>,
296 {
297 value_gen().and_then(|vk| {
298 let VerifyingKey {
299 alpha_g1,
300 beta_g2,
301 gamma_g2,
302 delta_g2,
303 gamma_abc_g1,
304 } = vk.borrow().clone();
305 let alpha_g1 =
306 P::G1Gadget::alloc_input(cs.ns(|| "alpha_g1"), || Ok(alpha_g1.into_projective()))?;
307 let beta_g2 =
308 P::G2Gadget::alloc_input(cs.ns(|| "beta_g2"), || Ok(beta_g2.into_projective()))?;
309 let gamma_g2 =
310 P::G2Gadget::alloc_input(cs.ns(|| "gamma_g2"), || Ok(gamma_g2.into_projective()))?;
311 let delta_g2 =
312 P::G2Gadget::alloc_input(cs.ns(|| "delta_g2"), || Ok(delta_g2.into_projective()))?;
313
314 let gamma_abc_g1 = gamma_abc_g1
315 .into_iter()
316 .enumerate()
317 .map(|(i, gamma_abc_i)| {
318 P::G1Gadget::alloc_input(cs.ns(|| format!("gamma_abc_{}", i)), || {
319 Ok(gamma_abc_i.into_projective())
320 })
321 })
322 .collect::<Vec<_>>()
323 .into_iter()
324 .collect::<Result<_, _>>()?;
325
326 Ok(Self {
327 alpha_g1,
328 beta_g2,
329 gamma_g2,
330 delta_g2,
331 gamma_abc_g1,
332 })
333 })
334 }
335}
336
337impl<PairingE, ConstraintF, P> AllocGadget<Proof<PairingE>, ConstraintF>
338 for ProofGadget<PairingE, ConstraintF, P>
339where
340 PairingE: PairingEngine,
341 ConstraintF: Field,
342 P: PairingGadget<PairingE, ConstraintF>,
343{
344 #[inline]
345 fn alloc_constant<T, CS: ConstraintSystem<ConstraintF>>(
346 mut cs: CS,
347 val: T,
348 ) -> Result<Self, SynthesisError>
349 where
350 T: Borrow<Proof<PairingE>>,
351 {
352 let Proof { a, b, c } = val.borrow().clone();
353 let a = P::G1Gadget::alloc_constant(cs.ns(|| "a"), a.into_projective())?;
354 let b = P::G2Gadget::alloc_constant(cs.ns(|| "b"), b.into_projective())?;
355 let c = P::G1Gadget::alloc_constant(cs.ns(|| "c"), c.into_projective())?;
356 Ok(Self { a, b, c })
357 }
358
359 #[inline]
360 fn alloc<FN, T, CS: ConstraintSystem<ConstraintF>>(
361 mut cs: CS,
362 value_gen: FN,
363 ) -> Result<Self, SynthesisError>
364 where
365 FN: FnOnce() -> Result<T, SynthesisError>,
366 T: Borrow<Proof<PairingE>>,
367 {
368 value_gen().and_then(|proof| {
369 let Proof { a, b, c } = proof.borrow().clone();
370 let a = P::G1Gadget::alloc_checked(cs.ns(|| "a"), || Ok(a.into_projective()))?;
371 let b = P::G2Gadget::alloc_checked(cs.ns(|| "b"), || Ok(b.into_projective()))?;
372 let c = P::G1Gadget::alloc_checked(cs.ns(|| "c"), || Ok(c.into_projective()))?;
373 Ok(Self { a, b, c })
374 })
375 }
376
377 #[inline]
378 fn alloc_input<FN, T, CS: ConstraintSystem<ConstraintF>>(
379 mut cs: CS,
380 value_gen: FN,
381 ) -> Result<Self, SynthesisError>
382 where
383 FN: FnOnce() -> Result<T, SynthesisError>,
384 T: Borrow<Proof<PairingE>>,
385 {
386 value_gen().and_then(|proof| {
387 let Proof { a, b, c } = proof.borrow().clone();
388 // We don't need to check here because the prime order check can be performed
389 // in plain.
390 let a = P::G1Gadget::alloc_input(cs.ns(|| "a"), || Ok(a.into_projective()))?;
391 let b = P::G2Gadget::alloc_input(cs.ns(|| "b"), || Ok(b.into_projective()))?;
392 let c = P::G1Gadget::alloc_input(cs.ns(|| "c"), || Ok(c.into_projective()))?;
393 Ok(Self { a, b, c })
394 })
395 }
396}
397
398impl<PairingE, ConstraintF, P> ToBytesGadget<ConstraintF>
399 for VerifyingKeyGadget<PairingE, ConstraintF, P>
400where
401 PairingE: PairingEngine,
402 ConstraintF: Field,
403 P: PairingGadget<PairingE, ConstraintF>,
404{
405 #[inline]
406 fn to_bytes<CS: ConstraintSystem<ConstraintF>>(
407 &self,
408 mut cs: CS,
409 ) -> Result<Vec<UInt8>, SynthesisError> {
410 let mut bytes = Vec::new();
411 bytes.extend_from_slice(&self.alpha_g1.to_bytes(&mut cs.ns(|| "alpha_g1 to bytes"))?);
412 bytes.extend_from_slice(&self.beta_g2.to_bytes(&mut cs.ns(|| "beta_g2 to bytes"))?);
413 bytes.extend_from_slice(&self.gamma_g2.to_bytes(&mut cs.ns(|| "gamma_g2 to bytes"))?);
414 bytes.extend_from_slice(&self.delta_g2.to_bytes(&mut cs.ns(|| "delta_g2 to bytes"))?);
415 for (i, g) in self.gamma_abc_g1.iter().enumerate() {
416 let mut cs = cs.ns(|| format!("Iteration {}", i));
417 bytes.extend_from_slice(&g.to_bytes(&mut cs.ns(|| "g"))?);
418 }
419 Ok(bytes)
420 }
421}
422
423#[cfg(test)]
424mod test {
425 use groth16::*;
426 use r1cs_core::{ConstraintSynthesizer, ConstraintSystem, SynthesisError};
427
428 use super::*;
429 use algebra::{
430 bls12_377::{Bls12_377, Fq, Fr},
431 test_rng, BitIterator, PrimeField,
432 };
433 use r1cs_std::{
434 bls12_377::PairingGadget as Bls12_377PairingGadget, boolean::Boolean,
435 test_constraint_system::TestConstraintSystem,
436 };
437 use rand::Rng;
438
439 type TestProofSystem = Groth16<Bls12_377, Bench<Fr>, Fr>;
440 type TestVerifierGadget = Groth16VerifierGadget<Bls12_377, Fq, Bls12_377PairingGadget>;
441 type TestProofGadget = ProofGadget<Bls12_377, Fq, Bls12_377PairingGadget>;
442 type TestVkGadget = VerifyingKeyGadget<Bls12_377, Fq, Bls12_377PairingGadget>;
443
444 struct Bench<F: Field> {
445 inputs: Vec<Option<F>>,
446 num_constraints: usize,
447 }
448
449 impl<F: Field> ConstraintSynthesizer<F> for Bench<F> {
450 fn generate_constraints<CS: ConstraintSystem<F>>(
451 self,
452 cs: &mut CS,
453 ) -> Result<(), SynthesisError> {
454 assert!(self.inputs.len() >= 2);
455 assert!(self.num_constraints >= self.inputs.len());
456
457 let mut variables: Vec<_> = Vec::with_capacity(self.inputs.len());
458 for (i, input) in self.inputs.into_iter().enumerate() {
459 let input_var = cs.alloc_input(
460 || format!("Input {}", i),
461 || input.ok_or(SynthesisError::AssignmentMissing),
462 )?;
463 variables.push((input, input_var));
464 }
465
466 for i in 0..self.num_constraints {
467 let new_entry = {
468 let (input_1_val, input_1_var) = variables[i];
469 let (input_2_val, input_2_var) = variables[i + 1];
470 let result_val = input_1_val
471 .and_then(|input_1| input_2_val.map(|input_2| input_1 * &input_2));
472 let result_var = cs.alloc(
473 || format!("Result {}", i),
474 || result_val.ok_or(SynthesisError::AssignmentMissing),
475 )?;
476 cs.enforce(
477 || format!("Enforce constraint {}", i),
478 |lc| lc + input_1_var,
479 |lc| lc + input_2_var,
480 |lc| lc + result_var,
481 );
482 (result_val, result_var)
483 };
484 variables.push(new_entry);
485 }
486 Ok(())
487 }
488 }
489
490 #[test]
491 fn groth16_verifier_test() {
492 let num_inputs = 100;
493 let num_constraints = num_inputs;
494 let rng = &mut test_rng();
495 let mut inputs: Vec<Option<Fr>> = Vec::with_capacity(num_inputs);
496 for _ in 0..num_inputs {
497 inputs.push(Some(rng.gen()));
498 }
499 let params = {
500 let c = Bench::<Fr> {
501 inputs: vec![None; num_inputs],
502 num_constraints,
503 };
504
505 generate_random_parameters(c, rng).unwrap()
506 };
507
508 {
509 let proof = {
510 // Create an instance of our circuit (with the
511 // witness)
512 let c = Bench {
513 inputs: inputs.clone(),
514 num_constraints,
515 };
516 // Create a groth16 proof with our parameters.
517 create_random_proof(c, ¶ms, rng).unwrap()
518 };
519
520 // assert!(!verify_proof(&pvk, &proof, &[a]).unwrap());
521 let mut cs = TestConstraintSystem::<Fq>::new();
522
523 let inputs: Vec<_> = inputs.into_iter().map(|input| input.unwrap()).collect();
524 let mut input_gadgets = Vec::new();
525
526 {
527 let mut cs = cs.ns(|| "Allocate Input");
528 for (i, input) in inputs.into_iter().enumerate() {
529 let mut input_bits = BitIterator::new(input.into_repr()).collect::<Vec<_>>();
530 // Input must be in little-endian, but BitIterator outputs in big-endian.
531 input_bits.reverse();
532
533 let input_bits =
534 Vec::<Boolean>::alloc_input(cs.ns(|| format!("Input {}", i)), || {
535 Ok(input_bits)
536 })
537 .unwrap();
538 input_gadgets.push(input_bits);
539 }
540 }
541
542 let vk_gadget = TestVkGadget::alloc_input(cs.ns(|| "Vk"), || Ok(¶ms.vk)).unwrap();
543 let proof_gadget =
544 TestProofGadget::alloc(cs.ns(|| "Proof"), || Ok(proof.clone())).unwrap();
545 println!("Time to verify!\n\n\n\n");
546 <TestVerifierGadget as NIZKVerifierGadget<TestProofSystem, Fq>>::check_verify(
547 cs.ns(|| "Verify"),
548 &vk_gadget,
549 input_gadgets.iter(),
550 &proof_gadget,
551 )
552 .unwrap();
553 if !cs.is_satisfied() {
554 println!("=========================================================");
555 println!("Unsatisfied constraints:");
556 println!("{:?}", cs.which_is_unsatisfied().unwrap());
557 println!("=========================================================");
558 }
559
560 // cs.print_named_objects();
561 assert!(cs.is_satisfied());
562 }
563 }
564}
565
566#[cfg(test)]
567mod test_recursive {
568 use groth16::*;
569 use r1cs_core::{ConstraintSynthesizer, ConstraintSystem, SynthesisError};
570
571 use super::*;
572 use algebra::{
573 fields::FpParameters,
574 mnt4_298::{Fq as MNT4Fq, FqParameters as MNT4FqParameters, Fr as MNT4Fr, MNT4_298},
575 mnt6_298::{Fq as MNT6Fq, FqParameters as MNT6FqParameters, Fr as MNT6Fr, MNT6_298},
576 test_rng, BigInteger, PrimeField,
577 };
578 use r1cs_std::{
579 fields::fp::FpGadget, mnt4_298::PairingGadget as MNT4_298PairingGadget,
580 mnt6_298::PairingGadget as MNT6_298PairingGadget,
581 test_constraint_system::TestConstraintSystem, uint8::UInt8,
582 };
583 use rand::Rng;
584
585 type TestProofSystem1 = Groth16<MNT6_298, Bench<MNT4Fq>, MNT6Fr>;
586 type TestVerifierGadget1 = Groth16VerifierGadget<MNT6_298, MNT6Fq, MNT6_298PairingGadget>;
587 type TestProofGadget1 = ProofGadget<MNT6_298, MNT6Fq, MNT6_298PairingGadget>;
588 type TestVkGadget1 = VerifyingKeyGadget<MNT6_298, MNT6Fq, MNT6_298PairingGadget>;
589
590 type TestProofSystem2 = Groth16<MNT4_298, Wrapper, MNT4Fr>;
591 type TestVerifierGadget2 = Groth16VerifierGadget<MNT4_298, MNT4Fq, MNT4_298PairingGadget>;
592 type TestProofGadget2 = ProofGadget<MNT4_298, MNT4Fq, MNT4_298PairingGadget>;
593 type TestVkGadget2 = VerifyingKeyGadget<MNT4_298, MNT4Fq, MNT4_298PairingGadget>;
594
595 #[derive(Clone)]
596 struct Bench<F: Field> {
597 inputs: Vec<Option<F>>,
598 num_constraints: usize,
599 }
600
601 impl<F: Field> ConstraintSynthesizer<F> for Bench<F> {
602 fn generate_constraints<CS: ConstraintSystem<F>>(
603 self,
604 cs: &mut CS,
605 ) -> Result<(), SynthesisError> {
606 assert!(self.inputs.len() >= 2);
607 assert!(self.num_constraints >= self.inputs.len());
608
609 let mut variables: Vec<_> = Vec::with_capacity(self.inputs.len());
610 for (i, input) in self.inputs.into_iter().enumerate() {
611 let input_var = cs.alloc_input(
612 || format!("Input {}", i),
613 || input.ok_or(SynthesisError::AssignmentMissing),
614 )?;
615 variables.push((input, input_var));
616 }
617
618 for i in 0..self.num_constraints {
619 let new_entry = {
620 let (input_1_val, input_1_var) = variables[i];
621 let (input_2_val, input_2_var) = variables[i + 1];
622 let result_val = input_1_val
623 .and_then(|input_1| input_2_val.map(|input_2| input_1 * &input_2));
624 let result_var = cs.alloc(
625 || format!("Result {}", i),
626 || result_val.ok_or(SynthesisError::AssignmentMissing),
627 )?;
628 cs.enforce(
629 || format!("Enforce constraint {}", i),
630 |lc| lc + input_1_var,
631 |lc| lc + input_2_var,
632 |lc| lc + result_var,
633 );
634 (result_val, result_var)
635 };
636 variables.push(new_entry);
637 }
638 Ok(())
639 }
640 }
641
642 struct Wrapper {
643 inputs: Vec<Option<MNT4Fq>>,
644 params: Parameters<MNT6_298>,
645 proof: Proof<MNT6_298>,
646 }
647
648 impl ConstraintSynthesizer<MNT6Fq> for Wrapper {
649 fn generate_constraints<CS: ConstraintSystem<MNT6Fq>>(
650 self,
651 cs: &mut CS,
652 ) -> Result<(), SynthesisError> {
653 let params = self.params;
654 let proof = self.proof;
655 let inputs: Vec<_> = self
656 .inputs
657 .into_iter()
658 .map(|input| input.unwrap())
659 .collect();
660 let input_gadgets;
661
662 {
663 let mut cs = cs.ns(|| "Allocate Input");
664 // Chain all input values in one large byte array.
665 let input_bytes = inputs
666 .clone()
667 .into_iter()
668 .flat_map(|input| {
669 input
670 .into_repr()
671 .as_ref()
672 .iter()
673 .flat_map(|l| l.to_le_bytes().to_vec())
674 .collect::<Vec<_>>()
675 })
676 .collect::<Vec<_>>();
677
678 // Allocate this byte array as input packed into field elements.
679 let input_bytes = UInt8::alloc_input_vec(cs.ns(|| "Input"), &input_bytes[..])?;
680 // 40 byte
681 let element_size = <MNT4FqParameters as FpParameters>::BigInt::NUM_LIMBS * 8;
682 input_gadgets = input_bytes
683 .chunks(element_size)
684 .map(|chunk| {
685 chunk
686 .iter()
687 .flat_map(|byte| byte.into_bits_le())
688 .collect::<Vec<_>>()
689 })
690 .collect::<Vec<_>>();
691 }
692
693 let vk_gadget = TestVkGadget1::alloc(cs.ns(|| "Vk"), || Ok(¶ms.vk))?;
694 let proof_gadget =
695 TestProofGadget1::alloc(cs.ns(|| "Proof"), || Ok(proof.clone())).unwrap();
696 <TestVerifierGadget1 as NIZKVerifierGadget<TestProofSystem1, MNT6Fq>>::check_verify(
697 cs.ns(|| "Verify"),
698 &vk_gadget,
699 input_gadgets.iter(),
700 &proof_gadget,
701 )?;
702 Ok(())
703 }
704 }
705
706 #[test]
707 fn groth16_recursive_verifier_test() {
708 let num_inputs = 100;
709 let num_constraints = num_inputs;
710 let rng = &mut test_rng();
711 let mut inputs: Vec<Option<MNT4Fq>> = Vec::with_capacity(num_inputs);
712 for _ in 0..num_inputs {
713 inputs.push(Some(rng.gen()));
714 }
715
716 // Generate inner params and proof.
717 let inner_params = {
718 let c = Bench::<MNT4Fq> {
719 inputs: vec![None; num_inputs],
720 num_constraints,
721 };
722
723 generate_random_parameters(c, rng).unwrap()
724 };
725
726 let inner_proof = {
727 // Create an instance of our circuit (with the
728 // witness)
729 let c = Bench {
730 inputs: inputs.clone(),
731 num_constraints,
732 };
733 // Create a groth16 proof with our parameters.
734 create_random_proof(c, &inner_params, rng).unwrap()
735 };
736
737 // Generate outer params and proof.
738 let params = {
739 let c = Wrapper {
740 inputs: inputs.clone(),
741 params: inner_params.clone(),
742 proof: inner_proof.clone(),
743 };
744
745 generate_random_parameters(c, rng).unwrap()
746 };
747
748 {
749 let proof = {
750 // Create an instance of our circuit (with the
751 // witness)
752 let c = Wrapper {
753 inputs: inputs.clone(),
754 params: inner_params.clone(),
755 proof: inner_proof.clone(),
756 };
757 // Create a groth16 proof with our parameters.
758 create_random_proof(c, ¶ms, rng).unwrap()
759 };
760
761 let mut cs = TestConstraintSystem::<MNT4Fq>::new();
762
763 let inputs: Vec<_> = inputs.into_iter().map(|input| input.unwrap()).collect();
764 let mut input_gadgets = Vec::new();
765
766 {
767 let bigint_size = <MNT4FqParameters as FpParameters>::BigInt::NUM_LIMBS * 64;
768 let mut input_bits = Vec::new();
769 let mut cs = cs.ns(|| "Allocate Input");
770 for (i, input) in inputs.into_iter().enumerate() {
771 let input_gadget =
772 FpGadget::alloc_input(cs.ns(|| format!("Input {}", i)), || Ok(input))
773 .unwrap();
774 let mut fp_bits = input_gadget
775 .to_bits(cs.ns(|| format!("To bits {}", i)))
776 .unwrap();
777
778 // FpGadget::to_bits outputs a big-endian binary representation of
779 // fe_gadget's value, so we have to reverse it to get the little-endian
780 // form.
781 fp_bits.reverse();
782
783 // Use 320 bits per element.
784 for _ in fp_bits.len()..bigint_size {
785 fp_bits.push(Boolean::constant(false));
786 }
787 input_bits.extend_from_slice(&fp_bits);
788 }
789
790 // Pack input bits into field elements of the underlying circuit.
791 let max_size = 8 * (<MNT6FqParameters as FpParameters>::CAPACITY / 8) as usize;
792 let max_size = max_size as usize;
793 let bigint_size = <MNT6FqParameters as FpParameters>::BigInt::NUM_LIMBS * 64;
794 for chunk in input_bits.chunks(max_size) {
795 let mut chunk = chunk.to_vec();
796 let len = chunk.len();
797 for _ in len..bigint_size {
798 chunk.push(Boolean::constant(false));
799 }
800 input_gadgets.push(chunk);
801 }
802 // assert!(!verify_proof(&pvk, &proof, &[a]).unwrap());
803 }
804
805 let vk_gadget = TestVkGadget2::alloc_input(cs.ns(|| "Vk"), || Ok(¶ms.vk)).unwrap();
806 let proof_gadget =
807 TestProofGadget2::alloc(cs.ns(|| "Proof"), || Ok(proof.clone())).unwrap();
808 println!("Time to verify!\n\n\n\n");
809 <TestVerifierGadget2 as NIZKVerifierGadget<TestProofSystem2, MNT4Fq>>::check_verify(
810 cs.ns(|| "Verify"),
811 &vk_gadget,
812 input_gadgets.iter(),
813 &proof_gadget,
814 )
815 .unwrap();
816 if !cs.is_satisfied() {
817 println!("=========================================================");
818 println!("Unsatisfied constraints:");
819 println!("{:?}", cs.which_is_unsatisfied().unwrap());
820 println!("=========================================================");
821 }
822
823 // cs.print_named_objects();
824 assert!(cs.is_satisfied());
825 }
826 }
827}