use crate::nizk::{gm17::Gm17, NIZKVerifierGadget}; use algebra::{AffineCurve, Field, PairingEngine, ToConstraintField}; use r1cs_core::{ConstraintSynthesizer, ConstraintSystem, SynthesisError}; use r1cs_std::prelude::*; use gm17::{Proof, VerifyingKey}; use std::{borrow::Borrow, marker::PhantomData}; #[derive(Derivative)] #[derivative(Clone(bound = "P::G1Gadget: Clone, P::G2Gadget: Clone"))] pub struct ProofGadget< PairingE: PairingEngine, ConstraintF: Field, P: PairingGadget, > { pub a: P::G1Gadget, pub b: P::G2Gadget, pub c: P::G1Gadget, } #[derive(Derivative)] #[derivative(Clone( bound = "P::G1Gadget: Clone, P::GTGadget: Clone, P::G1PreparedGadget: Clone, \ P::G2PreparedGadget: Clone, " ))] pub struct VerifyingKeyGadget< PairingE: PairingEngine, ConstraintF: Field, P: PairingGadget, > { pub h_g2: P::G2Gadget, pub g_alpha_g1: P::G1Gadget, pub h_beta_g2: P::G2Gadget, pub g_gamma_g1: P::G1Gadget, pub h_gamma_g2: P::G2Gadget, pub query: Vec, } impl> VerifyingKeyGadget { pub fn prepare>( &self, mut cs: CS, ) -> Result, SynthesisError> { let mut cs = cs.ns(|| "Preparing verifying key"); let g_alpha_pc = P::prepare_g1(&mut cs.ns(|| "Prepare g_alpha_g1"), &self.g_alpha_g1)?; let h_beta_pc = P::prepare_g2(&mut cs.ns(|| "Prepare h_beta_g2"), &self.h_beta_g2)?; let g_gamma_pc = P::prepare_g1(&mut cs.ns(|| "Prepare g_gamma_pc"), &self.g_gamma_g1)?; let h_gamma_pc = P::prepare_g2(&mut cs.ns(|| "Prepare h_gamma_pc"), &self.h_gamma_g2)?; let h_pc = P::prepare_g2(&mut cs.ns(|| "Prepare h_pc"), &self.h_g2)?; Ok(PreparedVerifyingKeyGadget { g_alpha: self.g_alpha_g1.clone(), h_beta: self.h_beta_g2.clone(), g_alpha_pc, h_beta_pc, g_gamma_pc, h_gamma_pc, h_pc, query: self.query.clone(), }) } } #[derive(Derivative)] #[derivative(Clone( bound = "P::G1Gadget: Clone, P::GTGadget: Clone, P::G1PreparedGadget: Clone, \ P::G2PreparedGadget: Clone, " ))] pub struct PreparedVerifyingKeyGadget< PairingE: PairingEngine, ConstraintF: Field, P: PairingGadget, > { pub g_alpha: P::G1Gadget, pub h_beta: P::G2Gadget, pub g_alpha_pc: P::G1PreparedGadget, pub h_beta_pc: P::G2PreparedGadget, pub g_gamma_pc: P::G1PreparedGadget, pub h_gamma_pc: P::G2PreparedGadget, pub h_pc: P::G2PreparedGadget, pub query: Vec, } pub struct Gm17VerifierGadget where PairingE: PairingEngine, ConstraintF: Field, P: PairingGadget, { _pairing_engine: PhantomData, _engine: PhantomData, _pairing_gadget: PhantomData

, } impl NIZKVerifierGadget, ConstraintF> for Gm17VerifierGadget where PairingE: PairingEngine, ConstraintF: Field, C: ConstraintSynthesizer, V: ToConstraintField, P: PairingGadget, { type VerificationKeyGadget = VerifyingKeyGadget; type ProofGadget = ProofGadget; fn check_verify<'a, CS, I, T>( mut cs: CS, vk: &Self::VerificationKeyGadget, mut public_inputs: I, proof: &Self::ProofGadget, ) -> Result<(), SynthesisError> where CS: ConstraintSystem, I: Iterator, T: 'a + ToBitsGadget + ?Sized, { let pvk = vk.prepare(&mut cs.ns(|| "Prepare vk"))?; // e(A*G^{alpha}, B*H^{beta}) = e(G^{alpha}, H^{beta}) * e(G^{psi}, H^{gamma}) * // e(C, H) where psi = \sum_{i=0}^l input_i pvk.query[i] let g_psi = { let mut cs = cs.ns(|| "Process input"); let mut g_psi = pvk.query[0].clone(); let mut input_len = 1; for (i, (input, b)) in public_inputs .by_ref() .zip(pvk.query.iter().skip(1)) .enumerate() { let input_bits = input.to_bits(cs.ns(|| format!("Input {}", i)))?; g_psi = b.mul_bits(cs.ns(|| format!("Mul {}", i)), &g_psi, input_bits.iter())?; input_len += 1; } // Check that the input and the query in the verification are of the // same length. assert!(input_len == pvk.query.len() && public_inputs.next().is_none()); g_psi }; let mut test1_a_g_alpha = proof.a.add(cs.ns(|| "A * G^{alpha}"), &pvk.g_alpha)?; let test1_b_h_beta = proof.b.add(cs.ns(|| "B * H^{beta}"), &pvk.h_beta)?; let test1_exp = { test1_a_g_alpha = test1_a_g_alpha.negate(cs.ns(|| "neg 1"))?; let test1_a_g_alpha_prep = P::prepare_g1(cs.ns(|| "First prep"), &test1_a_g_alpha)?; let test1_b_h_beta_prep = P::prepare_g2(cs.ns(|| "Second prep"), &test1_b_h_beta)?; let g_psi_prep = P::prepare_g1(cs.ns(|| "Third prep"), &g_psi)?; let c_prep = P::prepare_g1(cs.ns(|| "Fourth prep"), &proof.c)?; P::miller_loop( cs.ns(|| "Miller loop 1"), &[ test1_a_g_alpha_prep, g_psi_prep, c_prep, pvk.g_alpha_pc.clone(), ], &[ test1_b_h_beta_prep, pvk.h_gamma_pc.clone(), pvk.h_pc.clone(), pvk.h_beta_pc.clone(), ], )? }; let test1 = P::final_exponentiation(cs.ns(|| "Final Exp 1"), &test1_exp).unwrap(); // e(A, H^{gamma}) = e(G^{gamma}, B) let test2_exp = { let a_prep = P::prepare_g1(cs.ns(|| "Fifth prep"), &proof.a)?; // pvk.h_gamma_pc //&pvk.g_gamma_pc let proof_b = proof.b.negate(cs.ns(|| "Negate b"))?; let b_prep = P::prepare_g2(cs.ns(|| "Sixth prep"), &proof_b)?; P::miller_loop( cs.ns(|| "Miller loop 4"), &[a_prep, pvk.g_gamma_pc.clone()], &[pvk.h_gamma_pc, b_prep], )? }; let test2 = P::final_exponentiation(cs.ns(|| "Final Exp 2"), &test2_exp)?; let one = P::GTGadget::one(cs.ns(|| "GT One"))?; test1.enforce_equal(cs.ns(|| "Test 1"), &one)?; test2.enforce_equal(cs.ns(|| "Test 2"), &one)?; Ok(()) } } impl AllocGadget, ConstraintF> for VerifyingKeyGadget where PairingE: PairingEngine, ConstraintF: Field, P: PairingGadget, { #[inline] fn alloc>( mut cs: CS, value_gen: FN, ) -> Result where FN: FnOnce() -> Result, T: Borrow>, { value_gen().and_then(|vk| { let VerifyingKey { h_g2, g_alpha_g1, h_beta_g2, g_gamma_g1, h_gamma_g2, query, } = vk.borrow().clone(); let h_g2 = P::G2Gadget::alloc(cs.ns(|| "h_g2"), || Ok(h_g2.into_projective()))?; let g_alpha_g1 = P::G1Gadget::alloc(cs.ns(|| "g_alpha"), || Ok(g_alpha_g1.into_projective()))?; let h_beta_g2 = P::G2Gadget::alloc(cs.ns(|| "h_beta"), || Ok(h_beta_g2.into_projective()))?; let g_gamma_g1 = P::G1Gadget::alloc(cs.ns(|| "g_gamma_g1"), || Ok(g_gamma_g1.into_projective()))?; let h_gamma_g2 = P::G2Gadget::alloc(cs.ns(|| "h_gamma_g2"), || Ok(h_gamma_g2.into_projective()))?; let query = query .into_iter() .enumerate() .map(|(i, query_i)| { P::G1Gadget::alloc(cs.ns(|| format!("query_{}", i)), || { Ok(query_i.into_projective()) }) }) .collect::>() .into_iter() .collect::>()?; Ok(Self { h_g2, g_alpha_g1, h_beta_g2, g_gamma_g1, h_gamma_g2, query, }) }) } #[inline] fn alloc_input>( mut cs: CS, value_gen: FN, ) -> Result where FN: FnOnce() -> Result, T: Borrow>, { value_gen().and_then(|vk| { let VerifyingKey { h_g2, g_alpha_g1, h_beta_g2, g_gamma_g1, h_gamma_g2, query, } = vk.borrow().clone(); let h_g2 = P::G2Gadget::alloc_input(cs.ns(|| "h_g2"), || Ok(h_g2.into_projective()))?; let g_alpha_g1 = P::G1Gadget::alloc_input(cs.ns(|| "g_alpha"), || Ok(g_alpha_g1.into_projective()))?; let h_beta_g2 = P::G2Gadget::alloc_input(cs.ns(|| "h_beta"), || Ok(h_beta_g2.into_projective()))?; let g_gamma_g1 = P::G1Gadget::alloc_input(cs.ns(|| "g_gamma_g1"), || { Ok(g_gamma_g1.into_projective()) })?; let h_gamma_g2 = P::G2Gadget::alloc_input(cs.ns(|| "h_gamma_g2"), || { Ok(h_gamma_g2.into_projective()) })?; let query = query .into_iter() .enumerate() .map(|(i, query_i)| { P::G1Gadget::alloc_input(cs.ns(|| format!("query_{}", i)), || { Ok(query_i.into_projective()) }) }) .collect::>() .into_iter() .collect::>()?; Ok(Self { h_g2, g_alpha_g1, h_beta_g2, g_gamma_g1, h_gamma_g2, query, }) }) } } impl AllocGadget, ConstraintF> for ProofGadget where PairingE: PairingEngine, ConstraintF: Field, P: PairingGadget, { #[inline] fn alloc>( mut cs: CS, value_gen: FN, ) -> Result where FN: FnOnce() -> Result, T: Borrow>, { value_gen().and_then(|proof| { let Proof { a, b, c } = proof.borrow().clone(); let a = P::G1Gadget::alloc_checked(cs.ns(|| "a"), || Ok(a.into_projective()))?; let b = P::G2Gadget::alloc_checked(cs.ns(|| "b"), || Ok(b.into_projective()))?; let c = P::G1Gadget::alloc_checked(cs.ns(|| "c"), || Ok(c.into_projective()))?; Ok(Self { a, b, c }) }) } #[inline] fn alloc_input>( mut cs: CS, value_gen: FN, ) -> Result where FN: FnOnce() -> Result, T: Borrow>, { value_gen().and_then(|proof| { let Proof { a, b, c } = proof.borrow().clone(); // We don't need to check here because the prime order check can be performed // in plain. let a = P::G1Gadget::alloc_input(cs.ns(|| "a"), || Ok(a.into_projective()))?; let b = P::G2Gadget::alloc_input(cs.ns(|| "b"), || Ok(b.into_projective()))?; let c = P::G1Gadget::alloc_input(cs.ns(|| "c"), || Ok(c.into_projective()))?; Ok(Self { a, b, c }) }) } } impl ToBytesGadget for VerifyingKeyGadget where PairingE: PairingEngine, ConstraintF: Field, P: PairingGadget, { #[inline] fn to_bytes>( &self, mut cs: CS, ) -> Result, SynthesisError> { let mut bytes = Vec::new(); bytes.extend_from_slice(&self.h_g2.to_bytes(&mut cs.ns(|| "h_g2 to bytes"))?); bytes.extend_from_slice( &self .g_alpha_g1 .to_bytes(&mut cs.ns(|| "g_alpha_g1 to bytes"))?, ); bytes.extend_from_slice( &self .h_beta_g2 .to_bytes(&mut cs.ns(|| "h_beta_g2 to bytes"))?, ); bytes.extend_from_slice( &self .g_gamma_g1 .to_bytes(&mut cs.ns(|| "g_gamma_g1 to bytes"))?, ); bytes.extend_from_slice( &self .h_gamma_g2 .to_bytes(&mut cs.ns(|| "h_gamma_g2 to bytes"))?, ); for (i, q) in self.query.iter().enumerate() { let mut cs = cs.ns(|| format!("Iteration {}", i)); bytes.extend_from_slice(&q.to_bytes(&mut cs.ns(|| "q"))?); } Ok(bytes) } fn to_bytes_strict>( &self, cs: CS, ) -> Result, SynthesisError> { self.to_bytes(cs) } } #[cfg(test)] mod test { use gm17::*; use r1cs_core::{ConstraintSynthesizer, ConstraintSystem, SynthesisError}; use super::*; use algebra::{ curves::bls12_377::Bls12_377, fields::bls12_377::{Fq, Fr}, BitIterator, PrimeField, }; use r1cs_std::{ boolean::Boolean, pairing::bls12_377::PairingGadget as Bls12_377PairingGadget, test_constraint_system::TestConstraintSystem, }; use rand::{thread_rng, Rng}; type TestProofSystem = Gm17, Fr>; type TestVerifierGadget = Gm17VerifierGadget; type TestProofGadget = ProofGadget; type TestVkGadget = VerifyingKeyGadget; struct Bench { inputs: Vec>, num_constraints: usize, } impl ConstraintSynthesizer for Bench { fn generate_constraints>( self, cs: &mut CS, ) -> Result<(), SynthesisError> { assert!(self.inputs.len() >= 2); assert!(self.num_constraints >= self.inputs.len()); let mut variables: Vec<_> = Vec::with_capacity(self.inputs.len()); for (i, input) in self.inputs.into_iter().enumerate() { let input_var = cs.alloc_input( || format!("Input {}", i), || input.ok_or(SynthesisError::AssignmentMissing), )?; variables.push((input, input_var)); } for i in 0..self.num_constraints { let new_entry = { let (input_1_val, input_1_var) = variables[i]; let (input_2_val, input_2_var) = variables[i + 1]; let result_val = input_1_val .and_then(|input_1| input_2_val.map(|input_2| input_1 * &input_2)); let result_var = cs.alloc( || format!("Result {}", i), || result_val.ok_or(SynthesisError::AssignmentMissing), )?; cs.enforce( || format!("Enforce constraint {}", i), |lc| lc + input_1_var, |lc| lc + input_2_var, |lc| lc + result_var, ); (result_val, result_var) }; variables.push(new_entry); } Ok(()) } } #[test] fn gm17_verifier_test() { let num_inputs = 100; let num_constraints = num_inputs; let rng = &mut thread_rng(); let mut inputs: Vec> = Vec::with_capacity(num_inputs); for _ in 0..num_inputs { inputs.push(Some(rng.gen())); } let params = { let c = Bench:: { inputs: vec![None; num_inputs], num_constraints, }; generate_random_parameters(c, rng).unwrap() }; { let proof = { // Create an instance of our circuit (with the // witness) let c = Bench { inputs: inputs.clone(), num_constraints, }; // Create a gm17 proof with our parameters. create_random_proof(c, ¶ms, rng).unwrap() }; // assert!(!verify_proof(&pvk, &proof, &[a]).unwrap()); let mut cs = TestConstraintSystem::::new(); let inputs: Vec<_> = inputs.into_iter().map(|input| input.unwrap()).collect(); let mut input_gadgets = Vec::new(); { let mut cs = cs.ns(|| "Allocate Input"); for (i, input) in inputs.into_iter().enumerate() { let mut input_bits = BitIterator::new(input.into_repr()).collect::>(); // Input must be in little-endian, but BitIterator outputs in big-endian. input_bits.reverse(); let input_bits = Vec::::alloc_input(cs.ns(|| format!("Input {}", i)), || { Ok(input_bits) }) .unwrap(); input_gadgets.push(input_bits); } } let vk_gadget = TestVkGadget::alloc_input(cs.ns(|| "Vk"), || Ok(¶ms.vk)).unwrap(); let proof_gadget = TestProofGadget::alloc(cs.ns(|| "Proof"), || Ok(proof.clone())).unwrap(); println!("Time to verify!\n\n\n\n"); >::check_verify( cs.ns(|| "Verify"), &vk_gadget, input_gadgets.iter(), &proof_gadget, ) .unwrap(); if !cs.is_satisfied() { println!("========================================================="); println!("Unsatisfied constraints:"); println!("{:?}", cs.which_is_unsatisfied().unwrap()); println!("========================================================="); } // cs.print_named_objects(); assert!(cs.is_satisfied()); } } }