diff --git a/src/gm17/generator.rs b/src/gm17/generator.rs new file mode 100644 index 0000000..6895cde --- /dev/null +++ b/src/gm17/generator.rs @@ -0,0 +1,700 @@ +use super::super::verbose_flag; + +use rand::Rng; + +use std::sync::Arc; + +use pairing::{ + Engine, + Wnaf, + CurveProjective, + CurveAffine +}; + +use ff::{ + PrimeField, + Field +}; + +use super::{ + Parameters, + VerifyingKey +}; + +use ::{ + SynthesisError, + Circuit, + ConstraintSystem, + LinearCombination, + Variable, + Index +}; + +use ::domain::{ + EvaluationDomain, + Scalar +}; + +use ::multicore::{ + Worker +}; + +// /// Generates a random common reference string for +// /// a circuit. +// pub fn generate_random_parameters( +// circuit: C, +// rng: &mut R +// ) -> Result, SynthesisError> +// where E: Engine, C: Circuit, R: Rng +// { +// let g1 = rng.gen(); +// let g2 = rng.gen(); +// let alpha = rng.gen(); +// let beta = rng.gen(); +// let gamma = rng.gen(); +// let delta = rng.gen(); +// let tau = rng.gen(); + +// generate_parameters::( +// circuit, +// g1, +// g2, +// alpha, +// beta, +// gamma, +// delta, +// tau +// ) +// } + +/// This is our assembly structure that we'll use to synthesize the +/// circuit into a SAP. Square arithmetic problem is different from QAP in a form: +/// it's A*A - C = 0 instead of A*B - C = 0 +struct KeypairAssembly { + num_inputs: usize, + num_aux: usize, + num_constraints: usize, + num_r1cs_aux: usize, + num_r1cs_constraints: usize, + at_inputs: Vec>, + ct_inputs: Vec>, + at_aux: Vec>, + ct_aux: Vec> +} + +impl ConstraintSystem for KeypairAssembly { + type Root = Self; + + fn alloc( + &mut self, + _: A, + _: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into + { + // There is no assignment, so we don't even invoke the + // function for obtaining one. + + let index = self.num_aux; + self.num_aux += 1; + + self.num_r1cs_aux += 1; + + self.at_aux.push(vec![]); + self.ct_aux.push(vec![]); + + Ok(Variable(Index::Aux(index))) + } + + fn alloc_input( + &mut self, + _: A, + _: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into + { + // There is no assignment, so we don't even invoke the + // function for obtaining one. + + let index = self.num_inputs; + self.num_inputs += 1; + + self.at_inputs.push(vec![]); + self.ct_inputs.push(vec![]); + + Ok(Variable(Index::Input(index))) + } + + 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 + { + use std::ops::{Add, Sub}; + + // this is where reduction happens. First we need to re-arrange initial constraints + // from the form * = to an artificial + // * = y + // * = 4* + y + + fn quadruple( + coeff: E::Fr + ) -> E::Fr { + let mut tmp = coeff; + tmp.double(); + tmp.double(); + + tmp + } + + fn eval( + l: LinearCombination, + inputs: &mut [Vec<(E::Fr, usize)>], + aux: &mut [Vec<(E::Fr, usize)>], + this_constraint: usize + ) + { + for (index, coeff) in l.0 { + match index { + Variable(Index::Input(id)) => inputs[id].push((coeff, this_constraint)), + Variable(Index::Aux(id)) => aux[id].push((coeff, this_constraint)) + } + } + } + + // * = x_i + let i = self.num_constraints; + let y = self.alloc( + || format!("SAP reduction y_{}", i), + || Ok(E::Fr::one()) + ).expect("must allocate SAP reduction variable"); + self.num_r1cs_aux -= 1; + + let lc_a = a(LinearCombination::zero()); + let lc_b = b(LinearCombination::zero()); + let lc_c = c(LinearCombination::zero()); + + let lc_a_minus_b = lc_a.clone().sub(&lc_b); + + let mut lc_y: LinearCombination = LinearCombination::zero(); + lc_y = lc_y.add(y); + + eval(lc_a_minus_b, &mut self.at_inputs, &mut self.at_aux, self.num_constraints); + eval(lc_y, &mut self.ct_inputs, &mut self.ct_aux, self.num_constraints); + + self.num_constraints += 1; + + // * = 4* + y + let lc_a_plus_b = lc_a.add(&lc_b); + + let mut lc_c_quadrupled: LinearCombination = LinearCombination::zero(); + for s in &lc_c.0 { + let tmp = quadruple::(s.1); + lc_c_quadrupled = lc_c_quadrupled + (tmp, s.0); + } + lc_c_quadrupled = lc_c_quadrupled.add(y); + + eval(lc_a_plus_b, &mut self.at_inputs, &mut self.at_aux, self.num_constraints); + eval(lc_c_quadrupled, &mut self.ct_inputs, &mut self.ct_aux, self.num_constraints); + + self.num_constraints += 1; + + self.num_r1cs_constraints += 1; + } + + 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 + } +} + +/// Create parameters for a circuit, given some toxic waste. +pub fn generate_parameters( + circuit: C, + g1: E::G1, + g2: E::G2, + alpha: E::Fr, + beta: E::Fr, + gamma: E::Fr, + // delta: E::Fr, + tau: E::Fr +) -> Result<(), SynthesisError> +// Result, SynthesisError> + where E: Engine, C: Circuit +{ + let verbose = verbose_flag(); + + let mut assembly = KeypairAssembly { + num_inputs: 0, + num_aux: 0, + num_constraints: 0, + num_r1cs_aux: 0, + num_r1cs_constraints: 0, + at_inputs: vec![], + ct_inputs: vec![], + at_aux: vec![], + ct_aux: vec![] + }; + + // Allocate the "one" input variable + let input_0 = assembly.alloc_input(|| "", || Ok(E::Fr::one()))?; + + // Synthesize the circuit. + circuit.synthesize(&mut assembly)?; + + let num_inputs_without_identity = assembly.num_inputs - 1; + + // inputs must be constrained manually in SAP style, + // so input 0 (identity) is constrained as 1*1=1 + { + use std::ops::{Add, Sub}; + + fn eval_lc( + l: LinearCombination, + inputs: &mut [Vec<(E::Fr, usize)>], + aux: &mut [Vec<(E::Fr, usize)>], + this_constraint: usize + ) + { + for (index, coeff) in l.0 { + match index { + Variable(Index::Input(id)) => inputs[id].push((coeff, this_constraint)), + Variable(Index::Aux(id)) => aux[id].push((coeff, this_constraint)) + } + } + } + + let mut lc_input_0_a: LinearCombination = LinearCombination::zero(); + lc_input_0_a = lc_input_0_a.add(input_0.clone()); + eval_lc(lc_input_0_a, &mut assembly.at_inputs, &mut assembly.at_aux, assembly.num_constraints); + + assembly.num_constraints += 1; + } + + let num_constraints_before_inputs_constraining = assembly.num_constraints; + let num_aux_before_inputs_constraining = assembly.num_aux; + + // Other inputs are constrained as x_i * 1 = x_i where + // 1 is actually input number 0 (identity) + + for i in 1..assembly.num_inputs { + assembly.enforce(|| "", + |lc| lc + Variable(Index::Input(i)), + |lc| lc + Variable(Index::Input(0)), + |lc| lc + Variable(Index::Input(i)), + ); + } + + // check that each input generates 2 constraints + assert_eq!(num_inputs_without_identity * 2 + + num_constraints_before_inputs_constraining, + assembly.num_constraints, + "each input must produce two extra constraints"); + // and that it creates one extra variable + assert_eq!(num_inputs_without_identity + + num_aux_before_inputs_constraining, + assembly.num_aux, + "each input must generate an extra variable"); + + assert_eq!(assembly.num_inputs + assembly.num_r1cs_constraints + assembly.num_r1cs_aux, + assembly.num_inputs + assembly.num_aux, + "each constraint in principle adds one variable"); + + if verbose {eprintln!("Constraint system size is {}", assembly.num_constraints)}; + // Create bases for blind evaluation of polynomials at tau + let powers_of_tau = vec![Scalar::(E::Fr::zero()); assembly.num_constraints]; + let mut domain = EvaluationDomain::from_coeffs(powers_of_tau)?; + + // Compute G1 window table + let mut g1_wnaf = Wnaf::new(); + let g1_wnaf = g1_wnaf.base(g1, { + 2*(assembly.num_inputs + assembly.num_r1cs_constraints + assembly.num_r1cs_aux) + + assembly.num_r1cs_constraints + assembly.num_r1cs_aux + + 2*(assembly.num_inputs + assembly.num_r1cs_constraints) + }); + + // Compute gamma*G2 window table + let mut g2_wnaf = Wnaf::new(); + // let gamma_g2 = g2.into_affine().mul(gamma.into_repr()); + let g2_wnaf = g2_wnaf.base(g2, { + // B query + assembly.num_inputs + assembly.num_aux + // alternatively expressed as + // assembly.num_inputs + assembly.num_r1cs_constraints + assembly.num_r1cs_aux + }); + + let worker = Worker::new(); + + // let z_at_tau = { + // // Compute powers of tau + // if verbose {eprintln!("computing powers of tau...")}; + + // let start = std::time::Instant::now(); + + // { + // let domain = domain.as_mut(); + // worker.scope(domain.len(), |scope, chunk| { + // for (i, subdomain) in domain.chunks_mut(chunk).enumerate() + // { + // scope.spawn(move || { + // let mut current_power = tau.pow(&[(i*chunk) as u64]); + + // for p in subdomain { + // p.0 = current_power; + // current_power.mul_assign(&tau); + // } + // }); + // } + // }); + // } + // if verbose {eprintln!("powers of tau stage 1 done in {} s", start.elapsed().as_millis() as f64 / 1000.0);}; + + // // z_at_tau = t(x) + // let z_at_tau = domain.z(&tau); + + // z_at_tau + // }; + + let domain_length = domain.as_ref().len(); + + if verbose {eprintln!("Domain length is {} ", domain_length)}; + + // G1^{gamma^2 * Z(t) * t^i} for 0 <= i < 2^m - 1 for 2^m domains + let mut gamma2_z_t_g1 = vec![E::G1::zero(); domain.as_ref().len() - 1]; + let mut z_at_tau = E::Fr::zero(); + + { + // Compute powers of tau + if verbose {eprintln!("computing powers of tau...")}; + + let start = std::time::Instant::now(); + + { + let domain = domain.as_mut(); + worker.scope(domain.len(), |scope, chunk| { + for (i, subdomain) in domain.chunks_mut(chunk).enumerate() + { + scope.spawn(move || { + let mut current_power = tau.pow(&[(i*chunk) as u64]); + + for p in subdomain { + p.0 = current_power; + current_power.mul_assign(&tau); + } + }); + } + }); + } + if verbose {eprintln!("powers of tau stage 1 done in {} s", start.elapsed().as_millis() as f64 / 1000.0);}; + + // z_at_tau = t(x) + z_at_tau = domain.z(&tau); + + let mut gamma2_z_t = z_at_tau; + gamma2_z_t.mul_assign(&gamma); + gamma2_z_t.mul_assign(&gamma); + + if verbose {eprintln!("computing the `G1^(gamma^2 * Z(t) * t^i)` query with multiple threads...")}; + + let start = std::time::Instant::now(); + + // Compute the H query with multiple threads + worker.scope(gamma2_z_t_g1.len(), |scope, chunk| { + for (gamma2_z_t_g1, p) in gamma2_z_t_g1.chunks_mut(chunk).zip(domain.as_ref().chunks(chunk)) + { + let mut g1_wnaf = g1_wnaf.shared(); + scope.spawn(move || { + // Set values of the H query to g1^{(tau^i * t(tau)) / delta} + for (gamma2_z_t_g1, p) in gamma2_z_t_g1.iter_mut().zip(p.iter()) + { + // Compute final exponent + let mut exp = p.0; + exp.mul_assign(&gamma2_z_t); + + // Exponentiate + *gamma2_z_t_g1 = g1_wnaf.scalar(exp.into_repr()); + } + + // Batch normalize + E::G1::batch_normalization(gamma2_z_t_g1); + }); + } + }); + if verbose {eprintln!("computing the `G1^(gamma^2 * Z(t) * t^i)` query done in {} s", start.elapsed().as_millis() as f64 / 1000.0);}; + } + + // G1^{gamma * A_i(t)} for 0 <= i <= num_variables + let mut a_g1 = vec![E::G1::zero(); assembly.num_inputs + assembly.num_aux]; + // G2^{gamma * A_i(t)} for 0 <= i <= num_variables + let mut a_g2 = vec![E::G2::zero(); assembly.num_inputs + assembly.num_aux]; + + // G1^{gamma^2 * C_i(t) + (alpha + beta) * gamma * A_i(t)} + // for num_inputs + 1 < i <= num_variables + let mut c_1_g1 = vec![E::G1::zero(); assembly.num_inputs + assembly.num_aux]; + // G1^{2 * gamma^2 * Z(t) * A_i(t)} for 0 <= i <= num_variables + let mut c_2_g1 = vec![E::G1::zero(); assembly.num_inputs + assembly.num_aux]; + + // G1^{gamma * Z(t)} + let mut gamma_zt = gamma; + gamma_zt.mul_assign(&z_at_tau); + + let gamma_z = g1.into_affine().mul(gamma.into_repr()); + // G2^{gamma * Z(t)} + let gamma_z_g2 = g2.into_affine().mul(gamma.into_repr()); + + let mut ab_gamma = alpha; + ab_gamma.add_assign(&beta); + ab_gamma.mul_assign(&gamma); + // G1^{(alpha + beta) * gamma * Z(t)} + let ab_gamma_z_g1 = g1.into_affine().mul(ab_gamma.into_repr()); + + let mut gamma2_z2 = gamma; + gamma2_z2.mul_assign(&z_at_tau); + gamma2_z2.square(); + // G1^{gamma^2 * Z(t)^2} + let gamma2_z2_g1 = g1.into_affine().mul(gamma2_z2.into_repr()); + + // G^{gamma^2 * Z(t) * t^i} for 0 <= i < 2^m - 1 for 2^m domains + let mut gamma2_z_t = vec![E::G1::zero(); domain.as_ref().len() - 1]; + + if verbose {eprintln!("using inverse FFT to convert to intepolation coefficients...")}; + + let start = std::time::Instant::now(); + + // Use inverse FFT to convert to intepolation coefficients + domain.ifft(&worker); + let powers_of_tau = domain.into_coeffs(); + // domain is now a set of scalars + + if verbose {eprintln!("powers of tau evaluation in radix2 domain in {} s", start.elapsed().as_millis() as f64 / 1000.0)}; + + if verbose {eprintln!("evaluating polynomials...")}; + let start = std::time::Instant::now(); + + // overall strategy: + // a_g1, a_g2, c_1_g1, c_2_g1 should be combined together by computing + // ab = (alpha + beta) + // g_2 = gamma^2 + // t0 = gamma*A_i(t) + // t1 = g_2*C_t(t) + // a_g1 = t0*G1 + // a_g2 = t0*G2 + // c_1_g1 = (t1 + ab*t0)*G1 + // c_2_g1 = (2*gamma*z_at_tau*t0)*G1 + + fn eval_stage_1( + // wNAF window tables + g1_wnaf: &Wnaf>, + g2_wnaf: &Wnaf>, + + // powers of tau coefficients + powers_of_tau: &[Scalar], + + // SAP polynomials + at: &[Vec<(E::Fr, usize)>], + ct: &[Vec<(E::Fr, usize)>], + + // Resulting evaluated SAP polynomials + a_g1: &mut [E::G1], + a_g2: &mut [E::G2], + c_1_g1: &mut [E::G1], + c_2_g1: &mut [E::G1], + + // Trapdoors + alpha: &E::Fr, + beta: &E::Fr, + gamma: &E::Fr, + z_at_tau: &E::Fr, + + // Worker + worker: &Worker + ) + + { + // Sanity check + assert_eq!(a_g1.len(), at.len()); + assert_eq!(a_g1.len(), ct.len()); + assert_eq!(a_g1.len(), a_g2.len()); + assert_eq!(a_g1.len(), c_1_g1.len()); + assert_eq!(a_g1.len(), c_2_g1.len()); + + // compute once + let mut ab = *alpha; + ab.add_assign(&beta); + + let mut gamma2 = *gamma; + gamma2.square(); + + // Evaluate polynomials in multiple threads + worker.scope(a_g1.len(), |scope, chunk| { + for (((((a_g1, a_g2), c_1_g1), c_2_g1), at), ct) in a_g1.chunks_mut(chunk) + .zip(a_g2.chunks_mut(chunk)) + .zip(c_1_g1.chunks_mut(chunk)) + .zip(c_2_g1.chunks_mut(chunk)) + .zip(at.chunks(chunk)) + .zip(ct.chunks(chunk)) + { + let mut g1_wnaf = g1_wnaf.shared(); + let mut g2_wnaf = g2_wnaf.shared(); + + scope.spawn(move || { + for (((((a_g1, a_g2), c_1_g1), c_2_g1), at), ct) in a_g1.iter_mut() + .zip(a_g2.iter_mut()) + .zip(c_1_g1.iter_mut()) + .zip(c_2_g1.iter_mut()) + .zip(at.iter()) + .zip(ct.iter()) + { + fn eval_at_tau( + powers_of_tau: &[Scalar], + p: &[(E::Fr, usize)] + ) -> E::Fr + { + let mut acc = E::Fr::zero(); + + for &(ref coeff, index) in p { + let mut n = powers_of_tau[index].0; + n.mul_assign(coeff); + acc.add_assign(&n); + } + + acc + } + + // Evaluate SAP polynomials at tau + // t0 = gamma*A_i(t) + let mut t0 = eval_at_tau(powers_of_tau, at); + t0.mul_assign(&gamma); + // t1 = gamma^2*C_t(t) + let mut t1 = eval_at_tau(powers_of_tau, ct); + t1.mul_assign(&gamma2); + + // a_g1 = t0*G1 + // a_g2 = t0*G2 + // c_1_g1 = (t1 + ab*t0)*G1 + // c_2_g1 = (2*gamma*z_at_tau*t0)*G1 + + // Compute a_g1 and a_g2 + if !t0.is_zero() { + *a_g1 = g1_wnaf.scalar(t0.into_repr()); + *a_g2 = g2_wnaf.scalar(t0.into_repr()); + } + + let mut c_1_g1_factor = t0; + c_1_g1_factor.mul_assign(&ab); + c_1_g1_factor.add_assign(&t1); + + // (2*gamma*z_at_tau*t0) inplace + t0.mul_assign(&z_at_tau); + t0.mul_assign(&gamma); + t0.double(); + + *c_1_g1 = g1_wnaf.scalar(c_1_g1_factor.into_repr()); + *c_2_g1 = g1_wnaf.scalar(t0.into_repr()); + } + + // Batch normalize + E::G1::batch_normalization(a_g1); + E::G2::batch_normalization(a_g2); + E::G1::batch_normalization(c_1_g1); + E::G1::batch_normalization(c_2_g1); + }); + }; + }); + } + + // Evaluate for inputs. + eval_stage_1( + &g1_wnaf, + &g2_wnaf, + &powers_of_tau, + &assembly.at_inputs, + &assembly.ct_inputs, + &mut a_g1[0..assembly.num_inputs], + &mut a_g2[0..assembly.num_inputs], + &mut c_1_g1[0..assembly.num_inputs], + &mut c_2_g1[0..assembly.num_inputs], + &alpha, + &beta, + &gamma, + &z_at_tau, + &worker + ); + + // Evaluate for inputs. + eval_stage_1( + &g1_wnaf, + &g2_wnaf, + &powers_of_tau, + &assembly.at_aux, + &assembly.ct_aux, + &mut a_g1[assembly.num_inputs..], + &mut a_g2[assembly.num_inputs..], + &mut c_1_g1[assembly.num_inputs..], + &mut c_2_g1[assembly.num_inputs..], + &alpha, + &beta, + &gamma, + &z_at_tau, + &worker + ); + + // for _ in 0..assembly.num_inputs { + // c_1_g1.remove(0); + // } + + if verbose {eprintln!("evaluating polynomials done in {} s", start.elapsed().as_millis() as f64 / 1000.0);}; + + // // Don't allow any elements be unconstrained, so that + // // the L query is always fully dense. + // for e in l.iter() { + // if e.is_zero() { + // return Err(SynthesisError::UnconstrainedVariable); + // } + // } + + // let g1 = g1.into_affine(); + // let g2 = g2.into_affine(); + + // let vk = VerifyingKey:: { + // alpha_g1: g1.mul(alpha).into_affine(), + // beta_g1: g1.mul(beta).into_affine(), + // beta_g2: g2.mul(beta).into_affine(), + // gamma_g2: g2.mul(gamma).into_affine(), + // delta_g1: g1.mul(delta).into_affine(), + // delta_g2: g2.mul(delta).into_affine(), + // ic: ic.into_iter().map(|e| e.into_affine()).collect() + // }; + + println!("Has generated {} points", a_g1.len()); + + Ok(()) + + // Ok(Parameters { + // vk: vk, + // h: Arc::new(h.into_iter().map(|e| e.into_affine()).collect()), + // l: Arc::new(l.into_iter().map(|e| e.into_affine()).collect()), + + // // Filter points at infinity away from A/B queries + // a: Arc::new(a.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()), + // b_g1: Arc::new(b_g1.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()), + // b_g2: Arc::new(b_g2.into_iter().filter(|e| !e.is_zero()).map(|e| e.into_affine()).collect()) + // }) +} diff --git a/src/gm17/mod.rs b/src/gm17/mod.rs new file mode 100644 index 0000000..b8c5b7f --- /dev/null +++ b/src/gm17/mod.rs @@ -0,0 +1,563 @@ +use pairing::{ + Engine, + CurveAffine, + EncodedPoint +}; + +use ::{ + SynthesisError +}; + +use multiexp::SourceBuilder; +use std::io::{self, Read, Write}; +use std::sync::Arc; +use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; + +#[cfg(test)] +mod tests; + +mod generator; +// mod prover; +// mod verifier; + +pub use self::generator::*; +// pub use self::prover::*; +// pub use self::verifier::*; + +#[derive(Debug, Clone)] +pub struct Proof { + pub a: E::G1Affine, + pub b: E::G2Affine, + pub c: E::G1Affine +} + +impl PartialEq for Proof { + fn eq(&self, other: &Self) -> bool { + self.a == other.a && + self.b == other.b && + self.c == other.c + } +} + +impl Proof { + pub fn write( + &self, + mut writer: W + ) -> io::Result<()> + { + writer.write_all(self.a.into_compressed().as_ref())?; + writer.write_all(self.b.into_compressed().as_ref())?; + writer.write_all(self.c.into_compressed().as_ref())?; + + Ok(()) + } + + pub fn read( + mut reader: R + ) -> io::Result + { + let mut g1_repr = ::Compressed::empty(); + let mut g2_repr = ::Compressed::empty(); + + reader.read_exact(g1_repr.as_mut())?; + let a = g1_repr + .into_affine() + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + .and_then(|e| if e.is_zero() { + Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) + } else { + Ok(e) + })?; + + reader.read_exact(g2_repr.as_mut())?; + let b = g2_repr + .into_affine() + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + .and_then(|e| if e.is_zero() { + Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) + } else { + Ok(e) + })?; + + reader.read_exact(g1_repr.as_mut())?; + let c = g1_repr + .into_affine() + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + .and_then(|e| if e.is_zero() { + Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) + } else { + Ok(e) + })?; + + Ok(Proof { + a: a, + b: b, + c: c + }) + } +} + +#[derive(Clone)] +pub struct VerifyingKey { + pub h_g2: E::G2Affine, + + // alpha in g1 for verifying and for creating A/C elements of + // proof. Never the point at infinity. + pub alpha_g1: E::G1Affine, + + // beta in g2 for verifying. Never the point at infinity. + pub beta_g2: E::G2Affine, + + // gamma in g1 for verifying. Never the point at infinity. + pub gamma_g1: E::G1Affine, + + // gamma in g2 for verifying. Never the point at infinity. + pub gamma_g2: E::G2Affine, + + // Elements of the form G^{gamma * A_i(t) + (alpha + beta) * A_i(t)} + // for all public inputs. Because all public inputs have a dummy constraint, + // this is the same size as the number of inputs, and never contains points + // at infinity. + pub ic: Vec +} + +impl PartialEq for VerifyingKey { + fn eq(&self, other: &Self) -> bool { + self.h_g2 == other.h_g2 && + self.alpha_g1 == other.alpha_g1 && + self.beta_g2 == other.beta_g2 && + self.gamma_g1 == other.gamma_g1 && + self.gamma_g2 == other.gamma_g2 && + self.ic == other.ic + } +} + +impl VerifyingKey { + pub fn write( + &self, + mut writer: W + ) -> io::Result<()> + { + writer.write_all(self.h_g2.into_uncompressed().as_ref())?; + writer.write_all(self.alpha_g1.into_uncompressed().as_ref())?; + writer.write_all(self.beta_g2.into_uncompressed().as_ref())?; + writer.write_all(self.gamma_g1.into_uncompressed().as_ref())?; + writer.write_all(self.gamma_g2.into_uncompressed().as_ref())?; + writer.write_u32::(self.ic.len() as u32)?; + for ic in &self.ic { + writer.write_all(ic.into_uncompressed().as_ref())?; + } + + Ok(()) + } + + pub fn read( + mut reader: R + ) -> io::Result + { + let mut g1_repr = ::Uncompressed::empty(); + let mut g2_repr = ::Uncompressed::empty(); + + reader.read_exact(g2_repr.as_mut())?; + let h_h2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + reader.read_exact(g1_repr.as_mut())?; + let alpha_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + reader.read_exact(g2_repr.as_mut())?; + let beta_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + reader.read_exact(g1_repr.as_mut())?; + let gamma_g1 = g1_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + reader.read_exact(g2_repr.as_mut())?; + let gamma_g2 = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + let ic_len = reader.read_u32::()? as usize; + + let mut ic = vec![]; + + for _ in 0..ic_len { + reader.read_exact(g1_repr.as_mut())?; + let g1 = g1_repr + .into_affine() + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + .and_then(|e| if e.is_zero() { + Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) + } else { + Ok(e) + })?; + + ic.push(g1); + } + + Ok(VerifyingKey { + h_g2: h_h2, + alpha_g1: alpha_g1, + beta_g2: beta_g2, + gamma_g1: gamma_g1, + gamma_g2: gamma_g2, + ic: ic + }) + } +} + +#[derive(Clone)] +pub struct Parameters { + pub vk: VerifyingKey, + pub a_g1: Arc>, + pub a_g2: Arc>, + + pub c_1_g1: Arc>, + pub c_2_g1: Arc>, + + pub gamma_z: E::G1Affine, + pub gamma_z_g2: E::G2Affine, + + pub ab_gamma_z_g1: E::G1Affine, + pub gamma2_z2_g1: E::G1Affine, + + pub gamma2_z_t: Arc>, +} + +impl PartialEq for Parameters { + fn eq(&self, other: &Self) -> bool { + self.vk == other.vk && + self.a_g1 == other.a_g1 && + self.a_g2 == other.a_g2 && + self.c_1_g1 == other.c_1_g1 && + self.c_2_g1 == other.c_2_g1 && + self.gamma_z == other.gamma_z && + self.gamma_z_g2 == other.gamma_z_g2 && + self.ab_gamma_z_g1 == other.ab_gamma_z_g1 && + self.gamma2_z2_g1 == other.gamma2_z2_g1 && + self.gamma2_z_t == other.gamma2_z_t + } +} + +// impl Parameters { +// pub fn write( +// &self, +// mut writer: W +// ) -> io::Result<()> +// { +// self.vk.write(&mut writer)?; + +// writer.write_u32::(self.h.len() as u32)?; +// for g in &self.h[..] { +// writer.write_all(g.into_uncompressed().as_ref())?; +// } + +// writer.write_u32::(self.l.len() as u32)?; +// for g in &self.l[..] { +// writer.write_all(g.into_uncompressed().as_ref())?; +// } + +// writer.write_u32::(self.a.len() as u32)?; +// for g in &self.a[..] { +// writer.write_all(g.into_uncompressed().as_ref())?; +// } + +// writer.write_u32::(self.b_g1.len() as u32)?; +// for g in &self.b_g1[..] { +// writer.write_all(g.into_uncompressed().as_ref())?; +// } + +// writer.write_u32::(self.b_g2.len() as u32)?; +// for g in &self.b_g2[..] { +// writer.write_all(g.into_uncompressed().as_ref())?; +// } + +// Ok(()) +// } + +// pub fn read( +// mut reader: R, +// checked: bool +// ) -> io::Result +// { +// let read_g1 = |reader: &mut R| -> io::Result { +// let mut repr = ::Uncompressed::empty(); +// reader.read_exact(repr.as_mut())?; + +// if checked { +// repr +// .into_affine() +// } else { +// repr +// .into_affine_unchecked() +// } +// .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) +// .and_then(|e| if e.is_zero() { +// Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) +// } else { +// Ok(e) +// }) +// }; + +// let read_g2 = |reader: &mut R| -> io::Result { +// let mut repr = ::Uncompressed::empty(); +// reader.read_exact(repr.as_mut())?; + +// if checked { +// repr +// .into_affine() +// } else { +// repr +// .into_affine_unchecked() +// } +// .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) +// .and_then(|e| if e.is_zero() { +// Err(io::Error::new(io::ErrorKind::InvalidData, "point at infinity")) +// } else { +// Ok(e) +// }) +// }; + +// let vk = VerifyingKey::::read(&mut reader)?; + +// let mut h = vec![]; +// let mut l = vec![]; +// let mut a = vec![]; +// let mut b_g1 = vec![]; +// let mut b_g2 = vec![]; + +// { +// let len = reader.read_u32::()? as usize; +// for _ in 0..len { +// h.push(read_g1(&mut reader)?); +// } +// } + +// { +// let len = reader.read_u32::()? as usize; +// for _ in 0..len { +// l.push(read_g1(&mut reader)?); +// } +// } + +// { +// let len = reader.read_u32::()? as usize; +// for _ in 0..len { +// a.push(read_g1(&mut reader)?); +// } +// } + +// { +// let len = reader.read_u32::()? as usize; +// for _ in 0..len { +// b_g1.push(read_g1(&mut reader)?); +// } +// } + +// { +// let len = reader.read_u32::()? as usize; +// for _ in 0..len { +// b_g2.push(read_g2(&mut reader)?); +// } +// } + +// Ok(Parameters { +// vk: vk, +// h: Arc::new(h), +// l: Arc::new(l), +// a: Arc::new(a), +// b_g1: Arc::new(b_g1), +// b_g2: Arc::new(b_g2) +// }) +// } +// } + +// pub struct PreparedVerifyingKey { +// /// Pairing result of alpha*beta +// alpha_g1_beta_g2: E::Fqk, +// /// -gamma in G2 +// neg_gamma_g2: ::Prepared, +// /// -delta in G2 +// neg_delta_g2: ::Prepared, +// /// Copy of IC from `VerifiyingKey`. +// ic: Vec +// } + +// pub trait ParameterSource { +// type G1Builder: SourceBuilder; +// type G2Builder: SourceBuilder; + +// fn get_vk( +// &mut self, +// num_ic: usize +// ) -> Result, SynthesisError>; +// fn get_h( +// &mut self, +// num_h: usize +// ) -> Result; +// fn get_l( +// &mut self, +// num_l: usize +// ) -> Result; +// fn get_a( +// &mut self, +// num_inputs: usize, +// num_aux: usize +// ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>; +// fn get_b_g1( +// &mut self, +// num_inputs: usize, +// num_aux: usize +// ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError>; +// fn get_b_g2( +// &mut self, +// num_inputs: usize, +// num_aux: usize +// ) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError>; +// } + +// impl<'a, E: Engine> ParameterSource for &'a Parameters { +// type G1Builder = (Arc>, usize); +// type G2Builder = (Arc>, usize); + +// fn get_vk( +// &mut self, +// _: usize +// ) -> Result, SynthesisError> +// { +// Ok(self.vk.clone()) +// } + +// fn get_h( +// &mut self, +// _: usize +// ) -> Result +// { +// Ok((self.h.clone(), 0)) +// } + +// fn get_l( +// &mut self, +// _: usize +// ) -> Result +// { +// Ok((self.l.clone(), 0)) +// } + +// fn get_a( +// &mut self, +// num_inputs: usize, +// _: usize +// ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> +// { +// Ok(((self.a.clone(), 0), (self.a.clone(), num_inputs))) +// } + +// fn get_b_g1( +// &mut self, +// num_inputs: usize, +// _: usize +// ) -> Result<(Self::G1Builder, Self::G1Builder), SynthesisError> +// { +// Ok(((self.b_g1.clone(), 0), (self.b_g1.clone(), num_inputs))) +// } + +// fn get_b_g2( +// &mut self, +// num_inputs: usize, +// _: usize +// ) -> Result<(Self::G2Builder, Self::G2Builder), SynthesisError> +// { +// Ok(((self.b_g2.clone(), 0), (self.b_g2.clone(), num_inputs))) +// } +// } + +// #[cfg(test)] +// mod test_with_bls12_381 { +// use super::*; +// use {Circuit, SynthesisError, ConstraintSystem}; + +// use rand::{Rand, thread_rng}; +// use ff::{Field}; +// use pairing::bls12_381::{Bls12, Fr}; + +// #[test] +// fn serialization() { +// struct MySillyCircuit { +// a: Option, +// b: Option +// } + +// impl Circuit for MySillyCircuit { +// fn synthesize>( +// self, +// cs: &mut CS +// ) -> Result<(), SynthesisError> +// { +// let a = cs.alloc(|| "a", || self.a.ok_or(SynthesisError::AssignmentMissing))?; +// let b = cs.alloc(|| "b", || self.b.ok_or(SynthesisError::AssignmentMissing))?; +// let c = cs.alloc_input(|| "c", || { +// let mut a = self.a.ok_or(SynthesisError::AssignmentMissing)?; +// let b = self.b.ok_or(SynthesisError::AssignmentMissing)?; + +// a.mul_assign(&b); +// Ok(a) +// })?; + +// cs.enforce( +// || "a*b=c", +// |lc| lc + a, +// |lc| lc + b, +// |lc| lc + c +// ); + +// Ok(()) +// } +// } + +// let rng = &mut thread_rng(); + +// let params = generate_random_parameters::( +// MySillyCircuit { a: None, b: None }, +// rng +// ).unwrap(); + +// { +// let mut v = vec![]; + +// params.write(&mut v).unwrap(); +// assert_eq!(v.len(), 2136); + +// let de_params = Parameters::read(&v[..], true).unwrap(); +// assert!(params == de_params); + +// let de_params = Parameters::read(&v[..], false).unwrap(); +// assert!(params == de_params); +// } + +// let pvk = prepare_verifying_key::(¶ms.vk); + +// for _ in 0..100 { +// let a = Fr::rand(rng); +// let b = Fr::rand(rng); +// let mut c = a; +// c.mul_assign(&b); + +// let proof = create_random_proof( +// MySillyCircuit { +// a: Some(a), +// b: Some(b) +// }, +// ¶ms, +// rng +// ).unwrap(); + +// let mut v = vec![]; +// proof.write(&mut v).unwrap(); + +// assert_eq!(v.len(), 192); + +// let de_proof = Proof::read(&v[..]).unwrap(); +// assert!(proof == de_proof); + +// assert!(verify_proof(&pvk, &proof, &[c]).unwrap()); +// assert!(!verify_proof(&pvk, &proof, &[a]).unwrap()); +// } +// } +// } \ No newline at end of file diff --git a/src/gm17/tests/mod.rs b/src/gm17/tests/mod.rs new file mode 100644 index 0000000..4440b19 --- /dev/null +++ b/src/gm17/tests/mod.rs @@ -0,0 +1,329 @@ +use pairing::{ + Engine +}; + +use ff:: { + Field, + PrimeField, +}; + +use super::super::tests::dummy_engine::*; +use super::super::tests::XORDemo; + +use std::marker::PhantomData; + +use ::{ + Circuit, + ConstraintSystem, + SynthesisError +}; + +use super::{ + generate_parameters, + // prepare_verifying_key, + // create_proof, + // verify_proof +}; + +#[test] +fn test_gm17_xordemo() { + let g1 = Fr::one(); + let g2 = Fr::one(); + let alpha = Fr::from_str("48577").unwrap(); + let beta = Fr::from_str("22580").unwrap(); + let gamma = Fr::from_str("53332").unwrap(); + // let delta = Fr::from_str("5481").unwrap(); + let tau = Fr::from_str("3673").unwrap(); + + let params = { + let c = XORDemo:: { + a: None, + b: None, + _marker: PhantomData + }; + + generate_parameters( + c, + g1, + g2, + alpha, + beta, + gamma, + tau + ).unwrap() + }; + + // // This will synthesize the constraint system: + // // + // // public inputs: a_0 = 1, a_1 = c + // // aux inputs: a_2 = a, a_3 = b + // // constraints: + // // (a_0 - a_2) * (a_2) = 0 + // // (a_0 - a_3) * (a_3) = 0 + // // (a_2 + a_2) * (a_3) = (a_2 + a_3 - a_1) + // // (a_0) * 0 = 0 + // // (a_1) * 0 = 0 + + // // The evaluation domain is 8. The H query should + // // have 7 elements (it's a quotient polynomial) + // assert_eq!(7, params.h.len()); + + // let mut root_of_unity = Fr::root_of_unity(); + + // // We expect this to be a 2^10 root of unity + // assert_eq!(Fr::one(), root_of_unity.pow(&[1 << 10])); + + // // Let's turn it into a 2^3 root of unity. + // root_of_unity = root_of_unity.pow(&[1 << 7]); + // assert_eq!(Fr::one(), root_of_unity.pow(&[1 << 3])); + // assert_eq!(Fr::from_str("20201").unwrap(), root_of_unity); + + // // Let's compute all the points in our evaluation domain. + // let mut points = Vec::with_capacity(8); + // for i in 0..8 { + // points.push(root_of_unity.pow(&[i])); + // } + + // // Let's compute t(tau) = (tau - p_0)(tau - p_1)... + // // = tau^8 - 1 + // let mut t_at_tau = tau.pow(&[8]); + // t_at_tau.sub_assign(&Fr::one()); + // { + // let mut tmp = Fr::one(); + // for p in &points { + // let mut term = tau; + // term.sub_assign(p); + // tmp.mul_assign(&term); + // } + // assert_eq!(tmp, t_at_tau); + // } + + // // We expect our H query to be 7 elements of the form... + // // {tau^i t(tau) / delta} + // let delta_inverse = delta.inverse().unwrap(); + // let gamma_inverse = gamma.inverse().unwrap(); + // { + // let mut coeff = delta_inverse; + // coeff.mul_assign(&t_at_tau); + + // let mut cur = Fr::one(); + // for h in params.h.iter() { + // let mut tmp = cur; + // tmp.mul_assign(&coeff); + + // assert_eq!(*h, tmp); + + // cur.mul_assign(&tau); + // } + // } + + // // The density of the IC query is 2 (2 inputs) + // assert_eq!(2, params.vk.ic.len()); + + // // The density of the L query is 2 (2 aux variables) + // assert_eq!(2, params.l.len()); + + // // The density of the A query is 4 (each variable is in at least one A term) + // assert_eq!(4, params.a.len()); + + // // The density of the B query is 2 (two variables are in at least one B term) + // assert_eq!(2, params.b_g1.len()); + // assert_eq!(2, params.b_g2.len()); + + // /* + // Lagrange interpolation polynomials in our evaluation domain: + + // ,-------------------------------. ,-------------------------------. ,-------------------------------. + // | A TERM | | B TERM | | C TERM | + // `-------------------------------. `-------------------------------' `-------------------------------' + // | a_0 | a_1 | a_2 | a_3 | | a_0 | a_1 | a_2 | a_3 | | a_0 | a_1 | a_2 | a_3 | + // | 1 | 0 | 64512 | 0 | | 0 | 0 | 1 | 0 | | 0 | 0 | 0 | 0 | + // | 1 | 0 | 0 | 64512 | | 0 | 0 | 0 | 1 | | 0 | 0 | 0 | 0 | + // | 0 | 0 | 2 | 0 | | 0 | 0 | 0 | 1 | | 0 | 64512 | 1 | 1 | + // | 1 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 | + // | 0 | 1 | 0 | 0 | | 0 | 0 | 0 | 0 | | 0 | 0 | 0 | 0 | + // `-------'-------'-------'-------' `-------'-------'-------'-------' `-------'-------'-------'-------' + + // Example for u_0: + + // sage: r = 64513 + // sage: Fr = GF(r) + // sage: omega = (Fr(5)^63)^(2^7) + // sage: tau = Fr(3673) + // sage: R. = PolynomialRing(Fr, 'x') + // sage: def eval(tau, c0, c1, c2, c3, c4): + // ....: p = R.lagrange_polynomial([(omega^0, c0), (omega^1, c1), (omega^2, c2), (omega^3, c3), (omega^4, c4), (omega^5, 0), (omega^6, 0), (omega^7, 0)]) + // ....: return p.substitute(tau) + // sage: eval(tau, 1, 1, 0, 1, 0) + // 59158 + // */ + + // let u_i = [59158, 48317, 21767, 10402].iter().map(|e| { + // Fr::from_str(&format!("{}", e)).unwrap() + // }).collect::>(); + // let v_i = [0, 0, 60619, 30791].iter().map(|e| { + // Fr::from_str(&format!("{}", e)).unwrap() + // }).collect::>(); + // let w_i = [0, 23320, 41193, 41193].iter().map(|e| { + // Fr::from_str(&format!("{}", e)).unwrap() + // }).collect::>(); + + // for (u, a) in u_i.iter() + // .zip(¶ms.a[..]) + // { + // assert_eq!(u, a); + // } + + // for (v, b) in v_i.iter() + // .filter(|&&e| e != Fr::zero()) + // .zip(¶ms.b_g1[..]) + // { + // assert_eq!(v, b); + // } + + // for (v, b) in v_i.iter() + // .filter(|&&e| e != Fr::zero()) + // .zip(¶ms.b_g2[..]) + // { + // assert_eq!(v, b); + // } + + // for i in 0..4 { + // let mut tmp1 = beta; + // tmp1.mul_assign(&u_i[i]); + + // let mut tmp2 = alpha; + // tmp2.mul_assign(&v_i[i]); + + // tmp1.add_assign(&tmp2); + // tmp1.add_assign(&w_i[i]); + + // if i < 2 { + // // Check the correctness of the IC query elements + // tmp1.mul_assign(&gamma_inverse); + + // assert_eq!(tmp1, params.vk.ic[i]); + // } else { + // // Check the correctness of the L query elements + // tmp1.mul_assign(&delta_inverse); + + // assert_eq!(tmp1, params.l[i - 2]); + // } + // } + + // // Check consistency of the other elements + // assert_eq!(alpha, params.vk.alpha_g1); + // assert_eq!(beta, params.vk.beta_g1); + // assert_eq!(beta, params.vk.beta_g2); + // assert_eq!(gamma, params.vk.gamma_g2); + // assert_eq!(delta, params.vk.delta_g1); + // assert_eq!(delta, params.vk.delta_g2); + + // let pvk = prepare_verifying_key(¶ms.vk); + + // let r = Fr::from_str("27134").unwrap(); + // let s = Fr::from_str("17146").unwrap(); + + // let proof = { + // let c = XORDemo { + // a: Some(true), + // b: Some(false), + // _marker: PhantomData + // }; + + // create_proof( + // c, + // ¶ms, + // r, + // s + // ).unwrap() + // }; + + // // A(x) = + // // a_0 * (44865*x^7 + 56449*x^6 + 44865*x^5 + 8064*x^4 + 3520*x^3 + 56449*x^2 + 3520*x + 40321) + + // // a_1 * (8064*x^7 + 56449*x^6 + 8064*x^5 + 56449*x^4 + 8064*x^3 + 56449*x^2 + 8064*x + 56449) + + // // a_2 * (16983*x^7 + 24192*x^6 + 63658*x^5 + 56449*x^4 + 16983*x^3 + 24192*x^2 + 63658*x + 56449) + + // // a_3 * (5539*x^7 + 27797*x^6 + 6045*x^5 + 56449*x^4 + 58974*x^3 + 36716*x^2 + 58468*x + 8064) + + // { + // // proof A = alpha + A(tau) + delta * r + // let mut expected_a = delta; + // expected_a.mul_assign(&r); + // expected_a.add_assign(&alpha); + // expected_a.add_assign(&u_i[0]); // a_0 = 1 + // expected_a.add_assign(&u_i[1]); // a_1 = 1 + // expected_a.add_assign(&u_i[2]); // a_2 = 1 + // // a_3 = 0 + // assert_eq!(proof.a, expected_a); + // } + + // // B(x) = + // // a_0 * (0) + + // // a_1 * (0) + + // // a_2 * (56449*x^7 + 56449*x^6 + 56449*x^5 + 56449*x^4 + 56449*x^3 + 56449*x^2 + 56449*x + 56449) + + // // a_3 * (31177*x^7 + 44780*x^6 + 21752*x^5 + 42255*x^3 + 35861*x^2 + 33842*x + 48385) + // { + // // proof B = beta + B(tau) + delta * s + // let mut expected_b = delta; + // expected_b.mul_assign(&s); + // expected_b.add_assign(&beta); + // expected_b.add_assign(&v_i[0]); // a_0 = 1 + // expected_b.add_assign(&v_i[1]); // a_1 = 1 + // expected_b.add_assign(&v_i[2]); // a_2 = 1 + // // a_3 = 0 + // assert_eq!(proof.b, expected_b); + // } + + // // C(x) = + // // a_0 * (0) + + // // a_1 * (27797*x^7 + 56449*x^6 + 36716*x^5 + 8064*x^4 + 27797*x^3 + 56449*x^2 + 36716*x + 8064) + + // // a_2 * (36716*x^7 + 8064*x^6 + 27797*x^5 + 56449*x^4 + 36716*x^3 + 8064*x^2 + 27797*x + 56449) + + // // a_3 * (36716*x^7 + 8064*x^6 + 27797*x^5 + 56449*x^4 + 36716*x^3 + 8064*x^2 + 27797*x + 56449) + // // + // // If A * B = C at each point in the domain, then the following polynomial... + // // P(x) = A(x) * B(x) - C(x) + // // = 49752*x^14 + 13914*x^13 + 29243*x^12 + 27227*x^11 + 62362*x^10 + 35703*x^9 + 4032*x^8 + 14761*x^6 + 50599*x^5 + 35270*x^4 + 37286*x^3 + 2151*x^2 + 28810*x + 60481 + // // + // // ... should be divisible by t(x), producing the quotient polynomial: + // // h(x) = P(x) / t(x) + // // = 49752*x^6 + 13914*x^5 + 29243*x^4 + 27227*x^3 + 62362*x^2 + 35703*x + 4032 + // { + // let mut expected_c = Fr::zero(); + + // // A * s + // let mut tmp = proof.a; + // tmp.mul_assign(&s); + // expected_c.add_assign(&tmp); + + // // B * r + // let mut tmp = proof.b; + // tmp.mul_assign(&r); + // expected_c.add_assign(&tmp); + + // // delta * r * s + // let mut tmp = delta; + // tmp.mul_assign(&r); + // tmp.mul_assign(&s); + // expected_c.sub_assign(&tmp); + + // // L query answer + // // a_2 = 1, a_3 = 0 + // expected_c.add_assign(¶ms.l[0]); + + // // H query answer + // for (i, coeff) in [5040, 11763, 10755, 63633, 128, 9747, 8739].iter().enumerate() { + // let coeff = Fr::from_str(&format!("{}", coeff)).unwrap(); + + // let mut tmp = params.h[i]; + // tmp.mul_assign(&coeff); + // expected_c.add_assign(&tmp); + // } + + // assert_eq!(expected_c, proof.c); + // } + + // assert!(verify_proof( + // &pvk, + // &proof, + // &[Fr::one()] + // ).unwrap()); +} diff --git a/src/groth16/tests/mod.rs b/src/groth16/tests/mod.rs index 798b046..5a2bb2c 100644 --- a/src/groth16/tests/mod.rs +++ b/src/groth16/tests/mod.rs @@ -7,8 +7,8 @@ use ff:: { PrimeField, }; -mod dummy_engine; -use self::dummy_engine::*; +use super::super::tests::dummy_engine::*; +use super::super::tests::XORDemo; use std::marker::PhantomData; @@ -25,79 +25,6 @@ use super::{ verify_proof }; -struct XORDemo { - a: Option, - b: Option, - _marker: PhantomData -} - -impl Circuit for XORDemo { - fn synthesize>( - self, - cs: &mut CS - ) -> Result<(), SynthesisError> - { - let a_var = cs.alloc(|| "a", || { - if self.a.is_some() { - if self.a.unwrap() { - Ok(E::Fr::one()) - } else { - Ok(E::Fr::zero()) - } - } else { - Err(SynthesisError::AssignmentMissing) - } - })?; - - cs.enforce( - || "a_boolean_constraint", - |lc| lc + CS::one() - a_var, - |lc| lc + a_var, - |lc| lc - ); - - let b_var = cs.alloc(|| "b", || { - if self.b.is_some() { - if self.b.unwrap() { - Ok(E::Fr::one()) - } else { - Ok(E::Fr::zero()) - } - } else { - Err(SynthesisError::AssignmentMissing) - } - })?; - - cs.enforce( - || "b_boolean_constraint", - |lc| lc + CS::one() - b_var, - |lc| lc + b_var, - |lc| lc - ); - - let c_var = cs.alloc_input(|| "c", || { - if self.a.is_some() && self.b.is_some() { - if self.a.unwrap() ^ self.b.unwrap() { - Ok(E::Fr::one()) - } else { - Ok(E::Fr::zero()) - } - } else { - Err(SynthesisError::AssignmentMissing) - } - })?; - - cs.enforce( - || "c_xor_constraint", - |lc| lc + a_var + a_var, - |lc| lc + b_var, - |lc| lc + a_var + b_var - c_var - ); - - Ok(()) - } -} - #[test] fn test_xordemo() { let g1 = Fr::one(); diff --git a/src/lib.rs b/src/lib.rs index c43cdd1..d6952ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,10 @@ pub mod multicore; mod multiexp; pub mod domain; pub mod groth16; +pub mod gm17; + +#[cfg(test)] +mod tests; use pairing::{Engine}; use ff::Field; diff --git a/src/groth16/tests/dummy_engine.rs b/src/tests/dummy_engine.rs similarity index 100% rename from src/groth16/tests/dummy_engine.rs rename to src/tests/dummy_engine.rs diff --git a/src/tests/mod.rs b/src/tests/mod.rs new file mode 100644 index 0000000..f9f6ce9 --- /dev/null +++ b/src/tests/mod.rs @@ -0,0 +1,93 @@ +use pairing::{ + Engine +}; + +use ff:: { + Field, + PrimeField, +}; + +pub mod dummy_engine; +use self::dummy_engine::*; + +use std::marker::PhantomData; + +use ::{ + Circuit, + ConstraintSystem, + SynthesisError +}; + +pub(crate) struct XORDemo { + pub(crate) a: Option, + pub(crate) b: Option, + pub(crate) _marker: PhantomData +} + +impl Circuit for XORDemo { + fn synthesize>( + self, + cs: &mut CS + ) -> Result<(), SynthesisError> + { + let a_var = cs.alloc(|| "a", || { + if self.a.is_some() { + if self.a.unwrap() { + Ok(E::Fr::one()) + } else { + Ok(E::Fr::zero()) + } + } else { + Err(SynthesisError::AssignmentMissing) + } + })?; + + cs.enforce( + || "a_boolean_constraint", + |lc| lc + CS::one() - a_var, + |lc| lc + a_var, + |lc| lc + ); + + let b_var = cs.alloc(|| "b", || { + if self.b.is_some() { + if self.b.unwrap() { + Ok(E::Fr::one()) + } else { + Ok(E::Fr::zero()) + } + } else { + Err(SynthesisError::AssignmentMissing) + } + })?; + + cs.enforce( + || "b_boolean_constraint", + |lc| lc + CS::one() - b_var, + |lc| lc + b_var, + |lc| lc + ); + + let c_var = cs.alloc_input(|| "c", || { + if self.a.is_some() && self.b.is_some() { + if self.a.unwrap() ^ self.b.unwrap() { + Ok(E::Fr::one()) + } else { + Ok(E::Fr::zero()) + } + } else { + Err(SynthesisError::AssignmentMissing) + } + })?; + + cs.enforce( + || "c_xor_constraint", + |lc| lc + a_var + a_var, + |lc| lc + b_var, + |lc| lc + a_var + b_var - c_var + ); + + Ok(()) + } +} +