use rand_core::RngCore; use std::ops::{AddAssign, MulAssign}; use std::sync::Arc; use futures::Future; use ff::Field; use group::{CurveAffine, CurveProjective}; use pairing::Engine; use super::{ParameterSource, Proof}; use crate::{Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable}; use crate::domain::{EvaluationDomain, Scalar}; use crate::multiexp::{multiexp, DensityTracker, FullDensity}; use crate::multicore::Worker; fn eval( lc: &LinearCombination, mut input_density: Option<&mut DensityTracker>, mut aux_density: Option<&mut DensityTracker>, input_assignment: &[E::Fr], aux_assignment: &[E::Fr], ) -> E::Fr { let mut acc = E::Fr::zero(); for &(index, coeff) in lc.0.iter() { let mut tmp; match index { Variable(Index::Input(i)) => { tmp = input_assignment[i]; if let Some(ref mut v) = input_density { v.inc(i); } } Variable(Index::Aux(i)) => { tmp = aux_assignment[i]; if let Some(ref mut v) = aux_density { v.inc(i); } } } if coeff == E::Fr::one() { acc.add_assign(&tmp); } else { tmp.mul_assign(&coeff); acc.add_assign(&tmp); } } acc } struct ProvingAssignment { // Density of queries a_aux_density: DensityTracker, b_input_density: DensityTracker, b_aux_density: DensityTracker, // Evaluations of A, B, C polynomials a: Vec>, b: Vec>, c: Vec>, // Assignments of variables input_assignment: Vec, aux_assignment: Vec, } impl ConstraintSystem for ProvingAssignment { type Root = Self; fn alloc(&mut self, _: A, f: F) -> Result where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into, { self.aux_assignment.push(f()?); self.a_aux_density.add_element(); self.b_aux_density.add_element(); Ok(Variable(Index::Aux(self.aux_assignment.len() - 1))) } fn alloc_input(&mut self, _: A, f: F) -> Result where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into, { self.input_assignment.push(f()?); self.b_input_density.add_element(); Ok(Variable(Index::Input(self.input_assignment.len() - 1))) } fn enforce(&mut self, _: A, a: LA, b: LB, c: LC) where A: FnOnce() -> AR, AR: Into, LA: FnOnce(LinearCombination) -> LinearCombination, LB: FnOnce(LinearCombination) -> LinearCombination, LC: FnOnce(LinearCombination) -> LinearCombination, { let a = a(LinearCombination::zero()); let b = b(LinearCombination::zero()); let c = c(LinearCombination::zero()); self.a.push(Scalar(eval( &a, // Inputs have full density in the A query // because there are constraints of the // form x * 0 = 0 for each input. None, Some(&mut self.a_aux_density), &self.input_assignment, &self.aux_assignment, ))); self.b.push(Scalar(eval( &b, Some(&mut self.b_input_density), Some(&mut self.b_aux_density), &self.input_assignment, &self.aux_assignment, ))); self.c.push(Scalar(eval( &c, // There is no C polynomial query, // though there is an (beta)A + (alpha)B + C // query for all aux variables. // However, that query has full density. None, None, &self.input_assignment, &self.aux_assignment, ))); } fn push_namespace(&mut self, _: N) where NR: Into, N: FnOnce() -> NR, { // Do nothing; we don't care about namespaces in this context. } fn pop_namespace(&mut self) { // Do nothing; we don't care about namespaces in this context. } fn get_root(&mut self) -> &mut Self::Root { self } } pub fn create_random_proof>( circuit: C, params: P, rng: &mut R, ) -> Result, SynthesisError> where E: Engine, C: Circuit, R: RngCore, { let r = E::Fr::random(rng); let s = E::Fr::random(rng); create_proof::(circuit, params, r, s) } pub fn create_proof>( circuit: C, mut params: P, r: E::Fr, s: E::Fr, ) -> Result, SynthesisError> where E: Engine, C: Circuit, { let mut prover = ProvingAssignment { a_aux_density: DensityTracker::new(), b_input_density: DensityTracker::new(), b_aux_density: DensityTracker::new(), a: vec![], b: vec![], c: vec![], input_assignment: vec![], aux_assignment: vec![], }; prover.alloc_input(|| "", || Ok(E::Fr::one()))?; circuit.synthesize(&mut prover)?; for i in 0..prover.input_assignment.len() { prover.enforce(|| "", |lc| lc + Variable(Index::Input(i)), |lc| lc, |lc| lc); } let worker = Worker::new(); let vk = params.get_vk(prover.input_assignment.len())?; let h = { let mut a = EvaluationDomain::from_coeffs(prover.a)?; let mut b = EvaluationDomain::from_coeffs(prover.b)?; let mut c = EvaluationDomain::from_coeffs(prover.c)?; a.ifft(&worker); a.coset_fft(&worker); b.ifft(&worker); b.coset_fft(&worker); c.ifft(&worker); c.coset_fft(&worker); a.mul_assign(&worker, &b); drop(b); a.sub_assign(&worker, &c); drop(c); a.divide_by_z_on_coset(&worker); a.icoset_fft(&worker); let mut a = a.into_coeffs(); let a_len = a.len() - 1; a.truncate(a_len); // TODO: parallelize if it's even helpful let a = Arc::new(a.into_iter().map(|s| s.0).collect::>()); multiexp(&worker, params.get_h(a.len())?, FullDensity, a) }; // TODO: parallelize if it's even helpful let input_assignment = Arc::new(prover.input_assignment); let aux_assignment = Arc::new(prover.aux_assignment); let l = multiexp( &worker, params.get_l(aux_assignment.len())?, FullDensity, aux_assignment.clone(), ); let a_aux_density_total = prover.a_aux_density.get_total_density(); let (a_inputs_source, a_aux_source) = params.get_a(input_assignment.len(), a_aux_density_total)?; let a_inputs = multiexp( &worker, a_inputs_source, FullDensity, input_assignment.clone(), ); let a_aux = multiexp( &worker, a_aux_source, Arc::new(prover.a_aux_density), aux_assignment.clone(), ); let b_input_density = Arc::new(prover.b_input_density); let b_input_density_total = b_input_density.get_total_density(); let b_aux_density = Arc::new(prover.b_aux_density); let b_aux_density_total = b_aux_density.get_total_density(); let (b_g1_inputs_source, b_g1_aux_source) = params.get_b_g1(b_input_density_total, b_aux_density_total)?; let b_g1_inputs = multiexp( &worker, b_g1_inputs_source, b_input_density.clone(), input_assignment.clone(), ); let b_g1_aux = multiexp( &worker, b_g1_aux_source, b_aux_density.clone(), aux_assignment.clone(), ); let (b_g2_inputs_source, b_g2_aux_source) = params.get_b_g2(b_input_density_total, b_aux_density_total)?; let b_g2_inputs = multiexp( &worker, b_g2_inputs_source, b_input_density, input_assignment, ); let b_g2_aux = multiexp(&worker, b_g2_aux_source, b_aux_density, aux_assignment); if vk.delta_g1.is_zero() || vk.delta_g2.is_zero() { // If this element is zero, someone is trying to perform a // subversion-CRS attack. return Err(SynthesisError::UnexpectedIdentity); } let mut g_a = vk.delta_g1.mul(r); AddAssign::<&E::G1Affine>::add_assign(&mut g_a, &vk.alpha_g1); let mut g_b = vk.delta_g2.mul(s); AddAssign::<&E::G2Affine>::add_assign(&mut g_b, &vk.beta_g2); let mut g_c; { let mut rs = r; rs.mul_assign(&s); g_c = vk.delta_g1.mul(rs); AddAssign::<&E::G1>::add_assign(&mut g_c, &vk.alpha_g1.mul(s)); AddAssign::<&E::G1>::add_assign(&mut g_c, &vk.beta_g1.mul(r)); } let mut a_answer = a_inputs.wait()?; AddAssign::<&E::G1>::add_assign(&mut a_answer, &a_aux.wait()?); AddAssign::<&E::G1>::add_assign(&mut g_a, &a_answer); a_answer.mul_assign(s); AddAssign::<&E::G1>::add_assign(&mut g_c, &a_answer); let mut b1_answer: E::G1 = b_g1_inputs.wait()?; AddAssign::<&E::G1>::add_assign(&mut b1_answer, &b_g1_aux.wait()?); let mut b2_answer = b_g2_inputs.wait()?; AddAssign::<&E::G2>::add_assign(&mut b2_answer, &b_g2_aux.wait()?); AddAssign::<&E::G2>::add_assign(&mut g_b, &b2_answer); b1_answer.mul_assign(r); AddAssign::<&E::G1>::add_assign(&mut g_c, &b1_answer); AddAssign::<&E::G1>::add_assign(&mut g_c, &h.wait()?); AddAssign::<&E::G1>::add_assign(&mut g_c, &l.wait()?); Ok(Proof { a: g_a.into_affine(), b: g_b.into_affine(), c: g_c.into_affine(), }) }