arnaucube
arnaucube/NovaPublic Self-hosted repository
Nova/src/provider/bn256_grumpkin.rs
src/provider/bn256_grumpkin.rsRaw
  1//! This module implements the Nova traits for bn256::Point, bn256::Scalar, grumpkin::Point, grumpkin::Scalar.
  2use crate::{
  3  provider::{
  4    cpu_best_multiexp,
  5    keccak::Keccak256Transcript,
  6    pedersen::CommitmentEngine,
  7    poseidon::{PoseidonRO, PoseidonROCircuit},
  8  },
  9  traits::{CompressedGroup, Group, PrimeFieldExt, TranscriptReprTrait},
 10};
 11use digest::{ExtendableOutput, Input};
 12use ff::{FromUniformBytes, PrimeField};
 13use num_bigint::BigInt;
 14use num_traits::Num;
 15use pasta_curves::{
 16  self,
 17  arithmetic::{CurveAffine, CurveExt},
 18  group::{cofactor::CofactorCurveAffine, Curve, Group as AnotherGroup, GroupEncoding},
 19};
 20use rayon::prelude::*;
 21use sha3::Shake256;
 22use std::io::Read;
 23
 24use halo2curves::bn256::{
 25  G1Affine as Bn256Affine, G1Compressed as Bn256Compressed, G1 as Bn256Point,
 26};
 27use halo2curves::grumpkin::{
 28  G1Affine as GrumpkinAffine, G1Compressed as GrumpkinCompressed, G1 as GrumpkinPoint,
 29};
 30
 31/// Re-exports that give access to the standard aliases used in the code base, for bn256
 32pub mod bn256 {
 33  pub use halo2curves::bn256::{
 34    Fq as Base, Fr as Scalar, G1Affine as Affine, G1Compressed as Compressed, G1 as Point,
 35  };
 36}
 37
 38/// Re-exports that give access to the standard aliases used in the code base, for grumpkin
 39pub mod grumpkin {
 40  pub use halo2curves::grumpkin::{
 41    Fq as Base, Fr as Scalar, G1Affine as Affine, G1Compressed as Compressed, G1 as Point,
 42  };
 43}
 44
 45// This implementation behaves in ways specific to the bn256/grumpkin curves in:
 46// - to_coordinates,
 47// - vartime_multiscalar_mul, where it does not call into accelerated implementations.
 48macro_rules! impl_traits {
 49  (
 50    $name:ident,
 51    $name_compressed:ident,
 52    $name_curve:ident,
 53    $name_curve_affine:ident,
 54    $order_str:literal
 55  ) => {
 56    impl Group for $name::Point {
 57      type Base = $name::Base;
 58      type Scalar = $name::Scalar;
 59      type CompressedGroupElement = $name_compressed;
 60      type PreprocessedGroupElement = $name::Affine;
 61      type RO = PoseidonRO<Self::Base, Self::Scalar>;
 62      type ROCircuit = PoseidonROCircuit<Self::Base>;
 63      type TE = Keccak256Transcript<Self>;
 64      type CE = CommitmentEngine<Self>;
 65
 66      fn vartime_multiscalar_mul(
 67        scalars: &[Self::Scalar],
 68        bases: &[Self::PreprocessedGroupElement],
 69      ) -> Self {
 70        cpu_best_multiexp(scalars, bases)
 71      }
 72
 73      fn preprocessed(&self) -> Self::PreprocessedGroupElement {
 74        self.to_affine()
 75      }
 76
 77      fn compress(&self) -> Self::CompressedGroupElement {
 78        self.to_bytes()
 79      }
 80
 81      fn from_label(label: &'static [u8], n: usize) -> Vec<Self::PreprocessedGroupElement> {
 82        let mut shake = Shake256::default();
 83        shake.input(label);
 84        let mut reader = shake.xof_result();
 85        let mut uniform_bytes_vec = Vec::new();
 86        for _ in 0..n {
 87          let mut uniform_bytes = [0u8; 32];
 88          reader.read_exact(&mut uniform_bytes).unwrap();
 89          uniform_bytes_vec.push(uniform_bytes);
 90        }
 91        let gens_proj: Vec<$name_curve> = (0..n)
 92          .collect::<Vec<usize>>()
 93          .into_par_iter()
 94          .map(|i| {
 95            let hash = $name_curve::hash_to_curve("from_uniform_bytes");
 96            hash(&uniform_bytes_vec[i])
 97          })
 98          .collect();
 99
100        let num_threads = rayon::current_num_threads();
101        if gens_proj.len() > num_threads {
102          let chunk = (gens_proj.len() as f64 / num_threads as f64).ceil() as usize;
103          (0..num_threads)
104            .collect::<Vec<usize>>()
105            .into_par_iter()
106            .map(|i| {
107              let start = i * chunk;
108              let end = if i == num_threads - 1 {
109                gens_proj.len()
110              } else {
111                core::cmp::min((i + 1) * chunk, gens_proj.len())
112              };
113              if end > start {
114                let mut gens = vec![$name_curve_affine::identity(); end - start];
115                <Self as Curve>::batch_normalize(&gens_proj[start..end], &mut gens);
116                gens
117              } else {
118                vec![]
119              }
120            })
121            .collect::<Vec<Vec<$name_curve_affine>>>()
122            .into_par_iter()
123            .flatten()
124            .collect()
125        } else {
126          let mut gens = vec![$name_curve_affine::identity(); n];
127          <Self as Curve>::batch_normalize(&gens_proj, &mut gens);
128          gens
129        }
130      }
131
132      fn to_coordinates(&self) -> (Self::Base, Self::Base, bool) {
133        let coordinates = self.to_affine().coordinates();
134        if coordinates.is_some().unwrap_u8() == 1
135          // The bn256/grumpkin convention is to define and return the identity point's affine encoding (not None)
136          && (Self::PreprocessedGroupElement::identity() != self.to_affine())
137        {
138          (*coordinates.unwrap().x(), *coordinates.unwrap().y(), false)
139        } else {
140          (Self::Base::zero(), Self::Base::zero(), true)
141        }
142      }
143
144      fn get_curve_params() -> (Self::Base, Self::Base, BigInt) {
145        let A = $name::Point::a();
146        let B = $name::Point::b();
147        let order = BigInt::from_str_radix($order_str, 16).unwrap();
148
149        (A, B, order)
150      }
151
152      fn zero() -> Self {
153        $name::Point::identity()
154      }
155
156      fn get_generator() -> Self {
157        $name::Point::generator()
158      }
159    }
160
161    impl PrimeFieldExt for $name::Scalar {
162      fn from_uniform(bytes: &[u8]) -> Self {
163        let bytes_arr: [u8; 64] = bytes.try_into().unwrap();
164        $name::Scalar::from_uniform_bytes(&bytes_arr)
165      }
166    }
167
168    impl<G: Group> TranscriptReprTrait<G> for $name_compressed {
169      fn to_transcript_bytes(&self) -> Vec<u8> {
170        self.as_ref().to_vec()
171      }
172    }
173
174    impl CompressedGroup for $name_compressed {
175      type GroupElement = $name::Point;
176
177      fn decompress(&self) -> Option<$name::Point> {
178        Some($name_curve::from_bytes(&self).unwrap())
179      }
180    }
181  };
182}
183
184impl<G: Group> TranscriptReprTrait<G> for grumpkin::Base {
185  fn to_transcript_bytes(&self) -> Vec<u8> {
186    self.to_repr().to_vec()
187  }
188}
189
190impl<G: Group> TranscriptReprTrait<G> for grumpkin::Scalar {
191  fn to_transcript_bytes(&self) -> Vec<u8> {
192    self.to_repr().to_vec()
193  }
194}
195
196impl_traits!(
197  bn256,
198  Bn256Compressed,
199  Bn256Point,
200  Bn256Affine,
201  "30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"
202);
203
204impl_traits!(
205  grumpkin,
206  GrumpkinCompressed,
207  GrumpkinPoint,
208  GrumpkinAffine,
209  "30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"
210);
211
212#[cfg(test)]
213mod tests {
214  use super::*;
215  type G = bn256::Point;
216
217  fn from_label_serial(label: &'static [u8], n: usize) -> Vec<Bn256Affine> {
218    let mut shake = Shake256::default();
219    shake.input(label);
220    let mut reader = shake.xof_result();
221    let mut ck = Vec::new();
222    for _ in 0..n {
223      let mut uniform_bytes = [0u8; 32];
224      reader.read_exact(&mut uniform_bytes).unwrap();
225      let hash = bn256::Point::hash_to_curve("from_uniform_bytes");
226      ck.push(hash(&uniform_bytes).to_affine());
227    }
228    ck
229  }
230
231  #[test]
232  fn test_from_label() {
233    let label = b"test_from_label";
234    for n in [
235      1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 1021,
236    ] {
237      let ck_par = <G as Group>::from_label(label, n);
238      let ck_ser = from_label_serial(label, n);
239      assert_eq!(ck_par.len(), n);
240      assert_eq!(ck_ser.len(), n);
241      assert_eq!(ck_par, ck_ser);
242    }
243  }
244}