use pairing::*; use std::sync::Arc; use std::io; use bit_vec::{self, BitVec}; use std::iter; use futures::{Future}; use futures_cpupool::CpuPool; use super::Error; /// 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 { /// Parses the element from the source. Fails if the point is at infinity. fn add_assign_mixed(&mut self, to: &mut ::Projective) -> Result<(), Error>; /// Skips `amt` elements from the source, avoiding deserialization. fn skip(&mut self, amt: usize) -> Result<(), Error>; } 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 add_assign_mixed(&mut self, to: &mut ::Projective) -> Result<(), Error> { 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(Error::UnexpectedIdentity) } to.add_assign_mixed(&self.0[self.1]); self.1 += 1; Ok(()) } fn skip(&mut self, amt: usize) -> Result<(), Error> { 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: &CpuPool, bases: S, density_map: D, exponents: Arc::Fr as PrimeField>::Repr>>, mut skip: u32, c: u32, handle_trivial: bool ) -> Box::Projective, Error=Error>> where for<'a> &'a Q: QueryDensity, D: Send + Sync + 'static + Clone + AsRef, G: CurveAffine, S: SourceBuilder { // Perform this region of the multiexp let this = { let bases = bases.clone(); let exponents = exponents.clone(); let density_map = density_map.clone(); pool.spawn_fn(move || { // Accumulate the result let mut acc = G::Projective::zero(); // Build a source for the bases let mut bases = bases.new(); // 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(); // Sort the bases into buckets for (&exp, density) in exponents.iter().zip(density_map.as_ref().iter()) { 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 { let mut exp = exp; exp.divn(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 >= ::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( pool: &CpuPool, bases: S, density_map: D, exponents: Arc::Fr as PrimeField>::Repr>>, // TODO // c: u32 ) -> Box::Projective, Error=Error>> where for<'a> &'a Q: QueryDensity, D: Send + Sync + 'static + Clone + AsRef, G: CurveAffine, S: SourceBuilder { 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, 12, true) } #[test] fn test_with_bls12() { fn naive_multiexp( bases: Arc>, exponents: Arc::Repr>> ) -> G::Projective { assert_eq!(bases.len(), exponents.len()); let mut acc = G::Projective::zero(); for (base, exp) in bases.iter().zip(exponents.iter()) { acc.add_assign(&base.mul(*exp)); } acc } use rand::{self, Rand}; use pairing::bls12_381::Bls12; const SAMPLES: usize = 1 << 17; let rng = &mut rand::thread_rng(); 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()); let pool = CpuPool::new_num_cpus(); let fast = multiexp( &pool, (g, 0), FullDensity, v, // TODO //7 ).wait().unwrap(); assert_eq!(naive, fast); }