From cb53708767096990bc99644ec6d4de38787b496f Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Wed, 14 Feb 2018 12:31:43 -0700 Subject: [PATCH] New implementation of library API. --- Cargo.toml | 2 +- examples/mimc.rs | 239 +++++++++ oldsrc/groth16/mod.rs | 424 --------------- oldsrc/groth16/prover.rs | 233 --------- oldsrc/groth16/verifier.rs | 185 ------- oldsrc/lib.rs | 534 ------------------- oldsrc/multicore.rs | 53 -- {oldsrc => src}/domain.rs | 156 +++--- {oldsrc => src}/groth16/generator.rs | 301 ++++++----- src/groth16/mod.rs | 183 +++++++ src/groth16/prover.rs | 328 ++++++++++++ src/groth16/tests/dummy_engine.rs | 455 ++++++++++++++++ src/groth16/tests/mod.rs | 400 ++++++++++++++ src/groth16/verifier.rs | 66 +++ src/lib.rs | 750 ++++++++++++++------------- src/multicore.rs | 106 ++++ {oldsrc => src}/multiexp.rs | 37 +- 17 files changed, 2441 insertions(+), 2011 deletions(-) create mode 100644 examples/mimc.rs delete mode 100644 oldsrc/groth16/mod.rs delete mode 100644 oldsrc/groth16/prover.rs delete mode 100644 oldsrc/groth16/verifier.rs delete mode 100644 oldsrc/lib.rs delete mode 100644 oldsrc/multicore.rs rename {oldsrc => src}/domain.rs (77%) rename {oldsrc => src}/groth16/generator.rs (69%) create mode 100644 src/groth16/mod.rs create mode 100644 src/groth16/prover.rs create mode 100644 src/groth16/tests/dummy_engine.rs create mode 100644 src/groth16/tests/mod.rs create mode 100644 src/groth16/verifier.rs create mode 100644 src/multicore.rs rename {oldsrc => src}/multiexp.rs (91%) diff --git a/Cargo.toml b/Cargo.toml index 78313180c..3bc34975a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ rand = "0.3" bit-vec = "0.4.4" futures = "0.1" futures-cpupool = "0.1" -num_cpus = "1.6" +num_cpus = "1" crossbeam = "0.3" pairing = "0.13" diff --git a/examples/mimc.rs b/examples/mimc.rs new file mode 100644 index 000000000..614e76537 --- /dev/null +++ b/examples/mimc.rs @@ -0,0 +1,239 @@ +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, + Field +}; + +// We're going to use the BLS12-381 pairing-friendly elliptic curve. +use pairing::bls12_381::{ + Bls12 +}; + +// 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::{ + generate_random_parameters, + prepare_verifying_key, + create_random_proof, + verify_proof, +}; + +const MIMC_ROUNDS: usize = 322; + +/// This is an implementation of MiMC, specifically a +/// variant named `LongsightF322p3` for BLS12-381. +/// See http://eprint.iacr.org/2016/492 for more +/// information about this construction. +/// +/// ``` +/// function LongsightF322p3(xL ⦂ Fp, xR ⦂ Fp) { +/// for i from 0 up to 321 { +/// xL, xR := xR + (xL + Ci)^3, xL +/// } +/// return xL +/// } +/// ``` +fn mimc( + mut xl: E::Fr, + mut xr: E::Fr, + constants: &[E::Fr] +) -> E::Fr +{ + assert_eq!(constants.len(), MIMC_ROUNDS); + + for i in 0..MIMC_ROUNDS { + let mut tmp1 = xl; + tmp1.add_assign(&constants[i]); + let mut tmp2 = tmp1; + tmp2.square(); + tmp2.mul_assign(&tmp1); + tmp2.add_assign(&xr); + xr = xl; + xl = tmp2; + } + + xl +} + +/// This is our demo circuit for proving knowledge of the +/// preimage of a MiMC hash invocation. +struct MiMCDemo<'a, E: Engine> { + xl: Option, + xr: 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 MiMCDemo<'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 allocate a public input. + cs.alloc_input(|| "image", || { + new_xl_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(()) + } +} + +fn main() { + // 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::>(); + + println!("Creating parameters..."); + + // Create parameters for our circuit + let params = { + let c = MiMCDemo:: { + xl: None, + xr: None, + constants: &constants + }; + + generate_random_parameters(c, rng).unwrap() + }; + + // Prepare the verification key (for proof verification) + let pvk = prepare_verifying_key(¶ms.vk); + + println!("Creating proofs..."); + + // Let's benchmark stuff! + const SAMPLES: u32 = 50; + let mut total_proving = Duration::new(0, 0); + let mut total_verifying = Duration::new(0, 0); + + for _ in 0..SAMPLES { + // Generate a random preimage and compute the image + let xl = rng.gen(); + let xr = rng.gen(); + let image = mimc::(xl, xr, &constants); + + let start = Instant::now(); + let proof = { + // Create an instance of our circuit (with the + // witness) + let c = MiMCDemo { + xl: Some(xl), + xr: Some(xr), + constants: &constants + }; + + // Create a groth16 proof with our parameters. + create_random_proof(c, ¶ms, rng).unwrap() + }; + total_proving += start.elapsed(); + + let start = Instant::now(); + // Check the proof + assert!(verify_proof( + &pvk, + &proof, + &[image] + ).unwrap()); + total_verifying += start.elapsed(); + } + let proving_avg = total_proving / SAMPLES; + let proving_avg = proving_avg.subsec_nanos() as f64 / 1_000_000_000f64 + + (proving_avg.as_secs() as f64); + + let verifying_avg = total_verifying / SAMPLES; + let verifying_avg = verifying_avg.subsec_nanos() as f64 / 1_000_000_000f64 + + (verifying_avg.as_secs() as f64); + + println!("Average proving time: {:?} seconds", proving_avg); + println!("Average verifying time: {:?} seconds", verifying_avg); +} diff --git a/oldsrc/groth16/mod.rs b/oldsrc/groth16/mod.rs deleted file mode 100644 index bf344e38c..000000000 --- a/oldsrc/groth16/mod.rs +++ /dev/null @@ -1,424 +0,0 @@ -use pairing::*; -use std::sync::Arc; - -mod generator; -pub use self::generator::*; -mod prover; -pub use self::prover::*; -mod verifier; -pub use self::verifier::*; - -use ::Error; -use std::io::{self, Write, Read}; -use multiexp::{Source, SourceBuilder}; - -pub struct Proof { - a: E::G1Affine, - b: E::G2Affine, - c: E::G1Affine -} - -pub struct PreparedVerifyingKey { - alpha_g1_beta_g2: E::Fqk, - neg_gamma_g2: ::Prepared, - neg_delta_g2: ::Prepared, - ic: Vec -} - -pub struct VerifyingKey { - // alpha in g1 for verifying and for creating A/C elements of - // proof. Never the point at infinity. - alpha_g1: E::G1Affine, - - // beta in g1 and g2 for verifying and for creating B/C elements - // of proof. Never the point at infinity. - beta_g1: E::G1Affine, - beta_g2: E::G2Affine, - - // gamma in g2 for verifying. Never the point at infinity. - gamma_g2: E::G2Affine, - - // delta in g1/g2 for verifying and proving, essentially the magic - // trapdoor that forces the prover to evaluate the C element of the - // proof with only components from the CRS. Never the point at - // infinity. - delta_g1: E::G1Affine, - delta_g2: E::G2Affine, - - // Elements of the form (beta * u_i(tau) + alpha v_i(tau) + w_i(tau)) / gamma - // for all public inputs. Because all public inputs have a "soundness - // of input consistency" constraint, this is the same size as the - // number of inputs, and never contains points at infinity. - ic: Vec -} - -impl Clone for VerifyingKey { - fn clone(&self) -> VerifyingKey { - VerifyingKey { - alpha_g1: self.alpha_g1.clone(), - beta_g1: self.beta_g1.clone(), - beta_g2: self.beta_g2.clone(), - gamma_g2: self.gamma_g2.clone(), - delta_g1: self.delta_g1.clone(), - delta_g2: self.delta_g2.clone(), - ic: self.ic.clone() - } - } -} - -impl PartialEq for VerifyingKey { - fn eq(&self, other: &VerifyingKey) -> bool { - self.alpha_g1 == other.alpha_g1 && - self.beta_g1 == other.beta_g1 && - self.beta_g2 == other.beta_g2 && - self.gamma_g2 == other.gamma_g2 && - self.delta_g1 == other.delta_g1 && - self.delta_g2 == other.delta_g2 && - self.ic == other.ic - } -} - -fn read_nonzero(reader: &mut R) -> Result { - let mut repr = G::Uncompressed::empty(); - reader.read_exact(repr.as_mut())?; - - let affine = repr.into_affine_unchecked(); // TODO - - match affine { - Ok(affine) => { - if affine.is_zero() { - Err(Error::UnexpectedIdentity) - } else { - Ok(affine) - } - }, - Err(e) => Err(io::Error::new(io::ErrorKind::InvalidData, e).into()) - } -} - -impl VerifyingKey { - fn size(num_ic: usize) -> usize { - let mut acc = 0; - acc += ::Uncompressed::size(); // alpha_g1 - acc += ::Uncompressed::size(); // beta_g1 - acc += ::Uncompressed::size(); // delta_g1 - acc += ::Uncompressed::size() * num_ic; // IC - acc += ::Uncompressed::size(); // beta_g2 - acc += ::Uncompressed::size(); // gamma_g2 - acc += ::Uncompressed::size(); // delta_g2 - - acc - } - - pub fn write(&self, writer: &mut W) -> Result<(), io::Error> { - writer.write_all(self.alpha_g1.into_uncompressed().as_ref())?; - writer.write_all(self.beta_g1.into_uncompressed().as_ref())?; - writer.write_all(self.beta_g2.into_uncompressed().as_ref())?; - writer.write_all(self.gamma_g2.into_uncompressed().as_ref())?; - writer.write_all(self.delta_g1.into_uncompressed().as_ref())?; - writer.write_all(self.delta_g2.into_uncompressed().as_ref())?; - for ic in &self.ic { - writer.write_all(ic.into_uncompressed().as_ref())?; - } - - Ok(()) - } - - pub fn read(reader: &mut R, num_ic: usize) -> Result, Error> { - let alpha_g1 = read_nonzero(reader)?; - let beta_g1 = read_nonzero(reader)?; - let beta_g2 = read_nonzero(reader)?; - let gamma_g2 = read_nonzero(reader)?; - let delta_g1 = read_nonzero(reader)?; - let delta_g2 = read_nonzero(reader)?; - - let mut ic = vec![]; - for _ in 0..num_ic { - ic.push(read_nonzero(reader)?); - } - - Ok(VerifyingKey { - alpha_g1: alpha_g1, - beta_g1: beta_g1, - beta_g2: beta_g2, - gamma_g2: gamma_g2, - delta_g1: delta_g1, - delta_g2: delta_g2, - ic: ic - }) - } -} - -pub struct Parameters { - pub vk: VerifyingKey, - - // Elements of the form ((tau^i * t(tau)) / delta) for i between 0 and - // m-2 inclusive. Never contains points at infinity. - h: Arc>, - - // Elements of the form (beta * u_i(tau) + alpha v_i(tau) + w_i(tau)) / delta - // for all auxillary inputs. Variables can never be unconstrained, so this - // never contains points at infinity. - l: Arc>, - - // QAP "A" polynomials evaluated at tau in the Lagrange basis. Never contains - // points at infinity: polynomials that evaluate to zero are omitted from - // the CRS and the prover can deterministically skip their evaluation. - a: Arc>, - - // QAP "B" polynomials evaluated at tau in the Lagrange basis. Needed in - // G1 and G2 for C/B queries, respectively. Never contains points at - // infinity for the same reason as the "A" polynomials. - b_g1: Arc>, - b_g2: Arc> -} - -impl Parameters { - pub fn write(&self, writer: &mut W) -> Result<(), io::Error> { - self.vk.write(writer)?; - - for e in &*self.h { - writer.write_all(e.into_uncompressed().as_ref())?; - } - - for e in &*self.l { - writer.write_all(e.into_uncompressed().as_ref())?; - } - - for e in &*self.a { - writer.write_all(e.into_uncompressed().as_ref())?; - } - - for e in &*self.b_g1 { - writer.write_all(e.into_uncompressed().as_ref())?; - } - - for e in &*self.b_g2 { - writer.write_all(e.into_uncompressed().as_ref())?; - } - - Ok(()) - } -} - -pub trait ParameterSource { - type G1Builder: SourceBuilder; - type G2Builder: SourceBuilder; - - fn get_vk(&mut self, num_ic: usize) -> Result, Error>; - 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), Error>; - fn get_b_g1(&mut self, num_inputs: usize, num_aux: usize) -> Result<(Self::G1Builder, Self::G1Builder), Error>; - fn get_b_g2(&mut self, num_inputs: usize, num_aux: usize) -> Result<(Self::G2Builder, Self::G2Builder), Error>; -} - -impl<'a, E: Engine> ParameterSource for &'a Parameters { - type G1Builder = (Arc>, usize); - type G2Builder = (Arc>, usize); - - fn get_vk(&mut self, num_ic: usize) -> Result, Error> { - assert_eq!(self.vk.ic.len(), num_ic); - - Ok(self.vk.clone()) - } - - fn get_h(&mut self, num_h: usize) -> Result { - assert_eq!(self.h.len(), num_h); - - Ok((self.h.clone(), 0)) - } - - fn get_l(&mut self, num_l: usize) -> Result { - assert_eq!(self.l.len(), num_l); - - Ok((self.l.clone(), 0)) - } - - fn get_a(&mut self, num_inputs: usize, num_aux: usize) -> Result<(Self::G1Builder, Self::G1Builder), Error> { - assert_eq!(self.a.len(), num_inputs + num_aux); - - Ok(((self.a.clone(), 0), (self.a.clone(), num_inputs))) - } - - fn get_b_g1(&mut self, num_inputs: usize, num_aux: usize) -> Result<(Self::G1Builder, Self::G1Builder), Error> { - assert_eq!(self.b_g1.len(), num_inputs + num_aux); - - Ok(((self.b_g1.clone(), 0), (self.b_g1.clone(), num_inputs))) - } - - fn get_b_g2(&mut self, num_inputs: usize, num_aux: usize) -> Result<(Self::G2Builder, Self::G2Builder), Error> { - assert_eq!(self.b_g2.len(), num_inputs + num_aux); - - Ok(((self.b_g2.clone(), 0), (self.b_g2.clone(), num_inputs))) - } -} - -use std::fs::File; -use std::io::{Seek, SeekFrom}; - -pub struct ProverStream { - path: String, - cursor: u64, - fh: Option -} - -impl Clone for ProverStream { - fn clone(&self) -> ProverStream { - ProverStream { - path: self.path.clone(), - cursor: self.cursor, - fh: None - } - } -} - -impl ProverStream { - pub fn new(path: &str) -> Result { - Ok(ProverStream { - path: path.to_string(), - cursor: 0, - fh: None - }) - } - - fn open_if_needed(&mut self) -> Result<(), Error> { - if self.fh.is_none() { - let mut fh = File::open(&self.path)?; - fh.seek(SeekFrom::Start(self.cursor))?; - - self.fh = Some(fh); - } - - Ok(()) - } -} - -impl Source for ProverStream { - fn add_assign_mixed(&mut self, to: &mut ::Projective) -> Result<(), Error> { - self.open_if_needed()?; - - let r: G = read_nonzero(self.fh.as_mut().unwrap())?; - - self.cursor += G::Uncompressed::size() as u64; - - to.add_assign_mixed(&r); - - Ok(()) - } - fn skip(&mut self, amt: usize) -> Result<(), Error> { - self.open_if_needed()?; - - let size_to_skip = amt * G::Uncompressed::size(); - - self.cursor += size_to_skip as u64; - - self.fh.as_mut().unwrap().seek(SeekFrom::Current(size_to_skip as i64))?; - - Ok(()) - } -} - -impl SourceBuilder for ProverStream { - type Source = Self; - - fn new(self) -> Self::Source { - self - } -} - -impl ParameterSource for ProverStream { - type G1Builder = ProverStream; - type G2Builder = ProverStream; - - fn get_vk(&mut self, num_ic: usize) -> Result, Error> { - self.open_if_needed()?; - - let vk = VerifyingKey::read(self.fh.as_mut().unwrap(), num_ic)?; - - self.cursor += VerifyingKey::::size(num_ic) as u64; - - Ok(vk) - } - fn get_h(&mut self, num_h: usize) -> Result { - self.open_if_needed()?; - - let res = self.clone(); - - let amount_to_seek = num_h * ::Uncompressed::size(); - - self.fh.as_mut().unwrap().seek(SeekFrom::Current(amount_to_seek as i64))?; - self.cursor += amount_to_seek as u64; - - Ok(res) - } - fn get_l(&mut self, num_l: usize) -> Result { - self.open_if_needed()?; - - let res = self.clone(); - - let amount_to_seek = num_l * ::Uncompressed::size(); - - self.fh.as_mut().unwrap().seek(SeekFrom::Current(amount_to_seek as i64))?; - self.cursor += amount_to_seek as u64; - - Ok(res) - } - fn get_a(&mut self, num_inputs: usize, num_aux: usize) -> Result<(Self::G1Builder, Self::G1Builder), Error> { - self.open_if_needed()?; - - let res1 = self.clone(); - - let amount_to_seek = num_inputs * ::Uncompressed::size(); - - self.fh.as_mut().unwrap().seek(SeekFrom::Current(amount_to_seek as i64))?; - self.cursor += amount_to_seek as u64; - - let res2 = self.clone(); - - let amount_to_seek = num_aux * ::Uncompressed::size(); - - self.fh.as_mut().unwrap().seek(SeekFrom::Current(amount_to_seek as i64))?; - self.cursor += amount_to_seek as u64; - - Ok((res1, res2)) - } - fn get_b_g1(&mut self, num_inputs: usize, num_aux: usize) -> Result<(Self::G1Builder, Self::G1Builder), Error> { - self.open_if_needed()?; - - let res1 = self.clone(); - - let amount_to_seek = num_inputs * ::Uncompressed::size(); - - self.fh.as_mut().unwrap().seek(SeekFrom::Current(amount_to_seek as i64))?; - self.cursor += amount_to_seek as u64; - - let res2 = self.clone(); - - let amount_to_seek = num_aux * ::Uncompressed::size(); - - self.fh.as_mut().unwrap().seek(SeekFrom::Current(amount_to_seek as i64))?; - self.cursor += amount_to_seek as u64; - - Ok((res1, res2)) - } - fn get_b_g2(&mut self, num_inputs: usize, num_aux: usize) -> Result<(Self::G2Builder, Self::G2Builder), Error> { - self.open_if_needed()?; - - let res1 = self.clone(); - - let amount_to_seek = num_inputs * ::Uncompressed::size(); - - self.fh.as_mut().unwrap().seek(SeekFrom::Current(amount_to_seek as i64))?; - self.cursor += amount_to_seek as u64; - - let res2 = self.clone(); - - let amount_to_seek = num_aux * ::Uncompressed::size(); - - self.fh.as_mut().unwrap().seek(SeekFrom::Current(amount_to_seek as i64))?; - self.cursor += amount_to_seek as u64; - - Ok((res1, res2)) - } -} diff --git a/oldsrc/groth16/prover.rs b/oldsrc/groth16/prover.rs deleted file mode 100644 index 93c26df32..000000000 --- a/oldsrc/groth16/prover.rs +++ /dev/null @@ -1,233 +0,0 @@ -use pairing::*; -use domain::{Scalar, EvaluationDomain}; -use ::{ - ConstraintSystem, - PublicConstraintSystem, - Circuit, - Input, - Index, - Error, - Variable, - LinearCombination, - Namespace -}; -use std::marker::PhantomData; -use multiexp::*; -use super::{ParameterSource, Proof}; -use rand::Rng; -use std::sync::Arc; -use futures::Future; -use futures_cpupool::CpuPool; - -pub fn create_random_proof>( - circuit: C, - params: P, - rng: &mut R -) -> Result, Error> - where E: Engine, C: Circuit, R: Rng -{ - let r = rng.gen(); - let s = rng.gen(); - - create_proof::(circuit, params, r, s) -} - -pub fn create_proof>( - circuit: C, - mut params: P, - r: E::Fr, - s: E::Fr -) -> Result, Error> - where E: Engine, C: Circuit -{ - struct ProvingAssignment { - // Density of queries - a_aux_density: DensityTracker, - b_input_density: DensityTracker, - b_aux_density: DensityTracker, - - // Evaluations of A, B, C polynomials - a: Vec>, - b: Vec>, - c: Vec>, - - // Assignments of variables - input_assignment: Vec, - aux_assignment: Vec - } - - impl PublicConstraintSystem for ProvingAssignment { - fn alloc_input( - &mut self, - _: N, - f: F - ) -> Result - where NR: Into, N: FnOnce() -> NR, F: FnOnce() -> Result - { - self.input_assignment.push(f()?); - self.b_input_density.add_element(); - - Ok(Variable(Index::Input(self.input_assignment.len() - 1))) - } - } - - impl ConstraintSystem for ProvingAssignment { - type Root = Self; - - fn alloc( - &mut self, - _: N, - f: F - ) -> Result - where NR: Into, N: FnOnce() -> NR, F: FnOnce() -> Result - { - self.aux_assignment.push(f()?); - self.a_aux_density.add_element(); - self.b_aux_density.add_element(); - - Ok(Variable(Index::Aux(self.aux_assignment.len() - 1))) - } - - fn enforce, N: FnOnce() -> NR>( - &mut self, - _: N, - a: LinearCombination, - b: LinearCombination, - c: LinearCombination - ) - { - self.a.push(Scalar(a.eval(None, Some(&mut self.a_aux_density), &self.input_assignment, &self.aux_assignment))); - self.b.push(Scalar(b.eval(Some(&mut self.b_input_density), Some(&mut self.b_aux_density), &self.input_assignment, &self.aux_assignment))); - self.c.push(Scalar(c.eval(None, None, &self.input_assignment, &self.aux_assignment))); - } - - /// Begin a namespace for the constraint system - fn namespace<'a, NR, N>( - &'a mut self, - _: N - ) -> Namespace<'a, E, Self::Root> - where NR: Into, N: FnOnce() -> NR - { - Namespace(self, PhantomData) - } - } - - let mut prover = ProvingAssignment { - a_aux_density: DensityTracker::new(), - b_input_density: DensityTracker::new(), - b_aux_density: DensityTracker::new(), - a: vec![], - b: vec![], - c: vec![], - input_assignment: vec![], - aux_assignment: vec![] - }; - - prover.alloc_input(|| "", || Ok(E::Fr::one()))?; - - circuit.synthesize(&mut prover)?.synthesize(&mut prover)?; - - // Input consistency constraints: x * 0 = 0 - for i in 0..prover.input_assignment.len() { - prover.enforce(|| "", - LinearCombination::zero() + Variable(Index::Input(i)), - LinearCombination::zero(), - LinearCombination::zero()); - } - - let cpupool = CpuPool::new_num_cpus(); - - let vk = params.get_vk(prover.input_assignment.len())?; - - let h = { - let mut a = EvaluationDomain::from_coeffs(prover.a)?; - let mut b = EvaluationDomain::from_coeffs(prover.b)?; - let mut c = EvaluationDomain::from_coeffs(prover.c)?; - a.ifft(); - a.coset_fft(); - b.ifft(); - b.coset_fft(); - c.ifft(); - c.coset_fft(); - - a.mul_assign(&b); - drop(b); - a.sub_assign(&c); - drop(c); - a.divide_by_z_on_coset(); - a.icoset_fft(); - let mut a = a.into_coeffs(); - let a_len = a.len() - 1; - a.truncate(a_len); - // TODO: parallelize if it's even helpful - let a = Arc::new(a.into_iter().map(|s| s.0.into_repr()).collect::>()); - - multiexp(&cpupool, params.get_h(a.len())?, FullDensity, a) - }; - - // TODO: parallelize if it's even helpful - 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 l = multiexp(&cpupool, params.get_l(aux_assignment.len())?, FullDensity, aux_assignment.clone()); - - let a_aux_density_total = prover.a_aux_density.get_total_density(); - - let (a_inputs_source, a_aux_source) = params.get_a(input_assignment.len(), a_aux_density_total)?; - - let a_inputs = multiexp(&cpupool, a_inputs_source, FullDensity, input_assignment.clone()); - let a_aux = multiexp(&cpupool, a_aux_source, Arc::new(prover.a_aux_density), aux_assignment.clone()); - - let b_input_density = Arc::new(prover.b_input_density); - let b_input_density_total = b_input_density.get_total_density(); - let b_aux_density = Arc::new(prover.b_aux_density); - let b_aux_density_total = b_aux_density.get_total_density(); - - let (b_g1_inputs_source, b_g1_aux_source) = params.get_b_g1(b_input_density_total, b_aux_density_total)?; - - let b_g1_inputs = multiexp(&cpupool, b_g1_inputs_source, b_input_density.clone(), input_assignment.clone()); - let b_g1_aux = multiexp(&cpupool, b_g1_aux_source, b_aux_density.clone(), aux_assignment.clone()); - - let (b_g2_inputs_source, b_g2_aux_source) = params.get_b_g2(b_input_density_total, b_aux_density_total)?; - - let b_g2_inputs = multiexp(&cpupool, b_g2_inputs_source, b_input_density, input_assignment.clone()); - let b_g2_aux = multiexp(&cpupool, b_g2_aux_source, b_aux_density, aux_assignment); - - drop(input_assignment); - - let mut g_a = vk.delta_g1.mul(r); - g_a.add_assign_mixed(&vk.alpha_g1); - let mut g_b = vk.delta_g2.mul(s); - g_b.add_assign_mixed(&vk.beta_g2); - let mut g_c; - { - let mut rs = r; - rs.mul_assign(&s); - - g_c = vk.delta_g1.mul(rs); - g_c.add_assign(&vk.alpha_g1.mul(s)); - g_c.add_assign(&vk.beta_g1.mul(r)); - } - let mut a_answer = a_inputs.wait()?; - a_answer.add_assign(&a_aux.wait()?); - g_a.add_assign(&a_answer); - a_answer.mul_assign(s); - g_c.add_assign(&a_answer); - - let mut b1_answer = b_g1_inputs.wait()?; - b1_answer.add_assign(&b_g1_aux.wait()?); - let mut b2_answer = b_g2_inputs.wait()?; - b2_answer.add_assign(&b_g2_aux.wait()?); - - g_b.add_assign(&b2_answer); - b1_answer.mul_assign(r); - g_c.add_assign(&b1_answer); - g_c.add_assign(&h.wait()?); - g_c.add_assign(&l.wait()?); - - Ok(Proof { - a: g_a.into_affine(), - b: g_b.into_affine(), - c: g_c.into_affine() - }) -} diff --git a/oldsrc/groth16/verifier.rs b/oldsrc/groth16/verifier.rs deleted file mode 100644 index 668fdb4ea..000000000 --- a/oldsrc/groth16/verifier.rs +++ /dev/null @@ -1,185 +0,0 @@ -use pairing::*; -use ::{ - Input, - Error, - LinearCombination, - Index, - Variable, - ConstraintSystem, - PublicConstraintSystem, - Namespace -}; -use super::{Proof, VerifyingKey, PreparedVerifyingKey}; -use std::marker::PhantomData; - -/// This is the constraint system synthesizer that is made available to -/// callers of the verification function when they wish to perform -/// allocations. In that context, allocation of inputs is not allowed. -pub struct VerifierInput<'a, E: Engine> { - acc: E::G1, - ic: &'a [E::G1Affine], - insufficient_inputs: bool, - num_inputs: usize, - num_aux: usize -} - -impl<'cs, E: Engine> ConstraintSystem for VerifierInput<'cs, E> { - type Root = Self; - - fn alloc( - &mut self, - _: N, - f: F - ) -> Result - where NR: Into, N: FnOnce() -> NR, F: FnOnce() -> Result - { - // Run the function for calculating the allocation but ignore the output, - // since we don't care about the assignment of auxillary variables during - // verification. - let _ = f(); - - let index = self.num_aux; - self.num_aux += 1; - - Ok(Variable(Index::Aux(index))) - } - - fn enforce, N: FnOnce() -> NR>( - &mut self, - _: N, - _: LinearCombination, - _: LinearCombination, - _: LinearCombination - ) - { - // Do nothing; we don't care about the constraint system - // in this context. - } - - /// Begin a namespace for the constraint system - fn namespace<'a, NR, N>( - &'a mut self, - _: N - ) -> Namespace<'a, E, Self::Root> - where NR: Into, N: FnOnce() -> NR - { - Namespace(self, PhantomData) - } -} - -/// This is intended to be a wrapper around VerifierInput that is kept -/// private and used for input allocation. -struct InputAllocator(T); - -impl<'cs, 'b, E: Engine> ConstraintSystem for InputAllocator<&'cs mut VerifierInput<'b, E>> { - type Root = Self; - - fn alloc( - &mut self, - name_fn: N, - f: F - ) -> Result - where NR: Into, N: FnOnce() -> NR, F: FnOnce() -> Result - { - self.0.alloc(name_fn, f) - } - - fn enforce, N: FnOnce() -> NR>( - &mut self, - _: N, - _: LinearCombination, - _: LinearCombination, - _: LinearCombination - ) - { - // Do nothing; we don't care about the constraint system - // in this context. - } - - /// Begin a namespace for the constraint system - fn namespace<'a, NR, N>( - &'a mut self, - _: N - ) -> Namespace<'a, E, Self::Root> - where NR: Into, N: FnOnce() -> NR - { - Namespace(self, PhantomData) - } -} - -impl<'a, 'b, E: Engine> PublicConstraintSystem for InputAllocator<&'a mut VerifierInput<'b, E>> { - fn alloc_input( - &mut self, - _: N, - f: F - ) -> Result - where NR: Into, N: FnOnce() -> NR, F: FnOnce() -> Result - { - if self.0.ic.len() == 0 { - self.0.insufficient_inputs = true; - } else { - self.0.acc.add_assign(&self.0.ic[0].mul(f()?)); - self.0.ic = &self.0.ic[1..]; - } - - let index = self.0.num_inputs; - self.0.num_inputs += 1; - - Ok(Variable(Index::Input(index))) - } -} - -pub fn verify_proof<'a, E, C, F>( - pvk: &'a PreparedVerifyingKey, - proof: &Proof, - circuit: F -) -> Result - where E: Engine, C: Input, F: FnOnce(&mut VerifierInput<'a, E>) -> Result -{ - let mut witness = VerifierInput:: { - acc: pvk.ic[0].into_projective(), - ic: &pvk.ic[1..], - insufficient_inputs: false, - num_inputs: 1, - num_aux: 0 - }; - - circuit(&mut witness)?.synthesize(&mut InputAllocator(&mut witness))?; - - if witness.ic.len() != 0 || witness.insufficient_inputs { - return Err(Error::MalformedVerifyingKey); - } - - // The original verification equation is: - // A * B = alpha * beta + inputs * gamma + C * delta - // ... however, we rearrange it so that it is: - // A * B - inputs * gamma - C * delta = alpha * beta - // or equivalently: - // A * B + inputs * (-gamma) + C * (-delta) = alpha * beta - // which allows us to do a single final exponentiation. - - Ok(E::final_exponentiation( - &E::miller_loop([ - (&proof.a.prepare(), &proof.b.prepare()), - (&witness.acc.into_affine().prepare(), &pvk.neg_gamma_g2), - (&proof.c.prepare(), &pvk.neg_delta_g2) - ].into_iter()) - ).unwrap() == pvk.alpha_g1_beta_g2) -} - -pub fn prepare_verifying_key( - vk: &VerifyingKey -) -> PreparedVerifyingKey -{ - let mut gamma = vk.gamma_g2; - gamma.negate(); - let mut delta = vk.delta_g2; - delta.negate(); - - PreparedVerifyingKey { - alpha_g1_beta_g2: E::pairing(vk.alpha_g1, vk.beta_g2), - neg_gamma_g2: gamma.prepare(), - neg_delta_g2: delta.prepare(), - ic: vk.ic.clone() - } -} diff --git a/oldsrc/lib.rs b/oldsrc/lib.rs deleted file mode 100644 index bf78edfe1..000000000 --- a/oldsrc/lib.rs +++ /dev/null @@ -1,534 +0,0 @@ -extern crate pairing; -extern crate rand; -extern crate bit_vec; -extern crate futures; -extern crate futures_cpupool; -extern crate num_cpus; -extern crate crossbeam; - -use pairing::{Engine, Field}; -use std::ops::{Add, Sub}; -use std::io; - -pub mod multicore; -pub mod domain; -pub mod groth16; -pub mod multiexp; - -#[derive(Debug)] -pub enum Error { - PolynomialDegreeTooLarge, - MalformedVerifyingKey, - AssignmentMissing, - UnexpectedIdentity, - UnconstrainedVariable(Variable), - IoError(io::Error) -} - -impl From for Error { - fn from(e: io::Error) -> Error { - Error::IoError(e) - } -} - -#[derive(Copy, Clone, Debug)] -pub struct Variable(Index); - -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] -enum Index { - Input(usize), - Aux(usize) -} - -pub struct LinearCombination(Vec<(Index, E::Fr)>); - -impl Clone for LinearCombination { - fn clone(&self) -> LinearCombination { - LinearCombination(self.0.clone()) - } -} - -impl LinearCombination { - pub fn zero() -> LinearCombination { - LinearCombination(vec![]) - } - - pub fn eval( - &self, - mut input_density: Option<&mut multiexp::DensityTracker>, - mut aux_density: Option<&mut multiexp::DensityTracker>, - input_assignment: &[E::Fr], - aux_assignment: &[E::Fr] - ) -> E::Fr - { - let mut acc = E::Fr::zero(); - - for &(index, coeff) in self.0.iter() { - let mut tmp; - - match index { - Index::Input(i) => { - tmp = input_assignment[i]; - if let Some(ref mut v) = input_density { - v.inc(i); - } - }, - Index::Aux(i) => { - tmp = aux_assignment[i]; - if let Some(ref mut v) = aux_density { - v.inc(i); - } - } - } - - if coeff == E::Fr::one() { - acc.add_assign(&tmp); - } else { - tmp.mul_assign(&coeff); - acc.add_assign(&tmp); - } - } - - acc - } -} - -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 Add<(E::Fr, Variable)> for LinearCombination { - type Output = LinearCombination; - - fn add(mut self, (coeff, var): (E::Fr, Variable)) -> LinearCombination { - let mut must_insert = true; - - for &mut (ref index, ref mut fr) in &mut self.0 { - if *index == var.0 { - fr.add_assign(&coeff); - must_insert = false; - break; - } - } - - if must_insert { - self.0.push((var.0, 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<'a, E: Engine> Add<&'a LinearCombination> for LinearCombination { - type Output = LinearCombination; - - fn add(mut self, other: &'a LinearCombination) -> LinearCombination { - for &(k, v) in other.0.iter() { - self = self + (v, Variable(k)); - } - - self - } -} - -impl<'a, E: Engine> Sub<&'a LinearCombination> for LinearCombination { - type Output = LinearCombination; - - fn sub(mut self, other: &'a LinearCombination) -> LinearCombination { - for &(k, v) in other.0.iter() { - self = self - (v, Variable(k)); - } - - self - } -} - -pub trait Circuit { - type InputMap: Input; - - /// Synthesize the circuit into a rank-1 quadratic constraint system - #[must_use] - fn synthesize>(self, cs: &mut CS) -> Result; -} - -pub trait Input { - /// Synthesize the circuit, except with additional access to public input - /// variables - fn synthesize>(self, cs: &mut CS) -> Result<(), Error>; -} - -pub trait PublicConstraintSystem: ConstraintSystem { - /// Allocate a public input that the verifier knows. The provided function is used to - /// determine the assignment of the variable. - fn alloc_input( - &mut self, - name_fn: N, - f: F - ) -> Result - where NR: Into, N: FnOnce() -> NR, F: FnOnce() -> Result; -} - -pub trait ConstraintSystem: Sized { - type Root: ConstraintSystem; - - /// Return the "one" input variable - fn one() -> Variable { - Variable(Index::Input(0)) - } - - /// Allocate a private variable in the constraint system. The provided function is used to - /// determine the assignment of the variable. - fn alloc( - &mut self, - name_fn: N, - f: F - ) -> Result - where NR: Into, N: FnOnce() -> NR, F: FnOnce() -> Result; - - /// Enforce that `A` * `B` = `C`. - fn enforce, N: FnOnce() -> NR>( - &mut self, - name_fn: N, - a: LinearCombination, - b: LinearCombination, - c: LinearCombination - ); - - fn push_namespace(&mut self, _: N) - where NR: Into, N: FnOnce() -> NR - { - // Default is to do nothing. - } - - fn pop_namespace(&mut self) - { - // Default is to do nothing. - } - - /// Begin a namespace for the constraint system - fn namespace<'a, NR, N>( - &'a mut self, - name_fn: N - ) -> Namespace<'a, E, Self::Root> - where NR: Into, N: FnOnce() -> NR; -} - -impl<'cs, E: Engine, CS: ConstraintSystem> ConstraintSystem for &'cs mut CS { - type Root = CS::Root; - - /// Allocate a private variable in the constraint system. The provided function is used to - /// determine the assignment of the variable. - fn alloc( - &mut self, - name_fn: N, - f: F - ) -> Result - where NR: Into, N: FnOnce() -> NR, F: FnOnce() -> Result - { - (*self).alloc(name_fn, f) - } - - /// Enforce that `A` * `B` = `C`. - fn enforce, N: FnOnce() -> NR>( - &mut self, - name_fn: N, - a: LinearCombination, - b: LinearCombination, - c: LinearCombination - ) - { - (*self).enforce(name_fn, 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() - } - - /// Begin a namespace for the 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).namespace(name_fn) - } -} - -use std::marker::PhantomData; - -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; - - fn alloc( - &mut self, - name_fn: N, - f: F - ) -> Result - where NR: Into, N: FnOnce() -> NR, F: FnOnce() -> Result - { - self.0.alloc(name_fn, f) - } - - fn enforce, N: FnOnce() -> NR>( - &mut self, - name_fn: N, - a: LinearCombination, - b: LinearCombination, - c: LinearCombination - ) - { - self.0.enforce(name_fn, a, b, c) - } - - fn push_namespace(&mut self, name_fn: N) - where NR: Into, N: FnOnce() -> NR - { - self.0.push_namespace(name_fn); - } - - fn pop_namespace(&mut self) - { - self.0.pop_namespace(); - } - - /// Begin a namespace for the 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.0.push_namespace(name_fn); - - Namespace(self.0, PhantomData) - } -} - -impl<'a, E: Engine, CS: ConstraintSystem> Drop for Namespace<'a, E, CS> { - fn drop(&mut self) { - self.0.pop_namespace() - } -} - -use std::collections::HashMap; - -#[derive(Debug)] -enum NamedObject { - Constraint(usize), - Input(usize), - Aux(usize), - Namespace -} - -/// Constraint system for testing purposes. -pub struct TestConstraintSystem { - named_objects: HashMap, - current_namespace: Vec, - constraints: Vec<(LinearCombination, LinearCombination, LinearCombination, String)>, - inputs: Vec, - aux: Vec -} - -impl TestConstraintSystem { - pub fn new() -> TestConstraintSystem { - TestConstraintSystem { - named_objects: HashMap::new(), - current_namespace: vec![], - constraints: vec![], - inputs: vec![E::Fr::one()], - aux: vec![] - } - } - - pub fn which_is_unsatisfied(&self) -> Option<&str> { - for &(ref a, ref b, ref c, ref path) in &self.constraints { - let mut a = a.eval(None, None, &self.inputs, &self.aux); - let b = b.eval(None, None, &self.inputs, &self.aux); - let c = c.eval(None, None, &self.inputs, &self.aux); - - a.mul_assign(&b); - - if a != c { - return Some(&*path) - } - } - - None - } - - pub fn is_satisfied(&self) -> bool - { - self.which_is_unsatisfied().is_none() - } - - pub fn num_constraints(&self) -> usize - { - self.constraints.len() - } - - pub fn assign(&mut self, path: &str, to: E::Fr) - { - match self.named_objects.get(path) { - Some(&NamedObject::Input(index)) => self.inputs[index] = to, - Some(&NamedObject::Aux(index)) => self.aux[index] = to, - Some(e) => panic!("tried to assign `{:?}` a value at path: {}", e, path), - _ => panic!("no variable exists at path: {}", path) - } - } - - pub fn get(&mut self, path: &str) -> E::Fr - { - match self.named_objects.get(path) { - Some(&NamedObject::Input(index)) => self.inputs[index], - Some(&NamedObject::Aux(index)) => self.aux[index], - Some(e) => panic!("tried to get value of `{:?}` at path: {}", e, path), - _ => panic!("no variable exists at path: {}", path) - } - } - - fn set_named_obj(&mut self, path: String, to: NamedObject) { - if self.named_objects.contains_key(&path) { - panic!("tried to create object at existing path: {}", path); - } - - self.named_objects.insert(path, to); - } -} - -fn compute_path(ns: &[String], this: String) -> String { - if this.chars().any(|a| a == '/') { - panic!("'/' is not allowed in names"); - } - - let mut name = String::new(); - - let mut needs_separation = false; - for ns in ns.iter().chain(Some(&this).into_iter()) - { - if needs_separation { - name += "/"; - } - - name += ns; - needs_separation = true; - } - - name -} - -impl PublicConstraintSystem for TestConstraintSystem { - fn alloc_input( - &mut self, - name_fn: N, - f: F - ) -> Result - where NR: Into, N: FnOnce() -> NR, F: FnOnce() -> Result - { - let this_path = compute_path(&self.current_namespace, name_fn().into()); - let this_obj = NamedObject::Input(self.inputs.len()); - self.set_named_obj(this_path, this_obj); - - let var = Variable(Index::Input(self.inputs.len())); - - self.inputs.push(f()?); - - Ok(var) - } -} - -impl ConstraintSystem for TestConstraintSystem { - type Root = Self; - - fn alloc( - &mut self, - name_fn: N, - f: F - ) -> Result - where NR: Into, N: FnOnce() -> NR, F: FnOnce() -> Result - { - let this_path = compute_path(&self.current_namespace, name_fn().into()); - let this_obj = NamedObject::Aux(self.aux.len()); - self.set_named_obj(this_path, this_obj); - - let var = Variable(Index::Aux(self.aux.len())); - - self.aux.push(f()?); - - Ok(var) - } - - fn enforce, N: FnOnce() -> NR>( - &mut self, - name_fn: N, - a: LinearCombination, - b: LinearCombination, - c: LinearCombination - ) - { - let this_path = compute_path(&self.current_namespace, name_fn().into()); - let this_obj = NamedObject::Constraint(self.constraints.len()); - self.set_named_obj(this_path.clone(), this_obj); - - self.constraints.push((a, b, c, this_path)); - } - - fn push_namespace(&mut self, name_fn: N) - where NR: Into, N: FnOnce() -> NR - { - let name = name_fn().into(); - let this_path = compute_path(&self.current_namespace, name.clone()); - - self.set_named_obj(this_path, NamedObject::Namespace); - - self.current_namespace.push(name); - } - - fn pop_namespace(&mut self) - { - self.current_namespace.pop(); - } - - /// Begin a namespace for the 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.push_namespace(name_fn); - - Namespace(self, PhantomData) - } -} diff --git a/oldsrc/multicore.rs b/oldsrc/multicore.rs deleted file mode 100644 index 632c5a5c6..000000000 --- a/oldsrc/multicore.rs +++ /dev/null @@ -1,53 +0,0 @@ -use crossbeam::{self, Scope, ScopedJoinHandle}; -use num_cpus; - -pub enum MaybeJoinHandle { - MultiThreaded(ScopedJoinHandle), - SingleThreaded(T) -} - -impl MaybeJoinHandle { - pub fn join(self) -> T { - match self { - MaybeJoinHandle::MultiThreaded(scope) => scope.join(), - MaybeJoinHandle::SingleThreaded(t) => t - } - } -} - -#[derive(Clone, Copy)] -pub enum MaybeScope<'a, 'b: 'a> { - MultiThreaded(&'a Scope<'b>), - SingleThreaded -} - -impl<'a, 'b> MaybeScope<'a, 'b> { - pub fn spawn(&self, f: F) -> MaybeJoinHandle - where F: FnOnce() -> T + Send + 'b, T: Send + 'b - { - match self { - &MaybeScope::MultiThreaded(scope) => MaybeJoinHandle::MultiThreaded(scope.spawn(f)), - &MaybeScope::SingleThreaded => MaybeJoinHandle::SingleThreaded(f()) - } - } -} - -pub fn scope<'a, F, R>( - elements: usize, - f: F -) -> R where F: for<'b> FnOnce(MaybeScope<'b, 'a>, usize) -> R -{ - let num_cpus = num_cpus::get(); - - if elements <= num_cpus { - if elements == 0 { - f(MaybeScope::SingleThreaded, 1) - } else { - f(MaybeScope::SingleThreaded, elements) - } - } else { - crossbeam::scope(|scope| { - f(MaybeScope::MultiThreaded(scope), elements / num_cpus) - }) - } -} diff --git a/oldsrc/domain.rs b/src/domain.rs similarity index 77% rename from oldsrc/domain.rs rename to src/domain.rs index abbe8b7fd..6257a80b4 100644 --- a/oldsrc/domain.rs +++ b/src/domain.rs @@ -1,10 +1,14 @@ -use pairing::*; +use pairing::{ + Engine, + Field, + PrimeField +}; + use super::{ - Error + SynthesisError }; -use crossbeam; -use num_cpus; -use multicore; + +use super::multicore::Worker; const LARGEST_POLYNOMIAL_DEGREE: usize = 1 << 28; @@ -22,36 +26,43 @@ impl> EvaluationDomain { &self.coeffs } - pub fn into_coeffs(self) -> Vec { - self.coeffs - } - pub fn as_mut(&mut self) -> &mut [G] { &mut self.coeffs } - pub fn from_coeffs(mut coeffs: Vec) -> Result, Error> + pub fn into_coeffs(self) -> Vec { + self.coeffs + } + + pub fn from_coeffs(mut coeffs: Vec) -> Result, SynthesisError> { + // For platform compatibility, we expect not to + // deal with these kinds of large polynomials. if coeffs.len() > LARGEST_POLYNOMIAL_DEGREE { - return Err(Error::PolynomialDegreeTooLarge) + return Err(SynthesisError::PolynomialDegreeTooLarge) } + // Compute the size of our evaluation domain let mut m = 1; let mut exp = 0; 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(Error::PolynomialDegreeTooLarge) + return Err(SynthesisError::PolynomialDegreeTooLarge) } } + // 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 the coeffs vector with zeroes if necessary coeffs.resize(m, G::group_zero()); Ok(EvaluationDomain { @@ -64,16 +75,16 @@ impl> EvaluationDomain { }) } - pub fn fft(&mut self) + pub fn fft(&mut self, worker: &Worker) { - best_fft(&mut self.coeffs, &self.omega, self.exp); + best_fft(&mut self.coeffs, worker, &self.omega, self.exp); } - pub fn ifft(&mut self) + pub fn ifft(&mut self, worker: &Worker) { - best_fft(&mut self.coeffs, &self.omegainv, self.exp); + best_fft(&mut self.coeffs, worker, &self.omegainv, self.exp); - multicore::scope(self.coeffs.len(), |scope, chunk| { + worker.scope(self.coeffs.len(), |scope, chunk| { let minv = self.minv; for v in self.coeffs.chunks_mut(chunk) { @@ -86,9 +97,9 @@ impl> EvaluationDomain { }); } - fn mul_coset(&mut self, g: E::Fr) + pub fn distribute_powers(&mut self, worker: &Worker, g: E::Fr) { - multicore::scope(self.coeffs.len(), |scope, chunk| { + worker.scope(self.coeffs.len(), |scope, chunk| { for (i, v) in self.coeffs.chunks_mut(chunk).enumerate() { scope.spawn(move || { let mut u = g.pow(&[(i * chunk) as u64]); @@ -101,18 +112,18 @@ impl> EvaluationDomain { }); } - pub fn coset_fft(&mut self) + pub fn coset_fft(&mut self, worker: &Worker) { - self.mul_coset(E::Fr::multiplicative_generator()); - self.fft(); + self.distribute_powers(worker, E::Fr::multiplicative_generator()); + self.fft(worker); } - pub fn icoset_fft(&mut self) + pub fn icoset_fft(&mut self, worker: &Worker) { let geninv = self.geninv; - self.ifft(); - self.mul_coset(geninv); + self.ifft(worker); + self.distribute_powers(worker, geninv); } pub fn z(&self, tau: &E::Fr) -> E::Fr { @@ -122,11 +133,11 @@ impl> EvaluationDomain { tmp } - pub fn divide_by_z_on_coset(&mut self) + pub fn divide_by_z_on_coset(&mut self, worker: &Worker) { let i = self.z(&E::Fr::multiplicative_generator()).inverse().unwrap(); - multicore::scope(self.coeffs.len(), |scope, chunk| { + worker.scope(self.coeffs.len(), |scope, chunk| { for v in self.coeffs.chunks_mut(chunk) { scope.spawn(move || { for v in v { @@ -137,10 +148,10 @@ impl> EvaluationDomain { }); } - pub fn mul_assign(&mut self, other: &EvaluationDomain>) { + pub fn mul_assign(&mut self, worker: &Worker, other: &EvaluationDomain>) { assert_eq!(self.coeffs.len(), other.coeffs.len()); - multicore::scope(self.coeffs.len(), |scope, chunk| { + worker.scope(self.coeffs.len(), |scope, chunk| { for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) { scope.spawn(move || { for (a, b) in a.iter_mut().zip(b.iter()) { @@ -151,10 +162,10 @@ impl> EvaluationDomain { }); } - pub fn sub_assign(&mut self, other: &EvaluationDomain) { + pub fn sub_assign(&mut self, worker: &Worker, other: &EvaluationDomain) { assert_eq!(self.coeffs.len(), other.coeffs.len()); - multicore::scope(self.coeffs.len(), |scope, chunk| { + worker.scope(self.coeffs.len(), |scope, chunk| { for (a, b) in self.coeffs.chunks_mut(chunk).zip(other.coeffs.chunks(chunk)) { scope.spawn(move || { for (a, b) in a.iter_mut().zip(b.iter()) { @@ -204,43 +215,14 @@ impl Group for Scalar { } } -fn get_log_cpus() -> u32 { - let num = num_cpus::get(); - log2_floor(num) -} - -fn log2_floor(num: usize) -> u32 { - assert!(num > 0); - - let mut pow = 0; - - while (1 << (pow+1)) <= num { - pow += 1; - } - - pow -} - -#[test] -fn test_log2_floor() { - assert_eq!(log2_floor(1), 0); - assert_eq!(log2_floor(2), 1); - assert_eq!(log2_floor(3), 1); - assert_eq!(log2_floor(4), 2); - assert_eq!(log2_floor(5), 2); - assert_eq!(log2_floor(6), 2); - assert_eq!(log2_floor(7), 2); - assert_eq!(log2_floor(8), 3); -} - -fn best_fft>(a: &mut [T], omega: &E::Fr, log_n: u32) +fn best_fft>(a: &mut [T], worker: &Worker, omega: &E::Fr, log_n: u32) { - let log_cpus = get_log_cpus(); + let log_cpus = worker.log_num_cpus(); - if log_n < log_cpus { + if log_n <= log_cpus { serial_fft(a, omega, log_n); } else { - parallel_fft(a, omega, log_n, log_cpus); + parallel_fft(a, worker, omega, log_n, log_cpus); } } @@ -289,7 +271,13 @@ fn serial_fft>(a: &mut [T], omega: &E::Fr, log_n: u32) } } -fn parallel_fft>(a: &mut [T], omega: &E::Fr, log_n: u32, log_cpus: u32) +fn parallel_fft>( + a: &mut [T], + worker: &Worker, + omega: &E::Fr, + log_n: u32, + log_cpus: u32 +) { assert!(log_n >= log_cpus); @@ -298,7 +286,7 @@ fn parallel_fft>(a: &mut [T], omega: &E::Fr, log_n: u32, let mut tmp = vec![vec![T::group_zero(); 1 << log_new_n]; num_cpus]; let new_omega = omega.pow(&[num_cpus as u64]); - crossbeam::scope(|scope| { + worker.scope(0, |scope, _| { let a = &*a; for (j, tmp) in tmp.iter_mut().enumerate() { @@ -326,7 +314,7 @@ fn parallel_fft>(a: &mut [T], omega: &E::Fr, log_n: u32, }); // TODO: does this hurt or help? - multicore::scope(a.len(), |scope, chunk| { + worker.scope(a.len(), |scope, chunk| { let tmp = &tmp; for (idx, a) in a.chunks_mut(chunk).enumerate() { @@ -351,6 +339,8 @@ fn polynomial_arith() { fn test_mul(rng: &mut R) { + let worker = Worker::new(); + for coeffs_a in 0..70 { for coeffs_b in 0..70 { let mut a: Vec<_> = (0..coeffs_a).map(|_| Scalar::(E::Fr::rand(rng))).collect(); @@ -372,10 +362,10 @@ fn polynomial_arith() { let mut a = EvaluationDomain::from_coeffs(a).unwrap(); let mut b = EvaluationDomain::from_coeffs(b).unwrap(); - a.fft(); - b.fft(); - a.mul_assign(&b); - a.ifft(); + a.fft(&worker); + b.fft(&worker); + a.mul_assign(&worker, &b); + a.ifft(&worker); for (naive, fft) in naive.iter().zip(a.coeffs.iter()) { assert!(naive == fft); @@ -396,6 +386,8 @@ fn fft_composition() { fn test_comp(rng: &mut R) { + let worker = Worker::new(); + for coeffs in 0..10 { let coeffs = 1 << coeffs; @@ -405,17 +397,17 @@ fn fft_composition() { } let mut domain = EvaluationDomain::from_coeffs(v.clone()).unwrap(); - domain.ifft(); - domain.fft(); + domain.ifft(&worker); + domain.fft(&worker); assert!(v == domain.coeffs); - domain.fft(); - domain.ifft(); + domain.fft(&worker); + domain.ifft(&worker); assert!(v == domain.coeffs); - domain.icoset_fft(); - domain.coset_fft(); + domain.icoset_fft(&worker); + domain.coset_fft(&worker); assert!(v == domain.coeffs); - domain.coset_fft(); - domain.icoset_fft(); + domain.coset_fft(&worker); + domain.icoset_fft(&worker); assert!(v == domain.coeffs); } } @@ -433,6 +425,8 @@ fn parallel_fft_consistency() { fn test_consistency(rng: &mut R) { + let worker = Worker::new(); + for _ in 0..5 { for log_d in 0..10 { let d = 1 << log_d; @@ -441,8 +435,8 @@ fn parallel_fft_consistency() { let mut v1 = EvaluationDomain::from_coeffs(v1).unwrap(); let mut v2 = EvaluationDomain::from_coeffs(v1.coeffs.clone()).unwrap(); - for log_cpus in 0..min(log_d, 3) { - parallel_fft(&mut v1.coeffs, &v1.omega, log_d, log_cpus); + for log_cpus in log_d..min(log_d+1, 3) { + parallel_fft(&mut v1.coeffs, &worker, &v1.omega, log_d, log_cpus); serial_fft(&mut v2.coeffs, &v2.omega, log_d); assert!(v1.coeffs == v2.coeffs); diff --git a/oldsrc/groth16/generator.rs b/src/groth16/generator.rs similarity index 69% rename from oldsrc/groth16/generator.rs rename to src/groth16/generator.rs index 8f71fb567..6ee777501 100644 --- a/oldsrc/groth16/generator.rs +++ b/src/groth16/generator.rs @@ -1,26 +1,45 @@ -use pairing::*; +use rand::Rng; + +use std::sync::Arc; + +use pairing::{ + Engine, + PrimeField, + Field, + Wnaf, + CurveProjective, + CurveAffine +}; + +use super::{ + Parameters, + VerifyingKey +}; + use ::{ - Input, - Error, - LinearCombination, - Index, + SynthesisError, Circuit, - Variable, ConstraintSystem, - PublicConstraintSystem, - Namespace + LinearCombination, + Variable, + Index }; -use std::marker::PhantomData; -use super::{VerifyingKey, Parameters}; -use domain::{Scalar, EvaluationDomain}; -use rand::Rng; -use multicore; -use std::sync::Arc; +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, Error> +) -> Result, SynthesisError> where E: Engine, C: Circuit, R: Rng { let g1 = rng.gen(); @@ -43,119 +62,126 @@ pub fn generate_random_parameters( ) } -/// Create parameters for a circuit, given some trapdoors. -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, Error> - where E: Engine, C: Circuit -{ - // This is our assembly structure that we'll use to synthesize the - // circuit into a QAP. - struct KeypairAssembly { - num_inputs: usize, - num_aux: usize, - num_constraints: usize, - at_inputs: Vec>, - bt_inputs: Vec>, - ct_inputs: Vec>, - at_aux: Vec>, - bt_aux: Vec>, - ct_aux: Vec> - } +/// This is our assembly structure that we'll use to synthesize the +/// circuit into a QAP. +struct KeypairAssembly { + num_inputs: usize, + num_aux: usize, + num_constraints: usize, + at_inputs: Vec>, + bt_inputs: Vec>, + ct_inputs: Vec>, + at_aux: Vec>, + bt_aux: Vec>, + ct_aux: Vec> +} - impl PublicConstraintSystem for KeypairAssembly { - fn alloc_input( - &mut self, - _: N, - f: F - ) -> Result - where NR: Into, N: FnOnce() -> NR, F: FnOnce() -> Result - { - // In this context, we don't have an assignment. - let _ = f(); +impl ConstraintSystem for KeypairAssembly { + type Root = Self; - let index = self.num_inputs; - self.num_inputs += 1; + 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. - self.at_inputs.push(vec![]); - self.bt_inputs.push(vec![]); - self.ct_inputs.push(vec![]); + let index = self.num_aux; + self.num_aux += 1; - Ok(Variable(Index::Input(index))) - } - } + self.at_aux.push(vec![]); + self.bt_aux.push(vec![]); + self.ct_aux.push(vec![]); - impl ConstraintSystem for KeypairAssembly { - type Root = Self; + Ok(Variable(Index::Aux(index))) + } - fn alloc( - &mut self, - _: N, - f: F - ) -> Result - where NR: Into, N: FnOnce() -> NR, F: FnOnce() -> Result - { - // In this context, we don't have an assignment. - let _ = f(); + 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_aux; - self.num_aux += 1; + let index = self.num_inputs; + self.num_inputs += 1; - self.at_aux.push(vec![]); - self.bt_aux.push(vec![]); - self.ct_aux.push(vec![]); + self.at_inputs.push(vec![]); + self.bt_inputs.push(vec![]); + self.ct_inputs.push(vec![]); - Ok(Variable(Index::Aux(index))) - } + Ok(Variable(Index::Input(index))) + } - fn enforce, N: FnOnce() -> NR>( - &mut self, - _: N, - a: LinearCombination, - b: LinearCombination, - c: LinearCombination + 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 + { + fn eval( + l: LinearCombination, + inputs: &mut [Vec<(E::Fr, usize)>], + aux: &mut [Vec<(E::Fr, usize)>], + this_constraint: usize ) { - fn qap_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 { - Index::Input(id) => inputs[id].push((coeff, this_constraint)), - Index::Aux(id) => aux[id].push((coeff, this_constraint)) - } + 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)) } } + } - qap_eval(a, &mut self.at_inputs, &mut self.at_aux, self.num_constraints); - qap_eval(b, &mut self.bt_inputs, &mut self.bt_aux, self.num_constraints); - qap_eval(c, &mut self.ct_inputs, &mut self.ct_aux, self.num_constraints); + eval(a(LinearCombination::zero()), &mut self.at_inputs, &mut self.at_aux, self.num_constraints); + eval(b(LinearCombination::zero()), &mut self.bt_inputs, &mut self.bt_aux, self.num_constraints); + eval(c(LinearCombination::zero()), &mut self.ct_inputs, &mut self.ct_aux, self.num_constraints); - self.num_constraints += 1; - } + self.num_constraints += 1; + } - /// Begin a namespace for the constraint system - fn namespace<'a, NR, N>( - &'a mut self, - _: N - ) -> Namespace<'a, E, Self::Root> - where NR: Into, N: FnOnce() -> NR - { - Namespace(self, PhantomData) - } + 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> + where E: Engine, C: Circuit +{ let mut assembly = KeypairAssembly { num_inputs: 0, num_aux: 0, @@ -172,27 +198,18 @@ pub fn generate_parameters( assembly.alloc_input(|| "", || Ok(E::Fr::one()))?; // Synthesize the circuit. - circuit.synthesize(&mut assembly)?.synthesize(&mut assembly)?; + circuit.synthesize(&mut assembly)?; // Input consistency constraints: x * 0 = 0 for i in 0..assembly.num_inputs { assembly.enforce(|| "", - LinearCombination::zero() + Variable(Index::Input(i)), - LinearCombination::zero(), - LinearCombination::zero()); + |lc| lc + Variable(Index::Input(i)), + |lc| lc, + |lc| lc, + ); } - // Ensure that all auxillary variables are constrained - for i in 0..assembly.num_aux { - if assembly.at_aux[i].len() == 0 && - assembly.bt_aux[i].len() == 0 && - assembly.ct_aux[i].len() == 0 - { - return Err(Error::UnconstrainedVariable(Variable(Index::Aux(i)))); - } - } - - // Create evaluation domain for the QAP + // Create bases for blind evaluation of polynomials at tau let powers_of_tau = vec![Scalar::(E::Fr::zero()); assembly.num_constraints]; let mut powers_of_tau = EvaluationDomain::from_coeffs(powers_of_tau)?; @@ -216,16 +233,17 @@ pub fn generate_parameters( assembly.num_inputs + assembly.num_aux }); - let gamma_inverse = gamma.inverse().ok_or(Error::UnexpectedIdentity)?; - let delta_inverse = delta.inverse().ok_or(Error::UnexpectedIdentity)?; + let gamma_inverse = gamma.inverse().ok_or(SynthesisError::UnexpectedIdentity)?; + let delta_inverse = delta.inverse().ok_or(SynthesisError::UnexpectedIdentity)?; + + let worker = Worker::new(); - // Compute the H query let mut h = vec![E::G1::zero(); powers_of_tau.as_ref().len() - 1]; { - // Compute the powers of tau + // Compute powers of tau { let powers_of_tau = powers_of_tau.as_mut(); - multicore::scope(powers_of_tau.len(), |scope, chunk| { + worker.scope(powers_of_tau.len(), |scope, chunk| { for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate() { scope.spawn(move || { @@ -245,7 +263,7 @@ pub fn generate_parameters( coeff.mul_assign(&delta_inverse); // Compute the H query with multiple threads - multicore::scope(h.len(), |scope, chunk| { + worker.scope(h.len(), |scope, chunk| { for (h, p) in h.chunks_mut(chunk).zip(powers_of_tau.as_ref().chunks(chunk)) { let mut g1_wnaf = g1_wnaf.shared(); @@ -270,7 +288,7 @@ pub fn generate_parameters( } // Use inverse FFT to convert powers of tau to Lagrange coefficients - powers_of_tau.ifft(); + powers_of_tau.ifft(&worker); let powers_of_tau = powers_of_tau.into_coeffs(); let mut a = vec![E::G1::zero(); assembly.num_inputs + assembly.num_aux]; @@ -303,7 +321,10 @@ pub fn generate_parameters( // Trapdoors alpha: &E::Fr, - beta: &E::Fr + beta: &E::Fr, + + // Worker + worker: &Worker ) { // Sanity check @@ -315,7 +336,7 @@ pub fn generate_parameters( assert_eq!(a.len(), ext.len()); // Evaluate polynomials in multiple threads - multicore::scope(a.len(), |scope, chunk| { + worker.scope(a.len(), |scope, chunk| { for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a.chunks_mut(chunk) .zip(b_g1.chunks_mut(chunk)) .zip(b_g2.chunks_mut(chunk)) @@ -404,7 +425,8 @@ pub fn generate_parameters( &mut ic, &gamma_inverse, &alpha, - &beta + &beta, + &worker ); // Evaluate for auxillary variables. @@ -421,9 +443,18 @@ pub fn generate_parameters( &mut l, &delta_inverse, &alpha, - &beta + &beta, + &worker ); + // 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(); diff --git a/src/groth16/mod.rs b/src/groth16/mod.rs new file mode 100644 index 000000000..19cffc2d5 --- /dev/null +++ b/src/groth16/mod.rs @@ -0,0 +1,183 @@ +use pairing::{ + Engine, + CurveAffine +}; + +use ::{ + SynthesisError +}; + +use multiexp::SourceBuilder; + +use std::sync::Arc; + +#[cfg(test)] +mod tests; + +mod generator; +mod prover; +mod verifier; + +pub use self::generator::*; +pub use self::prover::*; +pub use self::verifier::*; + +#[derive(Clone)] +pub struct Proof { + a: E::G1Affine, + b: E::G2Affine, + c: E::G1Affine +} + +#[derive(Clone)] +pub struct VerifyingKey { + // alpha in g1 for verifying and for creating A/C elements of + // proof. Never the point at infinity. + alpha_g1: E::G1Affine, + + // beta in g1 and g2 for verifying and for creating B/C elements + // of proof. Never the point at infinity. + beta_g1: E::G1Affine, + beta_g2: E::G2Affine, + + // gamma in g2 for verifying. Never the point at infinity. + gamma_g2: E::G2Affine, + + // delta in g1/g2 for verifying and proving, essentially the magic + // trapdoor that forces the prover to evaluate the C element of the + // proof with only components from the CRS. Never the point at + // infinity. + delta_g1: E::G1Affine, + delta_g2: E::G2Affine, + + // Elements of the form (beta * u_i(tau) + alpha v_i(tau) + w_i(tau)) / gamma + // for all public inputs. Because all public inputs have a "soundness + // of input consistency" constraint, this is the same size as the + // number of inputs, and never contains points at infinity. + ic: Vec +} + +#[derive(Clone)] +pub struct Parameters { + pub vk: VerifyingKey, + + // Elements of the form ((tau^i * t(tau)) / delta) for i between 0 and + // m-2 inclusive. Never contains points at infinity. + h: Arc>, + + // Elements of the form (beta * u_i(tau) + alpha v_i(tau) + w_i(tau)) / delta + // for all auxillary inputs. Variables can never be unconstrained, so this + // never contains points at infinity. + l: Arc>, + + // QAP "A" polynomials evaluated at tau in the Lagrange basis. Never contains + // points at infinity: polynomials that evaluate to zero are omitted from + // the CRS and the prover can deterministically skip their evaluation. + a: Arc>, + + // QAP "B" polynomials evaluated at tau in the Lagrange basis. Needed in + // G1 and G2 for C/B queries, respectively. Never contains points at + // infinity for the same reason as the "A" polynomials. + b_g1: Arc>, + b_g2: Arc> +} + +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))) + } +} diff --git a/src/groth16/prover.rs b/src/groth16/prover.rs new file mode 100644 index 000000000..2863774f3 --- /dev/null +++ b/src/groth16/prover.rs @@ -0,0 +1,328 @@ +use rand::Rng; + +use std::sync::Arc; + +use futures::Future; + +use pairing::{ + Engine, + PrimeField, + Field, + CurveProjective, + CurveAffine +}; + +use super::{ + ParameterSource, + Proof +}; + +use ::{ + SynthesisError, + Circuit, + ConstraintSystem, + LinearCombination, + Variable, + Index +}; + +use ::domain::{ + EvaluationDomain, + Scalar +}; + +use ::multiexp::{ + DensityTracker, + FullDensity, + multiexp +}; + +use ::multicore::{ + Worker +}; + +fn eval( + lc: &LinearCombination, + mut input_density: Option<&mut DensityTracker>, + mut aux_density: Option<&mut DensityTracker>, + input_assignment: &[E::Fr], + aux_assignment: &[E::Fr] +) -> E::Fr +{ + let mut acc = E::Fr::zero(); + + for &(index, coeff) in lc.0.iter() { + let mut tmp; + + match index { + Variable(Index::Input(i)) => { + tmp = input_assignment[i]; + if let Some(ref mut v) = input_density { + v.inc(i); + } + }, + Variable(Index::Aux(i)) => { + tmp = aux_assignment[i]; + if let Some(ref mut v) = aux_density { + v.inc(i); + } + } + } + + if coeff == E::Fr::one() { + acc.add_assign(&tmp); + } else { + tmp.mul_assign(&coeff); + acc.add_assign(&tmp); + } + } + + acc +} + +struct ProvingAssignment { + // Density of queries + a_aux_density: DensityTracker, + b_input_density: DensityTracker, + b_aux_density: DensityTracker, + + // Evaluations of A, B, C polynomials + a: Vec>, + b: Vec>, + c: Vec>, + + // Assignments of variables + input_assignment: Vec, + aux_assignment: Vec +} + +impl ConstraintSystem for ProvingAssignment { + type Root = Self; + + fn alloc( + &mut self, + _: A, + f: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into + { + self.aux_assignment.push(f()?); + self.a_aux_density.add_element(); + self.b_aux_density.add_element(); + + Ok(Variable(Index::Aux(self.aux_assignment.len() - 1))) + } + + fn alloc_input( + &mut self, + _: A, + f: F + ) -> Result + where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into + { + self.input_assignment.push(f()?); + self.b_input_density.add_element(); + + Ok(Variable(Index::Input(self.input_assignment.len() - 1))) + } + + fn enforce( + &mut self, + _: A, + a: LA, + b: LB, + c: LC + ) + where A: FnOnce() -> AR, AR: Into, + LA: FnOnce(LinearCombination) -> LinearCombination, + LB: FnOnce(LinearCombination) -> LinearCombination, + LC: FnOnce(LinearCombination) -> LinearCombination + { + let a = a(LinearCombination::zero()); + let b = b(LinearCombination::zero()); + let c = c(LinearCombination::zero()); + + self.a.push(Scalar(eval( + &a, + // Inputs have full density in the A query + // because there are constraints of the + // form x * 0 = 0 for each input. + None, + Some(&mut self.a_aux_density), + &self.input_assignment, + &self.aux_assignment + ))); + self.b.push(Scalar(eval( + &b, + Some(&mut self.b_input_density), + Some(&mut self.b_aux_density), + &self.input_assignment, + &self.aux_assignment + ))); + self.c.push(Scalar(eval( + &c, + // There is no C polynomial query, + // though there is an (beta)A + (alpha)B + C + // query for all aux variables. + // However, that query has full density. + None, + None, + &self.input_assignment, + &self.aux_assignment + ))); + } + + fn push_namespace(&mut self, _: N) + where NR: Into, N: FnOnce() -> NR + { + // Do nothing; we don't care about namespaces in this context. + } + + fn pop_namespace(&mut self) + { + // Do nothing; we don't care about namespaces in this context. + } + + fn get_root(&mut self) -> &mut Self::Root { + self + } +} + +pub fn create_random_proof>( + circuit: C, + params: P, + rng: &mut R +) -> Result, SynthesisError> + where E: Engine, C: Circuit, R: Rng +{ + let r = rng.gen(); + let s = rng.gen(); + + create_proof::(circuit, params, r, s) +} + +pub fn create_proof>( + circuit: C, + mut params: P, + r: E::Fr, + s: E::Fr +) -> Result, SynthesisError> + where E: Engine, C: Circuit +{ + let mut prover = ProvingAssignment { + a_aux_density: DensityTracker::new(), + b_input_density: DensityTracker::new(), + b_aux_density: DensityTracker::new(), + a: vec![], + b: vec![], + c: vec![], + input_assignment: vec![], + aux_assignment: vec![] + }; + + prover.alloc_input(|| "", || Ok(E::Fr::one()))?; + + circuit.synthesize(&mut prover)?; + + for i in 0..prover.input_assignment.len() { + prover.enforce(|| "", + |lc| lc + Variable(Index::Input(i)), + |lc| lc, + |lc| lc, + ); + } + + let worker = Worker::new(); + + let vk = params.get_vk(prover.input_assignment.len())?; + + let h = { + let mut a = EvaluationDomain::from_coeffs(prover.a)?; + let mut b = EvaluationDomain::from_coeffs(prover.b)?; + let mut c = EvaluationDomain::from_coeffs(prover.c)?; + a.ifft(&worker); + a.coset_fft(&worker); + b.ifft(&worker); + b.coset_fft(&worker); + c.ifft(&worker); + c.coset_fft(&worker); + + a.mul_assign(&worker, &b); + drop(b); + a.sub_assign(&worker, &c); + drop(c); + a.divide_by_z_on_coset(&worker); + a.icoset_fft(&worker); + let mut a = a.into_coeffs(); + let a_len = a.len() - 1; + a.truncate(a_len); + // TODO: parallelize if it's even helpful + let a = Arc::new(a.into_iter().map(|s| s.0.into_repr()).collect::>()); + + multiexp(&worker, params.get_h(a.len())?, FullDensity, a) + }; + + // TODO: parallelize if it's even helpful + let input_assignment = Arc::new(prover.input_assignment.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 l = multiexp(&worker, params.get_l(aux_assignment.len())?, FullDensity, aux_assignment.clone()); + + let a_aux_density_total = prover.a_aux_density.get_total_density(); + + let (a_inputs_source, a_aux_source) = params.get_a(input_assignment.len(), a_aux_density_total)?; + + let a_inputs = multiexp(&worker, a_inputs_source, FullDensity, input_assignment.clone()); + let a_aux = multiexp(&worker, a_aux_source, Arc::new(prover.a_aux_density), aux_assignment.clone()); + + let b_input_density = Arc::new(prover.b_input_density); + let b_input_density_total = b_input_density.get_total_density(); + let b_aux_density = Arc::new(prover.b_aux_density); + let b_aux_density_total = b_aux_density.get_total_density(); + + let (b_g1_inputs_source, b_g1_aux_source) = params.get_b_g1(b_input_density_total, b_aux_density_total)?; + + let b_g1_inputs = multiexp(&worker, b_g1_inputs_source, b_input_density.clone(), input_assignment.clone()); + let b_g1_aux = multiexp(&worker, b_g1_aux_source, b_aux_density.clone(), aux_assignment.clone()); + + let (b_g2_inputs_source, b_g2_aux_source) = params.get_b_g2(b_input_density_total, b_aux_density_total)?; + + let b_g2_inputs = multiexp(&worker, b_g2_inputs_source, b_input_density, input_assignment); + let b_g2_aux = multiexp(&worker, b_g2_aux_source, b_aux_density, aux_assignment); + + let mut g_a = vk.delta_g1.mul(r); + g_a.add_assign_mixed(&vk.alpha_g1); + let mut g_b = vk.delta_g2.mul(s); + g_b.add_assign_mixed(&vk.beta_g2); + let mut g_c; + { + let mut rs = r; + rs.mul_assign(&s); + + g_c = vk.delta_g1.mul(rs); + g_c.add_assign(&vk.alpha_g1.mul(s)); + g_c.add_assign(&vk.beta_g1.mul(r)); + } + let mut a_answer = a_inputs.wait()?; + a_answer.add_assign(&a_aux.wait()?); + g_a.add_assign(&a_answer); + a_answer.mul_assign(s); + g_c.add_assign(&a_answer); + + let mut b1_answer = b_g1_inputs.wait()?; + b1_answer.add_assign(&b_g1_aux.wait()?); + let mut b2_answer = b_g2_inputs.wait()?; + b2_answer.add_assign(&b_g2_aux.wait()?); + + g_b.add_assign(&b2_answer); + b1_answer.mul_assign(r); + g_c.add_assign(&b1_answer); + g_c.add_assign(&h.wait()?); + g_c.add_assign(&l.wait()?); + + Ok(Proof { + a: g_a.into_affine(), + b: g_b.into_affine(), + c: g_c.into_affine() + }) +} diff --git a/src/groth16/tests/dummy_engine.rs b/src/groth16/tests/dummy_engine.rs new file mode 100644 index 000000000..016aa90cf --- /dev/null +++ b/src/groth16/tests/dummy_engine.rs @@ -0,0 +1,455 @@ +use pairing::{ + Engine, + PrimeField, + PrimeFieldRepr, + Field, + SqrtField, + LegendreSymbol, + CurveProjective, + CurveAffine, + PrimeFieldDecodingError, + GroupDecodingError, + EncodedPoint +}; + +use std::cmp::Ordering; +use std::fmt; +use rand::{Rand, Rng}; +use std::num::Wrapping; + +const MODULUS_R: Wrapping = Wrapping(64513); + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct Fr(Wrapping); + +impl fmt::Display for Fr { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}", (self.0).0) + } +} + +impl Rand for Fr { + fn rand(rng: &mut R) -> Self { + Fr(Wrapping(rng.gen()) % MODULUS_R) + } +} + +impl Field for Fr { + fn zero() -> Self { + Fr(Wrapping(0)) + } + + fn one() -> Self { + Fr(Wrapping(1)) + } + + fn is_zero(&self) -> bool { + (self.0).0 == 0 + } + + fn square(&mut self) { + self.0 = (self.0 * self.0) % MODULUS_R; + } + + fn double(&mut self) { + self.0 = (self.0 << 1) % MODULUS_R; + } + + fn negate(&mut self) { + if !::is_zero(self) { + self.0 = MODULUS_R - self.0; + } + } + + fn add_assign(&mut self, other: &Self) { + self.0 = (self.0 + other.0) % MODULUS_R; + } + + fn sub_assign(&mut self, other: &Self) { + self.0 = ((MODULUS_R + self.0) - other.0) % MODULUS_R; + } + + fn mul_assign(&mut self, other: &Self) { + self.0 = (self.0 * other.0) % MODULUS_R; + } + + fn inverse(&self) -> Option { + if ::is_zero(self) { + None + } else { + Some(self.pow(&[(MODULUS_R.0 as u64) - 2])) + } + } + + fn frobenius_map(&mut self, _: usize) { + // identity + } +} + +impl SqrtField for Fr { + fn legendre(&self) -> LegendreSymbol { + // s = self^((r - 1) // 2) + let s = self.pow([32256]); + if s == ::zero() { LegendreSymbol::Zero } + else if s == ::one() { LegendreSymbol::QuadraticResidue } + else { LegendreSymbol::QuadraticNonResidue } + } + + fn sqrt(&self) -> Option { + // Tonelli-Shank's algorithm for q mod 16 = 1 + // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) + match self.legendre() { + LegendreSymbol::Zero => Some(*self), + LegendreSymbol::QuadraticNonResidue => None, + LegendreSymbol::QuadraticResidue => { + let mut c = Fr::root_of_unity(); + // r = self^((t + 1) // 2) + let mut r = self.pow([32]); + // t = self^t + let mut t = self.pow([63]); + let mut m = Fr::S; + + while t != ::one() { + let mut i = 1; + { + let mut t2i = t; + t2i.square(); + loop { + if t2i == ::one() { + break; + } + t2i.square(); + i += 1; + } + } + + for _ in 0..(m - i - 1) { + c.square(); + } + ::mul_assign(&mut r, &c); + c.square(); + ::mul_assign(&mut t, &c); + m = i; + } + + Some(r) + } + } + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct FrRepr([u64; 1]); + +impl Ord for FrRepr { + fn cmp(&self, other: &FrRepr) -> Ordering { + (self.0)[0].cmp(&(other.0)[0]) + } +} + +impl PartialOrd for FrRepr { + fn partial_cmp(&self, other: &FrRepr) -> Option { + Some(self.cmp(other)) + } +} + +impl Rand for FrRepr { + fn rand(rng: &mut R) -> Self { + FrRepr([rng.gen()]) + } +} + +impl fmt::Display for FrRepr { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "{}", (self.0)[0]) + } +} + +impl From for FrRepr { + fn from(v: u64) -> FrRepr { + FrRepr([v]) + } +} + +impl From for FrRepr { + fn from(v: Fr) -> FrRepr { + FrRepr([(v.0).0 as u64]) + } +} + +impl AsMut<[u64]> for FrRepr { + fn as_mut(&mut self) -> &mut [u64] { + &mut self.0[..] + } +} + +impl AsRef<[u64]> for FrRepr { + fn as_ref(&self) -> &[u64] { + &self.0[..] + } +} + +impl Default for FrRepr { + fn default() -> FrRepr { + FrRepr::from(0u64) + } +} + +impl PrimeFieldRepr for FrRepr { + fn sub_noborrow(&mut self, other: &Self) -> bool { + self.0[0] = self.0[0].wrapping_sub(other.0[0]); + + false + } + fn add_nocarry(&mut self, other: &Self) -> bool { + self.0[0] = self.0[0].wrapping_add(other.0[0]); + + false + } + fn num_bits(&self) -> u32 { + 64 - self.0[0].leading_zeros() + } + fn is_zero(&self) -> bool { + self.0[0] == 0 + } + fn is_odd(&self) -> bool { + !self.is_even() + } + fn is_even(&self) -> bool { + self.0[0] % 2 == 0 + } + fn div2(&mut self) { + self.divn(1) + } + fn divn(&mut self, amt: u32) { + self.0[0] >>= amt; + } + fn mul2(&mut self) { + self.muln(1) + } + fn muln(&mut self, amt: u32) { + self.0[0] <<= amt; + } +} + +impl PrimeField for Fr { + type Repr = FrRepr; + + const NUM_BITS: u32 = 16; + const CAPACITY: u32 = 15; + const S: u32 = 10; + + fn from_repr(repr: FrRepr) -> Result { + if repr.0[0] >= (MODULUS_R.0 as u64) { + Err(PrimeFieldDecodingError::NotInField(format!("{}", repr))) + } else { + Ok(Fr(Wrapping(repr.0[0] as u32))) + } + } + + fn into_repr(&self) -> FrRepr { + FrRepr::from(*self) + } + + fn char() -> FrRepr { + Fr(MODULUS_R).into() + } + + fn multiplicative_generator() -> Fr { + Fr(Wrapping(5)) + } + + fn root_of_unity() -> Fr { + Fr(Wrapping(57751)) + } +} + +#[derive(Clone)] +pub struct DummyEngine; + +impl Engine for DummyEngine { + type Fr = Fr; + type G1 = Fr; + type G1Affine = Fr; + type G2 = Fr; + type G2Affine = Fr; + type Fq = Fr; + type Fqe = Fr; + + // TODO: This should be F_645131 or something. Doesn't matter for now. + type Fqk = Fr; + + fn miller_loop<'a, I>(i: I) -> Self::Fqk + where I: IntoIterator::Prepared, + &'a ::Prepared + )> + { + let mut acc = ::zero(); + + for &(a, b) in i { + let mut tmp = *a; + ::mul_assign(&mut tmp, b); + ::add_assign(&mut acc, &tmp); + } + + acc + } + + /// Perform final exponentiation of the result of a miller loop. + fn final_exponentiation(this: &Self::Fqk) -> Option + { + Some(*this) + } +} + +impl CurveProjective for Fr { + type Affine = Fr; + type Base = Fr; + type Scalar = Fr; + type Engine = DummyEngine; + + fn zero() -> Self { + ::zero() + } + + fn one() -> Self { + ::one() + } + + fn is_zero(&self) -> bool { + ::is_zero(self) + } + + fn batch_normalization(_: &mut [Self]) { + + } + + fn is_normalized(&self) -> bool { + true + } + + fn double(&mut self) { + ::double(self); + } + + fn add_assign(&mut self, other: &Self) { + ::add_assign(self, other); + } + + fn add_assign_mixed(&mut self, other: &Self) { + ::add_assign(self, other); + } + + fn negate(&mut self) { + ::negate(self); + } + + fn mul_assign::Repr>>(&mut self, other: S) + { + let tmp = Fr::from_repr(other.into()).unwrap(); + + ::mul_assign(self, &tmp); + } + + fn into_affine(&self) -> Fr { + *self + } + + fn recommended_wnaf_for_scalar(_: ::Repr) -> usize { + 3 + } + + fn recommended_wnaf_for_num_scalars(_: usize) -> usize { + 3 + } +} + +#[derive(Copy, Clone)] +pub struct FakePoint; + +impl AsMut<[u8]> for FakePoint { + fn as_mut(&mut self) -> &mut [u8] { + unimplemented!() + } +} + +impl AsRef<[u8]> for FakePoint { + fn as_ref(&self) -> &[u8] { + unimplemented!() + } +} + +impl EncodedPoint for FakePoint { + type Affine = Fr; + + fn empty() -> Self { + unimplemented!() + } + + fn size() -> usize { + unimplemented!() + } + + fn into_affine(&self) -> Result { + unimplemented!() + } + + fn into_affine_unchecked(&self) -> Result { + unimplemented!() + } + + fn from_affine(_: Self::Affine) -> Self { + unimplemented!() + } +} + +impl CurveAffine for Fr { + type Pair = Fr; + type PairingResult = Fr; + type Compressed = FakePoint; + type Uncompressed = FakePoint; + type Prepared = Fr; + type Projective = Fr; + type Base = Fr; + type Scalar = Fr; + type Engine = DummyEngine; + + fn zero() -> Self { + ::zero() + } + + fn one() -> Self { + ::one() + } + + fn is_zero(&self) -> bool { + ::is_zero(self) + } + + fn negate(&mut self) { + ::negate(self); + } + + fn mul::Repr>>(&self, other: S) -> Self::Projective + { + let mut res = *self; + let tmp = Fr::from_repr(other.into()).unwrap(); + + ::mul_assign(&mut res, &tmp); + + res + } + + fn prepare(&self) -> Self::Prepared { + *self + } + + fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult { + self.mul(*other) + } + + fn into_projective(&self) -> Self::Projective { + *self + } +} diff --git a/src/groth16/tests/mod.rs b/src/groth16/tests/mod.rs new file mode 100644 index 000000000..a8e291477 --- /dev/null +++ b/src/groth16/tests/mod.rs @@ -0,0 +1,400 @@ +use pairing::{ + Engine, + Field, + PrimeField +}; + +mod dummy_engine; +use self::dummy_engine::*; + +use std::marker::PhantomData; + +use ::{ + Circuit, + ConstraintSystem, + SynthesisError +}; + +use super::{ + generate_parameters, + prepare_verifying_key, + create_proof, + 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(); + 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, + delta, + 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/verifier.rs b/src/groth16/verifier.rs new file mode 100644 index 000000000..083e1d025 --- /dev/null +++ b/src/groth16/verifier.rs @@ -0,0 +1,66 @@ +use pairing::{ + Engine, + CurveProjective, + CurveAffine, + PrimeField +}; + +use super::{ + Proof, + VerifyingKey, + PreparedVerifyingKey +}; + +use ::{ + SynthesisError +}; + +pub fn prepare_verifying_key( + vk: &VerifyingKey +) -> PreparedVerifyingKey +{ + let mut gamma = vk.gamma_g2; + gamma.negate(); + let mut delta = vk.delta_g2; + delta.negate(); + + PreparedVerifyingKey { + alpha_g1_beta_g2: E::pairing(vk.alpha_g1, vk.beta_g2), + neg_gamma_g2: gamma.prepare(), + neg_delta_g2: delta.prepare(), + ic: vk.ic.clone() + } +} + +pub fn verify_proof<'a, E: Engine>( + pvk: &'a PreparedVerifyingKey, + proof: &Proof, + public_inputs: &[E::Fr] +) -> Result +{ + if (public_inputs.len() + 1) != pvk.ic.len() { + return Err(SynthesisError::MalformedVerifyingKey); + } + + let mut acc = pvk.ic[0].into_projective(); + + for (i, b) in public_inputs.iter().zip(pvk.ic.iter().skip(1)) { + acc.add_assign(&b.mul(i.into_repr())); + } + + // The original verification equation is: + // A * B = alpha * beta + inputs * gamma + C * delta + // ... however, we rearrange it so that it is: + // A * B - inputs * gamma - C * delta = alpha * beta + // or equivalently: + // A * B + inputs * (-gamma) + C * (-delta) = alpha * beta + // which allows us to do a single final exponentiation. + + Ok(E::final_exponentiation( + &E::miller_loop([ + (&proof.a.prepare(), &proof.b.prepare()), + (&acc.into_affine().prepare(), &pvk.neg_gamma_g2), + (&proof.c.prepare(), &pvk.neg_delta_g2) + ].into_iter()) + ).unwrap() == pvk.alpha_g1_beta_g2) +} diff --git a/src/lib.rs b/src/lib.rs index ba2390168..beec15138 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,67 +1,119 @@ extern crate pairing; +extern crate rand; +extern crate num_cpus; +extern crate futures; +extern crate futures_cpupool; +extern crate bit_vec; +extern crate crossbeam; + +pub mod multicore; +pub mod multiexp; +pub mod domain; +pub mod groth16; use pairing::{Engine, 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(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<(T, E::Fr)>); +pub struct LinearCombination(Vec<(Variable, E::Fr)>); -impl AsRef<[(T, E::Fr)]> for LinearCombination { - fn as_ref(&self) -> &[(T, E::Fr)] { +impl AsRef<[(Variable, E::Fr)]> for LinearCombination { + fn as_ref(&self) -> &[(Variable, E::Fr)] { &self.0 } } -impl LinearCombination { - pub fn zero() -> LinearCombination { +impl LinearCombination { + pub fn zero() -> LinearCombination { LinearCombination(vec![]) } } -impl Add<(E::Fr, T)> for LinearCombination { - type Output = LinearCombination; +impl Add<(E::Fr, Variable)> for LinearCombination { + type Output = LinearCombination; - fn add(mut self, (coeff, var): (E::Fr, T)) -> LinearCombination { + fn add(mut self, (coeff, var): (E::Fr, Variable)) -> LinearCombination { self.0.push((var, coeff)); self } } -impl Sub<(E::Fr, T)> for LinearCombination { - type Output = LinearCombination; +impl Sub<(E::Fr, Variable)> for LinearCombination { + type Output = LinearCombination; - fn sub(self, (mut coeff, var): (E::Fr, T)) -> LinearCombination { + fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination { coeff.negate(); self + (coeff, var) } } -impl Add for LinearCombination { - type Output = LinearCombination; +impl Add for LinearCombination { + type Output = LinearCombination; - fn add(self, other: T) -> LinearCombination { + fn add(self, other: Variable) -> LinearCombination { self + (E::Fr::one(), other) } } -impl Sub for LinearCombination { - type Output = LinearCombination; +impl Sub for LinearCombination { + type Output = LinearCombination; - fn sub(self, other: T) -> LinearCombination { + fn sub(self, other: Variable) -> LinearCombination { self - (E::Fr::one(), other) } } -impl<'a, T: Copy, E: Engine> Add<&'a LinearCombination> for LinearCombination { - type Output = LinearCombination; +impl<'a, E: Engine> Add<&'a LinearCombination> for LinearCombination { + type Output = LinearCombination; - fn add(mut self, other: &'a LinearCombination) -> LinearCombination { + fn add(mut self, other: &'a LinearCombination) -> LinearCombination { for s in &other.0 { self = self + (s.1, s.0); } @@ -70,10 +122,10 @@ impl<'a, T: Copy, E: Engine> Add<&'a LinearCombination> for LinearCombinat } } -impl<'a, T: Copy, E: Engine> Sub<&'a LinearCombination> for LinearCombination { - type Output = LinearCombination; +impl<'a, E: Engine> Sub<&'a LinearCombination> for LinearCombination { + type Output = LinearCombination; - fn sub(mut self, other: &'a LinearCombination) -> LinearCombination { + fn sub(mut self, other: &'a LinearCombination) -> LinearCombination { for s in &other.0 { self = self - (s.1, s.0); } @@ -82,10 +134,10 @@ impl<'a, T: Copy, E: Engine> Sub<&'a LinearCombination> for LinearCombinat } } -impl<'a, T: Copy, E: Engine> Add<(E::Fr, &'a LinearCombination)> for LinearCombination { - type Output = LinearCombination; +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 { + 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); @@ -96,10 +148,10 @@ impl<'a, T: Copy, E: Engine> Add<(E::Fr, &'a LinearCombination)> for Linea } } -impl<'a, T: Copy, E: Engine> Sub<(E::Fr, &'a LinearCombination)> for LinearCombination { - type Output = LinearCombination; +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 { + 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); @@ -110,76 +162,71 @@ impl<'a, T: Copy, E: Engine> Sub<(E::Fr, &'a LinearCombination)> for Linea } } -#[test] -fn test_lc() { - use pairing::bls12_381::{Bls12, Fr}; - use pairing::PrimeField; - - let a = LinearCombination::::zero() + 0usize + 1usize + 2usize - 3usize; - - let mut negone = Fr::one(); - negone.negate(); - - assert_eq!(a.0, vec![(0usize, Fr::one()), (1usize, Fr::one()), (2usize, Fr::one()), (3usize, negone)]); - - let x = LinearCombination::::zero() + (Fr::one(), 0usize) - (Fr::one(), 1usize); - let y = LinearCombination::::zero() + (Fr::one(), 2usize) - (Fr::one(), 3usize); - let z = x.clone() + &y - &y; - - assert_eq!(z.0, vec![ - (0usize, Fr::one()), - (1usize, negone), - (2usize, Fr::one()), - (3usize, negone), - (2usize, negone), - (3usize, Fr::one()) - ]); - - let coeff = Fr::from_str("3").unwrap(); - let mut neg_coeff = coeff; - neg_coeff.negate(); - let z = x + (coeff, &y) - (coeff, &y); - - assert_eq!(z.0, vec![ - (0usize, Fr::one()), - (1usize, negone), - (2usize, Fr::from_str("3").unwrap()), - (3usize, neg_coeff), - (2usize, neg_coeff), - (3usize, Fr::from_str("3").unwrap()) - ]); -} - /// This is an error that could occur during circuit synthesis contexts, /// such as CRS generation, proving or verification. #[derive(Debug)] pub enum SynthesisError { - AssignmentMissing + /// 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::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> { - write!(f, "{}", self.description()) + 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 { - type Variable: Sized + Copy + Clone; - /// Represents the type of the "root" of this constraint system /// so that nested namespaces can minimize indirection. - type Root: ConstraintSystem; + type Root: ConstraintSystem; /// Return the "one" input variable - fn one(&self) -> Self::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 @@ -189,19 +236,31 @@ pub trait ConstraintSystem: Sized { &mut self, annotation: A, f: F - ) -> Result + ) -> 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( + fn enforce( &mut self, annotation: A, - a: LinearCombination, - b: LinearCombination, - c: LinearCombination + a: LA, + b: LB, + c: LC ) - where A: FnOnce() -> AR, AR: Into; + 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. @@ -229,89 +288,48 @@ pub trait ConstraintSystem: Sized { } } -pub trait PublicConstraintSystem: ConstraintSystem -{ - /// Represents the type of the "root" of this constraint system - /// so that nested namespaces can minimize indirection. - type PublicRoot: PublicConstraintSystem; - - /// 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; - - /// Gets the "root" constraint system, bypassing the namespacing. - /// Not intended for downstream use; use `namespace` instead. - fn get_public_root(&mut self) -> &mut Self::PublicRoot; - - /// Begin a namespace for this constraint system. - fn namespace_public<'a, NR, N>( - &'a mut self, - name_fn: N - ) -> Namespace<'a, E, Self::PublicRoot> - where NR: Into, N: FnOnce() -> NR - { - self.get_root().push_namespace(name_fn); - - Namespace(self.get_public_root(), PhantomData) - } -} - -use std::marker::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: PublicConstraintSystem> PublicConstraintSystem for Namespace<'cs, E, CS> { - type PublicRoot = CS::PublicRoot; +impl<'cs, E: Engine, CS: ConstraintSystem> ConstraintSystem for Namespace<'cs, E, CS> { + type Root = CS::Root; + + fn one() -> Variable { + CS::one() + } - fn alloc_input( + fn alloc( &mut self, annotation: A, f: F - ) -> Result + ) -> Result where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into { - self.0.alloc_input(annotation, f) - } - - fn get_public_root(&mut self) -> &mut Self::PublicRoot - { - self.0.get_public_root() - } -} - -impl<'cs, E: Engine, CS: ConstraintSystem> ConstraintSystem for Namespace<'cs, E, CS> { - type Variable = CS::Variable; - type Root = CS::Root; - - fn one(&self) -> Self::Variable { - self.0.one() + self.0.alloc(annotation, f) } - fn alloc( + fn alloc_input( &mut self, annotation: A, f: F - ) -> Result + ) -> Result where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into { - self.0.alloc(annotation, f) + self.0.alloc_input(annotation, f) } - fn enforce( + fn enforce( &mut self, annotation: A, - a: LinearCombination, - b: LinearCombination, - c: LinearCombination + a: LA, + b: LB, + c: LC ) - where A: FnOnce() -> AR, AR: Into + 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) } @@ -343,55 +361,46 @@ impl<'a, E: Engine, CS: ConstraintSystem> Drop for Namespace<'a, E, CS> { } } -/// Convenience implementation of PublicConstraintSystem for mutable references to -/// public constraint systems. -impl<'cs, E: Engine, CS: PublicConstraintSystem> PublicConstraintSystem for &'cs mut CS { - type PublicRoot = CS::PublicRoot; - - 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 get_public_root(&mut self) -> &mut Self::PublicRoot - { - (**self).get_public_root() - } -} - /// Convenience implementation of ConstraintSystem for mutable references to /// constraint systems. impl<'cs, E: Engine, CS: ConstraintSystem> ConstraintSystem for &'cs mut CS { - type Variable = CS::Variable; type Root = CS::Root; - fn one(&self) -> Self::Variable { - (**self).one() + fn one() -> Variable { + CS::one() } fn alloc( &mut self, annotation: A, f: F - ) -> Result + ) -> Result where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into { (**self).alloc(annotation, f) } - fn enforce( + 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: LinearCombination, - b: LinearCombination, - c: LinearCombination + a: LA, + b: LB, + c: LC ) - where A: FnOnce() -> AR, AR: Into + where A: FnOnce() -> AR, AR: Into, + LA: FnOnce(LinearCombination) -> LinearCombination, + LB: FnOnce(LinearCombination) -> LinearCombination, + LC: FnOnce(LinearCombination) -> LinearCombination { (**self).enforce(annotation, a, b, c) } @@ -413,180 +422,221 @@ impl<'cs, E: Engine, CS: ConstraintSystem> ConstraintSystem for &'cs mut C } } -#[test] -fn test_cs() { - use pairing::bls12_381::{Bls12, Fr}; - - #[derive(PartialEq, Copy, Clone)] - enum Var { - Input(usize), - Aux(usize) - } - - struct MySillyConstraintSystem { - inputs: Vec<(E::Fr, String)>, - aux: Vec<(E::Fr, String)>, - constraints: Vec<(LinearCombination, LinearCombination, LinearCombination, String)>, - current_namespace: Vec - } - - fn compute_path(ns: &[String], this: String) -> String { - let mut name = String::new(); - - let mut needs_separation = false; - for ns in ns.iter().chain(Some(&this).into_iter()) - { - if needs_separation { - name += "/"; - } - - name += ns; - needs_separation = true; - } - - name - } - - impl PublicConstraintSystem for MySillyConstraintSystem { - type PublicRoot = Self; - - fn alloc_input( - &mut self, - annotation: A, - f: F - ) -> Result - where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into - { - let index = self.inputs.len(); - let path = compute_path(&self.current_namespace, annotation().into()); - self.inputs.push((f()?, path)); - - Ok(Var::Input(index)) - } - - fn get_public_root(&mut self) -> &mut Self::PublicRoot - { - self - } - } - - impl ConstraintSystem for MySillyConstraintSystem { - type Variable = Var; - type Root = Self; - - fn one(&self) -> Self::Variable { - Var::Input(0) - } - - fn alloc( - &mut self, - annotation: A, - f: F - ) -> Result - where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into - { - let index = self.aux.len(); - let path = compute_path(&self.current_namespace, annotation().into()); - self.aux.push((f()?, path)); - - Ok(Var::Aux(index)) - } - - fn enforce( - &mut self, - annotation: A, - a: LinearCombination, - b: LinearCombination, - c: LinearCombination - ) - where A: FnOnce() -> AR, AR: Into - { - let path = compute_path(&self.current_namespace, annotation().into()); - - self.constraints.push((a, b, c, path)); - } - - fn push_namespace(&mut self, name_fn: N) - where NR: Into, N: FnOnce() -> NR - { - self.current_namespace.push(name_fn().into()); - } - - fn pop_namespace(&mut self) - { - self.current_namespace.pop(); - } - - fn get_root(&mut self) -> &mut Self::Root - { - self - } - } - - fn do_stuff_with_pcs>(mut cs: CS, one_more: bool) - { - cs.alloc_input(|| "something", || Ok(E::Fr::zero())).unwrap(); - - if one_more { - do_stuff_with_pcs(cs.namespace_public(|| "cool namespace"), false); - } - } - - let mut cs = MySillyConstraintSystem:: { - inputs: vec![(Fr::one(), "ONE".into())], - aux: vec![], - constraints: vec![], - current_namespace: vec![] - }; - cs.alloc(|| "something", || Ok(Fr::zero())).unwrap(); - assert_eq!(cs.inputs, vec![(Fr::one(), "ONE".into())]); - assert_eq!(cs.aux, vec![(Fr::zero(), "something".into())]); - { - let mut cs = cs.namespace(|| "woohoo"); - - cs.alloc(|| "whatever", || Ok(Fr::one())).unwrap(); - cs.alloc(|| "you", || Ok(Fr::zero())).unwrap(); - cs.alloc(|| "say", || Ok(Fr::one())).unwrap(); - - { - let mut cs = cs.namespace(|| "hehe"); - - let v1 = cs.alloc(|| "hehe, indeed", || Ok(Fr::one())).unwrap(); - let v2 = cs.alloc_input(|| "works lol", || Ok(Fr::zero())).unwrap(); - - let one = cs.one(); - - cs.enforce( - || "great constraint", - LinearCombination::zero() + v1, - LinearCombination::zero() + one, - LinearCombination::zero() + v2 - ); - } - } - assert_eq!(cs.aux, vec![ - (Fr::zero(), "something".into()), - (Fr::one(), "woohoo/whatever".into()), - (Fr::zero(), "woohoo/you".into()), - (Fr::one(), "woohoo/say".into()), - (Fr::one(), "woohoo/hehe/hehe, indeed".into()), - ]); - assert_eq!(cs.inputs, vec![ - (Fr::one(), "ONE".into()), - (Fr::zero(), "woohoo/hehe/works lol".into()), - ]); - assert!(cs.constraints.len() == 1); - assert!((cs.constraints[0].0).0 == vec![(Var::Aux(4), Fr::one())]); - assert!((cs.constraints[0].1).0 == vec![(Var::Input(0), Fr::one())]); - assert!((cs.constraints[0].2).0 == vec![(Var::Input(1), Fr::one())]); - assert!(cs.constraints[0].3 == "woohoo/hehe/great constraint"); - - do_stuff_with_pcs(cs.namespace(|| "namey"), true); - - assert_eq!(cs.inputs, vec![ - (Fr::one(), "ONE".into()), - (Fr::zero(), "woohoo/hehe/works lol".into()), - (Fr::zero(), "namey/something".into()), - (Fr::zero(), "namey/cool namespace/something".into()), - ]); -} +// #[test] +// fn test_cs() { +// use pairing::bls12_381::{Bls12, Fr}; + +// struct MySillyConstraintSystem { +// inputs: Vec<(E::Fr, String)>, +// aux: Vec<(E::Fr, String)>, +// constraints: Vec<(LinearCombination, LinearCombination, LinearCombination, String)>, +// current_namespace: Vec +// } + +// fn compute_path(ns: &[String], this: String) -> String { +// let mut name = String::new(); + +// let mut needs_separation = false; +// for ns in ns.iter().chain(Some(&this).into_iter()) +// { +// if needs_separation { +// name += "/"; +// } + +// name += ns; +// needs_separation = true; +// } + +// name +// } + +// impl PublicConstraintSystem for MySillyConstraintSystem { +// type PublicRoot = Self; + +// fn alloc_input( +// &mut self, +// annotation: A, +// f: F +// ) -> Result +// where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into +// { +// let index = self.inputs.len(); +// let path = compute_path(&self.current_namespace, annotation().into()); +// self.inputs.push((f()?, path)); + +// Ok(Var::Input(index)) +// } + +// fn get_public_root(&mut self) -> &mut Self::PublicRoot +// { +// self +// } +// } + +// impl ConstraintSystem for MySillyConstraintSystem { +// type Variable = Var; +// type Root = Self; + +// fn one(&self) -> Variable { +// Var::Input(0) +// } + +// fn alloc( +// &mut self, +// annotation: A, +// f: F +// ) -> Result +// where F: FnOnce() -> Result, A: FnOnce() -> AR, AR: Into +// { +// let index = self.aux.len(); +// let path = compute_path(&self.current_namespace, annotation().into()); +// self.aux.push((f()?, path)); + +// Ok(Var::Aux(index)) +// } + +// 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 +// { +// let path = compute_path(&self.current_namespace, annotation().into()); + +// let a = a(LinearCombination::zero()); +// let b = b(LinearCombination::zero()); +// let c = c(LinearCombination::zero()); + +// self.constraints.push((a, b, c, path)); +// } + +// fn push_namespace(&mut self, name_fn: N) +// where NR: Into, N: FnOnce() -> NR +// { +// self.current_namespace.push(name_fn().into()); +// } + +// fn pop_namespace(&mut self) +// { +// self.current_namespace.pop(); +// } + +// fn get_root(&mut self) -> &mut Self::Root +// { +// self +// } +// } + +// fn do_stuff_with_pcs>(mut cs: CS, one_more: bool) +// { +// cs.alloc_input(|| "something", || Ok(E::Fr::zero())).unwrap(); + +// if one_more { +// do_stuff_with_pcs(cs.namespace_public(|| "cool namespace"), false); +// } +// } + +// let mut cs = MySillyConstraintSystem:: { +// inputs: vec![(Fr::one(), "ONE".into())], +// aux: vec![], +// constraints: vec![], +// current_namespace: vec![] +// }; +// cs.alloc(|| "something", || Ok(Fr::zero())).unwrap(); +// assert_eq!(cs.inputs, vec![(Fr::one(), "ONE".into())]); +// assert_eq!(cs.aux, vec![(Fr::zero(), "something".into())]); +// { +// let mut cs = cs.namespace(|| "woohoo"); + +// cs.alloc(|| "whatever", || Ok(Fr::one())).unwrap(); +// cs.alloc(|| "you", || Ok(Fr::zero())).unwrap(); +// cs.alloc(|| "say", || Ok(Fr::one())).unwrap(); + +// { +// let mut cs = cs.namespace(|| "hehe"); + +// let v1 = cs.alloc(|| "hehe, indeed", || Ok(Fr::one())).unwrap(); +// let v2 = cs.alloc_input(|| "works lol", || Ok(Fr::zero())).unwrap(); + +// let one = cs.one(); + +// cs.enforce( +// || "great constraint", +// |lc| lc + v1, +// |lc| lc + one, +// |lc| lc + v2 +// ); +// } +// } +// assert_eq!(cs.aux, vec![ +// (Fr::zero(), "something".into()), +// (Fr::one(), "woohoo/whatever".into()), +// (Fr::zero(), "woohoo/you".into()), +// (Fr::one(), "woohoo/say".into()), +// (Fr::one(), "woohoo/hehe/hehe, indeed".into()), +// ]); +// assert_eq!(cs.inputs, vec![ +// (Fr::one(), "ONE".into()), +// (Fr::zero(), "woohoo/hehe/works lol".into()), +// ]); +// assert!(cs.constraints.len() == 1); +// assert!((cs.constraints[0].0).0 == vec![(Var::Aux(4), Fr::one())]); +// assert!((cs.constraints[0].1).0 == vec![(Var::Input(0), Fr::one())]); +// assert!((cs.constraints[0].2).0 == vec![(Var::Input(1), Fr::one())]); +// assert!(cs.constraints[0].3 == "woohoo/hehe/great constraint"); + +// do_stuff_with_pcs(cs.namespace(|| "namey"), true); + +// assert_eq!(cs.inputs, vec![ +// (Fr::one(), "ONE".into()), +// (Fr::zero(), "woohoo/hehe/works lol".into()), +// (Fr::zero(), "namey/something".into()), +// (Fr::zero(), "namey/cool namespace/something".into()), +// ]); +// } + +// #[test] +// fn test_lc() { +// use pairing::bls12_381::{Bls12, Fr}; +// use pairing::PrimeField; + +// let a = LinearCombination::::zero() + 0usize + 1usize + 2usize - 3usize; + +// let mut negone = Fr::one(); +// negone.negate(); + +// assert_eq!(a.0, vec![(0usize, Fr::one()), (1usize, Fr::one()), (2usize, Fr::one()), (3usize, negone)]); + +// let x = LinearCombination::::zero() + (Fr::one(), 0usize) - (Fr::one(), 1usize); +// let y = LinearCombination::::zero() + (Fr::one(), 2usize) - (Fr::one(), 3usize); +// let z = x.clone() + &y - &y; + +// assert_eq!(z.0, vec![ +// (0usize, Fr::one()), +// (1usize, negone), +// (2usize, Fr::one()), +// (3usize, negone), +// (2usize, negone), +// (3usize, Fr::one()) +// ]); + +// let coeff = Fr::from_str("3").unwrap(); +// let mut neg_coeff = coeff; +// neg_coeff.negate(); +// let z = x + (coeff, &y) - (coeff, &y); + +// assert_eq!(z.0, vec![ +// (0usize, Fr::one()), +// (1usize, negone), +// (2usize, Fr::from_str("3").unwrap()), +// (3usize, neg_coeff), +// (2usize, neg_coeff), +// (3usize, Fr::from_str("3").unwrap()) +// ]); +// } diff --git a/src/multicore.rs b/src/multicore.rs new file mode 100644 index 000000000..c0062fc0f --- /dev/null +++ b/src/multicore.rs @@ -0,0 +1,106 @@ +//! This is an interface for dealing with the kinds of +//! parallel computations involved in bellman. It's +//! currently just a thin wrapper around CpuPool and +//! 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, Scope}; + +#[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: cpus, + pool: CpuPool::new(cpus) + } + } + + pub fn new() -> Worker { + Self::new_with_cpus(num_cpus::get()) + } + + pub fn log_num_cpus(&self) -> u32 { + log2_floor(self.cpus) + } + + 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 = if elements < self.cpus { + 1 + } else { + elements / self.cpus + }; + + crossbeam::scope(|scope| { + f(scope, chunk_size) + }) + } +} + +pub struct WorkerFuture { + future: CpuFuture +} + +impl Future for WorkerFuture { + type Item = T; + type Error = E; + + fn poll(&mut self) -> Poll + { + self.future.poll() + } +} + +fn log2_floor(num: usize) -> u32 { + assert!(num > 0); + + let mut pow = 0; + + while (1 << (pow+1)) <= num { + pow += 1; + } + + pow +} + +#[test] +fn test_log2_floor() { + assert_eq!(log2_floor(1), 0); + assert_eq!(log2_floor(2), 1); + assert_eq!(log2_floor(3), 1); + assert_eq!(log2_floor(4), 2); + assert_eq!(log2_floor(5), 2); + assert_eq!(log2_floor(6), 2); + assert_eq!(log2_floor(7), 2); + assert_eq!(log2_floor(8), 3); +} diff --git a/oldsrc/multiexp.rs b/src/multiexp.rs similarity index 91% rename from oldsrc/multiexp.rs rename to src/multiexp.rs index 89990fce5..c23decc67 100644 --- a/oldsrc/multiexp.rs +++ b/src/multiexp.rs @@ -1,12 +1,19 @@ -use pairing::*; +use pairing::{ + CurveAffine, + CurveProjective, + Engine, + PrimeField, + Field, + PrimeFieldRepr +}; use std::sync::Arc; use std::io; use bit_vec::{self, BitVec}; use std::iter; use futures::{Future}; -use futures_cpupool::CpuPool; +use super::multicore::Worker; -use super::Error; +use super::SynthesisError; /// An object that builds a source of bases. pub trait SourceBuilder: Send + Sync + 'static + Clone { @@ -18,10 +25,10 @@ pub trait SourceBuilder: Send + Sync + 'static + Clone { /// 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<(), Error>; + 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<(), Error>; + fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>; } impl SourceBuilder for (Arc>, usize) { @@ -33,13 +40,13 @@ impl SourceBuilder for (Arc>, usize) { } impl Source for (Arc>, usize) { - fn add_assign_mixed(&mut self, to: &mut ::Projective) -> Result<(), Error> { + 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 from source").into()); } if self.0[self.1].is_zero() { - return Err(Error::UnexpectedIdentity) + return Err(SynthesisError::UnexpectedIdentity) } to.add_assign_mixed(&self.0[self.1]); @@ -49,7 +56,7 @@ impl Source for (Arc>, usize) { Ok(()) } - fn skip(&mut self, amt: usize) -> Result<(), Error> { + 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 from source").into()); } @@ -131,14 +138,14 @@ impl DensityTracker { } fn multiexp_inner( - pool: &CpuPool, + pool: &Worker, bases: S, density_map: D, exponents: Arc::Fr as PrimeField>::Repr>>, mut skip: u32, c: u32, handle_trivial: bool -) -> Box::Projective, Error=Error>> +) -> Box::Projective, Error=SynthesisError>> where for<'a> &'a Q: QueryDensity, D: Send + Sync + 'static + Clone + AsRef, G: CurveAffine, @@ -150,7 +157,7 @@ fn multiexp_inner( let exponents = exponents.clone(); let density_map = density_map.clone(); - pool.spawn_fn(move || { + pool.compute(move || { // Accumulate the result let mut acc = G::Projective::zero(); @@ -228,11 +235,11 @@ fn multiexp_inner( /// Perform multi-exponentiation. The caller is responsible for ensuring the /// query size is the same as the number of exponents. pub fn multiexp( - pool: &CpuPool, + pool: &Worker, bases: S, density_map: D, exponents: Arc::Fr as PrimeField>::Repr>> -) -> Box::Projective, Error=Error>> +) -> Box::Projective, Error=SynthesisError>> where for<'a> &'a Q: QueryDensity, D: Send + Sync + 'static + Clone + AsRef, G: CurveAffine, @@ -275,7 +282,7 @@ fn test_with_bls12() { use rand::{self, Rand}; use pairing::bls12_381::Bls12; - const SAMPLES: usize = 1 << 17; + const SAMPLES: usize = 1 << 14; let rng = &mut rand::thread_rng(); let v = Arc::new((0..SAMPLES).map(|_| ::Fr::rand(rng).into_repr()).collect::>()); @@ -283,7 +290,7 @@ fn test_with_bls12() { let naive = naive_multiexp(g.clone(), v.clone()); - let pool = CpuPool::new_num_cpus(); + let pool = Worker::new(); let fast = multiexp( &pool,