migrate to community edition

This commit is contained in:
Alex Vlasov 2018-12-08 05:31:26 +08:00
parent 10c5010fd9
commit d7d2b45441
12 changed files with 631 additions and 42 deletions

@ -1,12 +1,12 @@
[package]
authors = ["Sean Bowe <ewillbefull@gmail.com>"]
authors = ["Sean Bowe <ewillbefull@gmail.com>", "Alex Vlasov <alex.m.vlasov@gmail.com>"]
description = "zk-SNARK library"
documentation = "https://github.com/ebfull/bellman"
homepage = "https://github.com/ebfull/bellman"
documentation = "https://github.com/matterinc/bellman"
homepage = "https://github.com/matterinc/bellman"
license = "MIT/Apache-2.0"
name = "bellman"
repository = "https://github.com/ebfull/bellman"
version = "0.1.0"
repository = "https://github.com/matterinc/bellman"
version = "0.1.1"
[dependencies]
rand = "0.4"
@ -15,8 +15,11 @@ futures = "0.1"
futures-cpupool = "0.1"
num_cpus = "1"
crossbeam = "0.3"
pairing = "0.14"
Pairing = { git = 'https://github.com/matterinc/pairing' }
byteorder = "1"
ff = { version = "0.4", features = ["derive"] }
pbr = "1.0.1"
time = "0.1"
[features]
default = []

@ -12,11 +12,14 @@
use pairing::{
Engine,
Field,
PrimeField,
CurveProjective
};
use ff::{
Field,
PrimeField
};
use super::{
SynthesisError
};
@ -47,22 +50,36 @@ impl<E: Engine, G: Group<E>> EvaluationDomain<E, G> {
pub fn from_coeffs(mut coeffs: Vec<G>) -> Result<EvaluationDomain<E, G>, 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();
}

@ -1,16 +1,29 @@
extern crate time;
extern crate pbr;
use super::super::verbose_flag;
use self::time::PreciseTime;
//use self::pbr::{MultiBar};
use super::super::progress_bar::{MultiBar};
use rand::Rng;
use std::sync::Arc;
use pairing::{
Engine,
PrimeField,
Field,
Wnaf,
CurveProjective,
CurveAffine
};
use ff::{
PrimeField,
Field
};
use super::{
Parameters,
VerifyingKey
@ -169,6 +182,8 @@ impl<E: Engine> ConstraintSystem<E> for KeypairAssembly<E> {
}
}
const MIN_STEP: u64 = 1000;
/// Create parameters for a circuit, given some toxic waste.
pub fn generate_parameters<E, C>(
circuit: C,
@ -182,6 +197,8 @@ pub fn generate_parameters<E, C>(
) -> Result<Parameters<E>, SynthesisError>
where E: Engine, C: Circuit<E>
{
let verbose = verbose_flag();
let mut assembly = KeypairAssembly {
num_inputs: 0,
num_aux: 0,
@ -214,6 +231,8 @@ pub fn generate_parameters<E, C>(
let powers_of_tau = vec![Scalar::<E>(E::Fr::zero()); assembly.num_constraints];
let mut powers_of_tau = EvaluationDomain::from_coeffs(powers_of_tau)?;
if verbose {eprintln!("assembly.num_constraints: {}", assembly.num_constraints)};
// Compute G1 window table
let mut g1_wnaf = Wnaf::new();
let g1_wnaf = g1_wnaf.base(g1, {
@ -242,11 +261,14 @@ pub fn generate_parameters<E, C>(
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| {
for (i, powers_of_tau) in powers_of_tau.chunks_mut(chunk).enumerate()
{
//let mut progress_bar = mb.create_bar(a.len() as u64);
scope.spawn(move || {
let mut current_tau_power = tau.pow(&[(i*chunk) as u64]);
@ -258,19 +280,24 @@ pub fn generate_parameters<E, C>(
}
});
}
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| {
let mut mb = if verbose {Some(MultiBar::new())} else {None};
for (h, p) in h.chunks_mut(chunk).zip(powers_of_tau.as_ref().chunks(chunk))
{
let mut g1_wnaf = g1_wnaf.shared();
let mut progress_bar = if let Some(mb) = mb.as_mut() {Some(mb.create_bar(h.len() as u64))} else {None};
scope.spawn(move || {
// Set values of the H query to g1^{(tau^i * t(tau)) / delta}
let mut step: u64 = 0;
for (h, p) in h.iter_mut().zip(p.iter())
{
// Compute final exponent
@ -279,18 +306,30 @@ pub fn generate_parameters<E, C>(
// Exponentiate
*h = g1_wnaf.scalar(exp.into_repr());
if let Some(progress_bar) = progress_bar.as_mut() {
step += 1;
if step % MIN_STEP == 0 {
progress_bar.add(MIN_STEP);
};
};
}
// Batch normalize
E::G1::batch_normalization(h);
if let Some(progress_bar) = progress_bar.as_mut() {progress_bar.finish();}
});
}
if let Some(mb) = mb.as_mut() {mb.listen();}
});
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];
@ -298,6 +337,8 @@ pub fn generate_parameters<E, C>(
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<E: Engine>(
// wNAF window tables
g1_wnaf: &Wnaf<usize, &[E::G1], &mut Vec<i64>>,
@ -328,6 +369,8 @@ pub fn generate_parameters<E, C>(
worker: &Worker
)
{
let verbose = verbose_flag();
// Sanity check
assert_eq!(a.len(), at.len());
assert_eq!(a.len(), bt.len());
@ -338,6 +381,7 @@ pub fn generate_parameters<E, C>(
// Evaluate polynomials in multiple threads
worker.scope(a.len(), |scope, chunk| {
let mut mb = if verbose {Some(MultiBar::new())} else {None};
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))
@ -349,7 +393,9 @@ pub fn generate_parameters<E, C>(
let mut g1_wnaf = g1_wnaf.shared();
let mut g2_wnaf = g2_wnaf.shared();
let mut progress_bar = if let Some(mb) = mb.as_mut() {Some(mb.create_bar(a.len() as u64))} else {None};
scope.spawn(move || {
let mut step: u64 = 0;
for ((((((a, b_g1), b_g2), ext), at), bt), ct) in a.iter_mut()
.zip(b_g1.iter_mut())
.zip(b_g2.iter_mut())
@ -400,15 +446,24 @@ pub fn generate_parameters<E, C>(
e.mul_assign(inv);
*ext = g1_wnaf.scalar(e.into_repr());
if let Some(progress_bar) = progress_bar.as_mut() {
step += 1;
if step % MIN_STEP == 0 {
progress_bar.add(MIN_STEP);
};
};
}
if let Some(progress_bar) = progress_bar.as_mut() {progress_bar.finish();}
// Batch normalize
E::G1::batch_normalization(a);
E::G1::batch_normalization(b_g1);
E::G2::batch_normalization(b_g2);
E::G1::batch_normalization(ext);
});
}
};
if let Some(mb) = mb.as_mut() {mb.listen();}
});
}
@ -448,6 +503,8 @@ pub fn generate_parameters<E, C>(
&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() {

@ -24,7 +24,7 @@ pub use self::generator::*;
pub use self::prover::*;
pub use self::verifier::*;
#[derive(Clone)]
#[derive(Debug, Clone)]
pub struct Proof<E: Engine> {
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());
}
}
}
}

@ -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<E: Engine>(
acc
}
// This is a proving assignment with densities precalculated
pub struct PreparedProver<E: Engine>{
assignment: ProvingAssignment<E>,
}
#[derive(Clone)]
struct ProvingAssignment<E: Engine> {
// Density of queries
a_aux_density: DensityTracker,
@ -96,6 +110,191 @@ struct ProvingAssignment<E: Engine> {
aux_assignment: Vec<E::Fr>
}
pub fn prepare_prover<E, C>(
circuit: C,
) -> Result<PreparedProver<E>, SynthesisError>
where E: Engine, C: Circuit<E>
{
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<E:Engine> PreparedProver<E> {
pub fn create_random_proof<R, P: ParameterSource<E>>(
& self,
params: P,
rng: &mut R
) -> Result<Proof<E>, SynthesisError>
where R: Rng
{
let r = rng.gen();
let s = rng.gen();
self.create_proof(params, r, s)
}
pub fn create_proof<P: ParameterSource<E>>(
& self,
mut params: P,
r: E::Fr,
s: E::Fr
) -> Result<Proof<E>, 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::<Vec<_>>());
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::<Vec<_>>());
let aux_assignment = Arc::new(prover.aux_assignment.into_iter().map(|s| s.into_repr()).collect::<Vec<_>>());
// 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<E: Engine> ConstraintSystem<E> for ProvingAssignment<E> {
type Root = Self;
@ -209,6 +408,8 @@ pub fn create_proof<E, C, P: ParameterSource<E>>(
) -> Result<Proof<E>, SynthesisError>
where E: Engine, C: Circuit<E>
{
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<E, C, P: ParameterSource<E>>(
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::<Vec<_>>());
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::<Vec<_>>());
let aux_assignment = Arc::new(prover.aux_assignment.into_iter().map(|s| s.into_repr()).collect::<Vec<_>>());
// 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<E, C, P: ParameterSource<E>>(
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(),

@ -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;

@ -1,7 +1,10 @@
use pairing::{
Engine,
Engine
};
use ff:: {
Field,
PrimeField
PrimeField,
};
mod dummy_engine;

@ -1,10 +1,11 @@
use pairing::{
Engine,
CurveProjective,
CurveAffine,
PrimeField
CurveAffine
};
use ff::{PrimeField};
use super::{
Proof,
VerifyingKey,

@ -1,3 +1,5 @@
#![allow(unused_imports)]
extern crate pairing;
extern crate rand;
extern crate num_cpus;
@ -6,13 +8,16 @@ extern crate futures_cpupool;
extern crate bit_vec;
extern crate crossbeam;
extern crate byteorder;
extern crate ff;
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;
@ -85,7 +90,7 @@ impl<E: Engine> Add<(E::Fr, Variable)> for LinearCombination<E> {
}
}
impl<E: Engine> Sub<(E::Fr, Variable)> for LinearCombination<E> {
impl<E: Engine> Sub<(E::Fr, Variable)> for LinearCombination<E> {
type Output = LinearCombination<E>;
fn sub(self, (mut coeff, var): (E::Fr, Variable)) -> LinearCombination<E> {
@ -422,3 +427,20 @@ impl<'cs, E: Engine, CS: ConstraintSystem<E>> ConstraintSystem<E> 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(0);
}
match VERBOSE_SWITCH {
1 => true,
_ => false,
}
}
}

@ -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};
@ -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<Q, D, G, S>(
pool: &Worker,
bases: S,
density_map: D,
exponents: Arc<Vec<<<G::Engine as Engine>::Fr as PrimeField>::Repr>>,
exponents: Arc<Vec<<G::Scalar as PrimeField>::Repr>>,
// exponents: Arc<Vec<<<G::Engine as Engine>::Fr as PrimeField>::Repr>>,
mut skip: u32,
c: u32,
handle_trivial: bool
@ -157,6 +163,7 @@ fn multiexp_inner<Q, D, G, S>(
let exponents = exponents.clone();
let density_map = density_map.clone();
// This looks like a Pippengers algorithm
pool.compute(move || {
// Accumulate the result
let mut acc = G::Projective::zero();
@ -164,14 +171,18 @@ fn multiexp_inner<Q, D, G, S>(
// 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![<G as CurveAffine>::Projective::zero(); (1 << c) - 1];
let zero = <G::Engine as Engine>::Fr::zero().into_repr();
let one = <G::Engine as Engine>::Fr::one().into_repr();
let zero = <G::Engine as ScalarEngine>::Fr::zero().into_repr();
let one = <G::Engine as ScalarEngine>::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<Q, D, G, S>(
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<Q, D, G, S>(
skip += c;
if skip >= <G::Engine as Engine>::Fr::NUM_BITS {
if skip >= <G::Engine as ScalarEngine>::Fr::NUM_BITS {
// There isn't another region.
Box::new(this)
} else {
@ -238,7 +254,7 @@ pub fn multiexp<Q, D, G, S>(
pool: &Worker,
bases: S,
density_map: D,
exponents: Arc<Vec<<<G::Engine as Engine>::Fr as PrimeField>::Repr>>
exponents: Arc<Vec<<<G::Engine as ScalarEngine>::Fr as PrimeField>::Repr>>
) -> Box<Future<Item=<G as CurveAffine>::Projective, Error=SynthesisError>>
where for<'a> &'a Q: QueryDensity,
D: Send + Sync + 'static + Clone + AsRef<Q>,
@ -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(|_| <Bls12 as Engine>::Fr::rand(rng).into_repr()).collect::<Vec<_>>());
let v = Arc::new((0..SAMPLES).map(|_| <Bls12 as ScalarEngine>::Fr::rand(rng).into_repr()).collect::<Vec<_>>());
let g = Arc::new((0..SAMPLES).map(|_| <Bls12 as Engine>::G1::rand(rng).into_affine()).collect::<Vec<_>>());
let naive = naive_multiexp(g.clone(), v.clone());

146
src/progress_bar.rs Normal file

@ -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<AtomicUsize>,
tx: Sender<u64>,
rx: Receiver<u64>,
}
pub struct ProgressBar {
//chunk: u64,
acc: u64,
step: Arc<AtomicUsize>,
tx: Option<Sender<u64>>,
}
/// 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();
}

@ -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::<Vec<_>>();
println!("Creating parameters...");
// Create parameters for our circuit
let params = {
let c = MiMCDemo::<Bn256> {
xl: None,
xr: None,
constants: &constants
};
generate_random_parameters(c, rng).unwrap()
};
// Prepare the verification key (for proof verification)
let pvk = prepare_verifying_key(&params.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::<Bn256>(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, &params, 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);
}