From ff6c46240b6d715faca65498fb5f0894ace2d1bb Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Fri, 1 Feb 2019 18:36:50 +0300 Subject: [PATCH 01/42] start implementing GM17 proof system --- src/gm17/generator.rs | 700 ++++++++++++++++++++++++ src/gm17/mod.rs | 563 +++++++++++++++++++ src/gm17/tests/mod.rs | 329 +++++++++++ src/groth16/tests/mod.rs | 77 +-- src/lib.rs | 4 + src/{groth16 => }/tests/dummy_engine.rs | 0 src/tests/mod.rs | 93 ++++ 7 files changed, 1691 insertions(+), 75 deletions(-) create mode 100644 src/gm17/generator.rs create mode 100644 src/gm17/mod.rs create mode 100644 src/gm17/tests/mod.rs rename src/{groth16 => }/tests/dummy_engine.rs (100%) create mode 100644 src/tests/mod.rs 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(()) + } +} + From f06f92a9ccaeaf84fca664bcb4163d95a5ecdf6c Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Mon, 4 Feb 2019 13:18:44 +0300 Subject: [PATCH 02/42] integrate initial part of SONIC. Use original code, restructure, mock Transcript --- Cargo.toml | 2 +- src/domain.rs | 14 +- src/gm17/generator.rs | 6 +- src/groth16/generator.rs | 6 +- src/lib.rs | 1 + src/multicore.rs | 5 +- src/sonic/README.md | 11 + src/sonic/cs/lc.rs | 139 ++++++++ src/sonic/cs/mod.rs | 220 +++++++++++++ src/sonic/helped/batch.rs | 128 ++++++++ src/sonic/helped/mod.rs | 39 +++ src/sonic/helped/poly.rs | 264 +++++++++++++++ src/sonic/helped/prover.rs | 599 +++++++++++++++++++++++++++++++++++ src/sonic/helped/verifier.rs | 463 +++++++++++++++++++++++++++ src/sonic/mod.rs | 15 + src/sonic/paper.rs | 310 ++++++++++++++++++ src/sonic/sonic/adaptor.rs | 166 ++++++++++ src/sonic/sonic/mod.rs | 6 + src/sonic/srs/mod.rs | 5 + src/sonic/srs/srs.rs | 95 ++++++ src/sonic/transcript/mod.rs | 69 ++++ src/sonic/util.rs | 337 ++++++++++++++++++++ tests/mimc.rs | 198 ++++++++++++ 23 files changed, 3082 insertions(+), 16 deletions(-) create mode 100644 src/sonic/README.md create mode 100644 src/sonic/cs/lc.rs create mode 100644 src/sonic/cs/mod.rs create mode 100644 src/sonic/helped/batch.rs create mode 100644 src/sonic/helped/mod.rs create mode 100644 src/sonic/helped/poly.rs create mode 100644 src/sonic/helped/prover.rs create mode 100644 src/sonic/helped/verifier.rs create mode 100644 src/sonic/mod.rs create mode 100644 src/sonic/paper.rs create mode 100644 src/sonic/sonic/adaptor.rs create mode 100644 src/sonic/sonic/mod.rs create mode 100644 src/sonic/srs/mod.rs create mode 100644 src/sonic/srs/srs.rs create mode 100644 src/sonic/transcript/mod.rs create mode 100644 src/sonic/util.rs diff --git a/Cargo.toml b/Cargo.toml index 641a667..0a71a2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ bit-vec = "0.4.4" futures = "0.1" futures-cpupool = "0.1" num_cpus = "1" -crossbeam = "0.3" +crossbeam = "0.7.1" pairing = { git = 'https://github.com/matterinc/pairing' } byteorder = "1" ff = { git = 'https://github.com/matterinc/ff', features = ["derive"] } diff --git a/src/domain.rs b/src/domain.rs index 43f3d38..313e561 100644 --- a/src/domain.rs +++ b/src/domain.rs @@ -110,7 +110,7 @@ impl> EvaluationDomain { let minv = self.minv; for v in self.coeffs.chunks_mut(chunk) { - scope.spawn(move || { + scope.spawn(move |_| { for v in v { v.group_mul_assign(&minv); } @@ -123,7 +123,7 @@ impl> EvaluationDomain { { worker.scope(self.coeffs.len(), |scope, chunk| { for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() { - scope.spawn(move || { + scope.spawn(move |_| { let mut u = g.pow(&[(i * chunk) as u64]); for v in v.iter_mut() { v.group_mul_assign(&u); @@ -166,7 +166,7 @@ impl> EvaluationDomain { worker.scope(self.coeffs.len(), |scope, chunk| { for v in self.coeffs.chunks_mut(chunk) { - scope.spawn(move || { + scope.spawn(move |_| { for v in v { v.group_mul_assign(&i); } @@ -181,7 +181,7 @@ impl> EvaluationDomain { worker.scope(self.coeffs.len(), |scope, chunk| { for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) { - scope.spawn(move || { + scope.spawn(move |_| { for (a, b) in a.iter_mut().zip(b.iter()) { a.group_mul_assign(&b.0); } @@ -196,7 +196,7 @@ impl> EvaluationDomain { worker.scope(self.coeffs.len(), |scope, chunk| { for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) { - scope.spawn(move || { + scope.spawn(move |_| { for (a, b) in a.iter_mut().zip(b.iter()) { a.group_sub_assign(&b); } @@ -350,7 +350,7 @@ fn parallel_fft>( let a = &*a; for (j, tmp) in tmp.iter_mut().enumerate() { - scope.spawn(move || { + scope.spawn(move |_| { // Shuffle into a sub-FFT let omega_j = omega.pow(&[j as u64]); let omega_step = omega.pow(&[(j as u64) << log_new_n]); @@ -378,7 +378,7 @@ fn parallel_fft>( let tmp = &tmp; for (idx, a) in a.chunks_mut(chunk).enumerate() { - scope.spawn(move || { + scope.spawn(move |_| { let mut idx = idx * chunk; let mask = (1 << log_cpus) - 1; for a in a { diff --git a/src/gm17/generator.rs b/src/gm17/generator.rs index 6895cde..059a041 100644 --- a/src/gm17/generator.rs +++ b/src/gm17/generator.rs @@ -391,7 +391,7 @@ pub fn generate_parameters( worker.scope(domain.len(), |scope, chunk| { for (i, subdomain) in domain.chunks_mut(chunk).enumerate() { - scope.spawn(move || { + scope.spawn(move |_| { let mut current_power = tau.pow(&[(i*chunk) as u64]); for p in subdomain { @@ -420,7 +420,7 @@ pub fn generate_parameters( 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 || { + 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()) { @@ -554,7 +554,7 @@ pub fn generate_parameters( let mut g1_wnaf = g1_wnaf.shared(); let mut g2_wnaf = g2_wnaf.shared(); - scope.spawn(move || { + 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()) diff --git a/src/groth16/generator.rs b/src/groth16/generator.rs index bb72ef0..0d597a6 100644 --- a/src/groth16/generator.rs +++ b/src/groth16/generator.rs @@ -259,7 +259,7 @@ pub fn generate_parameters( worker.scope(powers_of_tau.len(), |scope, chunk| { for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate() { - scope.spawn(move || { + scope.spawn(move |_| { let mut current_tau_power = tau.pow(&[(i*chunk) as u64]); for p in powers_of_tau { @@ -285,7 +285,7 @@ pub fn generate_parameters( for (h, p) in h.chunks_mut(chunk).zip(powers_of_tau.as_ref().chunks(chunk)) { let mut g1_wnaf = g1_wnaf.shared(); - scope.spawn(move || { + scope.spawn(move |_| { // Set values of the H query to g1^{(tau^i * t(tau)) / delta} for (h, p) in h.iter_mut().zip(p.iter()) { @@ -376,7 +376,7 @@ pub fn generate_parameters( let mut g1_wnaf = g1_wnaf.shared(); let mut g2_wnaf = g2_wnaf.shared(); - scope.spawn(move || { + scope.spawn(move |_| { for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a.iter_mut() .zip(b_g1.iter_mut()) .zip(b_g2.iter_mut()) diff --git a/src/lib.rs b/src/lib.rs index d6952ac..1ef74b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,7 @@ mod multiexp; pub mod domain; pub mod groth16; pub mod gm17; +pub mod sonic; #[cfg(test)] mod tests; diff --git a/src/multicore.rs b/src/multicore.rs index c0062fc..84055d7 100644 --- a/src/multicore.rs +++ b/src/multicore.rs @@ -7,7 +7,8 @@ use num_cpus; use futures::{Future, IntoFuture, Poll}; use futures_cpupool::{CpuPool, CpuFuture}; -use crossbeam::{self, Scope}; +use crossbeam::{self}; +use crossbeam::thread::{Scope}; #[derive(Clone)] pub struct Worker { @@ -63,7 +64,7 @@ impl Worker { crossbeam::scope(|scope| { f(scope, chunk_size) - }) + }).expect("must run") } } diff --git a/src/sonic/README.md b/src/sonic/README.md new file mode 100644 index 0000000..1e0c6c2 --- /dev/null +++ b/src/sonic/README.md @@ -0,0 +1,11 @@ +# Description + +Initial SONIC proof system integration using the code from the [original implementation](https://github.com/zknuckles/sonic.git). It's here for experimental reasons and evaluation of the following properties: + +- How applicable is "helped" procedure for a case of Ethereum +- What is a final verification cost for "helped" and "unhelped" procedures +- Prover efficiency in both cases +- Implementation of a memory constrained prover and helper +- Smart-contract implementation of verifiers +- Code cleanup +- Migration for smart-contract compatible transcripts \ No newline at end of file diff --git a/src/sonic/cs/lc.rs b/src/sonic/cs/lc.rs new file mode 100644 index 0000000..806eb61 --- /dev/null +++ b/src/sonic/cs/lc.rs @@ -0,0 +1,139 @@ +use ff::{Field}; +use pairing::{Engine}; +use std::ops::{Add, Sub, Neg}; + +/// This represents a linear combination of some variables, with coefficients +/// in the scalar field of a pairing-friendly elliptic curve group. +#[derive(Clone)] +pub struct LinearCombination(Vec<(Variable, Coeff)>); + +impl From for LinearCombination { + fn from(var: Variable) -> LinearCombination { + LinearCombination::::zero() + var + } +} + +impl AsRef<[(Variable, Coeff)]> for LinearCombination { + fn as_ref(&self) -> &[(Variable, Coeff)] { + &self.0 + } +} + +impl LinearCombination { + pub fn zero() -> LinearCombination { + LinearCombination(vec![]) + } +} + +impl Add<(Coeff, Variable)> for LinearCombination { + type Output = LinearCombination; + + fn add(mut self, (coeff, var): (Coeff, Variable)) -> LinearCombination { + self.0.push((var, coeff)); + + self + } +} + +impl Sub<(Coeff, Variable)> for LinearCombination { + type Output = LinearCombination; + + fn sub(self, (coeff, var): (Coeff, Variable)) -> LinearCombination { + self + (-coeff, var) + } +} + +impl Add for LinearCombination { + type Output = LinearCombination; + + fn add(self, other: Variable) -> LinearCombination { + self + (Coeff::One, other) + } +} + +impl Sub for LinearCombination { + type Output = LinearCombination; + + fn sub(self, other: Variable) -> LinearCombination { + self - (Coeff::One, other) + } +} + +impl<'a, E: Engine> Add<&'a LinearCombination> for LinearCombination { + type Output = LinearCombination; + + fn add(mut self, other: &'a LinearCombination) -> LinearCombination { + for s in &other.0 { + self = self + (s.1, s.0); + } + + self + } +} + +impl<'a, E: Engine> Sub<&'a LinearCombination> for LinearCombination { + type Output = LinearCombination; + + fn sub(mut self, other: &'a LinearCombination) -> LinearCombination { + for s in &other.0 { + self = self - (s.1, s.0); + } + + self + } +} + +#[derive(Copy, Clone, Debug)] +pub enum Variable { + A(usize), + B(usize), + C(usize), +} + +#[derive(Debug)] +pub enum Coeff { + Zero, + One, + NegativeOne, + Full(E::Fr), +} + +impl Coeff { + pub fn multiply(&self, with: &mut E::Fr) { + match self { + Coeff::Zero => { + *with = E::Fr::zero(); + }, + Coeff::One => {}, + Coeff::NegativeOne => { + with.negate(); + }, + Coeff::Full(val) => { + with.mul_assign(val); + } + } + } +} + +impl Copy for Coeff {} +impl Clone for Coeff { + fn clone(&self) -> Self { + *self + } +} + +impl Neg for Coeff { + type Output = Coeff; + + fn neg(self) -> Self { + match self { + Coeff::Zero => Coeff::Zero, + Coeff::One => Coeff::NegativeOne, + Coeff::NegativeOne => Coeff::One, + Coeff::Full(mut a) => { + a.negate(); + Coeff::Full(a) + } + } + } +} \ No newline at end of file diff --git a/src/sonic/cs/mod.rs b/src/sonic/cs/mod.rs new file mode 100644 index 0000000..3138c06 --- /dev/null +++ b/src/sonic/cs/mod.rs @@ -0,0 +1,220 @@ +extern crate ff; +extern crate pairing; + +use ff::{Field}; +use pairing::{Engine}; + +use crate::{SynthesisError}; +use std::marker::PhantomData; + + +mod lc; +pub use self::lc::{Coeff, Variable, LinearCombination}; + +pub trait Circuit { + fn synthesize>(&self, cs: &mut CS) -> Result<(), SynthesisError>; +} + +pub trait ConstraintSystem { + const ONE: Variable; + + fn alloc(&mut self, value: F) -> Result + where + F: FnOnce() -> Result; + + fn alloc_input(&mut self, value: F) -> Result + where + F: FnOnce() -> Result; + + fn enforce_zero(&mut self, lc: LinearCombination); + + fn multiply(&mut self, values: F) -> Result<(Variable, Variable, Variable), SynthesisError> + where + F: FnOnce() -> Result<(E::Fr, E::Fr, E::Fr), SynthesisError>; + + // TODO: get rid of this + fn get_value(&self, _var: Variable) -> Result { + Err(()) + } +} + + + +/// This is a backend for the `SynthesisDriver` to relay information about +/// the concrete circuit. One backend might just collect basic information +/// about the circuit for verification, while another actually constructs +/// a witness. +pub trait Backend { + /// Get the value of a variable. Can return None if we don't know. + fn get_var(&self, _variable: Variable) -> Option { None } + + /// Set the value of a variable. Might error if this backend expects to know it. + fn set_var(&mut self, _variable: Variable, _value: F) -> Result<(), SynthesisError> + where F: FnOnce() -> Result { Ok(()) } + + /// Create a new multiplication gate. + fn new_multiplication_gate(&mut self) { } + + /// Create a new linear constraint. + fn new_linear_constraint(&mut self) { } + + /// Insert a term into a linear constraint. TODO: bad name of function + fn insert_coefficient(&mut self, _var: Variable, _coeff: Coeff) { } + + /// Mark y^{_index} as the power of y cooresponding to the public input + /// coefficient for the next public input, in the k(Y) polynomial. + fn new_k_power(&mut self, _index: usize) { } +} + +/// This is an abstraction which synthesizes circuits. +pub trait SynthesisDriver { + fn synthesize, B: Backend>(backend: B, circuit: &C) -> Result<(), SynthesisError>; +} + +pub struct Basic; + +impl SynthesisDriver for Basic { + fn synthesize, B: Backend>(backend: B, circuit: &C) -> Result<(), SynthesisError> { + struct Synthesizer> { + backend: B, + current_variable: Option, + _marker: PhantomData, + q: usize, + n: usize, + } + + impl> ConstraintSystem for Synthesizer { + const ONE: Variable = Variable::A(1); + + fn alloc(&mut self, value: F) -> Result + where + F: FnOnce() -> Result + { + match self.current_variable.take() { + Some(index) => { + let var_a = Variable::A(index); + let var_b = Variable::B(index); + let var_c = Variable::C(index); + + let mut product = None; + + let value_a = self.backend.get_var(var_a); + + self.backend.set_var(var_b, || { + let value_b = value()?; + product = Some(value_a.ok_or(SynthesisError::AssignmentMissing)?); + product.as_mut().map(|product| product.mul_assign(&value_b)); + + Ok(value_b) + })?; + + self.backend.set_var(var_c, || { + product.ok_or(SynthesisError::AssignmentMissing) + })?; + + self.current_variable = None; + + Ok(var_b) + }, + None => { + self.n += 1; + let index = self.n; + self.backend.new_multiplication_gate(); + + let var_a = Variable::A(index); + + self.backend.set_var(var_a, value)?; + + self.current_variable = Some(index); + + Ok(var_a) + } + } + } + + fn alloc_input(&mut self, value: F) -> Result + where + F: FnOnce() -> Result + { + let input_var = self.alloc(value)?; + + self.enforce_zero(LinearCombination::zero() + input_var); + self.backend.new_k_power(self.q); + + Ok(input_var) + } + + fn enforce_zero(&mut self, lc: LinearCombination) + { + self.q += 1; + self.backend.new_linear_constraint(); + + for (var, coeff) in lc.as_ref() { + self.backend.insert_coefficient(*var, *coeff); + } + } + + fn multiply(&mut self, values: F) -> Result<(Variable, Variable, Variable), SynthesisError> + where + F: FnOnce() -> Result<(E::Fr, E::Fr, E::Fr), SynthesisError> + { + self.n += 1; + let index = self.n; + self.backend.new_multiplication_gate(); + + let a = Variable::A(index); + let b = Variable::B(index); + let c = Variable::C(index); + + let mut b_val = None; + let mut c_val = None; + + self.backend.set_var(a, || { + let (a, b, c) = values()?; + + b_val = Some(b); + c_val = Some(c); + + Ok(a) + })?; + + self.backend.set_var(b, || { + b_val.ok_or(SynthesisError::AssignmentMissing) + })?; + + self.backend.set_var(c, || { + c_val.ok_or(SynthesisError::AssignmentMissing) + })?; + + Ok((a, b, c)) + } + + fn get_value(&self, var: Variable) -> Result { + self.backend.get_var(var).ok_or(()) + } + } + + let mut tmp: Synthesizer = Synthesizer { + backend: backend, + current_variable: None, + _marker: PhantomData, + q: 0, + n: 0, + }; + + let one = tmp.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues"); + + match (one, as ConstraintSystem>::ONE) { + (Variable::A(1), Variable::A(1)) => {}, + _ => panic!("one variable is incorrect") + } + + circuit.synthesize(&mut tmp)?; + + // TODO: add blinding factors so we actually get zero-knowledge + + // println!("n = {}", tmp.n); + + Ok(()) + } +} diff --git a/src/sonic/helped/batch.rs b/src/sonic/helped/batch.rs new file mode 100644 index 0000000..81fe5f6 --- /dev/null +++ b/src/sonic/helped/batch.rs @@ -0,0 +1,128 @@ +//! Our protocol allows the verification of multiple proofs and even +//! of individual proofs to batch the pairing operations such that +//! only a smaller, fixed number of pairings must occur for an entire +//! batch of proofs. This is possible because G2 elements are fixed +//! in our protocol and never appear in proofs; everything can be +//! combined probabilistically. +//! +//! This submodule contains the `Batch` abstraction for creating a +//! context for batch verification. + +use ff::{Field}; +use pairing::{Engine, CurveAffine, CurveProjective}; +use crate::sonic::srs::SRS; +use crate::sonic::util::multiexp; + +// One of the primary functions of the `Batch` abstraction is handling +// Kate commitment openings: +// +// e(P', [\alpha(x - z)] H) = e(P, H) e([-v] G, [\alpha] H) +// ==> e(P', [\alpha x] H) e([-z] P', [\alpha] H) = e(P, H) e([-v] G, [\alpha] H) +// +// Many of these can be opened simultaneously by sampling random `r` and +// accumulating... +// +// e([r] P', [\alpha x] H) +// e([-rz] P', [\alpha] H) +// e([r] P, -H) +// e([rv] G, [\alpha] H) +// +// ... and checking that the result is the identity in the target group. +pub struct Batch { + alpha_x: Vec<(E::G1Affine, E::Fr)>, + alpha_x_precomp: ::Prepared, + + alpha: Vec<(E::G1Affine, E::Fr)>, + alpha_precomp: ::Prepared, + + neg_h: Vec<(E::G1Affine, E::Fr)>, + neg_h_precomp: ::Prepared, + + neg_x_n_minus_d: Vec<(E::G1Affine, E::Fr)>, + neg_x_n_minus_d_precomp: ::Prepared, + + // The value paired with [\alpha] H, accumulated in the field + // to save group operations. + value: E::Fr, + g: E::G1Affine, +} + +impl Batch { + pub fn new(srs: &SRS, n: usize) -> Self { + Batch { + alpha_x: vec![], + alpha_x_precomp: srs.h_positive_x_alpha[1].prepare(), + + alpha: vec![], + alpha_precomp: srs.h_positive_x_alpha[0].prepare(), + + neg_h: vec![], + neg_h_precomp: { + let mut tmp = srs.h_negative_x[0]; + tmp.negate(); + tmp.prepare() + }, + + neg_x_n_minus_d: vec![], + neg_x_n_minus_d_precomp: { + let mut tmp = srs.h_negative_x[srs.d - n]; + tmp.negate(); + tmp.prepare() + }, + + value: E::Fr::zero(), + g: srs.g_positive_x[0], + } + } + + pub fn add_opening(&mut self, p: E::G1Affine, mut r: E::Fr, point: E::Fr) { + self.alpha_x.push((p, r)); + r.mul_assign(&point); + r.negate(); + self.alpha.push((p, r)); + } + + pub fn add_commitment(&mut self, p: E::G1Affine, r: E::Fr) { + self.neg_h.push((p, r)); + } + + pub fn add_commitment_max_n(&mut self, p: E::G1Affine, r: E::Fr) { + self.neg_x_n_minus_d.push((p, r)); + } + + pub fn add_opening_value(&mut self, mut r: E::Fr, point: E::Fr) { + r.mul_assign(&point); + self.value.add_assign(&r); + } + + pub fn check_all(mut self) -> bool { + self.alpha.push((self.g, self.value)); + + let alpha_x = multiexp( + self.alpha_x.iter().map(|x| &x.0), + self.alpha_x.iter().map(|x| &x.1), + ).into_affine().prepare(); + + let alpha = multiexp( + self.alpha.iter().map(|x| &x.0), + self.alpha.iter().map(|x| &x.1), + ).into_affine().prepare(); + + let neg_h = multiexp( + self.neg_h.iter().map(|x| &x.0), + self.neg_h.iter().map(|x| &x.1), + ).into_affine().prepare(); + + let neg_x_n_minus_d = multiexp( + self.neg_x_n_minus_d.iter().map(|x| &x.0), + self.neg_x_n_minus_d.iter().map(|x| &x.1), + ).into_affine().prepare(); + + E::final_exponentiation(&E::miller_loop(&[ + (&alpha_x, &self.alpha_x_precomp), + (&alpha, &self.alpha_precomp), + (&neg_h, &self.neg_h_precomp), + (&neg_x_n_minus_d, &self.neg_x_n_minus_d_precomp), + ])).unwrap() == E::Fqk::one() + } +} diff --git a/src/sonic/helped/mod.rs b/src/sonic/helped/mod.rs new file mode 100644 index 0000000..bf59409 --- /dev/null +++ b/src/sonic/helped/mod.rs @@ -0,0 +1,39 @@ +extern crate ff; +extern crate pairing; +// extern crate merlin; + +use ff::{Field}; +use pairing::{Engine, CurveProjective}; +use std::marker::PhantomData; +// use merlin::{Transcript}; + +mod verifier; +mod prover; +mod batch; +mod poly; + +pub use self::verifier::{MultiVerifier, create_aggregate}; +pub use self::prover::{Aggregate, create_proof, create_advice}; + +// use super::super::util::*; +// pub use super::batch::Batch; +// use crate::synthesis::{Backend, SynthesisDriver}; +// use crate::{Circuit, SynthesisError, Variable, Coeff}; +// use crate::srs::SRS; + +#[derive(Clone)] +pub struct SxyAdvice { + s: E::G1Affine, + opening: E::G1Affine, + szy: E::Fr, +} + +#[derive(Clone)] +pub struct Proof { + r: E::G1Affine, + t: E::G1Affine, + rz: E::Fr, + rzy: E::Fr, + z_opening: E::G1Affine, + zy_opening: E::G1Affine +} \ No newline at end of file diff --git a/src/sonic/helped/poly.rs b/src/sonic/helped/poly.rs new file mode 100644 index 0000000..6001061 --- /dev/null +++ b/src/sonic/helped/poly.rs @@ -0,0 +1,264 @@ +use ff::{Field}; +use pairing::{Engine, CurveProjective}; +use std::marker::PhantomData; + +use crate::sonic::cs::{Backend}; +use crate::sonic::cs::{Coeff, Variable, LinearCombination}; + +/* +s(X, Y) = \sum\limits_{i=1}^N u_i(Y) X^{-i} + + \sum\limits_{i=1}^N v_i(Y) X^{i} + + \sum\limits_{i=1}^N w_i(Y) X^{i+N} + +where + + u_i(Y) = \sum\limits_{q=1}^Q Y^{q+N} u_{i,q} + v_i(Y) = \sum\limits_{q=1}^Q Y^{q+N} v_{i,q} + w_i(Y) = -Y^i + -Y^{-i} + \sum\limits_{q=1}^Q Y^{q+N} w_{i,q} + +*/ +#[derive(Clone)] +pub struct SxEval { + y: E::Fr, + + // current value of y^{q+N} + yqn: E::Fr, + + // x^{-i} (\sum\limits_{q=1}^Q y^{q+N} u_{q,i}) + u: Vec, + // x^{i} (\sum\limits_{q=1}^Q y^{q+N} v_{q,i}) + v: Vec, + // x^{i+N} (-y^i -y^{-i} + \sum\limits_{q=1}^Q y^{q+N} w_{q,i}) + w: Vec, +} + +impl SxEval { + pub fn new(y: E::Fr, n: usize) -> Self { + let y_inv = y.inverse().unwrap(); // TODO + + let yqn = y.pow(&[n as u64]); + + let u = vec![E::Fr::zero(); n]; + let v = vec![E::Fr::zero(); n]; + let mut w = vec![E::Fr::zero(); n]; + + let mut tmp1 = y; + let mut tmp2 = y_inv; + for w in &mut w { + let mut new = tmp1; + new.add_assign(&tmp2); + new.negate(); + *w = new; + tmp1.mul_assign(&y); + tmp2.mul_assign(&y_inv); + } + + SxEval { + y, + yqn, + u, + v, + w, + } + } + + pub fn poly(mut self) -> (Vec, Vec) { + self.v.extend(self.w); + + (self.u, self.v) + } + + pub fn finalize(self, x: E::Fr) -> E::Fr { + let x_inv = x.inverse().unwrap(); // TODO + + let mut tmp = x_inv; + + let mut acc = E::Fr::zero(); + for mut u in self.u { + u.mul_assign(&tmp); + acc.add_assign(&u); + tmp.mul_assign(&x_inv); + } + + let mut tmp = x; + for mut v in self.v { + v.mul_assign(&tmp); + acc.add_assign(&v); + tmp.mul_assign(&x); + } + for mut w in self.w { + w.mul_assign(&tmp); + acc.add_assign(&w); + tmp.mul_assign(&x); + } + + acc + } +} + +impl<'a, E: Engine> Backend for &'a mut SxEval { + fn new_linear_constraint(&mut self) { + self.yqn.mul_assign(&self.y); + } + + fn insert_coefficient(&mut self, var: Variable, coeff: Coeff) { + let acc = match var { + Variable::A(index) => { + &mut self.u[index - 1] + } + Variable::B(index) => { + &mut self.v[index - 1] + } + Variable::C(index) => { + &mut self.w[index - 1] + } + }; + + match coeff { + Coeff::Zero => { }, + Coeff::One => { + acc.add_assign(&self.yqn); + }, + Coeff::NegativeOne => { + acc.sub_assign(&self.yqn); + }, + Coeff::Full(mut val) => { + val.mul_assign(&self.yqn); + acc.add_assign(&val); + } + } + } +} + +/* +s(X, Y) = \sum\limits_{i=1}^N \sum\limits_{q=1}^Q Y^{q+N} u_{i,q} X^{-i} + + \sum\limits_{i=1}^N \sum\limits_{q=1}^Q Y^{q+N} v_{i,q} X^{i} + + \sum\limits_{i=1}^N \sum\limits_{q=1}^Q Y^{q+N} w_{i,q} X^{i+N} + - \sum\limits_{i=1}^N Y^i X^{i+N} + - \sum\limits_{i=1}^N Y^{-i} X^{i+N} +*/ +pub struct SyEval { + max_n: usize, + current_q: usize, + + // x^{-1}, ..., x^{-N} + a: Vec, + + // x^1, ..., x^{N} + b: Vec, + + // x^{N+1}, ..., x^{2*N} + c: Vec, + + // coeffs for y^1, ..., y^{N+Q} + positive_coeffs: Vec, + + // coeffs for y^{-1}, y^{-2}, ..., y^{-N} + negative_coeffs: Vec, +} + + +impl SyEval { + pub fn new(x: E::Fr, n: usize, q: usize) -> Self { + let xinv = x.inverse().unwrap(); + let mut tmp = E::Fr::one(); + let mut a = vec![E::Fr::zero(); n]; + for a in &mut a { + tmp.mul_assign(&xinv); // tmp = x^{-i} + *a = tmp; + } + + let mut tmp = E::Fr::one(); + let mut b = vec![E::Fr::zero(); n]; + for b in &mut b { + tmp.mul_assign(&x); // tmp = x^{i} + *b = tmp; + } + + let mut positive_coeffs = vec![E::Fr::zero(); n + q]; + let mut negative_coeffs = vec![E::Fr::zero(); n]; + + let mut c = vec![E::Fr::zero(); n]; + for ((c, positive_coeff), negative_coeff) in c.iter_mut().zip(&mut positive_coeffs).zip(&mut negative_coeffs) { + tmp.mul_assign(&x); // tmp = x^{i+N} + *c = tmp; + + // - \sum\limits_{i=1}^N Y^i X^{i+N} + let mut tmp = tmp; + tmp.negate(); + *positive_coeff = tmp; + + // - \sum\limits_{i=1}^N Y^{-i} X^{i+N} + *negative_coeff = tmp; + } + + SyEval { + a, + b, + c, + positive_coeffs, + negative_coeffs, + current_q: 0, + max_n: n, + } + } + + pub fn poly(self) -> (Vec, Vec) { + (self.negative_coeffs, self.positive_coeffs) + } + + pub fn finalize(self, y: E::Fr) -> E::Fr { + let mut acc = E::Fr::zero(); + + let mut tmp = y; + for mut coeff in self.positive_coeffs { + coeff.mul_assign(&tmp); + acc.add_assign(&coeff); + tmp.mul_assign(&y); + } + let yinv = y.inverse().unwrap(); // TODO + let mut tmp = yinv; + for mut coeff in self.negative_coeffs { + coeff.mul_assign(&tmp); + acc.add_assign(&coeff); + tmp.mul_assign(&yinv); + } + + acc + } +} + +impl<'a, E: Engine> Backend for &'a mut SyEval { + fn new_linear_constraint(&mut self) { + self.current_q += 1; + } + + fn insert_coefficient(&mut self, var: Variable, coeff: Coeff) { + match var { + Variable::A(index) => { + let index = index - 1; + // Y^{q+N} += X^{-i} * coeff + let mut tmp = self.a[index]; + coeff.multiply(&mut tmp); + let yindex = self.current_q + self.max_n; + self.positive_coeffs[yindex - 1].add_assign(&tmp); + } + Variable::B(index) => { + let index = index - 1; + // Y^{q+N} += X^{i} * coeff + let mut tmp = self.b[index]; + coeff.multiply(&mut tmp); + let yindex = self.current_q + self.max_n; + self.positive_coeffs[yindex - 1].add_assign(&tmp); + } + Variable::C(index) => { + let index = index - 1; + // Y^{q+N} += X^{i+N} * coeff + let mut tmp = self.c[index]; + coeff.multiply(&mut tmp); + let yindex = self.current_q + self.max_n; + self.positive_coeffs[yindex - 1].add_assign(&tmp); + } + }; + } +} \ No newline at end of file diff --git a/src/sonic/helped/prover.rs b/src/sonic/helped/prover.rs new file mode 100644 index 0000000..8b13dd2 --- /dev/null +++ b/src/sonic/helped/prover.rs @@ -0,0 +1,599 @@ +use ff::{Field}; +use pairing::{Engine, CurveProjective}; +use std::marker::PhantomData; + +use super::{Proof, SxyAdvice}; +use super::batch::Batch; +use super::poly::{SxEval, SyEval}; + +use crate::SynthesisError; + +use crate::sonic::transcript::{Transcript, TranscriptProtocol}; +use crate::sonic::util::*; +use crate::sonic::cs::{Backend, SynthesisDriver}; +use crate::sonic::cs::{Circuit, Variable, Coeff}; +use crate::sonic::srs::SRS; + +#[derive(Clone)] +pub struct Aggregate { + // Commitment to s(z, Y) + c: E::G1Affine, + // We have to open each of the S commitments to a random point `z` + s_opening: E::G1Affine, + // We have to open C to each constituent `y` + c_openings: Vec<(E::G1Affine, E::Fr)>, + // Then we have to finally open C + opening: E::G1Affine, +} + +pub fn create_aggregate, S: SynthesisDriver>( + circuit: &C, + inputs: &[(Proof, SxyAdvice)], + srs: &SRS, +) -> Aggregate +{ + // TODO: precompute this? + let (n, q) = { + struct CountN { + n: usize, + q: usize + } + + impl<'a, E: Engine> Backend for &'a mut CountN { + fn new_multiplication_gate(&mut self) { + self.n += 1; + } + + fn new_linear_constraint(&mut self) { + self.q += 1; + } + } + + let mut tmp = CountN{n:0,q:0}; + S::synthesize(&mut tmp, circuit).unwrap(); // TODO + + (tmp.n, tmp.q) + }; + + let mut transcript = Transcript::new(&[]); + let mut y_values: Vec = Vec::with_capacity(inputs.len()); + for &(ref proof, ref sxyadvice) in inputs { + { + let mut transcript = Transcript::new(&[]); + transcript.commit_point(&proof.r); + y_values.push(transcript.get_challenge_scalar()); + } + + transcript.commit_point(&sxyadvice.s); + } + + let z: E::Fr = transcript.get_challenge_scalar(); + + // Compute s(z, Y) + let (s_poly_negative, s_poly_positive) = { + let mut tmp = SyEval::new(z, n, q); + S::synthesize(&mut tmp, circuit).unwrap(); // TODO + + tmp.poly() + }; + + // Compute C = g^{s(z, x)} + let c = multiexp( + srs.g_positive_x_alpha[0..(n + q)] + .iter() + .chain_ext(srs.g_negative_x_alpha[0..n].iter()), + s_poly_positive.iter().chain_ext(s_poly_negative.iter()) + ).into_affine(); + + transcript.commit_point(&c); + + // Open C at w + let w: E::Fr = transcript.get_challenge_scalar(); + + let value = compute_value::(&w, &s_poly_positive, &s_poly_negative); + + let opening = { + let mut value = value; + value.negate(); + + let poly = kate_divison( + s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()), + w, + ); + + let negative_poly = poly[0..n].iter().rev(); + let positive_poly = poly[n..].iter(); + multiexp( + srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( + srs.g_positive_x[0..positive_poly.len()].iter() + ), + negative_poly.chain_ext(positive_poly) + ).into_affine() + }; + + // Let's open up C to every y. + fn compute_value(y: &E::Fr, poly_positive: &[E::Fr], poly_negative: &[E::Fr]) -> E::Fr { + let mut value = E::Fr::zero(); + + let yinv = y.inverse().unwrap(); // TODO + let mut tmp = yinv; + for &coeff in poly_negative { + let mut coeff = coeff; + coeff.mul_assign(&tmp); + value.add_assign(&coeff); + tmp.mul_assign(&yinv); + } + + let mut tmp = *y; + for &coeff in poly_positive { + let mut coeff = coeff; + coeff.mul_assign(&tmp); + value.add_assign(&coeff); + tmp.mul_assign(&y); + } + + value + } + + let mut c_openings = vec![]; + for y in &y_values { + let value = compute_value::(y, &s_poly_positive, &s_poly_negative); + + let opening = { + let mut value = value; + value.negate(); + + let poly = kate_divison( + s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()), + *y, + ); + + let negative_poly = poly[0..n].iter().rev(); + let positive_poly = poly[n..].iter(); + multiexp( + srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( + srs.g_positive_x[0..positive_poly.len()].iter() + ), + negative_poly.chain_ext(positive_poly) + ).into_affine() + }; + + c_openings.push((opening, value)); + } + + // Okay, great. Now we need to open up each S at the same point z to the same value. + // Since we're opening up all the S's at the same point, we create a bunch of random + // challenges instead and open up a random linear combination. + + let mut poly_negative = vec![E::Fr::zero(); n]; + let mut poly_positive = vec![E::Fr::zero(); 2*n]; + let mut expected_value = E::Fr::zero(); + + for (y, c_opening) in y_values.iter().zip(c_openings.iter()) { + // Compute s(X, y_i) + let (s_poly_negative, s_poly_positive) = { + let mut tmp = SxEval::new(*y, n); + S::synthesize(&mut tmp, circuit).unwrap(); // TODO + + tmp.poly() + }; + + let mut value = c_opening.1; + let r: E::Fr = transcript.get_challenge_scalar(); + value.mul_assign(&r); + expected_value.add_assign(&value); + + for (mut coeff, target) in s_poly_negative.into_iter().zip(poly_negative.iter_mut()) { + coeff.mul_assign(&r); + target.add_assign(&coeff); + } + + for (mut coeff, target) in s_poly_positive.into_iter().zip(poly_positive.iter_mut()) { + coeff.mul_assign(&r); + target.add_assign(&coeff); + } + } + + let s_opening = { + let mut value = expected_value; + value.negate(); + + let poly = kate_divison( + poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(poly_positive.iter()), + z, + ); + + let negative_poly = poly[0..n].iter().rev(); + let positive_poly = poly[n..].iter(); + multiexp( + srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( + srs.g_positive_x[0..positive_poly.len()].iter() + ), + negative_poly.chain_ext(positive_poly) + ).into_affine() + }; + + Aggregate { + // Commitment to s(z, Y) + c, + // We have to open each of the S commitments to a random point `z` + s_opening, + // We have to open C to each constituent `y` + c_openings, + // Then we have to finally open C + opening, + } +} + +pub fn create_advice, S: SynthesisDriver>( + circuit: &C, + proof: &Proof, + srs: &SRS +) -> SxyAdvice +{ + // annoying, but we need n to compute s(z, y), and this isn't + // precomputed anywhere yet + let n = { + struct CountN { + n: usize + } + + impl<'a, E: Engine> Backend for &'a mut CountN { + fn new_multiplication_gate(&mut self) { + self.n += 1; + } + } + + let mut tmp = CountN{n:0}; + S::synthesize(&mut tmp, circuit).unwrap(); // TODO + + tmp.n + }; + + let z: E::Fr; + let y: E::Fr; + { + let mut transcript = Transcript::new(&[]); + transcript.commit_point(&proof.r); + y = transcript.get_challenge_scalar(); + transcript.commit_point(&proof.t); + z = transcript.get_challenge_scalar(); + } + let z_inv = z.inverse().unwrap(); // TODO + + let (s_poly_negative, s_poly_positive) = { + let mut tmp = SxEval::new(y, n); + S::synthesize(&mut tmp, circuit).unwrap(); // TODO + + tmp.poly() + }; + + // Compute S commitment + let s = multiexp( + srs.g_positive_x_alpha[0..(2 * n)] + .iter() + .chain_ext(srs.g_negative_x_alpha[0..(n)].iter()), + s_poly_positive.iter().chain_ext(s_poly_negative.iter()) + ).into_affine(); + + // Compute s(z, y) + let mut szy = E::Fr::zero(); + { + let mut tmp = z; + for &p in &s_poly_positive { + let mut p = p; + p.mul_assign(&tmp); + szy.add_assign(&p); + tmp.mul_assign(&z); + } + let mut tmp = z_inv; + for &p in &s_poly_negative { + let mut p = p; + p.mul_assign(&tmp); + szy.add_assign(&p); + tmp.mul_assign(&z_inv); + } + } + + // Compute kate opening + let opening = { + let mut open = szy; + open.negate(); + + let poly = kate_divison( + s_poly_negative.iter().rev().chain_ext(Some(open).iter()).chain_ext(s_poly_positive.iter()), + z, + ); + + let negative_poly = poly[0..n].iter().rev(); + let positive_poly = poly[n..].iter(); + multiexp( + srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( + srs.g_positive_x[0..positive_poly.len()].iter() + ), + negative_poly.chain_ext(positive_poly) + ).into_affine() + }; + + SxyAdvice { + s, + szy, + opening + } +} + +pub fn create_proof, S: SynthesisDriver>( + circuit: &C, + srs: &SRS +) -> Result, SynthesisError> +{ + struct Wires { + a: Vec, + b: Vec, + c: Vec + } + + impl<'a, E: Engine> Backend for &'a mut Wires { + fn new_multiplication_gate(&mut self) { + self.a.push(E::Fr::zero()); + self.b.push(E::Fr::zero()); + self.c.push(E::Fr::zero()); + } + + fn get_var(&self, variable: Variable) -> Option { + Some(match variable { + Variable::A(index) => { + self.a[index - 1] + }, + Variable::B(index) => { + self.b[index - 1] + }, + Variable::C(index) => { + self.c[index - 1] + } + }) + } + + fn set_var(&mut self, variable: Variable, value: F) -> Result<(), SynthesisError> + where F: FnOnce() -> Result + { + let value = value()?; + + match variable { + Variable::A(index) => { + self.a[index - 1] = value; + }, + Variable::B(index) => { + self.b[index - 1] = value; + }, + Variable::C(index) => { + self.c[index - 1] = value; + } + } + + Ok(()) + } + } + + let mut wires = Wires { + a: vec![], + b: vec![], + c: vec![], + }; + + S::synthesize(&mut wires, circuit)?; + + let n = wires.a.len(); + + let mut transcript = Transcript::new(&[]); + + let r = multiexp( + srs.g_positive_x_alpha[(srs.d - 3*n - 1)..].iter(), + wires.c.iter().rev() + .chain_ext(wires.b.iter().rev()) + .chain_ext(Some(E::Fr::zero()).iter()) + .chain_ext(wires.a.iter()), + ).into_affine(); + + transcript.commit_point(&r); + + let y: E::Fr = transcript.get_challenge_scalar(); + + let mut rx1 = wires.b; + rx1.extend(wires.c); + rx1.reverse(); + rx1.push(E::Fr::zero()); + rx1.extend(wires.a); + + let mut rxy = rx1.clone(); + let y_inv = y.inverse().unwrap(); // TODO + let mut tmp = y.pow(&[n as u64]); + + for rxy in rxy.iter_mut().rev() { + rxy.mul_assign(&tmp); + tmp.mul_assign(&y_inv); + } + + let (s_poly_negative, s_poly_positive) = { + let mut tmp = SxEval::new(y, n); + S::synthesize(&mut tmp, circuit).unwrap(); // TODO + + tmp.poly() + }; + + let mut rxy_prime = rxy.clone(); + { + rxy_prime.resize(4 * n + 1, E::Fr::zero()); + // Add s(x, y) + for (r, s) in rxy_prime[0..(2 * n)] + .iter_mut() + .rev() + .zip(s_poly_negative) + { + r.add_assign(&s); + } + for (r, s) in rxy_prime[(2 * n + 1)..].iter_mut().zip(s_poly_positive) { + r.add_assign(&s); + } + } + + let mut txy = multiply_polynomials::(rx1.clone(), rxy_prime); + txy[4 * n] = E::Fr::zero(); // -k(y) + + let t = multiexp( + srs.g_positive_x_alpha[0..(3 * n)] + .iter() + .chain_ext(srs.g_negative_x_alpha[0..(4 * n)].iter()), + txy[(4 * n + 1)..] + .iter() + .chain_ext(txy[0..4 * n].iter().rev()), + ).into_affine(); + + transcript.commit_point(&t); + + let z: E::Fr = transcript.get_challenge_scalar(); + let z_inv = z.inverse().unwrap(); // TODO + + // TODO: use the faster way to evaluate the polynomials + let mut rz = E::Fr::zero(); + { + let mut tmp = z.pow(&[n as u64]); + + for coeff in rx1.iter().rev() { + let mut coeff = *coeff; + coeff.mul_assign(&tmp); + rz.add_assign(&coeff); + tmp.mul_assign(&z_inv); + } + } + + let mut rzy = E::Fr::zero(); + { + let mut tmp = z.pow(&[n as u64]); + + for mut coeff in rxy.into_iter().rev() { + coeff.mul_assign(&tmp); + rzy.add_assign(&coeff); + tmp.mul_assign(&z_inv); + } + } + + transcript.commit_scalar(&rz); + transcript.commit_scalar(&rzy); + + let r1: E::Fr = transcript.get_challenge_scalar(); + + let zy_opening = { + // r(X, 1) - r(z, y) + rx1[2 * n].sub_assign(&rzy); + + let mut point = y; + point.mul_assign(&z); + let poly = kate_divison( + rx1.iter(), + point, + ); + + let negative_poly = poly[0..2*n].iter().rev(); + let positive_poly = poly[2*n..].iter(); + multiexp( + srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( + srs.g_positive_x[0..positive_poly.len()].iter() + ), + negative_poly.chain_ext(positive_poly) + ).into_affine() + }; + + let z_opening = { + rx1[2 * n].add_assign(&rzy); // restore + + for (t, &r) in txy[2 * n..].iter_mut().zip(rx1.iter()) { + let mut r = r; + r.mul_assign(&r1); + t.add_assign(&r); + } + + let mut val = E::Fr::zero(); + { + assert_eq!(txy.len(), 3*n + 1 + 4*n); + let mut tmp = z.pow(&[(3*n) as u64]); + + for coeff in txy.iter().rev() { + let mut coeff = *coeff; + coeff.mul_assign(&tmp); + val.add_assign(&coeff); + tmp.mul_assign(&z_inv); + } + } + + txy[4 * n].sub_assign(&val); + + let poly = kate_divison( + txy.iter(), + z, + ); + + let negative_poly = poly[0..4*n].iter().rev(); + let positive_poly = poly[4*n..].iter(); + multiexp( + srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( + srs.g_positive_x[0..positive_poly.len()].iter() + ), + negative_poly.chain_ext(positive_poly) + ).into_affine() + }; + + Ok(Proof { + r, rz, rzy, t, z_opening, zy_opening + }) +} + +#[test] +fn my_fun_circuit_test() { + use ff::PrimeField; + use pairing::bls12_381::{Bls12, Fr}; + use super::*; + use crate::sonic::cs::{Basic, ConstraintSystem, LinearCombination}; + + struct MyCircuit; + + impl Circuit for MyCircuit { + fn synthesize>(&self, cs: &mut CS) -> Result<(), SynthesisError> { + let (a, b, _) = cs.multiply(|| { + Ok(( + E::Fr::from_str("10").unwrap(), + E::Fr::from_str("20").unwrap(), + E::Fr::from_str("200").unwrap(), + )) + })?; + + cs.enforce_zero(LinearCombination::from(a) + a - b); + + //let multiplier = cs.alloc_input(|| Ok(E::Fr::from_str("20").unwrap()))?; + + //cs.enforce_zero(LinearCombination::from(b) - multiplier); + + Ok(()) + } + } + + let srs = SRS::::new( + 20, + Fr::from_str("22222").unwrap(), + Fr::from_str("33333333").unwrap(), + ); + let proof = create_proof::(&MyCircuit, &srs).unwrap(); + + use std::time::{Instant}; + let start = Instant::now(); + let mut batch = MultiVerifier::::new(MyCircuit, &srs).unwrap(); + + for _ in 0..1 { + batch.add_proof(&proof, &[/*Fr::from_str("20").unwrap()*/], |_, _| None); + } + + assert!(batch.check_all()); + + let elapsed = start.elapsed(); + println!("time to verify: {:?}", elapsed); +} diff --git a/src/sonic/helped/verifier.rs b/src/sonic/helped/verifier.rs new file mode 100644 index 0000000..9b68d17 --- /dev/null +++ b/src/sonic/helped/verifier.rs @@ -0,0 +1,463 @@ +use ff::{Field}; +use pairing::{Engine, CurveProjective}; +use std::marker::PhantomData; + +use super::{Proof, SxyAdvice}; +use super::batch::Batch; +use super::poly::{SxEval, SyEval}; + +use crate::SynthesisError; + +use crate::sonic::transcript::{Transcript, TranscriptProtocol}; +use crate::sonic::util::*; +use crate::sonic::cs::{Backend, SynthesisDriver}; +use crate::sonic::cs::{Circuit, Variable, Coeff}; +use crate::sonic::srs::SRS; + +pub struct MultiVerifier, S: SynthesisDriver> { + circuit: C, + batch: Batch, + k_map: Vec, + n: usize, + q: usize, + _marker: PhantomData<(E, S)> +} + +impl, S: SynthesisDriver> MultiVerifier { + pub fn new(circuit: C, srs: &SRS) -> Result { + struct Preprocess { + k_map: Vec, + n: usize, + q: usize, + _marker: PhantomData + } + + impl<'a, E: Engine> Backend for &'a mut Preprocess { + fn new_k_power(&mut self, index: usize) { + self.k_map.push(index); + } + + fn new_multiplication_gate(&mut self) { + self.n += 1; + } + + fn new_linear_constraint(&mut self) { + self.q += 1; + } + } + + let mut preprocess = Preprocess { k_map: vec![], n: 0, q: 0, _marker: PhantomData }; + + S::synthesize(&mut preprocess, &circuit)?; + + Ok(MultiVerifier { + circuit, + batch: Batch::new(srs, preprocess.n), + k_map: preprocess.k_map, + n: preprocess.n, + q: preprocess.q, + _marker: PhantomData + }) + } + + pub fn add_aggregate( + &mut self, + proofs: &[(Proof, SxyAdvice)], + aggregate: &Aggregate, + ) + { + let mut transcript = Transcript::new(&[]); + let mut y_values: Vec = Vec::with_capacity(proofs.len()); + for &(ref proof, ref sxyadvice) in proofs { + { + let mut transcript = Transcript::new(&[]); + transcript.commit_point(&proof.r); + y_values.push(transcript.get_challenge_scalar()); + } + + transcript.commit_point(&sxyadvice.s); + } + + let z: E::Fr = transcript.get_challenge_scalar(); + + transcript.commit_point(&aggregate.c); + + let w: E::Fr = transcript.get_challenge_scalar(); + + let szw = { + let mut tmp = SxEval::new(w, self.n); + S::synthesize(&mut tmp, &self.circuit).unwrap(); // TODO + + tmp.finalize(z) + }; + + { + // TODO: like everything else doing this, this isn't really random + let random: E::Fr; + let mut transcript = transcript.clone(); + random = transcript.get_challenge_scalar(); + + self.batch.add_opening(aggregate.opening, random, w); + self.batch.add_commitment(aggregate.c, random); + self.batch.add_opening_value(szw, random); + } + + for ((opening, value), &y) in aggregate.c_openings.iter().zip(y_values.iter()) { + let random: E::Fr; + let mut transcript = transcript.clone(); + random = transcript.get_challenge_scalar(); + + self.batch.add_opening(*opening, random, y); + self.batch.add_commitment(aggregate.c, random); + self.batch.add_opening_value(*value, random); + } + + let random: E::Fr; + { + let mut transcript = transcript.clone(); + random = transcript.get_challenge_scalar(); + } + + let mut expected_value = E::Fr::zero(); + for ((_, advice), c_opening) in proofs.iter().zip(aggregate.c_openings.iter()) { + let mut r: E::Fr = transcript.get_challenge_scalar(); + + // expected value of the later opening + { + let mut tmp = c_opening.1; + tmp.mul_assign(&r); + expected_value.add_assign(&tmp); + } + + r.mul_assign(&random); + + self.batch.add_commitment(advice.s, r); + } + + self.batch.add_opening_value(expected_value, random); + self.batch.add_opening(aggregate.s_opening, random, z); + } + + pub fn add_proof_with_advice( + &mut self, + proof: &Proof, + inputs: &[E::Fr], + advice: &SxyAdvice, + ) + { + let mut z = None; + + self.add_proof(proof, inputs, |_z, _y| { + z = Some(_z); + Some(advice.szy) + }); + + let z = z.unwrap(); + + // We need to open up SxyAdvice.s at z using SxyAdvice.opening + let mut transcript = Transcript::new(&[]); + transcript.commit_point(&advice.opening); + transcript.commit_point(&advice.s); + transcript.commit_scalar(&advice.szy); + let random: E::Fr = transcript.get_challenge_scalar(); + + self.batch.add_opening(advice.opening, random, z); + self.batch.add_commitment(advice.s, random); + self.batch.add_opening_value(advice.szy, random); + } + + pub fn add_proof( + &mut self, + proof: &Proof, + inputs: &[E::Fr], + sxy: F + ) + where F: FnOnce(E::Fr, E::Fr) -> Option + { + let mut transcript = Transcript::new(&[]); + + transcript.commit_point(&proof.r); + + let y: E::Fr = transcript.get_challenge_scalar(); + + transcript.commit_point(&proof.t); + + let z: E::Fr = transcript.get_challenge_scalar(); + + transcript.commit_scalar(&proof.rz); + transcript.commit_scalar(&proof.rzy); + + let r1: E::Fr = transcript.get_challenge_scalar(); + + transcript.commit_point(&proof.z_opening); + transcript.commit_point(&proof.zy_opening); + + // First, the easy one. Let's open up proof.r at zy, using proof.zy_opening + // as the evidence and proof.rzy as the opening. + { + let random = transcript.get_challenge_scalar(); + let mut zy = z; + zy.mul_assign(&y); + self.batch.add_opening(proof.zy_opening, random, zy); + self.batch.add_commitment_max_n(proof.r, random); + self.batch.add_opening_value(proof.rzy, random); + } + + // Now we need to compute t(z, y) with what we have. Let's compute k(y). + let mut ky = E::Fr::zero(); + for (exp, input) in self.k_map.iter().zip(Some(E::Fr::one()).iter().chain(inputs.iter())) { + let mut term = y.pow(&[(*exp + self.n) as u64]); + term.mul_assign(input); + ky.add_assign(&term); + } + + // Compute s(z, y) + let szy = sxy(z, y).unwrap_or_else(|| { + let mut tmp = SxEval::new(y, self.n); + S::synthesize(&mut tmp, &self.circuit).unwrap(); // TODO + + tmp.finalize(z) + + // let mut tmp = SyEval::new(z, self.n, self.q); + // S::synthesize(&mut tmp, &self.circuit).unwrap(); // TODO + + // tmp.finalize(y) + }); + + // Finally, compute t(z, y) + let mut tzy = proof.rzy; + tzy.add_assign(&szy); + tzy.mul_assign(&proof.rz); + tzy.sub_assign(&ky); + + // We open these both at the same time by keeping their commitments + // linearly independent (using r1). + { + let mut random = transcript.get_challenge_scalar(); + + self.batch.add_opening(proof.z_opening, random, z); + self.batch.add_opening_value(tzy, random); + self.batch.add_commitment(proof.t, random); + + random.mul_assign(&r1); + + self.batch.add_opening_value(proof.rz, random); + self.batch.add_commitment_max_n(proof.r, random); + } + } + + pub fn check_all(self) -> bool { + self.batch.check_all() + } +} + +#[derive(Clone)] +pub struct Aggregate { + // Commitment to s(z, Y) + c: E::G1Affine, + // We have to open each of the S commitments to a random point `z` + s_opening: E::G1Affine, + // We have to open C to each constituent `y` + c_openings: Vec<(E::G1Affine, E::Fr)>, + // Then we have to finally open C + opening: E::G1Affine, +} + +pub fn create_aggregate, S: SynthesisDriver>( + circuit: &C, + inputs: &[(Proof, SxyAdvice)], + srs: &SRS, +) -> Aggregate +{ + // TODO: precompute this? + let (n, q) = { + struct CountN { + n: usize, + q: usize + } + + impl<'a, E: Engine> Backend for &'a mut CountN { + fn new_multiplication_gate(&mut self) { + self.n += 1; + } + + fn new_linear_constraint(&mut self) { + self.q += 1; + } + } + + let mut tmp = CountN{n:0,q:0}; + S::synthesize(&mut tmp, circuit).unwrap(); // TODO + + (tmp.n, tmp.q) + }; + + let mut transcript = Transcript::new(&[]); + let mut y_values: Vec = Vec::with_capacity(inputs.len()); + for &(ref proof, ref sxyadvice) in inputs { + { + let mut transcript = Transcript::new(&[]); + transcript.commit_point(&proof.r); + y_values.push(transcript.get_challenge_scalar()); + } + + transcript.commit_point(&sxyadvice.s); + } + + let z: E::Fr = transcript.get_challenge_scalar(); + + // Compute s(z, Y) + let (s_poly_negative, s_poly_positive) = { + let mut tmp = SyEval::new(z, n, q); + S::synthesize(&mut tmp, circuit).unwrap(); // TODO + + tmp.poly() + }; + + // Compute C = g^{s(z, x)} + let c = multiexp( + srs.g_positive_x_alpha[0..(n + q)] + .iter() + .chain_ext(srs.g_negative_x_alpha[0..n].iter()), + s_poly_positive.iter().chain_ext(s_poly_negative.iter()) + ).into_affine(); + + transcript.commit_point(&c); + + // Open C at w + let w: E::Fr = transcript.get_challenge_scalar(); + + let value = compute_value::(&w, &s_poly_positive, &s_poly_negative); + + let opening = { + let mut value = value; + value.negate(); + + let poly = kate_divison( + s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()), + w, + ); + + let negative_poly = poly[0..n].iter().rev(); + let positive_poly = poly[n..].iter(); + multiexp( + srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( + srs.g_positive_x[0..positive_poly.len()].iter() + ), + negative_poly.chain_ext(positive_poly) + ).into_affine() + }; + + // Let's open up C to every y. + fn compute_value(y: &E::Fr, poly_positive: &[E::Fr], poly_negative: &[E::Fr]) -> E::Fr { + let mut value = E::Fr::zero(); + + let yinv = y.inverse().unwrap(); // TODO + let mut tmp = yinv; + for &coeff in poly_negative { + let mut coeff = coeff; + coeff.mul_assign(&tmp); + value.add_assign(&coeff); + tmp.mul_assign(&yinv); + } + + let mut tmp = *y; + for &coeff in poly_positive { + let mut coeff = coeff; + coeff.mul_assign(&tmp); + value.add_assign(&coeff); + tmp.mul_assign(&y); + } + + value + } + + let mut c_openings = vec![]; + for y in &y_values { + let value = compute_value::(y, &s_poly_positive, &s_poly_negative); + + let opening = { + let mut value = value; + value.negate(); + + let poly = kate_divison( + s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()), + *y, + ); + + let negative_poly = poly[0..n].iter().rev(); + let positive_poly = poly[n..].iter(); + multiexp( + srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( + srs.g_positive_x[0..positive_poly.len()].iter() + ), + negative_poly.chain_ext(positive_poly) + ).into_affine() + }; + + c_openings.push((opening, value)); + } + + // Okay, great. Now we need to open up each S at the same point z to the same value. + // Since we're opening up all the S's at the same point, we create a bunch of random + // challenges instead and open up a random linear combination. + + let mut poly_negative = vec![E::Fr::zero(); n]; + let mut poly_positive = vec![E::Fr::zero(); 2*n]; + let mut expected_value = E::Fr::zero(); + + for (y, c_opening) in y_values.iter().zip(c_openings.iter()) { + // Compute s(X, y_i) + let (s_poly_negative, s_poly_positive) = { + let mut tmp = SxEval::new(*y, n); + S::synthesize(&mut tmp, circuit).unwrap(); // TODO + + tmp.poly() + }; + + let mut value = c_opening.1; + let r: E::Fr = transcript.get_challenge_scalar(); + value.mul_assign(&r); + expected_value.add_assign(&value); + + for (mut coeff, target) in s_poly_negative.into_iter().zip(poly_negative.iter_mut()) { + coeff.mul_assign(&r); + target.add_assign(&coeff); + } + + for (mut coeff, target) in s_poly_positive.into_iter().zip(poly_positive.iter_mut()) { + coeff.mul_assign(&r); + target.add_assign(&coeff); + } + } + + let s_opening = { + let mut value = expected_value; + value.negate(); + + let poly = kate_divison( + poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(poly_positive.iter()), + z, + ); + + let negative_poly = poly[0..n].iter().rev(); + let positive_poly = poly[n..].iter(); + multiexp( + srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( + srs.g_positive_x[0..positive_poly.len()].iter() + ), + negative_poly.chain_ext(positive_poly) + ).into_affine() + }; + + Aggregate { + // Commitment to s(z, Y) + c, + // We have to open each of the S commitments to a random point `z` + s_opening, + // We have to open C to each constituent `y` + c_openings, + // Then we have to finally open C + opening, + } +} \ No newline at end of file diff --git a/src/sonic/mod.rs b/src/sonic/mod.rs new file mode 100644 index 0000000..54e3965 --- /dev/null +++ b/src/sonic/mod.rs @@ -0,0 +1,15 @@ +extern crate ff; +extern crate pairing; + +pub use crate::{SynthesisError}; + +pub mod sonic; +pub mod srs; +pub mod util; +pub mod helped; +pub mod cs; + +mod transcript; + + + diff --git a/src/sonic/paper.rs b/src/sonic/paper.rs new file mode 100644 index 0000000..e0eed44 --- /dev/null +++ b/src/sonic/paper.rs @@ -0,0 +1,310 @@ + +#[test] +fn test_paper_results() { + use pairing::bls12_381::{Bls12, Fr}; + use std::time::{Instant}; + + let srs_x = Fr::from_str("23923").unwrap(); + let srs_alpha = Fr::from_str("23728792").unwrap(); + println!("making srs"); + let start = Instant::now(); + let srs = SRS::::dummy(830564, srs_x, srs_alpha); + println!("done in {:?}", start.elapsed()); + + struct PedersenHashPreimageCircuit<'a, E: sapling_crypto::jubjub::JubjubEngine + 'a> { + preimage: Vec>, + params: &'a E::Params, + } + + impl<'a, E: sapling_crypto::jubjub::JubjubEngine + 'a> Clone for PedersenHashPreimageCircuit<'a, E> { + fn clone(&self) -> Self { + PedersenHashPreimageCircuit { + preimage: self.preimage.clone(), + params: self.params + } + } + } + + impl<'a, E: sapling_crypto::jubjub::JubjubEngine> bellman::Circuit for PedersenHashPreimageCircuit<'a, E> { + fn synthesize>( + self, + cs: &mut CS + ) -> Result<(), bellman::SynthesisError> + { + //use bellman::ConstraintSystem; + use sapling_crypto::circuit::boolean::{AllocatedBit, Boolean}; + use sapling_crypto::circuit::pedersen_hash; + + let mut preimage = vec![]; + + for &bit in self.preimage.iter() { + preimage.push(Boolean::from(AllocatedBit::alloc(&mut* cs, bit)?)); + } + + pedersen_hash::pedersen_hash( + &mut* cs, pedersen_hash::Personalization::NoteCommitment, &preimage, self.params)?; + + Ok(()) + } + } + + #[derive(Clone)] + struct SHA256PreimageCircuit { + preimage: Vec>, + } + + impl bellman::Circuit for SHA256PreimageCircuit { + fn synthesize>( + self, + cs: &mut CS, + ) -> Result<(), bellman::SynthesisError> { + //use bellman::ConstraintSystem; + use sapling_crypto::circuit::boolean::{AllocatedBit, Boolean}; + use sapling_crypto::circuit::sha256::sha256_block_no_padding; + + let mut preimage = vec![]; + + for &bit in self.preimage.iter() { + preimage.push(Boolean::from(AllocatedBit::alloc(&mut *cs, bit)?)); + } + + sha256_block_no_padding(&mut *cs, &preimage)?; + sha256_block_no_padding(&mut *cs, &preimage)?; + sha256_block_no_padding(&mut *cs, &preimage)?; + // sha256_block_no_padding(&mut *cs, &preimage)?; + + Ok(()) + } + } + + { + use pairing::{CurveAffine}; + use pairing::bls12_381::{G1Affine, G2Affine}; + let a = G1Affine::one(); + let b = G2Affine::one(); + let c = G1Affine::one(); + + let alpha = G1Affine::one(); + let beta = G2Affine::one(); + let iv = G1Affine::one(); + let gamma = G2Affine::one().prepare(); + let delta = G2Affine::one().prepare(); + + let alphabeta = ::pairing(alpha, beta); + + println!("verifying an idealized groth16 proof"); + let start = Instant::now(); + assert!(::final_exponentiation( + &::miller_loop([ + (&a.prepare(), &b.prepare()), + (&iv.prepare(), &gamma), + (&c.prepare(), &delta), + ].into_iter()) + ).unwrap() != alphabeta); + println!("done in {:?}", start.elapsed()); + } + + { + use sonic::util::multiexp; + use pairing::{CurveAffine}; + use pairing::bls12_381::{G1Affine, G2Affine}; + // e([\alpha G], [\beta H]) = e(A, B) e(IV, [\gamma] H) e(C, [\delta] H) + let a = G1Affine::one(); + let b = G2Affine::one(); + let c = vec![G1Affine::one(); 100]; + let mut tmp = Fr::one(); + tmp.double(); + tmp = tmp.inverse().unwrap(); + let cscalars = (0..100).map(|_| {tmp.square(); tmp}).collect::>(); + + let alpha = G1Affine::one(); + let beta = G2Affine::one(); + let iv = G1Affine::one(); + let gamma = G2Affine::one().prepare(); + let delta = G2Affine::one().prepare(); + + let alphabeta = ::pairing(alpha, beta); + + println!("verifying 100 idealized groth16 proofs"); + let start = Instant::now(); + let c = multiexp( + c.iter(), + cscalars.iter(), + ).into_affine(); + assert!(::final_exponentiation( + &::miller_loop([ + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&a.prepare(), &b.prepare()), + (&iv.prepare(), &gamma), + (&c.prepare(), &delta), + ].into_iter()) + ).unwrap() != alphabeta); + println!("done in {:?}", start.elapsed()); + } + + { + let samples: usize = 100; + + const NUM_BITS: usize = 384; + + let params = sapling_crypto::jubjub::JubjubBls12::new(); + let circuit = PedersenHashPreimageCircuit { + preimage: vec![Some(true); NUM_BITS], + params: ¶ms + }; + + println!("creating proof"); + let start = Instant::now(); + let proof = create_proof::(&AdaptorCircuit(circuit.clone()), &srs).unwrap(); + println!("done in {:?}", start.elapsed()); + + println!("creating advice"); + let start = Instant::now(); + let advice = create_advice::(&AdaptorCircuit(circuit.clone()), &proof, &srs); + println!("done in {:?}", start.elapsed()); + + println!("creating aggregate for {} proofs", samples); + let start = Instant::now(); + let proofs: Vec<_> = (0..samples).map(|_| (proof.clone(), advice.clone())).collect(); + let aggregate = create_aggregate::(&AdaptorCircuit(circuit.clone()), &proofs, &srs); + println!("done in {:?}", start.elapsed()); + + { + let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); + println!("verifying 1 proof without advice"); + let start = Instant::now(); + { + for _ in 0..1 { + verifier.add_proof(&proof, &[], |_, _| None); + } + assert_eq!(verifier.check_all(), true); // TODO + } + println!("done in {:?}", start.elapsed()); + } + + { + let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); + println!("verifying {} proofs without advice", samples); + let start = Instant::now(); + { + for _ in 0..samples { + verifier.add_proof(&proof, &[], |_, _| None); + } + assert_eq!(verifier.check_all(), true); // TODO + } + println!("done in {:?}", start.elapsed()); + } + + { + let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); + println!("verifying 100 proofs with advice"); + let start = Instant::now(); + { + for (ref proof, ref advice) in &proofs { + verifier.add_proof_with_advice(proof, &[], advice); + } + verifier.add_aggregate(&proofs, &aggregate); + assert_eq!(verifier.check_all(), true); // TODO + } + println!("done in {:?}", start.elapsed()); + } + } +} diff --git a/src/sonic/sonic/adaptor.rs b/src/sonic/sonic/adaptor.rs new file mode 100644 index 0000000..6bed160 --- /dev/null +++ b/src/sonic/sonic/adaptor.rs @@ -0,0 +1,166 @@ +extern crate ff; +extern crate pairing; +extern crate rand; + +use ff::{Field, PrimeField}; +use pairing::{Engine, CurveProjective}; + +// this one is for all external interfaces +// use crate::{LinearCombination, ConstraintSystem, Circuit, Variable}; + +use crate::SynthesisError; + +use crate::sonic::srs::SRS; +use crate::sonic::cs::LinearCombination as SonicLinearCombination; +use crate::sonic::cs::Circuit as SonicCircuit; +use crate::sonic::cs::ConstraintSystem as SonicConstraintSystem; +use crate::sonic::cs::Variable as SonicVariable; +use crate::sonic::cs::Coeff; +// use crate::sonic::cs::synthesis::*; +use std::marker::PhantomData; + +struct Adaptor<'a, E: Engine, CS: SonicConstraintSystem + 'a> { + cs: &'a mut CS, + _marker: PhantomData, +} + +impl<'a, E: Engine, CS: SonicConstraintSystem + 'a> crate::ConstraintSystem + for Adaptor<'a, E, CS> +{ + type Root = Self; + + // this is an important change + fn one() -> crate::Variable { + crate::Variable::new_unchecked(crate::Index::Input(1)) + } + + fn alloc(&mut self, _: A, f: F) -> Result + where + F: FnOnce() -> Result, + A: FnOnce() -> AR, + AR: Into, + { + let var = self.cs.alloc(|| { + f().map_err(|_| crate::SynthesisError::AssignmentMissing) + }).map_err(|_| crate::SynthesisError::AssignmentMissing)?; + + Ok(match var { + SonicVariable::A(index) => crate::Variable::new_unchecked(crate::Index::Input(index)), + SonicVariable::B(index) => crate::Variable::new_unchecked(crate::Index::Aux(index)), + _ => unreachable!(), + }) + } + + fn alloc_input( + &mut self, + _: A, + f: F, + ) -> Result + where + F: FnOnce() -> Result, + A: FnOnce() -> AR, + AR: Into, + { + let var = self.cs.alloc_input(|| { + f().map_err(|_| crate::SynthesisError::AssignmentMissing) + }).map_err(|_| crate::SynthesisError::AssignmentMissing)?; + + Ok(match var { + SonicVariable::A(index) => crate::Variable::new_unchecked(crate::Index::Input(index)), + SonicVariable::B(index) => crate::Variable::new_unchecked(crate::Index::Aux(index)), + _ => unreachable!(), + }) + } + + fn enforce(&mut self, _: A, a: LA, b: LB, c: LC) + where + A: FnOnce() -> AR, + AR: Into, + LA: FnOnce(crate::LinearCombination) -> crate::LinearCombination, + LB: FnOnce(crate::LinearCombination) -> crate::LinearCombination, + LC: FnOnce(crate::LinearCombination) -> crate::LinearCombination, + { + fn convert(lc: crate::LinearCombination) -> SonicLinearCombination { + let mut ret = SonicLinearCombination::zero(); + + for &(v, coeff) in lc.as_ref().iter() { + let var = match v.get_unchecked() { + crate::Index::Input(i) => SonicVariable::A(i), + crate::Index::Aux(i) => SonicVariable::B(i), + }; + + ret = ret + (Coeff::Full(coeff), var); + } + + ret + } + + fn eval>( + lc: &SonicLinearCombination, + cs: &CS, + ) -> Option { + let mut ret = E::Fr::zero(); + + for &(v, coeff) in lc.as_ref().iter() { + let mut tmp = match cs.get_value(v) { + Ok(tmp) => tmp, + Err(_) => return None, + }; + coeff.multiply(&mut tmp); + ret.add_assign(&tmp); + } + + Some(ret) + } + + let a_lc = convert(a(crate::LinearCombination::zero())); + let a_value = eval(&a_lc, &*self.cs); + let b_lc = convert(b(crate::LinearCombination::zero())); + let b_value = eval(&b_lc, &*self.cs); + let c_lc = convert(c(crate::LinearCombination::zero())); + let c_value = eval(&c_lc, &*self.cs); + + let (a, b, c) = self + .cs + .multiply(|| Ok((a_value.unwrap(), b_value.unwrap(), c_value.unwrap()))) + .unwrap(); + + self.cs.enforce_zero(a_lc - a); + self.cs.enforce_zero(b_lc - b); + self.cs.enforce_zero(c_lc - c); + } + + 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 struct AdaptorCircuit(pub T); + +impl<'a, E: Engine, C: crate::Circuit + Clone> SonicCircuit for AdaptorCircuit { + fn synthesize>(&self, cs: &mut CS) -> Result<(), SynthesisError> { + let mut adaptor = Adaptor { + cs: cs, + _marker: PhantomData, + }; + + match self.0.clone().synthesize(&mut adaptor) { + Err(_) => return Err(SynthesisError::AssignmentMissing), + Ok(_) => {} + }; + + Ok(()) + } +} \ No newline at end of file diff --git a/src/sonic/sonic/mod.rs b/src/sonic/sonic/mod.rs new file mode 100644 index 0000000..fc4b7a7 --- /dev/null +++ b/src/sonic/sonic/mod.rs @@ -0,0 +1,6 @@ +extern crate ff; +extern crate pairing; + +mod adaptor; + +pub use self::adaptor::{AdaptorCircuit}; \ No newline at end of file diff --git a/src/sonic/srs/mod.rs b/src/sonic/srs/mod.rs new file mode 100644 index 0000000..d2eae6e --- /dev/null +++ b/src/sonic/srs/mod.rs @@ -0,0 +1,5 @@ +extern crate ff; +extern crate pairing; + +mod srs; +pub use self::srs::SRS; \ No newline at end of file diff --git a/src/sonic/srs/srs.rs b/src/sonic/srs/srs.rs new file mode 100644 index 0000000..1583d84 --- /dev/null +++ b/src/sonic/srs/srs.rs @@ -0,0 +1,95 @@ +use ff::{Field, PrimeField}; +use pairing::{CurveAffine, CurveProjective, Engine, Wnaf}; + +pub struct SRS { + pub d: usize, + + // g^{x^0}, g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}} + pub g_negative_x: Vec, + + // g^{x^0}, g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}} + pub g_positive_x: Vec, + + // g^{x^0}, g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}} + pub h_negative_x: Vec, + + // g^{x^0}, g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}} + pub h_positive_x: Vec, + + // alpha*(g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}}) + pub g_negative_x_alpha: Vec, + + // alpha*(g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}}) + pub g_positive_x_alpha: Vec, + + // alpha*(h^{x^0}, h^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}}) + pub h_negative_x_alpha: Vec, + + // alpha*(h^{x^0}, g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}}) + pub h_positive_x_alpha: Vec, +} + +impl SRS { + pub fn dummy(d: usize, _: E::Fr, _: E::Fr) -> Self { + SRS { + d: d, + g_negative_x: vec![E::G1Affine::one(); d + 1], + g_positive_x: vec![E::G1Affine::one(); d + 1], + + h_negative_x: vec![E::G2Affine::one(); d + 1], + h_positive_x: vec![E::G2Affine::one(); d + 1], + + g_negative_x_alpha: vec![E::G1Affine::one(); d], + g_positive_x_alpha: vec![E::G1Affine::one(); d], + + h_negative_x_alpha: vec![E::G2Affine::one(); d + 1], + h_positive_x_alpha: vec![E::G2Affine::one(); d + 1], + } + } + + pub fn new(d: usize, x: E::Fr, alpha: E::Fr) -> Self { + let mut g1 = Wnaf::new(); + let mut g1 = g1.base(E::G1::one(), d * 4); + let mut g2 = Wnaf::new(); + let mut g2 = g2.base(E::G2::one(), d * 4); + + fn table( + mut cur: C::Scalar, + step: C::Scalar, + num: usize, + table: &mut Wnaf>, + ) -> Vec { + let mut v = vec![]; + for _ in 0..num { + v.push(table.scalar(cur.into_repr())); + cur.mul_assign(&step); + } + C::Projective::batch_normalization(&mut v); + let v = v.into_iter().map(|e| e.into_affine()).collect(); + v + } + + let x_inv = x.inverse().unwrap(); + + let mut x_alpha = x; + x_alpha.mul_assign(&alpha); + + let mut inv_x_alpha = x_inv; + inv_x_alpha.mul_assign(&alpha); + + SRS { + d: d, + g_negative_x: table(E::Fr::one(), x_inv, d + 1, &mut g1), + g_positive_x: table(E::Fr::one(), x, d + 1, &mut g1), + + h_negative_x: table(E::Fr::one(), x_inv, d + 1, &mut g2), + h_positive_x: table(E::Fr::one(), x, d + 1, &mut g2), + + g_negative_x_alpha: table(inv_x_alpha, x_inv, d, &mut g1), + g_positive_x_alpha: table(x_alpha, x, d, &mut g1), + + h_negative_x_alpha: table(alpha, x_inv, d + 1, &mut g2), + h_positive_x_alpha: table(alpha, x, d + 1, &mut g2), + } + } +} diff --git a/src/sonic/transcript/mod.rs b/src/sonic/transcript/mod.rs new file mode 100644 index 0000000..d3d4d07 --- /dev/null +++ b/src/sonic/transcript/mod.rs @@ -0,0 +1,69 @@ +extern crate ff; +extern crate pairing; + +use ff::{Field, PrimeField, PrimeFieldRepr}; +use pairing::{CurveAffine, CurveProjective, Engine}; +use std::io; + +// transcript is mocked for now +#[derive(Clone)] +pub struct Transcript { + buffer: Vec +} + +impl Transcript { + pub fn new(personalization: &[u8]) -> Self { + Self { + buffer: vec![] + } + } + + pub fn commit_bytes(&mut self, personalization: &[u8], bytes: &[u8]) { + + } + + pub fn challenge_bytes(&mut self, personalization: &[u8], bytes: &[u8]) { + + } +} + +pub trait TranscriptProtocol { + fn commit_point(&mut self, point: &G); + fn commit_scalar(&mut self, scalar: &F); + fn get_challenge_scalar(&mut self) -> F; +} + +impl TranscriptProtocol for Transcript { + fn commit_point(&mut self, point: &G) { + self.commit_bytes(b"point", point.into_compressed().as_ref()); + } + + fn commit_scalar(&mut self, scalar: &F) { + let mut v = vec![]; + scalar.into_repr().write_le(&mut v).unwrap(); + + self.commit_bytes(b"scalar", &v); + } + + fn get_challenge_scalar(&mut self) -> F { + return F::one(); + // loop { + // let mut repr: F::Repr = Default::default(); + // repr.read_be(TranscriptReader(self)).unwrap(); + + // if let Ok(result) = F::from_repr(repr) { + // return result; + // } + // } + } +} + +struct TranscriptReader<'a>(&'a mut Transcript); + +impl<'a> io::Read for TranscriptReader<'a> { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.challenge_bytes(b"read", buf); + + Ok(buf.len()) + } +} \ No newline at end of file diff --git a/src/sonic/util.rs b/src/sonic/util.rs new file mode 100644 index 0000000..6dbd47c --- /dev/null +++ b/src/sonic/util.rs @@ -0,0 +1,337 @@ +use crate::SynthesisError; +use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine}; +use pairing::{CurveAffine, CurveProjective, Engine}; + +pub trait ChainExt: Iterator { + fn chain_ext(self, other: U) -> Chain + where + Self: Sized, + U: IntoIterator, + { + Chain { + t: self, + u: other.into_iter(), + } + } +} + +impl ChainExt for I {} + +#[derive(Clone)] +pub struct Chain { + t: T, + u: U, +} + +impl Iterator for Chain +where + T: Iterator, + U: Iterator, +{ + type Item = T::Item; + + fn next(&mut self) -> Option { + match self.t.next() { + Some(v) => Some(v), + None => match self.u.next() { + Some(v) => Some(v), + None => None, + }, + } + } +} + +impl ExactSizeIterator for Chain +where + T: Iterator, + U: Iterator, + T: ExactSizeIterator, + U: ExactSizeIterator, +{ + fn len(&self) -> usize { + self.t.len() + self.u.len() + } +} + +impl DoubleEndedIterator for Chain +where + T: Iterator, + U: Iterator, + T: DoubleEndedIterator, + U: DoubleEndedIterator, +{ + fn next_back(&mut self) -> Option { + match self.u.next_back() { + Some(v) => Some(v), + None => match self.t.next_back() { + Some(v) => Some(v), + None => None, + }, + } + } +} + +pub fn multiexp< + 'a, + G: CurveAffine, + IB: IntoIterator, + IS: IntoIterator, +>( + g: IB, + s: IS, +) -> G::Projective +where + IB::IntoIter: ExactSizeIterator + Clone, + IS::IntoIter: ExactSizeIterator, +{ + let g = g.into_iter(); + let s = s.into_iter(); + assert_eq!(g.len(), s.len()); + + let c = if s.len() < 32 { + 3u32 + } else { + (f64::from(s.len() as u32)).ln().ceil() as u32 + }; + + // Convert all of the scalars into representations + let mut s = s.map(|s| s.into_repr()).collect::>(); + + let mut windows = vec![]; + let mut buckets = vec![]; + + let mask = (1u64 << c) - 1u64; + let mut cur = 0; + let num_bits = ::Fr::NUM_BITS; + while cur <= num_bits { + let mut acc = G::Projective::zero(); + + buckets.truncate(0); + buckets.resize((1 << c) - 1, G::Projective::zero()); + + let g = g.clone(); + + for (s, g) in s.iter_mut().zip(g) { + let index = (s.as_ref()[0] & mask) as usize; + + if index != 0 { + buckets[index - 1].add_assign_mixed(g); + } + + s.shr(c as u32); + } + + let mut running_sum = G::Projective::zero(); + for exp in buckets.iter().rev() { + running_sum.add_assign(exp); + acc.add_assign(&running_sum); + } + + windows.push(acc); + + cur += c; + } + + let mut acc = G::Projective::zero(); + + for window in windows.into_iter().rev() { + for _ in 0..c { + acc.double(); + } + + acc.add_assign(&window); + } + + acc +} + +/// Divides polynomial `a` in `x` by `x - b` with +/// no remainder. +pub fn kate_divison<'a, F: Field, I: IntoIterator>(a: I, mut b: F) -> Vec +where + I::IntoIter: DoubleEndedIterator + ExactSizeIterator, +{ + b.negate(); + let a = a.into_iter(); + + let mut q = vec![F::zero(); a.len() - 1]; + + let mut tmp = F::zero(); + for (q, r) in q.iter_mut().rev().zip(a.rev()) { + let mut lead_coeff = *r; + lead_coeff.sub_assign(&tmp); + *q = lead_coeff; + tmp = lead_coeff; + tmp.mul_assign(&b); + } + + q +} + +#[test] +fn laurent_division() { + use ff::PrimeField; + use pairing::bls12_381::{Fr}; + + let mut poly = vec![ + Fr::from_str("328947234").unwrap(), + Fr::from_str("3545623451111").unwrap(), + Fr::from_str("112").unwrap(), + Fr::from_str("55555").unwrap(), + Fr::from_str("1235685").unwrap(), + ]; + + fn eval(poly: &[Fr], point: Fr) -> Fr { + let point_inv = point.inverse().unwrap(); + + let mut acc = Fr::zero(); + let mut tmp = Fr::one(); + for p in &poly[2..] { + let mut t = *p; + t.mul_assign(&tmp); + acc.add_assign(&t); + tmp.mul_assign(&point); + } + let mut tmp = point_inv; + for p in poly[0..2].iter().rev() { + let mut t = *p; + t.mul_assign(&tmp); + acc.add_assign(&t); + tmp.mul_assign(&point_inv); + } + + acc + } + + let x = Fr::from_str("23").unwrap(); + let z = Fr::from_str("2000").unwrap(); + + let p_at_x = eval(&poly, x); + let p_at_z = eval(&poly, z); + + // poly = poly(X) - poly(z) + poly[2].sub_assign(&p_at_z); + + let quotient_poly = kate_divison(&poly, z); + + let quotient = eval("ient_poly, x); + + // check that + // quotient * (x - z) = p_at_x - p_at_z + + let mut lhs = x; + lhs.sub_assign(&z); + lhs.mul_assign("ient); + + let mut rhs = p_at_x; + rhs.sub_assign(&p_at_z); + + assert_eq!(lhs, rhs); +} + +pub fn multiply_polynomials(mut a: Vec, mut b: Vec) -> Vec { + let result_len = a.len() + b.len() - 1; + + // Compute the size of our evaluation domain + let mut m = 1; + let mut exp = 0; + while m < result_len { + m *= 2; + exp += 1; + + // The pairing-friendly curve may not be able to support + // large enough (radix2) evaluation domains. + if exp >= E::Fr::S { + panic!("polynomial too large") + } + } + + // Compute omega, the 2^exp primitive root of unity + let mut omega = E::Fr::root_of_unity(); + for _ in exp..E::Fr::S { + omega.square(); + } + + // Extend with zeroes + a.resize(m, E::Fr::zero()); + b.resize(m, E::Fr::zero()); + + serial_fft::(&mut a[..], &omega, exp); + serial_fft::(&mut b[..], &omega, exp); + + for (a, b) in a.iter_mut().zip(b.iter()) { + a.mul_assign(b); + } + + serial_fft::(&mut a[..], &omega.inverse().unwrap(), exp); + + a.truncate(result_len); + + let minv = E::Fr::from_str(&format!("{}", m)) + .unwrap() + .inverse() + .unwrap(); + + for a in a.iter_mut() { + a.mul_assign(&minv); + } + + a +} + +fn serial_fft(a: &mut [E::Fr], omega: &E::Fr, log_n: u32) { + fn bitreverse(mut n: u32, l: u32) -> u32 { + let mut r = 0; + for _ in 0..l { + r = (r << 1) | (n & 1); + n >>= 1; + } + r + } + + let n = a.len() as u32; + assert_eq!(n, 1 << log_n); + + for k in 0..n { + let rk = bitreverse(k, log_n); + if k < rk { + a.swap(rk as usize, k as usize); + } + } + + let mut m = 1; + for _ in 0..log_n { + let w_m = omega.pow(&[(n / (2 * m)) as u64]); + + let mut k = 0; + while k < n { + let mut w = E::Fr::one(); + for j in 0..m { + let mut t = a[(k + j + m) as usize]; + t.mul_assign(&w); + let mut tmp = a[(k + j) as usize]; + tmp.sub_assign(&t); + a[(k + j + m) as usize] = tmp; + a[(k + j) as usize].add_assign(&t); + w.mul_assign(&w_m); + } + + k += 2 * m; + } + + m *= 2; + } +} + +pub trait OptionExt { + fn get(self) -> Result; +} + +impl OptionExt for Option { + fn get(self) -> Result { + match self { + Some(t) => Ok(t), + None => Err(SynthesisError::AssignmentMissing), + } + } +} diff --git a/tests/mimc.rs b/tests/mimc.rs index 3b24cb9..adf16e6 100644 --- a/tests/mimc.rs +++ b/tests/mimc.rs @@ -82,6 +82,7 @@ fn mimc( /// This is our demo circuit for proving knowledge of the /// preimage of a MiMC hash invocation. +#[derive(Clone)] struct MiMCDemo<'a, E: Engine> { xl: Option, xr: Option, @@ -342,3 +343,200 @@ fn test_mimc_bn256() { println!("Average verifying time: {:?} seconds", verifying_avg); } +/// This is our demo circuit for proving knowledge of the +/// preimage of a MiMC hash invocation. +#[derive(Clone)] +struct MiMCDemoNoInputs<'a, E: Engine> { + xl: Option, + xr: Option, + image: Option, + constants: &'a [E::Fr] +} + +/// Our demo circuit implements this `Circuit` trait which +/// is used during paramgen and proving in order to +/// synthesize the constraint system. +impl<'a, E: Engine> Circuit for MiMCDemoNoInputs<'a, E> { + fn synthesize>( + self, + cs: &mut CS + ) -> Result<(), SynthesisError> + { + assert_eq!(self.constants.len(), MIMC_ROUNDS); + + // Allocate the first component of the preimage. + let mut xl_value = self.xl; + let mut xl = cs.alloc(|| "preimage xl", || { + xl_value.ok_or(SynthesisError::AssignmentMissing) + })?; + + // Allocate the second component of the preimage. + let mut xr_value = self.xr; + let mut xr = cs.alloc(|| "preimage xr", || { + xr_value.ok_or(SynthesisError::AssignmentMissing) + })?; + + for i in 0..MIMC_ROUNDS { + // xL, xR := xR + (xL + Ci)^3, xL + let cs = &mut cs.namespace(|| format!("round {}", i)); + + // tmp = (xL + Ci)^2 + let mut tmp_value = xl_value.map(|mut e| { + e.add_assign(&self.constants[i]); + e.square(); + e + }); + let mut tmp = cs.alloc(|| "tmp", || { + tmp_value.ok_or(SynthesisError::AssignmentMissing) + })?; + + cs.enforce( + || "tmp = (xL + Ci)^2", + |lc| lc + xl + (self.constants[i], CS::one()), + |lc| lc + xl + (self.constants[i], CS::one()), + |lc| lc + tmp + ); + + // new_xL = xR + (xL + Ci)^3 + // new_xL = xR + tmp * (xL + Ci) + // new_xL - xR = tmp * (xL + Ci) + let mut new_xl_value = xl_value.map(|mut e| { + e.add_assign(&self.constants[i]); + e.mul_assign(&tmp_value.unwrap()); + e.add_assign(&xr_value.unwrap()); + e + }); + + let mut new_xl = if i == (MIMC_ROUNDS-1) { + // This is the last round, xL is our image and so + // we use the image + let image_value = self.image; + cs.alloc(|| "image", || { + image_value.ok_or(SynthesisError::AssignmentMissing) + })? + } else { + cs.alloc(|| "new_xl", || { + new_xl_value.ok_or(SynthesisError::AssignmentMissing) + })? + }; + + cs.enforce( + || "new_xL = xR + (xL + Ci)^3", + |lc| lc + tmp, + |lc| lc + xl + (self.constants[i], CS::one()), + |lc| lc + new_xl - xr + ); + + // xR = xL + xr = xl; + xr_value = xl_value; + + // xL = new_xL + xl = new_xl; + xl_value = new_xl_value; + } + + Ok(()) + } +} + +#[test] +fn test_sonic_mimc() { + use ff::{Field, PrimeField}; + use pairing::{Engine, CurveAffine, CurveProjective}; + use pairing::bls12_381::{Bls12, Fr}; + use std::time::{Instant}; + use bellman::sonic::srs::SRS; + + let srs_x = Fr::from_str("23923").unwrap(); + let srs_alpha = Fr::from_str("23728792").unwrap(); + println!("making srs"); + let start = Instant::now(); + let srs = SRS::::dummy(830564, srs_x, srs_alpha); + println!("done in {:?}", start.elapsed()); + + { + // This may not be cryptographically safe, use + // `OsRng` (for example) in production software. + let rng = &mut thread_rng(); + + // Generate the MiMC round constants + let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::>(); + let samples: usize = 100; + + const NUM_BITS: usize = 384; + + let xl = rng.gen(); + let xr = rng.gen(); + let image = mimc::(xl, xr, &constants); + + // Create an instance of our circuit (with the + // witness) + let circuit = MiMCDemoNoInputs { + xl: Some(xl), + xr: Some(xr), + image: Some(image), + constants: &constants + }; + + use bellman::sonic::cs::Basic; + use bellman::sonic::sonic::AdaptorCircuit; + use bellman::sonic::helped::{create_proof, create_advice, create_aggregate, MultiVerifier}; + + println!("creating proof"); + let start = Instant::now(); + let proof = create_proof::(&AdaptorCircuit(circuit.clone()), &srs).unwrap(); + println!("done in {:?}", start.elapsed()); + + println!("creating advice"); + let start = Instant::now(); + let advice = create_advice::(&AdaptorCircuit(circuit.clone()), &proof, &srs); + println!("done in {:?}", start.elapsed()); + + println!("creating aggregate for {} proofs", samples); + let start = Instant::now(); + let proofs: Vec<_> = (0..samples).map(|_| (proof.clone(), advice.clone())).collect(); + let aggregate = create_aggregate::(&AdaptorCircuit(circuit.clone()), &proofs, &srs); + println!("done in {:?}", start.elapsed()); + + { + let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); + println!("verifying 1 proof without advice"); + let start = Instant::now(); + { + for _ in 0..1 { + verifier.add_proof(&proof, &[], |_, _| None); + } + assert_eq!(verifier.check_all(), true); // TODO + } + println!("done in {:?}", start.elapsed()); + } + + { + let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); + println!("verifying {} proofs without advice", samples); + let start = Instant::now(); + { + for _ in 0..samples { + verifier.add_proof(&proof, &[], |_, _| None); + } + assert_eq!(verifier.check_all(), true); // TODO + } + println!("done in {:?}", start.elapsed()); + } + + { + let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); + println!("verifying 100 proofs with advice"); + let start = Instant::now(); + { + for (ref proof, ref advice) in &proofs { + verifier.add_proof_with_advice(proof, &[], advice); + } + verifier.add_aggregate(&proofs, &aggregate); + assert_eq!(verifier.check_all(), true); // TODO + } + println!("done in {:?}", start.elapsed()); + } + } +} \ No newline at end of file From e41e3624f730578b997f5a50f586a31c0f08320c Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Mon, 4 Feb 2019 14:01:44 +0300 Subject: [PATCH 03/42] make TODO plan --- src/sonic/README.md | 10 +++++++++- src/sonic/helped/verifier.rs | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/sonic/README.md b/src/sonic/README.md index 1e0c6c2..1f334e5 100644 --- a/src/sonic/README.md +++ b/src/sonic/README.md @@ -8,4 +8,12 @@ Initial SONIC proof system integration using the code from the [original impleme - Implementation of a memory constrained prover and helper - Smart-contract implementation of verifiers - Code cleanup -- Migration for smart-contract compatible transcripts \ No newline at end of file +- Migration for smart-contract compatible transcripts + +## TODO Plan + +- [ ] Parallelize using existing primitives +- [ ] Put public inputs into the account +- [ ] Basic Ethereum smart-contract +- [ ] Add blinding factors +- [ ] Implement unhelped version \ No newline at end of file diff --git a/src/sonic/helped/verifier.rs b/src/sonic/helped/verifier.rs index 9b68d17..35e1ae4 100644 --- a/src/sonic/helped/verifier.rs +++ b/src/sonic/helped/verifier.rs @@ -348,6 +348,7 @@ pub fn create_aggregate, S: SynthesisDriver>( ).into_affine() }; + // TODO: parallelize // Let's open up C to every y. fn compute_value(y: &E::Fr, poly_positive: &[E::Fr], poly_negative: &[E::Fr]) -> E::Fr { let mut value = E::Fr::zero(); @@ -431,6 +432,7 @@ pub fn create_aggregate, S: SynthesisDriver>( } } + // TODO: parallelize let s_opening = { let mut value = expected_value; value.negate(); From e51284e754b65c3e507ab17627048c29f2cc5b3d Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 5 Feb 2019 14:14:17 +0300 Subject: [PATCH 04/42] start real integration --- src/domain.rs | 58 +++++++++++++++++++++++-- src/sonic/README.md | 7 ++- src/sonic/util.rs | 85 +++++++++++++++++++++++++++++++++++++ tests/mimc.rs | 101 +++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 244 insertions(+), 7 deletions(-) diff --git a/src/domain.rs b/src/domain.rs index 313e561..d9327dd 100644 --- a/src/domain.rs +++ b/src/domain.rs @@ -97,6 +97,58 @@ impl> EvaluationDomain { }) } + // this one does expect coefficients to be smaller than `num_roots_of_unity/2` as we expect multiplication + pub fn from_coeffs_for_multiplication(mut coeffs: Vec, expected_power: usize) -> Result, SynthesisError> + { + use ff::PrimeField; + // Compute the size of our evaluation domain + + assert!(expected_power >= coeffs.len()); + + let coeffs_len = expected_power; + + // m is a size of domain where Z polynomial does NOT vanish + // in normal domain Z is in a form of (X-1)(X-2)...(X-N) + let mut m = 1; + let mut exp = 0; + let mut omega = E::Fr::root_of_unity(); + let max_degree = (1 << E::Fr::S) - 1; + + if coeffs_len > max_degree { + return Err(SynthesisError::PolynomialDegreeTooLarge) + } + + while m < coeffs_len { + m *= 2; + exp += 1; + + // The pairing-friendly curve may not be able to support + // large enough (radix2) evaluation domains. + if exp > E::Fr::S { + return Err(SynthesisError::PolynomialDegreeTooLarge) + } + } + + // If full domain is not needed - limit it, + // e.g. if (2^N)th power is not required, just double omega and get 2^(N-1)th + // Compute omega, the 2^exp primitive root of unity + for _ in exp..E::Fr::S { + omega.square(); + } + + // Extend the coeffs vector with zeroes if necessary + coeffs.resize(m, G::group_zero()); + + Ok(EvaluationDomain { + coeffs: coeffs, + exp: exp, + omega: omega, + omegainv: omega.inverse().unwrap(), + geninv: E::Fr::multiplicative_generator().inverse().unwrap(), + minv: E::Fr::from_str(&format!("{}", m)).unwrap().inverse().unwrap() + }) + } + pub fn fft(&mut self, worker: &Worker) { best_fft(&mut self.coeffs, worker, &self.omega, self.exp); @@ -275,7 +327,7 @@ impl Group for Scalar { } } -fn best_fft>(a: &mut [T], worker: &Worker, omega: &E::Fr, log_n: u32) +pub(crate) fn best_fft>(a: &mut [T], worker: &Worker, omega: &E::Fr, log_n: u32) { let log_cpus = worker.log_num_cpus(); @@ -286,7 +338,7 @@ fn best_fft>(a: &mut [T], worker: &Worker, omega: &E::Fr, } } -fn serial_fft>(a: &mut [T], omega: &E::Fr, log_n: u32) +pub(crate) fn serial_fft>(a: &mut [T], omega: &E::Fr, log_n: u32) { fn bitreverse(mut n: u32, l: u32) -> u32 { let mut r = 0; @@ -331,7 +383,7 @@ fn serial_fft>(a: &mut [T], omega: &E::Fr, log_n: u32) } } -fn parallel_fft>( +pub(crate) fn parallel_fft>( a: &mut [T], worker: &Worker, omega: &E::Fr, diff --git a/src/sonic/README.md b/src/sonic/README.md index 1f334e5..0f572db 100644 --- a/src/sonic/README.md +++ b/src/sonic/README.md @@ -12,8 +12,11 @@ Initial SONIC proof system integration using the code from the [original impleme ## TODO Plan -- [ ] Parallelize using existing primitives -- [ ] Put public inputs into the account +- [x] Test with public inputs +- [x] Test on BN256 +- [x] Parallelize using existing primitives +- [ ] Implement polynomial parallelized evaluation +- [ ] Make custom transcriptor that is easy to transform into the smart-contract - [ ] Basic Ethereum smart-contract - [ ] Add blinding factors - [ ] Implement unhelped version \ No newline at end of file diff --git a/src/sonic/util.rs b/src/sonic/util.rs index 6dbd47c..c087b5e 100644 --- a/src/sonic/util.rs +++ b/src/sonic/util.rs @@ -80,6 +80,43 @@ pub fn multiexp< g: IB, s: IS, ) -> G::Projective +where + IB::IntoIter: ExactSizeIterator + Clone, + IS::IntoIter: ExactSizeIterator, +{ + use std::sync::Arc; + use futures::Future; + use ff::PrimeFieldRepr; + use pairing::CurveAffine; + + use crate::multicore::Worker; + use crate::multiexp; + use crate::multiexp::FullDensity; + + let s: Arc::Repr>> = Arc::new(s.into_iter().map(|e| e.into_repr()).collect::>()); + let g: Arc> = Arc::new(g.into_iter().map(|e| *e).collect::>()); + + let pool = Worker::new(); + + let result = multiexp::multiexp( + &pool, + (g, 0), + FullDensity, + s + ).wait().unwrap(); + + result +} + +pub fn multiexp_serial< + 'a, + G: CurveAffine, + IB: IntoIterator, + IS: IntoIterator, +>( + g: IB, + s: IS, +) -> G::Projective where IB::IntoIter: ExactSizeIterator + Clone, IS::IntoIter: ExactSizeIterator, @@ -232,6 +269,34 @@ fn laurent_division() { pub fn multiply_polynomials(mut a: Vec, mut b: Vec) -> Vec { let result_len = a.len() + b.len() - 1; + use crate::multicore::Worker; + use crate::domain::{EvaluationDomain, Scalar}; + + let worker = Worker::new(); + let scalars_a: Vec> = a.into_iter().map(|e| Scalar::(e)).collect(); + let mut domain_a = EvaluationDomain::from_coeffs_for_multiplication(scalars_a, result_len).unwrap(); + + let scalars_b: Vec> = b.into_iter().map(|e| Scalar::(e)).collect(); + let mut domain_b = EvaluationDomain::from_coeffs_for_multiplication(scalars_b, result_len).unwrap(); + + domain_a.fft(&worker); + domain_b.fft(&worker); + + domain_a.mul_assign(&worker, &domain_b); + drop(domain_b); + + domain_a.ifft(&worker); + + let mut mul_result: Vec = domain_a.into_coeffs().iter().map(|e| e.0).collect(); + + mul_result.truncate(result_len); + + mul_result +} + +pub fn multiply_polynomials_serial(mut a: Vec, mut b: Vec) -> Vec { + let result_len = a.len() + b.len() - 1; + // Compute the size of our evaluation domain let mut m = 1; let mut exp = 0; @@ -335,3 +400,23 @@ impl OptionExt for Option { } } } + +#[test] + +fn test_mul() { + use rand::{self, Rand}; + use pairing::bls12_381::Bls12; + use pairing::bls12_381::Fr; + + const SAMPLES: usize = 100; + + let rng = &mut rand::thread_rng(); + let a = (0..SAMPLES).map(|_| Fr::rand(rng)).collect::>(); + let b = (0..SAMPLES).map(|_| Fr::rand(rng)).collect::>(); + + let serial_res = multiply_polynomials_serial::(a.clone(), b.clone()); + let parallel_res = multiply_polynomials::(a, b); + + assert_eq!(serial_res.len(), parallel_res.len()); + assert_eq!(serial_res, parallel_res); +} \ No newline at end of file diff --git a/tests/mimc.rs b/tests/mimc.rs index adf16e6..6501c07 100644 --- a/tests/mimc.rs +++ b/tests/mimc.rs @@ -464,8 +464,6 @@ fn test_sonic_mimc() { let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::>(); let samples: usize = 100; - const NUM_BITS: usize = 384; - let xl = rng.gen(); let xr = rng.gen(); let image = mimc::(xl, xr, &constants); @@ -539,4 +537,103 @@ fn test_sonic_mimc() { println!("done in {:?}", start.elapsed()); } } +} + +#[test] +fn test_inputs_into_sonic_mimc() { + use ff::{Field, PrimeField}; + use pairing::{Engine, CurveAffine, CurveProjective}; + use pairing::bn256::{Bn256, Fr}; + // use pairing::bls12_381::{Bls12, Fr}; + use std::time::{Instant}; + use bellman::sonic::srs::SRS; + + let srs_x = Fr::from_str("23923").unwrap(); + let srs_alpha = Fr::from_str("23728792").unwrap(); + println!("making srs"); + let start = Instant::now(); + let srs = SRS::::dummy(830564, srs_x, srs_alpha); + println!("done in {:?}", start.elapsed()); + + { + // This may not be cryptographically safe, use + // `OsRng` (for example) in production software. + let rng = &mut thread_rng(); + + // Generate the MiMC round constants + let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::>(); + let samples: usize = 100; + + let xl = rng.gen(); + let xr = rng.gen(); + let image = mimc::(xl, xr, &constants); + + // Create an instance of our circuit (with the + // witness) + let circuit = MiMCDemo { + xl: Some(xl), + xr: Some(xr), + constants: &constants + }; + + use bellman::sonic::cs::Basic; + use bellman::sonic::sonic::AdaptorCircuit; + use bellman::sonic::helped::{create_proof, create_advice, create_aggregate, MultiVerifier}; + + println!("creating proof"); + let start = Instant::now(); + let proof = create_proof::(&AdaptorCircuit(circuit.clone()), &srs).unwrap(); + println!("done in {:?}", start.elapsed()); + + println!("creating advice"); + let start = Instant::now(); + let advice = create_advice::(&AdaptorCircuit(circuit.clone()), &proof, &srs); + println!("done in {:?}", start.elapsed()); + + println!("creating aggregate for {} proofs", samples); + let start = Instant::now(); + let proofs: Vec<_> = (0..samples).map(|_| (proof.clone(), advice.clone())).collect(); + let aggregate = create_aggregate::(&AdaptorCircuit(circuit.clone()), &proofs, &srs); + println!("done in {:?}", start.elapsed()); + + { + let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); + println!("verifying 1 proof without advice"); + let start = Instant::now(); + { + for _ in 0..1 { + verifier.add_proof(&proof, &[image], |_, _| None); + } + assert_eq!(verifier.check_all(), true); // TODO + } + println!("done in {:?}", start.elapsed()); + } + + { + let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); + println!("verifying {} proofs without advice", samples); + let start = Instant::now(); + { + for _ in 0..samples { + verifier.add_proof(&proof, &[image], |_, _| None); + } + assert_eq!(verifier.check_all(), true); // TODO + } + println!("done in {:?}", start.elapsed()); + } + + { + let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); + println!("verifying 100 proofs with advice"); + let start = Instant::now(); + { + for (ref proof, ref advice) in &proofs { + verifier.add_proof_with_advice(proof, &[image], advice); + } + verifier.add_aggregate(&proofs, &aggregate); + assert_eq!(verifier.check_all(), true); // TODO + } + println!("done in {:?}", start.elapsed()); + } + } } \ No newline at end of file From 528f0623ef052e4320715d8f863682ef330992ab Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 5 Feb 2019 14:15:49 +0300 Subject: [PATCH 05/42] proper funciton name --- src/domain.rs | 6 +++--- src/sonic/util.rs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/domain.rs b/src/domain.rs index d9327dd..c3d011c 100644 --- a/src/domain.rs +++ b/src/domain.rs @@ -98,14 +98,14 @@ impl> EvaluationDomain { } // this one does expect coefficients to be smaller than `num_roots_of_unity/2` as we expect multiplication - pub fn from_coeffs_for_multiplication(mut coeffs: Vec, expected_power: usize) -> Result, SynthesisError> + pub fn from_coeffs_into_sized(mut coeffs: Vec, size: usize) -> Result, SynthesisError> { use ff::PrimeField; // Compute the size of our evaluation domain - assert!(expected_power >= coeffs.len()); + assert!(size >= coeffs.len()); - let coeffs_len = expected_power; + let coeffs_len = size; // m is a size of domain where Z polynomial does NOT vanish // in normal domain Z is in a form of (X-1)(X-2)...(X-N) diff --git a/src/sonic/util.rs b/src/sonic/util.rs index c087b5e..70982d2 100644 --- a/src/sonic/util.rs +++ b/src/sonic/util.rs @@ -266,7 +266,7 @@ fn laurent_division() { assert_eq!(lhs, rhs); } -pub fn multiply_polynomials(mut a: Vec, mut b: Vec) -> Vec { +pub fn multiply_polynomials(a: Vec, b: Vec) -> Vec { let result_len = a.len() + b.len() - 1; use crate::multicore::Worker; @@ -274,10 +274,10 @@ pub fn multiply_polynomials(mut a: Vec, mut b: Vec) -> let worker = Worker::new(); let scalars_a: Vec> = a.into_iter().map(|e| Scalar::(e)).collect(); - let mut domain_a = EvaluationDomain::from_coeffs_for_multiplication(scalars_a, result_len).unwrap(); + let mut domain_a = EvaluationDomain::from_coeffs_into_sized(scalars_a, result_len).unwrap(); let scalars_b: Vec> = b.into_iter().map(|e| Scalar::(e)).collect(); - let mut domain_b = EvaluationDomain::from_coeffs_for_multiplication(scalars_b, result_len).unwrap(); + let mut domain_b = EvaluationDomain::from_coeffs_into_sized(scalars_b, result_len).unwrap(); domain_a.fft(&worker); domain_b.fft(&worker); From 7239a60044354544b0c97dfca635227dc9436646 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 5 Feb 2019 17:23:26 +0300 Subject: [PATCH 06/42] make custom transcript that is implementable as SC --- Cargo.toml | 6 + src/sonic/helped/batch.rs | 36 ++++++ src/sonic/helped/helper.rs | 228 +++++++++++++++++++++++++++++++++ src/sonic/helped/mod.rs | 15 +-- src/sonic/helped/prover.rs | 211 ------------------------------ src/sonic/helped/verifier.rs | 214 +------------------------------ src/sonic/transcript/hasher.rs | 73 +++++++++++ src/sonic/transcript/mod.rs | 107 ++++++++++++---- 8 files changed, 434 insertions(+), 456 deletions(-) create mode 100644 src/sonic/helped/helper.rs create mode 100644 src/sonic/transcript/hasher.rs diff --git a/Cargo.toml b/Cargo.toml index 0a71a2d..5c62eac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,5 +22,11 @@ pairing = { git = 'https://github.com/matterinc/pairing' } byteorder = "1" ff = { git = 'https://github.com/matterinc/ff', features = ["derive"] } +tiny-keccak = "1.4.2" + +[dependencies.blake2-rfc] +git = "https://github.com/gtank/blake2-rfc" +rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9" + [features] default = [] diff --git a/src/sonic/helped/batch.rs b/src/sonic/helped/batch.rs index 81fe5f6..4ab4868 100644 --- a/src/sonic/helped/batch.rs +++ b/src/sonic/helped/batch.rs @@ -126,3 +126,39 @@ impl Batch { ])).unwrap() == E::Fqk::one() } } + + +pub struct VerificationKey { + alpha_x: E::G2Affine, + + alpha: E::G2Affine, + + neg_h: E::G2Affine, + + neg_x_n_minus_d: E::G2Affine + +} + +impl VerificationKey { + pub fn new(srs: &SRS, n: usize) -> Self { + Self { + alpha_x: srs.h_positive_x_alpha[1], + + alpha: srs.h_positive_x_alpha[0], + + neg_h: { + let mut tmp = srs.h_negative_x[0]; + tmp.negate(); + + tmp + }, + + neg_x_n_minus_d: { + let mut tmp = srs.h_negative_x[srs.d - n]; + tmp.negate(); + + tmp + }, + } + } +} \ No newline at end of file diff --git a/src/sonic/helped/helper.rs b/src/sonic/helped/helper.rs new file mode 100644 index 0000000..44d695a --- /dev/null +++ b/src/sonic/helped/helper.rs @@ -0,0 +1,228 @@ +use ff::{Field}; +use pairing::{Engine, CurveProjective}; +use std::marker::PhantomData; + +use super::{Proof, SxyAdvice}; +use super::batch::Batch; +use super::poly::{SxEval, SyEval}; + +use crate::SynthesisError; + +use crate::sonic::transcript::{Transcript, TranscriptProtocol}; +use crate::sonic::util::*; +use crate::sonic::cs::{Backend, SynthesisDriver}; +use crate::sonic::cs::{Circuit, Variable, Coeff}; +use crate::sonic::srs::SRS; + +#[derive(Clone)] +pub struct Aggregate { + // Commitment to s(z, Y) + pub c: E::G1Affine, + // We have to open each of the S commitments to a random point `z` + pub s_opening: E::G1Affine, + // We have to open C to each constituent `y` + pub c_openings: Vec<(E::G1Affine, E::Fr)>, + // Then we have to finally open C + pub opening: E::G1Affine, +} + +pub fn create_aggregate, S: SynthesisDriver>( + circuit: &C, + inputs: &[(Proof, SxyAdvice)], + srs: &SRS, +) -> Aggregate +{ + // TODO: precompute this? + let (n, q) = { + struct CountN { + n: usize, + q: usize + } + + impl<'a, E: Engine> Backend for &'a mut CountN { + fn new_multiplication_gate(&mut self) { + self.n += 1; + } + + fn new_linear_constraint(&mut self) { + self.q += 1; + } + } + + let mut tmp = CountN{n:0,q:0}; + S::synthesize(&mut tmp, circuit).unwrap(); // TODO + + (tmp.n, tmp.q) + }; + + let mut transcript = Transcript::new(&[]); + let mut y_values: Vec = Vec::with_capacity(inputs.len()); + for &(ref proof, ref sxyadvice) in inputs { + { + let mut transcript = Transcript::new(&[]); + transcript.commit_point(&proof.r); + y_values.push(transcript.get_challenge_scalar()); + } + + transcript.commit_point(&sxyadvice.s); + } + + let z: E::Fr = transcript.get_challenge_scalar(); + + // Compute s(z, Y) + let (s_poly_negative, s_poly_positive) = { + let mut tmp = SyEval::new(z, n, q); + S::synthesize(&mut tmp, circuit).unwrap(); // TODO + + tmp.poly() + }; + + // Compute C = g^{s(z, x)} + let c = multiexp( + srs.g_positive_x_alpha[0..(n + q)] + .iter() + .chain_ext(srs.g_negative_x_alpha[0..n].iter()), + s_poly_positive.iter().chain_ext(s_poly_negative.iter()) + ).into_affine(); + + transcript.commit_point(&c); + + // Open C at w + let w: E::Fr = transcript.get_challenge_scalar(); + + let value = compute_value::(&w, &s_poly_positive, &s_poly_negative); + + let opening = { + let mut value = value; + value.negate(); + + let poly = kate_divison( + s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()), + w, + ); + + let negative_poly = poly[0..n].iter().rev(); + let positive_poly = poly[n..].iter(); + multiexp( + srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( + srs.g_positive_x[0..positive_poly.len()].iter() + ), + negative_poly.chain_ext(positive_poly) + ).into_affine() + }; + + // TODO: parallelize + // Let's open up C to every y. + fn compute_value(y: &E::Fr, poly_positive: &[E::Fr], poly_negative: &[E::Fr]) -> E::Fr { + let mut value = E::Fr::zero(); + + let yinv = y.inverse().unwrap(); // TODO + let mut tmp = yinv; + for &coeff in poly_negative { + let mut coeff = coeff; + coeff.mul_assign(&tmp); + value.add_assign(&coeff); + tmp.mul_assign(&yinv); + } + + let mut tmp = *y; + for &coeff in poly_positive { + let mut coeff = coeff; + coeff.mul_assign(&tmp); + value.add_assign(&coeff); + tmp.mul_assign(&y); + } + + value + } + + let mut c_openings = vec![]; + for y in &y_values { + let value = compute_value::(y, &s_poly_positive, &s_poly_negative); + + let opening = { + let mut value = value; + value.negate(); + + let poly = kate_divison( + s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()), + *y, + ); + + let negative_poly = poly[0..n].iter().rev(); + let positive_poly = poly[n..].iter(); + multiexp( + srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( + srs.g_positive_x[0..positive_poly.len()].iter() + ), + negative_poly.chain_ext(positive_poly) + ).into_affine() + }; + + c_openings.push((opening, value)); + } + + // Okay, great. Now we need to open up each S at the same point z to the same value. + // Since we're opening up all the S's at the same point, we create a bunch of random + // challenges instead and open up a random linear combination. + + let mut poly_negative = vec![E::Fr::zero(); n]; + let mut poly_positive = vec![E::Fr::zero(); 2*n]; + let mut expected_value = E::Fr::zero(); + + for (y, c_opening) in y_values.iter().zip(c_openings.iter()) { + // Compute s(X, y_i) + let (s_poly_negative, s_poly_positive) = { + let mut tmp = SxEval::new(*y, n); + S::synthesize(&mut tmp, circuit).unwrap(); // TODO + + tmp.poly() + }; + + let mut value = c_opening.1; + let r: E::Fr = transcript.get_challenge_scalar(); + value.mul_assign(&r); + expected_value.add_assign(&value); + + for (mut coeff, target) in s_poly_negative.into_iter().zip(poly_negative.iter_mut()) { + coeff.mul_assign(&r); + target.add_assign(&coeff); + } + + for (mut coeff, target) in s_poly_positive.into_iter().zip(poly_positive.iter_mut()) { + coeff.mul_assign(&r); + target.add_assign(&coeff); + } + } + + // TODO: parallelize + let s_opening = { + let mut value = expected_value; + value.negate(); + + let poly = kate_divison( + poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(poly_positive.iter()), + z, + ); + + let negative_poly = poly[0..n].iter().rev(); + let positive_poly = poly[n..].iter(); + multiexp( + srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( + srs.g_positive_x[0..positive_poly.len()].iter() + ), + negative_poly.chain_ext(positive_poly) + ).into_affine() + }; + + Aggregate { + // Commitment to s(z, Y) + c, + // We have to open each of the S commitments to a random point `z` + s_opening, + // We have to open C to each constituent `y` + c_openings, + // Then we have to finally open C + opening, + } +} \ No newline at end of file diff --git a/src/sonic/helped/mod.rs b/src/sonic/helped/mod.rs index bf59409..51769f6 100644 --- a/src/sonic/helped/mod.rs +++ b/src/sonic/helped/mod.rs @@ -1,25 +1,20 @@ extern crate ff; extern crate pairing; -// extern crate merlin; use ff::{Field}; use pairing::{Engine, CurveProjective}; use std::marker::PhantomData; -// use merlin::{Transcript}; mod verifier; mod prover; mod batch; mod poly; +mod helper; -pub use self::verifier::{MultiVerifier, create_aggregate}; -pub use self::prover::{Aggregate, create_proof, create_advice}; - -// use super::super::util::*; -// pub use super::batch::Batch; -// use crate::synthesis::{Backend, SynthesisDriver}; -// use crate::{Circuit, SynthesisError, Variable, Coeff}; -// use crate::srs::SRS; +pub use self::batch::{Batch, VerificationKey}; +pub use self::helper::{Aggregate, create_aggregate}; +pub use self::verifier::{MultiVerifier}; +pub use self::prover::{create_proof, create_advice}; #[derive(Clone)] pub struct SxyAdvice { diff --git a/src/sonic/helped/prover.rs b/src/sonic/helped/prover.rs index 8b13dd2..65a29fa 100644 --- a/src/sonic/helped/prover.rs +++ b/src/sonic/helped/prover.rs @@ -14,217 +14,6 @@ use crate::sonic::cs::{Backend, SynthesisDriver}; use crate::sonic::cs::{Circuit, Variable, Coeff}; use crate::sonic::srs::SRS; -#[derive(Clone)] -pub struct Aggregate { - // Commitment to s(z, Y) - c: E::G1Affine, - // We have to open each of the S commitments to a random point `z` - s_opening: E::G1Affine, - // We have to open C to each constituent `y` - c_openings: Vec<(E::G1Affine, E::Fr)>, - // Then we have to finally open C - opening: E::G1Affine, -} - -pub fn create_aggregate, S: SynthesisDriver>( - circuit: &C, - inputs: &[(Proof, SxyAdvice)], - srs: &SRS, -) -> Aggregate -{ - // TODO: precompute this? - let (n, q) = { - struct CountN { - n: usize, - q: usize - } - - impl<'a, E: Engine> Backend for &'a mut CountN { - fn new_multiplication_gate(&mut self) { - self.n += 1; - } - - fn new_linear_constraint(&mut self) { - self.q += 1; - } - } - - let mut tmp = CountN{n:0,q:0}; - S::synthesize(&mut tmp, circuit).unwrap(); // TODO - - (tmp.n, tmp.q) - }; - - let mut transcript = Transcript::new(&[]); - let mut y_values: Vec = Vec::with_capacity(inputs.len()); - for &(ref proof, ref sxyadvice) in inputs { - { - let mut transcript = Transcript::new(&[]); - transcript.commit_point(&proof.r); - y_values.push(transcript.get_challenge_scalar()); - } - - transcript.commit_point(&sxyadvice.s); - } - - let z: E::Fr = transcript.get_challenge_scalar(); - - // Compute s(z, Y) - let (s_poly_negative, s_poly_positive) = { - let mut tmp = SyEval::new(z, n, q); - S::synthesize(&mut tmp, circuit).unwrap(); // TODO - - tmp.poly() - }; - - // Compute C = g^{s(z, x)} - let c = multiexp( - srs.g_positive_x_alpha[0..(n + q)] - .iter() - .chain_ext(srs.g_negative_x_alpha[0..n].iter()), - s_poly_positive.iter().chain_ext(s_poly_negative.iter()) - ).into_affine(); - - transcript.commit_point(&c); - - // Open C at w - let w: E::Fr = transcript.get_challenge_scalar(); - - let value = compute_value::(&w, &s_poly_positive, &s_poly_negative); - - let opening = { - let mut value = value; - value.negate(); - - let poly = kate_divison( - s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()), - w, - ); - - let negative_poly = poly[0..n].iter().rev(); - let positive_poly = poly[n..].iter(); - multiexp( - srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( - srs.g_positive_x[0..positive_poly.len()].iter() - ), - negative_poly.chain_ext(positive_poly) - ).into_affine() - }; - - // Let's open up C to every y. - fn compute_value(y: &E::Fr, poly_positive: &[E::Fr], poly_negative: &[E::Fr]) -> E::Fr { - let mut value = E::Fr::zero(); - - let yinv = y.inverse().unwrap(); // TODO - let mut tmp = yinv; - for &coeff in poly_negative { - let mut coeff = coeff; - coeff.mul_assign(&tmp); - value.add_assign(&coeff); - tmp.mul_assign(&yinv); - } - - let mut tmp = *y; - for &coeff in poly_positive { - let mut coeff = coeff; - coeff.mul_assign(&tmp); - value.add_assign(&coeff); - tmp.mul_assign(&y); - } - - value - } - - let mut c_openings = vec![]; - for y in &y_values { - let value = compute_value::(y, &s_poly_positive, &s_poly_negative); - - let opening = { - let mut value = value; - value.negate(); - - let poly = kate_divison( - s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()), - *y, - ); - - let negative_poly = poly[0..n].iter().rev(); - let positive_poly = poly[n..].iter(); - multiexp( - srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( - srs.g_positive_x[0..positive_poly.len()].iter() - ), - negative_poly.chain_ext(positive_poly) - ).into_affine() - }; - - c_openings.push((opening, value)); - } - - // Okay, great. Now we need to open up each S at the same point z to the same value. - // Since we're opening up all the S's at the same point, we create a bunch of random - // challenges instead and open up a random linear combination. - - let mut poly_negative = vec![E::Fr::zero(); n]; - let mut poly_positive = vec![E::Fr::zero(); 2*n]; - let mut expected_value = E::Fr::zero(); - - for (y, c_opening) in y_values.iter().zip(c_openings.iter()) { - // Compute s(X, y_i) - let (s_poly_negative, s_poly_positive) = { - let mut tmp = SxEval::new(*y, n); - S::synthesize(&mut tmp, circuit).unwrap(); // TODO - - tmp.poly() - }; - - let mut value = c_opening.1; - let r: E::Fr = transcript.get_challenge_scalar(); - value.mul_assign(&r); - expected_value.add_assign(&value); - - for (mut coeff, target) in s_poly_negative.into_iter().zip(poly_negative.iter_mut()) { - coeff.mul_assign(&r); - target.add_assign(&coeff); - } - - for (mut coeff, target) in s_poly_positive.into_iter().zip(poly_positive.iter_mut()) { - coeff.mul_assign(&r); - target.add_assign(&coeff); - } - } - - let s_opening = { - let mut value = expected_value; - value.negate(); - - let poly = kate_divison( - poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(poly_positive.iter()), - z, - ); - - let negative_poly = poly[0..n].iter().rev(); - let positive_poly = poly[n..].iter(); - multiexp( - srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( - srs.g_positive_x[0..positive_poly.len()].iter() - ), - negative_poly.chain_ext(positive_poly) - ).into_affine() - }; - - Aggregate { - // Commitment to s(z, Y) - c, - // We have to open each of the S commitments to a random point `z` - s_opening, - // We have to open C to each constituent `y` - c_openings, - // Then we have to finally open C - opening, - } -} - pub fn create_advice, S: SynthesisDriver>( circuit: &C, proof: &Proof, diff --git a/src/sonic/helped/verifier.rs b/src/sonic/helped/verifier.rs index 35e1ae4..1bed9a4 100644 --- a/src/sonic/helped/verifier.rs +++ b/src/sonic/helped/verifier.rs @@ -5,6 +5,7 @@ use std::marker::PhantomData; use super::{Proof, SxyAdvice}; use super::batch::Batch; use super::poly::{SxEval, SyEval}; +use super::helper::Aggregate; use crate::SynthesisError; @@ -249,217 +250,4 @@ impl, S: SynthesisDriver> MultiVerifier { pub fn check_all(self) -> bool { self.batch.check_all() } -} - -#[derive(Clone)] -pub struct Aggregate { - // Commitment to s(z, Y) - c: E::G1Affine, - // We have to open each of the S commitments to a random point `z` - s_opening: E::G1Affine, - // We have to open C to each constituent `y` - c_openings: Vec<(E::G1Affine, E::Fr)>, - // Then we have to finally open C - opening: E::G1Affine, -} - -pub fn create_aggregate, S: SynthesisDriver>( - circuit: &C, - inputs: &[(Proof, SxyAdvice)], - srs: &SRS, -) -> Aggregate -{ - // TODO: precompute this? - let (n, q) = { - struct CountN { - n: usize, - q: usize - } - - impl<'a, E: Engine> Backend for &'a mut CountN { - fn new_multiplication_gate(&mut self) { - self.n += 1; - } - - fn new_linear_constraint(&mut self) { - self.q += 1; - } - } - - let mut tmp = CountN{n:0,q:0}; - S::synthesize(&mut tmp, circuit).unwrap(); // TODO - - (tmp.n, tmp.q) - }; - - let mut transcript = Transcript::new(&[]); - let mut y_values: Vec = Vec::with_capacity(inputs.len()); - for &(ref proof, ref sxyadvice) in inputs { - { - let mut transcript = Transcript::new(&[]); - transcript.commit_point(&proof.r); - y_values.push(transcript.get_challenge_scalar()); - } - - transcript.commit_point(&sxyadvice.s); - } - - let z: E::Fr = transcript.get_challenge_scalar(); - - // Compute s(z, Y) - let (s_poly_negative, s_poly_positive) = { - let mut tmp = SyEval::new(z, n, q); - S::synthesize(&mut tmp, circuit).unwrap(); // TODO - - tmp.poly() - }; - - // Compute C = g^{s(z, x)} - let c = multiexp( - srs.g_positive_x_alpha[0..(n + q)] - .iter() - .chain_ext(srs.g_negative_x_alpha[0..n].iter()), - s_poly_positive.iter().chain_ext(s_poly_negative.iter()) - ).into_affine(); - - transcript.commit_point(&c); - - // Open C at w - let w: E::Fr = transcript.get_challenge_scalar(); - - let value = compute_value::(&w, &s_poly_positive, &s_poly_negative); - - let opening = { - let mut value = value; - value.negate(); - - let poly = kate_divison( - s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()), - w, - ); - - let negative_poly = poly[0..n].iter().rev(); - let positive_poly = poly[n..].iter(); - multiexp( - srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( - srs.g_positive_x[0..positive_poly.len()].iter() - ), - negative_poly.chain_ext(positive_poly) - ).into_affine() - }; - - // TODO: parallelize - // Let's open up C to every y. - fn compute_value(y: &E::Fr, poly_positive: &[E::Fr], poly_negative: &[E::Fr]) -> E::Fr { - let mut value = E::Fr::zero(); - - let yinv = y.inverse().unwrap(); // TODO - let mut tmp = yinv; - for &coeff in poly_negative { - let mut coeff = coeff; - coeff.mul_assign(&tmp); - value.add_assign(&coeff); - tmp.mul_assign(&yinv); - } - - let mut tmp = *y; - for &coeff in poly_positive { - let mut coeff = coeff; - coeff.mul_assign(&tmp); - value.add_assign(&coeff); - tmp.mul_assign(&y); - } - - value - } - - let mut c_openings = vec![]; - for y in &y_values { - let value = compute_value::(y, &s_poly_positive, &s_poly_negative); - - let opening = { - let mut value = value; - value.negate(); - - let poly = kate_divison( - s_poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(s_poly_positive.iter()), - *y, - ); - - let negative_poly = poly[0..n].iter().rev(); - let positive_poly = poly[n..].iter(); - multiexp( - srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( - srs.g_positive_x[0..positive_poly.len()].iter() - ), - negative_poly.chain_ext(positive_poly) - ).into_affine() - }; - - c_openings.push((opening, value)); - } - - // Okay, great. Now we need to open up each S at the same point z to the same value. - // Since we're opening up all the S's at the same point, we create a bunch of random - // challenges instead and open up a random linear combination. - - let mut poly_negative = vec![E::Fr::zero(); n]; - let mut poly_positive = vec![E::Fr::zero(); 2*n]; - let mut expected_value = E::Fr::zero(); - - for (y, c_opening) in y_values.iter().zip(c_openings.iter()) { - // Compute s(X, y_i) - let (s_poly_negative, s_poly_positive) = { - let mut tmp = SxEval::new(*y, n); - S::synthesize(&mut tmp, circuit).unwrap(); // TODO - - tmp.poly() - }; - - let mut value = c_opening.1; - let r: E::Fr = transcript.get_challenge_scalar(); - value.mul_assign(&r); - expected_value.add_assign(&value); - - for (mut coeff, target) in s_poly_negative.into_iter().zip(poly_negative.iter_mut()) { - coeff.mul_assign(&r); - target.add_assign(&coeff); - } - - for (mut coeff, target) in s_poly_positive.into_iter().zip(poly_positive.iter_mut()) { - coeff.mul_assign(&r); - target.add_assign(&coeff); - } - } - - // TODO: parallelize - let s_opening = { - let mut value = expected_value; - value.negate(); - - let poly = kate_divison( - poly_negative.iter().rev().chain_ext(Some(value).iter()).chain_ext(poly_positive.iter()), - z, - ); - - let negative_poly = poly[0..n].iter().rev(); - let positive_poly = poly[n..].iter(); - multiexp( - srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( - srs.g_positive_x[0..positive_poly.len()].iter() - ), - negative_poly.chain_ext(positive_poly) - ).into_affine() - }; - - Aggregate { - // Commitment to s(z, Y) - c, - // We have to open each of the S commitments to a random point `z` - s_opening, - // We have to open C to each constituent `y` - c_openings, - // Then we have to finally open C - opening, - } } \ No newline at end of file diff --git a/src/sonic/transcript/hasher.rs b/src/sonic/transcript/hasher.rs new file mode 100644 index 0000000..7474d30 --- /dev/null +++ b/src/sonic/transcript/hasher.rs @@ -0,0 +1,73 @@ +extern crate tiny_keccak; +extern crate blake2_rfc; + +use self::tiny_keccak::Keccak; +use self::blake2_rfc::blake2s::Blake2s; + +pub trait Hasher { + fn new(personalization: &[u8]) -> Self; + fn update(&mut self, data: &[u8]); + fn finalize(&mut self) -> Vec; +} + +#[derive(Clone)] +pub struct BlakeHasher { + h: Blake2s +} + +impl Hasher for BlakeHasher { + fn new(personalization: &[u8]) -> Self { + let h = Blake2s::with_params(32, &[], &[], personalization); + + Self { + h: h + } + } + + fn update(&mut self, data: &[u8]) { + self.h.update(data); + } + + fn finalize(&mut self) -> Vec { + use std::mem; + + let new_h = Blake2s::with_params(32, &[], &[], &[]); + let h = std::mem::replace(&mut self.h, new_h); + + let result = h.finalize(); + + result.as_ref().to_vec().clone() + } +} + +#[derive(Clone)] +pub struct Keccak256Hasher { + h: Keccak +} + +impl Hasher for Keccak256Hasher { + fn new(personalization: &[u8]) -> Self { + let mut h = Keccak::new_keccak256(); + h.update(personalization); + + Self { + h: h + } + } + + fn update(&mut self, data: &[u8]) { + self.h.update(data); + } + + fn finalize(&mut self) -> Vec { + use std::mem; + + let new_h = Keccak::new_keccak256(); + let h = std::mem::replace(&mut self.h, new_h); + + let mut res: [u8; 32] = [0; 32]; + h.finalize(&mut res); + + res[..].to_vec() + } +} \ No newline at end of file diff --git a/src/sonic/transcript/mod.rs b/src/sonic/transcript/mod.rs index d3d4d07..bede612 100644 --- a/src/sonic/transcript/mod.rs +++ b/src/sonic/transcript/mod.rs @@ -5,25 +5,77 @@ use ff::{Field, PrimeField, PrimeFieldRepr}; use pairing::{CurveAffine, CurveProjective, Engine}; use std::io; -// transcript is mocked for now +mod hasher; + +use self::hasher::{Hasher, Keccak256Hasher, BlakeHasher}; + #[derive(Clone)] pub struct Transcript { - buffer: Vec + transcriptor: RollingHashTranscript } impl Transcript { pub fn new(personalization: &[u8]) -> Self { Self { - buffer: vec![] + transcriptor: RollingHashTranscript::new(personalization) + } + } +} + +impl TranscriptProtocol for Transcript { + fn commit_point(&mut self, point: &G) { + self.transcriptor.commit_point(point); + } + + fn commit_scalar(&mut self, scalar: &F) { + self.transcriptor.commit_scalar(scalar); + } + + fn get_challenge_scalar(&mut self) -> F { + self.transcriptor.get_challenge_scalar() + } +} + +use std::marker::PhantomData; + +#[derive(Clone)] +pub struct RollingHashTranscript { + buffer: Vec, + last_finalized_value: Vec, + _marker: PhantomData +} + +impl RollingHashTranscript { + pub fn new(personalization: &[u8]) -> Self { + let mut h = H::new(personalization); + let buffer = h.finalize(); + + Self { + buffer: buffer, + last_finalized_value: vec![], + _marker: PhantomData } } pub fn commit_bytes(&mut self, personalization: &[u8], bytes: &[u8]) { + let mut h = H::new(personalization); + h.update(&self.buffer); + h.update(personalization); + h.update(bytes); + self.buffer = h.finalize(); } - pub fn challenge_bytes(&mut self, personalization: &[u8], bytes: &[u8]) { + pub fn get_challenge_bytes(&mut self, nonce: &[u8]) -> Vec { + let challenge_bytes = &self.buffer; + let mut h = H::new(&[]); + h.update(challenge_bytes); + h.update(nonce); + + let challenge_bytes = h.finalize(); + + challenge_bytes } } @@ -33,37 +85,48 @@ pub trait TranscriptProtocol { fn get_challenge_scalar(&mut self) -> F; } -impl TranscriptProtocol for Transcript { +impl TranscriptProtocol for RollingHashTranscript { fn commit_point(&mut self, point: &G) { - self.commit_bytes(b"point", point.into_compressed().as_ref()); + self.commit_bytes(b"point", point.into_uncompressed().as_ref()); + // self.commit_bytes(b"point", point.into_compressed().as_ref()); } fn commit_scalar(&mut self, scalar: &F) { let mut v = vec![]; - scalar.into_repr().write_le(&mut v).unwrap(); + scalar.into_repr().write_be(&mut v).unwrap(); + // scalar.into_repr().write_le(&mut v).unwrap(); self.commit_bytes(b"scalar", &v); } fn get_challenge_scalar(&mut self) -> F { - return F::one(); - // loop { - // let mut repr: F::Repr = Default::default(); - // repr.read_be(TranscriptReader(self)).unwrap(); + use byteorder::ByteOrder; - // if let Ok(result) = F::from_repr(repr) { - // return result; - // } - // } + let mut nonce = 0u32; + loop { + let mut nonce_bytes = vec![0u8; 4]; + byteorder::BigEndian::write_u32(&mut nonce_bytes, nonce); + let mut repr: F::Repr = Default::default(); + let challenge_bytes = self.get_challenge_bytes(&nonce_bytes); + repr.read_be(&challenge_bytes[..]).unwrap(); + + if let Ok(result) = F::from_repr(repr) { + return result; + } + if nonce == (0xffffffff as u32) { + panic!("can not make challenge scalar"); + } + nonce += 1; + } } } -struct TranscriptReader<'a>(&'a mut Transcript); +// struct TranscriptReader<'a, H:Hasher>(&'a mut Transcript); -impl<'a> io::Read for TranscriptReader<'a> { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.0.challenge_bytes(b"read", buf); +// impl<'a, H:Hasher> io::Read for TranscriptReader<'a, H: Hasher> { +// fn read(&mut self, buf: &mut [u8]) -> io::Result { +// self.0.challenge_bytes(b"read", buf); - Ok(buf.len()) - } -} \ No newline at end of file +// Ok(buf.len()) +// } +// } \ No newline at end of file From e8409bb70bb1a4903084ca60dce458ef87fe0665 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 5 Feb 2019 17:28:38 +0300 Subject: [PATCH 07/42] rename and make field public. Prepare to test on Eth --- src/sonic/helped/batch.rs | 12 ++++++------ src/sonic/helped/mod.rs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sonic/helped/batch.rs b/src/sonic/helped/batch.rs index 4ab4868..0196161 100644 --- a/src/sonic/helped/batch.rs +++ b/src/sonic/helped/batch.rs @@ -128,18 +128,18 @@ impl Batch { } -pub struct VerificationKey { - alpha_x: E::G2Affine, +pub struct VerifyingKey { + pub alpha_x: E::G2Affine, - alpha: E::G2Affine, + pub alpha: E::G2Affine, - neg_h: E::G2Affine, + pub neg_h: E::G2Affine, - neg_x_n_minus_d: E::G2Affine + pub neg_x_n_minus_d: E::G2Affine } -impl VerificationKey { +impl VerifyingKey { pub fn new(srs: &SRS, n: usize) -> Self { Self { alpha_x: srs.h_positive_x_alpha[1], diff --git a/src/sonic/helped/mod.rs b/src/sonic/helped/mod.rs index 51769f6..e555ba0 100644 --- a/src/sonic/helped/mod.rs +++ b/src/sonic/helped/mod.rs @@ -11,7 +11,7 @@ mod batch; mod poly; mod helper; -pub use self::batch::{Batch, VerificationKey}; +pub use self::batch::{Batch, VerifyingKey}; pub use self::helper::{Aggregate, create_aggregate}; pub use self::verifier::{MultiVerifier}; pub use self::prover::{create_proof, create_advice}; From e5bc2c197f609b167d6314d090c0bcf852d2c82f Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 5 Feb 2019 18:56:04 +0300 Subject: [PATCH 08/42] fetch values for input verification from synthesized circuit --- src/sonic/helped/verifier.rs | 8 ++++++++ tests/mimc.rs | 13 +++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/sonic/helped/verifier.rs b/src/sonic/helped/verifier.rs index 1bed9a4..5010d1c 100644 --- a/src/sonic/helped/verifier.rs +++ b/src/sonic/helped/verifier.rs @@ -247,6 +247,14 @@ impl, S: SynthesisDriver> MultiVerifier { } } + pub fn get_k_map(&self) -> Vec { + return self.k_map.clone(); + } + + pub fn get_n(&self) -> usize { + return self.n; + } + pub fn check_all(self) -> bool { self.batch.check_all() } diff --git a/tests/mimc.rs b/tests/mimc.rs index 6501c07..3be384d 100644 --- a/tests/mimc.rs +++ b/tests/mimc.rs @@ -621,6 +621,19 @@ fn test_inputs_into_sonic_mimc() { } println!("done in {:?}", start.elapsed()); } + + { + let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); + println!("verifying 1 proof with advice"); + let start = Instant::now(); + { + for _ in 0..1 { + verifier.add_proof_with_advice(&proof, &[image], &advice); + } + assert_eq!(verifier.check_all(), true); // TODO + } + println!("done in {:?}", start.elapsed()); + } { let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); From 9cb62348fb3edccc1d23f025e9cdf2262b26a46f Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 5 Feb 2019 19:46:00 +0300 Subject: [PATCH 09/42] can get Q too --- src/sonic/helped/verifier.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sonic/helped/verifier.rs b/src/sonic/helped/verifier.rs index 5010d1c..808cd2f 100644 --- a/src/sonic/helped/verifier.rs +++ b/src/sonic/helped/verifier.rs @@ -255,6 +255,10 @@ impl, S: SynthesisDriver> MultiVerifier { return self.n; } + pub fn get_q(&self) -> usize { + return self.q; + } + pub fn check_all(self) -> bool { self.batch.check_all() } From 746eb56e0f48c1e8cd358d34b5bd0644a61b7775 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 5 Feb 2019 23:10:14 +0300 Subject: [PATCH 10/42] verifying key is now self-sufficient for exports --- src/sonic/helped/batch.rs | 54 ++++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/src/sonic/helped/batch.rs b/src/sonic/helped/batch.rs index 0196161..445f7e5 100644 --- a/src/sonic/helped/batch.rs +++ b/src/sonic/helped/batch.rs @@ -10,9 +10,17 @@ use ff::{Field}; use pairing::{Engine, CurveAffine, CurveProjective}; + +use crate::SynthesisError; + +use crate::sonic::cs::{Backend, SynthesisDriver}; +use crate::sonic::cs::{Circuit}; + use crate::sonic::srs::SRS; use crate::sonic::util::multiexp; +use std::marker::PhantomData; + // One of the primary functions of the `Batch` abstraction is handling // Kate commitment openings: // @@ -128,20 +136,50 @@ impl Batch { } -pub struct VerifyingKey { +pub struct VerifyingKey { pub alpha_x: E::G2Affine, pub alpha: E::G2Affine, pub neg_h: E::G2Affine, - pub neg_x_n_minus_d: E::G2Affine + pub neg_x_n_minus_d: E::G2Affine, + pub k_map: Vec, + + pub n: usize, + + pub q: usize } impl VerifyingKey { - pub fn new(srs: &SRS, n: usize) -> Self { - Self { + pub fn new, S: SynthesisDriver>(circuit: C, srs: &SRS) -> Result { + struct Preprocess { + k_map: Vec, + n: usize, + q: usize, + _marker: PhantomData + } + + impl<'a, E: Engine> Backend for &'a mut Preprocess { + fn new_k_power(&mut self, index: usize) { + self.k_map.push(index); + } + + fn new_multiplication_gate(&mut self) { + self.n += 1; + } + + fn new_linear_constraint(&mut self) { + self.q += 1; + } + } + + let mut preprocess = Preprocess { k_map: vec![], n: 0, q: 0, _marker: PhantomData }; + + S::synthesize(&mut preprocess, &circuit)?; + + Ok(Self { alpha_x: srs.h_positive_x_alpha[1], alpha: srs.h_positive_x_alpha[0], @@ -154,11 +192,15 @@ impl VerifyingKey { }, neg_x_n_minus_d: { - let mut tmp = srs.h_negative_x[srs.d - n]; + let mut tmp = srs.h_negative_x[srs.d - preprocess.n]; tmp.negate(); tmp }, - } + + k_map: preprocess.k_map, + n: preprocess.n, + q: preprocess.q + }) } } \ No newline at end of file From a3f910cb52bb4e6782deabf63034002eb0ba0d37 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Wed, 6 Feb 2019 01:20:41 +0300 Subject: [PATCH 11/42] done junction with ETH --- src/sonic/helped/batch.rs | 18 +++++++++++++----- src/sonic/helped/mod.rs | 22 +++++++++++----------- src/sonic/transcript/mod.rs | 4 ++-- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/sonic/helped/batch.rs b/src/sonic/helped/batch.rs index 445f7e5..17f9871 100644 --- a/src/sonic/helped/batch.rs +++ b/src/sonic/helped/batch.rs @@ -109,22 +109,30 @@ impl Batch { let alpha_x = multiexp( self.alpha_x.iter().map(|x| &x.0), self.alpha_x.iter().map(|x| &x.1), - ).into_affine().prepare(); + ).into_affine(); + + let alpha_x = alpha_x.prepare(); let alpha = multiexp( self.alpha.iter().map(|x| &x.0), self.alpha.iter().map(|x| &x.1), - ).into_affine().prepare(); + ).into_affine(); + + let alpha = alpha.prepare(); let neg_h = multiexp( self.neg_h.iter().map(|x| &x.0), self.neg_h.iter().map(|x| &x.1), - ).into_affine().prepare(); + ).into_affine(); + + let neg_h = neg_h.prepare(); let neg_x_n_minus_d = multiexp( self.neg_x_n_minus_d.iter().map(|x| &x.0), self.neg_x_n_minus_d.iter().map(|x| &x.1), - ).into_affine().prepare(); + ).into_affine(); + + let neg_x_n_minus_d = neg_x_n_minus_d.prepare(); E::final_exponentiation(&E::miller_loop(&[ (&alpha_x, &self.alpha_x_precomp), @@ -135,7 +143,7 @@ impl Batch { } } - +#[derive(Clone, Debug)] pub struct VerifyingKey { pub alpha_x: E::G2Affine, diff --git a/src/sonic/helped/mod.rs b/src/sonic/helped/mod.rs index e555ba0..b330fed 100644 --- a/src/sonic/helped/mod.rs +++ b/src/sonic/helped/mod.rs @@ -16,19 +16,19 @@ pub use self::helper::{Aggregate, create_aggregate}; pub use self::verifier::{MultiVerifier}; pub use self::prover::{create_proof, create_advice}; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct SxyAdvice { - s: E::G1Affine, - opening: E::G1Affine, - szy: E::Fr, + pub s: E::G1Affine, + pub opening: E::G1Affine, + pub szy: E::Fr, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Proof { - r: E::G1Affine, - t: E::G1Affine, - rz: E::Fr, - rzy: E::Fr, - z_opening: E::G1Affine, - zy_opening: E::G1Affine + pub r: E::G1Affine, + pub t: E::G1Affine, + pub rz: E::Fr, + pub rzy: E::Fr, + pub z_opening: E::G1Affine, + pub zy_opening: E::G1Affine } \ No newline at end of file diff --git a/src/sonic/transcript/mod.rs b/src/sonic/transcript/mod.rs index bede612..fc5035f 100644 --- a/src/sonic/transcript/mod.rs +++ b/src/sonic/transcript/mod.rs @@ -58,7 +58,7 @@ impl RollingHashTranscript { } pub fn commit_bytes(&mut self, personalization: &[u8], bytes: &[u8]) { - let mut h = H::new(personalization); + let mut h = H::new(&[]); h.update(&self.buffer); h.update(personalization); h.update(bytes); @@ -101,7 +101,6 @@ impl TranscriptProtocol for RollingHashTranscript { fn get_challenge_scalar(&mut self) -> F { use byteorder::ByteOrder; - let mut nonce = 0u32; loop { let mut nonce_bytes = vec![0u8; 4]; @@ -111,6 +110,7 @@ impl TranscriptProtocol for RollingHashTranscript { repr.read_be(&challenge_bytes[..]).unwrap(); if let Ok(result) = F::from_repr(repr) { + // println!("Got a challenge {} for nonce = {}", result, nonce); return result; } if nonce == (0xffffffff as u32) { From cd00c4eaf796a0dc36aa90b63c5522a48d9b83f3 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Wed, 6 Feb 2019 02:23:27 +0300 Subject: [PATCH 12/42] mark progress --- src/sonic/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sonic/README.md b/src/sonic/README.md index 0f572db..3aa27d3 100644 --- a/src/sonic/README.md +++ b/src/sonic/README.md @@ -16,7 +16,7 @@ Initial SONIC proof system integration using the code from the [original impleme - [x] Test on BN256 - [x] Parallelize using existing primitives - [ ] Implement polynomial parallelized evaluation -- [ ] Make custom transcriptor that is easy to transform into the smart-contract -- [ ] Basic Ethereum smart-contract +- [x] Make custom transcriptor that is easy to transform into the smart-contract +- [x] Basic Ethereum smart-contract - [ ] Add blinding factors - [ ] Implement unhelped version \ No newline at end of file From 60596015187e0f7727f84b20465da377e80cdc9d Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Thu, 7 Feb 2019 14:27:08 +0300 Subject: [PATCH 13/42] start restructuring to use groth16-similar types --- src/sonic/cs/mod.rs | 1 - src/sonic/helped/batch.rs | 70 --- src/sonic/helped/generator.rs | 800 +++++++++++++++++++++++++++++++++ src/sonic/helped/mod.rs | 22 +- src/sonic/helped/parameters.rs | 689 ++++++++++++++++++++++++++++ src/sonic/sonic/adaptor.rs | 5 +- src/sonic/sonic/mod.rs | 2 +- src/sonic/srs/srs.rs | 179 ++++++++ tests/mimc.rs | 15 +- 9 files changed, 1689 insertions(+), 94 deletions(-) create mode 100644 src/sonic/helped/generator.rs create mode 100644 src/sonic/helped/parameters.rs diff --git a/src/sonic/cs/mod.rs b/src/sonic/cs/mod.rs index 3138c06..342d408 100644 --- a/src/sonic/cs/mod.rs +++ b/src/sonic/cs/mod.rs @@ -7,7 +7,6 @@ use pairing::{Engine}; use crate::{SynthesisError}; use std::marker::PhantomData; - mod lc; pub use self::lc::{Coeff, Variable, LinearCombination}; diff --git a/src/sonic/helped/batch.rs b/src/sonic/helped/batch.rs index 17f9871..3a7e23a 100644 --- a/src/sonic/helped/batch.rs +++ b/src/sonic/helped/batch.rs @@ -141,74 +141,4 @@ impl Batch { (&neg_x_n_minus_d, &self.neg_x_n_minus_d_precomp), ])).unwrap() == E::Fqk::one() } -} - -#[derive(Clone, Debug)] -pub struct VerifyingKey { - pub alpha_x: E::G2Affine, - - pub alpha: E::G2Affine, - - pub neg_h: E::G2Affine, - - pub neg_x_n_minus_d: E::G2Affine, - - pub k_map: Vec, - - pub n: usize, - - pub q: usize -} - -impl VerifyingKey { - pub fn new, S: SynthesisDriver>(circuit: C, srs: &SRS) -> Result { - struct Preprocess { - k_map: Vec, - n: usize, - q: usize, - _marker: PhantomData - } - - impl<'a, E: Engine> Backend for &'a mut Preprocess { - fn new_k_power(&mut self, index: usize) { - self.k_map.push(index); - } - - fn new_multiplication_gate(&mut self) { - self.n += 1; - } - - fn new_linear_constraint(&mut self) { - self.q += 1; - } - } - - let mut preprocess = Preprocess { k_map: vec![], n: 0, q: 0, _marker: PhantomData }; - - S::synthesize(&mut preprocess, &circuit)?; - - Ok(Self { - alpha_x: srs.h_positive_x_alpha[1], - - alpha: srs.h_positive_x_alpha[0], - - neg_h: { - let mut tmp = srs.h_negative_x[0]; - tmp.negate(); - - tmp - }, - - neg_x_n_minus_d: { - let mut tmp = srs.h_negative_x[srs.d - preprocess.n]; - tmp.negate(); - - tmp - }, - - k_map: preprocess.k_map, - n: preprocess.n, - q: preprocess.q - }) - } } \ No newline at end of file diff --git a/src/sonic/helped/generator.rs b/src/sonic/helped/generator.rs new file mode 100644 index 0000000..e385781 --- /dev/null +++ b/src/sonic/helped/generator.rs @@ -0,0 +1,800 @@ +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 crate::domain::{ + Scalar +}; + +use ::multicore::{ + Worker +}; + +use std::marker::PhantomData; + +use crate::sonic::cs::{Backend, Basic, SynthesisDriver}; +use crate::sonic::srs::SRS; +use crate::sonic::cs::LinearCombination as SonicLinearCombination; +use crate::sonic::cs::Circuit as SonicCircuit; +use crate::sonic::cs::ConstraintSystem as SonicConstraintSystem; +use crate::sonic::cs::Variable as SonicVariable; +use crate::sonic::cs::Coeff; +use crate::sonic::sonic::{AdaptorCircuit}; + +use crate::verbose_flag; + +/// 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 alpha = rng.gen(); + let x = rng.gen(); + + generate_parameters::( + circuit, + alpha, + x + ) +} + +/// This is our assembly structure that we'll use to synthesize the +/// circuit into +#[derive(Clone, Debug)] +pub struct CircuitParameters { + pub num_inputs: usize, + pub num_aux: usize, + pub num_constraints: usize, + pub k_map: Vec, + pub n: usize, + pub q: usize, + _marker: PhantomData +} + +/// This is our assembly structure that we'll use to synthesize the +/// circuit into +struct GeneratorAssembly<'a, E: Engine, CS: SonicConstraintSystem + 'a> { + cs: &'a mut CS, + num_inputs: usize, + num_aux: usize, + num_constraints: usize, + _marker: PhantomData +} + +impl<'a, E: Engine, CS: SonicConstraintSystem + 'a> crate::ConstraintSystem + for GeneratorAssembly<'a, E, CS> +{ + type Root = Self; + + // this is an important change + fn one() -> crate::Variable { + crate::Variable::new_unchecked(crate::Index::Input(1)) + } + + fn alloc(&mut self, _: A, f: F) -> Result + where + F: FnOnce() -> Result, + A: FnOnce() -> AR, + AR: Into, + { + self.num_aux += 1; + + let var = self.cs.alloc(|| { + f().map_err(|_| crate::SynthesisError::AssignmentMissing) + }).map_err(|_| crate::SynthesisError::AssignmentMissing)?; + + Ok(match var { + SonicVariable::A(index) => crate::Variable::new_unchecked(crate::Index::Input(index)), + SonicVariable::B(index) => crate::Variable::new_unchecked(crate::Index::Aux(index)), + _ => unreachable!(), + }) + } + + fn alloc_input( + &mut self, + _: A, + f: F, + ) -> Result + where + F: FnOnce() -> Result, + A: FnOnce() -> AR, + AR: Into, + { + self.num_inputs += 1; + + let var = self.cs.alloc_input(|| { + f().map_err(|_| crate::SynthesisError::AssignmentMissing) + }).map_err(|_| crate::SynthesisError::AssignmentMissing)?; + + Ok(match var { + SonicVariable::A(index) => crate::Variable::new_unchecked(crate::Index::Input(index)), + SonicVariable::B(index) => crate::Variable::new_unchecked(crate::Index::Aux(index)), + _ => unreachable!(), + }) + } + + fn enforce(&mut self, _: A, a: LA, b: LB, c: LC) + where + A: FnOnce() -> AR, + AR: Into, + LA: FnOnce(crate::LinearCombination) -> crate::LinearCombination, + LB: FnOnce(crate::LinearCombination) -> crate::LinearCombination, + LC: FnOnce(crate::LinearCombination) -> crate::LinearCombination, + { + fn convert(lc: crate::LinearCombination) -> SonicLinearCombination { + let mut ret = SonicLinearCombination::zero(); + + for &(v, coeff) in lc.as_ref().iter() { + let var = match v.get_unchecked() { + crate::Index::Input(i) => SonicVariable::A(i), + crate::Index::Aux(i) => SonicVariable::B(i), + }; + + ret = ret + (Coeff::Full(coeff), var); + } + + ret + } + + fn eval>( + lc: &SonicLinearCombination, + cs: &CS, + ) -> Option { + let mut ret = E::Fr::zero(); + + for &(v, coeff) in lc.as_ref().iter() { + let mut tmp = match cs.get_value(v) { + Ok(tmp) => tmp, + Err(_) => return None, + }; + coeff.multiply(&mut tmp); + ret.add_assign(&tmp); + } + + Some(ret) + } + + self.num_constraints += 1; + + let a_lc = convert(a(crate::LinearCombination::zero())); + let a_value = eval(&a_lc, &*self.cs); + let b_lc = convert(b(crate::LinearCombination::zero())); + let b_value = eval(&b_lc, &*self.cs); + let c_lc = convert(c(crate::LinearCombination::zero())); + let c_value = eval(&c_lc, &*self.cs); + + let (a, b, c) = self + .cs + .multiply(|| Ok((a_value.unwrap(), b_value.unwrap(), c_value.unwrap()))) + .unwrap(); + + self.cs.enforce_zero(a_lc - a); + self.cs.enforce_zero(b_lc - b); + self.cs.enforce_zero(c_lc - c); + } + + 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. +fn get_circuit_parameters( + circuit: C, +) -> Result, SynthesisError> + where E: Engine, C: Circuit + +{ + struct NonassigningSynthesizer> { + backend: B, + current_variable: Option, + _marker: PhantomData, + q: usize, + n: usize, + } + + impl> SonicConstraintSystem for NonassigningSynthesizer { + const ONE: SonicVariable = SonicVariable::A(1); + + fn alloc(&mut self, value: F) -> Result + where + F: FnOnce() -> Result + { + match self.current_variable.take() { + Some(index) => { + let var_b = SonicVariable::B(index); + + self.current_variable = None; + + Ok(var_b) + }, + None => { + self.n += 1; + let index = self.n; + self.backend.new_multiplication_gate(); + + let var_a = SonicVariable::A(index); + + self.current_variable = Some(index); + + Ok(var_a) + } + } + } + + fn alloc_input(&mut self, value: F) -> Result + where + F: FnOnce() -> Result + { + let input_var = self.alloc(value)?; + + self.enforce_zero(SonicLinearCombination::zero() + input_var); + self.backend.new_k_power(self.q); + + Ok(input_var) + } + + fn enforce_zero(&mut self, lc: SonicLinearCombination) + { + self.q += 1; + self.backend.new_linear_constraint(); + + for (var, coeff) in lc.as_ref() { + self.backend.insert_coefficient(*var, *coeff); + } + } + + fn multiply(&mut self, values: F) -> Result<(SonicVariable, SonicVariable, SonicVariable), SynthesisError> + where + F: FnOnce() -> Result<(E::Fr, E::Fr, E::Fr), SynthesisError> + { + self.n += 1; + let index = self.n; + self.backend.new_multiplication_gate(); + + let a = SonicVariable::A(index); + let b = SonicVariable::B(index); + let c = SonicVariable::C(index); + + Ok((a, b, c)) + } + + fn get_value(&self, var: SonicVariable) -> Result { + self.backend.get_var(var).ok_or(()) + } + } + + struct Preprocess { + k_map: Vec, + n: usize, + q: usize, + _marker: PhantomData + } + + impl<'a, E: Engine> Backend for &'a mut Preprocess { + fn new_k_power(&mut self, index: usize) { + self.k_map.push(index); + } + + fn new_multiplication_gate(&mut self) { + self.n += 1; + } + + fn new_linear_constraint(&mut self) { + self.q += 1; + } + } + + let mut preprocess = Preprocess { k_map: vec![], n: 0, q: 0, _marker: PhantomData }; + + let (num_inputs, num_aux, num_constraints) = { + + let mut cs: NonassigningSynthesizer> = NonassigningSynthesizer { + backend: &mut preprocess, + current_variable: None, + _marker: PhantomData, + q: 0, + n: 0, + }; + + let one = cs.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues"); + + match (one, > as SonicConstraintSystem>::ONE) { + (SonicVariable::A(1), SonicVariable::A(1)) => {}, + _ => return Err(SynthesisError::UnconstrainedVariable) + } + + // let adapted_circuit = AdaptorCircuit(circuit); + + + let mut assembly = GeneratorAssembly::<'_, E, _> { + cs: &mut cs, + num_inputs: 0, + num_aux: 0, + num_constraints: 0, + _marker: PhantomData + }; + + circuit.synthesize(&mut assembly)?; + + (assembly.num_inputs, assembly.num_aux, assembly.num_constraints) + }; + + Ok(CircuitParameters { + num_inputs: num_inputs, + num_aux: num_aux, + num_constraints: num_constraints, + k_map: preprocess.k_map, + n: preprocess.n, + q: preprocess.q, + _marker: PhantomData + }) +} + +pub fn generate_parameters( + circuit: C, + alpha: E::Fr, + x: E::Fr +) -> Result, SynthesisError> + where E: Engine, C: Circuit +{ + let circuit_parameters = get_circuit_parameters::(circuit)?; + let min_d = circuit_parameters.n * 3; + + let srs = generate_srs(alpha, x, min_d)?; + + let parameters = generate_parameters_on_srs_and_information::(&srs, circuit_parameters)?; + + Ok(parameters) +} + +pub fn generate_parameters_on_srs( + circuit: C, + srs: &SRS, +) -> Result, SynthesisError> + where E: Engine, C: Circuit +{ + let circuit_parameters = get_circuit_parameters::(circuit)?; + let parameters = generate_parameters_on_srs_and_information(&srs, circuit_parameters)?; + + Ok(parameters) +} + +pub fn generate_parameters_on_srs_and_information( + srs: &SRS, + information: CircuitParameters +) -> Result, SynthesisError> +{ + assert!(srs.d >= information.n * 3); + let min_d = information.n * 3; + + let trimmed_srs: SRS = SRS { + d: min_d, + g_negative_x: srs.g_negative_x[0..min_d+1].to_vec(), + g_positive_x: srs.g_positive_x[0..min_d+1].to_vec().clone(), + + h_negative_x: srs.h_negative_x[0..min_d+1].to_vec(), + h_positive_x: srs.h_positive_x[0..min_d+1].to_vec(), + + g_negative_x_alpha: srs.g_negative_x_alpha[0..min_d].to_vec(), + g_positive_x_alpha: srs.g_positive_x_alpha[0..min_d].to_vec(), + + h_negative_x_alpha: srs.h_negative_x_alpha[0..min_d+1].to_vec(), + h_positive_x_alpha: srs.h_positive_x_alpha[0..min_d+1].to_vec(), + + }; + + let vk = VerifyingKey { + alpha_x: trimmed_srs.h_positive_x_alpha[1], + + alpha: trimmed_srs.h_positive_x_alpha[0], + + neg_h: { + let mut tmp = trimmed_srs.h_negative_x[0]; + tmp.negate(); + + tmp + }, + + neg_x_n_minus_d: { + let mut tmp = trimmed_srs.h_negative_x[trimmed_srs.d - information.n]; + tmp.negate(); + + tmp + }, + + k_map: information.k_map, + n: information.n, + q: information.q + }; + + Ok(Parameters{ + vk: vk, + d: trimmed_srs.d, + g_negative_x: Arc::new(trimmed_srs.g_negative_x), + g_positive_x: Arc::new(trimmed_srs.g_positive_x), + h_negative_x: Arc::new(trimmed_srs.h_negative_x), + h_positive_x: Arc::new(trimmed_srs.h_positive_x), + g_negative_x_alpha: Arc::new(trimmed_srs.g_negative_x_alpha), + g_positive_x_alpha: Arc::new(trimmed_srs.g_positive_x_alpha), + h_negative_x_alpha: Arc::new(trimmed_srs.h_negative_x_alpha), + h_positive_x_alpha: Arc::new(trimmed_srs.h_positive_x_alpha) + }) +} + +pub fn generate_srs( + alpha: E::Fr, + x: E::Fr, + d: usize +) -> Result, SynthesisError> { + let verbose = verbose_flag(); + + let g1 = E::G1Affine::one().into_projective(); + let g2 = E::G2Affine::one().into_projective(); + + // Compute G1 window table + let mut g1_wnaf = Wnaf::new(); + let g1_wnaf = g1_wnaf.base(g1, 4*d); + + // Compute G2 window table + let mut g2_wnaf = Wnaf::new(); + let g2_wnaf = g2_wnaf.base(g2, 4*d); + + let x_inverse = x.inverse().ok_or(SynthesisError::UnexpectedIdentity)?; + + let worker = Worker::new(); + + let mut x_powers_positive = vec![Scalar::(E::Fr::zero()); d]; + let mut x_powers_negative = vec![Scalar::(E::Fr::zero()); d]; + { + // Compute powers of tau + if verbose {eprintln!("computing powers of tau...")}; + + let start = std::time::Instant::now(); + + { + worker.scope(d, |scope, chunk| { + for (i, x_powers) in x_powers_positive.chunks_mut(chunk).enumerate() + { + scope.spawn(move |_| { + let mut current_power = x.pow(&[(i*chunk + 1) as u64]); + + for p in x_powers { + p.0 = current_power; + current_power.mul_assign(&x); + } + }); + } + }); + } + { + worker.scope(d, |scope, chunk| { + for (i, x_powers) in x_powers_negative.chunks_mut(chunk).enumerate() + { + scope.spawn(move |_| { + let mut current_power = x_inverse.pow(&[(i*chunk + 1) as u64]); + + for p in x_powers { + p.0 = current_power; + current_power.mul_assign(&x_inverse); + } + }); + } + }); + } + if verbose {eprintln!("powers of x done in {} s", start.elapsed().as_millis() as f64 / 1000.0);}; + } + + // we will later add zero powers to g_x, h_x, h_x_alpha + let mut g_negative_x = vec![E::G1::one(); d]; + let mut g_positive_x = vec![E::G1::one(); d]; + + let mut h_negative_x = vec![E::G2::one(); d]; + let mut h_positive_x = vec![E::G2::one(); d]; + + let mut g_negative_x_alpha = vec![E::G1::one(); d]; + let mut g_positive_x_alpha = vec![E::G1::one(); d]; + + let mut h_negative_x_alpha = vec![E::G2::one(); d]; + let mut h_positive_x_alpha = vec![E::G2::one(); d]; + + fn eval( + // wNAF window tables + g1_wnaf: &Wnaf>, + g2_wnaf: &Wnaf>, + + powers_of_x: &[Scalar], + + g_x: &mut [E::G1], + g_x_alpha: &mut [E::G1], + h_x: &mut [E::G2], + h_x_alpha: &mut [E::G2], + + // Trapdoors + alpha: &E::Fr, + + // Worker + worker: &Worker + ) + + { + // Sanity check + assert_eq!(g_x.len(), powers_of_x.len()); + assert_eq!(g_x.len(), g_x_alpha.len()); + assert_eq!(g_x.len(), h_x.len()); + assert_eq!(g_x.len(), h_x_alpha.len()); + + // Evaluate polynomials in multiple threads + worker.scope(g_x.len(), |scope, chunk| { + for ((((x, g_x), g_x_alpha), h_x), h_x_alpha) in powers_of_x.chunks(chunk) + .zip(g_x.chunks_mut(chunk)) + .zip(g_x_alpha.chunks_mut(chunk)) + .zip(h_x.chunks_mut(chunk)) + .zip(h_x_alpha.chunks_mut(chunk)) + { + let mut g1_wnaf = g1_wnaf.shared(); + let mut g2_wnaf = g2_wnaf.shared(); + + scope.spawn(move |_| { + for ((((x, g_x), g_x_alpha), h_x), h_x_alpha) in x.iter() + .zip(g_x.iter_mut()) + .zip(g_x_alpha.iter_mut()) + .zip(h_x.iter_mut()) + .zip(h_x_alpha.iter_mut()) + { + let mut x_alpha = x.0; + x_alpha.mul_assign(&alpha); + + *g_x = g1_wnaf.scalar(x.0.into_repr()); + *h_x = g2_wnaf.scalar(x.0.into_repr()); + + *g_x_alpha = g1_wnaf.scalar(x_alpha.into_repr()); + *h_x_alpha = g2_wnaf.scalar(x_alpha.into_repr()); + } + + // Batch normalize + E::G1::batch_normalization(g_x); + E::G1::batch_normalization(g_x_alpha); + E::G2::batch_normalization(h_x); + E::G2::batch_normalization(h_x_alpha); + }); + }; + }); + } + + let start = std::time::Instant::now(); + + // Evaluate for positive powers. + eval( + &g1_wnaf, + &g2_wnaf, + &x_powers_positive, + &mut g_positive_x[..], + &mut g_positive_x_alpha[..], + &mut h_positive_x[..], + &mut h_positive_x_alpha[..], + &alpha, + &worker + ); + + // Evaluate for auxillary variables. + eval( + &g1_wnaf, + &g2_wnaf, + &x_powers_negative, + &mut g_negative_x[..], + &mut g_negative_x_alpha[..], + &mut h_negative_x[..], + &mut h_negative_x_alpha[..], + &alpha, + &worker + ); + + if verbose {eprintln!("evaluating points done in {} s", start.elapsed().as_millis() as f64 / 1000.0);}; + + let g1 = g1.into_affine(); + let g2 = g2.into_affine(); + + let h_alpha = g2.mul(alpha.into_repr()).into_affine(); + + let g_negative_x = { + let mut tmp = vec![g1]; + tmp.extend(g_negative_x.into_iter().map(|e| e.into_affine())); + + tmp + }; + let g_positive_x = { + let mut tmp = vec![g1]; + tmp.extend(g_positive_x.into_iter().map(|e| e.into_affine())); + + tmp + }; + + let h_negative_x = { + let mut tmp = vec![g2]; + tmp.extend(h_negative_x.into_iter().map(|e| e.into_affine())); + + tmp + }; + let h_positive_x = { + let mut tmp = vec![g2]; + tmp.extend(h_positive_x.into_iter().map(|e| e.into_affine())); + + tmp + }; + + let g_negative_x_alpha = g_negative_x_alpha.into_iter().map(|e| e.into_affine()).collect(); + let g_positive_x_alpha = g_positive_x_alpha.into_iter().map(|e| e.into_affine()).collect(); + + let h_negative_x_alpha = { + let mut tmp = vec![h_alpha]; + tmp.extend(h_negative_x_alpha.into_iter().map(|e| e.into_affine())); + + tmp + }; + let h_positive_x_alpha = { + let mut tmp = vec![h_alpha]; + tmp.extend(h_positive_x_alpha.into_iter().map(|e| e.into_affine())); + + tmp + }; + + Ok(SRS { + d: d, + g_negative_x: g_negative_x, + g_positive_x: g_positive_x, + + h_negative_x: h_negative_x, + h_positive_x: h_positive_x, + + g_negative_x_alpha: g_negative_x_alpha, + g_positive_x_alpha: g_positive_x_alpha, + + h_negative_x_alpha: h_negative_x_alpha, + h_positive_x_alpha: h_positive_x_alpha, + } + ) +} + +#[test] +fn parameters_generation() { + use pairing::bls12_381::{Bls12, Fr}; + 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(()) + } + } + + use rand::{Rand, thread_rng}; + + let info = get_circuit_parameters::(MySillyCircuit { a: None, b: None }).expect("Must get circuit info"); + + let rng = &mut thread_rng(); + + let x: Fr = rng.gen(); + let alpha: Fr = rng.gen(); + + let params = generate_parameters::(MySillyCircuit { a: None, b: None }, alpha, x).unwrap(); + let srs = generate_srs::(alpha, x, info.n * 100).unwrap(); + let naive_srs = SRS::::new( + info.n * 100, + x, + alpha, + ); + + assert!(srs == naive_srs); + + let params_on_srs = generate_parameters_on_srs_and_information::(&srs, info.clone()).unwrap(); + + assert!(params == params_on_srs); + + { + let mut v = vec![]; + + params.write(&mut v).unwrap(); + + 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/sonic/helped/mod.rs b/src/sonic/helped/mod.rs index b330fed..6f82c79 100644 --- a/src/sonic/helped/mod.rs +++ b/src/sonic/helped/mod.rs @@ -10,25 +10,11 @@ mod prover; mod batch; mod poly; mod helper; +mod parameters; +mod generator; -pub use self::batch::{Batch, VerifyingKey}; +pub use self::batch::{Batch}; pub use self::helper::{Aggregate, create_aggregate}; pub use self::verifier::{MultiVerifier}; pub use self::prover::{create_proof, create_advice}; - -#[derive(Clone, Debug)] -pub struct SxyAdvice { - pub s: E::G1Affine, - pub opening: E::G1Affine, - pub szy: E::Fr, -} - -#[derive(Clone, Debug)] -pub struct Proof { - pub r: E::G1Affine, - pub t: E::G1Affine, - pub rz: E::Fr, - pub rzy: E::Fr, - pub z_opening: E::G1Affine, - pub zy_opening: E::G1Affine -} \ No newline at end of file +pub use self::parameters::{Proof, SxyAdvice, Parameters, VerifyingKey, PreparedVerifyingKey}; \ No newline at end of file diff --git a/src/sonic/helped/parameters.rs b/src/sonic/helped/parameters.rs new file mode 100644 index 0000000..edb7361 --- /dev/null +++ b/src/sonic/helped/parameters.rs @@ -0,0 +1,689 @@ +use ff::{ + Field, + PrimeField, + PrimeFieldRepr +}; + +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}; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct SxyAdvice { + pub s: E::G1Affine, + pub opening: E::G1Affine, + pub szy: E::Fr, +} + + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Proof { + pub r: E::G1Affine, + pub t: E::G1Affine, + pub rz: E::Fr, + pub rzy: E::Fr, + pub z_opening: E::G1Affine, + pub zy_opening: E::G1Affine +} + +impl Proof { + pub fn write( + &self, + mut writer: W + ) -> io::Result<()> + { + use ff::{PrimeField, PrimeFieldRepr}; + writer.write_all(self.r.into_compressed().as_ref())?; + writer.write_all(self.t.into_compressed().as_ref())?; + let mut buffer = vec![]; + self.rz.into_repr().write_be(&mut buffer)?; + writer.write_all(&buffer[..])?; + self.rzy.into_repr().write_be(&mut buffer)?; + writer.write_all(&buffer[..])?; + writer.write_all(self.z_opening.into_compressed().as_ref())?; + writer.write_all(self.zy_opening.into_compressed().as_ref())?; + + Ok(()) + } + + pub fn read( + mut reader: R + ) -> io::Result + { + let mut g1_repr = ::Compressed::empty(); + let mut fr_repr = E::Fr::zero().into_repr(); + + reader.read_exact(g1_repr.as_mut())?; + let r = 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(g1_repr.as_mut())?; + let t = 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) + })?; + + fr_repr.read_be(&mut reader)?; + let rz = E::Fr::from_repr(fr_repr) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + .and_then(|e| if e.is_zero() { + Err(io::Error::new(io::ErrorKind::InvalidData, "field element is zero")) + } else { + Ok(e) + })?; + + fr_repr.read_be(&mut reader)?; + let rzy = E::Fr::from_repr(fr_repr) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) + .and_then(|e| if e.is_zero() { + Err(io::Error::new(io::ErrorKind::InvalidData, "field element is zero")) + } else { + Ok(e) + })?; + + + reader.read_exact(g1_repr.as_mut())?; + let z_opening = 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(g1_repr.as_mut())?; + let zy_opening = 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 { + r: r, + t: t, + rz: rz, + rzy: rzy, + z_opening: z_opening, + zy_opening: zy_opening + }) + } +} + +#[derive(Clone, Debug, Eq)] +pub struct VerifyingKey { + pub alpha_x: E::G2Affine, + + pub alpha: E::G2Affine, + + pub neg_h: E::G2Affine, + + pub neg_x_n_minus_d: E::G2Affine, + + pub k_map: Vec, + + pub n: usize, + + pub q: usize +} + +impl PartialEq for VerifyingKey { + fn eq(&self, other: &VerifyingKey) -> bool { + self.alpha_x == other.alpha_x && + self.alpha == other.alpha && + self.neg_h == other.neg_h && + self.neg_x_n_minus_d == other.neg_x_n_minus_d && + self.k_map == other.k_map && + self.n == other.n && + self.q == other.q + } +} + +impl VerifyingKey { + pub fn write( + &self, + mut writer: W + ) -> io::Result<()> + { + writer.write_all(self.alpha_x.into_uncompressed().as_ref())?; + writer.write_all(self.alpha.into_uncompressed().as_ref())?; + writer.write_all(self.neg_h.into_uncompressed().as_ref())?; + writer.write_all(self.neg_x_n_minus_d.into_uncompressed().as_ref())?; + + writer.write_u32::(self.k_map.len() as u32)?; + for k in &self.k_map { + writer.write_u32::(*k as u32)?; + } + writer.write_u32::(self.n as u32)?; + writer.write_u32::(self.q as u32)?; + + Ok(()) + } + + pub fn read( + mut reader: R + ) -> io::Result + { + let mut g2_repr = ::Uncompressed::empty(); + + reader.read_exact(g2_repr.as_mut())?; + let alpha_x = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + reader.read_exact(g2_repr.as_mut())?; + let alpha = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + reader.read_exact(g2_repr.as_mut())?; + let neg_h = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + reader.read_exact(g2_repr.as_mut())?; + let neg_x_n_minus_d = g2_repr.into_affine().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + let k_map_len = reader.read_u32::()? as usize; + + let mut k_map = vec![]; + + for _ in 0..k_map_len { + let k = reader.read_u32::()? as usize; + + k_map.push(k); + } + + let n = reader.read_u32::()? as usize; + + let q = reader.read_u32::()? as usize; + + Ok(VerifyingKey { + alpha_x: alpha_x, + alpha: alpha, + neg_h: neg_h, + neg_x_n_minus_d: neg_x_n_minus_d, + k_map: k_map, + n: n, + q: q + }) + } +} + +use crate::sonic::cs::{Backend, Basic, SynthesisDriver}; +use crate::sonic::srs::SRS; +use crate::sonic::cs::Circuit as SonicCircuit; +use std::marker::PhantomData; + +impl VerifyingKey { + pub fn new, S: SynthesisDriver>(circuit: C, srs: &SRS) -> Result { + struct Preprocess { + k_map: Vec, + n: usize, + q: usize, + _marker: PhantomData + } + + impl<'a, E: Engine> Backend for &'a mut Preprocess { + fn new_k_power(&mut self, index: usize) { + self.k_map.push(index); + } + + fn new_multiplication_gate(&mut self) { + self.n += 1; + } + + fn new_linear_constraint(&mut self) { + self.q += 1; + } + } + + let mut preprocess = Preprocess { k_map: vec![], n: 0, q: 0, _marker: PhantomData }; + + S::synthesize(&mut preprocess, &circuit)?; + + Ok(Self { + alpha_x: srs.h_positive_x_alpha[1], + + alpha: srs.h_positive_x_alpha[0], + + neg_h: { + let mut tmp = srs.h_negative_x[0]; + tmp.negate(); + + tmp + }, + + neg_x_n_minus_d: { + let mut tmp = srs.h_negative_x[srs.d - preprocess.n]; + tmp.negate(); + + tmp + }, + + k_map: preprocess.k_map, + n: preprocess.n, + q: preprocess.q + }) + } +} + +pub struct PreparedVerifyingKey { + alpha_x: ::Prepared, + alpha: ::Prepared, + neg_h: ::Prepared, + neg_x_n_minus_d: ::Prepared, + k_map: Vec, + n: usize, + q: usize +} + +#[derive(Clone, Eq)] +pub struct Parameters { + pub vk: VerifyingKey, + + pub d: usize, + + // g^{x^0}, g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}} + pub g_negative_x: Arc>, + + // g^{x^0}, g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}} + pub g_positive_x: Arc>, + + // g^{x^0}, g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}} + pub h_negative_x: Arc>, + + // g^{x^0}, g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}} + pub h_positive_x: Arc>, + + // alpha*(g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}}) + pub g_negative_x_alpha: Arc>, + + // alpha*(g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}}) + pub g_positive_x_alpha: Arc>, + + // alpha*(h^{x^0}, h^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}}) + pub h_negative_x_alpha: Arc>, + + // alpha*(h^{x^0}, g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}}) + pub h_positive_x_alpha: Arc>, +} + +impl PartialEq for Parameters { + fn eq(&self, other: &Parameters) -> bool { + self.vk == other.vk && + self.d == other.d && + self.g_negative_x == other.g_negative_x && + self.g_positive_x == other.g_positive_x && + self.h_negative_x == other.h_negative_x && + self.h_positive_x == other.h_positive_x && + self.g_negative_x_alpha == other.g_negative_x_alpha && + self.g_positive_x_alpha == other.g_positive_x_alpha && + self.h_negative_x_alpha == other.h_negative_x_alpha && + self.h_positive_x_alpha == other.h_positive_x_alpha + } +} + +impl Parameters { + pub fn write( + &self, + mut writer: W + ) -> io::Result<()> + { + self.vk.write(&mut writer)?; + + assert_eq!(self.d + 1, self.g_negative_x.len()); + assert_eq!(self.d + 1, self.g_positive_x.len()); + + assert_eq!(self.d + 1, self.h_negative_x.len()); + assert_eq!(self.d + 1, self.h_positive_x.len()); + + assert_eq!(self.d, self.g_negative_x_alpha.len()); + assert_eq!(self.d, self.g_positive_x_alpha.len()); + + assert_eq!(self.d + 1, self.h_negative_x_alpha.len()); + assert_eq!(self.d + 1, self.h_positive_x_alpha.len()); + + writer.write_u32::(self.d as u32)?; + + for g in &self.g_negative_x[..] { + writer.write_all(g.into_uncompressed().as_ref())?; + } + for g in &self.g_positive_x[..] { + writer.write_all(g.into_uncompressed().as_ref())?; + } + + for g in &self.h_negative_x[..] { + writer.write_all(g.into_uncompressed().as_ref())?; + } + for g in &self.h_positive_x[..] { + writer.write_all(g.into_uncompressed().as_ref())?; + } + + for g in &self.g_negative_x_alpha[..] { + writer.write_all(g.into_uncompressed().as_ref())?; + } + for g in &self.g_positive_x_alpha[..] { + writer.write_all(g.into_uncompressed().as_ref())?; + } + + for g in &self.h_negative_x_alpha[..] { + writer.write_all(g.into_uncompressed().as_ref())?; + } + for g in &self.h_positive_x_alpha[..] { + 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 g_negative_x = vec![]; + let mut g_positive_x = vec![]; + + let mut h_negative_x = vec![]; + let mut h_positive_x = vec![]; + + let mut g_negative_x_alpha = vec![]; + let mut g_positive_x_alpha = vec![]; + + let mut h_negative_x_alpha = vec![]; + let mut h_positive_x_alpha = vec![]; + + let d = reader.read_u32::()? as usize; + + { + for _ in 0..(d+1) { + g_negative_x.push(read_g1(&mut reader)?); + } + for _ in 0..(d+1) { + g_positive_x.push(read_g1(&mut reader)?); + } + } + + { + for _ in 0..(d+1) { + h_negative_x.push(read_g2(&mut reader)?); + } + for _ in 0..(d+1) { + h_positive_x.push(read_g2(&mut reader)?); + } + } + + { + for _ in 0..d { + g_negative_x_alpha.push(read_g1(&mut reader)?); + } + for _ in 0..d { + g_positive_x_alpha.push(read_g1(&mut reader)?); + } + } + + { + for _ in 0..(d+1) { + h_negative_x_alpha.push(read_g2(&mut reader)?); + } + for _ in 0..(d+1) { + h_positive_x_alpha.push(read_g2(&mut reader)?); + } + } + + Ok(Parameters { + vk: vk, + d: d, + g_negative_x: Arc::new(g_negative_x), + g_positive_x: Arc::new(g_positive_x), + h_negative_x: Arc::new(h_negative_x), + h_positive_x: Arc::new(h_positive_x), + g_negative_x_alpha: Arc::new(g_negative_x_alpha), + g_positive_x_alpha: Arc::new(g_positive_x_alpha), + h_negative_x_alpha: Arc::new(h_negative_x_alpha), + h_positive_x_alpha: Arc::new(h_positive_x_alpha) + }) + } +} + +// 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/sonic/sonic/adaptor.rs b/src/sonic/sonic/adaptor.rs index 6bed160..cf96240 100644 --- a/src/sonic/sonic/adaptor.rs +++ b/src/sonic/sonic/adaptor.rs @@ -16,10 +16,9 @@ use crate::sonic::cs::Circuit as SonicCircuit; use crate::sonic::cs::ConstraintSystem as SonicConstraintSystem; use crate::sonic::cs::Variable as SonicVariable; use crate::sonic::cs::Coeff; -// use crate::sonic::cs::synthesis::*; use std::marker::PhantomData; -struct Adaptor<'a, E: Engine, CS: SonicConstraintSystem + 'a> { +pub struct Adaptor<'a, E: Engine, CS: SonicConstraintSystem + 'a> { cs: &'a mut CS, _marker: PhantomData, } @@ -112,7 +111,7 @@ impl<'a, E: Engine, CS: SonicConstraintSystem + 'a> crate::ConstraintSystem { pub d: usize, @@ -29,6 +34,20 @@ pub struct SRS { pub h_positive_x_alpha: Vec, } +impl PartialEq for SRS { + fn eq(&self, other: &SRS) -> bool { + self.d == other.d && + self.g_negative_x == other.g_negative_x && + self.g_positive_x == other.g_positive_x && + self.h_negative_x == other.h_negative_x && + self.h_positive_x == other.h_positive_x && + self.g_negative_x_alpha == other.g_negative_x_alpha && + self.g_positive_x_alpha == other.g_positive_x_alpha && + self.h_negative_x_alpha == other.h_negative_x_alpha && + self.h_positive_x_alpha == other.h_positive_x_alpha + } +} + impl SRS { pub fn dummy(d: usize, _: E::Fr, _: E::Fr) -> Self { SRS { @@ -93,3 +112,163 @@ impl SRS { } } } + +impl SRS { + pub fn write( + &self, + mut writer: W + ) -> io::Result<()> + { + assert_eq!(self.d + 1, self.g_negative_x.len()); + assert_eq!(self.d + 1, self.g_positive_x.len()); + + assert_eq!(self.d + 1, self.h_negative_x.len()); + assert_eq!(self.d + 1, self.h_positive_x.len()); + + assert_eq!(self.d, self.g_negative_x_alpha.len()); + assert_eq!(self.d, self.g_positive_x_alpha.len()); + + assert_eq!(self.d + 1, self.h_negative_x_alpha.len()); + assert_eq!(self.d + 1, self.h_positive_x_alpha.len()); + + writer.write_u32::(self.d as u32)?; + + for g in &self.g_negative_x[..] { + writer.write_all(g.into_uncompressed().as_ref())?; + } + for g in &self.g_positive_x[..] { + writer.write_all(g.into_uncompressed().as_ref())?; + } + + for g in &self.h_negative_x[..] { + writer.write_all(g.into_uncompressed().as_ref())?; + } + for g in &self.h_positive_x[..] { + writer.write_all(g.into_uncompressed().as_ref())?; + } + + for g in &self.g_negative_x_alpha[..] { + writer.write_all(g.into_uncompressed().as_ref())?; + } + for g in &self.g_positive_x_alpha[..] { + writer.write_all(g.into_uncompressed().as_ref())?; + } + + for g in &self.h_negative_x_alpha[..] { + writer.write_all(g.into_uncompressed().as_ref())?; + } + for g in &self.h_positive_x_alpha[..] { + writer.write_all(g.into_uncompressed().as_ref())?; + } + + Ok(()) + } + + pub fn read( + mut reader: R, + checked: bool + ) -> io::Result + { + use pairing::EncodedPoint; + + 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 mut g_negative_x = vec![]; + let mut g_positive_x = vec![]; + + let mut h_negative_x = vec![]; + let mut h_positive_x = vec![]; + + let mut g_negative_x_alpha = vec![]; + let mut g_positive_x_alpha = vec![]; + + let mut h_negative_x_alpha = vec![]; + let mut h_positive_x_alpha = vec![]; + + let d = reader.read_u32::()? as usize; + + { + for _ in 0..(d+1) { + g_negative_x.push(read_g1(&mut reader)?); + } + for _ in 0..(d+1) { + g_positive_x.push(read_g1(&mut reader)?); + } + } + + { + for _ in 0..(d+1) { + h_negative_x.push(read_g2(&mut reader)?); + } + for _ in 0..(d+1) { + h_positive_x.push(read_g2(&mut reader)?); + } + } + + { + for _ in 0..d { + g_negative_x_alpha.push(read_g1(&mut reader)?); + } + for _ in 0..d { + g_positive_x_alpha.push(read_g1(&mut reader)?); + } + } + + { + for _ in 0..(d+1) { + h_negative_x_alpha.push(read_g2(&mut reader)?); + } + for _ in 0..(d+1) { + h_positive_x_alpha.push(read_g2(&mut reader)?); + } + } + + Ok(Self { + d: d, + g_negative_x: g_negative_x, + g_positive_x: g_positive_x, + h_negative_x: h_negative_x, + h_positive_x: h_positive_x, + g_negative_x_alpha: g_negative_x_alpha, + g_positive_x_alpha: g_positive_x_alpha, + h_negative_x_alpha: h_negative_x_alpha, + h_positive_x_alpha: h_positive_x_alpha + }) + } +} \ No newline at end of file diff --git a/tests/mimc.rs b/tests/mimc.rs index 3be384d..0037cd1 100644 --- a/tests/mimc.rs +++ b/tests/mimc.rs @@ -634,11 +634,24 @@ fn test_inputs_into_sonic_mimc() { } println!("done in {:?}", start.elapsed()); } - + { let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); println!("verifying 100 proofs with advice"); let start = Instant::now(); + { + for _ in 0..samples { + verifier.add_proof_with_advice(&proof, &[image], &advice); + } + assert_eq!(verifier.check_all(), true); // TODO + } + println!("done in {:?}", start.elapsed()); + } + + { + let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); + println!("verifying 100 proofs with advice and aggregate"); + let start = Instant::now(); { for (ref proof, ref advice) in &proofs { verifier.add_proof_with_advice(proof, &[image], advice); From 7aa7f3538dfcc2e172d12b19ac17f8d766495c41 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Thu, 7 Feb 2019 14:30:50 +0300 Subject: [PATCH 14/42] export functions --- src/sonic/helped/generator.rs | 13 ++++++------- src/sonic/helped/mod.rs | 9 +++++++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/sonic/helped/generator.rs b/src/sonic/helped/generator.rs index e385781..723d1d9 100644 --- a/src/sonic/helped/generator.rs +++ b/src/sonic/helped/generator.rs @@ -221,8 +221,10 @@ impl<'a, E: Engine, CS: SonicConstraintSystem + 'a> crate::ConstraintSystem( +/// Get circuit information such as number of input, variables, +/// constraints, and the corresponding SONIC parameters +/// k_map, n, q +pub fn get_circuit_parameters( circuit: C, ) -> Result, SynthesisError> where E: Engine, C: Circuit @@ -239,7 +241,7 @@ fn get_circuit_parameters( impl> SonicConstraintSystem for NonassigningSynthesizer { const ONE: SonicVariable = SonicVariable::A(1); - fn alloc(&mut self, value: F) -> Result + fn alloc(&mut self, _value: F) -> Result where F: FnOnce() -> Result { @@ -287,7 +289,7 @@ fn get_circuit_parameters( } } - fn multiply(&mut self, values: F) -> Result<(SonicVariable, SonicVariable, SonicVariable), SynthesisError> + fn multiply(&mut self, _values: F) -> Result<(SonicVariable, SonicVariable, SonicVariable), SynthesisError> where F: FnOnce() -> Result<(E::Fr, E::Fr, E::Fr), SynthesisError> { @@ -347,9 +349,6 @@ fn get_circuit_parameters( _ => return Err(SynthesisError::UnconstrainedVariable) } - // let adapted_circuit = AdaptorCircuit(circuit); - - let mut assembly = GeneratorAssembly::<'_, E, _> { cs: &mut cs, num_inputs: 0, diff --git a/src/sonic/helped/mod.rs b/src/sonic/helped/mod.rs index 6f82c79..2a81c7b 100644 --- a/src/sonic/helped/mod.rs +++ b/src/sonic/helped/mod.rs @@ -17,4 +17,13 @@ pub use self::batch::{Batch}; pub use self::helper::{Aggregate, create_aggregate}; pub use self::verifier::{MultiVerifier}; pub use self::prover::{create_proof, create_advice}; +pub use self::generator::{ + CircuitParameters, + generate_parameters, + generate_parameters_on_srs, + generate_parameters_on_srs_and_information, + generate_random_parameters, + generate_srs, + get_circuit_parameters +}; pub use self::parameters::{Proof, SxyAdvice, Parameters, VerifyingKey, PreparedVerifyingKey}; \ No newline at end of file From dc5e5ffe3163384f35befb5bc4a396705633e825 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Thu, 7 Feb 2019 15:04:40 +0300 Subject: [PATCH 15/42] whoops, prover needs not 3*n, but 4*n --- src/sonic/helped/generator.rs | 8 ++++---- src/sonic/util.rs | 1 + tests/mimc.rs | 5 ++++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/sonic/helped/generator.rs b/src/sonic/helped/generator.rs index 723d1d9..7b88823 100644 --- a/src/sonic/helped/generator.rs +++ b/src/sonic/helped/generator.rs @@ -381,7 +381,7 @@ pub fn generate_parameters( where E: Engine, C: Circuit { let circuit_parameters = get_circuit_parameters::(circuit)?; - let min_d = circuit_parameters.n * 3; + let min_d = circuit_parameters.n * 4; let srs = generate_srs(alpha, x, min_d)?; @@ -407,8 +407,8 @@ pub fn generate_parameters_on_srs_and_information( information: CircuitParameters ) -> Result, SynthesisError> { - assert!(srs.d >= information.n * 3); - let min_d = information.n * 3; + assert!(srs.d >= information.n * 4); + let min_d = information.n * 4; let trimmed_srs: SRS = SRS { d: min_d, @@ -736,7 +736,7 @@ fn parameters_generation() { use rand::{Rand, thread_rng}; let info = get_circuit_parameters::(MySillyCircuit { a: None, b: None }).expect("Must get circuit info"); - + println!("{:?}", info); let rng = &mut thread_rng(); let x: Fr = rng.gen(); diff --git a/src/sonic/util.rs b/src/sonic/util.rs index 70982d2..0903ec6 100644 --- a/src/sonic/util.rs +++ b/src/sonic/util.rs @@ -267,6 +267,7 @@ fn laurent_division() { } pub fn multiply_polynomials(a: Vec, b: Vec) -> Vec { + println!("Multiplying polynomails of degrees {} and {}", a.len(), b.len()); let result_len = a.len() + b.len() - 1; use crate::multicore::Worker; diff --git a/tests/mimc.rs b/tests/mimc.rs index 0037cd1..9d6e24f 100644 --- a/tests/mimc.rs +++ b/tests/mimc.rs @@ -578,7 +578,10 @@ fn test_inputs_into_sonic_mimc() { use bellman::sonic::cs::Basic; use bellman::sonic::sonic::AdaptorCircuit; - use bellman::sonic::helped::{create_proof, create_advice, create_aggregate, MultiVerifier}; + use bellman::sonic::helped::{create_proof, get_circuit_parameters, create_advice, create_aggregate, MultiVerifier}; + + let info = get_circuit_parameters::(circuit.clone()).expect("Must get circuit info"); + println!("{:?}", info); println!("creating proof"); let start = Instant::now(); From 056b7873cc26d175c2bcd2ac889ba98211a1fe10 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Thu, 7 Feb 2019 17:19:20 +0300 Subject: [PATCH 16/42] implement parallelized polynomial evaluation --- src/multicore.rs | 4 + src/sonic/helped/batch.rs | 21 ++++ src/sonic/helped/generator.rs | 10 +- src/sonic/helped/mod.rs | 8 +- src/sonic/helped/parameters.rs | 187 ++++----------------------------- src/sonic/helped/prover.rs | 63 +++++++---- src/sonic/util.rs | 177 +++++++++++++++++++++++++++++++ tests/mimc.rs | 8 +- 8 files changed, 278 insertions(+), 200 deletions(-) diff --git a/src/multicore.rs b/src/multicore.rs index 84055d7..14fab7a 100644 --- a/src/multicore.rs +++ b/src/multicore.rs @@ -35,6 +35,10 @@ impl Worker { log2_floor(self.cpus) } + pub(crate) fn num_cpus(&self) -> usize { + self.cpus + } + pub fn compute( &self, f: F ) -> WorkerFuture diff --git a/src/sonic/helped/batch.rs b/src/sonic/helped/batch.rs index 3a7e23a..9af97c2 100644 --- a/src/sonic/helped/batch.rs +++ b/src/sonic/helped/batch.rs @@ -16,6 +16,8 @@ use crate::SynthesisError; use crate::sonic::cs::{Backend, SynthesisDriver}; use crate::sonic::cs::{Circuit}; +use super::parameters::VerifyingKey; + use crate::sonic::srs::SRS; use crate::sonic::util::multiexp; @@ -83,6 +85,25 @@ impl Batch { } } + pub fn new_from_key(vk: &VerifyingKey) -> Self { + Batch { + alpha_x: vec![], + alpha_x_precomp: vk.alpha_x.prepare(), + + alpha: vec![], + alpha_precomp: vk.alpha.prepare(), + + neg_h: vec![], + neg_h_precomp: vk.neg_h.prepare(), + + neg_x_n_minus_d: vec![], + neg_x_n_minus_d_precomp: vk.neg_x_n_minus_d.prepare(), + + value: E::Fr::zero(), + g: E::G1Affine::one(), + } + } + pub fn add_opening(&mut self, p: E::G1Affine, mut r: E::Fr, point: E::Fr) { self.alpha_x.push((p, r)); r.mul_assign(&point); diff --git a/src/sonic/helped/generator.rs b/src/sonic/helped/generator.rs index 7b88823..230ff89 100644 --- a/src/sonic/helped/generator.rs +++ b/src/sonic/helped/generator.rs @@ -452,15 +452,7 @@ pub fn generate_parameters_on_srs_and_information( Ok(Parameters{ vk: vk, - d: trimmed_srs.d, - g_negative_x: Arc::new(trimmed_srs.g_negative_x), - g_positive_x: Arc::new(trimmed_srs.g_positive_x), - h_negative_x: Arc::new(trimmed_srs.h_negative_x), - h_positive_x: Arc::new(trimmed_srs.h_positive_x), - g_negative_x_alpha: Arc::new(trimmed_srs.g_negative_x_alpha), - g_positive_x_alpha: Arc::new(trimmed_srs.g_positive_x_alpha), - h_negative_x_alpha: Arc::new(trimmed_srs.h_negative_x_alpha), - h_positive_x_alpha: Arc::new(trimmed_srs.h_positive_x_alpha) + srs: trimmed_srs }) } diff --git a/src/sonic/helped/mod.rs b/src/sonic/helped/mod.rs index 2a81c7b..8cfda80 100644 --- a/src/sonic/helped/mod.rs +++ b/src/sonic/helped/mod.rs @@ -16,7 +16,13 @@ mod generator; pub use self::batch::{Batch}; pub use self::helper::{Aggregate, create_aggregate}; pub use self::verifier::{MultiVerifier}; -pub use self::prover::{create_proof, create_advice}; +pub use self::prover::{ + create_advice, + create_advice_on_information_and_srs, + create_advice_on_srs, + create_proof +}; + pub use self::generator::{ CircuitParameters, generate_parameters, diff --git a/src/sonic/helped/parameters.rs b/src/sonic/helped/parameters.rs index edb7361..255899c 100644 --- a/src/sonic/helped/parameters.rs +++ b/src/sonic/helped/parameters.rs @@ -300,45 +300,38 @@ pub struct PreparedVerifyingKey { pub struct Parameters { pub vk: VerifyingKey, - pub d: usize, + pub srs: SRS, + // pub d: usize, - // g^{x^0}, g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}} - pub g_negative_x: Arc>, + // // g^{x^0}, g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}} + // pub g_negative_x: Arc>, - // g^{x^0}, g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}} - pub g_positive_x: Arc>, + // // g^{x^0}, g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}} + // pub g_positive_x: Arc>, - // g^{x^0}, g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}} - pub h_negative_x: Arc>, + // // g^{x^0}, g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}} + // pub h_negative_x: Arc>, - // g^{x^0}, g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}} - pub h_positive_x: Arc>, + // // g^{x^0}, g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}} + // pub h_positive_x: Arc>, - // alpha*(g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}}) - pub g_negative_x_alpha: Arc>, + // // alpha*(g^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}}) + // pub g_negative_x_alpha: Arc>, - // alpha*(g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}}) - pub g_positive_x_alpha: Arc>, + // // alpha*(g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}}) + // pub g_positive_x_alpha: Arc>, - // alpha*(h^{x^0}, h^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}}) - pub h_negative_x_alpha: Arc>, + // // alpha*(h^{x^0}, h^{x^{-1}}, g^{x^{-2}}, ..., g^{x^{-d}}) + // pub h_negative_x_alpha: Arc>, - // alpha*(h^{x^0}, g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}}) - pub h_positive_x_alpha: Arc>, + // // alpha*(h^{x^0}, g^{x^{1}}, g^{x^{2}}, ..., g^{x^{d}}) + // pub h_positive_x_alpha: Arc>, } impl PartialEq for Parameters { fn eq(&self, other: &Parameters) -> bool { self.vk == other.vk && - self.d == other.d && - self.g_negative_x == other.g_negative_x && - self.g_positive_x == other.g_positive_x && - self.h_negative_x == other.h_negative_x && - self.h_positive_x == other.h_positive_x && - self.g_negative_x_alpha == other.g_negative_x_alpha && - self.g_positive_x_alpha == other.g_positive_x_alpha && - self.h_negative_x_alpha == other.h_negative_x_alpha && - self.h_positive_x_alpha == other.h_positive_x_alpha + self.srs == other.srs } } @@ -349,48 +342,7 @@ impl Parameters { ) -> io::Result<()> { self.vk.write(&mut writer)?; - - assert_eq!(self.d + 1, self.g_negative_x.len()); - assert_eq!(self.d + 1, self.g_positive_x.len()); - - assert_eq!(self.d + 1, self.h_negative_x.len()); - assert_eq!(self.d + 1, self.h_positive_x.len()); - - assert_eq!(self.d, self.g_negative_x_alpha.len()); - assert_eq!(self.d, self.g_positive_x_alpha.len()); - - assert_eq!(self.d + 1, self.h_negative_x_alpha.len()); - assert_eq!(self.d + 1, self.h_positive_x_alpha.len()); - - writer.write_u32::(self.d as u32)?; - - for g in &self.g_negative_x[..] { - writer.write_all(g.into_uncompressed().as_ref())?; - } - for g in &self.g_positive_x[..] { - writer.write_all(g.into_uncompressed().as_ref())?; - } - - for g in &self.h_negative_x[..] { - writer.write_all(g.into_uncompressed().as_ref())?; - } - for g in &self.h_positive_x[..] { - writer.write_all(g.into_uncompressed().as_ref())?; - } - - for g in &self.g_negative_x_alpha[..] { - writer.write_all(g.into_uncompressed().as_ref())?; - } - for g in &self.g_positive_x_alpha[..] { - writer.write_all(g.into_uncompressed().as_ref())?; - } - - for g in &self.h_negative_x_alpha[..] { - writer.write_all(g.into_uncompressed().as_ref())?; - } - for g in &self.h_positive_x_alpha[..] { - writer.write_all(g.into_uncompressed().as_ref())?; - } + self.srs.write(&mut writer)?; Ok(()) } @@ -400,107 +352,12 @@ impl Parameters { 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 srs = SRS::::read(&mut reader, checked)?; - let mut g_negative_x = vec![]; - let mut g_positive_x = vec![]; - - let mut h_negative_x = vec![]; - let mut h_positive_x = vec![]; - - let mut g_negative_x_alpha = vec![]; - let mut g_positive_x_alpha = vec![]; - - let mut h_negative_x_alpha = vec![]; - let mut h_positive_x_alpha = vec![]; - - let d = reader.read_u32::()? as usize; - - { - for _ in 0..(d+1) { - g_negative_x.push(read_g1(&mut reader)?); - } - for _ in 0..(d+1) { - g_positive_x.push(read_g1(&mut reader)?); - } - } - - { - for _ in 0..(d+1) { - h_negative_x.push(read_g2(&mut reader)?); - } - for _ in 0..(d+1) { - h_positive_x.push(read_g2(&mut reader)?); - } - } - - { - for _ in 0..d { - g_negative_x_alpha.push(read_g1(&mut reader)?); - } - for _ in 0..d { - g_positive_x_alpha.push(read_g1(&mut reader)?); - } - } - - { - for _ in 0..(d+1) { - h_negative_x_alpha.push(read_g2(&mut reader)?); - } - for _ in 0..(d+1) { - h_positive_x_alpha.push(read_g2(&mut reader)?); - } - } - Ok(Parameters { vk: vk, - d: d, - g_negative_x: Arc::new(g_negative_x), - g_positive_x: Arc::new(g_positive_x), - h_negative_x: Arc::new(h_negative_x), - h_positive_x: Arc::new(h_positive_x), - g_negative_x_alpha: Arc::new(g_negative_x_alpha), - g_positive_x_alpha: Arc::new(g_positive_x_alpha), - h_negative_x_alpha: Arc::new(h_negative_x_alpha), - h_positive_x_alpha: Arc::new(h_positive_x_alpha) + srs: srs }) } } diff --git a/src/sonic/helped/prover.rs b/src/sonic/helped/prover.rs index 65a29fa..2ae45ec 100644 --- a/src/sonic/helped/prover.rs +++ b/src/sonic/helped/prover.rs @@ -5,6 +5,7 @@ use std::marker::PhantomData; use super::{Proof, SxyAdvice}; use super::batch::Batch; use super::poly::{SxEval, SyEval}; +use super::parameters::{Parameters}; use crate::SynthesisError; @@ -14,31 +15,13 @@ use crate::sonic::cs::{Backend, SynthesisDriver}; use crate::sonic::cs::{Circuit, Variable, Coeff}; use crate::sonic::srs::SRS; -pub fn create_advice, S: SynthesisDriver>( +pub fn create_advice_on_information_and_srs, S: SynthesisDriver>( circuit: &C, proof: &Proof, - srs: &SRS + srs: &SRS, + n: usize ) -> SxyAdvice { - // annoying, but we need n to compute s(z, y), and this isn't - // precomputed anywhere yet - let n = { - struct CountN { - n: usize - } - - impl<'a, E: Engine> Backend for &'a mut CountN { - fn new_multiplication_gate(&mut self) { - self.n += 1; - } - } - - let mut tmp = CountN{n:0}; - S::synthesize(&mut tmp, circuit).unwrap(); // TODO - - tmp.n - }; - let z: E::Fr; let y: E::Fr; { @@ -111,6 +94,44 @@ pub fn create_advice, S: SynthesisDriver>( } } +pub fn create_advice, S: SynthesisDriver>( + circuit: &C, + proof: &Proof, + parameters: &Parameters, +) -> SxyAdvice +{ + let n = parameters.vk.n; + create_advice_on_information_and_srs::(circuit, proof, ¶meters.srs, n) +} + +pub fn create_advice_on_srs, S: SynthesisDriver>( + circuit: &C, + proof: &Proof, + srs: &SRS +) -> SxyAdvice +{ + // annoying, but we need n to compute s(z, y), and this isn't + // precomputed anywhere yet + let n = { + struct CountN { + n: usize + } + + impl<'a, E: Engine> Backend for &'a mut CountN { + fn new_multiplication_gate(&mut self) { + self.n += 1; + } + } + + let mut tmp = CountN{n:0}; + S::synthesize(&mut tmp, circuit).unwrap(); // TODO + + tmp.n + }; + + create_advice_on_information_and_srs::(circuit, proof, srs, n) +} + pub fn create_proof, S: SynthesisDriver>( circuit: &C, srs: &SRS diff --git a/src/sonic/util.rs b/src/sonic/util.rs index 0903ec6..2bc2850 100644 --- a/src/sonic/util.rs +++ b/src/sonic/util.rs @@ -71,6 +71,118 @@ where } } +extern crate crossbeam; +use self::crossbeam::channel::{unbounded, RecvError}; + +pub fn evaluate_at_consequitive_powers<'a, F: Field> ( + coeffs: &[F], + first_power: F, + base: F +) -> F + { + use crate::multicore::Worker; + + let (s, r) = unbounded(); + + let worker = Worker::new(); + + worker.scope(coeffs.len(), |scope, chunk| { + for (i, coeffs) in coeffs.chunks(chunk).enumerate() + { + let s = s.clone(); + scope.spawn(move |_| { + let mut current_power = base.pow(&[(i*chunk) as u64]); + current_power.mul_assign(&first_power); + + let mut acc = F::zero(); + + for p in coeffs { + let mut tmp = *p; + tmp.mul_assign(¤t_power); + acc.add_assign(&tmp); + + current_power.mul_assign(&base); + } + + s.send(acc).expect("must send"); + }); + } + }); + + drop(s); + + // all threads in a scope have done working, so we can safely read + let mut result = F::zero(); + + loop { + let v = r.recv(); + match v { + Ok(value) => { + result.add_assign(&value); + }, + Err(RecvError) => { + break; + } + } + } + + result +} + +pub fn mut_evaluate_at_consequitive_powers<'a, F: Field> ( + coeffs: &mut [F], + first_power: F, + base: F +) -> F + { + use crate::multicore::Worker; + + let (s, r) = unbounded(); + + let worker = Worker::new(); + + worker.scope(coeffs.len(), |scope, chunk| { + for (i, coeffs) in coeffs.chunks_mut(chunk).enumerate() + { + let s = s.clone(); + scope.spawn(move |_| { + let mut current_power = base.pow(&[(i*chunk) as u64]); + current_power.mul_assign(&first_power); + + let mut acc = F::zero(); + + for mut p in coeffs { + p.mul_assign(¤t_power); + acc.add_assign(&p); + + current_power.mul_assign(&base); + } + + s.send(acc).expect("must send"); + }); + } + }); + + drop(s); + + // all threads in a scope have done working, so we can safely read + let mut result = F::zero(); + + loop { + let v = r.recv(); + match v { + Ok(value) => { + result.add_assign(&value); + }, + Err(RecvError) => { + break; + } + } + } + + result +} + pub fn multiexp< 'a, G: CurveAffine, @@ -420,4 +532,69 @@ fn test_mul() { assert_eq!(serial_res.len(), parallel_res.len()); assert_eq!(serial_res, parallel_res); +} + +#[test] +fn test_eval_at_powers() { + use rand::{self, Rand, Rng}; + use pairing::bls12_381::Bls12; + use pairing::bls12_381::Fr; + + const SAMPLES: usize = 100000; + + let rng = &mut rand::thread_rng(); + let a = (0..SAMPLES).map(|_| Fr::rand(rng)).collect::>(); + let x: Fr = rng.gen(); + let n: u32 = rng.gen(); + + let mut acc = Fr::zero(); + + { + let mut tmp = x.pow(&[n as u64]); + + for coeff in a.iter() { + let mut c = *coeff; + c.mul_assign(&tmp); + acc.add_assign(&c); + tmp.mul_assign(&x); + } + } + + let first_power = x.pow(&[n as u64]); + let acc_parallel = evaluate_at_consequitive_powers(&a[..], first_power, x); + + assert_eq!(acc_parallel, acc); +} + +#[test] +fn test_mut_eval_at_powers() { + use rand::{self, Rand, Rng}; + use pairing::bls12_381::Bls12; + use pairing::bls12_381::Fr; + + const SAMPLES: usize = 100000; + + let rng = &mut rand::thread_rng(); + let mut a = (0..SAMPLES).map(|_| Fr::rand(rng)).collect::>(); + let mut b = a.clone(); + let x: Fr = rng.gen(); + let n: u32 = rng.gen(); + + let mut acc = Fr::zero(); + + { + let mut tmp = x.pow(&[n as u64]); + + for mut coeff in a.iter_mut() { + coeff.mul_assign(&tmp); + acc.add_assign(&coeff); + tmp.mul_assign(&x); + } + } + + let first_power = x.pow(&[n as u64]); + let acc_parallel = mut_evaluate_at_consequitive_powers(&mut b[..], first_power, x); + + assert_eq!(acc_parallel, acc); + assert!(a == b); } \ No newline at end of file diff --git a/tests/mimc.rs b/tests/mimc.rs index 9d6e24f..7d569a4 100644 --- a/tests/mimc.rs +++ b/tests/mimc.rs @@ -479,7 +479,7 @@ fn test_sonic_mimc() { use bellman::sonic::cs::Basic; use bellman::sonic::sonic::AdaptorCircuit; - use bellman::sonic::helped::{create_proof, create_advice, create_aggregate, MultiVerifier}; + use bellman::sonic::helped::{create_proof, create_advice, create_aggregate, MultiVerifier, create_advice_on_srs}; println!("creating proof"); let start = Instant::now(); @@ -488,7 +488,7 @@ fn test_sonic_mimc() { println!("creating advice"); let start = Instant::now(); - let advice = create_advice::(&AdaptorCircuit(circuit.clone()), &proof, &srs); + let advice = create_advice_on_srs::(&AdaptorCircuit(circuit.clone()), &proof, &srs); println!("done in {:?}", start.elapsed()); println!("creating aggregate for {} proofs", samples); @@ -578,7 +578,7 @@ fn test_inputs_into_sonic_mimc() { use bellman::sonic::cs::Basic; use bellman::sonic::sonic::AdaptorCircuit; - use bellman::sonic::helped::{create_proof, get_circuit_parameters, create_advice, create_aggregate, MultiVerifier}; + use bellman::sonic::helped::{create_proof, get_circuit_parameters, create_advice, create_aggregate, MultiVerifier, create_advice_on_srs}; let info = get_circuit_parameters::(circuit.clone()).expect("Must get circuit info"); println!("{:?}", info); @@ -590,7 +590,7 @@ fn test_inputs_into_sonic_mimc() { println!("creating advice"); let start = Instant::now(); - let advice = create_advice::(&AdaptorCircuit(circuit.clone()), &proof, &srs); + let advice = create_advice_on_srs::(&AdaptorCircuit(circuit.clone()), &proof, &srs); println!("done in {:?}", start.elapsed()); println!("creating aggregate for {} proofs", samples); From 614d486b99e5b4520d176952ddb4ebafd4e8dd86 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Thu, 7 Feb 2019 17:25:24 +0300 Subject: [PATCH 17/42] introduce error handling --- src/sonic/helped/prover.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/sonic/helped/prover.rs b/src/sonic/helped/prover.rs index 2ae45ec..43355d6 100644 --- a/src/sonic/helped/prover.rs +++ b/src/sonic/helped/prover.rs @@ -20,7 +20,7 @@ pub fn create_advice_on_information_and_srs, S: Synthes proof: &Proof, srs: &SRS, n: usize -) -> SxyAdvice +) -> Result, SynthesisError> { let z: E::Fr; let y: E::Fr; @@ -31,11 +31,11 @@ pub fn create_advice_on_information_and_srs, S: Synthes transcript.commit_point(&proof.t); z = transcript.get_challenge_scalar(); } - let z_inv = z.inverse().unwrap(); // TODO + let z_inv = z.inverse().ok_or(SynthesisError::DivisionByZero)?; let (s_poly_negative, s_poly_positive) = { let mut tmp = SxEval::new(y, n); - S::synthesize(&mut tmp, circuit).unwrap(); // TODO + S::synthesize(&mut tmp, circuit)?; tmp.poly() }; @@ -87,18 +87,18 @@ pub fn create_advice_on_information_and_srs, S: Synthes ).into_affine() }; - SxyAdvice { + Ok(SxyAdvice { s, szy, opening - } + }) } pub fn create_advice, S: SynthesisDriver>( circuit: &C, proof: &Proof, parameters: &Parameters, -) -> SxyAdvice +) -> Result, SynthesisError> { let n = parameters.vk.n; create_advice_on_information_and_srs::(circuit, proof, ¶meters.srs, n) @@ -108,7 +108,7 @@ pub fn create_advice_on_srs, S: SynthesisDriver>( circuit: &C, proof: &Proof, srs: &SRS -) -> SxyAdvice +) -> Result, SynthesisError> { // annoying, but we need n to compute s(z, y), and this isn't // precomputed anywhere yet @@ -124,7 +124,7 @@ pub fn create_advice_on_srs, S: SynthesisDriver>( } let mut tmp = CountN{n:0}; - S::synthesize(&mut tmp, circuit).unwrap(); // TODO + S::synthesize(&mut tmp, circuit)?; tmp.n }; @@ -216,7 +216,7 @@ pub fn create_proof, S: SynthesisDriver>( rx1.extend(wires.a); let mut rxy = rx1.clone(); - let y_inv = y.inverse().unwrap(); // TODO + let y_inv = y.inverse().ok_or(SynthesisError::DivisionByZero)?; let mut tmp = y.pow(&[n as u64]); for rxy in rxy.iter_mut().rev() { @@ -226,7 +226,7 @@ pub fn create_proof, S: SynthesisDriver>( let (s_poly_negative, s_poly_positive) = { let mut tmp = SxEval::new(y, n); - S::synthesize(&mut tmp, circuit).unwrap(); // TODO + S::synthesize(&mut tmp, circuit)?; tmp.poly() }; @@ -262,7 +262,7 @@ pub fn create_proof, S: SynthesisDriver>( transcript.commit_point(&t); let z: E::Fr = transcript.get_challenge_scalar(); - let z_inv = z.inverse().unwrap(); // TODO + let z_inv = z.inverse().ok_or(SynthesisError::DivisionByZero)?; // TODO: use the faster way to evaluate the polynomials let mut rz = E::Fr::zero(); From 3f766246ded60ba306eba045464b345e932bbcde Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Sun, 10 Feb 2019 01:36:40 +0300 Subject: [PATCH 18/42] continue to integrate helped SONIC. Now only verifier left --- Cargo.toml | 2 + src/domain.rs | 190 +-------------- src/group.rs | 82 +++++++ src/lib.rs | 24 +- src/multicore.rs | 4 - src/parallel_fft.rs | 143 ++++++++++++ src/{multiexp.rs => parallel_multiexp.rs} | 273 ++++++++++++---------- src/serial_fft.rs | 0 src/serial_multiexp.rs | 0 src/sonic/cs/mod.rs | 108 +++++++++ src/sonic/helped/adapted_prover.rs | 146 ++++++++++++ src/sonic/helped/generator.rs | 100 -------- src/sonic/helped/mod.rs | 19 +- src/sonic/helped/parameters.rs | 262 ++++++++------------- src/sonic/helped/prover.rs | 22 +- src/sonic/helped/verifier.rs | 1 + src/sonic/sonic/adaptor.rs | 3 +- src/sonic/util.rs | 17 +- src/source.rs | 141 +++++++++++ tests/mimc.rs | 40 +--- 20 files changed, 947 insertions(+), 630 deletions(-) create mode 100644 src/group.rs create mode 100644 src/parallel_fft.rs rename src/{multiexp.rs => parallel_multiexp.rs} (65%) create mode 100644 src/serial_fft.rs create mode 100644 src/serial_multiexp.rs create mode 100644 src/sonic/helped/adapted_prover.rs create mode 100644 src/source.rs diff --git a/Cargo.toml b/Cargo.toml index 5c62eac..761c6c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,3 +30,5 @@ rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9" [features] default = [] + +singlecore = [] diff --git a/src/domain.rs b/src/domain.rs index c3d011c..85c8a26 100644 --- a/src/domain.rs +++ b/src/domain.rs @@ -24,7 +24,12 @@ use super::{ SynthesisError }; + use super::multicore::Worker; +pub use super::group::*; + +#[feature(not(singlecore))] +use super::parallel_fft::*; pub struct EvaluationDomain> { coeffs: Vec, @@ -149,6 +154,7 @@ impl> EvaluationDomain { }) } + pub fn fft(&mut self, worker: &Worker) { best_fft(&mut self.coeffs, worker, &self.omega, self.exp); @@ -258,190 +264,6 @@ impl> EvaluationDomain { } } -pub trait Group: Sized + Copy + Clone + Send + Sync { - fn group_zero() -> Self; - fn group_mul_assign(&mut self, by: &E::Fr); - fn group_add_assign(&mut self, other: &Self); - fn group_sub_assign(&mut self, other: &Self); -} - -pub struct Point(pub G); - -impl PartialEq for Point { - fn eq(&self, other: &Point) -> bool { - self.0 == other.0 - } -} - -impl Copy for Point { } - -impl Clone for Point { - fn clone(&self) -> Point { - *self - } -} - -impl Group for Point { - fn group_zero() -> Self { - Point(G::zero()) - } - fn group_mul_assign(&mut self, by: &G::Scalar) { - self.0.mul_assign(by.into_repr()); - } - fn group_add_assign(&mut self, other: &Self) { - self.0.add_assign(&other.0); - } - fn group_sub_assign(&mut self, other: &Self) { - self.0.sub_assign(&other.0); - } -} - -pub struct Scalar(pub E::Fr); - -impl PartialEq for Scalar { - fn eq(&self, other: &Scalar) -> bool { - self.0 == other.0 - } -} - -impl Copy for Scalar { } - -impl Clone for Scalar { - fn clone(&self) -> Scalar { - *self - } -} - -impl Group for Scalar { - fn group_zero() -> Self { - Scalar(E::Fr::zero()) - } - fn group_mul_assign(&mut self, by: &E::Fr) { - self.0.mul_assign(by); - } - fn group_add_assign(&mut self, other: &Self) { - self.0.add_assign(&other.0); - } - fn group_sub_assign(&mut self, other: &Self) { - self.0.sub_assign(&other.0); - } -} - -pub(crate) fn best_fft>(a: &mut [T], worker: &Worker, omega: &E::Fr, log_n: u32) -{ - let log_cpus = worker.log_num_cpus(); - - if log_n <= log_cpus { - serial_fft(a, omega, log_n); - } else { - parallel_fft(a, worker, omega, log_n, log_cpus); - } -} - -pub(crate) fn serial_fft>(a: &mut [T], omega: &E::Fr, log_n: u32) -{ - fn bitreverse(mut n: u32, l: u32) -> u32 { - let mut r = 0; - for _ in 0..l { - r = (r << 1) | (n & 1); - n >>= 1; - } - r - } - - let n = a.len() as u32; - assert_eq!(n, 1 << log_n); - - for k in 0..n { - let rk = bitreverse(k, log_n); - if k < rk { - a.swap(rk as usize, k as usize); - } - } - - let mut m = 1; - for _ in 0..log_n { - let w_m = omega.pow(&[(n / (2*m)) as u64]); - - let mut k = 0; - while k < n { - let mut w = E::Fr::one(); - for j in 0..m { - let mut t = a[(k+j+m) as usize]; - t.group_mul_assign(&w); - let mut tmp = a[(k+j) as usize]; - tmp.group_sub_assign(&t); - a[(k+j+m) as usize] = tmp; - a[(k+j) as usize].group_add_assign(&t); - w.mul_assign(&w_m); - } - - k += 2*m; - } - - m *= 2; - } -} - -pub(crate) fn parallel_fft>( - a: &mut [T], - worker: &Worker, - omega: &E::Fr, - log_n: u32, - log_cpus: u32 -) -{ - assert!(log_n >= log_cpus); - - let num_cpus = 1 << log_cpus; - let log_new_n = log_n - log_cpus; - let mut tmp = vec![vec![T::group_zero(); 1 << log_new_n]; num_cpus]; - let new_omega = omega.pow(&[num_cpus as u64]); - - worker.scope(0, |scope, _| { - let a = &*a; - - for (j, tmp) in tmp.iter_mut().enumerate() { - scope.spawn(move |_| { - // Shuffle into a sub-FFT - let omega_j = omega.pow(&[j as u64]); - let omega_step = omega.pow(&[(j as u64) << log_new_n]); - - let mut elt = E::Fr::one(); - for i in 0..(1 << log_new_n) { - for s in 0..num_cpus { - let idx = (i + (s << log_new_n)) % (1 << log_n); - let mut t = a[idx]; - t.group_mul_assign(&elt); - tmp[i].group_add_assign(&t); - elt.mul_assign(&omega_step); - } - elt.mul_assign(&omega_j); - } - - // Perform sub-FFT - serial_fft(tmp, &new_omega, log_new_n); - }); - } - }); - - // TODO: does this hurt or help? - worker.scope(a.len(), |scope, chunk| { - let tmp = &tmp; - - for (idx, a) in a.chunks_mut(chunk).enumerate() { - scope.spawn(move |_| { - let mut idx = idx * chunk; - let mask = (1 << log_cpus) - 1; - for a in a { - *a = tmp[idx & mask][idx >> log_cpus]; - idx += 1; - } - }); - } - }); -} - // Test multiplying various (low degree) polynomials together and // comparing with naive evaluations. #[test] diff --git a/src/group.rs b/src/group.rs new file mode 100644 index 0000000..7b4fe2e --- /dev/null +++ b/src/group.rs @@ -0,0 +1,82 @@ +use pairing::{ + Engine, + CurveProjective +}; + +use ff::{ + Field, + PrimeField +}; + +use super::{ + SynthesisError +}; + +pub trait Group: Sized + Copy + Clone + Send + Sync { + fn group_zero() -> Self; + fn group_mul_assign(&mut self, by: &E::Fr); + fn group_add_assign(&mut self, other: &Self); + fn group_sub_assign(&mut self, other: &Self); +} + +pub struct Point(pub G); + +impl PartialEq for Point { + fn eq(&self, other: &Point) -> bool { + self.0 == other.0 + } +} + +impl Copy for Point { } + +impl Clone for Point { + fn clone(&self) -> Point { + *self + } +} + +impl Group for Point { + fn group_zero() -> Self { + Point(G::zero()) + } + fn group_mul_assign(&mut self, by: &G::Scalar) { + self.0.mul_assign(by.into_repr()); + } + fn group_add_assign(&mut self, other: &Self) { + self.0.add_assign(&other.0); + } + fn group_sub_assign(&mut self, other: &Self) { + self.0.sub_assign(&other.0); + } +} + +pub struct Scalar(pub E::Fr); + +impl PartialEq for Scalar { + fn eq(&self, other: &Scalar) -> bool { + self.0 == other.0 + } +} + +impl Copy for Scalar { } + +impl Clone for Scalar { + fn clone(&self) -> Scalar { + *self + } +} + +impl Group for Scalar { + fn group_zero() -> Self { + Scalar(E::Fr::zero()) + } + fn group_mul_assign(&mut self, by: &E::Fr) { + self.0.mul_assign(by); + } + fn group_add_assign(&mut self, other: &Self) { + self.0.add_assign(&other.0); + } + fn group_sub_assign(&mut self, other: &Self) { + self.0.sub_assign(&other.0); + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 1ef74b4..0e9c650 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,13 +10,23 @@ extern crate crossbeam; extern crate byteorder; extern crate ff; -pub mod multicore; -mod multiexp; pub mod domain; pub mod groth16; pub mod gm17; pub mod sonic; +mod group; +mod source; + +#[feature(not(singlecore))] +mod parallel_fft; +mod multicore; +mod parallel_multiexp; + +#[feature(singlecore)] +mod serial_fft; +mod serial_multiexp; + #[cfg(test)] mod tests; @@ -29,6 +39,16 @@ use std::error::Error; use std::io; use std::marker::PhantomData; +pub mod multiexp { + pub use source::*; + + #[feature(not(singlecore))] + pub use parallel_multiexp::*; + + #[feature(singlecore)] + pub use serial_multiexp::*; +} + /// Computations are expressed in terms of arithmetic circuits, in particular /// rank-1 quadratic constraint systems. The `Circuit` trait represents a /// circuit that can be synthesized. The `synthesize` method is called during diff --git a/src/multicore.rs b/src/multicore.rs index 14fab7a..84055d7 100644 --- a/src/multicore.rs +++ b/src/multicore.rs @@ -35,10 +35,6 @@ impl Worker { log2_floor(self.cpus) } - pub(crate) fn num_cpus(&self) -> usize { - self.cpus - } - pub fn compute( &self, f: F ) -> WorkerFuture diff --git a/src/parallel_fft.rs b/src/parallel_fft.rs new file mode 100644 index 0000000..687d146 --- /dev/null +++ b/src/parallel_fft.rs @@ -0,0 +1,143 @@ +//! This module contains an `EvaluationDomain` abstraction for +//! performing various kinds of polynomial arithmetic on top of +//! the scalar field. +//! +//! In pairing-based SNARKs like Groth16, we need to calculate +//! a quotient polynomial over a target polynomial with roots +//! at distinct points associated with each constraint of the +//! constraint system. In order to be efficient, we choose these +//! roots to be the powers of a 2^n root of unity in the field. +//! This allows us to perform polynomial operations in O(n) +//! by performing an O(n log n) FFT over such a domain. + +use pairing::{ + Engine, + CurveProjective +}; + +use ff::{ + Field, + PrimeField +}; + +use super::{ + SynthesisError +}; + +use super::multicore::Worker; +use super::group::*; + +pub(crate) fn best_fft>(a: &mut [T], worker: &Worker, omega: &E::Fr, log_n: u32) +{ + let log_cpus = worker.log_num_cpus(); + + if log_n <= log_cpus { + serial_fft(a, omega, log_n); + } else { + parallel_fft(a, worker, omega, log_n, log_cpus); + } +} + +pub(crate) fn serial_fft>(a: &mut [T], omega: &E::Fr, log_n: u32) +{ + fn bitreverse(mut n: u32, l: u32) -> u32 { + let mut r = 0; + for _ in 0..l { + r = (r << 1) | (n & 1); + n >>= 1; + } + r + } + + let n = a.len() as u32; + assert_eq!(n, 1 << log_n); + + for k in 0..n { + let rk = bitreverse(k, log_n); + if k < rk { + a.swap(rk as usize, k as usize); + } + } + + let mut m = 1; + for _ in 0..log_n { + let w_m = omega.pow(&[(n / (2*m)) as u64]); + + let mut k = 0; + while k < n { + let mut w = E::Fr::one(); + for j in 0..m { + let mut t = a[(k+j+m) as usize]; + t.group_mul_assign(&w); + let mut tmp = a[(k+j) as usize]; + tmp.group_sub_assign(&t); + a[(k+j+m) as usize] = tmp; + a[(k+j) as usize].group_add_assign(&t); + w.mul_assign(&w_m); + } + + k += 2*m; + } + + m *= 2; + } +} + +pub(crate) fn parallel_fft>( + a: &mut [T], + worker: &Worker, + omega: &E::Fr, + log_n: u32, + log_cpus: u32 +) +{ + assert!(log_n >= log_cpus); + + let num_cpus = 1 << log_cpus; + let log_new_n = log_n - log_cpus; + let mut tmp = vec![vec![T::group_zero(); 1 << log_new_n]; num_cpus]; + let new_omega = omega.pow(&[num_cpus as u64]); + + worker.scope(0, |scope, _| { + let a = &*a; + + for (j, tmp) in tmp.iter_mut().enumerate() { + scope.spawn(move |_| { + // Shuffle into a sub-FFT + let omega_j = omega.pow(&[j as u64]); + let omega_step = omega.pow(&[(j as u64) << log_new_n]); + + let mut elt = E::Fr::one(); + for i in 0..(1 << log_new_n) { + for s in 0..num_cpus { + let idx = (i + (s << log_new_n)) % (1 << log_n); + let mut t = a[idx]; + t.group_mul_assign(&elt); + tmp[i].group_add_assign(&t); + elt.mul_assign(&omega_step); + } + elt.mul_assign(&omega_j); + } + + // Perform sub-FFT + serial_fft(tmp, &new_omega, log_new_n); + }); + } + }); + + // TODO: does this hurt or help? + worker.scope(a.len(), |scope, chunk| { + let tmp = &tmp; + + for (idx, a) in a.chunks_mut(chunk).enumerate() { + scope.spawn(move |_| { + let mut idx = idx * chunk; + let mask = (1 << log_cpus) - 1; + for a in a { + *a = tmp[idx & mask][idx >> log_cpus]; + idx += 1; + } + }); + } + }); +} \ No newline at end of file diff --git a/src/multiexp.rs b/src/parallel_multiexp.rs similarity index 65% rename from src/multiexp.rs rename to src/parallel_multiexp.rs index e834f4c..60e1dc3 100644 --- a/src/multiexp.rs +++ b/src/parallel_multiexp.rs @@ -11,137 +11,12 @@ use ff::{ ScalarEngine}; use std::sync::Arc; -use std::io; -use bit_vec::{self, BitVec}; -use std::iter; +use super::source::*; use futures::{Future}; use super::multicore::Worker; use super::SynthesisError; -/// An object that builds a source of bases. -pub trait SourceBuilder: Send + Sync + 'static + Clone { - type Source: Source; - - fn new(self) -> Self::Source; -} - -/// A source of bases, like an iterator. -pub trait Source { - /// Parses the element from the source. Fails if the point is at infinity. - fn add_assign_mixed(&mut self, to: &mut ::Projective) -> Result<(), SynthesisError>; - - /// Skips `amt` elements from the source, avoiding deserialization. - fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>; -} - -impl SourceBuilder for (Arc>, usize) { - type Source = (Arc>, usize); - - fn new(self) -> (Arc>, usize) { - (self.0.clone(), self.1) - } -} - -impl Source for (Arc>, usize) { - fn add_assign_mixed(&mut self, to: &mut ::Projective) -> Result<(), SynthesisError> { - if self.0.len() <= self.1 { - return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases when adding from source").into()); - } - - if self.0[self.1].is_zero() { - return Err(SynthesisError::UnexpectedIdentity) - } - - to.add_assign_mixed(&self.0[self.1]); - - self.1 += 1; - - Ok(()) - } - - fn skip(&mut self, amt: usize) -> Result<(), SynthesisError> { - if self.0.len() <= self.1 { - return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases skipping from source").into()); - } - - self.1 += amt; - - Ok(()) - } -} - -pub trait QueryDensity { - /// Returns whether the base exists. - type Iter: Iterator; - - fn iter(self) -> Self::Iter; - fn get_query_size(self) -> Option; -} - -#[derive(Clone)] -pub struct FullDensity; - -impl AsRef for FullDensity { - fn as_ref(&self) -> &FullDensity { - self - } -} - -impl<'a> QueryDensity for &'a FullDensity { - type Iter = iter::Repeat; - - fn iter(self) -> Self::Iter { - iter::repeat(true) - } - - fn get_query_size(self) -> Option { - None - } -} - -#[derive(Clone)] -pub struct DensityTracker { - bv: BitVec, - total_density: usize -} - -impl<'a> QueryDensity for &'a DensityTracker { - type Iter = bit_vec::Iter<'a>; - - fn iter(self) -> Self::Iter { - self.bv.iter() - } - - fn get_query_size(self) -> Option { - Some(self.bv.len()) - } -} - -impl DensityTracker { - pub fn new() -> DensityTracker { - DensityTracker { - bv: BitVec::new(), - total_density: 0 - } - } - - pub fn add_element(&mut self) { - self.bv.push(false); - } - - pub fn inc(&mut self, idx: usize) { - if !self.bv.get(idx).unwrap() { - self.bv.set(idx, true); - self.total_density += 1; - } - } - - pub fn get_total_density(&self) -> usize { - self.total_density - } -} - /// This genious piece of code works in the following way: /// - choose `c` - the bit length of the region that one thread works on /// - make `2^c - 1` buckets and initialize them with `G = infinity` (that's equivalent of zero) @@ -307,6 +182,114 @@ pub fn multiexp( multiexp_inner(pool, bases, density_map, exponents, 0, c, true) } + +/// Perform multi-exponentiation. The caller is responsible for ensuring that +/// the number of bases is the same as the number of exponents. +pub fn dense_multiexp( + pool: &Worker, + bases: & [G], + exponents: & [<::Fr as PrimeField>::Repr] +) -> Result<::Projective, SynthesisError> +{ + if exponents.len() != bases.len() { + return Err(SynthesisError::AssignmentMissing); + } + let c = if exponents.len() < 32 { + 3u32 + } else { + (f64::from(exponents.len() as u32)).ln().ceil() as u32 + }; + + dense_multiexp_inner(pool, bases, exponents, 0, c, true) +} + +fn dense_multiexp_inner( + pool: &Worker, + bases: & [G], + exponents: & [<::Fr as PrimeField>::Repr], + mut skip: u32, + c: u32, + handle_trivial: bool +) -> Result<::Projective, SynthesisError> +{ + // Perform this region of the multiexp. We use a different strategy - go over region in parallel, + // then over another region, etc. No Arc required + let this = { + let this_region = pool.scope(bases.len(), |scope, chunk| { + let mut handles = vec![]; + let mut this_acc = ::Projective::zero(); + for (base, exp) in bases.chunks(chunk).zip(exponents.chunks(chunk)) { + let handle = scope.spawn(move |_| { + let mut buckets = vec![::Projective::zero(); (1 << c) - 1]; + // Accumulate the result + let mut acc = G::Projective::zero(); + let zero = ::Fr::zero().into_repr(); + let one = ::Fr::one().into_repr(); + + for (base, &exp) in base.iter().zip(exp.iter()) { + if exp != zero { + if exp == one { + if handle_trivial { + acc.add_assign_mixed(base); + } + } else { + let mut exp = exp; + exp.shr(skip); + let exp = exp.as_ref()[0] % (1 << c); + if exp != 0 { + buckets[(exp - 1) as usize].add_assign_mixed(base); + } + } + } + } + + // buckets are filled with the corresponding accumulated value, now sum + let mut running_sum = G::Projective::zero(); + for exp in buckets.into_iter().rev() { + running_sum.add_assign(&exp); + acc.add_assign(&running_sum); + } + + // acc contains values over this region + acc + }); + + handles.push(handle); + } + + // wait for all threads to finish + for r in handles.into_iter().rev() { + let thread_result = r.join().unwrap(); + this_acc.add_assign(&thread_result); + } + + this_acc + }); + + this_region + }; + + skip += c; + + if skip >= ::Fr::NUM_BITS { + // There isn't another region, and this will be the highest region + return Ok(this); + } else { + // next region is actually higher than this one, so double it enough times + let mut next_region = dense_multiexp_inner( + pool, bases, exponents, skip, c, false).unwrap(); + for _ in 0..c { + next_region.double(); + } + + next_region.add_assign(&this); + + return Ok(next_region); + } +} + + + #[test] fn test_with_bls12() { fn naive_multiexp( @@ -378,3 +361,41 @@ fn test_speed_with_bn256() { let time_per_sample = duration_ns/(SAMPLES as f64); println!("Tested on {} samples on {} CPUs with {} ns per multiplication", SAMPLES, cpus, time_per_sample); } + + +#[test] +fn test_dense_multiexp() { + use rand::{self, Rand}; + use pairing::bn256::Bn256; + use num_cpus; + + const SAMPLES: usize = 1 << 22; + + let rng = &mut rand::thread_rng(); + let v = (0..SAMPLES).map(|_| ::Fr::rand(rng).into_repr()).collect::>(); + let g = (0..SAMPLES).map(|_| ::G1::rand(rng).into_affine()).collect::>(); + + let pool = Worker::new(); + + let start = std::time::Instant::now(); + + let dense = dense_multiexp( + &pool, &g, &v).unwrap(); + + let duration_ns = start.elapsed().as_nanos() as f64; + println!("{} ns for dense for {} samples", duration_ns, SAMPLES); + + let start = std::time::Instant::now(); + + let sparse = multiexp( + &pool, + (Arc::new(g), 0), + FullDensity, + Arc::new(v) + ).wait().unwrap(); + + let duration_ns = start.elapsed().as_nanos() as f64; + println!("{} ns for sparse for {} samples", duration_ns, SAMPLES); + + assert_eq!(dense, sparse); +} \ No newline at end of file diff --git a/src/serial_fft.rs b/src/serial_fft.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/serial_multiexp.rs b/src/serial_multiexp.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/sonic/cs/mod.rs b/src/sonic/cs/mod.rs index 342d408..4860cf5 100644 --- a/src/sonic/cs/mod.rs +++ b/src/sonic/cs/mod.rs @@ -217,3 +217,111 @@ impl SynthesisDriver for Basic { Ok(()) } } + +pub struct Nonassigning; + +impl SynthesisDriver for Nonassigning { + fn synthesize, B: Backend>(backend: B, circuit: &C) -> Result<(), SynthesisError> { + struct NonassigningSynthesizer> { + backend: B, + current_variable: Option, + _marker: PhantomData, + q: usize, + n: usize, + } + + impl> ConstraintSystem for NonassigningSynthesizer { + const ONE: Variable = Variable::A(1); + + fn alloc(&mut self, _value: F) -> Result + where + F: FnOnce() -> Result + { + match self.current_variable.take() { + Some(index) => { + let var_b = Variable::B(index); + + self.current_variable = None; + + Ok(var_b) + }, + None => { + self.n += 1; + let index = self.n; + self.backend.new_multiplication_gate(); + + let var_a = Variable::A(index); + + self.current_variable = Some(index); + + Ok(var_a) + } + } + } + + fn alloc_input(&mut self, value: F) -> Result + where + F: FnOnce() -> Result + { + let input_var = self.alloc(value)?; + + self.enforce_zero(LinearCombination::zero() + input_var); + self.backend.new_k_power(self.q); + + Ok(input_var) + } + + fn enforce_zero(&mut self, lc: LinearCombination) + { + self.q += 1; + self.backend.new_linear_constraint(); + + for (var, coeff) in lc.as_ref() { + self.backend.insert_coefficient(*var, *coeff); + } + } + + fn multiply(&mut self, _values: F) -> Result<(Variable, Variable, Variable), SynthesisError> + where + F: FnOnce() -> Result<(E::Fr, E::Fr, E::Fr), SynthesisError> + { + self.n += 1; + let index = self.n; + self.backend.new_multiplication_gate(); + + let a = Variable::A(index); + let b = Variable::B(index); + let c = Variable::C(index); + + Ok((a, b, c)) + } + + fn get_value(&self, var: Variable) -> Result { + self.backend.get_var(var).ok_or(()) + } + } + + let mut tmp: NonassigningSynthesizer = NonassigningSynthesizer { + backend: backend, + current_variable: None, + _marker: PhantomData, + q: 0, + n: 0, + }; + + let one = tmp.alloc_input(|| Ok(E::Fr::one())).expect("should have no issues"); + + match (one, as ConstraintSystem>::ONE) { + (Variable::A(1), Variable::A(1)) => {}, + _ => panic!("one variable is incorrect") + } + + circuit.synthesize(&mut tmp)?; + + // TODO: add blinding factors so we actually get zero-knowledge + + // println!("n = {}", tmp.n); + + Ok(()) + } +} \ No newline at end of file diff --git a/src/sonic/helped/adapted_prover.rs b/src/sonic/helped/adapted_prover.rs new file mode 100644 index 0000000..2811db4 --- /dev/null +++ b/src/sonic/helped/adapted_prover.rs @@ -0,0 +1,146 @@ +use ff::{Field}; +use pairing::{Engine, CurveProjective}; +use std::marker::PhantomData; + +use super::{Proof, SxyAdvice}; +use super::batch::Batch; +use super::poly::{SxEval, SyEval}; +use super::parameters::{Parameters}; + +use crate::SynthesisError; + +use crate::sonic::transcript::{Transcript, TranscriptProtocol}; +use crate::sonic::util::*; +use crate::sonic::cs::{Backend, SynthesisDriver}; +use crate::{Circuit}; +use crate::sonic::sonic::AdaptorCircuit; +use crate::sonic::srs::SRS; +use crate::sonic::cs::Basic; +use super::prover::create_advice as create_advice_sonic_circuit; +use super::prover::create_advice_on_information_and_srs as create_advice_on_information_and_srs_sonic_circuit; +use super::prover::create_proof_on_srs as create_proof_on_srs_sonic_circuit; + +// pub fn create_advice_on_information_and_srs + Clone, S: SynthesisDriver>( +pub fn create_advice_on_information_and_srs + Clone>( + circuit: C, + proof: &Proof, + srs: &SRS, + n: usize +) -> Result, SynthesisError> +{ + let adapted_circuit = AdaptorCircuit(circuit); + + create_advice_on_information_and_srs_sonic_circuit::<_, _, Basic>(&adapted_circuit, proof, srs, n) +} + +// pub fn create_advice + Clone, S: SynthesisDriver>( +pub fn create_advice + Clone>( + circuit: C, + proof: &Proof, + parameters: &Parameters, +) -> Result, SynthesisError> +{ + let n = parameters.vk.n; + create_advice_on_information_and_srs::(circuit, proof, ¶meters.srs, n) +} + +// pub fn create_advice_on_srs + Clone, S: SynthesisDriver>( +pub fn create_advice_on_srs + Clone>( + circuit: C, + proof: &Proof, + srs: &SRS +) -> Result, SynthesisError> +{ + use crate::sonic::cs::Nonassigning; + + let adapted_circuit = AdaptorCircuit(circuit.clone()); + // annoying, but we need n to compute s(z, y), and this isn't + // precomputed anywhere yet + let n = { + struct CountN { + n: usize + } + + impl<'a, E: Engine> Backend for &'a mut CountN { + fn new_multiplication_gate(&mut self) { + self.n += 1; + } + } + + let mut tmp = CountN{n:0}; + Nonassigning::synthesize(&mut tmp, &adapted_circuit)?; + + tmp.n + }; + + create_advice_on_information_and_srs::(circuit, proof, srs, n) +} + +// pub fn create_proof + Clone, S: SynthesisDriver>( +pub fn create_proof + Clone>( + circuit: C, + parameters: &Parameters +) -> Result, SynthesisError> { + create_proof_on_srs::(circuit, ¶meters.srs) +} + +// pub fn create_proof_on_srs + Clone, S: SynthesisDriver>( +pub fn create_proof_on_srs + Clone>( + circuit: C, + srs: &SRS +) -> Result, SynthesisError> +{ + let adapted_circuit = AdaptorCircuit(circuit); + + create_proof_on_srs_sonic_circuit::<_, _, Basic>(&adapted_circuit, srs) +} + +// #[test] +// fn my_fun_circuit_test() { +// use ff::PrimeField; +// use pairing::bls12_381::{Bls12, Fr}; +// use super::*; +// use crate::sonic::cs::{Basic, ConstraintSystem, LinearCombination}; + +// struct MyCircuit; + +// impl Circuit for MyCircuit { +// fn synthesize>(&self, cs: &mut CS) -> Result<(), SynthesisError> { +// let (a, b, _) = cs.multiply(|| { +// Ok(( +// E::Fr::from_str("10").unwrap(), +// E::Fr::from_str("20").unwrap(), +// E::Fr::from_str("200").unwrap(), +// )) +// })?; + +// cs.enforce_zero(LinearCombination::from(a) + a - b); + +// //let multiplier = cs.alloc_input(|| Ok(E::Fr::from_str("20").unwrap()))?; + +// //cs.enforce_zero(LinearCombination::from(b) - multiplier); + +// Ok(()) +// } +// } + +// let srs = SRS::::new( +// 20, +// Fr::from_str("22222").unwrap(), +// Fr::from_str("33333333").unwrap(), +// ); +// let proof = create_proof_on_srs::(&MyCircuit, &srs).unwrap(); + +// use std::time::{Instant}; +// let start = Instant::now(); +// let mut batch = MultiVerifier::::new(MyCircuit, &srs).unwrap(); + +// for _ in 0..1 { +// batch.add_proof(&proof, &[/*Fr::from_str("20").unwrap()*/], |_, _| None); +// } + +// assert!(batch.check_all()); + +// let elapsed = start.elapsed(); +// println!("time to verify: {:?}", elapsed); +// } diff --git a/src/sonic/helped/generator.rs b/src/sonic/helped/generator.rs index 230ff89..ce7cea9 100644 --- a/src/sonic/helped/generator.rs +++ b/src/sonic/helped/generator.rs @@ -688,104 +688,4 @@ pub fn generate_srs( h_positive_x_alpha: h_positive_x_alpha, } ) -} - -#[test] -fn parameters_generation() { - use pairing::bls12_381::{Bls12, Fr}; - 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(()) - } - } - - use rand::{Rand, thread_rng}; - - let info = get_circuit_parameters::(MySillyCircuit { a: None, b: None }).expect("Must get circuit info"); - println!("{:?}", info); - let rng = &mut thread_rng(); - - let x: Fr = rng.gen(); - let alpha: Fr = rng.gen(); - - let params = generate_parameters::(MySillyCircuit { a: None, b: None }, alpha, x).unwrap(); - let srs = generate_srs::(alpha, x, info.n * 100).unwrap(); - let naive_srs = SRS::::new( - info.n * 100, - x, - alpha, - ); - - assert!(srs == naive_srs); - - let params_on_srs = generate_parameters_on_srs_and_information::(&srs, info.clone()).unwrap(); - - assert!(params == params_on_srs); - - { - let mut v = vec![]; - - params.write(&mut v).unwrap(); - - 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/sonic/helped/mod.rs b/src/sonic/helped/mod.rs index 8cfda80..86ca823 100644 --- a/src/sonic/helped/mod.rs +++ b/src/sonic/helped/mod.rs @@ -6,22 +6,18 @@ use pairing::{Engine, CurveProjective}; use std::marker::PhantomData; mod verifier; -mod prover; mod batch; mod poly; mod helper; mod parameters; mod generator; +mod adapted_prover; + +pub mod prover; pub use self::batch::{Batch}; pub use self::helper::{Aggregate, create_aggregate}; pub use self::verifier::{MultiVerifier}; -pub use self::prover::{ - create_advice, - create_advice_on_information_and_srs, - create_advice_on_srs, - create_proof -}; pub use self::generator::{ CircuitParameters, @@ -32,4 +28,11 @@ pub use self::generator::{ generate_srs, get_circuit_parameters }; -pub use self::parameters::{Proof, SxyAdvice, Parameters, VerifyingKey, PreparedVerifyingKey}; \ No newline at end of file +pub use self::parameters::{Proof, SxyAdvice, Parameters, VerifyingKey, PreparedVerifyingKey}; +pub use self::adapted_prover::{ + create_advice, + create_advice_on_srs, + create_advice_on_information_and_srs, + create_proof, + create_proof_on_srs, +}; \ No newline at end of file diff --git a/src/sonic/helped/parameters.rs b/src/sonic/helped/parameters.rs index 255899c..dfc27c2 100644 --- a/src/sonic/helped/parameters.rs +++ b/src/sonic/helped/parameters.rs @@ -19,15 +19,23 @@ use std::io::{self, Read, Write}; use std::sync::Arc; use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, Eq)] pub struct SxyAdvice { pub s: E::G1Affine, pub opening: E::G1Affine, pub szy: E::Fr, } +impl PartialEq for SxyAdvice { + fn eq(&self, other: &SxyAdvice) -> bool { + self.s == other.s && + self.opening == other.opening && + self.szy == other.szy + } +} -#[derive(Clone, Debug, PartialEq, Eq)] + +#[derive(Clone, Debug, Eq)] pub struct Proof { pub r: E::G1Affine, pub t: E::G1Affine, @@ -37,6 +45,17 @@ pub struct Proof { pub zy_opening: E::G1Affine } +impl PartialEq for Proof { + fn eq(&self, other: &Proof) -> bool { + self.r == other.r && + self.t == other.t && + self.rz == other.rz && + self.rzy == other.rzy && + self.z_opening == other.z_opening && + self.zy_opening == other.zy_opening + } +} + impl Proof { pub fn write( &self, @@ -49,6 +68,7 @@ impl Proof { let mut buffer = vec![]; self.rz.into_repr().write_be(&mut buffer)?; writer.write_all(&buffer[..])?; + let mut buffer = vec![]; self.rzy.into_repr().write_be(&mut buffer)?; writer.write_all(&buffer[..])?; writer.write_all(self.z_opening.into_compressed().as_ref())?; @@ -362,185 +382,105 @@ impl Parameters { } } -// pub trait ParameterSource { -// type G1Builder: SourceBuilder; -// type G2Builder: SourceBuilder; +#[test] +fn parameters_generation() { + use crate::{ConstraintSystem, Circuit}; -// 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>; -// } + use pairing::bls12_381::{Bls12, Fr}; -// impl<'a, E: Engine> ParameterSource for &'a Parameters { -// type G1Builder = (Arc>, usize); -// type G2Builder = (Arc>, usize); + #[derive(Clone)] + struct MySillyCircuit { + a: Option, + b: Option + } -// fn get_vk( -// &mut self, -// _: usize -// ) -> Result, SynthesisError> -// { -// Ok(self.vk.clone()) -// } + 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)?; -// fn get_h( -// &mut self, -// _: usize -// ) -> Result -// { -// Ok((self.h.clone(), 0)) -// } + a.mul_assign(&b); + Ok(a) + })?; -// fn get_l( -// &mut self, -// _: usize -// ) -> Result -// { -// Ok((self.l.clone(), 0)) -// } + cs.enforce( + || "a*b=c", + |lc| lc + a, + |lc| lc + b, + |lc| lc + c + ); -// 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))) -// } + Ok(()) + } + } -// 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))) -// } + use rand::{Rng, Rand, thread_rng}; + use super::{generate_parameters, get_circuit_parameters, generate_srs, generate_parameters_on_srs_and_information}; + use super::adapted_prover::create_proof; -// 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))) -// } -// } + let info = get_circuit_parameters::(MySillyCircuit { a: None, b: None }).expect("Must get circuit info"); + println!("{:?}", info); + let rng = &mut thread_rng(); -// #[cfg(test)] -// mod test_with_bls12_381 { -// use super::*; -// use {Circuit, SynthesisError, ConstraintSystem}; + let x: Fr = rng.gen(); + let alpha: Fr = rng.gen(); -// use rand::{Rand, thread_rng}; -// use ff::{Field}; -// use pairing::bls12_381::{Bls12, Fr}; + let params = generate_parameters::(MySillyCircuit { a: None, b: None }, alpha, x).unwrap(); + let srs = generate_srs::(alpha, x, info.n * 100).unwrap(); + let naive_srs = SRS::::new( + info.n * 100, + x, + alpha, + ); -// #[test] -// fn serialization() { -// struct MySillyCircuit { -// a: Option, -// b: Option -// } + assert!(srs == naive_srs); -// 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)?; + let params_on_srs = generate_parameters_on_srs_and_information::(&srs, info.clone()).unwrap(); -// a.mul_assign(&b); -// Ok(a) -// })?; + assert!(params == params_on_srs); -// cs.enforce( -// || "a*b=c", -// |lc| lc + a, -// |lc| lc + b, -// |lc| lc + c -// ); + { + let mut v = vec![]; -// Ok(()) -// } -// } + params.write(&mut v).unwrap(); -// let rng = &mut thread_rng(); + let de_params = Parameters::read(&v[..], true).unwrap(); + assert!(params == de_params); -// let params = generate_random_parameters::( -// MySillyCircuit { a: None, b: None }, -// rng -// ).unwrap(); + let de_params = Parameters::read(&v[..], false).unwrap(); + assert!(params == de_params); + } -// { -// let mut v = vec![]; + for _ in 0..100 { + let a = Fr::rand(rng); + let b = Fr::rand(rng); + let mut c = a; + c.mul_assign(&b); -// params.write(&mut v).unwrap(); -// assert_eq!(v.len(), 2136); + let proof = create_proof ( + MySillyCircuit { + a: Some(a), + b: Some(b) + }, + ¶ms, + ).unwrap(); -// let de_params = Parameters::read(&v[..], true).unwrap(); -// assert!(params == de_params); + let mut v = vec![]; + proof.write(&mut v).unwrap(); -// let de_params = Parameters::read(&v[..], false).unwrap(); -// assert!(params == de_params); -// } + assert_eq!(v.len(), 256); -// let pvk = prepare_verifying_key::(¶ms.vk); + let de_proof = Proof::read(&v[..]).unwrap(); + assert!(proof == de_proof); -// 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 + // assert!(verify_proof(&pvk, &proof, &[c]).unwrap()); + // assert!(!verify_proof(&pvk, &proof, &[a]).unwrap()); + } +} \ No newline at end of file diff --git a/src/sonic/helped/prover.rs b/src/sonic/helped/prover.rs index 43355d6..613aa3a 100644 --- a/src/sonic/helped/prover.rs +++ b/src/sonic/helped/prover.rs @@ -133,6 +133,13 @@ pub fn create_advice_on_srs, S: SynthesisDriver>( } pub fn create_proof, S: SynthesisDriver>( + circuit: &C, + parameters: &Parameters +) -> Result, SynthesisError> { + create_proof_on_srs::(circuit, ¶meters.srs) +} + +pub fn create_proof_on_srs, S: SynthesisDriver>( circuit: &C, srs: &SRS ) -> Result, SynthesisError> @@ -209,6 +216,10 @@ pub fn create_proof, S: SynthesisDriver>( let y: E::Fr = transcript.get_challenge_scalar(); + // create r(X, 1) by observation that it's just a series of coefficients. + // Used representation is for powers X^{-2n}...X^{-n-1}, X^{-n}...X^{-1}, X^{1}...X^{n} + // Same representation is ok for r(X, Y) too cause powers always match + // TODO: add blindings c_{n+1}*X^{-2n - 1}, c_{n+2}*X^{-2n - 2}, c_{n+3}*X^{-2n - 3}, c_{n+4}*X^{-2n - 4} let mut rx1 = wires.b; rx1.extend(wires.c); rx1.reverse(); @@ -219,11 +230,13 @@ pub fn create_proof, S: SynthesisDriver>( let y_inv = y.inverse().ok_or(SynthesisError::DivisionByZero)?; let mut tmp = y.pow(&[n as u64]); + // evaluate r(X, y) for rxy in rxy.iter_mut().rev() { rxy.mul_assign(&tmp); tmp.mul_assign(&y_inv); } + // negative powers [-n, -1], positive [1, 2n] let (s_poly_negative, s_poly_positive) = { let mut tmp = SxEval::new(y, n); S::synthesize(&mut tmp, circuit)?; @@ -231,10 +244,11 @@ pub fn create_proof, S: SynthesisDriver>( tmp.poly() }; + // r'(X, y) = r(X, y) + s(X, y). Note `y` - those are evaluated at the point already let mut rxy_prime = rxy.clone(); { rxy_prime.resize(4 * n + 1, E::Fr::zero()); - // Add s(x, y) + // add coefficients in front of X^{-2n}...X^{-n-1}, X^{-n}...X^{-1} for (r, s) in rxy_prime[0..(2 * n)] .iter_mut() .rev() @@ -242,14 +256,18 @@ pub fn create_proof, S: SynthesisDriver>( { r.add_assign(&s); } + // add coefficients in front of X^{1}...X^{n}, X^{n+1}...X^{2*n} for (r, s) in rxy_prime[(2 * n + 1)..].iter_mut().zip(s_poly_positive) { r.add_assign(&s); } } + // t(X, y) = r'(X, y)*r(X, 1) and will be later evaluated at z + // contained degree in respect to X are from -4*n to 3*n including X^0 let mut txy = multiply_polynomials::(rx1.clone(), rxy_prime); txy[4 * n] = E::Fr::zero(); // -k(y) + // commit to t(X, y) to later open at z let t = multiexp( srs.g_positive_x_alpha[0..(3 * n)] .iter() @@ -392,7 +410,7 @@ fn my_fun_circuit_test() { Fr::from_str("22222").unwrap(), Fr::from_str("33333333").unwrap(), ); - let proof = create_proof::(&MyCircuit, &srs).unwrap(); + let proof = self::create_proof_on_srs::(&MyCircuit, &srs).unwrap(); use std::time::{Instant}; let start = Instant::now(); diff --git a/src/sonic/helped/verifier.rs b/src/sonic/helped/verifier.rs index 808cd2f..f6fada4 100644 --- a/src/sonic/helped/verifier.rs +++ b/src/sonic/helped/verifier.rs @@ -226,6 +226,7 @@ impl, S: SynthesisDriver> MultiVerifier { }); // Finally, compute t(z, y) + // t(z, y) = (r(z, y) + s(z,y))*r(z, 1) - k(y) let mut tzy = proof.rzy; tzy.add_assign(&szy); tzy.mul_assign(&proof.rz); diff --git a/src/sonic/sonic/adaptor.rs b/src/sonic/sonic/adaptor.rs index cf96240..f38eaed 100644 --- a/src/sonic/sonic/adaptor.rs +++ b/src/sonic/sonic/adaptor.rs @@ -111,7 +111,7 @@ impl<'a, E: Engine, CS: SonicConstraintSystem + 'a> crate::ConstraintSystem + 'a> crate::ConstraintSystem(pub T); impl<'a, E: Engine, C: crate::Circuit + Clone> SonicCircuit for AdaptorCircuit { diff --git a/src/sonic/util.rs b/src/sonic/util.rs index 2bc2850..bc12159 100644 --- a/src/sonic/util.rs +++ b/src/sonic/util.rs @@ -202,20 +202,18 @@ where use pairing::CurveAffine; use crate::multicore::Worker; - use crate::multiexp; - use crate::multiexp::FullDensity; + use crate::multiexp::dense_multiexp; - let s: Arc::Repr>> = Arc::new(s.into_iter().map(|e| e.into_repr()).collect::>()); - let g: Arc> = Arc::new(g.into_iter().map(|e| *e).collect::>()); + let s: Vec<::Repr> = s.into_iter().map(|e| e.into_repr()).collect::>(); + let g: Vec = g.into_iter().map(|e| *e).collect::>(); let pool = Worker::new(); - let result = multiexp::multiexp( + let result = dense_multiexp( &pool, - (g, 0), - FullDensity, - s - ).wait().unwrap(); + &g, + &s + ).unwrap(); result } @@ -379,7 +377,6 @@ fn laurent_division() { } pub fn multiply_polynomials(a: Vec, b: Vec) -> Vec { - println!("Multiplying polynomails of degrees {} and {}", a.len(), b.len()); let result_len = a.len() + b.len() - 1; use crate::multicore::Worker; diff --git a/src/source.rs b/src/source.rs new file mode 100644 index 0000000..5a77d8f --- /dev/null +++ b/src/source.rs @@ -0,0 +1,141 @@ +use pairing::{ + CurveAffine, + CurveProjective, + Engine +}; + +use ff::{ + PrimeField, + Field, + PrimeFieldRepr, + ScalarEngine}; + +use std::sync::Arc; +use std::io; +use bit_vec::{self, BitVec}; +use std::iter; + +use super::SynthesisError; + +/// An object that builds a source of bases. +pub trait SourceBuilder: Send + Sync + 'static + Clone { + type Source: Source; + + fn new(self) -> Self::Source; +} + +/// A source of bases, like an iterator. +pub trait Source { + /// Parses the element from the source. Fails if the point is at infinity. + fn add_assign_mixed(&mut self, to: &mut ::Projective) -> Result<(), SynthesisError>; + + /// Skips `amt` elements from the source, avoiding deserialization. + fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>; +} + +impl SourceBuilder for (Arc>, usize) { + type Source = (Arc>, usize); + + fn new(self) -> (Arc>, usize) { + (self.0.clone(), self.1) + } +} + +impl Source for (Arc>, usize) { + fn add_assign_mixed(&mut self, to: &mut ::Projective) -> Result<(), SynthesisError> { + if self.0.len() <= self.1 { + return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases when adding from source").into()); + } + + if self.0[self.1].is_zero() { + return Err(SynthesisError::UnexpectedIdentity) + } + + to.add_assign_mixed(&self.0[self.1]); + + self.1 += 1; + + Ok(()) + } + + fn skip(&mut self, amt: usize) -> Result<(), SynthesisError> { + if self.0.len() <= self.1 { + return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases skipping from source").into()); + } + + self.1 += amt; + + Ok(()) + } +} + +pub trait QueryDensity { + /// Returns whether the base exists. + type Iter: Iterator; + + fn iter(self) -> Self::Iter; + fn get_query_size(self) -> Option; +} + +#[derive(Clone)] +pub struct FullDensity; + +impl AsRef for FullDensity { + fn as_ref(&self) -> &FullDensity { + self + } +} + +impl<'a> QueryDensity for &'a FullDensity { + type Iter = iter::Repeat; + + fn iter(self) -> Self::Iter { + iter::repeat(true) + } + + fn get_query_size(self) -> Option { + None + } +} + +#[derive(Clone)] +pub struct DensityTracker { + bv: BitVec, + total_density: usize +} + +impl<'a> QueryDensity for &'a DensityTracker { + type Iter = bit_vec::Iter<'a>; + + fn iter(self) -> Self::Iter { + self.bv.iter() + } + + fn get_query_size(self) -> Option { + Some(self.bv.len()) + } +} + +impl DensityTracker { + pub fn new() -> DensityTracker { + DensityTracker { + bv: BitVec::new(), + total_density: 0 + } + } + + pub fn add_element(&mut self) { + self.bv.push(false); + } + + pub fn inc(&mut self, idx: usize) { + if !self.bv.get(idx).unwrap() { + self.bv.set(idx, true); + self.total_density += 1; + } + } + + pub fn get_total_density(&self) -> usize { + self.total_density + } +} \ No newline at end of file diff --git a/tests/mimc.rs b/tests/mimc.rs index 7d569a4..7a37a7f 100644 --- a/tests/mimc.rs +++ b/tests/mimc.rs @@ -479,16 +479,17 @@ fn test_sonic_mimc() { use bellman::sonic::cs::Basic; use bellman::sonic::sonic::AdaptorCircuit; - use bellman::sonic::helped::{create_proof, create_advice, create_aggregate, MultiVerifier, create_advice_on_srs}; + use bellman::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs}; + use bellman::sonic::helped::{create_aggregate, MultiVerifier}; println!("creating proof"); let start = Instant::now(); - let proof = create_proof::(&AdaptorCircuit(circuit.clone()), &srs).unwrap(); + let proof = create_proof_on_srs::(&AdaptorCircuit(circuit.clone()), &srs).unwrap(); println!("done in {:?}", start.elapsed()); println!("creating advice"); let start = Instant::now(); - let advice = create_advice_on_srs::(&AdaptorCircuit(circuit.clone()), &proof, &srs); + let advice = create_advice_on_srs::(&AdaptorCircuit(circuit.clone()), &proof, &srs).unwrap(); println!("done in {:?}", start.elapsed()); println!("creating aggregate for {} proofs", samples); @@ -578,19 +579,20 @@ fn test_inputs_into_sonic_mimc() { use bellman::sonic::cs::Basic; use bellman::sonic::sonic::AdaptorCircuit; - use bellman::sonic::helped::{create_proof, get_circuit_parameters, create_advice, create_aggregate, MultiVerifier, create_advice_on_srs}; + use bellman::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs}; + use bellman::sonic::helped::{create_aggregate, MultiVerifier, get_circuit_parameters}; let info = get_circuit_parameters::(circuit.clone()).expect("Must get circuit info"); println!("{:?}", info); println!("creating proof"); let start = Instant::now(); - let proof = create_proof::(&AdaptorCircuit(circuit.clone()), &srs).unwrap(); + let proof = create_proof_on_srs::(&AdaptorCircuit(circuit.clone()), &srs).unwrap(); println!("done in {:?}", start.elapsed()); println!("creating advice"); let start = Instant::now(); - let advice = create_advice_on_srs::(&AdaptorCircuit(circuit.clone()), &proof, &srs); + let advice = create_advice_on_srs::(&AdaptorCircuit(circuit.clone()), &proof, &srs).unwrap(); println!("done in {:?}", start.elapsed()); println!("creating aggregate for {} proofs", samples); @@ -624,32 +626,6 @@ fn test_inputs_into_sonic_mimc() { } println!("done in {:?}", start.elapsed()); } - - { - let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); - println!("verifying 1 proof with advice"); - let start = Instant::now(); - { - for _ in 0..1 { - verifier.add_proof_with_advice(&proof, &[image], &advice); - } - assert_eq!(verifier.check_all(), true); // TODO - } - println!("done in {:?}", start.elapsed()); - } - - { - let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); - println!("verifying 100 proofs with advice"); - let start = Instant::now(); - { - for _ in 0..samples { - verifier.add_proof_with_advice(&proof, &[image], &advice); - } - assert_eq!(verifier.check_all(), true); // TODO - } - println!("done in {:?}", start.elapsed()); - } { let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); From 2327a4527be6f5e1b6228b1cb01f5b2056dc476a Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Sun, 10 Feb 2019 01:44:01 +0300 Subject: [PATCH 19/42] proper use of features for compilation --- src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0e9c650..b2a758a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,12 +18,12 @@ pub mod sonic; mod group; mod source; -#[feature(not(singlecore))] +#[cfg(not(feature = "singlecore"))] mod parallel_fft; mod multicore; mod parallel_multiexp; -#[feature(singlecore)] +#[cfg(feature = "singlecore")] mod serial_fft; mod serial_multiexp; @@ -42,10 +42,10 @@ use std::marker::PhantomData; pub mod multiexp { pub use source::*; - #[feature(not(singlecore))] + #[cfg(not(feature = "singlecore"))] pub use parallel_multiexp::*; - #[feature(singlecore)] + #[cfg(feature = "singlecore")] pub use serial_multiexp::*; } From f98b6eaaf4c3de78ed0e19fe75cf3bd35e67c39a Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Sun, 10 Feb 2019 19:20:06 +0300 Subject: [PATCH 20/42] introduce high-level verifier api --- src/domain.rs | 2 +- src/sonic/helped/adapted_verifier.rs | 102 ++++++++++++++++++++++++++ src/sonic/helped/generator.rs | 2 +- src/sonic/helped/mod.rs | 6 ++ src/sonic/helped/prover.rs | 4 +- src/sonic/helped/verifier.rs | 105 ++++++++++++++++++++++----- tests/mimc.rs | 99 +++++++++++++++++++++++-- 7 files changed, 292 insertions(+), 28 deletions(-) create mode 100644 src/sonic/helped/adapted_verifier.rs diff --git a/src/domain.rs b/src/domain.rs index 85c8a26..d9c87b2 100644 --- a/src/domain.rs +++ b/src/domain.rs @@ -28,7 +28,7 @@ use super::{ use super::multicore::Worker; pub use super::group::*; -#[feature(not(singlecore))] +#[cfg(not(feature = "singlecore"))] use super::parallel_fft::*; pub struct EvaluationDomain> { diff --git a/src/sonic/helped/adapted_verifier.rs b/src/sonic/helped/adapted_verifier.rs new file mode 100644 index 0000000..0c4b0c8 --- /dev/null +++ b/src/sonic/helped/adapted_verifier.rs @@ -0,0 +1,102 @@ +use ff::{Field}; +use pairing::{Engine, CurveProjective}; +use std::marker::PhantomData; + +use rand::{Rand, Rng}; + +use super::{Proof, SxyAdvice}; +use super::batch::Batch; +use super::poly::{SxEval, SyEval}; +use super::parameters::{Parameters}; +use super::helper::{Aggregate}; + +use crate::SynthesisError; + +use crate::sonic::transcript::{Transcript, TranscriptProtocol}; +use crate::sonic::util::*; +use crate::sonic::cs::{Backend, SynthesisDriver}; +use crate::{Circuit}; +use crate::sonic::sonic::AdaptorCircuit; +use crate::sonic::srs::SRS; +use crate::sonic::cs::Nonassigning; +use super::verifier::verify_aggregate_on_srs as verify_aggregate_on_srs_sonic_circuit; +use super::verifier::verify_proofs_on_srs as verify_proofs_on_srs_sonic_circuit; + +pub fn verify_proofs + Clone, R: Rng>( + proofs: &[Proof], + inputs: &[Vec], + circuit: C, + rng: R, + params: &Parameters, +) -> Result +{ + let adapted_circuit = AdaptorCircuit(circuit); + + verify_proofs_on_srs_sonic_circuit::<_, _, Nonassigning, _>(proofs, inputs, adapted_circuit, rng, ¶ms.srs) +} + +/// Check multiple proofs with aggregation. Verifier's work is +/// not succint due to `S(X, Y)` evaluation +pub fn verify_aggregate + Clone, R: Rng>( + proofs: &[(Proof, SxyAdvice)], + aggregate: &Aggregate, + inputs: &[Vec], + circuit: C, + rng: R, + params: &Parameters, +) -> Result { + let adapted_circuit = AdaptorCircuit(circuit); + + verify_aggregate_on_srs_sonic_circuit::<_, _, Nonassigning, _>(proofs, aggregate, inputs, adapted_circuit, rng, ¶ms.srs) +} + + +// #[test] +// fn my_fun_circuit_test() { +// use ff::PrimeField; +// use pairing::bls12_381::{Bls12, Fr}; +// use super::*; +// use crate::sonic::cs::{Basic, ConstraintSystem, LinearCombination}; + +// struct MyCircuit; + +// impl Circuit for MyCircuit { +// fn synthesize>(&self, cs: &mut CS) -> Result<(), SynthesisError> { +// let (a, b, _) = cs.multiply(|| { +// Ok(( +// E::Fr::from_str("10").unwrap(), +// E::Fr::from_str("20").unwrap(), +// E::Fr::from_str("200").unwrap(), +// )) +// })?; + +// cs.enforce_zero(LinearCombination::from(a) + a - b); + +// //let multiplier = cs.alloc_input(|| Ok(E::Fr::from_str("20").unwrap()))?; + +// //cs.enforce_zero(LinearCombination::from(b) - multiplier); + +// Ok(()) +// } +// } + +// let srs = SRS::::new( +// 20, +// Fr::from_str("22222").unwrap(), +// Fr::from_str("33333333").unwrap(), +// ); +// let proof = create_proof_on_srs::(&MyCircuit, &srs).unwrap(); + +// use std::time::{Instant}; +// let start = Instant::now(); +// let mut batch = MultiVerifier::::new(MyCircuit, &srs).unwrap(); + +// for _ in 0..1 { +// batch.add_proof(&proof, &[/*Fr::from_str("20").unwrap()*/], |_, _| None); +// } + +// assert!(batch.check_all()); + +// let elapsed = start.elapsed(); +// println!("time to verify: {:?}", elapsed); +// } diff --git a/src/sonic/helped/generator.rs b/src/sonic/helped/generator.rs index ce7cea9..79e8b79 100644 --- a/src/sonic/helped/generator.rs +++ b/src/sonic/helped/generator.rs @@ -482,7 +482,7 @@ pub fn generate_srs( let mut x_powers_negative = vec![Scalar::(E::Fr::zero()); d]; { // Compute powers of tau - if verbose {eprintln!("computing powers of tau...")}; + if verbose {eprintln!("computing powers of x...")}; let start = std::time::Instant::now(); diff --git a/src/sonic/helped/mod.rs b/src/sonic/helped/mod.rs index 86ca823..05d2816 100644 --- a/src/sonic/helped/mod.rs +++ b/src/sonic/helped/mod.rs @@ -12,6 +12,7 @@ mod helper; mod parameters; mod generator; mod adapted_prover; +mod adapted_verifier; pub mod prover; @@ -35,4 +36,9 @@ pub use self::adapted_prover::{ create_advice_on_information_and_srs, create_proof, create_proof_on_srs, +}; + +pub use self::adapted_verifier::{ + verify_proofs, + verify_aggregate }; \ No newline at end of file diff --git a/src/sonic/helped/prover.rs b/src/sonic/helped/prover.rs index 613aa3a..ff78c2d 100644 --- a/src/sonic/helped/prover.rs +++ b/src/sonic/helped/prover.rs @@ -382,6 +382,7 @@ fn my_fun_circuit_test() { use pairing::bls12_381::{Bls12, Fr}; use super::*; use crate::sonic::cs::{Basic, ConstraintSystem, LinearCombination}; + use rand::{thread_rng}; struct MyCircuit; @@ -414,7 +415,8 @@ fn my_fun_circuit_test() { use std::time::{Instant}; let start = Instant::now(); - let mut batch = MultiVerifier::::new(MyCircuit, &srs).unwrap(); + let rng = thread_rng(); + let mut batch = MultiVerifier::::new(MyCircuit, &srs, rng).unwrap(); for _ in 0..1 { batch.add_proof(&proof, &[/*Fr::from_str("20").unwrap()*/], |_, _| None); diff --git a/src/sonic/helped/verifier.rs b/src/sonic/helped/verifier.rs index f6fada4..768500d 100644 --- a/src/sonic/helped/verifier.rs +++ b/src/sonic/helped/verifier.rs @@ -1,11 +1,13 @@ use ff::{Field}; use pairing::{Engine, CurveProjective}; use std::marker::PhantomData; +use rand::{Rand, Rng}; use super::{Proof, SxyAdvice}; use super::batch::Batch; use super::poly::{SxEval, SyEval}; use super::helper::Aggregate; +use super::parameters::{Parameters}; use crate::SynthesisError; @@ -15,17 +17,19 @@ use crate::sonic::cs::{Backend, SynthesisDriver}; use crate::sonic::cs::{Circuit, Variable, Coeff}; use crate::sonic::srs::SRS; -pub struct MultiVerifier, S: SynthesisDriver> { +pub struct MultiVerifier, S: SynthesisDriver, R: Rng> { circuit: C, batch: Batch, k_map: Vec, n: usize, q: usize, + randomness_source: R, _marker: PhantomData<(E, S)> } -impl, S: SynthesisDriver> MultiVerifier { - pub fn new(circuit: C, srs: &SRS) -> Result { +impl, S: SynthesisDriver, R: Rng> MultiVerifier { + // This constructor consumes randomness source cause it's later used internally + pub fn new(circuit: C, srs: &SRS, rng: R) -> Result { struct Preprocess { k_map: Vec, n: usize, @@ -57,6 +61,7 @@ impl, S: SynthesisDriver> MultiVerifier { k_map: preprocess.k_map, n: preprocess.n, q: preprocess.q, + randomness_source: rng, _marker: PhantomData }) } @@ -93,10 +98,7 @@ impl, S: SynthesisDriver> MultiVerifier { }; { - // TODO: like everything else doing this, this isn't really random - let random: E::Fr; - let mut transcript = transcript.clone(); - random = transcript.get_challenge_scalar(); + let random: E::Fr = self.randomness_source.gen(); self.batch.add_opening(aggregate.opening, random, w); self.batch.add_commitment(aggregate.c, random); @@ -104,20 +106,14 @@ impl, S: SynthesisDriver> MultiVerifier { } for ((opening, value), &y) in aggregate.c_openings.iter().zip(y_values.iter()) { - let random: E::Fr; - let mut transcript = transcript.clone(); - random = transcript.get_challenge_scalar(); + let random: E::Fr = self.randomness_source.gen(); self.batch.add_opening(*opening, random, y); self.batch.add_commitment(aggregate.c, random); self.batch.add_opening_value(*value, random); } - let random: E::Fr; - { - let mut transcript = transcript.clone(); - random = transcript.get_challenge_scalar(); - } + let random: E::Fr = self.randomness_source.gen(); let mut expected_value = E::Fr::zero(); for ((_, advice), c_opening) in proofs.iter().zip(aggregate.c_openings.iter()) { @@ -139,6 +135,7 @@ impl, S: SynthesisDriver> MultiVerifier { self.batch.add_opening(aggregate.s_opening, random, z); } + /// Caller must ensure to add aggregate after adding a proof pub fn add_proof_with_advice( &mut self, proof: &Proof, @@ -160,7 +157,7 @@ impl, S: SynthesisDriver> MultiVerifier { transcript.commit_point(&advice.opening); transcript.commit_point(&advice.s); transcript.commit_scalar(&advice.szy); - let random: E::Fr = transcript.get_challenge_scalar(); + let random: E::Fr = self.randomness_source.gen(); self.batch.add_opening(advice.opening, random, z); self.batch.add_commitment(advice.s, random); @@ -196,7 +193,7 @@ impl, S: SynthesisDriver> MultiVerifier { // First, the easy one. Let's open up proof.r at zy, using proof.zy_opening // as the evidence and proof.rzy as the opening. { - let random = transcript.get_challenge_scalar(); + let random: E::Fr = self.randomness_source.gen(); let mut zy = z; zy.mul_assign(&y); self.batch.add_opening(proof.zy_opening, random, zy); @@ -235,7 +232,7 @@ impl, S: SynthesisDriver> MultiVerifier { // We open these both at the same time by keeping their commitments // linearly independent (using r1). { - let mut random = transcript.get_challenge_scalar(); + let mut random: E::Fr = self.randomness_source.gen(); self.batch.add_opening(proof.z_opening, random, z); self.batch.add_opening_value(tzy, random); @@ -263,4 +260,74 @@ impl, S: SynthesisDriver> MultiVerifier { pub fn check_all(self) -> bool { self.batch.check_all() } -} \ No newline at end of file +} + +/// Check multiple proofs without aggregation. Verifier's work is +/// not succint due to `S(X, Y)` evaluation +pub fn verify_proofs, S: SynthesisDriver, R: Rng>( + proofs: &[Proof], + inputs: &[Vec], + circuit: C, + rng: R, + params: &Parameters, +) -> Result { + verify_proofs_on_srs::(proofs, inputs, circuit, rng, ¶ms.srs) +} + +/// Check multiple proofs without aggregation. Verifier's work is +/// not succint due to `S(X, Y)` evaluation +pub fn verify_proofs_on_srs, S: SynthesisDriver, R: Rng>( + proofs: &[Proof], + inputs: &[Vec], + circuit: C, + rng: R, + srs: &SRS, +) -> Result { + let mut verifier = MultiVerifier::::new(circuit, srs, rng)?; + let expected_inputs_size = verifier.get_k_map().len() - 1; + for (proof, inputs) in proofs.iter().zip(inputs.iter()) { + if inputs.len() != expected_inputs_size { + return Err(SynthesisError::Unsatisfiable); + } + verifier.add_proof(proof, &inputs, |_, _| None); + } + + Ok(verifier.check_all()) +} + +/// Check multiple proofs with aggregation. Verifier's work is +/// not succint due to `S(X, Y)` evaluation +pub fn verify_aggregate, S: SynthesisDriver,R: Rng>( + proofs: &[(Proof, SxyAdvice)], + aggregate: &Aggregate, + inputs: &[Vec], + circuit: C, + rng: R, + params: &Parameters, +) -> Result { + verify_aggregate_on_srs::(proofs, aggregate, inputs, circuit, rng, ¶ms.srs) +} + +/// Check multiple proofs with aggregation. Verifier's work is +/// not succint due to `S(X, Y)` evaluation +pub fn verify_aggregate_on_srs, S: SynthesisDriver, R: Rng>( + proofs: &[(Proof, SxyAdvice)], + aggregate: &Aggregate, + inputs: &[Vec], + circuit: C, + rng: R, + srs: &SRS, +) -> Result { + let mut verifier = MultiVerifier::::new(circuit, srs, rng)?; + let expected_inputs_size = verifier.get_k_map().len() - 1; + for ((proof, advice), inputs) in proofs.iter().zip(inputs.iter()) { + if inputs.len() != expected_inputs_size { + return Err(SynthesisError::Unsatisfiable); + } + verifier.add_proof_with_advice(proof, &inputs, &advice); + } + verifier.add_aggregate(proofs, aggregate); + + Ok(verifier.check_all()) +} + diff --git a/tests/mimc.rs b/tests/mimc.rs index 7a37a7f..fbaf382 100644 --- a/tests/mimc.rs +++ b/tests/mimc.rs @@ -499,7 +499,8 @@ fn test_sonic_mimc() { println!("done in {:?}", start.elapsed()); { - let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); + let rng = thread_rng(); + let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); println!("verifying 1 proof without advice"); let start = Instant::now(); { @@ -512,7 +513,8 @@ fn test_sonic_mimc() { } { - let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); + let rng = thread_rng(); + let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); println!("verifying {} proofs without advice", samples); let start = Instant::now(); { @@ -525,7 +527,8 @@ fn test_sonic_mimc() { } { - let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); + let rng = thread_rng(); + let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); println!("verifying 100 proofs with advice"); let start = Instant::now(); { @@ -602,7 +605,8 @@ fn test_inputs_into_sonic_mimc() { println!("done in {:?}", start.elapsed()); { - let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); + let rng = thread_rng(); + let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); println!("verifying 1 proof without advice"); let start = Instant::now(); { @@ -615,7 +619,8 @@ fn test_inputs_into_sonic_mimc() { } { - let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); + let rng = thread_rng(); + let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); println!("verifying {} proofs without advice", samples); let start = Instant::now(); { @@ -628,7 +633,8 @@ fn test_inputs_into_sonic_mimc() { } { - let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs).unwrap(); + let rng = thread_rng(); + let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); println!("verifying 100 proofs with advice and aggregate"); let start = Instant::now(); { @@ -641,4 +647,85 @@ fn test_inputs_into_sonic_mimc() { println!("done in {:?}", start.elapsed()); } } +} + +#[test] +fn test_high_level_sonic_api() { + use ff::{Field, PrimeField}; + use pairing::{Engine, CurveAffine, CurveProjective}; + use pairing::bn256::{Bn256, Fr}; + use std::time::{Instant}; + use bellman::sonic::helped::{generate_random_parameters, + verify_aggregate, + verify_proofs, + create_proof, + create_advice, + create_aggregate + }; + use bellman::sonic::cs::Basic; + use bellman::sonic::sonic::AdaptorCircuit; + + { + // This may not be cryptographically safe, use + // `OsRng` (for example) in production software. + let mut rng = &mut thread_rng(); + + // Generate the MiMC round constants + let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::>(); + let samples: usize = 100; + + let xl = rng.gen(); + let xr = rng.gen(); + let image = mimc::(xl, xr, &constants); + + // Create an instance of our circuit (with the + // witness) + let circuit = MiMCDemo { + xl: Some(xl), + xr: Some(xr), + constants: &constants + }; + + let params = generate_random_parameters(circuit.clone(), &mut rng).unwrap(); + + println!("creating proof"); + let start = Instant::now(); + let proof = create_proof(circuit.clone(), ¶ms).unwrap(); + println!("done in {:?}", start.elapsed()); + + println!("creating advice"); + let start = Instant::now(); + let advice = create_advice(circuit.clone(), &proof, ¶ms).unwrap(); + println!("done in {:?}", start.elapsed()); + + println!("creating aggregate for {} proofs", samples); + let start = Instant::now(); + let proofs: Vec<_> = (0..samples).map(|_| (proof.clone(), advice.clone())).collect(); + let aggregate = create_aggregate::(&AdaptorCircuit(circuit.clone()), &proofs, ¶ms.srs); + println!("done in {:?}", start.elapsed()); + + { + println!("verifying 1 proof without advice"); + let rng = thread_rng(); + let start = Instant::now(); + assert_eq!(verify_proofs(&vec![proof.clone()], &vec![vec![image.clone()]], circuit.clone(), rng, ¶ms).unwrap(), true); + println!("done in {:?}", start.elapsed()); + } + + { + println!("verifying {} proofs without advice", samples); + let rng = thread_rng(); + let start = Instant::now(); + assert_eq!(verify_proofs(&vec![proof.clone(); 100], &vec![vec![image.clone()]; 100], circuit.clone(), rng, ¶ms).unwrap(), true); + println!("done in {:?}", start.elapsed()); + } + + { + println!("verifying 100 proofs with advice and aggregate"); + let rng = thread_rng(); + let start = Instant::now(); + assert_eq!(verify_aggregate(&vec![(proof.clone(), advice.clone()); 100], &aggregate, &vec![vec![image.clone()]; 100], circuit.clone(), rng, ¶ms).unwrap(), true); + println!("done in {:?}", start.elapsed()); + } + } } \ No newline at end of file From b7d59787ed68baff98c21999faab0859063aa06b Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Sun, 10 Feb 2019 19:44:53 +0300 Subject: [PATCH 21/42] implement high-level helper api --- src/sonic/helped/adapted_helper.rs | 33 ++++++++++++++++++++++++++++++ src/sonic/helped/helper.rs | 24 ++++++++++++++++++++++ src/sonic/helped/mod.rs | 21 +++++++++++++------ tests/mimc.rs | 21 +++++++++---------- 4 files changed, 82 insertions(+), 17 deletions(-) create mode 100644 src/sonic/helped/adapted_helper.rs diff --git a/src/sonic/helped/adapted_helper.rs b/src/sonic/helped/adapted_helper.rs new file mode 100644 index 0000000..87e99da --- /dev/null +++ b/src/sonic/helped/adapted_helper.rs @@ -0,0 +1,33 @@ +use ff::{Field}; +use pairing::{Engine, CurveProjective}; +use std::marker::PhantomData; + +use rand::{Rand, Rng}; + +use super::{Proof, SxyAdvice}; +use super::batch::Batch; +use super::poly::{SxEval, SyEval}; +use super::parameters::{Parameters}; +use super::helper::{Aggregate}; + +use crate::SynthesisError; + +use crate::sonic::transcript::{Transcript, TranscriptProtocol}; +use crate::sonic::util::*; +use crate::sonic::cs::{Backend, SynthesisDriver}; +use crate::{Circuit}; +use crate::sonic::sonic::AdaptorCircuit; +use crate::sonic::srs::SRS; +use crate::sonic::cs::Nonassigning; +use super::helper::create_aggregate as create_aggregate_sonic_circuit; + +pub fn create_aggregate + Clone>( + circuit: C, + inputs: &[(Proof, SxyAdvice)], + params: &Parameters, +) -> Aggregate +{ + let adapted_circuit = AdaptorCircuit(circuit); + + create_aggregate_sonic_circuit::<_, _, Nonassigning>(&adapted_circuit, inputs, params) +} diff --git a/src/sonic/helped/helper.rs b/src/sonic/helped/helper.rs index 44d695a..5b11f11 100644 --- a/src/sonic/helped/helper.rs +++ b/src/sonic/helped/helper.rs @@ -5,6 +5,7 @@ use std::marker::PhantomData; use super::{Proof, SxyAdvice}; use super::batch::Batch; use super::poly::{SxEval, SyEval}; +use super::Parameters; use crate::SynthesisError; @@ -27,6 +28,18 @@ pub struct Aggregate { } pub fn create_aggregate, S: SynthesisDriver>( + circuit: &C, + inputs: &[(Proof, SxyAdvice)], + params: &Parameters, +) -> Aggregate +{ + let n = params.vk.n; + let q = params.vk.q; + + create_aggregate_on_srs_using_information::(circuit, inputs, ¶ms.srs, n, q) +} + +pub fn create_aggregate_on_srs, S: SynthesisDriver>( circuit: &C, inputs: &[(Proof, SxyAdvice)], srs: &SRS, @@ -55,6 +68,17 @@ pub fn create_aggregate, S: SynthesisDriver>( (tmp.n, tmp.q) }; + create_aggregate_on_srs_using_information::(circuit, inputs, srs, n, q) +} + +pub fn create_aggregate_on_srs_using_information, S: SynthesisDriver>( + circuit: &C, + inputs: &[(Proof, SxyAdvice)], + srs: &SRS, + n: usize, + q: usize, +) -> Aggregate +{ let mut transcript = Transcript::new(&[]); let mut y_values: Vec = Vec::with_capacity(inputs.len()); for &(ref proof, ref sxyadvice) in inputs { diff --git a/src/sonic/helped/mod.rs b/src/sonic/helped/mod.rs index 05d2816..3a0be60 100644 --- a/src/sonic/helped/mod.rs +++ b/src/sonic/helped/mod.rs @@ -5,19 +5,18 @@ use ff::{Field}; use pairing::{Engine, CurveProjective}; use std::marker::PhantomData; -mod verifier; mod batch; mod poly; -mod helper; +pub mod prover; +pub mod verifier; +pub mod helper; mod parameters; mod generator; mod adapted_prover; mod adapted_verifier; - -pub mod prover; +mod adapted_helper; pub use self::batch::{Batch}; -pub use self::helper::{Aggregate, create_aggregate}; pub use self::verifier::{MultiVerifier}; pub use self::generator::{ @@ -29,7 +28,13 @@ pub use self::generator::{ generate_srs, get_circuit_parameters }; -pub use self::parameters::{Proof, SxyAdvice, Parameters, VerifyingKey, PreparedVerifyingKey}; +pub use self::parameters::{ + Proof, + SxyAdvice, + Parameters, + VerifyingKey, + PreparedVerifyingKey +}; pub use self::adapted_prover::{ create_advice, create_advice_on_srs, @@ -41,4 +46,8 @@ pub use self::adapted_prover::{ pub use self::adapted_verifier::{ verify_proofs, verify_aggregate +}; + +pub use self::adapted_helper::{ + create_aggregate }; \ No newline at end of file diff --git a/tests/mimc.rs b/tests/mimc.rs index fbaf382..8091f99 100644 --- a/tests/mimc.rs +++ b/tests/mimc.rs @@ -480,7 +480,8 @@ fn test_sonic_mimc() { use bellman::sonic::cs::Basic; use bellman::sonic::sonic::AdaptorCircuit; use bellman::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs}; - use bellman::sonic::helped::{create_aggregate, MultiVerifier}; + use bellman::sonic::helped::{MultiVerifier, get_circuit_parameters}; + use bellman::sonic::helped::helper::{create_aggregate_on_srs}; println!("creating proof"); let start = Instant::now(); @@ -495,7 +496,7 @@ fn test_sonic_mimc() { println!("creating aggregate for {} proofs", samples); let start = Instant::now(); let proofs: Vec<_> = (0..samples).map(|_| (proof.clone(), advice.clone())).collect(); - let aggregate = create_aggregate::(&AdaptorCircuit(circuit.clone()), &proofs, &srs); + let aggregate = create_aggregate_on_srs::(&AdaptorCircuit(circuit.clone()), &proofs, &srs); println!("done in {:?}", start.elapsed()); { @@ -583,7 +584,8 @@ fn test_inputs_into_sonic_mimc() { use bellman::sonic::cs::Basic; use bellman::sonic::sonic::AdaptorCircuit; use bellman::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs}; - use bellman::sonic::helped::{create_aggregate, MultiVerifier, get_circuit_parameters}; + use bellman::sonic::helped::{MultiVerifier, get_circuit_parameters}; + use bellman::sonic::helped::helper::{create_aggregate_on_srs}; let info = get_circuit_parameters::(circuit.clone()).expect("Must get circuit info"); println!("{:?}", info); @@ -601,7 +603,7 @@ fn test_inputs_into_sonic_mimc() { println!("creating aggregate for {} proofs", samples); let start = Instant::now(); let proofs: Vec<_> = (0..samples).map(|_| (proof.clone(), advice.clone())).collect(); - let aggregate = create_aggregate::(&AdaptorCircuit(circuit.clone()), &proofs, &srs); + let aggregate = create_aggregate_on_srs::(&AdaptorCircuit(circuit.clone()), &proofs, &srs); println!("done in {:?}", start.elapsed()); { @@ -651,19 +653,16 @@ fn test_inputs_into_sonic_mimc() { #[test] fn test_high_level_sonic_api() { - use ff::{Field, PrimeField}; - use pairing::{Engine, CurveAffine, CurveProjective}; - use pairing::bn256::{Bn256, Fr}; + use pairing::bn256::{Bn256}; use std::time::{Instant}; - use bellman::sonic::helped::{generate_random_parameters, + use bellman::sonic::helped::{ + generate_random_parameters, verify_aggregate, verify_proofs, create_proof, create_advice, create_aggregate }; - use bellman::sonic::cs::Basic; - use bellman::sonic::sonic::AdaptorCircuit; { // This may not be cryptographically safe, use @@ -701,7 +700,7 @@ fn test_high_level_sonic_api() { println!("creating aggregate for {} proofs", samples); let start = Instant::now(); let proofs: Vec<_> = (0..samples).map(|_| (proof.clone(), advice.clone())).collect(); - let aggregate = create_aggregate::(&AdaptorCircuit(circuit.clone()), &proofs, ¶ms.srs); + let aggregate = create_aggregate::(circuit.clone(), &proofs, ¶ms); println!("done in {:?}", start.elapsed()); { From 57a6ce94e8bc9c8fc26fb015977d356d7df8da12 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Mon, 11 Feb 2019 21:58:23 +0300 Subject: [PATCH 22/42] use another sync primitive for dense multiexp --- src/lib.rs | 8 ++++-- src/parallel_multiexp.rs | 60 ++++++++++++++++++++++++++++------------ src/sonic/util.rs | 5 ---- 3 files changed, 48 insertions(+), 25 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index b2a758a..e7f17e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,13 +2,15 @@ extern crate pairing; extern crate rand; +extern crate bit_vec; +extern crate byteorder; +extern crate ff; + +#[cfg(not(feature = "singlecore"))] extern crate num_cpus; extern crate futures; extern crate futures_cpupool; -extern crate bit_vec; extern crate crossbeam; -extern crate byteorder; -extern crate ff; pub mod domain; pub mod groth16; diff --git a/src/parallel_multiexp.rs b/src/parallel_multiexp.rs index 60e1dc3..8b3d3bb 100644 --- a/src/parallel_multiexp.rs +++ b/src/parallel_multiexp.rs @@ -211,15 +211,21 @@ fn dense_multiexp_inner( c: u32, handle_trivial: bool ) -> Result<::Projective, SynthesisError> -{ +{ + use crossbeam::channel::{unbounded, RecvError}; // Perform this region of the multiexp. We use a different strategy - go over region in parallel, // then over another region, etc. No Arc required let this = { - let this_region = pool.scope(bases.len(), |scope, chunk| { - let mut handles = vec![]; - let mut this_acc = ::Projective::zero(); + // let mask = (1u64 << c) - 1u64; + let (s, r) = unbounded(); + // let this_region = + pool.scope(bases.len(), |scope, chunk| { + // let mut handles = vec![]; + // let mut this_acc = ::Projective::zero(); for (base, exp) in bases.chunks(chunk).zip(exponents.chunks(chunk)) { - let handle = scope.spawn(move |_| { + let s = s.clone(); + // let handle = + scope.spawn(move |_| { let mut buckets = vec![::Projective::zero(); (1 << c) - 1]; // Accumulate the result let mut acc = G::Projective::zero(); @@ -227,6 +233,14 @@ fn dense_multiexp_inner( let one = ::Fr::one().into_repr(); for (base, &exp) in base.iter().zip(exp.iter()) { + // let index = (exp.as_ref()[0] & mask) as usize; + + // if index != 0 { + // buckets[index - 1].add_assign_mixed(base); + // } + + // exp.shr(c as u32); + if exp != zero { if exp == one { if handle_trivial { @@ -251,21 +265,33 @@ fn dense_multiexp_inner( } // acc contains values over this region - acc + s.send(acc).expect("must send result"); + + // acc }); - handles.push(handle); + // handles.push(handle); } - // wait for all threads to finish - for r in handles.into_iter().rev() { - let thread_result = r.join().unwrap(); - this_acc.add_assign(&thread_result); - } + // // wait for all threads to finish + // for r in handles.into_iter() { + // let thread_result = r.join().unwrap(); + // this_acc.add_assign(&thread_result); + // } - this_acc + + // this_acc }); + let mut this_region = ::Projective::zero(); + loop { + if r.is_empty() { + break; + } + let value = r.recv().expect("must have value"); + this_region.add_assign(&value); + } + this_region }; @@ -365,14 +391,14 @@ fn test_speed_with_bn256() { #[test] fn test_dense_multiexp() { - use rand::{self, Rand}; + use rand::{XorShiftRng, SeedableRng, Rand, Rng}; use pairing::bn256::Bn256; use num_cpus; const SAMPLES: usize = 1 << 22; + let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - let rng = &mut rand::thread_rng(); - let v = (0..SAMPLES).map(|_| ::Fr::rand(rng).into_repr()).collect::>(); + let mut v = (0..SAMPLES).map(|_| ::Fr::rand(rng).into_repr()).collect::>(); let g = (0..SAMPLES).map(|_| ::G1::rand(rng).into_affine()).collect::>(); let pool = Worker::new(); @@ -380,7 +406,7 @@ fn test_dense_multiexp() { let start = std::time::Instant::now(); let dense = dense_multiexp( - &pool, &g, &v).unwrap(); + &pool, &g, &mut v.clone()).unwrap(); let duration_ns = start.elapsed().as_nanos() as f64; println!("{} ns for dense for {} samples", duration_ns, SAMPLES); diff --git a/src/sonic/util.rs b/src/sonic/util.rs index bc12159..958c0bd 100644 --- a/src/sonic/util.rs +++ b/src/sonic/util.rs @@ -196,11 +196,6 @@ where IB::IntoIter: ExactSizeIterator + Clone, IS::IntoIter: ExactSizeIterator, { - use std::sync::Arc; - use futures::Future; - use ff::PrimeFieldRepr; - use pairing::CurveAffine; - use crate::multicore::Worker; use crate::multiexp::dense_multiexp; From 2d69758c1874a15ae2001e4c1f33775b510838e5 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 12 Feb 2019 01:52:18 +0300 Subject: [PATCH 23/42] parallelize almost everything --- src/sonic/helped/parameters.rs | 4 +- src/sonic/helped/prover.rs | 416 ++++++++++++++++++++++++++------- src/sonic/helped/verifier.rs | 2 +- src/sonic/util.rs | 60 +++++ 4 files changed, 395 insertions(+), 87 deletions(-) diff --git a/src/sonic/helped/parameters.rs b/src/sonic/helped/parameters.rs index dfc27c2..8128e61 100644 --- a/src/sonic/helped/parameters.rs +++ b/src/sonic/helped/parameters.rs @@ -19,6 +19,9 @@ use std::io::{self, Read, Write}; use std::sync::Arc; use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; +pub const NUM_BLINDINGS: usize = 4; +// pub const NUM_BLINDINGS: usize = 0; + #[derive(Clone, Debug, Eq)] pub struct SxyAdvice { pub s: E::G1Affine, @@ -34,7 +37,6 @@ impl PartialEq for SxyAdvice { } } - #[derive(Clone, Debug, Eq)] pub struct Proof { pub r: E::G1Affine, diff --git a/src/sonic/helped/prover.rs b/src/sonic/helped/prover.rs index ff78c2d..6e730ee 100644 --- a/src/sonic/helped/prover.rs +++ b/src/sonic/helped/prover.rs @@ -5,7 +5,7 @@ use std::marker::PhantomData; use super::{Proof, SxyAdvice}; use super::batch::Batch; use super::poly::{SxEval, SyEval}; -use super::parameters::{Parameters}; +use super::parameters::{Parameters, NUM_BLINDINGS}; use crate::SynthesisError; @@ -139,6 +139,9 @@ pub fn create_proof, S: SynthesisDriver>( create_proof_on_srs::(circuit, ¶meters.srs) } +extern crate rand; +use self::rand::{Rand, Rng, thread_rng}; + pub fn create_proof_on_srs, S: SynthesisDriver>( circuit: &C, srs: &SRS @@ -204,13 +207,108 @@ pub fn create_proof_on_srs, S: SynthesisDriver>( let mut transcript = Transcript::new(&[]); - let r = multiexp( - srs.g_positive_x_alpha[(srs.d - 3*n - 1)..].iter(), - wires.c.iter().rev() + let rng = &mut thread_rng(); + + // c_{n+1}, c_{n+2}, c_{n+3}, c_{n+4} + let blindings: Vec = (0..NUM_BLINDINGS).into_iter().map(|_| E::Fr::rand(rng)).collect(); + + // let blindings: Vec = vec![E::Fr::zero(); NUM_BLINDINGS]; + + // let max_n = 3*n + 1 + NUM_BLINDINGS; + + // let max_n = 3*n + 1; + + fn polynomial_commitment< + 'a, + EE: Engine, + IS: IntoIterator, + >( + max: usize, + largest_negative_power: usize, + largest_positive_power: usize, + srs: &'a SRS, + s: IS, + ) -> EE::G1Affine + where + IS::IntoIter: ExactSizeIterator, + { + // smallest power is d - max - largest_negative_power; It should either be 1 for use of positive powers only, + // of we should use part of the negative powers + let d = srs.d; + assert!(max >= largest_positive_power); + if d < max + largest_negative_power + 1 { + println!("Use negative powers for polynomial commitment"); + let min_power = largest_negative_power + max - d; + let max_power = d + largest_positive_power - max; + println!("Min power = {}, max = {}", min_power, max_power); + // need to use negative powers to make a proper commitment + return multiexp( + srs.g_negative_x_alpha[0..min_power].iter().rev() + .chain_ext(srs.g_positive_x_alpha[..max_power].iter()), + s + ).into_affine(); + } else { + println!("Use positive powers only for polynomial commitment"); + return multiexp( + srs.g_positive_x_alpha[(srs.d - max - largest_negative_power - 1)..].iter(), + s + ).into_affine(); + } + } + + fn polynomial_commitment_opening< + 'a, + EE: Engine, + I: IntoIterator + >( + largest_negative_power: usize, + largest_positive_power: usize, + polynomial_coefficients: I, + mut point: EE::Fr, + srs: &'a SRS, + ) -> EE::G1Affine + where I::IntoIter: DoubleEndedIterator + ExactSizeIterator, + { + let poly = kate_divison( + polynomial_coefficients, + point, + ); + + let negative_poly = poly[0..largest_negative_power].iter().rev(); + let positive_poly = poly[largest_negative_power..].iter(); + multiexp( + srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( + srs.g_positive_x[0..positive_poly.len()].iter() + ), + negative_poly.chain_ext(positive_poly) + ).into_affine() + } + + let r = polynomial_commitment::( + n, + 2*n + NUM_BLINDINGS, + n, + &srs, + blindings.iter().rev() + .chain_ext(wires.c.iter().rev()) + // wires.c.iter().rev() .chain_ext(wires.b.iter().rev()) .chain_ext(Some(E::Fr::zero()).iter()) .chain_ext(wires.a.iter()), - ).into_affine(); + ); + + // let r = multiexp( + // // F <- g^{alpha*x^{d-max}*f(x)} = g^{alpha*x^{d-n}*\sum_{i = -2n - 4}^{n} k_i*x^{i}} = + // // = g^{alpha*\sum_{i = d - 3n - 4}^{n} k_i*x^{i}} + // // g^{alpha*(x^{d - 3n - 4}*k_{-2n-4} + ... + x^{d - n}*k_{0} + ... + x^{d + n + 1}*k_{n}) + // srs.g_positive_x_alpha[(srs.d - max_n)..].iter(), + // blindings.iter().rev() + // .chain_ext(wires.c.iter().rev()) + // // wires.c.iter().rev() + // .chain_ext(wires.b.iter().rev()) + // .chain_ext(Some(E::Fr::zero()).iter()) + // .chain_ext(wires.a.iter()), + // ).into_affine(); transcript.commit_point(&r); @@ -226,17 +324,48 @@ pub fn create_proof_on_srs, S: SynthesisDriver>( rx1.push(E::Fr::zero()); rx1.extend(wires.a); - let mut rxy = rx1.clone(); + + // let mut rxy = rx1.clone(); + // it's not yet blinded, so blind explicitly here + let mut rxy = rx1; + let mut rx1 = { + // c_{n+1}*X^{-2n - 1}, c_{n+2}*X^{-2n - 2}, c_{n+3}*X^{-2n - 3}, c_{n+4}*X^{-2n - 4} + let mut tmp = blindings.clone(); + // c_{n+4}*X^{-2n - 4}, c_{n+3}*X^{-2n - 3}, c_{n+2}*X^{-2n - 2}, c_{n+1}*X^{-2n - 1}, + tmp.reverse(); + tmp.extend(rxy.clone()); + + tmp + }; + let y_inv = y.inverse().ok_or(SynthesisError::DivisionByZero)?; - let mut tmp = y.pow(&[n as u64]); - // evaluate r(X, y) - for rxy in rxy.iter_mut().rev() { - rxy.mul_assign(&tmp); - tmp.mul_assign(&y_inv); - } + // y^{-2n} + let tmp = y_inv.pow(&[2*n as u64]); + mut_distribute_consequitive_powers( + &mut rxy, + tmp, + y, + ); - // negative powers [-n, -1], positive [1, 2n] + // let mut tmp = y.pow(&[n as u64]); + // // evaluate r(X, y) + // for rxy in rxy.iter_mut().rev() { + // rxy.mul_assign(&tmp); + // tmp.mul_assign(&y_inv); + // } + + // Blindings are not affected by evaluation on Y cause blindings make constant term over Y + // We just add them here to have it now in a form of coefficients over X + let rxy = { + let mut tmp = blindings.clone(); + tmp.reverse(); + tmp.extend(rxy); + + tmp + }; + + // negative powers [-1, -2n], positive [1, n] let (s_poly_negative, s_poly_positive) = { let mut tmp = SxEval::new(y, n); S::synthesize(&mut tmp, circuit)?; @@ -244,12 +373,14 @@ pub fn create_proof_on_srs, S: SynthesisDriver>( tmp.poly() }; + // TODO: Parallelize // r'(X, y) = r(X, y) + s(X, y). Note `y` - those are evaluated at the point already let mut rxy_prime = rxy.clone(); { - rxy_prime.resize(4 * n + 1, E::Fr::zero()); + // extend to have powers [n+1, 2n] + rxy_prime.resize(4 * n + 1 + NUM_BLINDINGS, E::Fr::zero()); // add coefficients in front of X^{-2n}...X^{-n-1}, X^{-n}...X^{-1} - for (r, s) in rxy_prime[0..(2 * n)] + for (r, s) in rxy_prime[NUM_BLINDINGS..(2 * n + NUM_BLINDINGS)] .iter_mut() .rev() .zip(s_poly_negative) @@ -257,54 +388,79 @@ pub fn create_proof_on_srs, S: SynthesisDriver>( r.add_assign(&s); } // add coefficients in front of X^{1}...X^{n}, X^{n+1}...X^{2*n} - for (r, s) in rxy_prime[(2 * n + 1)..].iter_mut().zip(s_poly_positive) { + for (r, s) in rxy_prime[(2 * n + 1 + NUM_BLINDINGS)..].iter_mut().zip(s_poly_positive) { r.add_assign(&s); } } + // by this point all R related polynomials are blinded and evaluated for Y variable + // t(X, y) = r'(X, y)*r(X, 1) and will be later evaluated at z // contained degree in respect to X are from -4*n to 3*n including X^0 let mut txy = multiply_polynomials::(rx1.clone(), rxy_prime); - txy[4 * n] = E::Fr::zero(); // -k(y) + txy[4 * n + 2 * NUM_BLINDINGS] = E::Fr::zero(); // -k(y) // commit to t(X, y) to later open at z - let t = multiexp( - srs.g_positive_x_alpha[0..(3 * n)] - .iter() - .chain_ext(srs.g_negative_x_alpha[0..(4 * n)].iter()), - txy[(4 * n + 1)..] - .iter() - .chain_ext(txy[0..4 * n].iter().rev()), - ).into_affine(); + + let t = polynomial_commitment( + srs.d, + (4 * n) + 2*NUM_BLINDINGS, + 3 * n, + srs, + // skip what would be zero power + txy[0..(4 * n) + 2*NUM_BLINDINGS].iter() + .chain_ext(txy[(4 * n + 2*NUM_BLINDINGS + 1)..].iter()), + ); + + // let t = multiexp( + // srs.g_positive_x_alpha[0..(3 * n)] + // .iter() + // .chain_ext(srs.g_negative_x_alpha[0..(4 * n) + 2*NUM_BLINDINGS].iter()), + // txy[(4 * n + 1 + 2*NUM_BLINDINGS)..] + // .iter() + // .chain_ext(txy[0..(4 * n + 2*NUM_BLINDINGS)].iter().rev()), + // ).into_affine(); transcript.commit_point(&t); + let z: E::Fr = transcript.get_challenge_scalar(); let z_inv = z.inverse().ok_or(SynthesisError::DivisionByZero)?; - // TODO: use the faster way to evaluate the polynomials - let mut rz = E::Fr::zero(); - { - let mut tmp = z.pow(&[n as u64]); + let rz = { + let tmp = z_inv.pow(&[(2*n + NUM_BLINDINGS) as u64]); - for coeff in rx1.iter().rev() { - let mut coeff = *coeff; - coeff.mul_assign(&tmp); - rz.add_assign(&coeff); - tmp.mul_assign(&z_inv); - } - } + evaluate_at_consequitive_powers(&rx1, tmp, z) + }; - let mut rzy = E::Fr::zero(); - { - let mut tmp = z.pow(&[n as u64]); + // let mut rz = E::Fr::zero(); + // { + // let mut tmp = z.pow(&[n as u64]); - for mut coeff in rxy.into_iter().rev() { - coeff.mul_assign(&tmp); - rzy.add_assign(&coeff); - tmp.mul_assign(&z_inv); - } - } + // for coeff in rx1.iter().rev() { + // let mut coeff = *coeff; + // coeff.mul_assign(&tmp); + // rz.add_assign(&coeff); + // tmp.mul_assign(&z_inv); + // } + // } + + let rzy = { + let tmp = z_inv.pow(&[(2*n + NUM_BLINDINGS) as u64]); + + evaluate_at_consequitive_powers(&rxy, tmp, z) + }; + + // let mut rzy = E::Fr::zero(); + // { + // let mut tmp = z.pow(&[n as u64]); + + // for mut coeff in rxy.into_iter().rev() { + // coeff.mul_assign(&tmp); + // rzy.add_assign(&coeff); + // tmp.mul_assign(&z_inv); + // } + // } transcript.commit_scalar(&rz); transcript.commit_scalar(&rzy); @@ -313,62 +469,88 @@ pub fn create_proof_on_srs, S: SynthesisDriver>( let zy_opening = { // r(X, 1) - r(z, y) - rx1[2 * n].sub_assign(&rzy); + // subtract constant term from R(X, 1) + rx1[(2 * n + NUM_BLINDINGS)].sub_assign(&rzy); let mut point = y; point.mul_assign(&z); - let poly = kate_divison( - rx1.iter(), - point, - ); - let negative_poly = poly[0..2*n].iter().rev(); - let positive_poly = poly[2*n..].iter(); - multiexp( - srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( - srs.g_positive_x[0..positive_poly.len()].iter() - ), - negative_poly.chain_ext(positive_poly) - ).into_affine() + polynomial_commitment_opening( + 2 * n + NUM_BLINDINGS, + n, + &rx1, + point, + srs + ) + + // let poly = kate_divison( + // rx1.iter(), + // point, + // ); + + // let negative_poly = poly[0..(2*n + NUM_BLINDINGS)].iter().rev(); + // let positive_poly = poly[(2*n + NUM_BLINDINGS)..].iter(); + // multiexp( + // srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( + // srs.g_positive_x[0..positive_poly.len()].iter() + // ), + // negative_poly.chain_ext(positive_poly) + // ).into_affine() }; - let z_opening = { - rx1[2 * n].add_assign(&rzy); // restore + assert_eq!(rx1.len(), 3*n + NUM_BLINDINGS + 1); - for (t, &r) in txy[2 * n..].iter_mut().zip(rx1.iter()) { + let z_opening = { + rx1[(2 * n + NUM_BLINDINGS)].add_assign(&rzy); // restore + + // skip powers from until reach -2n - NUM_BLINDINGS + for (t, &r) in txy[(2 * n + NUM_BLINDINGS)..].iter_mut().zip(rx1.iter()) { let mut r = r; r.mul_assign(&r1); t.add_assign(&r); } - let mut val = E::Fr::zero(); - { - assert_eq!(txy.len(), 3*n + 1 + 4*n); - let mut tmp = z.pow(&[(3*n) as u64]); + let val = { + let tmp = z_inv.pow(&[(4*n + 2*NUM_BLINDINGS) as u64]); - for coeff in txy.iter().rev() { - let mut coeff = *coeff; - coeff.mul_assign(&tmp); - val.add_assign(&coeff); - tmp.mul_assign(&z_inv); - } - } + evaluate_at_consequitive_powers(&txy, tmp, z) + }; - txy[4 * n].sub_assign(&val); + // let mut val = E::Fr::zero(); + // { + // assert_eq!(txy.len(), 3*n + 1 + 4*n + 2*NUM_BLINDINGS); + // let mut tmp = z.pow(&[(3*n) as u64]); - let poly = kate_divison( - txy.iter(), - z, - ); + // for coeff in txy.iter().rev() { + // let mut coeff = *coeff; + // coeff.mul_assign(&tmp); + // val.add_assign(&coeff); + // tmp.mul_assign(&z_inv); + // } + // } - let negative_poly = poly[0..4*n].iter().rev(); - let positive_poly = poly[4*n..].iter(); - multiexp( - srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( - srs.g_positive_x[0..positive_poly.len()].iter() - ), - negative_poly.chain_ext(positive_poly) - ).into_affine() + txy[(4 * n + 2*NUM_BLINDINGS)].sub_assign(&val); + + polynomial_commitment_opening( + 4*n + 2*NUM_BLINDINGS, + 3*n, + &txy, + z, + srs) + + // let poly = kate_divison( + // txy.iter(), + // z, + // ); + + // let negative_poly = poly[0..(4*n + 2*NUM_BLINDINGS)].iter().rev(); + // let positive_poly = poly[(4*n + 2*NUM_BLINDINGS)..].iter(); + // multiexp( + // srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( + // srs.g_positive_x[0..positive_poly.len()].iter() + // ), + // negative_poly.chain_ext(positive_poly) + // ).into_affine() }; Ok(Proof { @@ -427,3 +609,67 @@ fn my_fun_circuit_test() { let elapsed = start.elapsed(); println!("time to verify: {:?}", elapsed); } + +#[test] +fn polynomial_commitment_test() { + use ff::PrimeField; + use pairing::bls12_381::{Bls12, Fr}; + use super::*; + use crate::sonic::cs::{Basic, ConstraintSystem, LinearCombination}; + use rand::{thread_rng}; + + struct MyCircuit; + + impl Circuit for MyCircuit { + fn synthesize>(&self, cs: &mut CS) -> Result<(), SynthesisError> { + let (a, b, _) = cs.multiply(|| { + Ok(( + E::Fr::from_str("10").unwrap(), + E::Fr::from_str("20").unwrap(), + E::Fr::from_str("200").unwrap(), + )) + })?; + + cs.enforce_zero(LinearCombination::from(a) + a - b); + + //let multiplier = cs.alloc_input(|| Ok(E::Fr::from_str("20").unwrap()))?; + + //cs.enforce_zero(LinearCombination::from(b) - multiplier); + + Ok(()) + } + } + + let srs = SRS::::new( + 20, + Fr::from_str("22222").unwrap(), + Fr::from_str("33333333").unwrap(), + ); + let proof = self::create_proof_on_srs::(&MyCircuit, &srs).unwrap(); + + let z: Fr; + let y: Fr; + { + let mut transcript = Transcript::new(&[]); + transcript.commit_point(&proof.r); + y = transcript.get_challenge_scalar(); + transcript.commit_point(&proof.t); + z = transcript.get_challenge_scalar(); + } + + use std::time::{Instant}; + + let rng = thread_rng(); + let mut batch = MultiVerifier::::new(MyCircuit, &srs, rng).unwrap().batch; + + // try to open commitment to r at r(z, 1); + + let mut random = Fr::one(); + random.double(); + + batch.add_opening(proof.z_opening, random, z); + batch.add_commitment_max_n(proof.r, random); + batch.add_opening_value(proof.rz, random); + + assert!(batch.check_all()); +} diff --git a/src/sonic/helped/verifier.rs b/src/sonic/helped/verifier.rs index 768500d..2bded1b 100644 --- a/src/sonic/helped/verifier.rs +++ b/src/sonic/helped/verifier.rs @@ -19,7 +19,7 @@ use crate::sonic::srs::SRS; pub struct MultiVerifier, S: SynthesisDriver, R: Rng> { circuit: C, - batch: Batch, + pub(crate) batch: Batch, k_map: Vec, n: usize, q: usize, diff --git a/src/sonic/util.rs b/src/sonic/util.rs index 958c0bd..d011879 100644 --- a/src/sonic/util.rs +++ b/src/sonic/util.rs @@ -183,6 +183,35 @@ pub fn mut_evaluate_at_consequitive_powers<'a, F: Field> ( result } +/// Multiply each coefficient by some power of the base in a form +/// `first_power * base^{i}` +pub fn mut_distribute_consequitive_powers<'a, F: Field> ( + coeffs: &mut [F], + first_power: F, + base: F +) + { + use crate::multicore::Worker; + + let worker = Worker::new(); + + worker.scope(coeffs.len(), |scope, chunk| { + for (i, coeffs_chunk) in coeffs.chunks_mut(chunk).enumerate() + { + scope.spawn(move |_| { + let mut current_power = base.pow(&[(i*chunk) as u64]); + current_power.mul_assign(&first_power); + + for mut p in coeffs_chunk { + p.mul_assign(¤t_power); + + current_power.mul_assign(&base); + } + }); + } + }); +} + pub fn multiexp< 'a, G: CurveAffine, @@ -202,6 +231,8 @@ where let s: Vec<::Repr> = s.into_iter().map(|e| e.into_repr()).collect::>(); let g: Vec = g.into_iter().map(|e| *e).collect::>(); + assert_eq!(s.len(), g.len(), "scalars and exponents must have the same length"); + let pool = Worker::new(); let result = dense_multiexp( @@ -588,5 +619,34 @@ fn test_mut_eval_at_powers() { let acc_parallel = mut_evaluate_at_consequitive_powers(&mut b[..], first_power, x); assert_eq!(acc_parallel, acc); + assert!(a == b); +} + +#[test] +fn test_mut_distribute_powers() { + use rand::{self, Rand, Rng}; + use pairing::bls12_381::Bls12; + use pairing::bls12_381::Fr; + + const SAMPLES: usize = 100000; + + let rng = &mut rand::thread_rng(); + let mut a = (0..SAMPLES).map(|_| Fr::rand(rng)).collect::>(); + let mut b = a.clone(); + let x: Fr = rng.gen(); + let n: u32 = rng.gen(); + + { + let mut tmp = x.pow(&[n as u64]); + + for mut coeff in a.iter_mut() { + coeff.mul_assign(&tmp); + tmp.mul_assign(&x); + } + } + + let first_power = x.pow(&[n as u64]); + mut_distribute_consequitive_powers(&mut b[..], first_power, x); + assert!(a == b); } \ No newline at end of file From 5f8618b437288bba1268fc2521ee90c9d89a36e8 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 12 Feb 2019 02:31:07 +0300 Subject: [PATCH 24/42] ok, blinding are may be in place, but need more carefull evaluation cause r(zy, 1) != r(z, y) if follow the algorithm 1 from the paper --- src/sonic/helped/generator.rs | 7 ++-- src/sonic/helped/prover.rs | 70 +------------------------------ src/sonic/util.rs | 78 +++++++++++++++++++++++++++++++---- 3 files changed, 75 insertions(+), 80 deletions(-) diff --git a/src/sonic/helped/generator.rs b/src/sonic/helped/generator.rs index 79e8b79..f99f534 100644 --- a/src/sonic/helped/generator.rs +++ b/src/sonic/helped/generator.rs @@ -46,6 +46,7 @@ use crate::sonic::cs::ConstraintSystem as SonicConstraintSystem; use crate::sonic::cs::Variable as SonicVariable; use crate::sonic::cs::Coeff; use crate::sonic::sonic::{AdaptorCircuit}; +use super::parameters::NUM_BLINDINGS; use crate::verbose_flag; @@ -381,7 +382,7 @@ pub fn generate_parameters( where E: Engine, C: Circuit { let circuit_parameters = get_circuit_parameters::(circuit)?; - let min_d = circuit_parameters.n * 4; + let min_d = circuit_parameters.n * 4 + NUM_BLINDINGS; let srs = generate_srs(alpha, x, min_d)?; @@ -407,8 +408,8 @@ pub fn generate_parameters_on_srs_and_information( information: CircuitParameters ) -> Result, SynthesisError> { - assert!(srs.d >= information.n * 4); - let min_d = information.n * 4; + assert!(srs.d >= information.n * 4 + NUM_BLINDINGS); + let min_d = information.n * 4 + NUM_BLINDINGS; let trimmed_srs: SRS = SRS { d: min_d, diff --git a/src/sonic/helped/prover.rs b/src/sonic/helped/prover.rs index 6e730ee..21af7ef 100644 --- a/src/sonic/helped/prover.rs +++ b/src/sonic/helped/prover.rs @@ -210,80 +210,14 @@ pub fn create_proof_on_srs, S: SynthesisDriver>( let rng = &mut thread_rng(); // c_{n+1}, c_{n+2}, c_{n+3}, c_{n+4} - let blindings: Vec = (0..NUM_BLINDINGS).into_iter().map(|_| E::Fr::rand(rng)).collect(); + // let blindings: Vec = (0..NUM_BLINDINGS).into_iter().map(|_| E::Fr::rand(rng)).collect(); - // let blindings: Vec = vec![E::Fr::zero(); NUM_BLINDINGS]; + let blindings: Vec = vec![E::Fr::zero(); NUM_BLINDINGS]; // let max_n = 3*n + 1 + NUM_BLINDINGS; // let max_n = 3*n + 1; - fn polynomial_commitment< - 'a, - EE: Engine, - IS: IntoIterator, - >( - max: usize, - largest_negative_power: usize, - largest_positive_power: usize, - srs: &'a SRS, - s: IS, - ) -> EE::G1Affine - where - IS::IntoIter: ExactSizeIterator, - { - // smallest power is d - max - largest_negative_power; It should either be 1 for use of positive powers only, - // of we should use part of the negative powers - let d = srs.d; - assert!(max >= largest_positive_power); - if d < max + largest_negative_power + 1 { - println!("Use negative powers for polynomial commitment"); - let min_power = largest_negative_power + max - d; - let max_power = d + largest_positive_power - max; - println!("Min power = {}, max = {}", min_power, max_power); - // need to use negative powers to make a proper commitment - return multiexp( - srs.g_negative_x_alpha[0..min_power].iter().rev() - .chain_ext(srs.g_positive_x_alpha[..max_power].iter()), - s - ).into_affine(); - } else { - println!("Use positive powers only for polynomial commitment"); - return multiexp( - srs.g_positive_x_alpha[(srs.d - max - largest_negative_power - 1)..].iter(), - s - ).into_affine(); - } - } - - fn polynomial_commitment_opening< - 'a, - EE: Engine, - I: IntoIterator - >( - largest_negative_power: usize, - largest_positive_power: usize, - polynomial_coefficients: I, - mut point: EE::Fr, - srs: &'a SRS, - ) -> EE::G1Affine - where I::IntoIter: DoubleEndedIterator + ExactSizeIterator, - { - let poly = kate_divison( - polynomial_coefficients, - point, - ); - - let negative_poly = poly[0..largest_negative_power].iter().rev(); - let positive_poly = poly[largest_negative_power..].iter(); - multiexp( - srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( - srs.g_positive_x[0..positive_poly.len()].iter() - ), - negative_poly.chain_ext(positive_poly) - ).into_affine() - } - let r = polynomial_commitment::( n, 2*n + NUM_BLINDINGS, diff --git a/src/sonic/util.rs b/src/sonic/util.rs index d011879..240ad1f 100644 --- a/src/sonic/util.rs +++ b/src/sonic/util.rs @@ -1,6 +1,7 @@ use crate::SynthesisError; use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine}; use pairing::{CurveAffine, CurveProjective, Engine}; +use super::srs::SRS; pub trait ChainExt: Iterator { fn chain_ext(self, other: U) -> Chain @@ -71,8 +72,71 @@ where } } +pub fn polynomial_commitment< + 'a, + E: Engine, + IS: IntoIterator, + >( + max: usize, + largest_negative_power: usize, + largest_positive_power: usize, + srs: &'a SRS, + s: IS, + ) -> E::G1Affine + where + IS::IntoIter: ExactSizeIterator, + { + // smallest power is d - max - largest_negative_power; It should either be 1 for use of positive powers only, + // of we should use part of the negative powers + let d = srs.d; + assert!(max >= largest_positive_power); + if d < max + largest_negative_power + 1 { + let min_power = largest_negative_power + max - d; + let max_power = d + largest_positive_power - max; + // need to use negative powers to make a proper commitment + return multiexp( + srs.g_negative_x_alpha[0..min_power].iter().rev() + .chain_ext(srs.g_positive_x_alpha[..max_power].iter()), + s + ).into_affine(); + } else { + return multiexp( + srs.g_positive_x_alpha[(srs.d - max - largest_negative_power - 1)..].iter(), + s + ).into_affine(); + } + } + +pub fn polynomial_commitment_opening< + 'a, + E: Engine, + I: IntoIterator + >( + largest_negative_power: usize, + largest_positive_power: usize, + polynomial_coefficients: I, + mut point: E::Fr, + srs: &'a SRS, + ) -> E::G1Affine + where I::IntoIter: DoubleEndedIterator + ExactSizeIterator, + { + let poly = kate_divison( + polynomial_coefficients, + point, + ); + + let negative_poly = poly[0..largest_negative_power].iter().rev(); + let positive_poly = poly[largest_negative_power..].iter(); + multiexp( + srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( + srs.g_positive_x[0..positive_poly.len()].iter() + ), + negative_poly.chain_ext(positive_poly) + ).into_affine() + } + extern crate crossbeam; -use self::crossbeam::channel::{unbounded, RecvError}; +use self::crossbeam::channel::{unbounded}; pub fn evaluate_at_consequitive_powers<'a, F: Field> ( coeffs: &[F], @@ -115,15 +179,11 @@ pub fn evaluate_at_consequitive_powers<'a, F: Field> ( let mut result = F::zero(); loop { - let v = r.recv(); - match v { - Ok(value) => { - result.add_assign(&value); - }, - Err(RecvError) => { - break; - } + if r.is_empty() { + break; } + let value = r.recv().expect("must not be empty"); + result.add_assign(&value); } result From 6dc9055cf3365dd2d2b0deedc2709db7b8d936a5 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Mon, 18 Feb 2019 18:56:11 +0100 Subject: [PATCH 25/42] introduce blindings --- src/domain.rs | 2 +- src/sonic/cs/mod.rs | 44 ++++++- src/sonic/helped/generator.rs | 9 +- src/sonic/helped/parameters.rs | 2 +- src/sonic/helped/prover.rs | 228 +++++++++------------------------ src/sonic/util.rs | 53 ++++++-- tests/mimc.rs | 7 +- 7 files changed, 160 insertions(+), 185 deletions(-) diff --git a/src/domain.rs b/src/domain.rs index d9c87b2..1d0203a 100644 --- a/src/domain.rs +++ b/src/domain.rs @@ -392,7 +392,7 @@ fn test_field_element_multiplication_bn256() { use num_cpus; let cpus = num_cpus::get(); - const SAMPLES: usize = 1 << 27; + const SAMPLES: usize = 1 << 22; let rng = &mut rand::thread_rng(); let v1 = (0..SAMPLES).map(|_| Scalar::(Fr::rand(rng))).collect::>(); diff --git a/src/sonic/cs/mod.rs b/src/sonic/cs/mod.rs index 4860cf5..a2069e3 100644 --- a/src/sonic/cs/mod.rs +++ b/src/sonic/cs/mod.rs @@ -210,9 +210,49 @@ impl SynthesisDriver for Basic { circuit.synthesize(&mut tmp)?; - // TODO: add blinding factors so we actually get zero-knowledge + // let blindings_to_add = 6; - // println!("n = {}", tmp.n); + // for i in 0..blindings_to_add { + + // match tmp.current_variable.take() { + // Some(index) => { + // let var_a = Variable::A(index); + // let var_b = Variable::B(index); + // let var_c = Variable::C(index); + + // let mut product = None; + + // let value_a = tmp.backend.get_var(var_a); + + // tmp.backend.set_var(var_b, || { + // let value_b = E::Fr::one(); + // product = Some(value_a.ok_or(SynthesisError::AssignmentMissing)?); + // product.as_mut().map(|product| product.mul_assign(&value_b)); + + // Ok(value_b) + // })?; + + // tmp.backend.set_var(var_c, || { + // product.ok_or(SynthesisError::AssignmentMissing) + // })?; + + // tmp.current_variable = None; + // }, + // None => { + // self.n += 1; + // let index = self.n ; + // tmp.backend.new_multiplication_gate(); + + // let var_a = Variable::A(index); + + // tmp.backend.set_var(var_a, value)?; + + // tmp.current_variable = Some(index); + // } + // } + // } + + // TODO: add blinding factors so we actually get zero-knowledge Ok(()) } diff --git a/src/sonic/helped/generator.rs b/src/sonic/helped/generator.rs index f99f534..a1fb6de 100644 --- a/src/sonic/helped/generator.rs +++ b/src/sonic/helped/generator.rs @@ -382,7 +382,8 @@ pub fn generate_parameters( where E: Engine, C: Circuit { let circuit_parameters = get_circuit_parameters::(circuit)?; - let min_d = circuit_parameters.n * 4 + NUM_BLINDINGS; + let min_d = circuit_parameters.n * 4 + 2*NUM_BLINDINGS; + println!{"Mid d = {}", min_d}; let srs = generate_srs(alpha, x, min_d)?; @@ -408,8 +409,8 @@ pub fn generate_parameters_on_srs_and_information( information: CircuitParameters ) -> Result, SynthesisError> { - assert!(srs.d >= information.n * 4 + NUM_BLINDINGS); - let min_d = information.n * 4 + NUM_BLINDINGS; + assert!(srs.d >= information.n * 4 + 2*NUM_BLINDINGS); + let min_d = information.n * 4 + 2*NUM_BLINDINGS; let trimmed_srs: SRS = SRS { d: min_d, @@ -612,7 +613,7 @@ pub fn generate_srs( &worker ); - // Evaluate for auxillary variables. + // Evaluate for negative powers eval( &g1_wnaf, &g2_wnaf, diff --git a/src/sonic/helped/parameters.rs b/src/sonic/helped/parameters.rs index 8128e61..488c186 100644 --- a/src/sonic/helped/parameters.rs +++ b/src/sonic/helped/parameters.rs @@ -19,7 +19,7 @@ use std::io::{self, Read, Write}; use std::sync::Arc; use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; -pub const NUM_BLINDINGS: usize = 4; +pub const NUM_BLINDINGS: usize = 6; // pub const NUM_BLINDINGS: usize = 0; #[derive(Clone, Debug, Eq)] diff --git a/src/sonic/helped/prover.rs b/src/sonic/helped/prover.rs index 21af7ef..79cc0ec 100644 --- a/src/sonic/helped/prover.rs +++ b/src/sonic/helped/prover.rs @@ -210,14 +210,9 @@ pub fn create_proof_on_srs, S: SynthesisDriver>( let rng = &mut thread_rng(); // c_{n+1}, c_{n+2}, c_{n+3}, c_{n+4} - // let blindings: Vec = (0..NUM_BLINDINGS).into_iter().map(|_| E::Fr::rand(rng)).collect(); - - let blindings: Vec = vec![E::Fr::zero(); NUM_BLINDINGS]; - - // let max_n = 3*n + 1 + NUM_BLINDINGS; - - // let max_n = 3*n + 1; + let blindings: Vec = (0..NUM_BLINDINGS).into_iter().map(|_| E::Fr::rand(rng)).collect(); + // r is a commitment to r(X, 1) let r = polynomial_commitment::( n, 2*n + NUM_BLINDINGS, @@ -225,25 +220,11 @@ pub fn create_proof_on_srs, S: SynthesisDriver>( &srs, blindings.iter().rev() .chain_ext(wires.c.iter().rev()) - // wires.c.iter().rev() .chain_ext(wires.b.iter().rev()) .chain_ext(Some(E::Fr::zero()).iter()) .chain_ext(wires.a.iter()), ); - // let r = multiexp( - // // F <- g^{alpha*x^{d-max}*f(x)} = g^{alpha*x^{d-n}*\sum_{i = -2n - 4}^{n} k_i*x^{i}} = - // // = g^{alpha*\sum_{i = d - 3n - 4}^{n} k_i*x^{i}} - // // g^{alpha*(x^{d - 3n - 4}*k_{-2n-4} + ... + x^{d - n}*k_{0} + ... + x^{d + n + 1}*k_{n}) - // srs.g_positive_x_alpha[(srs.d - max_n)..].iter(), - // blindings.iter().rev() - // .chain_ext(wires.c.iter().rev()) - // // wires.c.iter().rev() - // .chain_ext(wires.b.iter().rev()) - // .chain_ext(Some(E::Fr::zero()).iter()) - // .chain_ext(wires.a.iter()), - // ).into_affine(); - transcript.commit_point(&r); let y: E::Fr = transcript.get_challenge_scalar(); @@ -254,50 +235,23 @@ pub fn create_proof_on_srs, S: SynthesisDriver>( // TODO: add blindings c_{n+1}*X^{-2n - 1}, c_{n+2}*X^{-2n - 2}, c_{n+3}*X^{-2n - 3}, c_{n+4}*X^{-2n - 4} let mut rx1 = wires.b; rx1.extend(wires.c); + rx1.extend(blindings.clone()); rx1.reverse(); rx1.push(E::Fr::zero()); rx1.extend(wires.a); - // let mut rxy = rx1.clone(); - // it's not yet blinded, so blind explicitly here - let mut rxy = rx1; - let mut rx1 = { - // c_{n+1}*X^{-2n - 1}, c_{n+2}*X^{-2n - 2}, c_{n+3}*X^{-2n - 3}, c_{n+4}*X^{-2n - 4} - let mut tmp = blindings.clone(); - // c_{n+4}*X^{-2n - 4}, c_{n+3}*X^{-2n - 3}, c_{n+2}*X^{-2n - 2}, c_{n+1}*X^{-2n - 1}, - tmp.reverse(); - tmp.extend(rxy.clone()); - - tmp - }; + let mut rxy = rx1.clone(); let y_inv = y.inverse().ok_or(SynthesisError::DivisionByZero)?; - // y^{-2n} - let tmp = y_inv.pow(&[2*n as u64]); + // y^(-2n - num blindings) + let tmp = y_inv.pow(&[(2*n + NUM_BLINDINGS) as u64]); mut_distribute_consequitive_powers( &mut rxy, tmp, y, ); - - // let mut tmp = y.pow(&[n as u64]); - // // evaluate r(X, y) - // for rxy in rxy.iter_mut().rev() { - // rxy.mul_assign(&tmp); - // tmp.mul_assign(&y_inv); - // } - - // Blindings are not affected by evaluation on Y cause blindings make constant term over Y - // We just add them here to have it now in a form of coefficients over X - let rxy = { - let mut tmp = blindings.clone(); - tmp.reverse(); - tmp.extend(rxy); - - tmp - }; // negative powers [-1, -2n], positive [1, n] let (s_poly_negative, s_poly_positive) = { @@ -335,7 +289,6 @@ pub fn create_proof_on_srs, S: SynthesisDriver>( txy[4 * n + 2 * NUM_BLINDINGS] = E::Fr::zero(); // -k(y) // commit to t(X, y) to later open at z - let t = polynomial_commitment( srs.d, (4 * n) + 2*NUM_BLINDINGS, @@ -346,18 +299,8 @@ pub fn create_proof_on_srs, S: SynthesisDriver>( .chain_ext(txy[(4 * n + 2*NUM_BLINDINGS + 1)..].iter()), ); - // let t = multiexp( - // srs.g_positive_x_alpha[0..(3 * n)] - // .iter() - // .chain_ext(srs.g_negative_x_alpha[0..(4 * n) + 2*NUM_BLINDINGS].iter()), - // txy[(4 * n + 1 + 2*NUM_BLINDINGS)..] - // .iter() - // .chain_ext(txy[0..(4 * n + 2*NUM_BLINDINGS)].iter().rev()), - // ).into_affine(); - transcript.commit_point(&t); - let z: E::Fr = transcript.get_challenge_scalar(); let z_inv = z.inverse().ok_or(SynthesisError::DivisionByZero)?; @@ -367,35 +310,30 @@ pub fn create_proof_on_srs, S: SynthesisDriver>( evaluate_at_consequitive_powers(&rx1, tmp, z) }; - // let mut rz = E::Fr::zero(); // { - // let mut tmp = z.pow(&[n as u64]); + // rx1[(2 * n + NUM_BLINDINGS)].sub_assign(&rz); - // for coeff in rx1.iter().rev() { - // let mut coeff = *coeff; - // coeff.mul_assign(&tmp); - // rz.add_assign(&coeff); - // tmp.mul_assign(&z_inv); - // } + // let opening = polynomial_commitment_opening( + // 2 * n + NUM_BLINDINGS, + // n, + // rx1.iter(), + // z, + // srs + // ); + + // let valid_rz_commitment = check_polynomial_commitment(&r, &z, &rz, &opening, n, &srs); + // assert!(valid_rz_commitment); + + // rx1[(2 * n + NUM_BLINDINGS)].add_assign(&rz); // } + // rzy is evaluation of r(X, Y) at z, y let rzy = { let tmp = z_inv.pow(&[(2*n + NUM_BLINDINGS) as u64]); evaluate_at_consequitive_powers(&rxy, tmp, z) }; - // let mut rzy = E::Fr::zero(); - // { - // let mut tmp = z.pow(&[n as u64]); - - // for mut coeff in rxy.into_iter().rev() { - // coeff.mul_assign(&tmp); - // rzy.add_assign(&coeff); - // tmp.mul_assign(&z_inv); - // } - // } - transcript.commit_scalar(&rz); transcript.commit_scalar(&rzy); @@ -416,24 +354,11 @@ pub fn create_proof_on_srs, S: SynthesisDriver>( point, srs ) - - // let poly = kate_divison( - // rx1.iter(), - // point, - // ); - - // let negative_poly = poly[0..(2*n + NUM_BLINDINGS)].iter().rev(); - // let positive_poly = poly[(2*n + NUM_BLINDINGS)..].iter(); - // multiexp( - // srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( - // srs.g_positive_x[0..positive_poly.len()].iter() - // ), - // negative_poly.chain_ext(positive_poly) - // ).into_affine() }; assert_eq!(rx1.len(), 3*n + NUM_BLINDINGS + 1); + // it's an opening of t(X, y) at z let z_opening = { rx1[(2 * n + NUM_BLINDINGS)].add_assign(&rzy); // restore @@ -450,19 +375,6 @@ pub fn create_proof_on_srs, S: SynthesisDriver>( evaluate_at_consequitive_powers(&txy, tmp, z) }; - // let mut val = E::Fr::zero(); - // { - // assert_eq!(txy.len(), 3*n + 1 + 4*n + 2*NUM_BLINDINGS); - // let mut tmp = z.pow(&[(3*n) as u64]); - - // for coeff in txy.iter().rev() { - // let mut coeff = *coeff; - // coeff.mul_assign(&tmp); - // val.add_assign(&coeff); - // tmp.mul_assign(&z_inv); - // } - // } - txy[(4 * n + 2*NUM_BLINDINGS)].sub_assign(&val); polynomial_commitment_opening( @@ -471,22 +383,13 @@ pub fn create_proof_on_srs, S: SynthesisDriver>( &txy, z, srs) - - // let poly = kate_divison( - // txy.iter(), - // z, - // ); - - // let negative_poly = poly[0..(4*n + 2*NUM_BLINDINGS)].iter().rev(); - // let positive_poly = poly[(4*n + 2*NUM_BLINDINGS)..].iter(); - // multiexp( - // srs.g_negative_x[1..(negative_poly.len() + 1)].iter().chain_ext( - // srs.g_positive_x[0..positive_poly.len()].iter() - // ), - // negative_poly.chain_ext(positive_poly) - // ).into_affine() }; + // let mut zy = z; + // zy.mul_assign(&y); + // let valid_rzy_commitment = check_polynomial_commitment(&r, &zy, &rzy, &zy_opening, n, &srs); + // assert!(valid_rzy_commitment); + Ok(Proof { r, rz, rzy, t, z_opening, zy_opening }) @@ -547,63 +450,56 @@ fn my_fun_circuit_test() { #[test] fn polynomial_commitment_test() { use ff::PrimeField; + use ff::PrimeFieldRepr; use pairing::bls12_381::{Bls12, Fr}; use super::*; use crate::sonic::cs::{Basic, ConstraintSystem, LinearCombination}; - use rand::{thread_rng}; - - struct MyCircuit; - - impl Circuit for MyCircuit { - fn synthesize>(&self, cs: &mut CS) -> Result<(), SynthesisError> { - let (a, b, _) = cs.multiply(|| { - Ok(( - E::Fr::from_str("10").unwrap(), - E::Fr::from_str("20").unwrap(), - E::Fr::from_str("200").unwrap(), - )) - })?; - - cs.enforce_zero(LinearCombination::from(a) + a - b); - - //let multiplier = cs.alloc_input(|| Ok(E::Fr::from_str("20").unwrap()))?; - - //cs.enforce_zero(LinearCombination::from(b) - multiplier); - - Ok(()) - } - } + use rand::{thread_rng}; + use pairing::{CurveAffine}; let srs = SRS::::new( 20, Fr::from_str("22222").unwrap(), Fr::from_str("33333333").unwrap(), ); - let proof = self::create_proof_on_srs::(&MyCircuit, &srs).unwrap(); - let z: Fr; - let y: Fr; - { - let mut transcript = Transcript::new(&[]); - transcript.commit_point(&proof.r); - y = transcript.get_challenge_scalar(); - transcript.commit_point(&proof.t); - z = transcript.get_challenge_scalar(); - } + let mut rng = thread_rng(); + // x^-4 + x^-3 + x^-2 + x^-1 + x + x^2 + let mut poly = vec![Fr::one(), Fr::one(), Fr::one(), Fr::one(), Fr::zero(), Fr::one(), Fr::one()]; + // make commitment to the poly + let commitment = polynomial_commitment(2, 4, 2, &srs, poly.iter()); + let point: Fr = rng.gen(); + let mut tmp = point.inverse().unwrap(); + tmp.square(); + let value = evaluate_at_consequitive_powers(&poly, tmp, point); + // evaluate f(z) + poly[4] = value; + poly[4].negate(); + // f(x) - f(z) - use std::time::{Instant}; + let opening = polynomial_commitment_opening(4, 2, poly.iter(), point, &srs); - let rng = thread_rng(); - let mut batch = MultiVerifier::::new(MyCircuit, &srs, rng).unwrap().batch; + // e(W , hα x )e(g^{v} * W{-z} , hα ) = e(F , h^{x^{−d +max}} ) - // try to open commitment to r at r(z, 1); + let alpha_x_precomp = srs.h_positive_x_alpha[1].prepare(); + let alpha_precomp = srs.h_positive_x_alpha[0].prepare(); + let mut neg_x_n_minus_d_precomp = srs.h_negative_x[srs.d - 2]; + neg_x_n_minus_d_precomp.negate(); + let neg_x_n_minus_d_precomp = neg_x_n_minus_d_precomp.prepare(); + // let neg_x_n_minus_d_precomp = srs.h_negative_x[0].prepare(); - let mut random = Fr::one(); - random.double(); + let w = opening.prepare(); + let mut gv = srs.g_positive_x[0].mul(value.into_repr()); + let mut z_neg = point; + z_neg.negate(); + let w_minus_z = opening.mul(z_neg.into_repr()); + gv.add_assign(&w_minus_z); - batch.add_opening(proof.z_opening, random, z); - batch.add_commitment_max_n(proof.r, random); - batch.add_opening_value(proof.rz, random); + let gv = gv.into_affine().prepare(); - assert!(batch.check_all()); + assert!(Bls12::final_exponentiation(&Bls12::miller_loop(&[ + (&w, &alpha_x_precomp), + (&gv, &alpha_precomp), + (&commitment.prepare(), &neg_x_n_minus_d_precomp), + ])).unwrap() == ::Fqk::one()); } diff --git a/src/sonic/util.rs b/src/sonic/util.rs index 240ad1f..8aad10c 100644 --- a/src/sonic/util.rs +++ b/src/sonic/util.rs @@ -86,7 +86,7 @@ pub fn polynomial_commitment< where IS::IntoIter: ExactSizeIterator, { - // smallest power is d - max - largest_negative_power; It should either be 1 for use of positive powers only, + // smallest power is d - max - largest_negative_power; It should either be 0 for use of positive powers only, // of we should use part of the negative powers let d = srs.d; assert!(max >= largest_positive_power); @@ -107,6 +107,8 @@ pub fn polynomial_commitment< } } + +/// For now this function MUST take a polynomial in a form f(x) - f(z) pub fn polynomial_commitment_opening< 'a, E: Engine, @@ -115,7 +117,7 @@ pub fn polynomial_commitment_opening< largest_negative_power: usize, largest_positive_power: usize, polynomial_coefficients: I, - mut point: E::Fr, + point: E::Fr, srs: &'a SRS, ) -> E::G1Affine where I::IntoIter: DoubleEndedIterator + ExactSizeIterator, @@ -229,15 +231,11 @@ pub fn mut_evaluate_at_consequitive_powers<'a, F: Field> ( let mut result = F::zero(); loop { - let v = r.recv(); - match v { - Ok(value) => { - result.add_assign(&value); - }, - Err(RecvError) => { - break; - } + if r.is_empty() { + break; } + let value = r.recv().expect("must not be empty"); + result.add_assign(&value); } result @@ -401,6 +399,41 @@ where q } +/// Convenience function to check polynomail commitment +pub fn check_polynomial_commitment( + commitment: &E::G1Affine, + point: &E::Fr, + value: &E::Fr, + opening: &E::G1Affine, + max: usize, + srs: &SRS +) -> bool { + // e(W , hα x )e(g^{v} * W{-z} , hα ) = e(F , h^{x^{−d +max}} ) + if srs.d < max { + return false; + } + let alpha_x_precomp = srs.h_positive_x_alpha[1].prepare(); + let alpha_precomp = srs.h_positive_x_alpha[0].prepare(); + let mut neg_x_n_minus_d_precomp = srs.h_negative_x[srs.d - max]; + neg_x_n_minus_d_precomp.negate(); + let neg_x_n_minus_d_precomp = neg_x_n_minus_d_precomp.prepare(); + + let w = opening.prepare(); + let mut gv = srs.g_positive_x[0].mul(value.into_repr()); + let mut z_neg = *point; + z_neg.negate(); + let w_minus_z = opening.mul(z_neg.into_repr()); + gv.add_assign(&w_minus_z); + + let gv = gv.into_affine().prepare(); + + E::final_exponentiation(&E::miller_loop(&[ + (&w, &alpha_x_precomp), + (&gv, &alpha_precomp), + (&commitment.prepare(), &neg_x_n_minus_d_precomp), + ])).unwrap() == E::Fqk::one() +} + #[test] fn laurent_division() { use ff::PrimeField; diff --git a/tests/mimc.rs b/tests/mimc.rs index 8091f99..c0dce24 100644 --- a/tests/mimc.rs +++ b/tests/mimc.rs @@ -661,7 +661,8 @@ fn test_high_level_sonic_api() { verify_proofs, create_proof, create_advice, - create_aggregate + create_aggregate, + get_circuit_parameters }; { @@ -685,6 +686,9 @@ fn test_high_level_sonic_api() { constants: &constants }; + let info = get_circuit_parameters::(circuit.clone()).expect("Must get circuit info"); + println!("{:?}", info); + let params = generate_random_parameters(circuit.clone(), &mut rng).unwrap(); println!("creating proof"); @@ -700,6 +704,7 @@ fn test_high_level_sonic_api() { println!("creating aggregate for {} proofs", samples); let start = Instant::now(); let proofs: Vec<_> = (0..samples).map(|_| (proof.clone(), advice.clone())).collect(); + let aggregate = create_aggregate::(circuit.clone(), &proofs, ¶ms); println!("done in {:?}", start.elapsed()); From d4b8a481f17eacac06e3410aa809dbf6359951f9 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Mon, 18 Feb 2019 17:56:15 -0500 Subject: [PATCH 26/42] start implementing unhelped sonic --- src/sonic/mod.rs | 1 + src/sonic/unhelped/mod.rs | 6 +++ src/sonic/unhelped/s2_proof.rs | 79 ++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) create mode 100644 src/sonic/unhelped/mod.rs create mode 100644 src/sonic/unhelped/s2_proof.rs diff --git a/src/sonic/mod.rs b/src/sonic/mod.rs index 54e3965..6786a7f 100644 --- a/src/sonic/mod.rs +++ b/src/sonic/mod.rs @@ -8,6 +8,7 @@ pub mod srs; pub mod util; pub mod helped; pub mod cs; +pub mod unhelped; mod transcript; diff --git a/src/sonic/unhelped/mod.rs b/src/sonic/unhelped/mod.rs new file mode 100644 index 0000000..63722fa --- /dev/null +++ b/src/sonic/unhelped/mod.rs @@ -0,0 +1,6 @@ +/// Largeley this module is implementation of provable evaluation of s(z, y), that is represented in two parts +/// s2(X, Y) = \sum_{i=1}^{N} (Y^{-i} + Y^{i})X^{i} +/// s1(X, Y) = ... +/// s1 part requires grand product and permutation arguments, that are also implemented + +pub mod s2_proof; \ No newline at end of file diff --git a/src/sonic/unhelped/s2_proof.rs b/src/sonic/unhelped/s2_proof.rs new file mode 100644 index 0000000..f7ce4c8 --- /dev/null +++ b/src/sonic/unhelped/s2_proof.rs @@ -0,0 +1,79 @@ +use ff::{Field}; +use pairing::{Engine, CurveProjective, CurveAffine}; +use std::marker::PhantomData; + +use crate::sonic::srs::SRS; +use crate::sonic::util::*; + +#[derive(Clone)] +pub struct S2Eval { + n: usize, + _marker: PhantomData +} + +#[derive(Clone)] +pub struct S2Proof { + o: E::G1Affine, + c_value: E::Fr, + d_value: E::Fr, + c_opening: E::G1Affine, + d_opening: E::G1Affine +} + +impl S2Eval { + pub fn new(n: usize) -> Self { + S2Eval { + n: n, + _marker: PhantomData + } + } + + pub fn evaluate(&self, x: E::Fr, y: E::Fr, srs: &SRS) -> S2Proof { + // create a reference element first + + // TODO: parallelize + let mut o = E::G1::zero(); + for i in 0..self.n { + o.add_assign_mixed(&srs.g_positive_x_alpha[i]); + } + + let mut poly = vec![E::Fr::one(); self.n+1]; + + let (c, c_opening) = { + let mut point = y; + point.mul_assign(&x); + let val = evaluate_at_consequitive_powers(&poly[1..], E::Fr::one(), point); + poly[0] = val; + poly[0].negate(); + let opening = polynomial_commitment_opening(0, self.n, poly.iter(), point, &srs); + + (val, opening) + }; + + let (d, d_opening) = { + let mut point = y.inverse().unwrap(); + point.mul_assign(&x); + let val = evaluate_at_consequitive_powers(&poly[1..], E::Fr::one(), point); + poly[0] = val; + poly[0].negate(); + let opening = polynomial_commitment_opening(0, self.n, poly.iter(), point, &srs); + + (val, opening) + }; + + + S2Proof { + o: o.into_affine(), + c_value: c, + d_value: d, + c_opening: c_opening, + d_opening: d_opening + } + } + + pub fn verify(proof: &S2Proof, srs: &SRS) -> bool { + true + } + + +} \ No newline at end of file From 37f57a99a67346dd86ca08d0968bf4d1dbfb0eb8 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Thu, 21 Feb 2019 18:55:56 -0500 Subject: [PATCH 27/42] implement s2 commitment --- src/sonic/unhelped/s2_proof.rs | 71 +++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 10 deletions(-) diff --git a/src/sonic/unhelped/s2_proof.rs b/src/sonic/unhelped/s2_proof.rs index f7ce4c8..fb2409e 100644 --- a/src/sonic/unhelped/s2_proof.rs +++ b/src/sonic/unhelped/s2_proof.rs @@ -1,4 +1,4 @@ -use ff::{Field}; +use ff::{Field, PrimeField, PrimeFieldRepr}; use pairing::{Engine, CurveProjective, CurveAffine}; use std::marker::PhantomData; @@ -21,6 +21,16 @@ pub struct S2Proof { } impl S2Eval { + pub fn calculate_commitment_element(n: usize, srs: &SRS) -> E::G1Affine { + // TODO: parallelize + let mut o = E::G1::zero(); + for i in 0..n { + o.add_assign_mixed(&srs.g_positive_x_alpha[i]); + } + + o.into_affine() + } + pub fn new(n: usize) -> Self { S2Eval { n: n, @@ -31,11 +41,7 @@ impl S2Eval { pub fn evaluate(&self, x: E::Fr, y: E::Fr, srs: &SRS) -> S2Proof { // create a reference element first - // TODO: parallelize - let mut o = E::G1::zero(); - for i in 0..self.n { - o.add_assign_mixed(&srs.g_positive_x_alpha[i]); - } + let o = Self::calculate_commitment_element(self.n, &srs); let mut poly = vec![E::Fr::one(); self.n+1]; @@ -63,7 +69,7 @@ impl S2Eval { S2Proof { - o: o.into_affine(), + o: o, c_value: c, d_value: d, c_opening: c_opening, @@ -71,9 +77,54 @@ impl S2Eval { } } - pub fn verify(proof: &S2Proof, srs: &SRS) -> bool { + pub fn verify(x: E::Fr, y: E::Fr, proof: &S2Proof, srs: &SRS) -> bool { + + // e(C,hαx)e(C−yz,hα) = e(O,h)e(g−c,hα) + + let alpha_x_precomp = srs.h_positive_x_alpha[1].prepare(); + let alpha_precomp = srs.h_positive_x_alpha[0].prepare(); + let mut h_prep = srs.h_positive_x[0]; + h_prep.negate(); + let h_prep = h_prep.prepare(); + + let mut c_minus_xy = proof.c_value; + let mut xy = x; + xy.mul_assign(&y); + + c_minus_xy.sub_assign(&xy); + + let mut c_in_c_minus_xy = proof.c_opening.mul(c_minus_xy.into_repr()).into_affine(); + + let valid = E::final_exponentiation(&E::miller_loop(&[ + (&proof.c_opening.prepare(), &alpha_x_precomp), + (&c_in_c_minus_xy.prepare(), &alpha_precomp), + (&proof.o.prepare(), &h_prep), + ])).unwrap() == E::Fqk::one(); + + if !valid { + return false; + } + + // e(D,hαx)e(D−y−1z,hα) = e(O,h)e(g−d,hα) + + let mut d_minus_x_y_inv = proof.d_value; + let mut x_y_inv = x; + x_y_inv.mul_assign(&y.inverse().unwrap()); + + d_minus_x_y_inv.sub_assign(&x_y_inv); + + let mut d_in_d_minus_x_y_inv = proof.d_opening.mul(d_minus_x_y_inv.into_repr()).into_affine(); + + let valid = E::final_exponentiation(&E::miller_loop(&[ + (&proof.d_opening.prepare(), &alpha_x_precomp), + (&d_in_d_minus_x_y_inv.prepare(), &alpha_precomp), + (&proof.o.prepare(), &h_prep), + ])).unwrap() == E::Fqk::one(); + + if !valid { + return false; + } + true } - - } \ No newline at end of file From 0089b98439417856cb5110067f8f0658c1a6403f Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Thu, 21 Feb 2019 20:26:45 -0500 Subject: [PATCH 28/42] start making wellformedness argument --- .../unhelped/coefficient_product_argument.rs | 3 + src/sonic/unhelped/mod.rs | 3 +- src/sonic/unhelped/permutation_argument.rs | 0 src/sonic/unhelped/wellformed_argument.rs | 159 ++++++++++++++++++ src/sonic/util.rs | 61 +++++++ 5 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 src/sonic/unhelped/coefficient_product_argument.rs create mode 100644 src/sonic/unhelped/permutation_argument.rs create mode 100644 src/sonic/unhelped/wellformed_argument.rs diff --git a/src/sonic/unhelped/coefficient_product_argument.rs b/src/sonic/unhelped/coefficient_product_argument.rs new file mode 100644 index 0000000..7e4beb9 --- /dev/null +++ b/src/sonic/unhelped/coefficient_product_argument.rs @@ -0,0 +1,3 @@ +/// One must prove that for commitments to two polynomials of degree n products of the coefficients +/// in those two polynomials are equal (part of the permutation argument) + diff --git a/src/sonic/unhelped/mod.rs b/src/sonic/unhelped/mod.rs index 63722fa..54a280f 100644 --- a/src/sonic/unhelped/mod.rs +++ b/src/sonic/unhelped/mod.rs @@ -3,4 +3,5 @@ /// s1(X, Y) = ... /// s1 part requires grand product and permutation arguments, that are also implemented -pub mod s2_proof; \ No newline at end of file +pub mod s2_proof; +mod wellformed_argument; \ No newline at end of file diff --git a/src/sonic/unhelped/permutation_argument.rs b/src/sonic/unhelped/permutation_argument.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/sonic/unhelped/wellformed_argument.rs b/src/sonic/unhelped/wellformed_argument.rs new file mode 100644 index 0000000..e0ede80 --- /dev/null +++ b/src/sonic/unhelped/wellformed_argument.rs @@ -0,0 +1,159 @@ +/// Wellformedness argument allows to verify that some committment was to multivariate polynomial of degree n, +/// with no constant term and negative powers + +use ff::{Field, PrimeField, PrimeFieldRepr}; +use pairing::{Engine, CurveProjective, CurveAffine}; +use std::marker::PhantomData; + +use crate::sonic::srs::SRS; +use crate::sonic::util::*; + +#[derive(Clone)] +pub struct WellformednessArgument { + polynomials: Vec> +} + +#[derive(Clone)] +pub struct WellformednessProof { + commitments: Vec, + challenges: Vec, + l: E::G1Affine, + r: E::G1Affine +} + +impl WellformednessArgument { + pub fn new(polynomials: Vec>) -> Self { + assert!(polynomials.len() > 0); + + let length = polynomials[0].len(); + for p in polynomials.iter() { + assert!(p.len() == length); + } + + WellformednessArgument { + polynomials: polynomials + } + } + + // Make a commitment to polynomial in a form \sum_{i=1}^{N} a_{i} X^{i} Y^{i} + pub fn commit(&self, srs: &SRS) -> Vec { + + let mut results = vec![]; + + let n = self.polynomials[0].len(); + + for p in self.polynomials.iter() { + let c = multiexp( + srs.g_positive_x_alpha[0..n].iter(), + p.iter() + ).into_affine(); + + results.push(c); + } + + results + } + + pub fn make_argument(self, challenges: Vec, srs: &SRS) -> WellformednessProof { + let commitments = self.commit(&srs); + + assert_eq!(commitments.len(), challenges.len()); + + let mut polynomials = self.polynomials; + let mut challenges = challenges; + + + + let mut p0 = polynomials.pop().unwrap(); + let r0 = challenges.pop().unwrap(); + let n = p0.len(); + mul_polynomial_by_scalar(&mut p0[..], r0); + + let m = polynomials.len(); + + for _ in 0..m { + let p = polynomials.pop().unwrap(); + let r = challenges.pop().unwrap(); + mul_add_polynomials(&mut p0[..], & p[..], r); + } + + let d = srs.d; + + // here the multiplier is x^-d, so largest negative power is -d + 1 + let l = polynomial_commitment::( + n, + d - 1, + 0, + &srs, + p0.iter()); + + // here the multiplier is x^d-n, so largest positive power is d + + let r = polynomial_commitment::( + n, + 0, + d, + &srs, + p0.iter()); + + WellformednessProof { + commitments: commitments, + challenges: challenges, + l: l, + r: r + } + + } + + + // pub fn verify(challenges: Vec, proof: &WellformednessProof, srs: &SRS) -> bool { + + // // e(C,hαx)e(C−yz,hα) = e(O,h)e(g−c,hα) + + // let alpha_x_precomp = srs.h_positive_x_alpha[1].prepare(); + // let alpha_precomp = srs.h_positive_x_alpha[0].prepare(); + // let mut h_prep = srs.h_positive_x[0]; + // h_prep.negate(); + // let h_prep = h_prep.prepare(); + + // let mut c_minus_xy = proof.c_value; + // let mut xy = x; + // xy.mul_assign(&y); + + // c_minus_xy.sub_assign(&xy); + + // let mut c_in_c_minus_xy = proof.c_opening.mul(c_minus_xy.into_repr()).into_affine(); + + // let valid = E::final_exponentiation(&E::miller_loop(&[ + // (&proof.c_opening.prepare(), &alpha_x_precomp), + // (&c_in_c_minus_xy.prepare(), &alpha_precomp), + // (&proof.o.prepare(), &h_prep), + // ])).unwrap() == E::Fqk::one(); + + // if !valid { + // return false; + // } + + // // e(D,hαx)e(D−y−1z,hα) = e(O,h)e(g−d,hα) + + // let mut d_minus_x_y_inv = proof.d_value; + // let mut x_y_inv = x; + // x_y_inv.mul_assign(&y.inverse().unwrap()); + + // d_minus_x_y_inv.sub_assign(&x_y_inv); + + // let mut d_in_d_minus_x_y_inv = proof.d_opening.mul(d_minus_x_y_inv.into_repr()).into_affine(); + + // let valid = E::final_exponentiation(&E::miller_loop(&[ + // (&proof.d_opening.prepare(), &alpha_x_precomp), + // (&d_in_d_minus_x_y_inv.prepare(), &alpha_precomp), + // (&proof.o.prepare(), &h_prep), + // ])).unwrap() == E::Fqk::one(); + + // if !valid { + // return false; + // } + + // true + // } +} \ No newline at end of file diff --git a/src/sonic/util.rs b/src/sonic/util.rs index 8aad10c..2f66ee9 100644 --- a/src/sonic/util.rs +++ b/src/sonic/util.rs @@ -573,6 +573,67 @@ pub fn multiply_polynomials_serial(mut a: Vec, mut b: Vec(a: &mut [F], b: &[F]) { + use crate::multicore::Worker; + use crate::domain::{EvaluationDomain, Scalar}; + + let worker = Worker::new(); + + assert_eq!(a.len(), b.len()); + + worker.scope(a.len(), |scope, chunk| { + for (a, b) in a.chunks_mut(chunk).zip(b.chunks(chunk)) + { + scope.spawn(move |_| { + for (a, b) in a.iter_mut().zip(b.iter()) { + a.add_assign(b); + } + }); + } + }); +} + +pub fn mul_polynomial_by_scalar(a: &mut [F], b: F) { + use crate::multicore::Worker; + use crate::domain::{EvaluationDomain, Scalar}; + + let worker = Worker::new(); + + worker.scope(a.len(), |scope, chunk| { + for a in a.chunks_mut(chunk) + { + scope.spawn(move |_| { + for a in a.iter_mut() { + a.mul_assign(&b); + } + }); + } + }); +} + +pub fn mul_add_polynomials(a: &mut [F], b: &[F], c: F) { + use crate::multicore::Worker; + use crate::domain::{EvaluationDomain, Scalar}; + + let worker = Worker::new(); + + assert_eq!(a.len(), b.len()); + + worker.scope(a.len(), |scope, chunk| { + for (a, b) in a.chunks_mut(chunk).zip(b.chunks(chunk)) + { + scope.spawn(move |_| { + for (a, b) in a.iter_mut().zip(b.iter()) { + let mut r = *b; + r.mul_assign(&c); + + a.add_assign(&r); + } + }); + } + }); +} + fn serial_fft(a: &mut [E::Fr], omega: &E::Fr, log_n: u32) { fn bitreverse(mut n: u32, l: u32) -> u32 { let mut r = 0; From 08f1384c7c102cda62e48648c45bece0050cbc47 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Thu, 21 Feb 2019 20:42:56 -0500 Subject: [PATCH 29/42] finish wellformedness argument, can now test --- src/sonic/unhelped/wellformed_argument.rs | 99 +++++++++-------------- src/sonic/util.rs | 5 +- 2 files changed, 43 insertions(+), 61 deletions(-) diff --git a/src/sonic/unhelped/wellformed_argument.rs b/src/sonic/unhelped/wellformed_argument.rs index e0ede80..f9510f2 100644 --- a/src/sonic/unhelped/wellformed_argument.rs +++ b/src/sonic/unhelped/wellformed_argument.rs @@ -16,7 +16,6 @@ pub struct WellformednessArgument { #[derive(Clone)] pub struct WellformednessProof { commitments: Vec, - challenges: Vec, l: E::G1Affine, r: E::G1Affine } @@ -79,81 +78,63 @@ impl WellformednessArgument { let d = srs.d; - // here the multiplier is x^-d, so largest negative power is -d + 1 - let l = polynomial_commitment::( - n, - d - 1, - 0, - &srs, - p0.iter()); + assert!(n < d); - // here the multiplier is x^d-n, so largest positive power is d + // here the multiplier is x^-d, so largest negative power is -(d - 1), smallest negative power is -(d - n) + let l = multiexp( + srs.g_negative_x[(d - n)..(d - 1)].iter().rev(), + p0.iter() + ).into_affine(); - let r = polynomial_commitment::( - n, - 0, - d, - &srs, - p0.iter()); + // here the multiplier is x^d-n, so largest positive power is d, smallest positive power is d - n + 1 + + let r = multiexp( + srs.g_positive_x[(d - n + 1)..].iter().rev(), + p0.iter() + ).into_affine(); WellformednessProof { commitments: commitments, - challenges: challenges, l: l, r: r } } + pub fn verify(n: usize, challenges: Vec, proof: &WellformednessProof, srs: &SRS) -> bool { + let d = srs.d; - // pub fn verify(challenges: Vec, proof: &WellformednessProof, srs: &SRS) -> bool { + let alpha_x_d_precomp = srs.h_positive_x_alpha[d].prepare(); + let alpha_x_n_minus_d_precomp = srs.h_negative_x_alpha[d - n].prepare(); + let mut h_prep = srs.h_positive_x[0]; + h_prep.negate(); + let h_prep = h_prep.prepare(); - // // e(C,hαx)e(C−yz,hα) = e(O,h)e(g−c,hα) + let a = multiexp( + proof.commitments.iter(), + challenges.iter(), + ).into_affine(); - // let alpha_x_precomp = srs.h_positive_x_alpha[1].prepare(); - // let alpha_precomp = srs.h_positive_x_alpha[0].prepare(); - // let mut h_prep = srs.h_positive_x[0]; - // h_prep.negate(); - // let h_prep = h_prep.prepare(); + let a = a.prepare(); - // let mut c_minus_xy = proof.c_value; - // let mut xy = x; - // xy.mul_assign(&y); + let valid = E::final_exponentiation(&E::miller_loop(&[ + (&a, &h_prep), + (&proof.l.prepare(), &alpha_x_d_precomp) + ])).unwrap() == E::Fqk::one(); - // c_minus_xy.sub_assign(&xy); + if !valid { + return false; + } - // let mut c_in_c_minus_xy = proof.c_opening.mul(c_minus_xy.into_repr()).into_affine(); + let valid = E::final_exponentiation(&E::miller_loop(&[ + (&a, &h_prep), + (&proof.r.prepare(), &alpha_x_n_minus_d_precomp) + ])).unwrap() == E::Fqk::one(); - // let valid = E::final_exponentiation(&E::miller_loop(&[ - // (&proof.c_opening.prepare(), &alpha_x_precomp), - // (&c_in_c_minus_xy.prepare(), &alpha_precomp), - // (&proof.o.prepare(), &h_prep), - // ])).unwrap() == E::Fqk::one(); + if !valid { + return false; + } - // if !valid { - // return false; - // } - - // // e(D,hαx)e(D−y−1z,hα) = e(O,h)e(g−d,hα) - - // let mut d_minus_x_y_inv = proof.d_value; - // let mut x_y_inv = x; - // x_y_inv.mul_assign(&y.inverse().unwrap()); - - // d_minus_x_y_inv.sub_assign(&x_y_inv); - - // let mut d_in_d_minus_x_y_inv = proof.d_opening.mul(d_minus_x_y_inv.into_repr()).into_affine(); - - // let valid = E::final_exponentiation(&E::miller_loop(&[ - // (&proof.d_opening.prepare(), &alpha_x_precomp), - // (&d_in_d_minus_x_y_inv.prepare(), &alpha_precomp), - // (&proof.o.prepare(), &h_prep), - // ])).unwrap() == E::Fqk::one(); - - // if !valid { - // return false; - // } - - // true - // } + true + } } \ No newline at end of file diff --git a/src/sonic/util.rs b/src/sonic/util.rs index 2f66ee9..9e9f6b1 100644 --- a/src/sonic/util.rs +++ b/src/sonic/util.rs @@ -90,6 +90,7 @@ pub fn polynomial_commitment< // of we should use part of the negative powers let d = srs.d; assert!(max >= largest_positive_power); + // use both positive and negative powers for commitment if d < max + largest_negative_power + 1 { let min_power = largest_negative_power + max - d; let max_power = d + largest_positive_power - max; @@ -101,8 +102,8 @@ pub fn polynomial_commitment< ).into_affine(); } else { return multiexp( - srs.g_positive_x_alpha[(srs.d - max - largest_negative_power - 1)..].iter(), - s + srs.g_positive_x_alpha[(srs.d - max - largest_negative_power - 1)..].iter(), + s ).into_affine(); } } From b5e024162dac953f966aa809c2797ab8478aace3 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Sat, 23 Feb 2019 16:02:38 -0500 Subject: [PATCH 30/42] implement tests for well formed argument --- src/sonic/README.md | 4 +- src/sonic/unhelped/s2_proof.rs | 2 +- src/sonic/unhelped/wellformed_argument.rs | 69 +++++++++++++++++++---- 3 files changed, 60 insertions(+), 15 deletions(-) diff --git a/src/sonic/README.md b/src/sonic/README.md index 3aa27d3..5785310 100644 --- a/src/sonic/README.md +++ b/src/sonic/README.md @@ -15,8 +15,8 @@ Initial SONIC proof system integration using the code from the [original impleme - [x] Test with public inputs - [x] Test on BN256 - [x] Parallelize using existing primitives -- [ ] Implement polynomial parallelized evaluation +- [x] Implement polynomial parallelized evaluation - [x] Make custom transcriptor that is easy to transform into the smart-contract - [x] Basic Ethereum smart-contract -- [ ] Add blinding factors +- [x] Add blinding factors - [ ] Implement unhelped version \ No newline at end of file diff --git a/src/sonic/unhelped/s2_proof.rs b/src/sonic/unhelped/s2_proof.rs index fb2409e..a652d38 100644 --- a/src/sonic/unhelped/s2_proof.rs +++ b/src/sonic/unhelped/s2_proof.rs @@ -59,7 +59,7 @@ impl S2Eval { let (d, d_opening) = { let mut point = y.inverse().unwrap(); point.mul_assign(&x); - let val = evaluate_at_consequitive_powers(&poly[1..], E::Fr::one(), point); + let val = evaluate_at_consequitive_powers(&poly[1..], E::Fr::one(), point); poly[0] = val; poly[0].negate(); let opening = polynomial_commitment_opening(0, self.n, poly.iter(), point, &srs); diff --git a/src/sonic/unhelped/wellformed_argument.rs b/src/sonic/unhelped/wellformed_argument.rs index f9510f2..8c3b652 100644 --- a/src/sonic/unhelped/wellformed_argument.rs +++ b/src/sonic/unhelped/wellformed_argument.rs @@ -15,7 +15,6 @@ pub struct WellformednessArgument { #[derive(Clone)] pub struct WellformednessProof { - commitments: Vec, l: E::G1Affine, r: E::G1Affine } @@ -54,15 +53,9 @@ impl WellformednessArgument { } pub fn make_argument(self, challenges: Vec, srs: &SRS) -> WellformednessProof { - let commitments = self.commit(&srs); - - assert_eq!(commitments.len(), challenges.len()); - let mut polynomials = self.polynomials; let mut challenges = challenges; - - let mut p0 = polynomials.pop().unwrap(); let r0 = challenges.pop().unwrap(); let n = p0.len(); @@ -82,7 +75,7 @@ impl WellformednessArgument { // here the multiplier is x^-d, so largest negative power is -(d - 1), smallest negative power is -(d - n) let l = multiexp( - srs.g_negative_x[(d - n)..(d - 1)].iter().rev(), + srs.g_negative_x[(d - n)..d].iter().rev(), p0.iter() ).into_affine(); @@ -94,14 +87,12 @@ impl WellformednessArgument { ).into_affine(); WellformednessProof { - commitments: commitments, l: l, r: r } - } - pub fn verify(n: usize, challenges: Vec, proof: &WellformednessProof, srs: &SRS) -> bool { + pub fn verify(n: usize, challenges: &Vec, commitments: &Vec, proof: &WellformednessProof, srs: &SRS) -> bool { let d = srs.d; let alpha_x_d_precomp = srs.h_positive_x_alpha[d].prepare(); @@ -111,7 +102,7 @@ impl WellformednessArgument { let h_prep = h_prep.prepare(); let a = multiexp( - proof.commitments.iter(), + commitments.iter(), challenges.iter(), ).into_affine(); @@ -137,4 +128,58 @@ impl WellformednessArgument { true } +} + +#[test] +fn test_argument() { + use pairing::bls12_381::{Fr, G1Affine, G1, Bls12}; + use rand::{XorShiftRng, SeedableRng, Rand, Rng}; + use crate::sonic::srs::SRS; + + let srs_x = Fr::from_str("23923").unwrap(); + let srs_alpha = Fr::from_str("23728792").unwrap(); + let srs = SRS::::dummy(830564, srs_x, srs_alpha); + + let n: usize = 1 << 16; + let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::>(); + + let argument = WellformednessArgument::new(vec![coeffs]); + let challenges = (0..1).map(|_| Fr::rand(rng)).collect::>(); + + let commitments = argument.commit(&srs); + + let proof = argument.make_argument(challenges.clone(), &srs); + + let valid = WellformednessArgument::verify(n, &challenges, &commitments, &proof, &srs); + + assert!(valid); +} + +#[test] +fn test_argument_soundness() { + use pairing::bls12_381::{Fr, G1Affine, G1, Bls12}; + use rand::{XorShiftRng, SeedableRng, Rand, Rng}; + use crate::sonic::srs::SRS; + + let srs_x = Fr::from_str("23923").unwrap(); + let srs_alpha = Fr::from_str("23728792").unwrap(); + let srs = SRS::::dummy(830564, srs_x, srs_alpha); + + let n: usize = 1 << 16; + let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::>(); + + let argument = WellformednessArgument::new(vec![coeffs]); + let commitments = argument.commit(&srs); + + let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::>(); + let argument = WellformednessArgument::new(vec![coeffs]); + let challenges = (0..1).map(|_| Fr::rand(rng)).collect::>(); + + let proof = argument.make_argument(challenges.clone(), &srs); + + let valid = WellformednessArgument::verify(n, &challenges, &commitments, &proof, &srs); + + assert!(!valid); } \ No newline at end of file From cbf95acb11a7cf46b69fcc1a9a7d2e8421579a7e Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Sat, 23 Feb 2019 16:04:31 -0500 Subject: [PATCH 31/42] update a progress --- src/sonic/README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/sonic/README.md b/src/sonic/README.md index 5785310..f38d191 100644 --- a/src/sonic/README.md +++ b/src/sonic/README.md @@ -19,4 +19,9 @@ Initial SONIC proof system integration using the code from the [original impleme - [x] Make custom transcriptor that is easy to transform into the smart-contract - [x] Basic Ethereum smart-contract - [x] Add blinding factors -- [ ] Implement unhelped version \ No newline at end of file +- [ ] Implement unhelped version + - [x] Implement a part of S poly precomputation (S2) + - [x] Implement a "well formed" argument + - [ ] Implement a coefficients product argument + - [ ] Implement a premutation argument + - [ ] Implement synthesizer for proper form of S polynomial \ No newline at end of file From be62b361edc27e121486c0a0fb0778fabd39608a Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Sat, 23 Feb 2019 16:46:34 -0500 Subject: [PATCH 32/42] start preparing grand product argument --- .../unhelped/coefficient_product_argument.rs | 229 +++++++++++++++++- src/sonic/unhelped/mod.rs | 7 +- 2 files changed, 233 insertions(+), 3 deletions(-) diff --git a/src/sonic/unhelped/coefficient_product_argument.rs b/src/sonic/unhelped/coefficient_product_argument.rs index 7e4beb9..c42c1cb 100644 --- a/src/sonic/unhelped/coefficient_product_argument.rs +++ b/src/sonic/unhelped/coefficient_product_argument.rs @@ -1,3 +1,230 @@ /// One must prove that for commitments to two polynomials of degree n products of the coefficients -/// in those two polynomials are equal (part of the permutation argument) +/// in those two polynomials are equal (part of the permutation argument) with additional assumption that +/// those coefficients are never equal to zero + +use ff::{Field, PrimeField, PrimeFieldRepr}; +use pairing::{Engine, CurveProjective, CurveAffine}; +use std::marker::PhantomData; + +use crate::sonic::srs::SRS; +use crate::sonic::util::*; + +#[derive(Clone)] +pub struct GrandProductArgument { + c_polynomials: Vec>, + v_elements: Vec +} + +#[derive(Clone)] +pub struct GrandProductProof { + l: E::G1Affine, + r: E::G1Affine +} + +impl GrandProductArgument { + pub fn new(polynomials: Vec<(Vec, Vec)>) -> Self { + assert!(polynomials.len() > 0); + + let length = polynomials[0].0.len(); + let mut c_polynomials = vec![]; + let mut v_elements = vec![]; + + // a_{1..n} = first poly + // a_{n+1..2n+1} = b_{1..n} = second poly + + // c_1 = a_1 + // c_2 = a_2 * c_1 = a_2 * a_1 + // c_3 = a_3 * c_2 = a_3 * a_2 * a_1 + // ... + // c_n = a_n * c_{n-1} = \prod a_i + // a_{n+1} = c_{n-1}^-1 + // c_{n+1} = 1 + // c_{n+1} = a_{n+2} * c_{n+1} = a_{n+2} + // ... + // c_{2n+1} = \prod a_{n+1+i} = \prod b_{i} + // v = c_{n}^-1 + + // calculate c, serially for now + + for p in polynomials.iter() { + assert!(p.0.len() == p.1.len()); + assert!(p.0.len() == length); + let mut c_poly: Vec = Vec::with_capacity(2*length + 1); + let mut c_coeff = E::Fr::one(); + // add a + for a in p.0.iter() { + c_coeff.mul_assign(a); + c_poly.push(c_coeff); + } + let v = c_coeff.inverse().unwrap(); + // add c_{n+1} + let mut c_coeff = E::Fr::one(); + c_poly.push(c_coeff); + // add b + for b in p.1.iter() { + c_coeff.mul_assign(b); + c_poly.push(c_coeff); + } + + assert_eq!(c_poly.len(), 2*length + 1); + c_polynomials.push(c_poly); + v_elements.push(v); + } + + GrandProductArgument { + c_polynomials: c_polynomials, + v_elements: v_elements + } + } + + // // Make a commitment to polynomial in a form \sum_{i=1}^{N} a_{i} X^{i} Y^{i} + // pub fn commit(&self, srs: &SRS) -> Vec { + + // let mut results = vec![]; + + // let n = self.polynomials[0].len(); + + // for p in self.polynomials.iter() { + // let c = multiexp( + // srs.g_positive_x_alpha[0..n].iter(), + // p.iter() + // ).into_affine(); + + // results.push(c); + // } + + // results + // } + + // pub fn make_argument(self, challenges: Vec, srs: &SRS) -> WellformednessProof { + // let mut polynomials = self.polynomials; + // let mut challenges = challenges; + + // let mut p0 = polynomials.pop().unwrap(); + // let r0 = challenges.pop().unwrap(); + // let n = p0.len(); + // mul_polynomial_by_scalar(&mut p0[..], r0); + + // let m = polynomials.len(); + + // for _ in 0..m { + // let p = polynomials.pop().unwrap(); + // let r = challenges.pop().unwrap(); + // mul_add_polynomials(&mut p0[..], & p[..], r); + // } + + // let d = srs.d; + + // assert!(n < d); + + // // here the multiplier is x^-d, so largest negative power is -(d - 1), smallest negative power is -(d - n) + // let l = multiexp( + // srs.g_negative_x[(d - n)..d].iter().rev(), + // p0.iter() + // ).into_affine(); + + // // here the multiplier is x^d-n, so largest positive power is d, smallest positive power is d - n + 1 + + // let r = multiexp( + // srs.g_positive_x[(d - n + 1)..].iter().rev(), + // p0.iter() + // ).into_affine(); + + // WellformednessProof { + // l: l, + // r: r + // } + // } + + // pub fn verify(n: usize, challenges: &Vec, commitments: &Vec, proof: &WellformednessProof, srs: &SRS) -> bool { + // let d = srs.d; + + // let alpha_x_d_precomp = srs.h_positive_x_alpha[d].prepare(); + // let alpha_x_n_minus_d_precomp = srs.h_negative_x_alpha[d - n].prepare(); + // let mut h_prep = srs.h_positive_x[0]; + // h_prep.negate(); + // let h_prep = h_prep.prepare(); + + // let a = multiexp( + // commitments.iter(), + // challenges.iter(), + // ).into_affine(); + + // let a = a.prepare(); + + // let valid = E::final_exponentiation(&E::miller_loop(&[ + // (&a, &h_prep), + // (&proof.l.prepare(), &alpha_x_d_precomp) + // ])).unwrap() == E::Fqk::one(); + + // if !valid { + // return false; + // } + + // let valid = E::final_exponentiation(&E::miller_loop(&[ + // (&a, &h_prep), + // (&proof.r.prepare(), &alpha_x_n_minus_d_precomp) + // ])).unwrap() == E::Fqk::one(); + + // if !valid { + // return false; + // } + + // true + // } +} + +// #[test] +// fn test_argument() { +// use pairing::bls12_381::{Fr, G1Affine, G1, Bls12}; +// use rand::{XorShiftRng, SeedableRng, Rand, Rng}; +// use crate::sonic::srs::SRS; + +// let srs_x = Fr::from_str("23923").unwrap(); +// let srs_alpha = Fr::from_str("23728792").unwrap(); +// let srs = SRS::::dummy(830564, srs_x, srs_alpha); + +// let n: usize = 1 << 16; +// let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); +// let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::>(); + +// let argument = WellformednessArgument::new(vec![coeffs]); +// let challenges = (0..1).map(|_| Fr::rand(rng)).collect::>(); + +// let commitments = argument.commit(&srs); + +// let proof = argument.make_argument(challenges.clone(), &srs); + +// let valid = WellformednessArgument::verify(n, &challenges, &commitments, &proof, &srs); + +// assert!(valid); +// } + +// #[test] +// fn test_argument_soundness() { +// use pairing::bls12_381::{Fr, G1Affine, G1, Bls12}; +// use rand::{XorShiftRng, SeedableRng, Rand, Rng}; +// use crate::sonic::srs::SRS; + +// let srs_x = Fr::from_str("23923").unwrap(); +// let srs_alpha = Fr::from_str("23728792").unwrap(); +// let srs = SRS::::dummy(830564, srs_x, srs_alpha); + +// let n: usize = 1 << 16; +// let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); +// let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::>(); + +// let argument = WellformednessArgument::new(vec![coeffs]); +// let commitments = argument.commit(&srs); + +// let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::>(); +// let argument = WellformednessArgument::new(vec![coeffs]); +// let challenges = (0..1).map(|_| Fr::rand(rng)).collect::>(); + +// let proof = argument.make_argument(challenges.clone(), &srs); + +// let valid = WellformednessArgument::verify(n, &challenges, &commitments, &proof, &srs); + +// assert!(!valid); +// } diff --git a/src/sonic/unhelped/mod.rs b/src/sonic/unhelped/mod.rs index 54a280f..0da1073 100644 --- a/src/sonic/unhelped/mod.rs +++ b/src/sonic/unhelped/mod.rs @@ -3,5 +3,8 @@ /// s1(X, Y) = ... /// s1 part requires grand product and permutation arguments, that are also implemented -pub mod s2_proof; -mod wellformed_argument; \ No newline at end of file +mod s2_proof; +mod wellformed_argument; +mod coefficient_product_argument; + +pub use self::wellformed_argument::{WellformednessArgument, WellformednessProof}; \ No newline at end of file From 5fdb9cf3536f8f85f51e45ea3c99cd4c738dfa4d Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Mon, 25 Feb 2019 20:57:43 +0300 Subject: [PATCH 33/42] first step of the grand product argument --- ..._argument.rs => grand_product_argument.rs} | 174 ++++++++++++------ src/sonic/unhelped/mod.rs | 2 +- src/sonic/unhelped/s2_proof.rs | 4 +- src/sonic/unhelped/wellformed_argument.rs | 1 - 4 files changed, 123 insertions(+), 58 deletions(-) rename src/sonic/unhelped/{coefficient_product_argument.rs => grand_product_argument.rs} (53%) diff --git a/src/sonic/unhelped/coefficient_product_argument.rs b/src/sonic/unhelped/grand_product_argument.rs similarity index 53% rename from src/sonic/unhelped/coefficient_product_argument.rs rename to src/sonic/unhelped/grand_product_argument.rs index c42c1cb..9a69c0b 100644 --- a/src/sonic/unhelped/coefficient_product_argument.rs +++ b/src/sonic/unhelped/grand_product_argument.rs @@ -11,8 +11,10 @@ use crate::sonic::util::*; #[derive(Clone)] pub struct GrandProductArgument { + a_polynomials: Vec>, c_polynomials: Vec>, - v_elements: Vec + v_elements: Vec, + n: usize } #[derive(Clone)] @@ -25,7 +27,8 @@ impl GrandProductArgument { pub fn new(polynomials: Vec<(Vec, Vec)>) -> Self { assert!(polynomials.len() > 0); - let length = polynomials[0].0.len(); + let n = polynomials[0].0.len(); + let mut a_polynomials = vec![]; let mut c_polynomials = vec![]; let mut v_elements = vec![]; @@ -46,95 +49,158 @@ impl GrandProductArgument { // calculate c, serially for now - for p in polynomials.iter() { - assert!(p.0.len() == p.1.len()); - assert!(p.0.len() == length); - let mut c_poly: Vec = Vec::with_capacity(2*length + 1); + for p in polynomials.into_iter() { + let (p0, p1) = p; + assert!(p0.len() == p1.len()); + assert!(p0.len() == n); + let mut c_poly: Vec = Vec::with_capacity(2*n + 1); + let mut a_poly: Vec = Vec::with_capacity(2*n + 1); let mut c_coeff = E::Fr::one(); // add a - for a in p.0.iter() { + for a in p0.iter() { c_coeff.mul_assign(a); c_poly.push(c_coeff); } + assert_eq!(c_poly.len(), n); + a_poly.extend(p0); + a_poly.push(c_poly[n - 2].inverse().unwrap()); + // a_{n+1} = c_{n-1}^-1 let v = c_coeff.inverse().unwrap(); // add c_{n+1} let mut c_coeff = E::Fr::one(); c_poly.push(c_coeff); // add b - for b in p.1.iter() { + for b in p1.iter() { c_coeff.mul_assign(b); c_poly.push(c_coeff); } - - assert_eq!(c_poly.len(), 2*length + 1); + assert_eq!(c_poly.len(), 2*n + 1); + a_poly.extend(p1); + a_polynomials.push(a_poly); c_polynomials.push(c_poly); v_elements.push(v); } GrandProductArgument { + a_polynomials: a_polynomials, c_polynomials: c_polynomials, - v_elements: v_elements + v_elements: v_elements, + n: n } } - // // Make a commitment to polynomial in a form \sum_{i=1}^{N} a_{i} X^{i} Y^{i} - // pub fn commit(&self, srs: &SRS) -> Vec { + // Make a commitment for the begining of the protocol, returns commitment and `v` scalar + pub fn commit(&self, srs: &SRS) -> Vec<(E::G1Affine, E::Fr)> { - // let mut results = vec![]; + let mut results = vec![]; - // let n = self.polynomials[0].len(); + let n = self.c_polynomials[0].len(); - // for p in self.polynomials.iter() { - // let c = multiexp( - // srs.g_positive_x_alpha[0..n].iter(), - // p.iter() - // ).into_affine(); + for (p, v) in self.c_polynomials.iter().zip(self.v_elements.iter()) { + let c = multiexp( + srs.g_positive_x_alpha[0..n].iter(), + p.iter() + ).into_affine(); - // results.push(c); - // } + results.push((c, *v)); + } - // results - // } + results + } - // pub fn make_argument(self, challenges: Vec, srs: &SRS) -> WellformednessProof { - // let mut polynomials = self.polynomials; - // let mut challenges = challenges; + // Argument is based on an approach of main SONIC construction, but with a custom S(X,Y) polynomial of a simple form + pub fn evaluate_t_polynomial(&self, challenges: Vec, y: E::Fr, srs: &SRS) -> E::G1Affine { + assert_eq!(challenges.len(), self.a_polynomials.len()); - // let mut p0 = polynomials.pop().unwrap(); - // let r0 = challenges.pop().unwrap(); - // let n = p0.len(); - // mul_polynomial_by_scalar(&mut p0[..], r0); + let n = self.n; - // let m = polynomials.len(); + let mut t_polynomial: Option> = None; - // for _ in 0..m { - // let p = polynomials.pop().unwrap(); - // let r = challenges.pop().unwrap(); - // mul_add_polynomials(&mut p0[..], & p[..], r); - // } + for (((a, c), v), challenge) in self.a_polynomials.iter() + .zip(self.c_polynomials.iter()) + .zip(self.v_elements.iter()) + .zip(challenges.into_iter()) + { + let mut a_xy = a.clone(); + let mut c_xy = c.clone(); + let v = *v; - // let d = srs.d; + assert_eq!(a_xy.len(), 2*n + 1); - // assert!(n < d); + // make a T polynomial - // // here the multiplier is x^-d, so largest negative power is -(d - 1), smallest negative power is -(d - n) - // let l = multiexp( - // srs.g_negative_x[(d - n)..d].iter().rev(), - // p0.iter() - // ).into_affine(); + let r: Vec = { + // p_a(X,Y)*Y + let mut tmp = y; + tmp.square(); + mut_distribute_consequitive_powers(&mut a_xy[..], tmp, y); - // // here the multiplier is x^d-n, so largest positive power is d, smallest positive power is d - n + 1 + // add extra terms + //v*(XY)^{n+1}*Y + X^{n+2} + X^{n+1}Y − X^{2n+2}*Y - // let r = multiexp( - // srs.g_positive_x[(d - n + 1)..].iter().rev(), - // p0.iter() - // ).into_affine(); + // n+1 term v*(XY)^{n+1}*Y + X^{n+1}Y + let tmp = y.pow(&[(n+2) as u64]); + let mut x_n_plus_one_term = v; + x_n_plus_one_term.mul_assign(&tmp); + x_n_plus_one_term.add_assign(&y); + a_xy[n].add_assign(&x_n_plus_one_term); - // WellformednessProof { - // l: l, - // r: r - // } - // } + // n+2 term + a_xy[n+1].add_assign(&E::Fr::one()); + + // 2n+2 term + let mut tmp = y; + tmp.negate(); + a_xy.push(tmp); + + assert_eq!(a_xy.len(), 2*n + 2); + + let mut r = vec![E::Fr::zero(); 2*n+3]; + r.extend(a_xy); + + r + }; + + // calculate product of the full term made of `a` poly with c(X^{-1}, 1) + X^-1 + let r_prime: Vec = { + let mut c_prime: Vec = c_xy.iter().rev().map(|el| *el).collect(); + c_prime.push(E::Fr::one()); + c_prime.push(E::Fr::zero()); + + c_prime + }; + + let mut t: Vec = multiply_polynomials::(r, r_prime); + + let mut val = { + let mut tmp = y; + tmp.square(); + evaluate_at_consequitive_powers(&c_xy, tmp, y) + }; + + val.add_assign(&E::Fr::one()); + + t[2*n+2].sub_assign(&val); + if t_polynomial.is_some() { + if let Some(t_poly) = t_polynomial.as_mut() { + mul_add_polynomials(&mut t_poly[..], &t, challenge); + } + } else { + mul_polynomial_by_scalar(&mut t, challenge); + t_polynomial = Some(t); + } + } + + let t_polynomial = t_polynomial.unwrap(); + + polynomial_commitment( + srs.d, + 2*n + 2, + 2*n + 2, + srs, + t_polynomial.iter() + ) + } // pub fn verify(n: usize, challenges: &Vec, commitments: &Vec, proof: &WellformednessProof, srs: &SRS) -> bool { // let d = srs.d; diff --git a/src/sonic/unhelped/mod.rs b/src/sonic/unhelped/mod.rs index 0da1073..763e90c 100644 --- a/src/sonic/unhelped/mod.rs +++ b/src/sonic/unhelped/mod.rs @@ -5,6 +5,6 @@ mod s2_proof; mod wellformed_argument; -mod coefficient_product_argument; +mod grand_product_argument; pub use self::wellformed_argument::{WellformednessArgument, WellformednessProof}; \ No newline at end of file diff --git a/src/sonic/unhelped/s2_proof.rs b/src/sonic/unhelped/s2_proof.rs index a652d38..72f9eb7 100644 --- a/src/sonic/unhelped/s2_proof.rs +++ b/src/sonic/unhelped/s2_proof.rs @@ -93,7 +93,7 @@ impl S2Eval { c_minus_xy.sub_assign(&xy); - let mut c_in_c_minus_xy = proof.c_opening.mul(c_minus_xy.into_repr()).into_affine(); + let c_in_c_minus_xy = proof.c_opening.mul(c_minus_xy.into_repr()).into_affine(); let valid = E::final_exponentiation(&E::miller_loop(&[ (&proof.c_opening.prepare(), &alpha_x_precomp), @@ -113,7 +113,7 @@ impl S2Eval { d_minus_x_y_inv.sub_assign(&x_y_inv); - let mut d_in_d_minus_x_y_inv = proof.d_opening.mul(d_minus_x_y_inv.into_repr()).into_affine(); + let d_in_d_minus_x_y_inv = proof.d_opening.mul(d_minus_x_y_inv.into_repr()).into_affine(); let valid = E::final_exponentiation(&E::miller_loop(&[ (&proof.d_opening.prepare(), &alpha_x_precomp), diff --git a/src/sonic/unhelped/wellformed_argument.rs b/src/sonic/unhelped/wellformed_argument.rs index 8c3b652..4eb38a0 100644 --- a/src/sonic/unhelped/wellformed_argument.rs +++ b/src/sonic/unhelped/wellformed_argument.rs @@ -33,7 +33,6 @@ impl WellformednessArgument { } } - // Make a commitment to polynomial in a form \sum_{i=1}^{N} a_{i} X^{i} Y^{i} pub fn commit(&self, srs: &SRS) -> Vec { let mut results = vec![]; From f8d99df9574bde9b134ab44e22c8232b147c8940 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 26 Feb 2019 16:07:57 +0300 Subject: [PATCH 34/42] first part of the grand product argument is complete --- src/sonic/unhelped/grand_product_argument.rs | 406 ++++++++++++++++--- 1 file changed, 348 insertions(+), 58 deletions(-) diff --git a/src/sonic/unhelped/grand_product_argument.rs b/src/sonic/unhelped/grand_product_argument.rs index 9a69c0b..b132f7e 100644 --- a/src/sonic/unhelped/grand_product_argument.rs +++ b/src/sonic/unhelped/grand_product_argument.rs @@ -14,13 +14,17 @@ pub struct GrandProductArgument { a_polynomials: Vec>, c_polynomials: Vec>, v_elements: Vec, + t_polynomial: Option>, n: usize } #[derive(Clone)] pub struct GrandProductProof { - l: E::G1Affine, - r: E::G1Affine + t_opening: E::G1Affine, + e_zinv: E::Fr, + e_opening: E::G1Affine, + f_y: E::Fr, + f_opening: E::G1Affine, } impl GrandProductArgument { @@ -63,9 +67,10 @@ impl GrandProductArgument { } assert_eq!(c_poly.len(), n); a_poly.extend(p0); - a_poly.push(c_poly[n - 2].inverse().unwrap()); - // a_{n+1} = c_{n-1}^-1 - let v = c_coeff.inverse().unwrap(); + // v = a_{n+1} = c_{n}^-1 + let v = c_poly[n-1].inverse().unwrap(); + a_poly.push(E::Fr::zero()); + // a_poly.push(v); // add c_{n+1} let mut c_coeff = E::Fr::one(); c_poly.push(c_coeff); @@ -76,6 +81,9 @@ impl GrandProductArgument { } assert_eq!(c_poly.len(), 2*n + 1); a_poly.extend(p1); + + assert_eq!(c_poly[n-1], c_poly[2*n]); + a_polynomials.push(a_poly); c_polynomials.push(c_poly); v_elements.push(v); @@ -85,12 +93,67 @@ impl GrandProductArgument { a_polynomials: a_polynomials, c_polynomials: c_polynomials, v_elements: v_elements, + t_polynomial: None, n: n } } + // Make a commitment to a polynomial in a form A*B^{x+1} = [a_1...a_{n}, 0, b_1...b_{n}] + pub fn commit_for_grand_product(a: &[E::Fr], b: &[E::Fr], srs: &SRS) -> E::G1Affine { + assert_eq!(a.len(), b.len()); + + let n = a.len(); + + multiexp( + srs.g_positive_x_alpha[0..(2*n+1)].iter(), + a.iter() + .chain_ext(Some(E::Fr::zero()).iter()) + .chain_ext(b.iter()) + ).into_affine() + } + + pub fn open_commitments_for_grand_product(&self, y: E::Fr, z: E::Fr, srs: &SRS) -> Vec<(E::Fr, E::G1Affine)> { + let n = self.n; + + let mut yz = y; + yz.mul_assign(&z); + + let mut results = vec![]; + + for a_poly in self.a_polynomials.iter() { + let a = & a_poly[0..n]; + let b = & a_poly[(n+1)..]; + assert_eq!(a.len(), n); + assert_eq!(b.len(), n); + let mut val = evaluate_at_consequitive_powers(a, yz, yz); + { + let tmp = yz.pow([(n+2) as u64]); + let v = evaluate_at_consequitive_powers(b, tmp, yz); + val.add_assign(&v); + } + + let mut constant_term = val; + constant_term.negate(); + + let opening = polynomial_commitment_opening( + 0, + 2*n + 1, + Some(constant_term).iter() + .chain_ext(a.iter()) + .chain_ext(Some(E::Fr::zero()).iter()) + .chain_ext(b.iter()), + yz, + &srs); + + results.push((val, opening)); + + } + + results + } + // Make a commitment for the begining of the protocol, returns commitment and `v` scalar - pub fn commit(&self, srs: &SRS) -> Vec<(E::G1Affine, E::Fr)> { + pub fn commit_to_individual_c_polynomials(&self, srs: &SRS) -> Vec<(E::G1Affine, E::Fr)> { let mut results = vec![]; @@ -109,7 +172,7 @@ impl GrandProductArgument { } // Argument is based on an approach of main SONIC construction, but with a custom S(X,Y) polynomial of a simple form - pub fn evaluate_t_polynomial(&self, challenges: Vec, y: E::Fr, srs: &SRS) -> E::G1Affine { + pub fn commit_to_t_polynomial(&mut self, challenges: & Vec, y: E::Fr, srs: &SRS) -> E::G1Affine { assert_eq!(challenges.len(), self.a_polynomials.len()); let n = self.n; @@ -119,13 +182,14 @@ impl GrandProductArgument { for (((a, c), v), challenge) in self.a_polynomials.iter() .zip(self.c_polynomials.iter()) .zip(self.v_elements.iter()) - .zip(challenges.into_iter()) + .zip(challenges.iter()) { let mut a_xy = a.clone(); let mut c_xy = c.clone(); let v = *v; assert_eq!(a_xy.len(), 2*n + 1); + assert_eq!(c_xy.len(), 2*n + 1); // make a T polynomial @@ -151,27 +215,44 @@ impl GrandProductArgument { // 2n+2 term let mut tmp = y; tmp.negate(); + a_xy.push(tmp); assert_eq!(a_xy.len(), 2*n + 2); - let mut r = vec![E::Fr::zero(); 2*n+3]; + let mut r = vec![E::Fr::zero(); 2*n + 3]; r.extend(a_xy); r }; - // calculate product of the full term made of `a` poly with c(X^{-1}, 1) + X^-1 let r_prime: Vec = { let mut c_prime: Vec = c_xy.iter().rev().map(|el| *el).collect(); c_prime.push(E::Fr::one()); c_prime.push(E::Fr::zero()); + assert_eq!(c_prime.len(), 2*n + 3); + c_prime }; + // multiply polynomials with powers [-2n-2, -1] and [1, 2n+2], + // expect result to be [-2n+1, 2n+1] let mut t: Vec = multiply_polynomials::(r, r_prime); + assert_eq!(t.len(), 6*n + 7); + + // drain first powers due to the padding and last element due to requirement of being zero + for (i, el) in t[0..(2*n+3)].iter().enumerate() { + assert_eq!(*el, E::Fr::zero(), "{}", format!("Element {} is non-zero", i)); + } + + t.drain(0..(2*n+3)); + let last = t.pop(); + assert_eq!(last.unwrap(), E::Fr::zero(), "last element should be zero"); + + assert_eq!(t.len(), 4*n + 3); + let mut val = { let mut tmp = y; tmp.square(); @@ -180,28 +261,239 @@ impl GrandProductArgument { val.add_assign(&E::Fr::one()); - t[2*n+2].sub_assign(&val); + // subtract at constant term + assert_eq!(t[2*n+1], val); + + t[2*n+1].sub_assign(&val); + if t_polynomial.is_some() { if let Some(t_poly) = t_polynomial.as_mut() { - mul_add_polynomials(&mut t_poly[..], &t, challenge); + mul_add_polynomials(&mut t_poly[..], &t, *challenge); } } else { - mul_polynomial_by_scalar(&mut t, challenge); + mul_polynomial_by_scalar(&mut t, *challenge); t_polynomial = Some(t); } } let t_polynomial = t_polynomial.unwrap(); - polynomial_commitment( - srs.d, - 2*n + 2, - 2*n + 2, - srs, - t_polynomial.iter() - ) + let c = multiexp(srs.g_negative_x_alpha[0..(2*n+1)].iter().rev() + .chain_ext(srs.g_positive_x_alpha[0..(2*n+1)].iter()), + t_polynomial[0..(2*n+1)].iter() + .chain_ext(t_polynomial[(2*n+2)..].iter())).into_affine(); + + self.t_polynomial = Some(t_polynomial); + + c } + + // Argument is based on an approach of main SONIC construction, but with a custom S(X,Y) polynomial of a simple form + pub fn make_argument(self, a_zy: & Vec, challenges: & Vec, y: E::Fr, z: E::Fr, srs: &SRS) -> GrandProductProof { + assert_eq!(a_zy.len(), self.a_polynomials.len()); + assert_eq!(challenges.len(), self.a_polynomials.len()); + + let n = self.n; + + let mut c_polynomials = self.c_polynomials; + let mut e_polynomial: Option> = None; + let mut f_polynomial: Option> = None; + + let mut yz = y; + yz.mul_assign(&z); + + let z_inv = z.inverse().unwrap(); + + for (((a, c), challenge), v) in a_zy.iter() + .zip(c_polynomials.into_iter()) + .zip(challenges.iter()) + .zip(self.v_elements.iter()) + { + // cj = ((aj + vj(yz)n+1)y + zn+2 + zn+1y − z2n+2y)z−1 + let mut c_zy = yz.pow([(n + 1) as u64]); + c_zy.mul_assign(v); + c_zy.add_assign(a); + c_zy.mul_assign(&y); + + let mut z_n_plus_1 = z.pow([(n + 1) as u64]); + + let mut z_n_plus_2 = z_n_plus_1; + z_n_plus_2.mul_assign(&z); + + let mut z_2n_plus_2 = z_n_plus_1; + z_2n_plus_2.square(); + z_2n_plus_2.mul_assign(&y); + + z_n_plus_1.mul_assign(&y); + + c_zy.add_assign(&z_n_plus_1); + c_zy.add_assign(&z_n_plus_2); + c_zy.sub_assign(&z_2n_plus_2); + + c_zy.mul_assign(&z_inv); + + let mut rc = c_zy; + rc.mul_assign(challenge); + let mut ry = y; + ry.mul_assign(challenge); + + if e_polynomial.is_some() && f_polynomial.is_some() { + if let Some(e_poly) = e_polynomial.as_mut() { + if let Some(f_poly) = f_polynomial.as_mut() { + mul_add_polynomials(&mut e_poly[..], &c, rc); + mul_add_polynomials(&mut f_poly[..], &c, ry); + } + } + } else { + let mut e = c.clone(); + let mut f = c; + mul_polynomial_by_scalar(&mut e, rc); + mul_polynomial_by_scalar(&mut f, ry); + e_polynomial = Some(e); + f_polynomial = Some(f); + } + } + + let e_polynomial = e_polynomial.unwrap(); + let f_polynomial = f_polynomial.unwrap(); + + // evaluate e at z^-1 + + let mut e_val = evaluate_at_consequitive_powers(&e_polynomial, z_inv, z_inv); + e_val.negate(); + + // evaluate f at y + + let mut f_val = evaluate_at_consequitive_powers(&f_polynomial, y, y); + f_val.negate(); + + let e_opening = polynomial_commitment_opening( + 0, + 2*n + 1, + Some(e_val).iter().chain_ext(e_polynomial.iter()), + z_inv, + srs); + + let f_opening = polynomial_commitment_opening( + 0, + 2*n + 1, + Some(f_val).iter().chain_ext(f_polynomial.iter()), + y, + srs); + + e_val.negate(); + f_val.negate(); + + let mut t_poly = self.t_polynomial.unwrap(); + + let t_zy = { + let tmp = z_inv.pow([(2*n+2) as u64]); + evaluate_at_consequitive_powers(&t_poly, tmp, z) + }; + + t_poly[2*n + 2].sub_assign(&t_zy); + + let t_opening = polynomial_commitment_opening( + 2*n + 2, + 2*n + 2, + t_poly.iter(), + z, + srs); + + GrandProductProof { + t_opening: t_opening, + e_zinv: e_val, + e_opening: e_opening, + f_y: f_val, + f_opening: f_opening, + } + } + + + pub fn verify_ab_commitment(n: usize, + randomness: & Vec, + a_commitments: &Vec, + b_commitments: &Vec, + openings: &Vec<(E::Fr, E::G1Affine)>, + y: E::Fr, + z: E::Fr, + srs: &SRS + ) -> bool { + assert_eq!(randomness.len(), a_commitments.len()); + assert_eq!(openings.len(), a_commitments.len()); + assert_eq!(b_commitments.len(), a_commitments.len()); + let d = srs.d; + + // e(Dj,hαx)e(D−yz,hα) = e(Aj,h)e(Bj,hxn+1)e(g−aj ,hα) + + let g = srs.g_positive_x[0]; + + let h_alpha_x_precomp = srs.h_positive_x_alpha[1].prepare(); + + let h_alpha_precomp = srs.h_positive_x_alpha[0].prepare(); + + let mut h_x_n_plus_one_precomp = srs.h_positive_x[n]; + h_x_n_plus_one_precomp.negate(); + let h_x_n_plus_one_precomp = h_x_n_plus_one_precomp.prepare(); + + let mut h_prep = srs.h_positive_x[0]; + h_prep.negate(); + let h_prep = h_prep.prepare(); + + let a = multiexp( + a_commitments.iter(), + randomness.iter(), + ).into_affine(); + + let a = a.prepare(); + + let b = multiexp( + b_commitments.iter(), + randomness.iter(), + ).into_affine(); + + let b = b.prepare(); + + let mut yz_neg = y; + yz_neg.mul_assign(&z); + yz_neg.negate(); + + let mut ops = vec![]; + let mut value = E::Fr::zero(); + + for (el, r) in openings.iter().zip(randomness.iter()) { + let (v, o) = el; + ops.push(o.clone()); + let mut val = *v; + val.mul_assign(&r); + value.add_assign(&val); + } + + let value = g.mul(value.into_repr()).into_affine().prepare(); + + let openings = multiexp( + ops.iter(), + randomness.iter(), + ).into_affine(); + + let openings_zy = openings.mul(yz_neg.into_repr()).into_affine().prepare(); + let openings = openings.prepare(); + + + // e(Dj,hαx)e(D−yz,hα) = e(Aj,h)e(Bj,hxn+1)e(g−aj ,hα) + + E::final_exponentiation(&E::miller_loop(&[ + (&openings, &h_alpha_x_precomp), + (&openings_zy, &h_alpha_precomp), + (&a, &h_prep), + (&b, &h_x_n_plus_one_precomp), + (&value, &h_alpha_precomp) + ])).unwrap() == E::Fqk::one() + } + + + // pub fn verify(n: usize, challenges: &Vec, commitments: &Vec, proof: &WellformednessProof, srs: &SRS) -> bool { // let d = srs.d; @@ -240,57 +532,55 @@ impl GrandProductArgument { // } } -// #[test] -// fn test_argument() { -// use pairing::bls12_381::{Fr, G1Affine, G1, Bls12}; -// use rand::{XorShiftRng, SeedableRng, Rand, Rng}; -// use crate::sonic::srs::SRS; +#[test] +fn test_grand_product_argument() { + use pairing::bls12_381::{Fr, G1Affine, G1, Bls12}; + use rand::{XorShiftRng, SeedableRng, Rand, Rng}; + use crate::sonic::srs::SRS; -// let srs_x = Fr::from_str("23923").unwrap(); -// let srs_alpha = Fr::from_str("23728792").unwrap(); -// let srs = SRS::::dummy(830564, srs_x, srs_alpha); + let srs_x = Fr::from_str("23923").unwrap(); + let srs_alpha = Fr::from_str("23728792").unwrap(); + let srs = SRS::::dummy(830564, srs_x, srs_alpha); -// let n: usize = 1 << 16; -// let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); -// let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::>(); + let n: usize = 1 << 1; + let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + // let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::>(); + let coeffs = vec![Fr::from_str("1").unwrap(), Fr::from_str("2").unwrap()]; + let mut permutation = coeffs.clone(); + rng.shuffle(&mut permutation); -// let argument = WellformednessArgument::new(vec![coeffs]); -// let challenges = (0..1).map(|_| Fr::rand(rng)).collect::>(); + let a_commitment = multiexp(srs.g_positive_x_alpha[0..n].iter(), coeffs.iter()).into_affine(); + let b_commitment = multiexp(srs.g_positive_x_alpha[0..n].iter(), permutation.iter()).into_affine(); -// let commitments = argument.commit(&srs); + let mut argument = GrandProductArgument::new(vec![(coeffs, permutation)]); -// let proof = argument.make_argument(challenges.clone(), &srs); + let commitments_and_v_values = argument.commit_to_individual_c_polynomials(&srs); -// let valid = WellformednessArgument::verify(n, &challenges, &commitments, &proof, &srs); + assert_eq!(commitments_and_v_values.len(), 1); -// assert!(valid); -// } + // let y : Fr = rng.gen(); -// #[test] -// fn test_argument_soundness() { -// use pairing::bls12_381::{Fr, G1Affine, G1, Bls12}; -// use rand::{XorShiftRng, SeedableRng, Rand, Rng}; -// use crate::sonic::srs::SRS; + let y = Fr::one(); -// let srs_x = Fr::from_str("23923").unwrap(); -// let srs_alpha = Fr::from_str("23728792").unwrap(); -// let srs = SRS::::dummy(830564, srs_x, srs_alpha); + let challenges = (0..1).map(|_| Fr::rand(rng)).collect::>(); -// let n: usize = 1 << 16; -// let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); -// let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::>(); + let t_commitment = argument.commit_to_t_polynomial(&challenges, y, &srs); -// let argument = WellformednessArgument::new(vec![coeffs]); -// let commitments = argument.commit(&srs); + let z : Fr = rng.gen(); -// let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::>(); -// let argument = WellformednessArgument::new(vec![coeffs]); -// let challenges = (0..1).map(|_| Fr::rand(rng)).collect::>(); + let grand_product_openings = argument.open_commitments_for_grand_product(y, z, &srs); -// let proof = argument.make_argument(challenges.clone(), &srs); + let randomness = (0..1).map(|_| Fr::rand(rng)).collect::>(); -// let valid = WellformednessArgument::verify(n, &challenges, &commitments, &proof, &srs); + let valid = GrandProductArgument::verify_ab_commitment(n, + &randomness, + &vec![a_commitment], + &vec![b_commitment], + &grand_product_openings, + y, + z, + &srs); -// assert!(!valid); -// } + assert!(valid); +} From b70af73621ed3311d3982132c4ef68353d7b3b19 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 26 Feb 2019 16:58:47 +0300 Subject: [PATCH 35/42] grand product argument is complete --- src/sonic/unhelped/grand_product_argument.rs | 218 +++++++++++++++---- 1 file changed, 171 insertions(+), 47 deletions(-) diff --git a/src/sonic/unhelped/grand_product_argument.rs b/src/sonic/unhelped/grand_product_argument.rs index b132f7e..234edc4 100644 --- a/src/sonic/unhelped/grand_product_argument.rs +++ b/src/sonic/unhelped/grand_product_argument.rs @@ -296,7 +296,7 @@ impl GrandProductArgument { let n = self.n; - let mut c_polynomials = self.c_polynomials; + let c_polynomials = self.c_polynomials; let mut e_polynomial: Option> = None; let mut f_polynomial: Option> = None; @@ -335,6 +335,7 @@ impl GrandProductArgument { let mut rc = c_zy; rc.mul_assign(challenge); + let mut ry = y; ry.mul_assign(challenge); @@ -386,17 +387,19 @@ impl GrandProductArgument { f_val.negate(); let mut t_poly = self.t_polynomial.unwrap(); + assert_eq!(t_poly.len(), 4*n + 3); + // largest negative power of t is -2n-1 let t_zy = { - let tmp = z_inv.pow([(2*n+2) as u64]); + let tmp = z_inv.pow([(2*n+1) as u64]); evaluate_at_consequitive_powers(&t_poly, tmp, z) }; - t_poly[2*n + 2].sub_assign(&t_zy); + t_poly[2*n + 1].sub_assign(&t_zy); let t_opening = polynomial_commitment_opening( - 2*n + 2, - 2*n + 2, + 2*n + 1, + 2*n + 1, t_poly.iter(), z, srs); @@ -410,7 +413,6 @@ impl GrandProductArgument { } } - pub fn verify_ab_commitment(n: usize, randomness: & Vec, a_commitments: &Vec, @@ -492,44 +494,149 @@ impl GrandProductArgument { ])).unwrap() == E::Fqk::one() } + pub fn verify( + n: usize, + randomness: & Vec, + a_zy: & Vec, + challenges: &Vec, + t_commitment: E::G1Affine, + commitments: &Vec<(E::G1Affine, E::Fr)>, + proof: &GrandProductProof, + y: E::Fr, + z: E::Fr, + srs: &SRS + ) -> bool { + assert_eq!(randomness.len(), 3); + assert_eq!(a_zy.len(), challenges.len()); + assert_eq!(commitments.len(), challenges.len()); + + let d = srs.d; + + let g = srs.g_positive_x[0]; + + let h_alpha_x_precomp = srs.h_positive_x_alpha[1].prepare(); + + let h_alpha_precomp = srs.h_positive_x_alpha[0].prepare(); + + let mut h_prep = srs.h_positive_x[0]; + h_prep.negate(); + let h_prep = h_prep.prepare(); + + // first re-calculate cj and t(z,y) + + let mut yz = y; + yz.mul_assign(&z); + + let z_inv = z.inverse().unwrap(); + + let mut t_zy = E::Fr::zero(); + t_zy.add_assign(&proof.e_zinv); + t_zy.sub_assign(&proof.f_y); + + let mut commitments_points = vec![]; + let mut rc_vec = vec![]; + let mut ry_vec = vec![]; + + for ((r, commitment), a) in challenges.iter() + .zip(commitments.iter()) + .zip(a_zy.iter()) { + let (c, v) = commitment; + commitments_points.push(c.clone()); + + // cj = ((aj + vj(yz)n+1)y + zn+2 + zn+1y − z2n+2y)z−1 + let mut c_zy = yz.pow([(n + 1) as u64]); + c_zy.mul_assign(v); + c_zy.add_assign(a); + c_zy.mul_assign(&y); + + let mut z_n_plus_1 = z.pow([(n + 1) as u64]); + + let mut z_n_plus_2 = z_n_plus_1; + z_n_plus_2.mul_assign(&z); + + let mut z_2n_plus_2 = z_n_plus_1; + z_2n_plus_2.square(); + z_2n_plus_2.mul_assign(&y); + + z_n_plus_1.mul_assign(&y); + + c_zy.add_assign(&z_n_plus_1); + c_zy.add_assign(&z_n_plus_2); + c_zy.sub_assign(&z_2n_plus_2); + + c_zy.mul_assign(&z_inv); + + let mut rc = c_zy; + rc.mul_assign(&r); + rc_vec.push(rc); + + let mut ry = y; + ry.mul_assign(&r); + ry_vec.push(ry); + + let mut val = rc; + val.sub_assign(r); + t_zy.add_assign(&val); + } + + let c_rc = multiexp( + commitments_points.iter(), + rc_vec.iter(), + ).into_affine(); + + let c_ry = multiexp( + commitments_points.iter(), + ry_vec.iter(), + ).into_affine(); + + let mut minus_y = y; + minus_y.negate(); + + let mut f_y = proof.f_opening.mul(minus_y.into_repr()); + let g_f = g.mul(proof.f_y.into_repr()); + f_y.add_assign(&g_f); + + let mut minus_z = z; + minus_z.negate(); + + let mut t_z = proof.t_opening.mul(minus_z.into_repr()); + let g_tzy = g.mul(t_zy.into_repr()); + t_z.add_assign(&g_tzy); + + let mut minus_z_inv = z_inv; + minus_z_inv.negate(); + + let mut e_z_inv = proof.e_opening.mul(minus_z_inv.into_repr()); + let g_e = g.mul(proof.e_zinv.into_repr()); + e_z_inv.add_assign(&g_e); + + let h_alpha_term = multiexp( + vec![e_z_inv.into_affine(), f_y.into_affine(), t_z.into_affine()].iter(), + randomness.iter(), + ).into_affine(); + + let h_alpha_x_term = multiexp( + Some(proof.e_opening).iter() + .chain_ext(Some(proof.f_opening).iter()) + .chain_ext(Some(proof.t_opening).iter()), + randomness.iter(), + ).into_affine(); - // pub fn verify(n: usize, challenges: &Vec, commitments: &Vec, proof: &WellformednessProof, srs: &SRS) -> bool { - // let d = srs.d; + let h_term = multiexp( + Some(c_rc).iter() + .chain_ext(Some(c_ry).iter()) + .chain_ext(Some(t_commitment).iter()), + randomness.iter(), + ).into_affine(); - // let alpha_x_d_precomp = srs.h_positive_x_alpha[d].prepare(); - // let alpha_x_n_minus_d_precomp = srs.h_negative_x_alpha[d - n].prepare(); - // let mut h_prep = srs.h_positive_x[0]; - // h_prep.negate(); - // let h_prep = h_prep.prepare(); + E::final_exponentiation(&E::miller_loop(&[ + (&h_alpha_x_term.prepare(), &h_alpha_x_precomp), + (&h_alpha_term.prepare(), &h_alpha_precomp), + (&h_term.prepare(), &h_prep), + ])).unwrap() == E::Fqk::one() - // let a = multiexp( - // commitments.iter(), - // challenges.iter(), - // ).into_affine(); - - // let a = a.prepare(); - - // let valid = E::final_exponentiation(&E::miller_loop(&[ - // (&a, &h_prep), - // (&proof.l.prepare(), &alpha_x_d_precomp) - // ])).unwrap() == E::Fqk::one(); - - // if !valid { - // return false; - // } - - // let valid = E::final_exponentiation(&E::miller_loop(&[ - // (&a, &h_prep), - // (&proof.r.prepare(), &alpha_x_n_minus_d_precomp) - // ])).unwrap() == E::Fqk::one(); - - // if !valid { - // return false; - // } - - // true - // } + } } #[test] @@ -542,10 +649,9 @@ fn test_grand_product_argument() { let srs_alpha = Fr::from_str("23728792").unwrap(); let srs = SRS::::dummy(830564, srs_x, srs_alpha); - let n: usize = 1 << 1; + let n: usize = 1 << 8; let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - // let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::>(); - let coeffs = vec![Fr::from_str("1").unwrap(), Fr::from_str("2").unwrap()]; + let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::>(); let mut permutation = coeffs.clone(); rng.shuffle(&mut permutation); @@ -558,9 +664,7 @@ fn test_grand_product_argument() { assert_eq!(commitments_and_v_values.len(), 1); - // let y : Fr = rng.gen(); - - let y = Fr::one(); + let y : Fr = rng.gen(); let challenges = (0..1).map(|_| Fr::rand(rng)).collect::>(); @@ -581,6 +685,26 @@ fn test_grand_product_argument() { z, &srs); - assert!(valid); + assert!(valid, "grand product commitments should be valid"); + + let a_zy: Vec = grand_product_openings.iter().map(|el| el.0.clone()).collect(); + + let proof = argument.make_argument(&a_zy, &challenges, y, z, &srs); + + let randomness = (0..3).map(|_| Fr::rand(rng)).collect::>(); + + let valid = GrandProductArgument::verify( + n, + &randomness, + &a_zy, + &challenges, + t_commitment, + &commitments_and_v_values, + &proof, + y, + z, + &srs); + + assert!(valid, "t commitment should be valid"); } From c64438e800c1d91a8225cee2de4d8c85be2440b8 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 26 Feb 2019 18:10:33 +0300 Subject: [PATCH 36/42] start implementing permutation argument --- src/sonic/README.md | 2 +- src/sonic/unhelped/mod.rs | 1 + src/sonic/unhelped/permutation_argument.rs | 732 +++++++++++++++++++++ 3 files changed, 734 insertions(+), 1 deletion(-) diff --git a/src/sonic/README.md b/src/sonic/README.md index f38d191..df0f9c1 100644 --- a/src/sonic/README.md +++ b/src/sonic/README.md @@ -22,6 +22,6 @@ Initial SONIC proof system integration using the code from the [original impleme - [ ] Implement unhelped version - [x] Implement a part of S poly precomputation (S2) - [x] Implement a "well formed" argument - - [ ] Implement a coefficients product argument + - [x] Implement a coefficients product argument - [ ] Implement a premutation argument - [ ] Implement synthesizer for proper form of S polynomial \ No newline at end of file diff --git a/src/sonic/unhelped/mod.rs b/src/sonic/unhelped/mod.rs index 763e90c..7c54dec 100644 --- a/src/sonic/unhelped/mod.rs +++ b/src/sonic/unhelped/mod.rs @@ -6,5 +6,6 @@ mod s2_proof; mod wellformed_argument; mod grand_product_argument; +mod permutation_argument; pub use self::wellformed_argument::{WellformednessArgument, WellformednessProof}; \ No newline at end of file diff --git a/src/sonic/unhelped/permutation_argument.rs b/src/sonic/unhelped/permutation_argument.rs index e69de29..41cdfb2 100644 --- a/src/sonic/unhelped/permutation_argument.rs +++ b/src/sonic/unhelped/permutation_argument.rs @@ -0,0 +1,732 @@ +/// Permutation argument allows to prove that a commitment to a vector A is +/// actually a commitment to a vector of values that are equal to `(s^{perm})_i * y^{perm(i)}` +/// for some fixed permutation `perm` + +use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine}; +use pairing::{Engine, CurveProjective, CurveAffine}; +use std::marker::PhantomData; + +use crate::sonic::srs::SRS; +use crate::sonic::util::*; + +#[derive(Clone)] +pub struct SpecializedSRS { + p_1: E::G1Affine, + p_2: Vec, + p_3: E::G1Affine, + p_4: Vec, + n: usize +} + + +#[derive(Clone)] +pub struct PermutationArgument { + coefficients: Vec>, + permutations: Vec>, + n: usize +} + +#[derive(Clone)] +pub struct PermutationProof { + t_opening: E::G1Affine, + e_zinv: E::Fr, + e_opening: E::G1Affine, + f_y: E::Fr, + f_opening: E::G1Affine, +} + + +fn permute(coeffs: &[F], permutation: & [usize]) -> Vec{ + assert_eq!(coeffs.len(), permutation.len()); + let mut result: Vec = vec![F::zero(); coeffs.len()]; + for (i, j) in permutation.iter().enumerate() { + result[*j] = coeffs[i]; + } + result +} + +impl PermutationArgument { + pub fn new(coefficients: Vec>, permutations: Vec>) -> Self { + assert!(coefficients.len() > 0); + assert_eq!(coefficients.len(), permutations.len()); + + let n = coefficients[0].len(); + + for (c, p) in coefficients.iter().zip(permutations.iter()) { + assert!(c.len() == p.len()); + assert!(c.len() == n); + } + + PermutationArgument { + coefficients: coefficients, + permutations: permutations, + n: n + } + } + + pub fn make_specialized_srs(coefficients: &Vec>, permutations: &Vec>, srs: &SRS) -> SpecializedSRS { + assert!(coefficients.len() > 0); + assert_eq!(coefficients.len(), permutations.len()); + + let n = coefficients[0].len(); + + // p1 is just a commitment to the powers of x + let p_1 = multiexp(srs.g_positive_x_alpha[0..n].iter(), vec![E::Fr::one(); n].iter()).into_affine(); + + let mut p_2 = vec![]; + + let p_3 = { + let values: Vec = (1..=n).map(|el| { + let mut repr = <::Fr as PrimeField>::Repr::default(); + repr.as_mut()[0] = el as u64; + let fe = E::Fr::from_repr(repr).unwrap(); + + fe + }).collect(); + + multiexp(srs.g_positive_x_alpha[0..n].iter(), values.iter()).into_affine() + }; + + let mut p_4 = vec![]; + + for (c, p) in coefficients.iter().zip(permutations.iter()) { + assert!(c.len() == p.len()); + assert!(c.len() == n); + + // p2 is a commitment to the s^{perm}_i * x^i + let permuted_coeffs = permute(&c[..], &p[..]); + let p2 = multiexp(srs.g_positive_x_alpha[0..n].iter(), permuted_coeffs.iter()).into_affine(); + p_2.push(p2); + + { + let values: Vec = p.iter().map(|el| { + let mut repr = <::Fr as PrimeField>::Repr::default(); + repr.as_mut()[0] = *el as u64; + let fe = E::Fr::from_repr(repr).unwrap(); + + fe + }).collect(); + let p4 = multiexp(srs.g_positive_x_alpha[0..n].iter(), values.iter()).into_affine(); + p_4.push(p4); + } + } + + SpecializedSRS { + p_1: p_1, + p_2: p_2, + p_3: p_3, + p_4: p_4, + n: n + } + } + + // // Make a commitment to a polynomial in a form A*B^{x+1} = [a_1...a_{n}, 0, b_1...b_{n}] + // pub fn commit_for_grand_product(a: &[E::Fr], b: &[E::Fr], srs: &SRS) -> E::G1Affine { + // assert_eq!(a.len(), b.len()); + + // let n = a.len(); + + // multiexp( + // srs.g_positive_x_alpha[0..(2*n+1)].iter(), + // a.iter() + // .chain_ext(Some(E::Fr::zero()).iter()) + // .chain_ext(b.iter()) + // ).into_affine() + // } + + // pub fn open_commitments_for_grand_product(&self, y: E::Fr, z: E::Fr, srs: &SRS) -> Vec<(E::Fr, E::G1Affine)> { + // let n = self.n; + + // let mut yz = y; + // yz.mul_assign(&z); + + // let mut results = vec![]; + + // for a_poly in self.a_polynomials.iter() { + // let a = & a_poly[0..n]; + // let b = & a_poly[(n+1)..]; + // assert_eq!(a.len(), n); + // assert_eq!(b.len(), n); + // let mut val = evaluate_at_consequitive_powers(a, yz, yz); + // { + // let tmp = yz.pow([(n+2) as u64]); + // let v = evaluate_at_consequitive_powers(b, tmp, yz); + // val.add_assign(&v); + // } + + // let mut constant_term = val; + // constant_term.negate(); + + // let opening = polynomial_commitment_opening( + // 0, + // 2*n + 1, + // Some(constant_term).iter() + // .chain_ext(a.iter()) + // .chain_ext(Some(E::Fr::zero()).iter()) + // .chain_ext(b.iter()), + // yz, + // &srs); + + // results.push((val, opening)); + + // } + + // results + // } + + // // Make a commitment for the begining of the protocol, returns commitment and `v` scalar + // pub fn commit_to_individual_c_polynomials(&self, srs: &SRS) -> Vec<(E::G1Affine, E::Fr)> { + + // let mut results = vec![]; + + // let n = self.c_polynomials[0].len(); + + // for (p, v) in self.c_polynomials.iter().zip(self.v_elements.iter()) { + // let c = multiexp( + // srs.g_positive_x_alpha[0..n].iter(), + // p.iter() + // ).into_affine(); + + // results.push((c, *v)); + // } + + // results + // } + + // // Argument is based on an approach of main SONIC construction, but with a custom S(X,Y) polynomial of a simple form + // pub fn commit_to_t_polynomial(&mut self, challenges: & Vec, y: E::Fr, srs: &SRS) -> E::G1Affine { + // assert_eq!(challenges.len(), self.a_polynomials.len()); + + // let n = self.n; + + // let mut t_polynomial: Option> = None; + + // for (((a, c), v), challenge) in self.a_polynomials.iter() + // .zip(self.c_polynomials.iter()) + // .zip(self.v_elements.iter()) + // .zip(challenges.iter()) + // { + // let mut a_xy = a.clone(); + // let mut c_xy = c.clone(); + // let v = *v; + + // assert_eq!(a_xy.len(), 2*n + 1); + // assert_eq!(c_xy.len(), 2*n + 1); + + // // make a T polynomial + + // let r: Vec = { + // // p_a(X,Y)*Y + // let mut tmp = y; + // tmp.square(); + // mut_distribute_consequitive_powers(&mut a_xy[..], tmp, y); + + // // add extra terms + // //v*(XY)^{n+1}*Y + X^{n+2} + X^{n+1}Y − X^{2n+2}*Y + + // // n+1 term v*(XY)^{n+1}*Y + X^{n+1}Y + // let tmp = y.pow(&[(n+2) as u64]); + // let mut x_n_plus_one_term = v; + // x_n_plus_one_term.mul_assign(&tmp); + // x_n_plus_one_term.add_assign(&y); + // a_xy[n].add_assign(&x_n_plus_one_term); + + // // n+2 term + // a_xy[n+1].add_assign(&E::Fr::one()); + + // // 2n+2 term + // let mut tmp = y; + // tmp.negate(); + + // a_xy.push(tmp); + + // assert_eq!(a_xy.len(), 2*n + 2); + + // let mut r = vec![E::Fr::zero(); 2*n + 3]; + // r.extend(a_xy); + + // r + // }; + + // let r_prime: Vec = { + // let mut c_prime: Vec = c_xy.iter().rev().map(|el| *el).collect(); + // c_prime.push(E::Fr::one()); + // c_prime.push(E::Fr::zero()); + + // assert_eq!(c_prime.len(), 2*n + 3); + + // c_prime + // }; + + // // multiply polynomials with powers [-2n-2, -1] and [1, 2n+2], + // // expect result to be [-2n+1, 2n+1] + // let mut t: Vec = multiply_polynomials::(r, r_prime); + + // assert_eq!(t.len(), 6*n + 7); + + // // drain first powers due to the padding and last element due to requirement of being zero + // for (i, el) in t[0..(2*n+3)].iter().enumerate() { + // assert_eq!(*el, E::Fr::zero(), "{}", format!("Element {} is non-zero", i)); + // } + + // t.drain(0..(2*n+3)); + // let last = t.pop(); + // assert_eq!(last.unwrap(), E::Fr::zero(), "last element should be zero"); + + // assert_eq!(t.len(), 4*n + 3); + + // let mut val = { + // let mut tmp = y; + // tmp.square(); + // evaluate_at_consequitive_powers(&c_xy, tmp, y) + // }; + + // val.add_assign(&E::Fr::one()); + + // // subtract at constant term + // assert_eq!(t[2*n+1], val); + + // t[2*n+1].sub_assign(&val); + + // if t_polynomial.is_some() { + // if let Some(t_poly) = t_polynomial.as_mut() { + // mul_add_polynomials(&mut t_poly[..], &t, *challenge); + // } + // } else { + // mul_polynomial_by_scalar(&mut t, *challenge); + // t_polynomial = Some(t); + // } + // } + + // let t_polynomial = t_polynomial.unwrap(); + + // let c = multiexp(srs.g_negative_x_alpha[0..(2*n+1)].iter().rev() + // .chain_ext(srs.g_positive_x_alpha[0..(2*n+1)].iter()), + // t_polynomial[0..(2*n+1)].iter() + // .chain_ext(t_polynomial[(2*n+2)..].iter())).into_affine(); + + // self.t_polynomial = Some(t_polynomial); + + // c + // } + + + // // Argument is based on an approach of main SONIC construction, but with a custom S(X,Y) polynomial of a simple form + // pub fn make_argument(self, a_zy: & Vec, challenges: & Vec, y: E::Fr, z: E::Fr, srs: &SRS) -> GrandProductProof { + // assert_eq!(a_zy.len(), self.a_polynomials.len()); + // assert_eq!(challenges.len(), self.a_polynomials.len()); + + // let n = self.n; + + // let c_polynomials = self.c_polynomials; + // let mut e_polynomial: Option> = None; + // let mut f_polynomial: Option> = None; + + // let mut yz = y; + // yz.mul_assign(&z); + + // let z_inv = z.inverse().unwrap(); + + // for (((a, c), challenge), v) in a_zy.iter() + // .zip(c_polynomials.into_iter()) + // .zip(challenges.iter()) + // .zip(self.v_elements.iter()) + // { + // // cj = ((aj + vj(yz)n+1)y + zn+2 + zn+1y − z2n+2y)z−1 + // let mut c_zy = yz.pow([(n + 1) as u64]); + // c_zy.mul_assign(v); + // c_zy.add_assign(a); + // c_zy.mul_assign(&y); + + // let mut z_n_plus_1 = z.pow([(n + 1) as u64]); + + // let mut z_n_plus_2 = z_n_plus_1; + // z_n_plus_2.mul_assign(&z); + + // let mut z_2n_plus_2 = z_n_plus_1; + // z_2n_plus_2.square(); + // z_2n_plus_2.mul_assign(&y); + + // z_n_plus_1.mul_assign(&y); + + // c_zy.add_assign(&z_n_plus_1); + // c_zy.add_assign(&z_n_plus_2); + // c_zy.sub_assign(&z_2n_plus_2); + + // c_zy.mul_assign(&z_inv); + + // let mut rc = c_zy; + // rc.mul_assign(challenge); + + // let mut ry = y; + // ry.mul_assign(challenge); + + // if e_polynomial.is_some() && f_polynomial.is_some() { + // if let Some(e_poly) = e_polynomial.as_mut() { + // if let Some(f_poly) = f_polynomial.as_mut() { + // mul_add_polynomials(&mut e_poly[..], &c, rc); + // mul_add_polynomials(&mut f_poly[..], &c, ry); + // } + // } + // } else { + // let mut e = c.clone(); + // let mut f = c; + // mul_polynomial_by_scalar(&mut e, rc); + // mul_polynomial_by_scalar(&mut f, ry); + // e_polynomial = Some(e); + // f_polynomial = Some(f); + // } + // } + + // let e_polynomial = e_polynomial.unwrap(); + // let f_polynomial = f_polynomial.unwrap(); + + // // evaluate e at z^-1 + + // let mut e_val = evaluate_at_consequitive_powers(&e_polynomial, z_inv, z_inv); + // e_val.negate(); + + // // evaluate f at y + + // let mut f_val = evaluate_at_consequitive_powers(&f_polynomial, y, y); + // f_val.negate(); + + // let e_opening = polynomial_commitment_opening( + // 0, + // 2*n + 1, + // Some(e_val).iter().chain_ext(e_polynomial.iter()), + // z_inv, + // srs); + + // let f_opening = polynomial_commitment_opening( + // 0, + // 2*n + 1, + // Some(f_val).iter().chain_ext(f_polynomial.iter()), + // y, + // srs); + + // e_val.negate(); + // f_val.negate(); + + // let mut t_poly = self.t_polynomial.unwrap(); + // assert_eq!(t_poly.len(), 4*n + 3); + + // // largest negative power of t is -2n-1 + // let t_zy = { + // let tmp = z_inv.pow([(2*n+1) as u64]); + // evaluate_at_consequitive_powers(&t_poly, tmp, z) + // }; + + // t_poly[2*n + 1].sub_assign(&t_zy); + + // let t_opening = polynomial_commitment_opening( + // 2*n + 1, + // 2*n + 1, + // t_poly.iter(), + // z, + // srs); + + // GrandProductProof { + // t_opening: t_opening, + // e_zinv: e_val, + // e_opening: e_opening, + // f_y: f_val, + // f_opening: f_opening, + // } + // } + + // pub fn verify_ab_commitment(n: usize, + // randomness: & Vec, + // a_commitments: &Vec, + // b_commitments: &Vec, + // openings: &Vec<(E::Fr, E::G1Affine)>, + // y: E::Fr, + // z: E::Fr, + // srs: &SRS + // ) -> bool { + // assert_eq!(randomness.len(), a_commitments.len()); + // assert_eq!(openings.len(), a_commitments.len()); + // assert_eq!(b_commitments.len(), a_commitments.len()); + // let d = srs.d; + + // // e(Dj,hαx)e(D−yz,hα) = e(Aj,h)e(Bj,hxn+1)e(g−aj ,hα) + + // let g = srs.g_positive_x[0]; + + // let h_alpha_x_precomp = srs.h_positive_x_alpha[1].prepare(); + + // let h_alpha_precomp = srs.h_positive_x_alpha[0].prepare(); + + // let mut h_x_n_plus_one_precomp = srs.h_positive_x[n]; + // h_x_n_plus_one_precomp.negate(); + // let h_x_n_plus_one_precomp = h_x_n_plus_one_precomp.prepare(); + + // let mut h_prep = srs.h_positive_x[0]; + // h_prep.negate(); + // let h_prep = h_prep.prepare(); + + // let a = multiexp( + // a_commitments.iter(), + // randomness.iter(), + // ).into_affine(); + + // let a = a.prepare(); + + // let b = multiexp( + // b_commitments.iter(), + // randomness.iter(), + // ).into_affine(); + + // let b = b.prepare(); + + // let mut yz_neg = y; + // yz_neg.mul_assign(&z); + // yz_neg.negate(); + + // let mut ops = vec![]; + // let mut value = E::Fr::zero(); + + // for (el, r) in openings.iter().zip(randomness.iter()) { + // let (v, o) = el; + // ops.push(o.clone()); + // let mut val = *v; + // val.mul_assign(&r); + // value.add_assign(&val); + // } + + // let value = g.mul(value.into_repr()).into_affine().prepare(); + + // let openings = multiexp( + // ops.iter(), + // randomness.iter(), + // ).into_affine(); + + // let openings_zy = openings.mul(yz_neg.into_repr()).into_affine().prepare(); + // let openings = openings.prepare(); + + + // // e(Dj,hαx)e(D−yz,hα) = e(Aj,h)e(Bj,hxn+1)e(g−aj ,hα) + + // E::final_exponentiation(&E::miller_loop(&[ + // (&openings, &h_alpha_x_precomp), + // (&openings_zy, &h_alpha_precomp), + // (&a, &h_prep), + // (&b, &h_x_n_plus_one_precomp), + // (&value, &h_alpha_precomp) + // ])).unwrap() == E::Fqk::one() + // } + + // pub fn verify( + // n: usize, + // randomness: & Vec, + // a_zy: & Vec, + // challenges: &Vec, + // t_commitment: E::G1Affine, + // commitments: &Vec<(E::G1Affine, E::Fr)>, + // proof: &GrandProductProof, + // y: E::Fr, + // z: E::Fr, + // srs: &SRS + // ) -> bool { + // assert_eq!(randomness.len(), 3); + // assert_eq!(a_zy.len(), challenges.len()); + // assert_eq!(commitments.len(), challenges.len()); + + // let d = srs.d; + + // let g = srs.g_positive_x[0]; + + // let h_alpha_x_precomp = srs.h_positive_x_alpha[1].prepare(); + + // let h_alpha_precomp = srs.h_positive_x_alpha[0].prepare(); + + // let mut h_prep = srs.h_positive_x[0]; + // h_prep.negate(); + // let h_prep = h_prep.prepare(); + + // // first re-calculate cj and t(z,y) + + // let mut yz = y; + // yz.mul_assign(&z); + + // let z_inv = z.inverse().unwrap(); + + // let mut t_zy = E::Fr::zero(); + // t_zy.add_assign(&proof.e_zinv); + // t_zy.sub_assign(&proof.f_y); + + // let mut commitments_points = vec![]; + // let mut rc_vec = vec![]; + // let mut ry_vec = vec![]; + + // for ((r, commitment), a) in challenges.iter() + // .zip(commitments.iter()) + // .zip(a_zy.iter()) { + // let (c, v) = commitment; + // commitments_points.push(c.clone()); + + // // cj = ((aj + vj(yz)n+1)y + zn+2 + zn+1y − z2n+2y)z−1 + // let mut c_zy = yz.pow([(n + 1) as u64]); + // c_zy.mul_assign(v); + // c_zy.add_assign(a); + // c_zy.mul_assign(&y); + + // let mut z_n_plus_1 = z.pow([(n + 1) as u64]); + + // let mut z_n_plus_2 = z_n_plus_1; + // z_n_plus_2.mul_assign(&z); + + // let mut z_2n_plus_2 = z_n_plus_1; + // z_2n_plus_2.square(); + // z_2n_plus_2.mul_assign(&y); + + // z_n_plus_1.mul_assign(&y); + + // c_zy.add_assign(&z_n_plus_1); + // c_zy.add_assign(&z_n_plus_2); + // c_zy.sub_assign(&z_2n_plus_2); + + // c_zy.mul_assign(&z_inv); + + // let mut rc = c_zy; + // rc.mul_assign(&r); + // rc_vec.push(rc); + + // let mut ry = y; + // ry.mul_assign(&r); + // ry_vec.push(ry); + + // let mut val = rc; + // val.sub_assign(r); + // t_zy.add_assign(&val); + // } + + // let c_rc = multiexp( + // commitments_points.iter(), + // rc_vec.iter(), + // ).into_affine(); + + // let c_ry = multiexp( + // commitments_points.iter(), + // ry_vec.iter(), + // ).into_affine(); + + // let mut minus_y = y; + // minus_y.negate(); + + // let mut f_y = proof.f_opening.mul(minus_y.into_repr()); + // let g_f = g.mul(proof.f_y.into_repr()); + // f_y.add_assign(&g_f); + + // let mut minus_z = z; + // minus_z.negate(); + + // let mut t_z = proof.t_opening.mul(minus_z.into_repr()); + // let g_tzy = g.mul(t_zy.into_repr()); + // t_z.add_assign(&g_tzy); + + // let mut minus_z_inv = z_inv; + // minus_z_inv.negate(); + + // let mut e_z_inv = proof.e_opening.mul(minus_z_inv.into_repr()); + // let g_e = g.mul(proof.e_zinv.into_repr()); + // e_z_inv.add_assign(&g_e); + + // let h_alpha_term = multiexp( + // vec![e_z_inv.into_affine(), f_y.into_affine(), t_z.into_affine()].iter(), + // randomness.iter(), + // ).into_affine(); + + // let h_alpha_x_term = multiexp( + // Some(proof.e_opening).iter() + // .chain_ext(Some(proof.f_opening).iter()) + // .chain_ext(Some(proof.t_opening).iter()), + // randomness.iter(), + // ).into_affine(); + + + // let h_term = multiexp( + // Some(c_rc).iter() + // .chain_ext(Some(c_ry).iter()) + // .chain_ext(Some(t_commitment).iter()), + // randomness.iter(), + // ).into_affine(); + + // E::final_exponentiation(&E::miller_loop(&[ + // (&h_alpha_x_term.prepare(), &h_alpha_x_precomp), + // (&h_alpha_term.prepare(), &h_alpha_precomp), + // (&h_term.prepare(), &h_prep), + // ])).unwrap() == E::Fqk::one() + + // } +} + +// #[test] +// fn test_grand_product_argument() { +// use pairing::bls12_381::{Fr, G1Affine, G1, Bls12}; +// use rand::{XorShiftRng, SeedableRng, Rand, Rng}; +// use crate::sonic::srs::SRS; + +// let srs_x = Fr::from_str("23923").unwrap(); +// let srs_alpha = Fr::from_str("23728792").unwrap(); +// let srs = SRS::::dummy(830564, srs_x, srs_alpha); + +// let n: usize = 1 << 8; +// let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); +// let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::>(); +// let mut permutation = coeffs.clone(); +// rng.shuffle(&mut permutation); + +// let a_commitment = multiexp(srs.g_positive_x_alpha[0..n].iter(), coeffs.iter()).into_affine(); +// let b_commitment = multiexp(srs.g_positive_x_alpha[0..n].iter(), permutation.iter()).into_affine(); + +// let mut argument = GrandProductArgument::new(vec![(coeffs, permutation)]); + +// let commitments_and_v_values = argument.commit_to_individual_c_polynomials(&srs); + +// assert_eq!(commitments_and_v_values.len(), 1); + +// let y : Fr = rng.gen(); + +// let challenges = (0..1).map(|_| Fr::rand(rng)).collect::>(); + +// let t_commitment = argument.commit_to_t_polynomial(&challenges, y, &srs); + +// let z : Fr = rng.gen(); + +// let grand_product_openings = argument.open_commitments_for_grand_product(y, z, &srs); + +// let randomness = (0..1).map(|_| Fr::rand(rng)).collect::>(); + +// let valid = GrandProductArgument::verify_ab_commitment(n, +// &randomness, +// &vec![a_commitment], +// &vec![b_commitment], +// &grand_product_openings, +// y, +// z, +// &srs); + +// assert!(valid, "grand product commitments should be valid"); + +// let a_zy: Vec = grand_product_openings.iter().map(|el| el.0.clone()).collect(); + +// let proof = argument.make_argument(&a_zy, &challenges, y, z, &srs); + +// let randomness = (0..3).map(|_| Fr::rand(rng)).collect::>(); + +// let valid = GrandProductArgument::verify( +// n, +// &randomness, +// &a_zy, +// &challenges, +// t_commitment, +// &commitments_and_v_values, +// &proof, +// y, +// z, +// &srs); + +// assert!(valid, "t commitment should be valid"); +// } + From 0a2ed705dca7d07eee71335f93f5f5e4da96f6fe Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Wed, 27 Feb 2019 01:06:14 +0300 Subject: [PATCH 37/42] start making permutation argument --- src/sonic/unhelped/permutation_argument.rs | 265 ++++++++++++--------- 1 file changed, 147 insertions(+), 118 deletions(-) diff --git a/src/sonic/unhelped/permutation_argument.rs b/src/sonic/unhelped/permutation_argument.rs index 41cdfb2..0dacc35 100644 --- a/src/sonic/unhelped/permutation_argument.rs +++ b/src/sonic/unhelped/permutation_argument.rs @@ -18,20 +18,18 @@ pub struct SpecializedSRS { n: usize } - #[derive(Clone)] pub struct PermutationArgument { - coefficients: Vec>, + non_permuted_coefficients: Vec>, + permuted_coefficients: Vec>, permutations: Vec>, n: usize } #[derive(Clone)] pub struct PermutationProof { - t_opening: E::G1Affine, - e_zinv: E::Fr, + v_zy: E::Fr, e_opening: E::G1Affine, - f_y: E::Fr, f_opening: E::G1Affine, } @@ -58,17 +56,18 @@ impl PermutationArgument { } PermutationArgument { - coefficients: coefficients, + non_permuted_coefficients: coefficients, + permuted_coefficients: vec![vec![]], permutations: permutations, n: n } } - pub fn make_specialized_srs(coefficients: &Vec>, permutations: &Vec>, srs: &SRS) -> SpecializedSRS { - assert!(coefficients.len() > 0); - assert_eq!(coefficients.len(), permutations.len()); + pub fn make_specialized_srs(non_permuted_coefficients: &Vec>, permutations: &Vec>, srs: &SRS) -> SpecializedSRS { + assert!(non_permuted_coefficients.len() > 0); + assert_eq!(non_permuted_coefficients.len(), permutations.len()); - let n = coefficients[0].len(); + let n = non_permuted_coefficients[0].len(); // p1 is just a commitment to the powers of x let p_1 = multiexp(srs.g_positive_x_alpha[0..n].iter(), vec![E::Fr::one(); n].iter()).into_affine(); @@ -89,14 +88,16 @@ impl PermutationArgument { let mut p_4 = vec![]; - for (c, p) in coefficients.iter().zip(permutations.iter()) { + for (c, p) in non_permuted_coefficients.iter().zip(permutations.iter()) { assert!(c.len() == p.len()); assert!(c.len() == n); // p2 is a commitment to the s^{perm}_i * x^i - let permuted_coeffs = permute(&c[..], &p[..]); - let p2 = multiexp(srs.g_positive_x_alpha[0..n].iter(), permuted_coeffs.iter()).into_affine(); - p_2.push(p2); + { + // let permuted_coeffs = permute(&c[..], &p[..]); + let p2 = multiexp(srs.g_positive_x_alpha[0..n].iter(), c.iter()).into_affine(); + p_2.push(p2); + } { let values: Vec = p.iter().map(|el| { @@ -120,59 +121,87 @@ impl PermutationArgument { } } - // // Make a commitment to a polynomial in a form A*B^{x+1} = [a_1...a_{n}, 0, b_1...b_{n}] - // pub fn commit_for_grand_product(a: &[E::Fr], b: &[E::Fr], srs: &SRS) -> E::G1Affine { - // assert_eq!(a.len(), b.len()); + // commit to s and s' at y. Mutates the state + pub fn commit(&mut self, y: E::Fr, srs: &SRS) -> Vec<(E::G1Affine, E::G1Affine)> { + let mut result = vec![]; - // let n = a.len(); + let n = self.non_permuted_coefficients[0].len(); - // multiexp( - // srs.g_positive_x_alpha[0..(2*n+1)].iter(), - // a.iter() - // .chain_ext(Some(E::Fr::zero()).iter()) - // .chain_ext(b.iter()) - // ).into_affine() - // } + let mut permuted_coefficients = vec![]; - // pub fn open_commitments_for_grand_product(&self, y: E::Fr, z: E::Fr, srs: &SRS) -> Vec<(E::Fr, E::G1Affine)> { - // let n = self.n; + for (c, p) in self.non_permuted_coefficients.iter().zip(self.permutations.iter()) { + let mut non_permuted = c.clone(); + let permuted = permute(&non_permuted[..], &p[..]); - // let mut yz = y; - // yz.mul_assign(&z); + mut_distribute_consequitive_powers(&mut non_permuted[..], y, y); + let s_prime = multiexp(srs.g_positive_x_alpha[0..n].iter(), non_permuted.iter()).into_affine(); - // let mut results = vec![]; + let mut permuted_at_y = permute(&non_permuted[..], &p[..]); + drop(non_permuted); - // for a_poly in self.a_polynomials.iter() { - // let a = & a_poly[0..n]; - // let b = & a_poly[(n+1)..]; - // assert_eq!(a.len(), n); - // assert_eq!(b.len(), n); - // let mut val = evaluate_at_consequitive_powers(a, yz, yz); - // { - // let tmp = yz.pow([(n+2) as u64]); - // let v = evaluate_at_consequitive_powers(b, tmp, yz); - // val.add_assign(&v); - // } + let s = multiexp(srs.g_positive_x_alpha[0..n].iter(), permuted_at_y.iter()).into_affine(); - // let mut constant_term = val; - // constant_term.negate(); + result.push((s, s_prime)); - // let opening = polynomial_commitment_opening( - // 0, - // 2*n + 1, - // Some(constant_term).iter() - // .chain_ext(a.iter()) - // .chain_ext(Some(E::Fr::zero()).iter()) - // .chain_ext(b.iter()), - // yz, - // &srs); + permuted_coefficients.push(permuted); + } - // results.push((val, opening)); + self.permuted_coefficients = permuted_coefficients; - // } + result + } - // results - // } + pub fn open_commitments_to_s(&self, challenges: &Vec, y: E::Fr, z_prime: E::Fr, srs: &SRS) -> PermutationProof { + let n = self.non_permuted_coefficients[0].len(); + + let mut yz = y; + yz.mul_assign(&z_prime); + + let mut polynomial: Option> = None; + + for (p, r) in self.non_permuted_coefficients.iter() + .zip(challenges.iter()) { + if polynomial.is_some() { + if let Some(poly) = polynomial.as_mut() { + mul_add_polynomials(&mut poly[..], &p[..], *r); + } + } else { + let mut poly = p.clone(); + mul_polynomial_by_scalar(&mut poly[..], *r); + + } + } + + let mut polynomial = polynomial.unwrap(); + let v = evaluate_at_consequitive_powers(&polynomial[..], yz, yz); + + let mut v_neg = v; + v_neg.negate(); + + let e = polynomial_commitment_opening( + 0, + n, + Some(v_neg).iter().chain_ext(polynomial.iter()), + yz, + &srs + ); + + mut_distribute_consequitive_powers(&mut polynomial[..], y, y); + + let f = polynomial_commitment_opening( + 0, + n, + Some(v_neg).iter().chain_ext(polynomial.iter()), + z_prime, + &srs + ); + + PermutationProof { + v_zy: v, + e_opening: e, + f_opening: f + } + } // // Make a commitment for the begining of the protocol, returns commitment and `v` scalar // pub fn commit_to_individual_c_polynomials(&self, srs: &SRS) -> Vec<(E::G1Affine, E::Fr)> { @@ -435,86 +464,86 @@ impl PermutationArgument { // } // } - // pub fn verify_ab_commitment(n: usize, - // randomness: & Vec, - // a_commitments: &Vec, - // b_commitments: &Vec, - // openings: &Vec<(E::Fr, E::G1Affine)>, - // y: E::Fr, - // z: E::Fr, - // srs: &SRS - // ) -> bool { - // assert_eq!(randomness.len(), a_commitments.len()); - // assert_eq!(openings.len(), a_commitments.len()); - // assert_eq!(b_commitments.len(), a_commitments.len()); - // let d = srs.d; + pub fn verify_s_prime_commitment(n: usize, + randomness: & Vec, + challenges: & Vec, + commitments: &Vec, + proof: &PermutationProof, + y: E::Fr, + z_prime: E::Fr, + specialized_srs: &SpecializedSRS, + srs: &SRS + ) -> bool { + assert_eq!(randomness.len(), 2); + assert_eq!(challenges.len(), commitments.len()); - // // e(Dj,hαx)e(D−yz,hα) = e(Aj,h)e(Bj,hxn+1)e(g−aj ,hα) + // e(E,hαx)e(E−z′,hα) = e(􏰗Mj=1Sj′rj,h)e(g−v,hα) + // e(F,hαx)e(F−yz′,hα) = e(􏰗Mj=1P2jrj,h)e(g−v,hα) - // let g = srs.g_positive_x[0]; + let g = srs.g_positive_x[0]; - // let h_alpha_x_precomp = srs.h_positive_x_alpha[1].prepare(); + let h_alpha_x_precomp = srs.h_positive_x_alpha[1].prepare(); - // let h_alpha_precomp = srs.h_positive_x_alpha[0].prepare(); + let h_alpha_precomp = srs.h_positive_x_alpha[0].prepare(); - // let mut h_x_n_plus_one_precomp = srs.h_positive_x[n]; - // h_x_n_plus_one_precomp.negate(); - // let h_x_n_plus_one_precomp = h_x_n_plus_one_precomp.prepare(); + let mut h_prep = srs.h_positive_x[0]; + h_prep.negate(); + let h_prep = h_prep.prepare(); - // let mut h_prep = srs.h_positive_x[0]; - // h_prep.negate(); - // let h_prep = h_prep.prepare(); + let mut value = E::Fr::zero(); - // let a = multiexp( - // a_commitments.iter(), - // randomness.iter(), - // ).into_affine(); + for r in randomness.iter() { + value.add_assign(&r); + } + value.mul_assign(&proof.v_zy); - // let a = a.prepare(); + let mut minus_yz = z_prime; + minus_yz.mul_assign(&y); + minus_yz.negate(); - // let b = multiexp( - // b_commitments.iter(), - // randomness.iter(), - // ).into_affine(); + let mut minus_z_prime = z_prime; + minus_z_prime.negate(); - // let b = b.prepare(); + let f_yz = proof.f_opening.mul(minus_yz.into_repr()); + let e_z = proof.e_opening.mul(minus_z_prime.into_repr()); - // let mut yz_neg = y; - // yz_neg.mul_assign(&z); - // yz_neg.negate(); + let mut h_alpha_term = multiexp( + vec![e_z.into_affine(), f_yz.into_affine()].iter(), + randomness.iter(), + ); - // let mut ops = vec![]; - // let mut value = E::Fr::zero(); + let g_v = g.mul(value.into_repr()); - // for (el, r) in openings.iter().zip(randomness.iter()) { - // let (v, o) = el; - // ops.push(o.clone()); - // let mut val = *v; - // val.mul_assign(&r); - // value.add_assign(&val); - // } + h_alpha_term.add_assign(&g_v); - // let value = g.mul(value.into_repr()).into_affine().prepare(); + let h_alpha_x_term = multiexp( + Some(proof.e_opening).iter() + .chain_ext(Some(proof.f_opening).iter()), + randomness.iter(), + ).into_affine(); - // let openings = multiexp( - // ops.iter(), - // randomness.iter(), - // ).into_affine(); + let s_r = multiexp( + commitments.iter(), + challenges.iter() + ).into_affine(); - // let openings_zy = openings.mul(yz_neg.into_repr()).into_affine().prepare(); - // let openings = openings.prepare(); + let p2_r = multiexp( + specialized_srs.p_2.iter(), + challenges.iter() + ).into_affine(); + let h_term = multiexp( + Some(s_r).iter() + .chain_ext(Some(p2_r).iter()), + randomness.iter() + ).into_affine(); - // // e(Dj,hαx)e(D−yz,hα) = e(Aj,h)e(Bj,hxn+1)e(g−aj ,hα) - - // E::final_exponentiation(&E::miller_loop(&[ - // (&openings, &h_alpha_x_precomp), - // (&openings_zy, &h_alpha_precomp), - // (&a, &h_prep), - // (&b, &h_x_n_plus_one_precomp), - // (&value, &h_alpha_precomp) - // ])).unwrap() == E::Fqk::one() - // } + E::final_exponentiation(&E::miller_loop(&[ + (&h_alpha_x_term.prepare(), &h_alpha_x_precomp), + (&h_alpha_term.into_affine().prepare(), &h_alpha_precomp), + (&h_term.prepare(), &h_prep), + ])).unwrap() == E::Fqk::one() + } // pub fn verify( // n: usize, From 04454890b9da64e18ba800526097274e21547975 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Thu, 28 Feb 2019 20:37:33 +0300 Subject: [PATCH 38/42] permutation argument is complete --- src/sonic/unhelped/grand_product_argument.rs | 19 +- src/sonic/unhelped/permutation_argument.rs | 799 ++++++++----------- src/sonic/unhelped/wellformed_argument.rs | 3 +- 3 files changed, 372 insertions(+), 449 deletions(-) diff --git a/src/sonic/unhelped/grand_product_argument.rs b/src/sonic/unhelped/grand_product_argument.rs index 234edc4..dad9505 100644 --- a/src/sonic/unhelped/grand_product_argument.rs +++ b/src/sonic/unhelped/grand_product_argument.rs @@ -112,6 +112,24 @@ impl GrandProductArgument { ).into_affine() } + // Make a commitment to a polynomial in a form A*B^{x+1} = [a_1...a_{n}, 0, b_1...b_{n}] + pub fn commit_for_individual_products(a: &[E::Fr], b: &[E::Fr], srs: &SRS) -> (E::G1Affine, E::G1Affine) { + assert_eq!(a.len(), b.len()); + + let n = a.len(); + + let a = multiexp( + srs.g_positive_x_alpha[0..n].iter(), + a.iter()).into_affine(); + + + let b = multiexp( + srs.g_positive_x_alpha[0..n].iter(), + b.iter()).into_affine(); + + (a, b) + } + pub fn open_commitments_for_grand_product(&self, y: E::Fr, z: E::Fr, srs: &SRS) -> Vec<(E::Fr, E::G1Affine)> { let n = self.n; @@ -288,7 +306,6 @@ impl GrandProductArgument { c } - // Argument is based on an approach of main SONIC construction, but with a custom S(X,Y) polynomial of a simple form pub fn make_argument(self, a_zy: & Vec, challenges: & Vec, y: E::Fr, z: E::Fr, srs: &SRS) -> GrandProductProof { assert_eq!(a_zy.len(), self.a_polynomials.len()); diff --git a/src/sonic/unhelped/permutation_argument.rs b/src/sonic/unhelped/permutation_argument.rs index 0dacc35..96c6039 100644 --- a/src/sonic/unhelped/permutation_argument.rs +++ b/src/sonic/unhelped/permutation_argument.rs @@ -8,6 +8,8 @@ use std::marker::PhantomData; use crate::sonic::srs::SRS; use crate::sonic::util::*; +use super::wellformed_argument::{WellformednessArgument, WellformednessProof}; +use super::grand_product_argument::{GrandProductArgument, GrandProductProof}; #[derive(Clone)] pub struct SpecializedSRS { @@ -22,6 +24,7 @@ pub struct SpecializedSRS { pub struct PermutationArgument { non_permuted_coefficients: Vec>, permuted_coefficients: Vec>, + permuted_at_y_coefficients: Vec>, permutations: Vec>, n: usize } @@ -33,12 +36,20 @@ pub struct PermutationProof { f_opening: E::G1Affine, } +#[derive(Clone)] +pub struct Proof { + j: usize, + s_opening: E::G1Affine, + s_zy: E::Fr +} + + fn permute(coeffs: &[F], permutation: & [usize]) -> Vec{ assert_eq!(coeffs.len(), permutation.len()); let mut result: Vec = vec![F::zero(); coeffs.len()]; for (i, j) in permutation.iter().enumerate() { - result[*j] = coeffs[i]; + result[*j - 1] = coeffs[i]; } result } @@ -58,6 +69,7 @@ impl PermutationArgument { PermutationArgument { non_permuted_coefficients: coefficients, permuted_coefficients: vec![vec![]], + permuted_at_y_coefficients: vec![vec![]], permutations: permutations, n: n } @@ -128,6 +140,7 @@ impl PermutationArgument { let n = self.non_permuted_coefficients[0].len(); let mut permuted_coefficients = vec![]; + let mut permuted_at_y_coefficients = vec![]; for (c, p) in self.non_permuted_coefficients.iter().zip(self.permutations.iter()) { let mut non_permuted = c.clone(); @@ -144,14 +157,22 @@ impl PermutationArgument { result.push((s, s_prime)); permuted_coefficients.push(permuted); + permuted_at_y_coefficients.push(permuted_at_y); } self.permuted_coefficients = permuted_coefficients; + self.permuted_at_y_coefficients = permuted_at_y_coefficients; result } - pub fn open_commitments_to_s(&self, challenges: &Vec, y: E::Fr, z_prime: E::Fr, srs: &SRS) -> PermutationProof { + pub fn open_commitments_to_s_prime( + &self, + challenges: &Vec, + y: E::Fr, + z_prime: E::Fr, + srs: &SRS + ) -> PermutationProof { let n = self.non_permuted_coefficients[0].len(); let mut yz = y; @@ -168,7 +189,7 @@ impl PermutationArgument { } else { let mut poly = p.clone(); mul_polynomial_by_scalar(&mut poly[..], *r); - + polynomial = Some(poly); } } @@ -178,7 +199,7 @@ impl PermutationArgument { let mut v_neg = v; v_neg.negate(); - let e = polynomial_commitment_opening( + let f = polynomial_commitment_opening( 0, n, Some(v_neg).iter().chain_ext(polynomial.iter()), @@ -188,7 +209,7 @@ impl PermutationArgument { mut_distribute_consequitive_powers(&mut polynomial[..], y, y); - let f = polynomial_commitment_opening( + let e = polynomial_commitment_opening( 0, n, Some(v_neg).iter().chain_ext(polynomial.iter()), @@ -203,268 +224,164 @@ impl PermutationArgument { } } - // // Make a commitment for the begining of the protocol, returns commitment and `v` scalar - // pub fn commit_to_individual_c_polynomials(&self, srs: &SRS) -> Vec<(E::G1Affine, E::Fr)> { - - // let mut results = vec![]; - - // let n = self.c_polynomials[0].len(); - - // for (p, v) in self.c_polynomials.iter().zip(self.v_elements.iter()) { - // let c = multiexp( - // srs.g_positive_x_alpha[0..n].iter(), - // p.iter() - // ).into_affine(); - - // results.push((c, *v)); - // } - - // results - // } - - // // Argument is based on an approach of main SONIC construction, but with a custom S(X,Y) polynomial of a simple form - // pub fn commit_to_t_polynomial(&mut self, challenges: & Vec, y: E::Fr, srs: &SRS) -> E::G1Affine { - // assert_eq!(challenges.len(), self.a_polynomials.len()); - - // let n = self.n; - - // let mut t_polynomial: Option> = None; - - // for (((a, c), v), challenge) in self.a_polynomials.iter() - // .zip(self.c_polynomials.iter()) - // .zip(self.v_elements.iter()) - // .zip(challenges.iter()) - // { - // let mut a_xy = a.clone(); - // let mut c_xy = c.clone(); - // let v = *v; - - // assert_eq!(a_xy.len(), 2*n + 1); - // assert_eq!(c_xy.len(), 2*n + 1); - - // // make a T polynomial - - // let r: Vec = { - // // p_a(X,Y)*Y - // let mut tmp = y; - // tmp.square(); - // mut_distribute_consequitive_powers(&mut a_xy[..], tmp, y); - - // // add extra terms - // //v*(XY)^{n+1}*Y + X^{n+2} + X^{n+1}Y − X^{2n+2}*Y - - // // n+1 term v*(XY)^{n+1}*Y + X^{n+1}Y - // let tmp = y.pow(&[(n+2) as u64]); - // let mut x_n_plus_one_term = v; - // x_n_plus_one_term.mul_assign(&tmp); - // x_n_plus_one_term.add_assign(&y); - // a_xy[n].add_assign(&x_n_plus_one_term); - - // // n+2 term - // a_xy[n+1].add_assign(&E::Fr::one()); - - // // 2n+2 term - // let mut tmp = y; - // tmp.negate(); - - // a_xy.push(tmp); - - // assert_eq!(a_xy.len(), 2*n + 2); - - // let mut r = vec![E::Fr::zero(); 2*n + 3]; - // r.extend(a_xy); - - // r - // }; - - // let r_prime: Vec = { - // let mut c_prime: Vec = c_xy.iter().rev().map(|el| *el).collect(); - // c_prime.push(E::Fr::one()); - // c_prime.push(E::Fr::zero()); - - // assert_eq!(c_prime.len(), 2*n + 3); - - // c_prime - // }; - - // // multiply polynomials with powers [-2n-2, -1] and [1, 2n+2], - // // expect result to be [-2n+1, 2n+1] - // let mut t: Vec = multiply_polynomials::(r, r_prime); - - // assert_eq!(t.len(), 6*n + 7); - - // // drain first powers due to the padding and last element due to requirement of being zero - // for (i, el) in t[0..(2*n+3)].iter().enumerate() { - // assert_eq!(*el, E::Fr::zero(), "{}", format!("Element {} is non-zero", i)); - // } - - // t.drain(0..(2*n+3)); - // let last = t.pop(); - // assert_eq!(last.unwrap(), E::Fr::zero(), "last element should be zero"); - - // assert_eq!(t.len(), 4*n + 3); - - // let mut val = { - // let mut tmp = y; - // tmp.square(); - // evaluate_at_consequitive_powers(&c_xy, tmp, y) - // }; - - // val.add_assign(&E::Fr::one()); - - // // subtract at constant term - // assert_eq!(t[2*n+1], val); - - // t[2*n+1].sub_assign(&val); - - // if t_polynomial.is_some() { - // if let Some(t_poly) = t_polynomial.as_mut() { - // mul_add_polynomials(&mut t_poly[..], &t, *challenge); - // } - // } else { - // mul_polynomial_by_scalar(&mut t, *challenge); - // t_polynomial = Some(t); - // } - // } - - // let t_polynomial = t_polynomial.unwrap(); - - // let c = multiexp(srs.g_negative_x_alpha[0..(2*n+1)].iter().rev() - // .chain_ext(srs.g_positive_x_alpha[0..(2*n+1)].iter()), - // t_polynomial[0..(2*n+1)].iter() - // .chain_ext(t_polynomial[(2*n+2)..].iter())).into_affine(); - - // self.t_polynomial = Some(t_polynomial); - - // c - // } - - - // // Argument is based on an approach of main SONIC construction, but with a custom S(X,Y) polynomial of a simple form - // pub fn make_argument(self, a_zy: & Vec, challenges: & Vec, y: E::Fr, z: E::Fr, srs: &SRS) -> GrandProductProof { - // assert_eq!(a_zy.len(), self.a_polynomials.len()); - // assert_eq!(challenges.len(), self.a_polynomials.len()); - - // let n = self.n; - - // let c_polynomials = self.c_polynomials; - // let mut e_polynomial: Option> = None; - // let mut f_polynomial: Option> = None; - - // let mut yz = y; - // yz.mul_assign(&z); - - // let z_inv = z.inverse().unwrap(); - - // for (((a, c), challenge), v) in a_zy.iter() - // .zip(c_polynomials.into_iter()) - // .zip(challenges.iter()) - // .zip(self.v_elements.iter()) - // { - // // cj = ((aj + vj(yz)n+1)y + zn+2 + zn+1y − z2n+2y)z−1 - // let mut c_zy = yz.pow([(n + 1) as u64]); - // c_zy.mul_assign(v); - // c_zy.add_assign(a); - // c_zy.mul_assign(&y); - - // let mut z_n_plus_1 = z.pow([(n + 1) as u64]); - - // let mut z_n_plus_2 = z_n_plus_1; - // z_n_plus_2.mul_assign(&z); - - // let mut z_2n_plus_2 = z_n_plus_1; - // z_2n_plus_2.square(); - // z_2n_plus_2.mul_assign(&y); - - // z_n_plus_1.mul_assign(&y); - - // c_zy.add_assign(&z_n_plus_1); - // c_zy.add_assign(&z_n_plus_2); - // c_zy.sub_assign(&z_2n_plus_2); - - // c_zy.mul_assign(&z_inv); - - // let mut rc = c_zy; - // rc.mul_assign(challenge); - - // let mut ry = y; - // ry.mul_assign(challenge); - - // if e_polynomial.is_some() && f_polynomial.is_some() { - // if let Some(e_poly) = e_polynomial.as_mut() { - // if let Some(f_poly) = f_polynomial.as_mut() { - // mul_add_polynomials(&mut e_poly[..], &c, rc); - // mul_add_polynomials(&mut f_poly[..], &c, ry); - // } - // } - // } else { - // let mut e = c.clone(); - // let mut f = c; - // mul_polynomial_by_scalar(&mut e, rc); - // mul_polynomial_by_scalar(&mut f, ry); - // e_polynomial = Some(e); - // f_polynomial = Some(f); - // } - // } - - // let e_polynomial = e_polynomial.unwrap(); - // let f_polynomial = f_polynomial.unwrap(); - - // // evaluate e at z^-1 - - // let mut e_val = evaluate_at_consequitive_powers(&e_polynomial, z_inv, z_inv); - // e_val.negate(); - - // // evaluate f at y - - // let mut f_val = evaluate_at_consequitive_powers(&f_polynomial, y, y); - // f_val.negate(); - - // let e_opening = polynomial_commitment_opening( - // 0, - // 2*n + 1, - // Some(e_val).iter().chain_ext(e_polynomial.iter()), - // z_inv, - // srs); - - // let f_opening = polynomial_commitment_opening( - // 0, - // 2*n + 1, - // Some(f_val).iter().chain_ext(f_polynomial.iter()), - // y, - // srs); - - // e_val.negate(); - // f_val.negate(); - - // let mut t_poly = self.t_polynomial.unwrap(); - // assert_eq!(t_poly.len(), 4*n + 3); - - // // largest negative power of t is -2n-1 - // let t_zy = { - // let tmp = z_inv.pow([(2*n+1) as u64]); - // evaluate_at_consequitive_powers(&t_poly, tmp, z) - // }; - - // t_poly[2*n + 1].sub_assign(&t_zy); - - // let t_opening = polynomial_commitment_opening( - // 2*n + 1, - // 2*n + 1, - // t_poly.iter(), - // z, - // srs); - - // GrandProductProof { - // t_opening: t_opening, - // e_zinv: e_val, - // e_opening: e_opening, - // f_y: f_val, - // f_opening: f_opening, - // } - // } - - pub fn verify_s_prime_commitment(n: usize, + // Argument a permutation argument. Current implementation consumes, cause extra arguments are required + pub fn make_argument(self, + beta: E::Fr, + gamma: E::Fr, + grand_product_challenges: & Vec, + wellformed_challenges: & Vec, + y: E::Fr, + z: E::Fr, + specialized_srs: &SpecializedSRS, + srs: &SRS + ) -> Proof { + // Sj(P4j)β(P1j)γ is equal to the product of the coefficients of Sj′(P3j)β(P1j)γ + // also open s = \sum self.permuted_coefficients(X, y) at z + + let n = self.n; + let j = self.non_permuted_coefficients.len(); + assert_eq!(j, grand_product_challenges.len()); + assert_eq!(2*j, wellformed_challenges.len()); + + let mut s_polynomial: Option> = None; + + for c in self.permuted_at_y_coefficients.iter() + { + if s_polynomial.is_some() { + if let Some(poly) = s_polynomial.as_mut() { + add_polynomials(&mut poly[..], & c[..]); + } + } else { + s_polynomial = Some(c.clone()); + } + } + let mut s_polynomial = s_polynomial.unwrap(); + // evaluate at z + let s_zy = evaluate_at_consequitive_powers(& s_polynomial[..], z, z); + + let mut s_zy_neg = s_zy; + s_zy_neg.negate(); + + let s_zy_opening = polynomial_commitment_opening( + 0, + n, + Some(s_zy_neg).iter().chain_ext(s_polynomial.iter()), + z, + &srs + ); + + // Sj(P4j)^β (P1j)^γ is equal to the product of the coefficients of Sj′(P3j)^β (P1j)^γ + + let p_1_values = vec![E::Fr::one(); n]; + let p_3_values: Vec = (1..=n).map(|el| { + let mut repr = <::Fr as PrimeField>::Repr::default(); + repr.as_mut()[0] = el as u64; + let fe = E::Fr::from_repr(repr).unwrap(); + + fe + }).collect(); + + let mut grand_products = vec![]; + + for (i, ((non_permuted, permuted), permutation)) in self.non_permuted_coefficients.into_iter() + .zip(self.permuted_coefficients.into_iter()) + .zip(self.permutations.into_iter()).enumerate() + + { + // \prod si+βσi+γ = \prod s'i + β*i + γ + let mut s_j_combination = non_permuted; + { + let p_4_values: Vec = permutation.into_iter().map(|el| { + let mut repr = <::Fr as PrimeField>::Repr::default(); + repr.as_mut()[0] = el as u64; + let fe = E::Fr::from_repr(repr).unwrap(); + + fe + }).collect(); + mul_add_polynomials(&mut s_j_combination[..], & p_4_values[..], beta); + mul_add_polynomials(&mut s_j_combination[..], & p_1_values[..], gamma); + } + + let mut s_prime_j_combination = permuted; + { + mul_add_polynomials(&mut s_prime_j_combination[..], & p_3_values[..], beta); + mul_add_polynomials(&mut s_prime_j_combination[..], & p_1_values[..], gamma); + } + + grand_products.push((s_j_combination, s_prime_j_combination)); + } + + let mut a_commitments = vec![]; + let mut b_commitments = vec![]; + + for (a, b) in grand_products.iter() { + let (c_a, c_b) = GrandProductArgument::commit_for_individual_products(& a[..], & b[..], &srs); + a_commitments.push(c_a); + b_commitments.push(c_b); + } + + { + let mut all_polys = vec![]; + for p in grand_products.iter() { + let (a, b) = p; + all_polys.push(a.clone()); + all_polys.push(b.clone()); + } + + let wellformed_argument = WellformednessArgument::new(all_polys); + let commitments = wellformed_argument.commit(&srs); + let proof = wellformed_argument.make_argument(wellformed_challenges.clone(), &srs); + let valid = WellformednessArgument::verify(n, &wellformed_challenges, &commitments, &proof, &srs); + + assert!(valid, "wellformedness argument must be valid"); + } + + let mut grand_product_argument = GrandProductArgument::new(grand_products); + let c_commitments = grand_product_argument.commit_to_individual_c_polynomials(&srs); + let t_commitment = grand_product_argument.commit_to_t_polynomial(&grand_product_challenges, y, &srs); + let grand_product_openings = grand_product_argument.open_commitments_for_grand_product(y, z, &srs); + let a_zy: Vec = grand_product_openings.iter().map(|el| el.0.clone()).collect(); + let proof = grand_product_argument.make_argument(&a_zy, &grand_product_challenges, y, z, &srs); + + { + use rand::{XorShiftRng, SeedableRng, Rand, Rng}; + let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + let randomness = (0..j).map(|_| E::Fr::rand(rng)).collect::>(); + + let valid = GrandProductArgument::verify_ab_commitment(n, + &randomness, + &a_commitments, + & b_commitments, + &grand_product_openings, + y, + z, + &srs); + assert!(valid, "ab part of grand product argument must be valid"); + + let randomness = (0..3).map(|_| E::Fr::rand(rng)).collect::>(); + let valid = GrandProductArgument::verify(n, + &randomness, + &a_zy, + &grand_product_challenges, + t_commitment, + &c_commitments, + &proof, + y, + z, + &srs); + + assert!(valid, "grand product argument must be valid"); + } + + Proof { + j: j, + s_opening: s_zy_opening, + s_zy: s_zy + } + } + + pub fn verify_s_prime_commitment( + n: usize, randomness: & Vec, challenges: & Vec, commitments: &Vec, @@ -477,6 +394,82 @@ impl PermutationArgument { assert_eq!(randomness.len(), 2); assert_eq!(challenges.len(), commitments.len()); + // let g = srs.g_positive_x[0]; + + // let h_alpha_x_precomp = srs.h_positive_x_alpha[1].prepare(); + + // let h_alpha_precomp = srs.h_positive_x_alpha[0].prepare(); + + // let mut h_prep = srs.h_positive_x[0]; + // h_prep.negate(); + // let h_prep = h_prep.prepare(); + + // let value = proof.v_zy; + // let g_v = g.mul(value.into_repr()); + + // { + + // let mut minus_z_prime = z_prime; + // minus_z_prime.negate(); + + // let e_z = proof.e_opening.mul(minus_z_prime.into_repr()); + + // let mut h_alpha_term = e_z; + + // h_alpha_term.add_assign(&g_v); + + // let h_alpha_x_term = proof.e_opening; + + // let s_r = multiexp( + // commitments.iter(), + // challenges.iter() + // ).into_affine(); + + // let h_term = s_r; + + // let valid = E::final_exponentiation(&E::miller_loop(&[ + // (&h_alpha_x_term.prepare(), &h_alpha_x_precomp), + // (&h_alpha_term.into_affine().prepare(), &h_alpha_precomp), + // (&h_term.prepare(), &h_prep), + // ])).unwrap() == E::Fqk::one(); + + // if !valid { + // return false; + // } + // } + // { + // let mut minus_yz = z_prime; + // minus_yz.mul_assign(&y); + // minus_yz.negate(); + + // let f_yz = proof.f_opening.mul(minus_yz.into_repr()); + + // let p2_r = multiexp( + // specialized_srs.p_2.iter(), + // challenges.iter() + // ).into_affine(); + + // let mut h_alpha_term = f_yz; + + // h_alpha_term.add_assign(&g_v); + + // let h_alpha_x_term = proof.f_opening; + + // let h_term = p2_r; + + // let valid = E::final_exponentiation(&E::miller_loop(&[ + // (&h_alpha_x_term.prepare(), &h_alpha_x_precomp), + // (&h_alpha_term.into_affine().prepare(), &h_alpha_precomp), + // (&h_term.prepare(), &h_prep), + // ])).unwrap() == E::Fqk::one(); + + // if !valid { + // return false; + // } + // } + + // true + // e(E,hαx)e(E−z′,hα) = e(􏰗Mj=1Sj′rj,h)e(g−v,hα) // e(F,hαx)e(F−yz′,hα) = e(􏰗Mj=1P2jrj,h)e(g−v,hα) @@ -545,217 +538,129 @@ impl PermutationArgument { ])).unwrap() == E::Fqk::one() } - // pub fn verify( - // n: usize, - // randomness: & Vec, - // a_zy: & Vec, - // challenges: &Vec, - // t_commitment: E::G1Affine, - // commitments: &Vec<(E::G1Affine, E::Fr)>, - // proof: &GrandProductProof, - // y: E::Fr, - // z: E::Fr, - // srs: &SRS - // ) -> bool { - // assert_eq!(randomness.len(), 3); - // assert_eq!(a_zy.len(), challenges.len()); - // assert_eq!(commitments.len(), challenges.len()); + pub fn verify( + s_commitments: &Vec, + proof: &Proof, + z: E::Fr, + srs: &SRS + ) -> bool { - // let d = srs.d; + let g = srs.g_positive_x[0]; - // let g = srs.g_positive_x[0]; + let h_alpha_x_precomp = srs.h_positive_x_alpha[1].prepare(); - // let h_alpha_x_precomp = srs.h_positive_x_alpha[1].prepare(); + let h_alpha_precomp = srs.h_positive_x_alpha[0].prepare(); - // let h_alpha_precomp = srs.h_positive_x_alpha[0].prepare(); + let mut h_prep = srs.h_positive_x[0]; + h_prep.negate(); + let h_prep = h_prep.prepare(); - // let mut h_prep = srs.h_positive_x[0]; - // h_prep.negate(); - // let h_prep = h_prep.prepare(); + let mut minus_z = z; + minus_z.negate(); - // // first re-calculate cj and t(z,y) + let opening_z = proof.s_opening.mul(minus_z.into_repr()); - // let mut yz = y; - // yz.mul_assign(&z); + let mut h_alpha_term = opening_z; + let g_s = g.mul(proof.s_zy.into_repr()); - // let z_inv = z.inverse().unwrap(); + h_alpha_term.add_assign(&g_s); - // let mut t_zy = E::Fr::zero(); - // t_zy.add_assign(&proof.e_zinv); - // t_zy.sub_assign(&proof.f_y); + let h_alpha_x_term = proof.s_opening; - // let mut commitments_points = vec![]; - // let mut rc_vec = vec![]; - // let mut ry_vec = vec![]; + let mut s = E::G1::zero(); + for p in s_commitments { + s.add_assign_mixed(&p); + } - // for ((r, commitment), a) in challenges.iter() - // .zip(commitments.iter()) - // .zip(a_zy.iter()) { - // let (c, v) = commitment; - // commitments_points.push(c.clone()); - - // // cj = ((aj + vj(yz)n+1)y + zn+2 + zn+1y − z2n+2y)z−1 - // let mut c_zy = yz.pow([(n + 1) as u64]); - // c_zy.mul_assign(v); - // c_zy.add_assign(a); - // c_zy.mul_assign(&y); + let h_term = s.into_affine(); - // let mut z_n_plus_1 = z.pow([(n + 1) as u64]); - - // let mut z_n_plus_2 = z_n_plus_1; - // z_n_plus_2.mul_assign(&z); - - // let mut z_2n_plus_2 = z_n_plus_1; - // z_2n_plus_2.square(); - // z_2n_plus_2.mul_assign(&y); - - // z_n_plus_1.mul_assign(&y); - - // c_zy.add_assign(&z_n_plus_1); - // c_zy.add_assign(&z_n_plus_2); - // c_zy.sub_assign(&z_2n_plus_2); - - // c_zy.mul_assign(&z_inv); - - // let mut rc = c_zy; - // rc.mul_assign(&r); - // rc_vec.push(rc); - - // let mut ry = y; - // ry.mul_assign(&r); - // ry_vec.push(ry); - - // let mut val = rc; - // val.sub_assign(r); - // t_zy.add_assign(&val); - // } - - // let c_rc = multiexp( - // commitments_points.iter(), - // rc_vec.iter(), - // ).into_affine(); - - // let c_ry = multiexp( - // commitments_points.iter(), - // ry_vec.iter(), - // ).into_affine(); - - // let mut minus_y = y; - // minus_y.negate(); - - // let mut f_y = proof.f_opening.mul(minus_y.into_repr()); - // let g_f = g.mul(proof.f_y.into_repr()); - // f_y.add_assign(&g_f); - - // let mut minus_z = z; - // minus_z.negate(); - - // let mut t_z = proof.t_opening.mul(minus_z.into_repr()); - // let g_tzy = g.mul(t_zy.into_repr()); - // t_z.add_assign(&g_tzy); - - // let mut minus_z_inv = z_inv; - // minus_z_inv.negate(); - - // let mut e_z_inv = proof.e_opening.mul(minus_z_inv.into_repr()); - // let g_e = g.mul(proof.e_zinv.into_repr()); - // e_z_inv.add_assign(&g_e); - - // let h_alpha_term = multiexp( - // vec![e_z_inv.into_affine(), f_y.into_affine(), t_z.into_affine()].iter(), - // randomness.iter(), - // ).into_affine(); - - // let h_alpha_x_term = multiexp( - // Some(proof.e_opening).iter() - // .chain_ext(Some(proof.f_opening).iter()) - // .chain_ext(Some(proof.t_opening).iter()), - // randomness.iter(), - // ).into_affine(); - - - // let h_term = multiexp( - // Some(c_rc).iter() - // .chain_ext(Some(c_ry).iter()) - // .chain_ext(Some(t_commitment).iter()), - // randomness.iter(), - // ).into_affine(); - - // E::final_exponentiation(&E::miller_loop(&[ - // (&h_alpha_x_term.prepare(), &h_alpha_x_precomp), - // (&h_alpha_term.prepare(), &h_alpha_precomp), - // (&h_term.prepare(), &h_prep), - // ])).unwrap() == E::Fqk::one() - - // } + E::final_exponentiation(&E::miller_loop(&[ + (&h_alpha_x_term.prepare(), &h_alpha_x_precomp), + (&h_alpha_term.into_affine().prepare(), &h_alpha_precomp), + (&h_term.prepare(), &h_prep), + ])).unwrap() == E::Fqk::one() + } } -// #[test] -// fn test_grand_product_argument() { -// use pairing::bls12_381::{Fr, G1Affine, G1, Bls12}; -// use rand::{XorShiftRng, SeedableRng, Rand, Rng}; -// use crate::sonic::srs::SRS; +#[test] +fn test_permutation_argument() { + use pairing::bls12_381::{Fr, G1Affine, G1, Bls12}; + use rand::{XorShiftRng, SeedableRng, Rand, Rng}; + use crate::sonic::srs::SRS; -// let srs_x = Fr::from_str("23923").unwrap(); -// let srs_alpha = Fr::from_str("23728792").unwrap(); -// let srs = SRS::::dummy(830564, srs_x, srs_alpha); + let srs_x = Fr::from_str("23923").unwrap(); + let srs_alpha = Fr::from_str("23728792").unwrap(); + let srs = SRS::::dummy(830564, srs_x, srs_alpha); -// let n: usize = 1 << 8; -// let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); -// let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::>(); -// let mut permutation = coeffs.clone(); -// rng.shuffle(&mut permutation); + let n: usize = 1 << 1; + let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); + // let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::>(); + // let mut permutation = (0..n).collect::>(); + // rng.shuffle(&mut permutation); -// let a_commitment = multiexp(srs.g_positive_x_alpha[0..n].iter(), coeffs.iter()).into_affine(); -// let b_commitment = multiexp(srs.g_positive_x_alpha[0..n].iter(), permutation.iter()).into_affine(); + let coeffs = vec![Fr::from_str("3").unwrap(), Fr::from_str("4").unwrap()]; + let permutation = vec![2, 1]; -// let mut argument = GrandProductArgument::new(vec![(coeffs, permutation)]); + let coeffs = vec![coeffs]; + let permutations = vec![permutation]; -// let commitments_and_v_values = argument.commit_to_individual_c_polynomials(&srs); + let specialized_srs = PermutationArgument::make_specialized_srs(&coeffs, &permutations, &srs); -// assert_eq!(commitments_and_v_values.len(), 1); + let mut argument = PermutationArgument::new(coeffs, permutations); -// let y : Fr = rng.gen(); + let y : Fr = rng.gen(); + let y : Fr = Fr::one(); + let y : Fr = Fr::from_str("2").unwrap(); -// let challenges = (0..1).map(|_| Fr::rand(rng)).collect::>(); + // let challenges = (0..1).map(|_| Fr::rand(rng)).collect::>(); -// let t_commitment = argument.commit_to_t_polynomial(&challenges, y, &srs); + let challenges = vec![Fr::one()]; -// let z : Fr = rng.gen(); + let commitments = argument.commit(y, &srs); + let mut s_commitments = vec![]; + let mut s_prime_commitments = vec![]; + for (s, s_prime) in commitments.into_iter() { + s_commitments.push(s); + s_prime_commitments.push(s_prime); + } -// let grand_product_openings = argument.open_commitments_for_grand_product(y, z, &srs); + let z_prime : Fr = rng.gen(); + let z_prime : Fr = Fr::one(); -// let randomness = (0..1).map(|_| Fr::rand(rng)).collect::>(); + let opening = argument.open_commitments_to_s_prime(&challenges, y, z_prime, &srs); -// let valid = GrandProductArgument::verify_ab_commitment(n, -// &randomness, -// &vec![a_commitment], -// &vec![b_commitment], -// &grand_product_openings, -// y, -// z, -// &srs); + let randomness = (0..2).map(|_| Fr::rand(rng)).collect::>(); -// assert!(valid, "grand product commitments should be valid"); + let valid = PermutationArgument::verify_s_prime_commitment(n, + &randomness, + &challenges, + &s_prime_commitments, + &opening, + y, + z_prime, + &specialized_srs, + &srs); -// let a_zy: Vec = grand_product_openings.iter().map(|el| el.0.clone()).collect(); + assert!(valid, "s' commitment must be valid"); -// let proof = argument.make_argument(&a_zy, &challenges, y, z, &srs); + let beta : Fr = rng.gen(); + let gamma : Fr = rng.gen(); -// let randomness = (0..3).map(|_| Fr::rand(rng)).collect::>(); + let grand_product_challenges = (0..1).map(|_| Fr::rand(rng)).collect::>(); + let wellformed_challenges = (0..2).map(|_| Fr::rand(rng)).collect::>(); -// let valid = GrandProductArgument::verify( -// n, -// &randomness, -// &a_zy, -// &challenges, -// t_commitment, -// &commitments_and_v_values, -// &proof, -// y, -// z, -// &srs); + let z : Fr = rng.gen(); -// assert!(valid, "t commitment should be valid"); -// } + let proof = argument.make_argument( + beta, + gamma, + & grand_product_challenges, + & wellformed_challenges, + y, z, + &specialized_srs, &srs); + + let valid = PermutationArgument::verify(&s_commitments, &proof, z, &srs); + + assert!(valid, "permutation argument must be valid"); +} diff --git a/src/sonic/unhelped/wellformed_argument.rs b/src/sonic/unhelped/wellformed_argument.rs index 4eb38a0..5e707f1 100644 --- a/src/sonic/unhelped/wellformed_argument.rs +++ b/src/sonic/unhelped/wellformed_argument.rs @@ -52,6 +52,7 @@ impl WellformednessArgument { } pub fn make_argument(self, challenges: Vec, srs: &SRS) -> WellformednessProof { + assert_eq!(challenges.len(), self.polynomials.len()); let mut polynomials = self.polynomials; let mut challenges = challenges; @@ -165,7 +166,7 @@ fn test_argument_soundness() { let srs_alpha = Fr::from_str("23728792").unwrap(); let srs = SRS::::dummy(830564, srs_x, srs_alpha); - let n: usize = 1 << 16; + let n: usize = 1 << 8; let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let coeffs = (0..n).map(|_| Fr::rand(rng)).collect::>(); From 810bb92ceee8436b33a077ae7eefc590405947d6 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Mon, 4 Mar 2019 23:04:39 +0300 Subject: [PATCH 39/42] start transitioning to versioning + more elegant re-exports --- Cargo.toml | 28 +- src/cs.rs | 411 +++++++++++++++++ src/domain.rs | 127 +++++- src/gm17/generator.rs | 8 +- src/gm17/mod.rs | 6 +- src/gm17/tests/mod.rs | 4 +- src/groth16/generator.rs | 8 +- src/groth16/mod.rs | 8 +- src/groth16/prover.rs | 15 +- src/groth16/tests/mod.rs | 4 +- src/groth16/verifier.rs | 4 +- src/group.rs | 2 +- src/lib.rs | 447 +------------------ src/multicore.rs | 13 +- src/{parallel_multiexp.rs => multiexp.rs} | 49 +- src/parallel_fft.rs | 143 ------ src/serial_fft.rs | 0 src/serial_multiexp.rs | 0 src/singlecore.rs | 94 ++++ src/sonic/cs/lc.rs | 2 +- src/sonic/cs/mod.rs | 3 +- src/sonic/helped/adapted_helper.rs | 2 +- src/sonic/helped/adapted_prover.rs | 4 +- src/sonic/helped/adapted_verifier.rs | 4 +- src/sonic/helped/batch.rs | 2 +- src/sonic/helped/generator.rs | 6 +- src/sonic/helped/helper.rs | 2 +- src/sonic/helped/mod.rs | 3 +- src/sonic/helped/parameters.rs | 8 +- src/sonic/helped/poly.rs | 2 +- src/sonic/helped/prover.rs | 8 +- src/sonic/helped/verifier.rs | 2 +- src/sonic/mod.rs | 1 - src/sonic/sonic/adaptor.rs | 3 +- src/sonic/sonic/mod.rs | 1 - src/sonic/srs/mod.rs | 1 - src/sonic/srs/srs.rs | 2 +- src/sonic/transcript/mod.rs | 3 +- src/sonic/unhelped/grand_product_argument.rs | 2 +- src/sonic/unhelped/permutation_argument.rs | 2 +- src/sonic/unhelped/s2_proof.rs | 2 +- src/sonic/unhelped/wellformed_argument.rs | 2 +- src/sonic/util.rs | 4 +- src/source.rs | 2 +- src/tests/dummy_engine.rs | 2 +- src/tests/mod.rs | 4 +- tests/mimc.rs | 7 +- 47 files changed, 758 insertions(+), 699 deletions(-) create mode 100644 src/cs.rs rename src/{parallel_multiexp.rs => multiexp.rs} (93%) delete mode 100644 src/parallel_fft.rs delete mode 100644 src/serial_fft.rs delete mode 100644 src/serial_multiexp.rs create mode 100644 src/singlecore.rs diff --git a/Cargo.toml b/Cargo.toml index 761c6c5..43d1f21 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,8 @@ homepage = "https://github.com/matterinc/bellman" license = "MIT/Apache-2.0" name = "bellman" repository = "https://github.com/matterinc/bellman" -version = "0.1.3" +version = "0.2.0" +edition = "2018" [lib] crate-type = ["cdylib", "lib", "staticlib"] @@ -15,20 +16,25 @@ crate-type = ["cdylib", "lib", "staticlib"] rand = "0.4" bit-vec = "0.4.4" futures = "0.1" -futures-cpupool = "0.1" -num_cpus = "1" -crossbeam = "0.7.1" -pairing = { git = 'https://github.com/matterinc/pairing' } -byteorder = "1" -ff = { git = 'https://github.com/matterinc/ff', features = ["derive"] } -tiny-keccak = "1.4.2" +#pairing = { git = 'https://github.com/matterinc/pairing', tag = "0.16.2" } +pairing = { path = "../pairing" } +byteorder = "1" + +futures-cpupool = {version = "0.1", optional = true} +num_cpus = {version = "1", optional = true} +crossbeam = {version = "0.7.1", optional = true} + +tiny-keccak = {version = "1.4.2", optional = true} [dependencies.blake2-rfc] git = "https://github.com/gtank/blake2-rfc" rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9" [features] -default = [] - -singlecore = [] +default = ["multicore", "gm17", "sonic"] +#default = ["singlecore"] +multicore = ["futures-cpupool", "num_cpus", "crossbeam"] +sonic = ["tiny-keccak"] +gm17 = [] +singlecore = ["futures-cpupool"] diff --git a/src/cs.rs b/src/cs.rs new file mode 100644 index 0000000..a84e606 --- /dev/null +++ b/src/cs.rs @@ -0,0 +1,411 @@ +use pairing::{Engine}; +use pairing::ff::Field; + +use std::ops::{Add, Sub}; +use std::fmt; +use std::error::Error; +use std::io; +use std::marker::PhantomData; + +/// Computations are expressed in terms of arithmetic circuits, in particular +/// rank-1 quadratic constraint systems. The `Circuit` trait represents a +/// circuit that can be synthesized. The `synthesize` method is called during +/// CRS generation and during proving. +pub trait Circuit { + /// Synthesize the circuit into a rank-1 quadratic constraint system + fn synthesize>( + self, + cs: &mut CS + ) -> Result<(), SynthesisError>; +} + +/// Represents a variable in our constraint system. +#[derive(Copy, Clone, Debug)] +pub struct Variable(pub(crate) Index); + +impl Variable { + /// This constructs a variable with an arbitrary index. + /// Circuit implementations are not recommended to use this. + pub fn new_unchecked(idx: Index) -> Variable { + Variable(idx) + } + + /// This returns the index underlying the variable. + /// Circuit implementations are not recommended to use this. + pub fn get_unchecked(&self) -> Index { + self.0 + } +} + +/// Represents the index of either an input variable or +/// auxillary variable. +#[derive(Copy, Clone, PartialEq, Debug)] +pub enum Index { + Input(usize), + Aux(usize) +} + +/// This represents a linear combination of some variables, with coefficients +/// in the scalar field of a pairing-friendly elliptic curve group. +#[derive(Clone)] +pub struct LinearCombination(pub(crate) Vec<(Variable, E::Fr)>); + +impl AsRef<[(Variable, E::Fr)]> for LinearCombination { + fn as_ref(&self) -> &[(Variable, E::Fr)] { + &self.0 + } +} + +impl LinearCombination { + pub fn zero() -> LinearCombination { + LinearCombination(vec![]) + } +} + +impl Add<(E::Fr, Variable)> for LinearCombination { + type Output = LinearCombination; + + fn add(mut self, (coeff, var): (E::Fr, Variable)) -> LinearCombination { + self.0.push((var, coeff)); + + self + } +} + +impl Sub<(E::Fr, Variable)> for LinearCombination { + type Output = LinearCombination; + + fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination { + coeff.negate(); + + self + (coeff, var) + } +} + +impl Add for LinearCombination { + type Output = LinearCombination; + + fn add(self, other: Variable) -> LinearCombination { + self + (E::Fr::one(), other) + } +} + +impl Sub for LinearCombination { + type Output = LinearCombination; + + fn sub(self, other: Variable) -> LinearCombination { + self - (E::Fr::one(), other) + } +} + +impl<'a, E: Engine> Add<&'a LinearCombination> for LinearCombination { + type Output = LinearCombination; + + fn add(mut self, other: &'a LinearCombination) -> LinearCombination { + for s in &other.0 { + self = self + (s.1, s.0); + } + + self + } +} + +impl<'a, E: Engine> Sub<&'a LinearCombination> for LinearCombination { + type Output = LinearCombination; + + fn sub(mut self, other: &'a LinearCombination) -> LinearCombination { + for s in &other.0 { + self = self - (s.1, s.0); + } + + self + } +} + +impl<'a, E: Engine> Add<(E::Fr, &'a LinearCombination)> for LinearCombination { + type Output = LinearCombination; + + fn add(mut self, (coeff, other): (E::Fr, &'a LinearCombination)) -> LinearCombination { + for s in &other.0 { + let mut tmp = s.1; + tmp.mul_assign(&coeff); + self = self + (tmp, s.0); + } + + self + } +} + +impl<'a, E: Engine> Sub<(E::Fr, &'a LinearCombination)> for LinearCombination { + type Output = LinearCombination; + + fn sub(mut self, (coeff, other): (E::Fr, &'a LinearCombination)) -> LinearCombination { + for s in &other.0 { + let mut tmp = s.1; + tmp.mul_assign(&coeff); + self = self - (tmp, s.0); + } + + self + } +} + +/// This is an error that could occur during circuit synthesis contexts, +/// such as CRS generation, proving or verification. +#[derive(Debug)] +pub enum SynthesisError { + /// During synthesis, we lacked knowledge of a variable assignment. + AssignmentMissing, + /// During synthesis, we divided by zero. + DivisionByZero, + /// During synthesis, we constructed an unsatisfiable constraint system. + Unsatisfiable, + /// During synthesis, our polynomials ended up being too high of degree + PolynomialDegreeTooLarge, + /// During proof generation, we encountered an identity in the CRS + UnexpectedIdentity, + /// During proof generation, we encountered an I/O error with the CRS + IoError(io::Error), + /// During verification, our verifying key was malformed. + MalformedVerifyingKey, + /// During CRS generation, we observed an unconstrained auxillary variable + UnconstrainedVariable +} + +impl From for SynthesisError { + fn from(e: io::Error) -> SynthesisError { + SynthesisError::IoError(e) + } +} + +impl Error for SynthesisError { + fn description(&self) -> &str { + match *self { + SynthesisError::AssignmentMissing => "an assignment for a variable could not be computed", + SynthesisError::DivisionByZero => "division by zero", + SynthesisError::Unsatisfiable => "unsatisfiable constraint system", + SynthesisError::PolynomialDegreeTooLarge => "polynomial degree is too large", + SynthesisError::UnexpectedIdentity => "encountered an identity element in the CRS", + SynthesisError::IoError(_) => "encountered an I/O error", + SynthesisError::MalformedVerifyingKey => "malformed verifying key", + SynthesisError::UnconstrainedVariable => "auxillary variable was unconstrained" + } + } +} + +impl fmt::Display for SynthesisError { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + if let &SynthesisError::IoError(ref e) = self { + write!(f, "I/O error: ")?; + e.fmt(f) + } else { + write!(f, "{}", self.description()) + } + } +} + +/// Represents a constraint system which can have new variables +/// allocated and constrains between them formed. +pub trait ConstraintSystem: Sized { + /// Represents the type of the "root" of this constraint system + /// so that nested namespaces can minimize indirection. + type Root: ConstraintSystem; + + /// Return the "one" input variable + fn one() -> Variable { + Variable::new_unchecked(Index::Input(0)) + } + + /// Allocate a private variable in the constraint system. The provided function is used to + /// determine the assignment of the variable. The given `annotation` function is invoked + /// in testing contexts in order to derive a unique name for this variable in the current + /// namespace. + fn alloc( + &mut self, + annotation: A, + f: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into; + + /// Allocate a public variable in the constraint system. The provided function is used to + /// determine the assignment of the variable. + fn alloc_input( + &mut self, + annotation: A, + f: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into; + + /// Enforce that `A` * `B` = `C`. The `annotation` function is invoked in testing contexts + /// in order to derive a unique name for the constraint in the current namespace. + fn enforce( + &mut self, + annotation: 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; + + /// Create a new (sub)namespace and enter into it. Not intended + /// for downstream use; use `namespace` instead. + fn push_namespace(&mut self, name_fn: N) + where NR: Into, N: FnOnce() -> NR; + + /// Exit out of the existing namespace. Not intended for + /// downstream use; use `namespace` instead. + fn pop_namespace(&mut self); + + /// Gets the "root" constraint system, bypassing the namespacing. + /// Not intended for downstream use; use `namespace` instead. + fn get_root(&mut self) -> &mut Self::Root; + + /// Begin a namespace for this constraint system. + fn namespace<'a, NR, N>( + &'a mut self, + name_fn: N + ) -> Namespace<'a, E, Self::Root> + where NR: Into, N: FnOnce() -> NR + { + self.get_root().push_namespace(name_fn); + + Namespace(self.get_root(), PhantomData) + } +} + +/// This is a "namespaced" constraint system which borrows a constraint system (pushing +/// a namespace context) and, when dropped, pops out of the namespace context. +pub struct Namespace<'a, E: Engine, CS: ConstraintSystem + 'a>(&'a mut CS, PhantomData); + +impl<'cs, E: Engine, CS: ConstraintSystem> ConstraintSystem for Namespace<'cs, E, CS> { + type Root = CS::Root; + + fn one() -> Variable { + CS::one() + } + + fn alloc( + &mut self, + annotation: A, + f: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into + { + self.0.alloc(annotation, f) + } + + fn alloc_input( + &mut self, + annotation: A, + f: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into + { + self.0.alloc_input(annotation, f) + } + + fn enforce( + &mut self, + annotation: 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 + { + self.0.enforce(annotation, a, b, c) + } + + // Downstream users who use `namespace` will never interact with these + // functions and they will never be invoked because the namespace is + // never a root constraint system. + + fn push_namespace(&mut self, _: N) + where NR: Into, N: FnOnce() -> NR + { + panic!("only the root's push_namespace should be called"); + } + + fn pop_namespace(&mut self) + { + panic!("only the root's pop_namespace should be called"); + } + + fn get_root(&mut self) -> &mut Self::Root + { + self.0.get_root() + } +} + +impl<'a, E: Engine, CS: ConstraintSystem> Drop for Namespace<'a, E, CS> { + fn drop(&mut self) { + self.get_root().pop_namespace() + } +} + +/// Convenience implementation of ConstraintSystem for mutable references to +/// constraint systems. +impl<'cs, E: Engine, CS: ConstraintSystem> ConstraintSystem for &'cs mut CS { + type Root = CS::Root; + + fn one() -> Variable { + CS::one() + } + + fn alloc( + &mut self, + annotation: A, + f: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into + { + (**self).alloc(annotation, f) + } + + fn alloc_input( + &mut self, + annotation: A, + f: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into + { + (**self).alloc_input(annotation, f) + } + + fn enforce( + &mut self, + annotation: 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 + { + (**self).enforce(annotation, a, b, c) + } + + fn push_namespace(&mut self, name_fn: N) + where NR: Into, N: FnOnce() -> NR + { + (**self).push_namespace(name_fn) + } + + fn pop_namespace(&mut self) + { + (**self).pop_namespace() + } + + fn get_root(&mut self) -> &mut Self::Root + { + (**self).get_root() + } +} \ No newline at end of file diff --git a/src/domain.rs b/src/domain.rs index 1d0203a..9622219 100644 --- a/src/domain.rs +++ b/src/domain.rs @@ -15,7 +15,7 @@ use pairing::{ CurveProjective }; -use ff::{ +use pairing::ff::{ Field, PrimeField }; @@ -24,13 +24,9 @@ use super::{ SynthesisError }; - -use super::multicore::Worker; +use super::worker::Worker; pub use super::group::*; -#[cfg(not(feature = "singlecore"))] -use super::parallel_fft::*; - pub struct EvaluationDomain> { coeffs: Vec, exp: u32, @@ -55,7 +51,7 @@ impl> EvaluationDomain { pub fn from_coeffs(mut coeffs: Vec) -> Result, SynthesisError> { - use ff::PrimeField; + use pairing::ff::PrimeField; // Compute the size of our evaluation domain let coeffs_len = coeffs.len(); @@ -105,7 +101,7 @@ impl> EvaluationDomain { // this one does expect coefficients to be smaller than `num_roots_of_unity/2` as we expect multiplication pub fn from_coeffs_into_sized(mut coeffs: Vec, size: usize) -> Result, SynthesisError> { - use ff::PrimeField; + use pairing::ff::PrimeField; // Compute the size of our evaluation domain assert!(size >= coeffs.len()); @@ -264,6 +260,121 @@ impl> EvaluationDomain { } } +pub(crate) fn best_fft>(a: &mut [T], worker: &Worker, omega: &E::Fr, log_n: u32) +{ + let log_cpus = worker.log_num_cpus(); + + if log_n <= log_cpus { + serial_fft(a, omega, log_n); + } else { + parallel_fft(a, worker, omega, log_n, log_cpus); + } +} + +pub(crate) fn serial_fft>(a: &mut [T], omega: &E::Fr, log_n: u32) +{ + fn bitreverse(mut n: u32, l: u32) -> u32 { + let mut r = 0; + for _ in 0..l { + r = (r << 1) | (n & 1); + n >>= 1; + } + r + } + + let n = a.len() as u32; + assert_eq!(n, 1 << log_n); + + for k in 0..n { + let rk = bitreverse(k, log_n); + if k < rk { + a.swap(rk as usize, k as usize); + } + } + + let mut m = 1; + for _ in 0..log_n { + let w_m = omega.pow(&[(n / (2*m)) as u64]); + + let mut k = 0; + while k < n { + let mut w = E::Fr::one(); + for j in 0..m { + let mut t = a[(k+j+m) as usize]; + t.group_mul_assign(&w); + let mut tmp = a[(k+j) as usize]; + tmp.group_sub_assign(&t); + a[(k+j+m) as usize] = tmp; + a[(k+j) as usize].group_add_assign(&t); + w.mul_assign(&w_m); + } + + k += 2*m; + } + + m *= 2; + } +} + +pub(crate) fn parallel_fft>( + a: &mut [T], + worker: &Worker, + omega: &E::Fr, + log_n: u32, + log_cpus: u32 +) +{ + assert!(log_n >= log_cpus); + + let num_cpus = 1 << log_cpus; + let log_new_n = log_n - log_cpus; + let mut tmp = vec![vec![T::group_zero(); 1 << log_new_n]; num_cpus]; + let new_omega = omega.pow(&[num_cpus as u64]); + + worker.scope(0, |scope, _| { + let a = &*a; + + for (j, tmp) in tmp.iter_mut().enumerate() { + scope.spawn(move |_| { + // Shuffle into a sub-FFT + let omega_j = omega.pow(&[j as u64]); + let omega_step = omega.pow(&[(j as u64) << log_new_n]); + + let mut elt = E::Fr::one(); + for i in 0..(1 << log_new_n) { + for s in 0..num_cpus { + let idx = (i + (s << log_new_n)) % (1 << log_n); + let mut t = a[idx]; + t.group_mul_assign(&elt); + tmp[i].group_add_assign(&t); + elt.mul_assign(&omega_step); + } + elt.mul_assign(&omega_j); + } + + // Perform sub-FFT + serial_fft(tmp, &new_omega, log_new_n); + }); + } + }); + + // TODO: does this hurt or help? + worker.scope(a.len(), |scope, chunk| { + let tmp = &tmp; + + for (idx, a) in a.chunks_mut(chunk).enumerate() { + scope.spawn(move |_| { + let mut idx = idx * chunk; + let mask = (1 << log_cpus) - 1; + for a in a { + *a = tmp[idx & mask][idx >> log_cpus]; + idx += 1; + } + }); + } + }); +} + // Test multiplying various (low degree) polynomials together and // comparing with naive evaluations. #[test] diff --git a/src/gm17/generator.rs b/src/gm17/generator.rs index 059a041..320ab07 100644 --- a/src/gm17/generator.rs +++ b/src/gm17/generator.rs @@ -11,7 +11,7 @@ use pairing::{ CurveAffine }; -use ff::{ +use pairing::ff::{ PrimeField, Field }; @@ -21,7 +21,7 @@ use super::{ VerifyingKey }; -use ::{ +use crate::{ SynthesisError, Circuit, ConstraintSystem, @@ -30,12 +30,12 @@ use ::{ Index }; -use ::domain::{ +use crate::domain::{ EvaluationDomain, Scalar }; -use ::multicore::{ +use crate::worker::{ Worker }; diff --git a/src/gm17/mod.rs b/src/gm17/mod.rs index b8c5b7f..aee5de6 100644 --- a/src/gm17/mod.rs +++ b/src/gm17/mod.rs @@ -4,11 +4,11 @@ use pairing::{ EncodedPoint }; -use ::{ +use crate::{ SynthesisError }; -use multiexp::SourceBuilder; +use crate::source::SourceBuilder; use std::io::{self, Read, Write}; use std::sync::Arc; use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; @@ -474,7 +474,7 @@ impl PartialEq for Parameters { // use {Circuit, SynthesisError, ConstraintSystem}; // use rand::{Rand, thread_rng}; -// use ff::{Field}; +// use pairing::ff::{Field}; // use pairing::bls12_381::{Bls12, Fr}; // #[test] diff --git a/src/gm17/tests/mod.rs b/src/gm17/tests/mod.rs index 4440b19..ee34ec6 100644 --- a/src/gm17/tests/mod.rs +++ b/src/gm17/tests/mod.rs @@ -2,7 +2,7 @@ use pairing::{ Engine }; -use ff:: { +use pairing::ff:: { Field, PrimeField, }; @@ -12,7 +12,7 @@ use super::super::tests::XORDemo; use std::marker::PhantomData; -use ::{ +use crate::{ Circuit, ConstraintSystem, SynthesisError diff --git a/src/groth16/generator.rs b/src/groth16/generator.rs index 0d597a6..3d4cb1f 100644 --- a/src/groth16/generator.rs +++ b/src/groth16/generator.rs @@ -11,7 +11,7 @@ use pairing::{ CurveAffine }; -use ff::{ +use pairing::ff::{ PrimeField, Field }; @@ -21,7 +21,7 @@ use super::{ VerifyingKey }; -use ::{ +use crate::{ SynthesisError, Circuit, ConstraintSystem, @@ -30,12 +30,12 @@ use ::{ Index }; -use ::domain::{ +use crate::domain::{ EvaluationDomain, Scalar }; -use ::multicore::{ +use crate::worker::{ Worker }; diff --git a/src/groth16/mod.rs b/src/groth16/mod.rs index 7dcc8ab..14fb642 100644 --- a/src/groth16/mod.rs +++ b/src/groth16/mod.rs @@ -4,11 +4,11 @@ use pairing::{ EncodedPoint }; -use ::{ +use crate::{ SynthesisError }; -use multiexp::SourceBuilder; +use crate::source::SourceBuilder; use std::io::{self, Read, Write}; use std::sync::Arc; use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; @@ -484,10 +484,10 @@ impl<'a, E: Engine> ParameterSource for &'a Parameters { #[cfg(test)] mod test_with_bls12_381 { use super::*; - use {Circuit, SynthesisError, ConstraintSystem}; + use crate::{Circuit, SynthesisError, ConstraintSystem}; use rand::{Rand, thread_rng}; - use ff::{Field}; + use pairing::ff::{Field}; use pairing::bls12_381::{Bls12, Fr}; #[test] diff --git a/src/groth16/prover.rs b/src/groth16/prover.rs index 98f2b62..f66e251 100644 --- a/src/groth16/prover.rs +++ b/src/groth16/prover.rs @@ -12,7 +12,7 @@ use pairing::{ CurveAffine }; -use ff::{ +use pairing::ff::{ PrimeField, Field }; @@ -22,7 +22,7 @@ use super::{ Proof }; -use ::{ +use crate::{ SynthesisError, Circuit, ConstraintSystem, @@ -31,18 +31,19 @@ use ::{ Index }; -use ::domain::{ +use crate::domain::{ EvaluationDomain, Scalar }; -use ::multiexp::{ +use crate::source::{ DensityTracker, - FullDensity, - multiexp + FullDensity }; -use ::multicore::{ +use crate::multiexp::*; + +use crate::worker::{ Worker }; diff --git a/src/groth16/tests/mod.rs b/src/groth16/tests/mod.rs index 5a2bb2c..7442501 100644 --- a/src/groth16/tests/mod.rs +++ b/src/groth16/tests/mod.rs @@ -2,7 +2,7 @@ use pairing::{ Engine }; -use ff:: { +use pairing::ff:: { Field, PrimeField, }; @@ -12,7 +12,7 @@ use super::super::tests::XORDemo; use std::marker::PhantomData; -use ::{ +use crate::{ Circuit, ConstraintSystem, SynthesisError diff --git a/src/groth16/verifier.rs b/src/groth16/verifier.rs index 6d30915..ad1c210 100644 --- a/src/groth16/verifier.rs +++ b/src/groth16/verifier.rs @@ -4,7 +4,7 @@ use pairing::{ CurveAffine }; -use ff::{PrimeField}; +use pairing::ff::{PrimeField}; use super::{ Proof, @@ -12,7 +12,7 @@ use super::{ PreparedVerifyingKey }; -use ::{ +use crate::{ SynthesisError }; diff --git a/src/group.rs b/src/group.rs index 7b4fe2e..f66b776 100644 --- a/src/group.rs +++ b/src/group.rs @@ -3,7 +3,7 @@ use pairing::{ CurveProjective }; -use ff::{ +use pairing::ff::{ Field, PrimeField }; diff --git a/src/lib.rs b/src/lib.rs index e7f17e3..690219a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,458 +1,45 @@ #![allow(unused_imports)] -extern crate pairing; +extern crate pairing as pairing_import; extern crate rand; extern crate bit_vec; extern crate byteorder; -extern crate ff; - -#[cfg(not(feature = "singlecore"))] -extern crate num_cpus; -extern crate futures; -extern crate futures_cpupool; -extern crate crossbeam; pub mod domain; pub mod groth16; + +#[cfg(feature = "gm17")] pub mod gm17; +#[cfg(feature = "sonic")] pub mod sonic; mod group; mod source; - -#[cfg(not(feature = "singlecore"))] -mod parallel_fft; -mod multicore; -mod parallel_multiexp; - -#[cfg(feature = "singlecore")] -mod serial_fft; -mod serial_multiexp; +mod multiexp; #[cfg(test)] mod tests; -use pairing::{Engine}; -use ff::Field; +#[cfg(feature = "multicore")] +mod multicore; -use std::ops::{Add, Sub}; -use std::fmt; -use std::error::Error; -use std::io; -use std::marker::PhantomData; +#[cfg(feature = "singlecore")] +mod singlecore; -pub mod multiexp { - pub use source::*; - - #[cfg(not(feature = "singlecore"))] - pub use parallel_multiexp::*; +mod worker { + #[cfg(feature = "multicore")] + pub use crate::multicore::*; #[cfg(feature = "singlecore")] - pub use serial_multiexp::*; + pub use crate::singlecore::*; } -/// Computations are expressed in terms of arithmetic circuits, in particular -/// rank-1 quadratic constraint systems. The `Circuit` trait represents a -/// circuit that can be synthesized. The `synthesize` method is called during -/// CRS generation and during proving. -pub trait Circuit { - /// Synthesize the circuit into a rank-1 quadratic constraint system - fn synthesize>( - self, - cs: &mut CS - ) -> Result<(), SynthesisError>; +pub mod pairing { + pub use pairing_import::*; } -/// Represents a variable in our constraint system. -#[derive(Copy, Clone, Debug)] -pub struct Variable(Index); - -impl Variable { - /// This constructs a variable with an arbitrary index. - /// Circuit implementations are not recommended to use this. - pub fn new_unchecked(idx: Index) -> Variable { - Variable(idx) - } - - /// This returns the index underlying the variable. - /// Circuit implementations are not recommended to use this. - pub fn get_unchecked(&self) -> Index { - self.0 - } -} - -/// Represents the index of either an input variable or -/// auxillary variable. -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum Index { - Input(usize), - Aux(usize) -} - -/// This represents a linear combination of some variables, with coefficients -/// in the scalar field of a pairing-friendly elliptic curve group. -#[derive(Clone)] -pub struct LinearCombination(Vec<(Variable, E::Fr)>); - -impl AsRef<[(Variable, E::Fr)]> for LinearCombination { - fn as_ref(&self) -> &[(Variable, E::Fr)] { - &self.0 - } -} - -impl LinearCombination { - pub fn zero() -> LinearCombination { - LinearCombination(vec![]) - } -} - -impl Add<(E::Fr, Variable)> for LinearCombination { - type Output = LinearCombination; - - fn add(mut self, (coeff, var): (E::Fr, Variable)) -> LinearCombination { - self.0.push((var, coeff)); - - self - } -} - -impl Sub<(E::Fr, Variable)> for LinearCombination { - type Output = LinearCombination; - - fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination { - coeff.negate(); - - self + (coeff, var) - } -} - -impl Add for LinearCombination { - type Output = LinearCombination; - - fn add(self, other: Variable) -> LinearCombination { - self + (E::Fr::one(), other) - } -} - -impl Sub for LinearCombination { - type Output = LinearCombination; - - fn sub(self, other: Variable) -> LinearCombination { - self - (E::Fr::one(), other) - } -} - -impl<'a, E: Engine> Add<&'a LinearCombination> for LinearCombination { - type Output = LinearCombination; - - fn add(mut self, other: &'a LinearCombination) -> LinearCombination { - for s in &other.0 { - self = self + (s.1, s.0); - } - - self - } -} - -impl<'a, E: Engine> Sub<&'a LinearCombination> for LinearCombination { - type Output = LinearCombination; - - fn sub(mut self, other: &'a LinearCombination) -> LinearCombination { - for s in &other.0 { - self = self - (s.1, s.0); - } - - self - } -} - -impl<'a, E: Engine> Add<(E::Fr, &'a LinearCombination)> for LinearCombination { - type Output = LinearCombination; - - fn add(mut self, (coeff, other): (E::Fr, &'a LinearCombination)) -> LinearCombination { - for s in &other.0 { - let mut tmp = s.1; - tmp.mul_assign(&coeff); - self = self + (tmp, s.0); - } - - self - } -} - -impl<'a, E: Engine> Sub<(E::Fr, &'a LinearCombination)> for LinearCombination { - type Output = LinearCombination; - - fn sub(mut self, (coeff, other): (E::Fr, &'a LinearCombination)) -> LinearCombination { - for s in &other.0 { - let mut tmp = s.1; - tmp.mul_assign(&coeff); - self = self - (tmp, s.0); - } - - self - } -} - -/// This is an error that could occur during circuit synthesis contexts, -/// such as CRS generation, proving or verification. -#[derive(Debug)] -pub enum SynthesisError { - /// During synthesis, we lacked knowledge of a variable assignment. - AssignmentMissing, - /// During synthesis, we divided by zero. - DivisionByZero, - /// During synthesis, we constructed an unsatisfiable constraint system. - Unsatisfiable, - /// During synthesis, our polynomials ended up being too high of degree - PolynomialDegreeTooLarge, - /// During proof generation, we encountered an identity in the CRS - UnexpectedIdentity, - /// During proof generation, we encountered an I/O error with the CRS - IoError(io::Error), - /// During verification, our verifying key was malformed. - MalformedVerifyingKey, - /// During CRS generation, we observed an unconstrained auxillary variable - UnconstrainedVariable -} - -impl From for SynthesisError { - fn from(e: io::Error) -> SynthesisError { - SynthesisError::IoError(e) - } -} - -impl Error for SynthesisError { - fn description(&self) -> &str { - match *self { - SynthesisError::AssignmentMissing => "an assignment for a variable could not be computed", - SynthesisError::DivisionByZero => "division by zero", - SynthesisError::Unsatisfiable => "unsatisfiable constraint system", - SynthesisError::PolynomialDegreeTooLarge => "polynomial degree is too large", - SynthesisError::UnexpectedIdentity => "encountered an identity element in the CRS", - SynthesisError::IoError(_) => "encountered an I/O error", - SynthesisError::MalformedVerifyingKey => "malformed verifying key", - SynthesisError::UnconstrainedVariable => "auxillary variable was unconstrained" - } - } -} - -impl fmt::Display for SynthesisError { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - if let &SynthesisError::IoError(ref e) = self { - write!(f, "I/O error: ")?; - e.fmt(f) - } else { - write!(f, "{}", self.description()) - } - } -} - -/// Represents a constraint system which can have new variables -/// allocated and constrains between them formed. -pub trait ConstraintSystem: Sized { - /// Represents the type of the "root" of this constraint system - /// so that nested namespaces can minimize indirection. - type Root: ConstraintSystem; - - /// Return the "one" input variable - fn one() -> Variable { - Variable::new_unchecked(Index::Input(0)) - } - - /// Allocate a private variable in the constraint system. The provided function is used to - /// determine the assignment of the variable. The given `annotation` function is invoked - /// in testing contexts in order to derive a unique name for this variable in the current - /// namespace. - fn alloc( - &mut self, - annotation: A, - f: F - ) -> Result - where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into; - - /// Allocate a public variable in the constraint system. The provided function is used to - /// determine the assignment of the variable. - fn alloc_input( - &mut self, - annotation: A, - f: F - ) -> Result - where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into; - - /// Enforce that `A` * `B` = `C`. The `annotation` function is invoked in testing contexts - /// in order to derive a unique name for the constraint in the current namespace. - fn enforce( - &mut self, - annotation: 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; - - /// Create a new (sub)namespace and enter into it. Not intended - /// for downstream use; use `namespace` instead. - fn push_namespace(&mut self, name_fn: N) - where NR: Into, N: FnOnce() -> NR; - - /// Exit out of the existing namespace. Not intended for - /// downstream use; use `namespace` instead. - fn pop_namespace(&mut self); - - /// Gets the "root" constraint system, bypassing the namespacing. - /// Not intended for downstream use; use `namespace` instead. - fn get_root(&mut self) -> &mut Self::Root; - - /// Begin a namespace for this constraint system. - fn namespace<'a, NR, N>( - &'a mut self, - name_fn: N - ) -> Namespace<'a, E, Self::Root> - where NR: Into, N: FnOnce() -> NR - { - self.get_root().push_namespace(name_fn); - - Namespace(self.get_root(), PhantomData) - } -} - -/// This is a "namespaced" constraint system which borrows a constraint system (pushing -/// a namespace context) and, when dropped, pops out of the namespace context. -pub struct Namespace<'a, E: Engine, CS: ConstraintSystem + 'a>(&'a mut CS, PhantomData); - -impl<'cs, E: Engine, CS: ConstraintSystem> ConstraintSystem for Namespace<'cs, E, CS> { - type Root = CS::Root; - - fn one() -> Variable { - CS::one() - } - - fn alloc( - &mut self, - annotation: A, - f: F - ) -> Result - where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into - { - self.0.alloc(annotation, f) - } - - fn alloc_input( - &mut self, - annotation: A, - f: F - ) -> Result - where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into - { - self.0.alloc_input(annotation, f) - } - - fn enforce( - &mut self, - annotation: 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 - { - self.0.enforce(annotation, a, b, c) - } - - // Downstream users who use `namespace` will never interact with these - // functions and they will never be invoked because the namespace is - // never a root constraint system. - - fn push_namespace(&mut self, _: N) - where NR: Into, N: FnOnce() -> NR - { - panic!("only the root's push_namespace should be called"); - } - - fn pop_namespace(&mut self) - { - panic!("only the root's pop_namespace should be called"); - } - - fn get_root(&mut self) -> &mut Self::Root - { - self.0.get_root() - } -} - -impl<'a, E: Engine, CS: ConstraintSystem> Drop for Namespace<'a, E, CS> { - fn drop(&mut self) { - self.get_root().pop_namespace() - } -} - -/// Convenience implementation of ConstraintSystem for mutable references to -/// constraint systems. -impl<'cs, E: Engine, CS: ConstraintSystem> ConstraintSystem for &'cs mut CS { - type Root = CS::Root; - - fn one() -> Variable { - CS::one() - } - - fn alloc( - &mut self, - annotation: A, - f: F - ) -> Result - where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into - { - (**self).alloc(annotation, f) - } - - fn alloc_input( - &mut self, - annotation: A, - f: F - ) -> Result - where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into - { - (**self).alloc_input(annotation, f) - } - - fn enforce( - &mut self, - annotation: 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 - { - (**self).enforce(annotation, a, b, c) - } - - fn push_namespace(&mut self, name_fn: N) - where NR: Into, N: FnOnce() -> NR - { - (**self).push_namespace(name_fn) - } - - fn pop_namespace(&mut self) - { - (**self).pop_namespace() - } - - fn get_root(&mut self) -> &mut Self::Root - { - (**self).get_root() - } -} +mod cs; +pub use self::cs::*; static mut VERBOSE_SWITCH: i8 = -1; diff --git a/src/multicore.rs b/src/multicore.rs index 84055d7..bb113c6 100644 --- a/src/multicore.rs +++ b/src/multicore.rs @@ -4,11 +4,14 @@ //! crossbeam but may be extended in the future to //! allow for various parallelism strategies. -use num_cpus; -use futures::{Future, IntoFuture, Poll}; -use futures_cpupool::{CpuPool, CpuFuture}; -use crossbeam::{self}; -use crossbeam::thread::{Scope}; +extern crate num_cpus; +extern crate futures; +extern crate futures_cpupool; +extern crate crossbeam; + +use self::futures::{Future, IntoFuture, Poll}; +use self::futures_cpupool::{CpuPool, CpuFuture}; +use self::crossbeam::thread::{Scope}; #[derive(Clone)] pub struct Worker { diff --git a/src/parallel_multiexp.rs b/src/multiexp.rs similarity index 93% rename from src/parallel_multiexp.rs rename to src/multiexp.rs index 8b3d3bb..1e114d9 100644 --- a/src/parallel_multiexp.rs +++ b/src/multiexp.rs @@ -4,7 +4,7 @@ use pairing::{ Engine }; -use ff::{ +use pairing::ff::{ PrimeField, Field, PrimeFieldRepr, @@ -13,7 +13,7 @@ use ff::{ use std::sync::Arc; use super::source::*; use futures::{Future}; -use super::multicore::Worker; +use super::worker::Worker; use super::SynthesisError; @@ -212,19 +212,16 @@ fn dense_multiexp_inner( handle_trivial: bool ) -> Result<::Projective, SynthesisError> { - use crossbeam::channel::{unbounded, RecvError}; // Perform this region of the multiexp. We use a different strategy - go over region in parallel, // then over another region, etc. No Arc required let this = { // let mask = (1u64 << c) - 1u64; - let (s, r) = unbounded(); - // let this_region = + let this_region = pool.scope(bases.len(), |scope, chunk| { - // let mut handles = vec![]; - // let mut this_acc = ::Projective::zero(); + let mut handles = vec![]; + let mut this_acc = ::Projective::zero(); for (base, exp) in bases.chunks(chunk).zip(exponents.chunks(chunk)) { - let s = s.clone(); - // let handle = + let handle = scope.spawn(move |_| { let mut buckets = vec![::Projective::zero(); (1 << c) - 1]; // Accumulate the result @@ -265,32 +262,32 @@ fn dense_multiexp_inner( } // acc contains values over this region - s.send(acc).expect("must send result"); + // s.send(acc).expect("must send result"); - // acc + acc }); - // handles.push(handle); + handles.push(handle); } - // // wait for all threads to finish - // for r in handles.into_iter() { - // let thread_result = r.join().unwrap(); - // this_acc.add_assign(&thread_result); - // } + // wait for all threads to finish + for r in handles.into_iter() { + let thread_result = r.join().unwrap(); + this_acc.add_assign(&thread_result); + } - // this_acc + this_acc }); - let mut this_region = ::Projective::zero(); - loop { - if r.is_empty() { - break; - } - let value = r.recv().expect("must have value"); - this_region.add_assign(&value); - } + // let mut this_region = ::Projective::zero(); + // loop { + // if r.is_empty() { + // break; + // } + // let value = r.recv().expect("must have value"); + // this_region.add_assign(&value); + // } this_region }; diff --git a/src/parallel_fft.rs b/src/parallel_fft.rs deleted file mode 100644 index 687d146..0000000 --- a/src/parallel_fft.rs +++ /dev/null @@ -1,143 +0,0 @@ -//! This module contains an `EvaluationDomain` abstraction for -//! performing various kinds of polynomial arithmetic on top of -//! the scalar field. -//! -//! In pairing-based SNARKs like Groth16, we need to calculate -//! a quotient polynomial over a target polynomial with roots -//! at distinct points associated with each constraint of the -//! constraint system. In order to be efficient, we choose these -//! roots to be the powers of a 2^n root of unity in the field. -//! This allows us to perform polynomial operations in O(n) -//! by performing an O(n log n) FFT over such a domain. - -use pairing::{ - Engine, - CurveProjective -}; - -use ff::{ - Field, - PrimeField -}; - -use super::{ - SynthesisError -}; - -use super::multicore::Worker; -use super::group::*; - -pub(crate) fn best_fft>(a: &mut [T], worker: &Worker, omega: &E::Fr, log_n: u32) -{ - let log_cpus = worker.log_num_cpus(); - - if log_n <= log_cpus { - serial_fft(a, omega, log_n); - } else { - parallel_fft(a, worker, omega, log_n, log_cpus); - } -} - -pub(crate) fn serial_fft>(a: &mut [T], omega: &E::Fr, log_n: u32) -{ - fn bitreverse(mut n: u32, l: u32) -> u32 { - let mut r = 0; - for _ in 0..l { - r = (r << 1) | (n & 1); - n >>= 1; - } - r - } - - let n = a.len() as u32; - assert_eq!(n, 1 << log_n); - - for k in 0..n { - let rk = bitreverse(k, log_n); - if k < rk { - a.swap(rk as usize, k as usize); - } - } - - let mut m = 1; - for _ in 0..log_n { - let w_m = omega.pow(&[(n / (2*m)) as u64]); - - let mut k = 0; - while k < n { - let mut w = E::Fr::one(); - for j in 0..m { - let mut t = a[(k+j+m) as usize]; - t.group_mul_assign(&w); - let mut tmp = a[(k+j) as usize]; - tmp.group_sub_assign(&t); - a[(k+j+m) as usize] = tmp; - a[(k+j) as usize].group_add_assign(&t); - w.mul_assign(&w_m); - } - - k += 2*m; - } - - m *= 2; - } -} - -pub(crate) fn parallel_fft>( - a: &mut [T], - worker: &Worker, - omega: &E::Fr, - log_n: u32, - log_cpus: u32 -) -{ - assert!(log_n >= log_cpus); - - let num_cpus = 1 << log_cpus; - let log_new_n = log_n - log_cpus; - let mut tmp = vec![vec![T::group_zero(); 1 << log_new_n]; num_cpus]; - let new_omega = omega.pow(&[num_cpus as u64]); - - worker.scope(0, |scope, _| { - let a = &*a; - - for (j, tmp) in tmp.iter_mut().enumerate() { - scope.spawn(move |_| { - // Shuffle into a sub-FFT - let omega_j = omega.pow(&[j as u64]); - let omega_step = omega.pow(&[(j as u64) << log_new_n]); - - let mut elt = E::Fr::one(); - for i in 0..(1 << log_new_n) { - for s in 0..num_cpus { - let idx = (i + (s << log_new_n)) % (1 << log_n); - let mut t = a[idx]; - t.group_mul_assign(&elt); - tmp[i].group_add_assign(&t); - elt.mul_assign(&omega_step); - } - elt.mul_assign(&omega_j); - } - - // Perform sub-FFT - serial_fft(tmp, &new_omega, log_new_n); - }); - } - }); - - // TODO: does this hurt or help? - worker.scope(a.len(), |scope, chunk| { - let tmp = &tmp; - - for (idx, a) in a.chunks_mut(chunk).enumerate() { - scope.spawn(move |_| { - let mut idx = idx * chunk; - let mask = (1 << log_cpus) - 1; - for a in a { - *a = tmp[idx & mask][idx >> log_cpus]; - idx += 1; - } - }); - } - }); -} \ No newline at end of file diff --git a/src/serial_fft.rs b/src/serial_fft.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/serial_multiexp.rs b/src/serial_multiexp.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/singlecore.rs b/src/singlecore.rs new file mode 100644 index 0000000..e669ac6 --- /dev/null +++ b/src/singlecore.rs @@ -0,0 +1,94 @@ +//! This is a dummy interface to substitute multicore worker +//! in environments like WASM +extern crate futures; +extern crate futures_cpupool; + +use std::marker::PhantomData; + +use self::futures::{Future, IntoFuture, Poll}; +use self::futures_cpupool::{CpuFuture, CpuPool}; + +#[derive(Clone)] +pub struct Worker { + cpus: usize, + pool: CpuPool +} + +impl Worker { + // We don't expose this outside the library so that + // all `Worker` instances have the same number of + // CPUs configured. + pub(crate) fn new_with_cpus(cpus: usize) -> Worker { + Worker { + cpus: 1, + pool: CpuPool::new(1) + } + } + + pub fn new() -> Worker { + Self::new_with_cpus(1) + } + + pub fn log_num_cpus(&self) -> u32 { + 0u32 + } + + pub fn compute( + &self, f: F + ) -> WorkerFuture + where F: FnOnce() -> R + Send + 'static, + R: IntoFuture + 'static, + R::Future: Send + 'static, + R::Item: Send + 'static, + R::Error: Send + 'static + { + WorkerFuture { + future: self.pool.spawn_fn(f) + } + } + + pub fn scope<'a, F, R>( + &self, + elements: usize, + f: F + ) -> R + where F: FnOnce(&Scope<'a>, usize) -> R + { + let chunk_size = elements; + + let scope = Scope{ + _marker: PhantomData + }; + + f(&scope, chunk_size) + } +} +#[derive(Clone)] +pub struct Scope<'a> { + _marker: PhantomData<& 'a usize> +} + +impl<'a> Scope<'a> { + pub fn spawn( + &self, + f: F + ) -> R + where F: FnOnce(&Scope<'a>) -> R + { + f(&self) + } +} + +pub struct WorkerFuture { + future: CpuFuture +} + +impl Future for WorkerFuture { + type Item = T; + type Error = E; + + fn poll(&mut self) -> Poll + { + self.future.poll() + } +} \ No newline at end of file diff --git a/src/sonic/cs/lc.rs b/src/sonic/cs/lc.rs index 806eb61..8730473 100644 --- a/src/sonic/cs/lc.rs +++ b/src/sonic/cs/lc.rs @@ -1,4 +1,4 @@ -use ff::{Field}; +use pairing::ff::{Field}; use pairing::{Engine}; use std::ops::{Add, Sub, Neg}; diff --git a/src/sonic/cs/mod.rs b/src/sonic/cs/mod.rs index a2069e3..0a42dfa 100644 --- a/src/sonic/cs/mod.rs +++ b/src/sonic/cs/mod.rs @@ -1,7 +1,6 @@ -extern crate ff; extern crate pairing; -use ff::{Field}; +use pairing::ff::{Field}; use pairing::{Engine}; use crate::{SynthesisError}; diff --git a/src/sonic/helped/adapted_helper.rs b/src/sonic/helped/adapted_helper.rs index 87e99da..0243298 100644 --- a/src/sonic/helped/adapted_helper.rs +++ b/src/sonic/helped/adapted_helper.rs @@ -1,4 +1,4 @@ -use ff::{Field}; +use pairing::ff::{Field}; use pairing::{Engine, CurveProjective}; use std::marker::PhantomData; diff --git a/src/sonic/helped/adapted_prover.rs b/src/sonic/helped/adapted_prover.rs index 2811db4..68f9565 100644 --- a/src/sonic/helped/adapted_prover.rs +++ b/src/sonic/helped/adapted_prover.rs @@ -1,4 +1,4 @@ -use ff::{Field}; +use pairing::ff::{Field}; use pairing::{Engine, CurveProjective}; use std::marker::PhantomData; @@ -97,7 +97,7 @@ pub fn create_proof_on_srs + Clone>( // #[test] // fn my_fun_circuit_test() { -// use ff::PrimeField; +// use pairing::ff::PrimeField; // use pairing::bls12_381::{Bls12, Fr}; // use super::*; // use crate::sonic::cs::{Basic, ConstraintSystem, LinearCombination}; diff --git a/src/sonic/helped/adapted_verifier.rs b/src/sonic/helped/adapted_verifier.rs index 0c4b0c8..0a4b159 100644 --- a/src/sonic/helped/adapted_verifier.rs +++ b/src/sonic/helped/adapted_verifier.rs @@ -1,4 +1,4 @@ -use ff::{Field}; +use pairing::ff::{Field}; use pairing::{Engine, CurveProjective}; use std::marker::PhantomData; @@ -53,7 +53,7 @@ pub fn verify_aggregate + Clone, R: Rng>( // #[test] // fn my_fun_circuit_test() { -// use ff::PrimeField; +// use pairing::ff::PrimeField; // use pairing::bls12_381::{Bls12, Fr}; // use super::*; // use crate::sonic::cs::{Basic, ConstraintSystem, LinearCombination}; diff --git a/src/sonic/helped/batch.rs b/src/sonic/helped/batch.rs index 9af97c2..3cec29c 100644 --- a/src/sonic/helped/batch.rs +++ b/src/sonic/helped/batch.rs @@ -8,7 +8,7 @@ //! This submodule contains the `Batch` abstraction for creating a //! context for batch verification. -use ff::{Field}; +use pairing::ff::{Field}; use pairing::{Engine, CurveAffine, CurveProjective}; use crate::SynthesisError; diff --git a/src/sonic/helped/generator.rs b/src/sonic/helped/generator.rs index a1fb6de..652c501 100644 --- a/src/sonic/helped/generator.rs +++ b/src/sonic/helped/generator.rs @@ -9,7 +9,7 @@ use pairing::{ CurveAffine }; -use ff::{ +use pairing::ff::{ PrimeField, Field }; @@ -19,7 +19,7 @@ use super::{ VerifyingKey }; -use ::{ +use crate::{ SynthesisError, Circuit, ConstraintSystem, @@ -32,7 +32,7 @@ use crate::domain::{ Scalar }; -use ::multicore::{ +use crate::multicore::{ Worker }; diff --git a/src/sonic/helped/helper.rs b/src/sonic/helped/helper.rs index 5b11f11..a8a853a 100644 --- a/src/sonic/helped/helper.rs +++ b/src/sonic/helped/helper.rs @@ -1,4 +1,4 @@ -use ff::{Field}; +use pairing::ff::{Field}; use pairing::{Engine, CurveProjective}; use std::marker::PhantomData; diff --git a/src/sonic/helped/mod.rs b/src/sonic/helped/mod.rs index 3a0be60..0135b66 100644 --- a/src/sonic/helped/mod.rs +++ b/src/sonic/helped/mod.rs @@ -1,7 +1,6 @@ -extern crate ff; extern crate pairing; -use ff::{Field}; +use pairing::ff::{Field}; use pairing::{Engine, CurveProjective}; use std::marker::PhantomData; diff --git a/src/sonic/helped/parameters.rs b/src/sonic/helped/parameters.rs index 488c186..2e000e6 100644 --- a/src/sonic/helped/parameters.rs +++ b/src/sonic/helped/parameters.rs @@ -1,4 +1,4 @@ -use ff::{ +use pairing::ff::{ Field, PrimeField, PrimeFieldRepr @@ -10,11 +10,11 @@ use pairing::{ EncodedPoint }; -use ::{ +use crate::{ SynthesisError }; -use multiexp::SourceBuilder; +use crate::source::SourceBuilder; use std::io::{self, Read, Write}; use std::sync::Arc; use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt}; @@ -64,7 +64,7 @@ impl Proof { mut writer: W ) -> io::Result<()> { - use ff::{PrimeField, PrimeFieldRepr}; + use pairing::ff::{PrimeField, PrimeFieldRepr}; writer.write_all(self.r.into_compressed().as_ref())?; writer.write_all(self.t.into_compressed().as_ref())?; let mut buffer = vec![]; diff --git a/src/sonic/helped/poly.rs b/src/sonic/helped/poly.rs index 6001061..22ce7e7 100644 --- a/src/sonic/helped/poly.rs +++ b/src/sonic/helped/poly.rs @@ -1,4 +1,4 @@ -use ff::{Field}; +use pairing::ff::{Field}; use pairing::{Engine, CurveProjective}; use std::marker::PhantomData; diff --git a/src/sonic/helped/prover.rs b/src/sonic/helped/prover.rs index 79cc0ec..f3f4645 100644 --- a/src/sonic/helped/prover.rs +++ b/src/sonic/helped/prover.rs @@ -1,4 +1,4 @@ -use ff::{Field}; +use pairing::ff::{Field}; use pairing::{Engine, CurveProjective}; use std::marker::PhantomData; @@ -397,7 +397,7 @@ pub fn create_proof_on_srs, S: SynthesisDriver>( #[test] fn my_fun_circuit_test() { - use ff::PrimeField; + use pairing::ff::PrimeField; use pairing::bls12_381::{Bls12, Fr}; use super::*; use crate::sonic::cs::{Basic, ConstraintSystem, LinearCombination}; @@ -449,8 +449,8 @@ fn my_fun_circuit_test() { #[test] fn polynomial_commitment_test() { - use ff::PrimeField; - use ff::PrimeFieldRepr; + use pairing::ff::PrimeField; + use pairing::ff::PrimeFieldRepr; use pairing::bls12_381::{Bls12, Fr}; use super::*; use crate::sonic::cs::{Basic, ConstraintSystem, LinearCombination}; diff --git a/src/sonic/helped/verifier.rs b/src/sonic/helped/verifier.rs index 2bded1b..9a520b7 100644 --- a/src/sonic/helped/verifier.rs +++ b/src/sonic/helped/verifier.rs @@ -1,4 +1,4 @@ -use ff::{Field}; +use pairing::ff::{Field}; use pairing::{Engine, CurveProjective}; use std::marker::PhantomData; use rand::{Rand, Rng}; diff --git a/src/sonic/mod.rs b/src/sonic/mod.rs index 6786a7f..2241a62 100644 --- a/src/sonic/mod.rs +++ b/src/sonic/mod.rs @@ -1,4 +1,3 @@ -extern crate ff; extern crate pairing; pub use crate::{SynthesisError}; diff --git a/src/sonic/sonic/adaptor.rs b/src/sonic/sonic/adaptor.rs index f38eaed..87cd9e7 100644 --- a/src/sonic/sonic/adaptor.rs +++ b/src/sonic/sonic/adaptor.rs @@ -1,8 +1,7 @@ -extern crate ff; extern crate pairing; extern crate rand; -use ff::{Field, PrimeField}; +use pairing::ff::{Field, PrimeField}; use pairing::{Engine, CurveProjective}; // this one is for all external interfaces diff --git a/src/sonic/sonic/mod.rs b/src/sonic/sonic/mod.rs index 8b3e220..aa16edb 100644 --- a/src/sonic/sonic/mod.rs +++ b/src/sonic/sonic/mod.rs @@ -1,4 +1,3 @@ -extern crate ff; extern crate pairing; mod adaptor; diff --git a/src/sonic/srs/mod.rs b/src/sonic/srs/mod.rs index d2eae6e..e93d24f 100644 --- a/src/sonic/srs/mod.rs +++ b/src/sonic/srs/mod.rs @@ -1,4 +1,3 @@ -extern crate ff; extern crate pairing; mod srs; diff --git a/src/sonic/srs/srs.rs b/src/sonic/srs/srs.rs index b8d5831..7833ab8 100644 --- a/src/sonic/srs/srs.rs +++ b/src/sonic/srs/srs.rs @@ -1,4 +1,4 @@ -use ff::{Field, PrimeField}; +use pairing::ff::{Field, PrimeField}; use pairing::{CurveAffine, CurveProjective, Engine, Wnaf}; use std::io::{self, Read, Write}; diff --git a/src/sonic/transcript/mod.rs b/src/sonic/transcript/mod.rs index fc5035f..4525030 100644 --- a/src/sonic/transcript/mod.rs +++ b/src/sonic/transcript/mod.rs @@ -1,7 +1,6 @@ -extern crate ff; extern crate pairing; -use ff::{Field, PrimeField, PrimeFieldRepr}; +use pairing::ff::{Field, PrimeField, PrimeFieldRepr}; use pairing::{CurveAffine, CurveProjective, Engine}; use std::io; diff --git a/src/sonic/unhelped/grand_product_argument.rs b/src/sonic/unhelped/grand_product_argument.rs index dad9505..61c914d 100644 --- a/src/sonic/unhelped/grand_product_argument.rs +++ b/src/sonic/unhelped/grand_product_argument.rs @@ -2,7 +2,7 @@ /// in those two polynomials are equal (part of the permutation argument) with additional assumption that /// those coefficients are never equal to zero -use ff::{Field, PrimeField, PrimeFieldRepr}; +use pairing::ff::{Field, PrimeField, PrimeFieldRepr}; use pairing::{Engine, CurveProjective, CurveAffine}; use std::marker::PhantomData; diff --git a/src/sonic/unhelped/permutation_argument.rs b/src/sonic/unhelped/permutation_argument.rs index 96c6039..1813ae6 100644 --- a/src/sonic/unhelped/permutation_argument.rs +++ b/src/sonic/unhelped/permutation_argument.rs @@ -2,7 +2,7 @@ /// actually a commitment to a vector of values that are equal to `(s^{perm})_i * y^{perm(i)}` /// for some fixed permutation `perm` -use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine}; +use pairing::ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine}; use pairing::{Engine, CurveProjective, CurveAffine}; use std::marker::PhantomData; diff --git a/src/sonic/unhelped/s2_proof.rs b/src/sonic/unhelped/s2_proof.rs index 72f9eb7..77bb8a8 100644 --- a/src/sonic/unhelped/s2_proof.rs +++ b/src/sonic/unhelped/s2_proof.rs @@ -1,4 +1,4 @@ -use ff::{Field, PrimeField, PrimeFieldRepr}; +use pairing::ff::{Field, PrimeField, PrimeFieldRepr}; use pairing::{Engine, CurveProjective, CurveAffine}; use std::marker::PhantomData; diff --git a/src/sonic/unhelped/wellformed_argument.rs b/src/sonic/unhelped/wellformed_argument.rs index 5e707f1..a321278 100644 --- a/src/sonic/unhelped/wellformed_argument.rs +++ b/src/sonic/unhelped/wellformed_argument.rs @@ -1,7 +1,7 @@ /// Wellformedness argument allows to verify that some committment was to multivariate polynomial of degree n, /// with no constant term and negative powers -use ff::{Field, PrimeField, PrimeFieldRepr}; +use pairing::ff::{Field, PrimeField, PrimeFieldRepr}; use pairing::{Engine, CurveProjective, CurveAffine}; use std::marker::PhantomData; diff --git a/src/sonic/util.rs b/src/sonic/util.rs index 9e9f6b1..9f3e055 100644 --- a/src/sonic/util.rs +++ b/src/sonic/util.rs @@ -1,5 +1,5 @@ use crate::SynthesisError; -use ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine}; +use pairing::ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine}; use pairing::{CurveAffine, CurveProjective, Engine}; use super::srs::SRS; @@ -437,7 +437,7 @@ pub fn check_polynomial_commitment( #[test] fn laurent_division() { - use ff::PrimeField; + use pairing::ff::PrimeField; use pairing::bls12_381::{Fr}; let mut poly = vec![ diff --git a/src/source.rs b/src/source.rs index 5a77d8f..8fa8a94 100644 --- a/src/source.rs +++ b/src/source.rs @@ -4,7 +4,7 @@ use pairing::{ Engine }; -use ff::{ +use pairing::ff::{ PrimeField, Field, PrimeFieldRepr, diff --git a/src/tests/dummy_engine.rs b/src/tests/dummy_engine.rs index a436533..0cc5cdc 100644 --- a/src/tests/dummy_engine.rs +++ b/src/tests/dummy_engine.rs @@ -6,7 +6,7 @@ use pairing::{ EncodedPoint }; -use ff::{ +use pairing::ff::{ PrimeField, PrimeFieldRepr, Field, diff --git a/src/tests/mod.rs b/src/tests/mod.rs index f9f6ce9..e72f954 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -2,7 +2,7 @@ use pairing::{ Engine }; -use ff:: { +use pairing::ff:: { Field, PrimeField, }; @@ -12,7 +12,7 @@ use self::dummy_engine::*; use std::marker::PhantomData; -use ::{ +use crate::{ Circuit, ConstraintSystem, SynthesisError diff --git a/tests/mimc.rs b/tests/mimc.rs index c0dce24..9446145 100644 --- a/tests/mimc.rs +++ b/tests/mimc.rs @@ -1,7 +1,6 @@ extern crate bellman; extern crate pairing; extern crate rand; -extern crate ff; // For randomness (during paramgen and proof generation) use rand::{thread_rng, Rng}; @@ -14,7 +13,7 @@ use pairing::{ Engine }; -use ff::{ +use pairing::ff::{ Field, }; @@ -442,7 +441,7 @@ impl<'a, E: Engine> Circuit for MiMCDemoNoInputs<'a, E> { #[test] fn test_sonic_mimc() { - use ff::{Field, PrimeField}; + use pairing::ff::{Field, PrimeField}; use pairing::{Engine, CurveAffine, CurveProjective}; use pairing::bls12_381::{Bls12, Fr}; use std::time::{Instant}; @@ -546,7 +545,7 @@ fn test_sonic_mimc() { #[test] fn test_inputs_into_sonic_mimc() { - use ff::{Field, PrimeField}; + use pairing::ff::{Field, PrimeField}; use pairing::{Engine, CurveAffine, CurveProjective}; use pairing::bn256::{Bn256, Fr}; // use pairing::bls12_381::{Bls12, Fr}; From a99132955ae88701bf6729c60a950591ec60720b Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 5 Mar 2019 09:44:38 +0100 Subject: [PATCH 40/42] fixed singlecore dense multiexp --- Cargo.toml | 2 +- src/multiexp.rs | 46 ++++++++++++++++++++-------------------------- src/singlecore.rs | 11 +++++------ 3 files changed, 26 insertions(+), 33 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 43d1f21..b45eb98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,4 +37,4 @@ default = ["multicore", "gm17", "sonic"] multicore = ["futures-cpupool", "num_cpus", "crossbeam"] sonic = ["tiny-keccak"] gm17 = [] -singlecore = ["futures-cpupool"] +singlecore = [] diff --git a/src/multiexp.rs b/src/multiexp.rs index 1e114d9..9fbec53 100644 --- a/src/multiexp.rs +++ b/src/multiexp.rs @@ -212,16 +212,18 @@ fn dense_multiexp_inner( handle_trivial: bool ) -> Result<::Projective, SynthesisError> { + use std::sync::{Mutex}; // Perform this region of the multiexp. We use a different strategy - go over region in parallel, // then over another region, etc. No Arc required let this = { // let mask = (1u64 << c) - 1u64; - let this_region = + let this_region = Mutex::new(::Projective::zero()); + let arc = Arc::new(this_region); pool.scope(bases.len(), |scope, chunk| { - let mut handles = vec![]; let mut this_acc = ::Projective::zero(); for (base, exp) in bases.chunks(chunk).zip(exponents.chunks(chunk)) { - let handle = + let this_region_rwlock = arc.clone(); + // let handle = scope.spawn(move |_| { let mut buckets = vec![::Projective::zero(); (1 << c) - 1]; // Accumulate the result @@ -261,33 +263,22 @@ fn dense_multiexp_inner( acc.add_assign(&running_sum); } - // acc contains values over this region - // s.send(acc).expect("must send result"); - - acc + let mut guard = match this_region_rwlock.lock() { + Ok(guard) => guard, + Err(poisoned) => { + panic!("poisoned!"); + // poisoned.into_inner() + } + }; + + (*guard).add_assign(&acc); }); - handles.push(handle); } - - // wait for all threads to finish - for r in handles.into_iter() { - let thread_result = r.join().unwrap(); - this_acc.add_assign(&thread_result); - } - - - this_acc }); - // let mut this_region = ::Projective::zero(); - // loop { - // if r.is_empty() { - // break; - // } - // let value = r.recv().expect("must have value"); - // this_region.add_assign(&value); - // } + let this_region = Arc::try_unwrap(arc).unwrap(); + let this_region = this_region.into_inner().unwrap(); this_region }; @@ -392,12 +383,15 @@ fn test_dense_multiexp() { use pairing::bn256::Bn256; use num_cpus; - const SAMPLES: usize = 1 << 22; + // const SAMPLES: usize = 1 << 22; + const SAMPLES: usize = 1 << 16; let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); let mut v = (0..SAMPLES).map(|_| ::Fr::rand(rng).into_repr()).collect::>(); let g = (0..SAMPLES).map(|_| ::G1::rand(rng).into_affine()).collect::>(); + println!("Done generating test points and scalars"); + let pool = Worker::new(); let start = std::time::Instant::now(); diff --git a/src/singlecore.rs b/src/singlecore.rs index e669ac6..df73c80 100644 --- a/src/singlecore.rs +++ b/src/singlecore.rs @@ -1,17 +1,15 @@ //! This is a dummy interface to substitute multicore worker //! in environments like WASM extern crate futures; -extern crate futures_cpupool; use std::marker::PhantomData; use self::futures::{Future, IntoFuture, Poll}; -use self::futures_cpupool::{CpuFuture, CpuPool}; +use self::futures::future::{result, FutureResult}; #[derive(Clone)] pub struct Worker { cpus: usize, - pool: CpuPool } impl Worker { @@ -21,7 +19,6 @@ impl Worker { pub(crate) fn new_with_cpus(cpus: usize) -> Worker { Worker { cpus: 1, - pool: CpuPool::new(1) } } @@ -42,8 +39,10 @@ impl Worker { R::Item: Send + 'static, R::Error: Send + 'static { + let future = f().into_future(); + WorkerFuture { - future: self.pool.spawn_fn(f) + future: result(future.wait()) } } @@ -80,7 +79,7 @@ impl<'a> Scope<'a> { } pub struct WorkerFuture { - future: CpuFuture + future: FutureResult } impl Future for WorkerFuture { From 2a453c3325c97b8bc88792d041a1b62b2648555e Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 5 Mar 2019 09:58:59 +0100 Subject: [PATCH 41/42] improve message about domain sizes and operation timings --- Cargo.toml | 5 +++-- src/groth16/prover.rs | 19 +++++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b45eb98..099343a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,8 +17,8 @@ rand = "0.4" bit-vec = "0.4.4" futures = "0.1" -#pairing = { git = 'https://github.com/matterinc/pairing', tag = "0.16.2" } -pairing = { path = "../pairing" } +pairing = { git = 'https://github.com/matterinc/pairing', tag = "0.16.2" } +#pairing = { path = "../pairing" } byteorder = "1" futures-cpupool = {version = "0.1", optional = true} @@ -32,6 +32,7 @@ git = "https://github.com/gtank/blake2-rfc" rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9" [features] +#default = ["multicore"] default = ["multicore", "gm17", "sonic"] #default = ["singlecore"] multicore = ["futures-cpupool", "num_cpus", "crossbeam"] diff --git a/src/groth16/prover.rs b/src/groth16/prover.rs index f66e251..3c7412c 100644 --- a/src/groth16/prover.rs +++ b/src/groth16/prover.rs @@ -177,6 +177,8 @@ impl PreparedProver { let mut a = EvaluationDomain::from_coeffs(prover.a)?; let mut b = EvaluationDomain::from_coeffs(prover.b)?; let mut c = EvaluationDomain::from_coeffs(prover.c)?; + if verbose {eprintln!("H query domain size is {}", a.as_ref().len())}; + // here a coset is a domain where denominator (z) does not vanish // inverse FFT is an interpolation a.ifft(&worker); @@ -207,18 +209,23 @@ impl PreparedProver { multiexp(&worker, params.get_h(a.len())?, FullDensity, a) }; - if verbose {eprintln!("{} seconds for prover for H evaluation", start.elapsed().as_secs())}; + if verbose {eprintln!("{} seconds for prover for H evaluation (mostly FFT)", start.elapsed().as_millis() as f64 / 1000.0)}; let start = std::time::Instant::now(); // TODO: Check that difference in operations for different chunks is small - // TODO: parallelize if it's even helpful // TODO: in large settings it may worth to parallelize let input_assignment = Arc::new(prover.input_assignment.into_iter().map(|s| s.into_repr()).collect::>()); let aux_assignment = Arc::new(prover.aux_assignment.into_iter().map(|s| s.into_repr()).collect::>()); + let input_len = input_assignment.len(); + let aux_len = aux_assignment.len(); + if verbose {eprintln!("H query is dense in G1,\nOther queries are {} elements in G1 and {} elements in G2", + 2*(input_len + aux_len) + aux_len, input_len + aux_len) + }; + // Run a dedicated process for dense vector let l = multiexp(&worker, params.get_l(aux_assignment.len())?, FullDensity, aux_assignment.clone()); @@ -280,7 +287,7 @@ impl PreparedProver { g_c.add_assign(&h.wait()?); g_c.add_assign(&l.wait()?); - if verbose {eprintln!("{} seconds for prover for point multiplication", start.elapsed().as_secs())}; + if verbose {eprintln!("{} seconds for prover for point multiplication", start.elapsed().as_millis() as f64 / 1000.0)}; Ok(Proof { a: g_a.into_affine(), @@ -439,6 +446,7 @@ pub fn create_proof>( let mut a = EvaluationDomain::from_coeffs(prover.a)?; let mut b = EvaluationDomain::from_coeffs(prover.b)?; let mut c = EvaluationDomain::from_coeffs(prover.c)?; + if verbose {eprintln!("H query domain size is {}", a.as_ref().len())}; // here a coset is a domain where denominator (z) does not vanish // inverse FFT is an interpolation a.ifft(&worker); @@ -469,13 +477,12 @@ pub fn create_proof>( multiexp(&worker, params.get_h(a.len())?, FullDensity, a) }; - if verbose {eprintln!("{} seconds for prover for H evaluation", start.elapsed().as_secs())}; + if verbose {eprintln!("{} seconds for prover for H evaluation (mostly FFT)", start.elapsed().as_millis() as f64 / 1000.0)}; let start = std::time::Instant::now(); // TODO: Check that difference in operations for different chunks is small - // TODO: parallelize if it's even helpful // TODO: in large settings it may worth to parallelize let input_assignment = Arc::new(prover.input_assignment.into_iter().map(|s| s.into_repr()).collect::>()); @@ -542,7 +549,7 @@ pub fn create_proof>( g_c.add_assign(&h.wait()?); g_c.add_assign(&l.wait()?); - if verbose {eprintln!("{} seconds for prover for point multiplication", start.elapsed().as_secs())}; + if verbose {eprintln!("{} seconds for prover for point multiplication", start.elapsed().as_millis() as f64 / 1000.0)}; Ok(Proof { a: g_a.into_affine(), From f12986aaeb37dfb6e0b723c1b0701f0dd39823b9 Mon Sep 17 00:00:00 2001 From: Alex Vlasov Date: Tue, 5 Mar 2019 10:26:28 +0100 Subject: [PATCH 42/42] fix compilation warnings, disable sonics in default features --- Cargo.toml | 4 +- src/multiexp.rs | 8 +- src/sonic/mod.rs | 2 + src/sonic/tests/sonics.rs | 436 ++++++++++++++++++++++++++++++++++++++ tests/mimc.rs | 399 +--------------------------------- 5 files changed, 448 insertions(+), 401 deletions(-) create mode 100644 src/sonic/tests/sonics.rs diff --git a/Cargo.toml b/Cargo.toml index 099343a..b97cbf9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,8 +32,8 @@ git = "https://github.com/gtank/blake2-rfc" rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9" [features] -#default = ["multicore"] -default = ["multicore", "gm17", "sonic"] +default = ["multicore"] +#default = ["multicore", "gm17", "sonic"] #default = ["singlecore"] multicore = ["futures-cpupool", "num_cpus", "crossbeam"] sonic = ["tiny-keccak"] diff --git a/src/multiexp.rs b/src/multiexp.rs index 9fbec53..e4682f2 100644 --- a/src/multiexp.rs +++ b/src/multiexp.rs @@ -185,6 +185,7 @@ pub fn multiexp( /// Perform multi-exponentiation. The caller is responsible for ensuring that /// the number of bases is the same as the number of exponents. +#[allow(dead_code)] pub fn dense_multiexp( pool: &Worker, bases: & [G], @@ -220,7 +221,6 @@ fn dense_multiexp_inner( let this_region = Mutex::new(::Projective::zero()); let arc = Arc::new(this_region); pool.scope(bases.len(), |scope, chunk| { - let mut this_acc = ::Projective::zero(); for (base, exp) in bases.chunks(chunk).zip(exponents.chunks(chunk)) { let this_region_rwlock = arc.clone(); // let handle = @@ -265,7 +265,7 @@ fn dense_multiexp_inner( let mut guard = match this_region_rwlock.lock() { Ok(guard) => guard, - Err(poisoned) => { + Err(_) => { panic!("poisoned!"); // poisoned.into_inner() } @@ -387,7 +387,7 @@ fn test_dense_multiexp() { const SAMPLES: usize = 1 << 16; let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]); - let mut v = (0..SAMPLES).map(|_| ::Fr::rand(rng).into_repr()).collect::>(); + let v = (0..SAMPLES).map(|_| ::Fr::rand(rng).into_repr()).collect::>(); let g = (0..SAMPLES).map(|_| ::G1::rand(rng).into_affine()).collect::>(); println!("Done generating test points and scalars"); @@ -397,7 +397,7 @@ fn test_dense_multiexp() { let start = std::time::Instant::now(); let dense = dense_multiexp( - &pool, &g, &mut v.clone()).unwrap(); + &pool, &g, &v.clone()).unwrap(); let duration_ns = start.elapsed().as_nanos() as f64; println!("{} ns for dense for {} samples", duration_ns, SAMPLES); diff --git a/src/sonic/mod.rs b/src/sonic/mod.rs index 2241a62..4c99ffa 100644 --- a/src/sonic/mod.rs +++ b/src/sonic/mod.rs @@ -11,5 +11,7 @@ pub mod unhelped; mod transcript; +#[cfg(test)] +mod tests; diff --git a/src/sonic/tests/sonics.rs b/src/sonic/tests/sonics.rs new file mode 100644 index 0000000..91d6ddd --- /dev/null +++ b/src/sonic/tests/sonics.rs @@ -0,0 +1,436 @@ +extern crate bellman; +extern crate pairing; +extern crate rand; + +// For randomness (during paramgen and proof generation) +use rand::{thread_rng, Rng}; + +// For benchmarking +use std::time::{Duration, Instant}; + +// Bring in some tools for using pairing-friendly curves +use pairing::{ + Engine +}; + +use pairing::ff::{ + Field, +}; + +// We're going to use the BLS12-381 pairing-friendly elliptic curve. +use pairing::bls12_381::{ + Bls12 +}; + +use pairing::bn256::{ + Bn256 +}; + +// We'll use these interfaces to construct our circuit. +use bellman::{ + Circuit, + ConstraintSystem, + SynthesisError +}; + +// We're going to use the Groth16 proving system. +use bellman::groth16::{ + Proof, + generate_random_parameters, + prepare_verifying_key, + create_random_proof, + verify_proof, +}; + +const MIMC_ROUNDS: usize = 322; + +/// This is our demo circuit for proving knowledge of the +/// preimage of a MiMC hash invocation. +#[derive(Clone)] +struct MiMCDemoNoInputs<'a, E: Engine> { + xl: Option, + xr: Option, + image: Option, + constants: &'a [E::Fr] +} + +/// Our demo circuit implements this `Circuit` trait which +/// is used during paramgen and proving in order to +/// synthesize the constraint system. +impl<'a, E: Engine> Circuit for MiMCDemoNoInputs<'a, E> { + fn synthesize>( + self, + cs: &mut CS + ) -> Result<(), SynthesisError> + { + assert_eq!(self.constants.len(), MIMC_ROUNDS); + + // Allocate the first component of the preimage. + let mut xl_value = self.xl; + let mut xl = cs.alloc(|| "preimage xl", || { + xl_value.ok_or(SynthesisError::AssignmentMissing) + })?; + + // Allocate the second component of the preimage. + let mut xr_value = self.xr; + let mut xr = cs.alloc(|| "preimage xr", || { + xr_value.ok_or(SynthesisError::AssignmentMissing) + })?; + + for i in 0..MIMC_ROUNDS { + // xL, xR := xR + (xL + Ci)^3, xL + let cs = &mut cs.namespace(|| format!("round {}", i)); + + // tmp = (xL + Ci)^2 + let tmp_value = xl_value.map(|mut e| { + e.add_assign(&self.constants[i]); + e.square(); + e + }); + let tmp = cs.alloc(|| "tmp", || { + tmp_value.ok_or(SynthesisError::AssignmentMissing) + })?; + + cs.enforce( + || "tmp = (xL + Ci)^2", + |lc| lc + xl + (self.constants[i], CS::one()), + |lc| lc + xl + (self.constants[i], CS::one()), + |lc| lc + tmp + ); + + // new_xL = xR + (xL + Ci)^3 + // new_xL = xR + tmp * (xL + Ci) + // new_xL - xR = tmp * (xL + Ci) + let new_xl_value = xl_value.map(|mut e| { + e.add_assign(&self.constants[i]); + e.mul_assign(&tmp_value.unwrap()); + e.add_assign(&xr_value.unwrap()); + e + }); + + let new_xl = if i == (MIMC_ROUNDS-1) { + // This is the last round, xL is our image and so + // we use the image + let image_value = self.image; + cs.alloc(|| "image", || { + image_value.ok_or(SynthesisError::AssignmentMissing) + })? + } else { + cs.alloc(|| "new_xl", || { + new_xl_value.ok_or(SynthesisError::AssignmentMissing) + })? + }; + + cs.enforce( + || "new_xL = xR + (xL + Ci)^3", + |lc| lc + tmp, + |lc| lc + xl + (self.constants[i], CS::one()), + |lc| lc + new_xl - xr + ); + + // xR = xL + xr = xl; + xr_value = xl_value; + + // xL = new_xL + xl = new_xl; + xl_value = new_xl_value; + } + + Ok(()) + } +} + +#[test] +fn test_sonic_mimc() { + use pairing::ff::{Field, PrimeField}; + use pairing::{Engine, CurveAffine, CurveProjective}; + use pairing::bls12_381::{Bls12, Fr}; + use std::time::{Instant}; + use bellman::sonic::srs::SRS; + + let srs_x = Fr::from_str("23923").unwrap(); + let srs_alpha = Fr::from_str("23728792").unwrap(); + println!("making srs"); + let start = Instant::now(); + let srs = SRS::::dummy(830564, srs_x, srs_alpha); + println!("done in {:?}", start.elapsed()); + + { + // This may not be cryptographically safe, use + // `OsRng` (for example) in production software. + let rng = &mut thread_rng(); + + // Generate the MiMC round constants + let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::>(); + let samples: usize = 100; + + let xl = rng.gen(); + let xr = rng.gen(); + let image = mimc::(xl, xr, &constants); + + // Create an instance of our circuit (with the + // witness) + let circuit = MiMCDemoNoInputs { + xl: Some(xl), + xr: Some(xr), + image: Some(image), + constants: &constants + }; + + use bellman::sonic::cs::Basic; + use bellman::sonic::sonic::AdaptorCircuit; + use bellman::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs}; + use bellman::sonic::helped::{MultiVerifier, get_circuit_parameters}; + use bellman::sonic::helped::helper::{create_aggregate_on_srs}; + + println!("creating proof"); + let start = Instant::now(); + let proof = create_proof_on_srs::(&AdaptorCircuit(circuit.clone()), &srs).unwrap(); + println!("done in {:?}", start.elapsed()); + + println!("creating advice"); + let start = Instant::now(); + let advice = create_advice_on_srs::(&AdaptorCircuit(circuit.clone()), &proof, &srs).unwrap(); + println!("done in {:?}", start.elapsed()); + + println!("creating aggregate for {} proofs", samples); + let start = Instant::now(); + let proofs: Vec<_> = (0..samples).map(|_| (proof.clone(), advice.clone())).collect(); + let aggregate = create_aggregate_on_srs::(&AdaptorCircuit(circuit.clone()), &proofs, &srs); + println!("done in {:?}", start.elapsed()); + + { + let rng = thread_rng(); + let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); + println!("verifying 1 proof without advice"); + let start = Instant::now(); + { + for _ in 0..1 { + verifier.add_proof(&proof, &[], |_, _| None); + } + assert_eq!(verifier.check_all(), true); // TODO + } + println!("done in {:?}", start.elapsed()); + } + + { + let rng = thread_rng(); + let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); + println!("verifying {} proofs without advice", samples); + let start = Instant::now(); + { + for _ in 0..samples { + verifier.add_proof(&proof, &[], |_, _| None); + } + assert_eq!(verifier.check_all(), true); // TODO + } + println!("done in {:?}", start.elapsed()); + } + + { + let rng = thread_rng(); + let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); + println!("verifying 100 proofs with advice"); + let start = Instant::now(); + { + for (ref proof, ref advice) in &proofs { + verifier.add_proof_with_advice(proof, &[], advice); + } + verifier.add_aggregate(&proofs, &aggregate); + assert_eq!(verifier.check_all(), true); // TODO + } + println!("done in {:?}", start.elapsed()); + } + } +} + +#[test] +fn test_inputs_into_sonic_mimc() { + use pairing::ff::{Field, PrimeField}; + use pairing::{Engine, CurveAffine, CurveProjective}; + use pairing::bn256::{Bn256, Fr}; + // use pairing::bls12_381::{Bls12, Fr}; + use std::time::{Instant}; + use bellman::sonic::srs::SRS; + + let srs_x = Fr::from_str("23923").unwrap(); + let srs_alpha = Fr::from_str("23728792").unwrap(); + println!("making srs"); + let start = Instant::now(); + let srs = SRS::::dummy(830564, srs_x, srs_alpha); + println!("done in {:?}", start.elapsed()); + + { + // This may not be cryptographically safe, use + // `OsRng` (for example) in production software. + let rng = &mut thread_rng(); + + // Generate the MiMC round constants + let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::>(); + let samples: usize = 100; + + let xl = rng.gen(); + let xr = rng.gen(); + let image = mimc::(xl, xr, &constants); + + // Create an instance of our circuit (with the + // witness) + let circuit = MiMCDemo { + xl: Some(xl), + xr: Some(xr), + constants: &constants + }; + + use bellman::sonic::cs::Basic; + use bellman::sonic::sonic::AdaptorCircuit; + use bellman::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs}; + use bellman::sonic::helped::{MultiVerifier, get_circuit_parameters}; + use bellman::sonic::helped::helper::{create_aggregate_on_srs}; + + let info = get_circuit_parameters::(circuit.clone()).expect("Must get circuit info"); + println!("{:?}", info); + + println!("creating proof"); + let start = Instant::now(); + let proof = create_proof_on_srs::(&AdaptorCircuit(circuit.clone()), &srs).unwrap(); + println!("done in {:?}", start.elapsed()); + + println!("creating advice"); + let start = Instant::now(); + let advice = create_advice_on_srs::(&AdaptorCircuit(circuit.clone()), &proof, &srs).unwrap(); + println!("done in {:?}", start.elapsed()); + + println!("creating aggregate for {} proofs", samples); + let start = Instant::now(); + let proofs: Vec<_> = (0..samples).map(|_| (proof.clone(), advice.clone())).collect(); + let aggregate = create_aggregate_on_srs::(&AdaptorCircuit(circuit.clone()), &proofs, &srs); + println!("done in {:?}", start.elapsed()); + + { + let rng = thread_rng(); + let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); + println!("verifying 1 proof without advice"); + let start = Instant::now(); + { + for _ in 0..1 { + verifier.add_proof(&proof, &[image], |_, _| None); + } + assert_eq!(verifier.check_all(), true); // TODO + } + println!("done in {:?}", start.elapsed()); + } + + { + let rng = thread_rng(); + let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); + println!("verifying {} proofs without advice", samples); + let start = Instant::now(); + { + for _ in 0..samples { + verifier.add_proof(&proof, &[image], |_, _| None); + } + assert_eq!(verifier.check_all(), true); // TODO + } + println!("done in {:?}", start.elapsed()); + } + + { + let rng = thread_rng(); + let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); + println!("verifying 100 proofs with advice and aggregate"); + let start = Instant::now(); + { + for (ref proof, ref advice) in &proofs { + verifier.add_proof_with_advice(proof, &[image], advice); + } + verifier.add_aggregate(&proofs, &aggregate); + assert_eq!(verifier.check_all(), true); // TODO + } + println!("done in {:?}", start.elapsed()); + } + } +} + +#[test] +fn test_high_level_sonic_api() { + use pairing::bn256::{Bn256}; + use std::time::{Instant}; + use bellman::sonic::helped::{ + generate_random_parameters, + verify_aggregate, + verify_proofs, + create_proof, + create_advice, + create_aggregate, + get_circuit_parameters + }; + + { + // This may not be cryptographically safe, use + // `OsRng` (for example) in production software. + let mut rng = &mut thread_rng(); + + // Generate the MiMC round constants + let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::>(); + let samples: usize = 100; + + let xl = rng.gen(); + let xr = rng.gen(); + let image = mimc::(xl, xr, &constants); + + // Create an instance of our circuit (with the + // witness) + let circuit = MiMCDemo { + xl: Some(xl), + xr: Some(xr), + constants: &constants + }; + + let info = get_circuit_parameters::(circuit.clone()).expect("Must get circuit info"); + println!("{:?}", info); + + let params = generate_random_parameters(circuit.clone(), &mut rng).unwrap(); + + println!("creating proof"); + let start = Instant::now(); + let proof = create_proof(circuit.clone(), ¶ms).unwrap(); + println!("done in {:?}", start.elapsed()); + + println!("creating advice"); + let start = Instant::now(); + let advice = create_advice(circuit.clone(), &proof, ¶ms).unwrap(); + println!("done in {:?}", start.elapsed()); + + println!("creating aggregate for {} proofs", samples); + let start = Instant::now(); + let proofs: Vec<_> = (0..samples).map(|_| (proof.clone(), advice.clone())).collect(); + + let aggregate = create_aggregate::(circuit.clone(), &proofs, ¶ms); + println!("done in {:?}", start.elapsed()); + + { + println!("verifying 1 proof without advice"); + let rng = thread_rng(); + let start = Instant::now(); + assert_eq!(verify_proofs(&vec![proof.clone()], &vec![vec![image.clone()]], circuit.clone(), rng, ¶ms).unwrap(), true); + println!("done in {:?}", start.elapsed()); + } + + { + println!("verifying {} proofs without advice", samples); + let rng = thread_rng(); + let start = Instant::now(); + assert_eq!(verify_proofs(&vec![proof.clone(); 100], &vec![vec![image.clone()]; 100], circuit.clone(), rng, ¶ms).unwrap(), true); + println!("done in {:?}", start.elapsed()); + } + + { + println!("verifying 100 proofs with advice and aggregate"); + let rng = thread_rng(); + let start = Instant::now(); + assert_eq!(verify_aggregate(&vec![(proof.clone(), advice.clone()); 100], &aggregate, &vec![vec![image.clone()]; 100], circuit.clone(), rng, ¶ms).unwrap(), true); + println!("done in {:?}", start.elapsed()); + } + } +} \ No newline at end of file diff --git a/tests/mimc.rs b/tests/mimc.rs index 9446145..89da2eb 100644 --- a/tests/mimc.rs +++ b/tests/mimc.rs @@ -116,12 +116,12 @@ impl<'a, E: Engine> Circuit for MiMCDemo<'a, E> { let cs = &mut cs.namespace(|| format!("round {}", i)); // tmp = (xL + Ci)^2 - let mut tmp_value = xl_value.map(|mut e| { + let tmp_value = xl_value.map(|mut e| { e.add_assign(&self.constants[i]); e.square(); e }); - let mut tmp = cs.alloc(|| "tmp", || { + let tmp = cs.alloc(|| "tmp", || { tmp_value.ok_or(SynthesisError::AssignmentMissing) })?; @@ -135,14 +135,14 @@ impl<'a, E: Engine> Circuit for MiMCDemo<'a, E> { // new_xL = xR + (xL + Ci)^3 // new_xL = xR + tmp * (xL + Ci) // new_xL - xR = tmp * (xL + Ci) - let mut new_xl_value = xl_value.map(|mut e| { + let new_xl_value = xl_value.map(|mut e| { e.add_assign(&self.constants[i]); e.mul_assign(&tmp_value.unwrap()); e.add_assign(&xr_value.unwrap()); e }); - let mut new_xl = if i == (MIMC_ROUNDS-1) { + let new_xl = if i == (MIMC_ROUNDS-1) { // This is the last round, xL is our image and so // we allocate a public input. cs.alloc_input(|| "image", || { @@ -341,394 +341,3 @@ fn test_mimc_bn256() { println!("Average proving time: {:?} seconds", proving_avg); println!("Average verifying time: {:?} seconds", verifying_avg); } - -/// This is our demo circuit for proving knowledge of the -/// preimage of a MiMC hash invocation. -#[derive(Clone)] -struct MiMCDemoNoInputs<'a, E: Engine> { - xl: Option, - xr: Option, - image: Option, - constants: &'a [E::Fr] -} - -/// Our demo circuit implements this `Circuit` trait which -/// is used during paramgen and proving in order to -/// synthesize the constraint system. -impl<'a, E: Engine> Circuit for MiMCDemoNoInputs<'a, E> { - fn synthesize>( - self, - cs: &mut CS - ) -> Result<(), SynthesisError> - { - assert_eq!(self.constants.len(), MIMC_ROUNDS); - - // Allocate the first component of the preimage. - let mut xl_value = self.xl; - let mut xl = cs.alloc(|| "preimage xl", || { - xl_value.ok_or(SynthesisError::AssignmentMissing) - })?; - - // Allocate the second component of the preimage. - let mut xr_value = self.xr; - let mut xr = cs.alloc(|| "preimage xr", || { - xr_value.ok_or(SynthesisError::AssignmentMissing) - })?; - - for i in 0..MIMC_ROUNDS { - // xL, xR := xR + (xL + Ci)^3, xL - let cs = &mut cs.namespace(|| format!("round {}", i)); - - // tmp = (xL + Ci)^2 - let mut tmp_value = xl_value.map(|mut e| { - e.add_assign(&self.constants[i]); - e.square(); - e - }); - let mut tmp = cs.alloc(|| "tmp", || { - tmp_value.ok_or(SynthesisError::AssignmentMissing) - })?; - - cs.enforce( - || "tmp = (xL + Ci)^2", - |lc| lc + xl + (self.constants[i], CS::one()), - |lc| lc + xl + (self.constants[i], CS::one()), - |lc| lc + tmp - ); - - // new_xL = xR + (xL + Ci)^3 - // new_xL = xR + tmp * (xL + Ci) - // new_xL - xR = tmp * (xL + Ci) - let mut new_xl_value = xl_value.map(|mut e| { - e.add_assign(&self.constants[i]); - e.mul_assign(&tmp_value.unwrap()); - e.add_assign(&xr_value.unwrap()); - e - }); - - let mut new_xl = if i == (MIMC_ROUNDS-1) { - // This is the last round, xL is our image and so - // we use the image - let image_value = self.image; - cs.alloc(|| "image", || { - image_value.ok_or(SynthesisError::AssignmentMissing) - })? - } else { - cs.alloc(|| "new_xl", || { - new_xl_value.ok_or(SynthesisError::AssignmentMissing) - })? - }; - - cs.enforce( - || "new_xL = xR + (xL + Ci)^3", - |lc| lc + tmp, - |lc| lc + xl + (self.constants[i], CS::one()), - |lc| lc + new_xl - xr - ); - - // xR = xL - xr = xl; - xr_value = xl_value; - - // xL = new_xL - xl = new_xl; - xl_value = new_xl_value; - } - - Ok(()) - } -} - -#[test] -fn test_sonic_mimc() { - use pairing::ff::{Field, PrimeField}; - use pairing::{Engine, CurveAffine, CurveProjective}; - use pairing::bls12_381::{Bls12, Fr}; - use std::time::{Instant}; - use bellman::sonic::srs::SRS; - - let srs_x = Fr::from_str("23923").unwrap(); - let srs_alpha = Fr::from_str("23728792").unwrap(); - println!("making srs"); - let start = Instant::now(); - let srs = SRS::::dummy(830564, srs_x, srs_alpha); - println!("done in {:?}", start.elapsed()); - - { - // This may not be cryptographically safe, use - // `OsRng` (for example) in production software. - let rng = &mut thread_rng(); - - // Generate the MiMC round constants - let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::>(); - let samples: usize = 100; - - let xl = rng.gen(); - let xr = rng.gen(); - let image = mimc::(xl, xr, &constants); - - // Create an instance of our circuit (with the - // witness) - let circuit = MiMCDemoNoInputs { - xl: Some(xl), - xr: Some(xr), - image: Some(image), - constants: &constants - }; - - use bellman::sonic::cs::Basic; - use bellman::sonic::sonic::AdaptorCircuit; - use bellman::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs}; - use bellman::sonic::helped::{MultiVerifier, get_circuit_parameters}; - use bellman::sonic::helped::helper::{create_aggregate_on_srs}; - - println!("creating proof"); - let start = Instant::now(); - let proof = create_proof_on_srs::(&AdaptorCircuit(circuit.clone()), &srs).unwrap(); - println!("done in {:?}", start.elapsed()); - - println!("creating advice"); - let start = Instant::now(); - let advice = create_advice_on_srs::(&AdaptorCircuit(circuit.clone()), &proof, &srs).unwrap(); - println!("done in {:?}", start.elapsed()); - - println!("creating aggregate for {} proofs", samples); - let start = Instant::now(); - let proofs: Vec<_> = (0..samples).map(|_| (proof.clone(), advice.clone())).collect(); - let aggregate = create_aggregate_on_srs::(&AdaptorCircuit(circuit.clone()), &proofs, &srs); - println!("done in {:?}", start.elapsed()); - - { - let rng = thread_rng(); - let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); - println!("verifying 1 proof without advice"); - let start = Instant::now(); - { - for _ in 0..1 { - verifier.add_proof(&proof, &[], |_, _| None); - } - assert_eq!(verifier.check_all(), true); // TODO - } - println!("done in {:?}", start.elapsed()); - } - - { - let rng = thread_rng(); - let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); - println!("verifying {} proofs without advice", samples); - let start = Instant::now(); - { - for _ in 0..samples { - verifier.add_proof(&proof, &[], |_, _| None); - } - assert_eq!(verifier.check_all(), true); // TODO - } - println!("done in {:?}", start.elapsed()); - } - - { - let rng = thread_rng(); - let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); - println!("verifying 100 proofs with advice"); - let start = Instant::now(); - { - for (ref proof, ref advice) in &proofs { - verifier.add_proof_with_advice(proof, &[], advice); - } - verifier.add_aggregate(&proofs, &aggregate); - assert_eq!(verifier.check_all(), true); // TODO - } - println!("done in {:?}", start.elapsed()); - } - } -} - -#[test] -fn test_inputs_into_sonic_mimc() { - use pairing::ff::{Field, PrimeField}; - use pairing::{Engine, CurveAffine, CurveProjective}; - use pairing::bn256::{Bn256, Fr}; - // use pairing::bls12_381::{Bls12, Fr}; - use std::time::{Instant}; - use bellman::sonic::srs::SRS; - - let srs_x = Fr::from_str("23923").unwrap(); - let srs_alpha = Fr::from_str("23728792").unwrap(); - println!("making srs"); - let start = Instant::now(); - let srs = SRS::::dummy(830564, srs_x, srs_alpha); - println!("done in {:?}", start.elapsed()); - - { - // This may not be cryptographically safe, use - // `OsRng` (for example) in production software. - let rng = &mut thread_rng(); - - // Generate the MiMC round constants - let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::>(); - let samples: usize = 100; - - let xl = rng.gen(); - let xr = rng.gen(); - let image = mimc::(xl, xr, &constants); - - // Create an instance of our circuit (with the - // witness) - let circuit = MiMCDemo { - xl: Some(xl), - xr: Some(xr), - constants: &constants - }; - - use bellman::sonic::cs::Basic; - use bellman::sonic::sonic::AdaptorCircuit; - use bellman::sonic::helped::prover::{create_advice_on_srs, create_proof_on_srs}; - use bellman::sonic::helped::{MultiVerifier, get_circuit_parameters}; - use bellman::sonic::helped::helper::{create_aggregate_on_srs}; - - let info = get_circuit_parameters::(circuit.clone()).expect("Must get circuit info"); - println!("{:?}", info); - - println!("creating proof"); - let start = Instant::now(); - let proof = create_proof_on_srs::(&AdaptorCircuit(circuit.clone()), &srs).unwrap(); - println!("done in {:?}", start.elapsed()); - - println!("creating advice"); - let start = Instant::now(); - let advice = create_advice_on_srs::(&AdaptorCircuit(circuit.clone()), &proof, &srs).unwrap(); - println!("done in {:?}", start.elapsed()); - - println!("creating aggregate for {} proofs", samples); - let start = Instant::now(); - let proofs: Vec<_> = (0..samples).map(|_| (proof.clone(), advice.clone())).collect(); - let aggregate = create_aggregate_on_srs::(&AdaptorCircuit(circuit.clone()), &proofs, &srs); - println!("done in {:?}", start.elapsed()); - - { - let rng = thread_rng(); - let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); - println!("verifying 1 proof without advice"); - let start = Instant::now(); - { - for _ in 0..1 { - verifier.add_proof(&proof, &[image], |_, _| None); - } - assert_eq!(verifier.check_all(), true); // TODO - } - println!("done in {:?}", start.elapsed()); - } - - { - let rng = thread_rng(); - let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); - println!("verifying {} proofs without advice", samples); - let start = Instant::now(); - { - for _ in 0..samples { - verifier.add_proof(&proof, &[image], |_, _| None); - } - assert_eq!(verifier.check_all(), true); // TODO - } - println!("done in {:?}", start.elapsed()); - } - - { - let rng = thread_rng(); - let mut verifier = MultiVerifier::::new(AdaptorCircuit(circuit.clone()), &srs, rng).unwrap(); - println!("verifying 100 proofs with advice and aggregate"); - let start = Instant::now(); - { - for (ref proof, ref advice) in &proofs { - verifier.add_proof_with_advice(proof, &[image], advice); - } - verifier.add_aggregate(&proofs, &aggregate); - assert_eq!(verifier.check_all(), true); // TODO - } - println!("done in {:?}", start.elapsed()); - } - } -} - -#[test] -fn test_high_level_sonic_api() { - use pairing::bn256::{Bn256}; - use std::time::{Instant}; - use bellman::sonic::helped::{ - generate_random_parameters, - verify_aggregate, - verify_proofs, - create_proof, - create_advice, - create_aggregate, - get_circuit_parameters - }; - - { - // This may not be cryptographically safe, use - // `OsRng` (for example) in production software. - let mut rng = &mut thread_rng(); - - // Generate the MiMC round constants - let constants = (0..MIMC_ROUNDS).map(|_| rng.gen()).collect::>(); - let samples: usize = 100; - - let xl = rng.gen(); - let xr = rng.gen(); - let image = mimc::(xl, xr, &constants); - - // Create an instance of our circuit (with the - // witness) - let circuit = MiMCDemo { - xl: Some(xl), - xr: Some(xr), - constants: &constants - }; - - let info = get_circuit_parameters::(circuit.clone()).expect("Must get circuit info"); - println!("{:?}", info); - - let params = generate_random_parameters(circuit.clone(), &mut rng).unwrap(); - - println!("creating proof"); - let start = Instant::now(); - let proof = create_proof(circuit.clone(), ¶ms).unwrap(); - println!("done in {:?}", start.elapsed()); - - println!("creating advice"); - let start = Instant::now(); - let advice = create_advice(circuit.clone(), &proof, ¶ms).unwrap(); - println!("done in {:?}", start.elapsed()); - - println!("creating aggregate for {} proofs", samples); - let start = Instant::now(); - let proofs: Vec<_> = (0..samples).map(|_| (proof.clone(), advice.clone())).collect(); - - let aggregate = create_aggregate::(circuit.clone(), &proofs, ¶ms); - println!("done in {:?}", start.elapsed()); - - { - println!("verifying 1 proof without advice"); - let rng = thread_rng(); - let start = Instant::now(); - assert_eq!(verify_proofs(&vec![proof.clone()], &vec![vec![image.clone()]], circuit.clone(), rng, ¶ms).unwrap(), true); - println!("done in {:?}", start.elapsed()); - } - - { - println!("verifying {} proofs without advice", samples); - let rng = thread_rng(); - let start = Instant::now(); - assert_eq!(verify_proofs(&vec![proof.clone(); 100], &vec![vec![image.clone()]; 100], circuit.clone(), rng, ¶ms).unwrap(), true); - println!("done in {:?}", start.elapsed()); - } - - { - println!("verifying 100 proofs with advice and aggregate"); - let rng = thread_rng(); - let start = Instant::now(); - assert_eq!(verify_aggregate(&vec![(proof.clone(), advice.clone()); 100], &aggregate, &vec![vec![image.clone()]; 100], circuit.clone(), rng, ¶ms).unwrap(), true); - println!("done in {:?}", start.elapsed()); - } - } -} \ No newline at end of file