migrate to community edition
This commit is contained in:
parent
10c5010fd9
commit
d7d2b45441
15
Cargo.toml
15
Cargo.toml
@ -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,
|
||||
|
26
src/lib.rs
26
src/lib.rs
@ -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 Pippenger’s 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
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(¶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::<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, ¶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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user