* bn256+grumpkin from halo2curves * chore: Integrate halo2curves more extensively - Extend existing tests with additional test cases using the new curve types * fix: Assign correct orders to bn256 and grumpkin scalar fields - Swap scalar orders between grumpkin and bn256 in `impl_traits!` implementation * test: Finish improving test integration with halo2curves - Enhances test coverage for `pasta_curves` and `halo2curves` - Cleans up commented code in `test_ivc_nontrivial` and `test_ivc_nontrivial_with_compression` tests - Updates relevant test cases in `src/lib.rs` to include new curve tests * chore: Remove commented-out/uneeded code in bn254_grumpkin.rs * test: reproduce test_from_label for bn254_grumpkin - Implement the `from_label_serial` function in bn254_grumpkin provider - Add a test to compare parallel and serial implementations of `from_label` function * refactor: Clean up to_coordinate & summarize changes * refactor: rename bn254_grumpkin -> bn256_grumpkin * test: Expand testing for public params digest using bn256 and grumpkin * chore: Update halo2curves dependency in Cargo.toml - Updated the `halo2curves` dependency in `Cargo.toml` to the latest version `0.1.0` from a specific git branch. * refactor: Refactor multi-exponentiation methods across providers - Updated bn256_grumpkin.rs to use the cpu_best_multiexp function from pasta provider instead of its native function. - Modified visibility of cpu_best_multiexp function in pasta.rs from private to crate level. * chore: set up dependencies to import the correct getrandom feature on Wasm --------- Co-authored-by: Leo Alt <leo@ethereum.org>main
@ -0,0 +1,255 @@ |
|||||
|
//! This module implements the Nova traits for bn256::Point, bn256::Scalar, grumpkin::Point, grumpkin::Scalar.
|
||||
|
use crate::{
|
||||
|
provider::{
|
||||
|
keccak::Keccak256Transcript,
|
||||
|
pedersen::CommitmentEngine,
|
||||
|
poseidon::{PoseidonRO, PoseidonROCircuit},
|
||||
|
},
|
||||
|
traits::{CompressedGroup, Group, PrimeFieldExt, TranscriptReprTrait},
|
||||
|
};
|
||||
|
use digest::{ExtendableOutput, Input};
|
||||
|
use ff::{FromUniformBytes, PrimeField};
|
||||
|
use num_bigint::BigInt;
|
||||
|
use num_traits::Num;
|
||||
|
use pasta_curves::{
|
||||
|
self,
|
||||
|
arithmetic::{CurveAffine, CurveExt},
|
||||
|
group::{cofactor::CofactorCurveAffine, Curve, Group as AnotherGroup, GroupEncoding},
|
||||
|
};
|
||||
|
use rayon::prelude::*;
|
||||
|
use sha3::Shake256;
|
||||
|
use std::io::Read;
|
||||
|
|
||||
|
use halo2curves::bn256::{
|
||||
|
G1Affine as Bn256Affine, G1Compressed as Bn256Compressed, G1 as Bn256Point,
|
||||
|
};
|
||||
|
use halo2curves::grumpkin::{
|
||||
|
G1Affine as GrumpkinAffine, G1Compressed as GrumpkinCompressed, G1 as GrumpkinPoint,
|
||||
|
};
|
||||
|
|
||||
|
/// Re-exports that give access to the standard aliases used in the code base, for bn256
|
||||
|
pub mod bn256 {
|
||||
|
pub use halo2curves::bn256::{
|
||||
|
Fq as Base, Fr as Scalar, G1Affine as Affine, G1Compressed as Compressed, G1 as Point,
|
||||
|
};
|
||||
|
}
|
||||
|
|
||||
|
/// Re-exports that give access to the standard aliases used in the code base, for grumpkin
|
||||
|
pub mod grumpkin {
|
||||
|
pub use halo2curves::grumpkin::{
|
||||
|
Fq as Base, Fr as Scalar, G1Affine as Affine, G1Compressed as Compressed, G1 as Point,
|
||||
|
};
|
||||
|
}
|
||||
|
|
||||
|
// This implementation behaves in ways specific to the bn256/grumpkin curves in:
|
||||
|
// - to_coordinates,
|
||||
|
// - vartime_multiscalar_mul, where it does not call into accelerated implementations.
|
||||
|
macro_rules! impl_traits {
|
||||
|
(
|
||||
|
$name:ident,
|
||||
|
$name_compressed:ident,
|
||||
|
$name_curve:ident,
|
||||
|
$name_curve_affine:ident,
|
||||
|
$order_str:literal
|
||||
|
) => {
|
||||
|
impl Group for $name::Point {
|
||||
|
type Base = $name::Base;
|
||||
|
type Scalar = $name::Scalar;
|
||||
|
type CompressedGroupElement = $name_compressed;
|
||||
|
type PreprocessedGroupElement = $name::Affine;
|
||||
|
type RO = PoseidonRO<Self::Base, Self::Scalar>;
|
||||
|
type ROCircuit = PoseidonROCircuit<Self::Base>;
|
||||
|
type TE = Keccak256Transcript<Self>;
|
||||
|
type CE = CommitmentEngine<Self>;
|
||||
|
|
||||
|
fn vartime_multiscalar_mul(
|
||||
|
scalars: &[Self::Scalar],
|
||||
|
bases: &[Self::PreprocessedGroupElement],
|
||||
|
) -> Self {
|
||||
|
cpu_best_multiexp(scalars, bases)
|
||||
|
}
|
||||
|
|
||||
|
fn preprocessed(&self) -> Self::PreprocessedGroupElement {
|
||||
|
self.to_affine()
|
||||
|
}
|
||||
|
|
||||
|
fn compress(&self) -> Self::CompressedGroupElement {
|
||||
|
self.to_bytes()
|
||||
|
}
|
||||
|
|
||||
|
fn from_label(label: &'static [u8], n: usize) -> Vec<Self::PreprocessedGroupElement> {
|
||||
|
let mut shake = Shake256::default();
|
||||
|
shake.input(label);
|
||||
|
let mut reader = shake.xof_result();
|
||||
|
let mut uniform_bytes_vec = Vec::new();
|
||||
|
for _ in 0..n {
|
||||
|
let mut uniform_bytes = [0u8; 32];
|
||||
|
reader.read_exact(&mut uniform_bytes).unwrap();
|
||||
|
uniform_bytes_vec.push(uniform_bytes);
|
||||
|
}
|
||||
|
let gens_proj: Vec<$name_curve> = (0..n)
|
||||
|
.collect::<Vec<usize>>()
|
||||
|
.into_par_iter()
|
||||
|
.map(|i| {
|
||||
|
let hash = $name_curve::hash_to_curve("from_uniform_bytes");
|
||||
|
hash(&uniform_bytes_vec[i])
|
||||
|
})
|
||||
|
.collect();
|
||||
|
|
||||
|
let num_threads = rayon::current_num_threads();
|
||||
|
if gens_proj.len() > num_threads {
|
||||
|
let chunk = (gens_proj.len() as f64 / num_threads as f64).ceil() as usize;
|
||||
|
(0..num_threads)
|
||||
|
.collect::<Vec<usize>>()
|
||||
|
.into_par_iter()
|
||||
|
.map(|i| {
|
||||
|
let start = i * chunk;
|
||||
|
let end = if i == num_threads - 1 {
|
||||
|
gens_proj.len()
|
||||
|
} else {
|
||||
|
core::cmp::min((i + 1) * chunk, gens_proj.len())
|
||||
|
};
|
||||
|
if end > start {
|
||||
|
let mut gens = vec![$name_curve_affine::identity(); end - start];
|
||||
|
<Self as Curve>::batch_normalize(&gens_proj[start..end], &mut gens);
|
||||
|
gens
|
||||
|
} else {
|
||||
|
vec![]
|
||||
|
}
|
||||
|
})
|
||||
|
.collect::<Vec<Vec<$name_curve_affine>>>()
|
||||
|
.into_par_iter()
|
||||
|
.flatten()
|
||||
|
.collect()
|
||||
|
} else {
|
||||
|
let mut gens = vec![$name_curve_affine::identity(); n];
|
||||
|
<Self as Curve>::batch_normalize(&gens_proj, &mut gens);
|
||||
|
gens
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
fn to_coordinates(&self) -> (Self::Base, Self::Base, bool) {
|
||||
|
let coordinates = self.to_affine().coordinates();
|
||||
|
if coordinates.is_some().unwrap_u8() == 1
|
||||
|
// The bn256/grumpkin convention is to define and return the identity point's affine encoding (not None)
|
||||
|
&& (Self::PreprocessedGroupElement::identity() != self.to_affine())
|
||||
|
{
|
||||
|
(*coordinates.unwrap().x(), *coordinates.unwrap().y(), false)
|
||||
|
} else {
|
||||
|
(Self::Base::zero(), Self::Base::zero(), true)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
fn get_curve_params() -> (Self::Base, Self::Base, BigInt) {
|
||||
|
let A = $name::Point::a();
|
||||
|
let B = $name::Point::b();
|
||||
|
let order = BigInt::from_str_radix($order_str, 16).unwrap();
|
||||
|
|
||||
|
(A, B, order)
|
||||
|
}
|
||||
|
|
||||
|
fn zero() -> Self {
|
||||
|
$name::Point::identity()
|
||||
|
}
|
||||
|
|
||||
|
fn get_generator() -> Self {
|
||||
|
$name::Point::generator()
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl PrimeFieldExt for $name::Scalar {
|
||||
|
fn from_uniform(bytes: &[u8]) -> Self {
|
||||
|
let bytes_arr: [u8; 64] = bytes.try_into().unwrap();
|
||||
|
$name::Scalar::from_uniform_bytes(&bytes_arr)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<G: Group> TranscriptReprTrait<G> for $name_compressed {
|
||||
|
fn to_transcript_bytes(&self) -> Vec<u8> {
|
||||
|
self.as_ref().to_vec()
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl CompressedGroup for $name_compressed {
|
||||
|
type GroupElement = $name::Point;
|
||||
|
|
||||
|
fn decompress(&self) -> Option<$name::Point> {
|
||||
|
Some($name_curve::from_bytes(&self).unwrap())
|
||||
|
}
|
||||
|
}
|
||||
|
};
|
||||
|
}
|
||||
|
|
||||
|
impl<G: Group> TranscriptReprTrait<G> for grumpkin::Base {
|
||||
|
fn to_transcript_bytes(&self) -> Vec<u8> {
|
||||
|
self.to_repr().to_vec()
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<G: Group> TranscriptReprTrait<G> for grumpkin::Scalar {
|
||||
|
fn to_transcript_bytes(&self) -> Vec<u8> {
|
||||
|
self.to_repr().to_vec()
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl_traits!(
|
||||
|
bn256,
|
||||
|
Bn256Compressed,
|
||||
|
Bn256Point,
|
||||
|
Bn256Affine,
|
||||
|
"30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"
|
||||
|
);
|
||||
|
|
||||
|
impl_traits!(
|
||||
|
grumpkin,
|
||||
|
GrumpkinCompressed,
|
||||
|
GrumpkinPoint,
|
||||
|
GrumpkinAffine,
|
||||
|
"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::*;
|
||||
|
type G = bn256::Point;
|
||||
|
|
||||
|
fn from_label_serial(label: &'static [u8], n: usize) -> Vec<Bn256Affine> {
|
||||
|
let mut shake = Shake256::default();
|
||||
|
shake.input(label);
|
||||
|
let mut reader = shake.xof_result();
|
||||
|
let mut ck = Vec::new();
|
||||
|
for _ in 0..n {
|
||||
|
let mut uniform_bytes = [0u8; 32];
|
||||
|
reader.read_exact(&mut uniform_bytes).unwrap();
|
||||
|
let hash = bn256::Point::hash_to_curve("from_uniform_bytes");
|
||||
|
ck.push(hash(&uniform_bytes).to_affine());
|
||||
|
}
|
||||
|
ck
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn test_from_label() {
|
||||
|
let label = b"test_from_label";
|
||||
|
for n in [
|
||||
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 1021,
|
||||
|
] {
|
||||
|
let ck_par = <G as Group>::from_label(label, n);
|
||||
|
let ck_ser = from_label_serial(label, n);
|
||||
|
assert_eq!(ck_par.len(), n);
|
||||
|
assert_eq!(ck_ser.len(), n);
|
||||
|
assert_eq!(ck_par, ck_ser);
|
||||
|
}
|
||||
|
}
|
||||
|
}
|