fix for newer pairing and ff

This commit is contained in:
Alex Vlasov 2019-07-13 16:58:25 +03:00
parent b6160fcd1b
commit 2d9f5528d4
5 changed files with 207 additions and 164 deletions

@ -6,7 +6,7 @@ homepage = "https://github.com/matter-labs/bellman"
license = "MIT/Apache-2.0"
name = "bellman_ce"
repository = "https://github.com/matter-labs/bellman"
version = "0.3.0"
version = "0.3.1"
edition = "2018"
[lib]
@ -18,8 +18,8 @@ bit-vec = "0.4.4"
futures = "0.1"
cfg-if = "0.1.7"
#pairing_ce = { path = "../pairing" }
pairing_ce = { version = "0.17.0" }
pairing = {package = "pairing_ce", path = "../pairing" }
#pairing = {package = "pairing_ce", version = "0.17.0" }
byteorder = "1"
futures-cpupool = {version = "0.1", optional = true}
@ -32,9 +32,7 @@ tiny-keccak = {version = "1.4.2", optional = true}
blake2-rfc = {version = "0.2.18", optional = true}
[features]
#default = ["multicore"]
default = ["multicore", "sonic"]
#default = ["multicore", "gm17", "sonic"]
default = ["multicore"]
#default = ["wasm"]
multicore = ["futures-cpupool", "num_cpus", "crossbeam"]
sonic = ["tiny-keccak", "blake2-rfc"]

@ -86,6 +86,48 @@ fn eval<E: Engine>(
acc
}
pub(crate) fn field_elements_into_representations<E: Engine>(
worker: &Worker,
scalars: Vec<E::Fr>
) -> Result<Vec<<E::Fr as PrimeField>::Repr>, SynthesisError>
{
let mut representations = vec![<E::Fr as PrimeField>::Repr::default(); scalars.len()];
worker.scope(scalars.len(), |scope, chunk| {
for (scalar, repr) in scalars.chunks(chunk)
.zip(representations.chunks_mut(chunk)) {
scope.spawn(move |_| {
for (scalar, repr) in scalar.iter()
.zip(repr.iter_mut()) {
*repr = scalar.into_repr();
}
});
}
});
Ok(representations)
}
pub(crate) fn scalars_into_representations<E: Engine>(
worker: &Worker,
scalars: Vec<Scalar<E>>
) -> Result<Vec<<E::Fr as PrimeField>::Repr>, SynthesisError>
{
let mut representations = vec![<E::Fr as PrimeField>::Repr::default(); scalars.len()];
worker.scope(scalars.len(), |scope, chunk| {
for (scalar, repr) in scalars.chunks(chunk)
.zip(representations.chunks_mut(chunk)) {
scope.spawn(move |_| {
for (scalar, repr) in scalar.iter()
.zip(repr.iter_mut()) {
*repr = scalar.0.into_repr();
}
});
}
});
Ok(representations)
}
// This is a proving assignment with densities precalculated
pub struct PreparedProver<E: Engine>{
assignment: ProvingAssignment<E>,
@ -145,7 +187,7 @@ pub fn prepare_prover<E, C>(
impl<E:Engine> PreparedProver<E> {
pub fn create_random_proof<R, P: ParameterSource<E>>(
& self,
self,
params: P,
rng: &mut R
) -> Result<Proof<E>, SynthesisError>
@ -158,16 +200,16 @@ impl<E:Engine> PreparedProver<E> {
}
pub fn create_proof<P: ParameterSource<E>>(
& self,
self,
mut params: P,
r: E::Fr,
s: E::Fr
) -> Result<Proof<E>, SynthesisError>
{
let prover = self.assignment.clone();
let prover = self.assignment;
let worker = Worker::new();
let vk = params.get_vk(self.assignment.input_assignment.len())?;
let vk = params.get_vk(prover.input_assignment.len())?;
let stopwatch = Stopwatch::new();
@ -202,7 +244,8 @@ impl<E:Engine> PreparedProver<E> {
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<_>>());
let a = Arc::new(scalars_into_representations::<E>(&worker, a)?);
// let a = Arc::new(a.into_iter().map(|s| s.0.into_repr()).collect::<Vec<_>>());
multiexp(&worker, params.get_h(a.len())?, FullDensity, a)
};
@ -213,13 +256,19 @@ impl<E:Engine> PreparedProver<E> {
// TODO: Check that difference in operations for different chunks is small
let input_len = prover.input_assignment.len();
let aux_len = prover.aux_assignment.len();
let input_assignment = Arc::new(field_elements_into_representations::<E>(&worker, prover.input_assignment)?);
let aux_assignment = Arc::new(field_elements_into_representations::<E>(&worker, prover.aux_assignment)?);
// 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<_>>());
// 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<_>>());
let input_len = input_assignment.len();
let aux_len = aux_assignment.len();
// let input_len = input_assignment.len();
// let aux_len = aux_assignment.len();
elog_verbose!("H query is dense in G1,\nOther queries are {} elements in G1 and {} elements in G2",
2*(input_len + aux_len) + aux_len, input_len + aux_len);
@ -402,153 +451,13 @@ pub fn create_random_proof<E, C, R, P: ParameterSource<E>>(
pub fn create_proof<E, C, P: ParameterSource<E>>(
circuit: C,
mut params: P,
params: P,
r: E::Fr,
s: E::Fr
) -> Result<Proof<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![]
};
let prover = prepare_prover(circuit)?;
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 stopwatch = Stopwatch::new();
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)?;
elog_verbose!("H query domain size is {}", a.as_ref().len());
// here a coset is a domain where denominator (z) does not vanish
// inverse FFT is an interpolation
a.ifft(&worker);
// 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)
};
elog_verbose!("{} seconds for prover for H evaluation (mostly FFT)", stopwatch.elapsed());
let stopwatch = Stopwatch::new();
// 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()?);
elog_verbose!("{} seconds for prover for point multiplication", stopwatch.elapsed());
Ok(Proof {
a: g_a.into_affine(),
b: g_b.into_affine(),
c: g_c.into_affine()
})
prover.create_proof(params, r, s)
}

@ -3,7 +3,7 @@
#[macro_use]
extern crate cfg_if;
extern crate pairing_ce as pairing_import;
pub extern crate pairing;
extern crate rand;
extern crate bit_vec;
extern crate byteorder;
@ -43,10 +43,6 @@ cfg_if! {
}
}
pub mod pairing {
pub use pairing_import::*;
}
mod cs;
pub use self::cs::*;

@ -68,7 +68,7 @@ fn multiexp_inner<Q, D, G, S>(
let exponents = exponents.clone();
let density_map = density_map.clone();
// This looks like a Pippengers algorithm
// This is a Pippengers algorithm
pool.compute(move || {
// Accumulate the result
let mut acc = G::Projective::zero();
@ -153,6 +153,116 @@ fn multiexp_inner<Q, D, G, S>(
}
}
fn multiexp_inner_with_prefetch<Q, D, G, S>(
pool: &Worker,
bases: S,
density_map: D,
exponents: Arc<Vec<<G::Scalar as PrimeField>::Repr>>,
mut skip: u32,
c: u32,
handle_trivial: bool
) -> Box<Future<Item=<G as CurveAffine>::Projective, Error=SynthesisError>>
where for<'a> &'a Q: QueryDensity,
D: Send + Sync + 'static + Clone + AsRef<Q>,
G: CurveAffine,
S: SourceBuilder<G>
{
// Perform this region of the multiexp
let this = {
let bases = bases.clone();
let exponents = exponents.clone();
let density_map = density_map.clone();
// This is a Pippengers algorithm
pool.compute(move || {
// Accumulate the result
let mut acc = G::Projective::zero();
// 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 ScalarEngine>::Fr::zero().into_repr();
let one = <G::Engine as ScalarEngine>::Fr::one().into_repr();
let padding = Arc::new(vec![zero]);
// Sort the bases into buckets
for ((&exp, &next_exp), density) in exponents.iter()
.zip(exponents.iter().skip(1).chain(padding.iter()))
.zip(density_map.as_ref().iter()) {
// no matter what happens - prefetch next bucket
// Go over density and exponents
if density {
if exp == zero {
bases.skip(1)?;
} else if exp == one {
if handle_trivial {
bases.add_assign_mixed(&mut acc)?;
} else {
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, so 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);
if exp != 0 {
bases.add_assign_mixed(&mut buckets[(exp - 1) as usize])?;
} else {
bases.skip(1)?;
}
}
}
}
// Summation by parts
// e.g. 3a + 2b + 1c = a +
// (a) + b +
// ((a) + b) + c
let mut running_sum = G::Projective::zero();
for exp in buckets.into_iter().rev() {
running_sum.add_assign(&exp);
acc.add_assign(&running_sum);
}
Ok(acc)
})
};
skip += c;
if skip >= <G::Engine as ScalarEngine>::Fr::NUM_BITS {
// There isn't another region.
Box::new(this)
} else {
// There's another region more significant. Calculate and join it with
// this region recursively.
Box::new(
this.join(multiexp_inner(pool, bases, density_map, exponents, skip, c, false))
.map(move |(this, mut higher)| {
for _ in 0..c {
higher.double();
}
higher.add_assign(&this);
higher
})
)
}
}
/// Perform multi-exponentiation. The caller is responsible for ensuring the
/// query size is the same as the number of exponents.
pub fn multiexp<Q, D, G, S>(

@ -3,6 +3,7 @@ use crate::pairing::{
CurveProjective,
CurveAffine,
GroupDecodingError,
RawEncodable,
EncodedPoint
};
@ -90,6 +91,23 @@ impl Field for Fr {
}
}
impl RawEncodable for Fr {
fn into_raw_uncompressed_le(&self) -> Self::Uncompressed {
Self::Uncompressed::empty()
}
fn from_raw_uncompressed_le_unchecked(
_encoded: &Self::Uncompressed,
_infinity: bool
) -> Result<Self, GroupDecodingError> {
Ok(<Self as Field>::zero())
}
fn from_raw_uncompressed_le(encoded: &Self::Uncompressed, _infinity: bool) -> Result<Self, GroupDecodingError> {
Self::from_raw_uncompressed_le_unchecked(&encoded, _infinity)
}
}
impl SqrtField for Fr {
fn legendre(&self) -> LegendreSymbol {
// s = self^((r - 1) // 2)
@ -247,6 +265,14 @@ impl PrimeField for Fr {
}
}
fn from_raw_repr(repr: FrRepr) -> Result<Self, PrimeFieldDecodingError> {
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)
}
@ -255,6 +281,10 @@ impl PrimeField for Fr {
Fr(MODULUS_R).into()
}
fn into_raw_repr(&self) -> FrRepr {
FrRepr::from(*self)
}
fn multiplicative_generator() -> Fr {
Fr(Wrapping(5))
}