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