use super::multicore::Worker; use bit_vec::{self, BitVec}; use ff::{Field, PrimeField, ScalarEngine}; use futures::Future; use group::{CurveAffine, CurveProjective}; use std::io; use std::iter; use std::ops::AddAssign; use std::sync::Arc; use super::SynthesisError; /// An object that builds a source of bases. pub trait SourceBuilder: Send + Sync + 'static + Clone { type Source: Source; fn new(self) -> Self::Source; } /// A source of bases, like an iterator. pub trait Source { fn next(&mut self) -> Result<&G, SynthesisError>; /// Skips `amt` elements from the source, avoiding deserialization. fn skip(&mut self, amt: usize) -> Result<(), SynthesisError>; } pub trait AddAssignFromSource: CurveProjective { /// Parses the element from the source. Fails if the point is at infinity. fn add_assign_from_source::Affine>>( &mut self, source: &mut S, ) -> Result<(), SynthesisError> { AddAssign::<&::Affine>::add_assign(self, source.next()?); Ok(()) } } impl AddAssignFromSource for G where G: CurveProjective {} impl SourceBuilder for (Arc>, usize) { type Source = (Arc>, usize); fn new(self) -> (Arc>, usize) { (self.0.clone(), self.1) } } impl Source for (Arc>, usize) { fn next(&mut self) -> Result<&G, SynthesisError> { if self.0.len() <= self.1 { return Err(io::Error::new( io::ErrorKind::UnexpectedEof, "expected more bases from source", ) .into()); } if self.0[self.1].is_zero() { return Err(SynthesisError::UnexpectedIdentity); } let ret = &self.0[self.1]; self.1 += 1; Ok(ret) } 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()); } self.1 += amt; Ok(()) } } pub trait QueryDensity { /// Returns whether the base exists. type Iter: Iterator; fn iter(self) -> Self::Iter; fn get_query_size(self) -> Option; } #[derive(Clone)] pub struct FullDensity; impl AsRef for FullDensity { fn as_ref(&self) -> &FullDensity { self } } impl<'a> QueryDensity for &'a FullDensity { type Iter = iter::Repeat; fn iter(self) -> Self::Iter { iter::repeat(true) } fn get_query_size(self) -> Option { None } } pub struct DensityTracker { bv: BitVec, total_density: usize, } impl<'a> QueryDensity for &'a DensityTracker { type Iter = bit_vec::Iter<'a>; fn iter(self) -> Self::Iter { self.bv.iter() } fn get_query_size(self) -> Option { Some(self.bv.len()) } } impl DensityTracker { pub fn new() -> DensityTracker { DensityTracker { bv: BitVec::new(), total_density: 0, } } pub fn add_element(&mut self) { self.bv.push(false); } pub fn inc(&mut self, idx: usize) { if !self.bv.get(idx).unwrap() { self.bv.set(idx, true); self.total_density += 1; } } pub fn get_total_density(&self) -> usize { self.total_density } } fn multiexp_inner( pool: &Worker, bases: S, density_map: D, exponents: Arc::Fr>>, mut skip: u32, c: u32, handle_trivial: bool, ) -> Box> where for<'a> &'a Q: QueryDensity, D: Send + Sync + 'static + Clone + AsRef, G: CurveProjective, S: SourceBuilder<::Affine>, { // Perform this region of the multiexp let this = { let bases = bases.clone(); let exponents = exponents.clone(); let density_map = density_map.clone(); pool.compute(move || { // Accumulate the result let mut acc = G::zero(); // Build a source for the bases let mut bases = bases.new(); // Create space for the buckets let mut buckets = vec![G::zero(); (1 << c) - 1]; let one = ::Fr::one(); // Sort the bases into buckets for (&exp, density) in exponents.iter().zip(density_map.as_ref().iter()) { if density { if exp.is_zero() { bases.skip(1)?; } else if exp == one { if handle_trivial { acc.add_assign_from_source(&mut bases)?; } else { bases.skip(1)?; } } else { let exp = exp >> skip; let exp = exp & ((1 << c) - 1); if exp != 0 { (&mut buckets[(exp - 1) as usize]) .add_assign_from_source(&mut bases)?; } else { bases.skip(1)?; } } } } // Summation by parts // e.g. 3a + 2b + 1c = a + // (a) + b + // ((a) + b) + c let mut running_sum = G::zero(); for exp in buckets.into_iter().rev() { running_sum.add_assign(&exp); acc.add_assign(&running_sum); } Ok(acc) }) }; skip += c; if skip >= ::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): (_, G)| { 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( pool: &Worker, bases: S, density_map: D, exponents: Arc::Fr>>, ) -> Box> where for<'a> &'a Q: QueryDensity, D: Send + Sync + 'static + Clone + AsRef, G: CurveProjective, S: SourceBuilder<::Affine>, { let c = if exponents.len() < 32 { 3u32 } else { (f64::from(exponents.len() as u32)).ln().ceil() as u32 }; if let Some(query_size) = density_map.as_ref().get_query_size() { // If the density map has a known query size, it should not be // inconsistent with the number of exponents. assert!(query_size == exponents.len()); } multiexp_inner(pool, bases, density_map, exponents, 0, c, true) } #[cfg(feature = "pairing")] #[test] fn test_with_bls12() { fn naive_multiexp( bases: Arc::Affine>>, exponents: Arc>, ) -> G { assert_eq!(bases.len(), exponents.len()); let mut acc = G::zero(); for (base, exp) in bases.iter().zip(exponents.iter()) { AddAssign::<&G>::add_assign(&mut acc, &base.mul(exp.into_repr())); } acc } use pairing::{bls12_381::Bls12, Engine}; use rand; const SAMPLES: usize = 1 << 14; let rng = &mut rand::thread_rng(); let v = Arc::new( (0..SAMPLES) .map(|_| ::Fr::random(rng)) .collect::>(), ); let g = Arc::new( (0..SAMPLES) .map(|_| ::G1::random(rng).into_affine()) .collect::>(), ); let naive: ::G1 = naive_multiexp(g.clone(), v.clone()); let pool = Worker::new(); let fast = multiexp(&pool, (g, 0), FullDensity, v).wait().unwrap(); assert_eq!(naive, fast); }