mirror of
https://github.com/arnaucube/Nova.git
synced 2026-01-12 00:51:28 +01:00
update README.md and version (#194)
* update README.md and version * move multiexp code to provider/mod.rs * update README.md * small edits * small edits
This commit is contained in:
@@ -872,7 +872,7 @@ mod tests {
|
||||
.to_repr()
|
||||
.as_ref()
|
||||
.iter()
|
||||
.map(|b| format!("{:02x}", b))
|
||||
.map(|b| format!("{b:02x}"))
|
||||
.collect::<String>();
|
||||
assert_eq!(digest_str, expected);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! This module implements the Nova traits for bn256::Point, bn256::Scalar, grumpkin::Point, grumpkin::Scalar.
|
||||
use crate::{
|
||||
provider::{
|
||||
cpu_best_multiexp,
|
||||
keccak::Keccak256Transcript,
|
||||
pedersen::CommitmentEngine,
|
||||
poseidon::{PoseidonRO, PoseidonROCircuit},
|
||||
@@ -208,18 +209,6 @@ impl_traits!(
|
||||
"30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"
|
||||
);
|
||||
|
||||
/// Performs a multi-exponentiation operation without GPU acceleration.
|
||||
///
|
||||
/// This function will panic if coeffs and bases have a different length.
|
||||
///
|
||||
/// This will use multithreading if beneficial.
|
||||
/// Adapted from zcash/halo2
|
||||
// TODO: update once https://github.com/privacy-scaling-explorations/halo2curves/pull/29
|
||||
// (or a successor thereof) is merged
|
||||
fn cpu_best_multiexp<C: CurveAffine>(coeffs: &[C::Scalar], bases: &[C]) -> C::Curve {
|
||||
crate::provider::pasta::cpu_best_multiexp(coeffs, bases)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! This module implements Nova's traits using the following configuration:
|
||||
//! `CommitmentEngine` with Pedersen's commitments
|
||||
//! `Group` with pasta curves
|
||||
//! `Group` with pasta curves and BN256/Grumpkin
|
||||
//! `RO` traits with Poseidon
|
||||
//! `EvaluationEngine` with an IPA-based polynomial evaluation argument
|
||||
|
||||
@@ -10,3 +10,131 @@ pub mod keccak;
|
||||
pub mod pasta;
|
||||
pub mod pedersen;
|
||||
pub mod poseidon;
|
||||
|
||||
use ff::PrimeField;
|
||||
use pasta_curves::{self, arithmetic::CurveAffine, group::Group as AnotherGroup};
|
||||
|
||||
/// Native implementation of fast multiexp
|
||||
/// Adapted from zcash/halo2
|
||||
fn cpu_multiexp_serial<C: CurveAffine>(coeffs: &[C::Scalar], bases: &[C], acc: &mut C::Curve) {
|
||||
let coeffs: Vec<_> = coeffs.iter().map(|a| a.to_repr()).collect();
|
||||
|
||||
let c = if bases.len() < 4 {
|
||||
1
|
||||
} else if bases.len() < 32 {
|
||||
3
|
||||
} else {
|
||||
(f64::from(bases.len() as u32)).ln().ceil() as usize
|
||||
};
|
||||
|
||||
fn get_at<F: PrimeField>(segment: usize, c: usize, bytes: &F::Repr) -> usize {
|
||||
let skip_bits = segment * c;
|
||||
let skip_bytes = skip_bits / 8;
|
||||
|
||||
if skip_bytes >= 32 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let mut v = [0; 8];
|
||||
for (v, o) in v.iter_mut().zip(bytes.as_ref()[skip_bytes..].iter()) {
|
||||
*v = *o;
|
||||
}
|
||||
|
||||
let mut tmp = u64::from_le_bytes(v);
|
||||
tmp >>= skip_bits - (skip_bytes * 8);
|
||||
tmp %= 1 << c;
|
||||
|
||||
tmp as usize
|
||||
}
|
||||
|
||||
let segments = (256 / c) + 1;
|
||||
|
||||
for current_segment in (0..segments).rev() {
|
||||
for _ in 0..c {
|
||||
*acc = acc.double();
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Bucket<C: CurveAffine> {
|
||||
None,
|
||||
Affine(C),
|
||||
Projective(C::Curve),
|
||||
}
|
||||
|
||||
impl<C: CurveAffine> Bucket<C> {
|
||||
fn add_assign(&mut self, other: &C) {
|
||||
*self = match *self {
|
||||
Bucket::None => Bucket::Affine(*other),
|
||||
Bucket::Affine(a) => Bucket::Projective(a + *other),
|
||||
Bucket::Projective(mut a) => {
|
||||
a += *other;
|
||||
Bucket::Projective(a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add(self, mut other: C::Curve) -> C::Curve {
|
||||
match self {
|
||||
Bucket::None => other,
|
||||
Bucket::Affine(a) => {
|
||||
other += a;
|
||||
other
|
||||
}
|
||||
Bucket::Projective(a) => other + a,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut buckets: Vec<Bucket<C>> = vec![Bucket::None; (1 << c) - 1];
|
||||
|
||||
for (coeff, base) in coeffs.iter().zip(bases.iter()) {
|
||||
let coeff = get_at::<C::Scalar>(current_segment, c, coeff);
|
||||
if coeff != 0 {
|
||||
buckets[coeff - 1].add_assign(base);
|
||||
}
|
||||
}
|
||||
|
||||
// Summation by parts
|
||||
// e.g. 3a + 2b + 1c = a +
|
||||
// (a) + b +
|
||||
// ((a) + b) + c
|
||||
let mut running_sum = C::Curve::identity();
|
||||
for exp in buckets.into_iter().rev() {
|
||||
running_sum = exp.add(running_sum);
|
||||
*acc += &running_sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs a multi-exponentiation operation without GPU acceleration.
|
||||
///
|
||||
/// This function will panic if coeffs and bases have a different length.
|
||||
///
|
||||
/// This will use multithreading if beneficial.
|
||||
/// Adapted from zcash/halo2
|
||||
pub(crate) fn cpu_best_multiexp<C: CurveAffine>(coeffs: &[C::Scalar], bases: &[C]) -> C::Curve {
|
||||
assert_eq!(coeffs.len(), bases.len());
|
||||
|
||||
let num_threads = rayon::current_num_threads();
|
||||
if coeffs.len() > num_threads {
|
||||
let chunk = coeffs.len() / num_threads;
|
||||
let num_chunks = coeffs.chunks(chunk).len();
|
||||
let mut results = vec![C::Curve::identity(); num_chunks];
|
||||
rayon::scope(|scope| {
|
||||
for ((coeffs, bases), acc) in coeffs
|
||||
.chunks(chunk)
|
||||
.zip(bases.chunks(chunk))
|
||||
.zip(results.iter_mut())
|
||||
{
|
||||
scope.spawn(move |_| {
|
||||
cpu_multiexp_serial(coeffs, bases, acc);
|
||||
});
|
||||
}
|
||||
});
|
||||
results.iter().fold(C::Curve::identity(), |a, b| a + b)
|
||||
} else {
|
||||
let mut acc = C::Curve::identity();
|
||||
cpu_multiexp_serial(coeffs, bases, &mut acc);
|
||||
acc
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! This module implements the Nova traits for pallas::Point, pallas::Scalar, vesta::Point, vesta::Scalar.
|
||||
use crate::{
|
||||
provider::{
|
||||
cpu_best_multiexp,
|
||||
keccak::Keccak256Transcript,
|
||||
pedersen::CommitmentEngine,
|
||||
poseidon::{PoseidonRO, PoseidonROCircuit},
|
||||
@@ -222,131 +223,6 @@ impl_traits!(
|
||||
"40000000000000000000000000000000224698fc094cf91b992d30ed00000001"
|
||||
);
|
||||
|
||||
/// Native implementation of fast multiexp for platforms that do not support pasta_msm/semolina
|
||||
/// Adapted from zcash/halo2
|
||||
fn cpu_multiexp_serial<C: CurveAffine>(coeffs: &[C::Scalar], bases: &[C], acc: &mut C::Curve) {
|
||||
let coeffs: Vec<_> = coeffs.iter().map(|a| a.to_repr()).collect();
|
||||
|
||||
let c = if bases.len() < 4 {
|
||||
1
|
||||
} else if bases.len() < 32 {
|
||||
3
|
||||
} else {
|
||||
(f64::from(bases.len() as u32)).ln().ceil() as usize
|
||||
};
|
||||
|
||||
fn get_at<F: PrimeField>(segment: usize, c: usize, bytes: &F::Repr) -> usize {
|
||||
let skip_bits = segment * c;
|
||||
let skip_bytes = skip_bits / 8;
|
||||
|
||||
if skip_bytes >= 32 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let mut v = [0; 8];
|
||||
for (v, o) in v.iter_mut().zip(bytes.as_ref()[skip_bytes..].iter()) {
|
||||
*v = *o;
|
||||
}
|
||||
|
||||
let mut tmp = u64::from_le_bytes(v);
|
||||
tmp >>= skip_bits - (skip_bytes * 8);
|
||||
tmp %= 1 << c;
|
||||
|
||||
tmp as usize
|
||||
}
|
||||
|
||||
let segments = (256 / c) + 1;
|
||||
|
||||
for current_segment in (0..segments).rev() {
|
||||
for _ in 0..c {
|
||||
*acc = acc.double();
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Bucket<C: CurveAffine> {
|
||||
None,
|
||||
Affine(C),
|
||||
Projective(C::Curve),
|
||||
}
|
||||
|
||||
impl<C: CurveAffine> Bucket<C> {
|
||||
fn add_assign(&mut self, other: &C) {
|
||||
*self = match *self {
|
||||
Bucket::None => Bucket::Affine(*other),
|
||||
Bucket::Affine(a) => Bucket::Projective(a + *other),
|
||||
Bucket::Projective(mut a) => {
|
||||
a += *other;
|
||||
Bucket::Projective(a)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add(self, mut other: C::Curve) -> C::Curve {
|
||||
match self {
|
||||
Bucket::None => other,
|
||||
Bucket::Affine(a) => {
|
||||
other += a;
|
||||
other
|
||||
}
|
||||
Bucket::Projective(a) => other + a,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut buckets: Vec<Bucket<C>> = vec![Bucket::None; (1 << c) - 1];
|
||||
|
||||
for (coeff, base) in coeffs.iter().zip(bases.iter()) {
|
||||
let coeff = get_at::<C::Scalar>(current_segment, c, coeff);
|
||||
if coeff != 0 {
|
||||
buckets[coeff - 1].add_assign(base);
|
||||
}
|
||||
}
|
||||
|
||||
// Summation by parts
|
||||
// e.g. 3a + 2b + 1c = a +
|
||||
// (a) + b +
|
||||
// ((a) + b) + c
|
||||
let mut running_sum = C::Curve::identity();
|
||||
for exp in buckets.into_iter().rev() {
|
||||
running_sum = exp.add(running_sum);
|
||||
*acc += &running_sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs a multi-exponentiation operation without GPU acceleration.
|
||||
///
|
||||
/// This function will panic if coeffs and bases have a different length.
|
||||
///
|
||||
/// This will use multithreading if beneficial.
|
||||
/// Adapted from zcash/halo2
|
||||
pub(crate) fn cpu_best_multiexp<C: CurveAffine>(coeffs: &[C::Scalar], bases: &[C]) -> C::Curve {
|
||||
assert_eq!(coeffs.len(), bases.len());
|
||||
|
||||
let num_threads = rayon::current_num_threads();
|
||||
if coeffs.len() > num_threads {
|
||||
let chunk = coeffs.len() / num_threads;
|
||||
let num_chunks = coeffs.chunks(chunk).len();
|
||||
let mut results = vec![C::Curve::identity(); num_chunks];
|
||||
rayon::scope(|scope| {
|
||||
for ((coeffs, bases), acc) in coeffs
|
||||
.chunks(chunk)
|
||||
.zip(bases.chunks(chunk))
|
||||
.zip(results.iter_mut())
|
||||
{
|
||||
scope.spawn(move |_| {
|
||||
cpu_multiexp_serial(coeffs, bases, acc);
|
||||
});
|
||||
}
|
||||
});
|
||||
results.iter().fold(C::Curve::identity(), |a, b| a + b)
|
||||
} else {
|
||||
let mut acc = C::Curve::identity();
|
||||
cpu_multiexp_serial(coeffs, bases, &mut acc);
|
||||
acc
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user