arnaucube
arnaucube/NovaPublic Self-hosted repository
Nova/src/provider/mod.rs
src/provider/mod.rsRaw
  1//! This module implements Nova's traits using the following configuration:
  2//! `CommitmentEngine` with Pedersen's commitments
  3//! `Group` with pasta curves and BN256/Grumpkin
  4//! `RO` traits with Poseidon
  5//! `EvaluationEngine` with an IPA-based polynomial evaluation argument
  6
  7pub mod bn256_grumpkin;
  8pub mod ipa_pc;
  9pub mod keccak;
 10pub mod pasta;
 11pub mod pedersen;
 12pub mod poseidon;
 13
 14use ff::PrimeField;
 15use pasta_curves::{self, arithmetic::CurveAffine, group::Group as AnotherGroup};
 16
 17/// Native implementation of fast multiexp
 18/// Adapted from zcash/halo2
 19fn cpu_multiexp_serial<C: CurveAffine>(coeffs: &[C::Scalar], bases: &[C], acc: &mut C::Curve) {
 20  let coeffs: Vec<_> = coeffs.iter().map(|a| a.to_repr()).collect();
 21
 22  let c = if bases.len() < 4 {
 23    1
 24  } else if bases.len() < 32 {
 25    3
 26  } else {
 27    (f64::from(bases.len() as u32)).ln().ceil() as usize
 28  };
 29
 30  fn get_at<F: PrimeField>(segment: usize, c: usize, bytes: &F::Repr) -> usize {
 31    let skip_bits = segment * c;
 32    let skip_bytes = skip_bits / 8;
 33
 34    if skip_bytes >= 32 {
 35      return 0;
 36    }
 37
 38    let mut v = [0; 8];
 39    for (v, o) in v.iter_mut().zip(bytes.as_ref()[skip_bytes..].iter()) {
 40      *v = *o;
 41    }
 42
 43    let mut tmp = u64::from_le_bytes(v);
 44    tmp >>= skip_bits - (skip_bytes * 8);
 45    tmp %= 1 << c;
 46
 47    tmp as usize
 48  }
 49
 50  let segments = (256 / c) + 1;
 51
 52  for current_segment in (0..segments).rev() {
 53    for _ in 0..c {
 54      *acc = acc.double();
 55    }
 56
 57    #[derive(Clone, Copy)]
 58    enum Bucket<C: CurveAffine> {
 59      None,
 60      Affine(C),
 61      Projective(C::Curve),
 62    }
 63
 64    impl<C: CurveAffine> Bucket<C> {
 65      fn add_assign(&mut self, other: &C) {
 66        *self = match *self {
 67          Bucket::None => Bucket::Affine(*other),
 68          Bucket::Affine(a) => Bucket::Projective(a + *other),
 69          Bucket::Projective(mut a) => {
 70            a += *other;
 71            Bucket::Projective(a)
 72          }
 73        }
 74      }
 75
 76      fn add(self, mut other: C::Curve) -> C::Curve {
 77        match self {
 78          Bucket::None => other,
 79          Bucket::Affine(a) => {
 80            other += a;
 81            other
 82          }
 83          Bucket::Projective(a) => other + a,
 84        }
 85      }
 86    }
 87
 88    let mut buckets: Vec<Bucket<C>> = vec![Bucket::None; (1 << c) - 1];
 89
 90    for (coeff, base) in coeffs.iter().zip(bases.iter()) {
 91      let coeff = get_at::<C::Scalar>(current_segment, c, coeff);
 92      if coeff != 0 {
 93        buckets[coeff - 1].add_assign(base);
 94      }
 95    }
 96
 97    // Summation by parts
 98    // e.g. 3a + 2b + 1c = a +
 99    //                    (a) + b +
100    //                    ((a) + b) + c
101    let mut running_sum = C::Curve::identity();
102    for exp in buckets.into_iter().rev() {
103      running_sum = exp.add(running_sum);
104      *acc += &running_sum;
105    }
106  }
107}
108
109/// Performs a multi-exponentiation operation without GPU acceleration.
110///
111/// This function will panic if coeffs and bases have a different length.
112///
113/// This will use multithreading if beneficial.
114/// Adapted from zcash/halo2
115pub(crate) fn cpu_best_multiexp<C: CurveAffine>(coeffs: &[C::Scalar], bases: &[C]) -> C::Curve {
116  assert_eq!(coeffs.len(), bases.len());
117
118  let num_threads = rayon::current_num_threads();
119  if coeffs.len() > num_threads {
120    let chunk = coeffs.len() / num_threads;
121    let num_chunks = coeffs.chunks(chunk).len();
122    let mut results = vec![C::Curve::identity(); num_chunks];
123    rayon::scope(|scope| {
124      for ((coeffs, bases), acc) in coeffs
125        .chunks(chunk)
126        .zip(bases.chunks(chunk))
127        .zip(results.iter_mut())
128      {
129        scope.spawn(move |_| {
130          cpu_multiexp_serial(coeffs, bases, acc);
131        });
132      }
133    });
134    results.iter().fold(C::Curve::identity(), |a, b| a + b)
135  } else {
136    let mut acc = C::Curve::identity();
137    cpu_multiexp_serial(coeffs, bases, &mut acc);
138    acc
139  }
140}