diff --git a/Cargo.toml b/Cargo.toml index cc00bb5fe..2a96c3f28 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,22 +1,25 @@ [package] -authors = ["Sean Bowe "] +authors = ["Sean Bowe ", "Alex Vlasov ", "Alex Gluchowski > EvaluationDomain { pub fn from_coeffs(mut coeffs: Vec) -> Result, SynthesisError> { + use ff::PrimeField; // Compute the size of our evaluation domain + + let coeffs_len = coeffs.len(); + + // m is a size of domain where Z polynomial does NOT vanish + // in normal domain Z is in a form of (X-1)(X-2)...(X-N) let mut m = 1; let mut exp = 0; - while m < coeffs.len() { + let mut omega = E::Fr::root_of_unity(); + let max_degree = (1 << E::Fr::S) - 1; + + if coeffs_len > max_degree { + return Err(SynthesisError::PolynomialDegreeTooLarge) + } + + while m < coeffs_len { m *= 2; exp += 1; // The pairing-friendly curve may not be able to support // large enough (radix2) evaluation domains. - if exp >= E::Fr::S { + if exp > E::Fr::S { return Err(SynthesisError::PolynomialDegreeTooLarge) } } + // If full domain is not needed - limit it, + // e.g. if (2^N)th power is not required, just double omega and get 2^(N-1)th // Compute omega, the 2^exp primitive root of unity - let mut omega = E::Fr::root_of_unity(); for _ in exp..E::Fr::S { omega.square(); } diff --git a/src/groth16/generator.rs b/src/groth16/generator.rs index 1eed62db0..98a384cb1 100644 --- a/src/groth16/generator.rs +++ b/src/groth16/generator.rs @@ -1,16 +1,25 @@ +extern crate time; + +use super::super::verbose_flag; + +use self::time::PreciseTime; + use rand::Rng; use std::sync::Arc; use pairing::{ Engine, - PrimeField, - Field, Wnaf, CurveProjective, CurveAffine }; +use ff::{ + PrimeField, + Field +}; + use super::{ Parameters, VerifyingKey @@ -182,6 +191,8 @@ pub fn generate_parameters( ) -> Result, SynthesisError> where E: Engine, C: Circuit { + let verbose = verbose_flag(); + let mut assembly = KeypairAssembly { num_inputs: 0, num_aux: 0, @@ -210,6 +221,7 @@ pub fn generate_parameters( ); } + if verbose {eprintln!("Making {} powers of tau", assembly.num_constraints)}; // Create bases for blind evaluation of polynomials at tau let powers_of_tau = vec![Scalar::(E::Fr::zero()); assembly.num_constraints]; let mut powers_of_tau = EvaluationDomain::from_coeffs(powers_of_tau)?; @@ -242,6 +254,8 @@ pub fn generate_parameters( let mut h = vec![E::G1::zero(); powers_of_tau.as_ref().len() - 1]; { // Compute powers of tau + if verbose {eprintln!("computing powers of tau...")}; + let start = PreciseTime::now(); { let powers_of_tau = powers_of_tau.as_mut(); worker.scope(powers_of_tau.len(), |scope, chunk| { @@ -258,17 +272,19 @@ pub fn generate_parameters( } }); } + if verbose {eprintln!("powers of tau stage 1 done in {} s", start.to(PreciseTime::now()).num_milliseconds() as f64 / 1000.0);}; // coeff = t(x) / delta let mut coeff = powers_of_tau.z(&tau); coeff.mul_assign(&delta_inverse); + if verbose {eprintln!("computing the H query with multiple threads...")}; + let start = PreciseTime::now(); // Compute the H query with multiple threads 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(); - scope.spawn(move || { // Set values of the H query to g1^{(tau^i * t(tau)) / delta} for (h, p) in h.iter_mut().zip(p.iter()) @@ -286,18 +302,27 @@ pub fn generate_parameters( }); } }); + if verbose {eprintln!("computing the H query done in {} s", start.to(PreciseTime::now()).num_milliseconds() as f64 / 1000.0);}; } + if verbose {eprintln!("using inverse FFT to convert powers of tau to Lagrange coefficients...")}; + let start = PreciseTime::now(); + // Use inverse FFT to convert powers of tau to Lagrange coefficients powers_of_tau.ifft(&worker); let powers_of_tau = powers_of_tau.into_coeffs(); + if verbose {eprintln!("powers of tau stage 2 done in {} s", start.to(PreciseTime::now()).num_milliseconds() as f64 / 1000.0)}; + let mut a = vec![E::G1::zero(); assembly.num_inputs + assembly.num_aux]; let mut b_g1 = vec![E::G1::zero(); assembly.num_inputs + assembly.num_aux]; let mut b_g2 = vec![E::G2::zero(); assembly.num_inputs + assembly.num_aux]; let mut ic = vec![E::G1::zero(); assembly.num_inputs]; let mut l = vec![E::G1::zero(); assembly.num_aux]; + if verbose {eprintln!("evaluating polynomials...")}; + let start = PreciseTime::now(); + fn eval( // wNAF window tables g1_wnaf: &Wnaf>, @@ -327,6 +352,7 @@ pub fn generate_parameters( // Worker worker: &Worker ) + { // Sanity check assert_eq!(a.len(), at.len()); @@ -408,7 +434,7 @@ pub fn generate_parameters( E::G2::batch_normalization(b_g2); E::G1::batch_normalization(ext); }); - } + }; }); } @@ -448,6 +474,8 @@ pub fn generate_parameters( &worker ); + if verbose {eprintln!("evaluating polynomials done in {} s", start.to(PreciseTime::now()).num_milliseconds() as f64 / 1000.0);}; + // Don't allow any elements be unconstrained, so that // the L query is always fully dense. for e in l.iter() { @@ -469,6 +497,8 @@ pub fn generate_parameters( ic: ic.into_iter().map(|e| e.into_affine()).collect() }; + println!("Has generated {} points", a.len()); + Ok(Parameters { vk: vk, h: Arc::new(h.into_iter().map(|e| e.into_affine()).collect()), diff --git a/src/groth16/mod.rs b/src/groth16/mod.rs index 3b8d67148..7dcc8ab4e 100644 --- a/src/groth16/mod.rs +++ b/src/groth16/mod.rs @@ -24,7 +24,7 @@ pub use self::generator::*; pub use self::prover::*; pub use self::verifier::*; -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct Proof { pub a: E::G1Affine, pub b: E::G2Affine, @@ -487,7 +487,7 @@ mod test_with_bls12_381 { use {Circuit, SynthesisError, ConstraintSystem}; use rand::{Rand, thread_rng}; - use pairing::{Field}; + use ff::{Field}; use pairing::bls12_381::{Bls12, Fr}; #[test] @@ -573,4 +573,4 @@ mod test_with_bls12_381 { assert!(!verify_proof(&pvk, &proof, &[a]).unwrap()); } } -} +} \ No newline at end of file diff --git a/src/groth16/prover.rs b/src/groth16/prover.rs index f21fcce90..ef19ebf6d 100644 --- a/src/groth16/prover.rs +++ b/src/groth16/prover.rs @@ -1,3 +1,8 @@ +extern crate time; +use self::time::PreciseTime; + +use super::super::verbose_flag; + use rand::Rng; use std::sync::Arc; @@ -6,12 +11,15 @@ use futures::Future; use pairing::{ Engine, - PrimeField, - Field, CurveProjective, CurveAffine }; +use ff::{ + PrimeField, + Field +}; + use super::{ ParameterSource, Proof @@ -80,6 +88,12 @@ fn eval( acc } +// This is a proving assignment with densities precalculated +pub struct PreparedProver{ + assignment: ProvingAssignment, +} + +#[derive(Clone)] struct ProvingAssignment { // Density of queries a_aux_density: DensityTracker, @@ -96,6 +110,191 @@ struct ProvingAssignment { aux_assignment: Vec } +pub fn prepare_prover( + circuit: C, +) -> 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 prepared = PreparedProver { + assignment: prover + }; + + return Ok(prepared) +} + +impl PreparedProver { + pub fn create_random_proof>( + & self, + params: P, + rng: &mut R + ) -> Result, SynthesisError> + where R: Rng + { + let r = rng.gen(); + let s = rng.gen(); + + self.create_proof(params, r, s) + } + + pub fn create_proof>( + & self, + mut params: P, + r: E::Fr, + s: E::Fr + ) -> Result, SynthesisError> + { + let verbose = verbose_flag(); + + let prover = self.assignment.clone(); + let worker = Worker::new(); + + let vk = params.get_vk(self.assignment.input_assignment.len())?; + + let h_start = PreciseTime::now(); + + 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)?; + // here a coset is a domain where denominator (z) does not vanish + // inverse FFT is an interpolation + a.ifft(&worker); + // evaluate in coset + a.coset_fft(&worker); + // same is for B and C + b.ifft(&worker); + b.coset_fft(&worker); + c.ifft(&worker); + c.coset_fft(&worker); + + // do A*B-C in coset + a.mul_assign(&worker, &b); + drop(b); + a.sub_assign(&worker, &c); + drop(c); + // z does not vanish in coset, so we divide by non-zero + a.divide_by_z_on_coset(&worker); + // interpolate back in coset + 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 + // TODO: in large settings it may worth to parallelize + let a = Arc::new(a.into_iter().map(|s| s.0.into_repr()).collect::>()); + + multiexp(&worker, params.get_h(a.len())?, FullDensity, a) + }; + + let h_end = PreciseTime::now(); + if verbose {eprintln!("{} seconds for prover for H evaluation", h_start.to(h_end))}; + + let points_start = PreciseTime::now(); + + // TODO: Check that difference in operations for different chunks is small + + + // TODO: parallelize if it's even helpful + // TODO: in large settings it may worth to parallelize + let input_assignment = Arc::new(prover.input_assignment.into_iter().map(|s| s.into_repr()).collect::>()); + let aux_assignment = Arc::new(prover.aux_assignment.into_iter().map(|s| s.into_repr()).collect::>()); + + // Run a dedicated process for dense vector + let l = multiexp(&worker, params.get_l(aux_assignment.len())?, FullDensity, aux_assignment.clone()); + + let a_aux_density_total = prover.a_aux_density.get_total_density(); + + let (a_inputs_source, a_aux_source) = params.get_a(input_assignment.len(), a_aux_density_total)?; + + let a_inputs = multiexp(&worker, a_inputs_source, FullDensity, input_assignment.clone()); + let a_aux = multiexp(&worker, a_aux_source, Arc::new(prover.a_aux_density), aux_assignment.clone()); + + let b_input_density = Arc::new(prover.b_input_density); + let b_input_density_total = b_input_density.get_total_density(); + let b_aux_density = Arc::new(prover.b_aux_density); + let b_aux_density_total = b_aux_density.get_total_density(); + + let (b_g1_inputs_source, b_g1_aux_source) = params.get_b_g1(b_input_density_total, b_aux_density_total)?; + + let b_g1_inputs = multiexp(&worker, b_g1_inputs_source, b_input_density.clone(), input_assignment.clone()); + let b_g1_aux = multiexp(&worker, b_g1_aux_source, b_aux_density.clone(), aux_assignment.clone()); + + let (b_g2_inputs_source, b_g2_aux_source) = params.get_b_g2(b_input_density_total, b_aux_density_total)?; + + let b_g2_inputs = multiexp(&worker, b_g2_inputs_source, b_input_density, input_assignment); + let b_g2_aux = multiexp(&worker, b_g2_aux_source, b_aux_density, aux_assignment); + + if vk.delta_g1.is_zero() || vk.delta_g2.is_zero() { + // If this element is zero, someone is trying to perform a + // subversion-CRS attack. + return Err(SynthesisError::UnexpectedIdentity); + } + + let mut g_a = vk.delta_g1.mul(r); + 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()?); + + let points_end = PreciseTime::now(); + if verbose {eprintln!("{} seconds for prover for point multiplication", points_start.to(points_end))}; + + Ok(Proof { + a: g_a.into_affine(), + b: g_b.into_affine(), + c: g_c.into_affine() + }) + } +} + + impl ConstraintSystem for ProvingAssignment { type Root = Self; @@ -209,6 +408,8 @@ pub fn create_proof>( ) -> Result, SynthesisError> where E: Engine, C: Circuit { + let verbose = verbose_flag(); + let mut prover = ProvingAssignment { a_aux_density: DensityTracker::new(), b_input_density: DensityTracker::new(), @@ -236,36 +437,56 @@ pub fn create_proof>( let vk = params.get_vk(prover.input_assignment.len())?; + let h_start = PreciseTime::now(); + 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)?; + // here a coset is a domain where denominator (z) does not vanish + // inverse FFT is an interpolation a.ifft(&worker); + // evaluate in coset a.coset_fft(&worker); + // same is for B and C b.ifft(&worker); b.coset_fft(&worker); c.ifft(&worker); c.coset_fft(&worker); + // do A*B-C in coset a.mul_assign(&worker, &b); drop(b); a.sub_assign(&worker, &c); drop(c); + // z does not vanish in coset, so we divide by non-zero a.divide_by_z_on_coset(&worker); + // interpolate back in coset 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 + // TODO: in large settings it may worth to parallelize let a = Arc::new(a.into_iter().map(|s| s.0.into_repr()).collect::>()); multiexp(&worker, params.get_h(a.len())?, FullDensity, a) }; + let h_end = PreciseTime::now(); + if verbose {eprintln!("{} seconds for prover for H evaluation", h_start.to(h_end))}; + + let points_start = PreciseTime::now(); + + // TODO: Check that difference in operations for different chunks is small + + // TODO: parallelize if it's even helpful + // TODO: in large settings it may worth to parallelize let input_assignment = Arc::new(prover.input_assignment.into_iter().map(|s| s.into_repr()).collect::>()); let aux_assignment = Arc::new(prover.aux_assignment.into_iter().map(|s| s.into_repr()).collect::>()); + // Run a dedicated process for dense vector 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(); @@ -326,6 +547,9 @@ pub fn create_proof>( g_c.add_assign(&h.wait()?); g_c.add_assign(&l.wait()?); + let points_end = PreciseTime::now(); + if verbose {eprintln!("{} seconds for prover for point multiplication", points_start.to(points_end))}; + Ok(Proof { a: g_a.into_affine(), b: g_b.into_affine(), diff --git a/src/groth16/tests/dummy_engine.rs b/src/groth16/tests/dummy_engine.rs index 26c899650..a436533de 100644 --- a/src/groth16/tests/dummy_engine.rs +++ b/src/groth16/tests/dummy_engine.rs @@ -1,15 +1,19 @@ use pairing::{ Engine, + CurveProjective, + CurveAffine, + GroupDecodingError, + EncodedPoint +}; + +use ff::{ PrimeField, PrimeFieldRepr, Field, SqrtField, LegendreSymbol, - CurveProjective, - CurveAffine, + ScalarEngine, PrimeFieldDecodingError, - GroupDecodingError, - EncodedPoint }; use std::cmp::Ordering; @@ -263,8 +267,11 @@ impl PrimeField for Fr { #[derive(Clone)] pub struct DummyEngine; -impl Engine for DummyEngine { +impl ScalarEngine for DummyEngine { type Fr = Fr; +} + +impl Engine for DummyEngine { type G1 = Fr; type G1Affine = Fr; type G2 = Fr; diff --git a/src/groth16/tests/mod.rs b/src/groth16/tests/mod.rs index a8e291477..798b04678 100644 --- a/src/groth16/tests/mod.rs +++ b/src/groth16/tests/mod.rs @@ -1,7 +1,10 @@ use pairing::{ - Engine, + Engine +}; + +use ff:: { Field, - PrimeField + PrimeField, }; mod dummy_engine; diff --git a/src/groth16/verifier.rs b/src/groth16/verifier.rs index 083e1d025..6d309154c 100644 --- a/src/groth16/verifier.rs +++ b/src/groth16/verifier.rs @@ -1,10 +1,11 @@ use pairing::{ Engine, CurveProjective, - CurveAffine, - PrimeField + CurveAffine }; +use ff::{PrimeField}; + use super::{ Proof, VerifyingKey, diff --git a/src/lib.rs b/src/lib.rs index cc846d6cd..1d04636a6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,11 @@ +#![allow(unused_imports)] + extern crate pairing; extern crate rand; extern crate futures; extern crate bit_vec; extern crate byteorder; +extern crate ff; #[cfg(feature = "multithread")] extern crate futures_cpupool; @@ -15,8 +18,10 @@ pub mod multicore; mod multiexp; pub mod domain; pub mod groth16; +pub mod progress_bar; -use pairing::{Engine, Field}; +use pairing::{Engine}; +use ff::Field; use std::ops::{Add, Sub}; use std::fmt; @@ -89,7 +94,7 @@ impl Add<(E::Fr, Variable)> for LinearCombination { } } -impl Sub<(E::Fr, Variable)> for LinearCombination { +impl Sub<(E::Fr, Variable)> for LinearCombination { type Output = LinearCombination; fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination { @@ -426,3 +431,20 @@ impl<'cs, E: Engine, CS: ConstraintSystem> ConstraintSystem for &'cs mut C (**self).get_root() } } + +static mut VERBOSE_SWITCH: i8 = -1; + +use std::str::FromStr; +use std::env; + +fn verbose_flag() -> bool { + unsafe { + if VERBOSE_SWITCH < 0 { + VERBOSE_SWITCH = FromStr::from_str(&env::var("BELLMAN_VERBOSE").unwrap_or(String::new())).unwrap_or(1); + } + match VERBOSE_SWITCH { + 1 => true, + _ => false, + } + } +} \ No newline at end of file diff --git a/src/multiexp.rs b/src/multiexp.rs index b1dc1f1fc..b32327e33 100644 --- a/src/multiexp.rs +++ b/src/multiexp.rs @@ -1,11 +1,15 @@ use pairing::{ CurveAffine, CurveProjective, - Engine, + Engine +}; + +use ff::{ PrimeField, Field, - PrimeFieldRepr -}; + PrimeFieldRepr, + ScalarEngine}; + use std::sync::Arc; use std::io; use bit_vec::{self, BitVec}; @@ -42,7 +46,7 @@ impl SourceBuilder for (Arc>, usize) { impl Source for (Arc>, usize) { fn add_assign_mixed(&mut self, to: &mut ::Projective) -> Result<(), SynthesisError> { if self.0.len() <= self.1 { - return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases from source").into()); + return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases when adding from source").into()); } if self.0[self.1].is_zero() { @@ -58,7 +62,7 @@ impl Source for (Arc>, usize) { 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()); + return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "expected more bases skipping from source").into()); } self.1 += amt; @@ -96,6 +100,7 @@ impl<'a> QueryDensity for &'a FullDensity { } } +#[derive(Clone)] pub struct DensityTracker { bv: BitVec, total_density: usize @@ -141,7 +146,8 @@ fn multiexp_inner( pool: &Worker, bases: S, density_map: D, - exponents: Arc::Fr as PrimeField>::Repr>>, + exponents: Arc::Repr>>, + // exponents: Arc::Fr as PrimeField>::Repr>>, mut skip: u32, c: u32, handle_trivial: bool @@ -157,6 +163,7 @@ fn multiexp_inner( let exponents = exponents.clone(); let density_map = density_map.clone(); + // This looks like a Pippenger’s algorithm pool.compute(move || { // Accumulate the result let mut acc = G::Projective::zero(); @@ -164,14 +171,18 @@ fn multiexp_inner( // Build a source for the bases let mut bases = bases.new(); + // Create buckets to place remainders s mod 2^c, + // it will be 2^c - 1 buckets (no bucket for zeroes) + // Create space for the buckets let mut buckets = vec![::Projective::zero(); (1 << c) - 1]; - let zero = ::Fr::zero().into_repr(); - let one = ::Fr::one().into_repr(); + let zero = ::Fr::zero().into_repr(); + let one = ::Fr::one().into_repr(); // Sort the bases into buckets for (&exp, density) in exponents.iter().zip(density_map.as_ref().iter()) { + // Go over density and exponents if density { if exp == zero { bases.skip(1)?; @@ -182,6 +193,11 @@ fn multiexp_inner( bases.skip(1)?; } } else { + // Place multiplication into the bucket: Separate s * P as + // (s/2^c) * P + (s mod 2^c) P + // First multiplication is c bits less, do one can do it, + // sum results from different buckets and double it c times, + // then add with (s mod 2^c) P parts let mut exp = exp; exp.shr(skip); let exp = exp.as_ref()[0] % (1 << c); @@ -211,7 +227,7 @@ fn multiexp_inner( skip += c; - if skip >= ::Fr::NUM_BITS { + if skip >= ::Fr::NUM_BITS { // There isn't another region. Box::new(this) } else { @@ -238,7 +254,7 @@ pub fn multiexp( pool: &Worker, bases: S, density_map: D, - exponents: Arc::Fr as PrimeField>::Repr>> + exponents: Arc::Fr as PrimeField>::Repr>> ) -> Box::Projective, Error=SynthesisError>> where for<'a> &'a Q: QueryDensity, D: Send + Sync + 'static + Clone + AsRef, @@ -285,7 +301,7 @@ fn test_with_bls12() { const SAMPLES: usize = 1 << 14; let rng = &mut rand::thread_rng(); - let v = Arc::new((0..SAMPLES).map(|_| ::Fr::rand(rng).into_repr()).collect::>()); + let v = Arc::new((0..SAMPLES).map(|_| ::Fr::rand(rng).into_repr()).collect::>()); let g = Arc::new((0..SAMPLES).map(|_| ::G1::rand(rng).into_affine()).collect::>()); let naive = naive_multiexp(g.clone(), v.clone()); diff --git a/src/progress_bar.rs b/src/progress_bar.rs new file mode 100644 index 000000000..f8574c0bf --- /dev/null +++ b/src/progress_bar.rs @@ -0,0 +1,146 @@ +extern crate time; + +use std::io::{Write}; +use std::sync::{ + mpsc::{channel, Sender, Receiver}, + Arc, + atomic::{AtomicUsize, Ordering} +}; +use self::time::precise_time_ns; +use std::time::Duration; + +static UPDATE_INTERVAL: u64 = 1000_000 * 1000; // ms + +pub struct MultiBar { + n_workers: u64, + + total: u64, + cur: u64, + + prev: u64, + prev_time: u64, + + total_elapsed: u64, + + step: Arc, + tx: Sender, + rx: Receiver, +} + +pub struct ProgressBar { + //chunk: u64, + acc: u64, + step: Arc, + tx: Option>, +} + +/// Simple efficient thread-safe progress indicator +/// It follows the interface of [https://github.com/a8m/pb](https://github.com/a8m/pb) +impl MultiBar { + + /// Create a new MultiBar for stdout + pub fn new() -> Self { + let (tx, rx) = channel(); + Self{ + n_workers: 0, + total: 0, + cur: 0, + prev: 0, + prev_time: precise_time_ns(), + total_elapsed: 0, + step: Arc::new(AtomicUsize::new(1)), + tx, + rx, + } + } + + // Create a ProgressBar for a process of `total` steps + pub fn create_bar(&mut self, chunk: u64) -> ProgressBar { + self.n_workers += 1; + self.total += chunk; + //println!("step 0 of {}", chunk); + ProgressBar{ + //chunk, + acc: 0, + tx: Some(Sender::clone(&self.tx)), + step: Arc::clone(&self.step), + } + } + + /// Start listening for updates from ProgressBars in different threads + pub fn listen(&mut self) { + //println!(""); + for d in &self.rx { + if d == 0 { + self.n_workers -= 1; + } + if self.n_workers == 0 { + break; + } + + self.cur += d; + let processed = self.cur - self.prev; + if processed > self.step.load(Ordering::Acquire) as u64 * self.n_workers { + let now = time::precise_time_ns(); + let elapsed = now - self.prev_time; + + if elapsed > UPDATE_INTERVAL { + self.prev = self.cur; + self.prev_time = now; + self.total_elapsed += elapsed; + + print!("\rprocessed {:2}%: {} of {}.", self.cur * 100 / self.total, self.cur, self.total); + + let r = Duration::from_nanos((self.total - self.cur) * self.total_elapsed / self.cur).as_secs(); + print!(" Remaining estimated: {} h {} min {} s", r / 3600, r % 3600 / 60, r % 60); + + let new_step = (self.cur * UPDATE_INTERVAL / self.total_elapsed) / self.n_workers; + self.step.store(new_step as usize, Ordering::Release); + + std::io::stdout().flush().unwrap(); + } + } + + } + println!("\rdone "); + } +} + +impl ProgressBar { + + /// Increment progress by `d` steps + pub fn add(&mut self, d: u64) { + self.acc += d; + if self.acc > (self.step.load(Ordering::Relaxed) as u64) { + if let Some(tx) = &self.tx { + tx.send(self.acc).unwrap(); + } + self.acc = 0; + } + } + + /// Finish the process + pub fn finish(&mut self) { + let tx = self.tx.take().unwrap(); + tx.send(0).unwrap(); + drop(tx); + } +} + +#[test] +fn test_progress_display() { + + let mut mb = MultiBar::new(); + + for _j in 1..=0 { + let mut pb = mb.create_bar(3600000); + std::thread::spawn(move || { + for _i in 0..3600000 { + std::thread::sleep(Duration::from_millis(1)); + pb.add(1); + } + pb.finish(); + }); + }; + //mb.listen(); +} diff --git a/tests/mimc.rs b/tests/mimc.rs index d6ff72b7b..3b24cb9ec 100644 --- a/tests/mimc.rs +++ b/tests/mimc.rs @@ -1,6 +1,7 @@ extern crate bellman; extern crate pairing; extern crate rand; +extern crate ff; // For randomness (during paramgen and proof generation) use rand::{thread_rng, Rng}; @@ -10,8 +11,11 @@ use std::time::{Duration, Instant}; // Bring in some tools for using pairing-friendly curves use pairing::{ - Engine, - Field + Engine +}; + +use ff::{ + Field, }; // We're going to use the BLS12-381 pairing-friendly elliptic curve. @@ -19,6 +23,10 @@ use pairing::bls12_381::{ Bls12 }; +use pairing::bn256::{ + Bn256 +}; + // We'll use these interfaces to construct our circuit. use bellman::{ Circuit, @@ -249,3 +257,88 @@ fn test_mimc() { println!("Average proving time: {:?} seconds", proving_avg); println!("Average verifying time: {:?} seconds", verifying_avg); } + +#[test] +fn test_mimc_bn256() { + // 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); + + // Just a place to put the proof data, so we can + // benchmark deserialization. + let mut proof_vec = vec![]; + + 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); + + proof_vec.truncate(0); + + let start = Instant::now(); + { + // 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. + let proof = create_random_proof(c, ¶ms, rng).unwrap(); + + proof.write(&mut proof_vec).unwrap(); + } + + total_proving += start.elapsed(); + + let start = Instant::now(); + let proof = Proof::read(&proof_vec[..]).unwrap(); + // 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); +} +