arnaucube
arnaucube/NovaPublic Self-hosted repository
Nova/src/pasta.rs
src/pasta.rsRaw
  1//! This module implements the Nova traits for pallas::Point, pallas::Scalar, vesta::Point, vesta::Scalar.
  2use crate::{
  3  poseidon::{PoseidonHashFunc, PoseidonHashFuncCircuit},
  4  traits::{ChallengeTrait, CompressedGroup, Group},
  5};
  6use digest::{ExtendableOutput, Input};
  7use ff::Field;
  8use merlin::Transcript;
  9use num_bigint::BigInt;
 10use num_traits::Num;
 11use pasta_curves::{
 12  self,
 13  arithmetic::{CurveAffine, CurveExt, Group as OtherGroup},
 14  group::{Curve, GroupEncoding},
 15  pallas, vesta, Ep, Eq,
 16};
 17use rand::SeedableRng;
 18use rand_chacha::ChaCha20Rng;
 19use rayon::prelude::*;
 20use sha3::Shake256;
 21use std::{io::Read, ops::Mul};
 22
 23//////////////////////////////////////Pallas///////////////////////////////////////////////
 24
 25/// A wrapper for compressed group elements that come from the pallas curve
 26#[derive(Clone, Copy, Debug, Eq, PartialEq)]
 27pub struct PallasCompressedElementWrapper {
 28  repr: [u8; 32],
 29}
 30
 31impl PallasCompressedElementWrapper {
 32  /// Wraps repr into the wrapper
 33  pub fn new(repr: [u8; 32]) -> Self {
 34    Self { repr }
 35  }
 36}
 37
 38impl Group for pallas::Point {
 39  type Base = pallas::Base;
 40  type Scalar = pallas::Scalar;
 41  type CompressedGroupElement = PallasCompressedElementWrapper;
 42  type PreprocessedGroupElement = pallas::Affine;
 43  type HashFunc = PoseidonHashFunc<Self::Base, Self::Scalar>;
 44  type HashFuncCircuit = PoseidonHashFuncCircuit<Self::Base>;
 45
 46  fn vartime_multiscalar_mul(
 47    scalars: &[Self::Scalar],
 48    bases: &[Self::PreprocessedGroupElement],
 49  ) -> Self {
 50    if scalars.len() >= 128 {
 51      pasta_msm::pallas(bases, scalars)
 52    } else {
 53      scalars
 54        .par_iter()
 55        .zip(bases)
 56        .map(|(scalar, base)| base.mul(scalar))
 57        .reduce(Ep::group_zero, |x, y| x + y)
 58    }
 59  }
 60
 61  fn preprocessed(&self) -> Self::PreprocessedGroupElement {
 62    self.to_affine()
 63  }
 64
 65  fn compress(&self) -> Self::CompressedGroupElement {
 66    PallasCompressedElementWrapper::new(self.to_bytes())
 67  }
 68
 69  fn from_label(label: &'static [u8], n: usize) -> Vec<Self::PreprocessedGroupElement> {
 70    let mut shake = Shake256::default();
 71    shake.input(label);
 72    let mut reader = shake.xof_result();
 73    let mut gens: Vec<Self::PreprocessedGroupElement> = Vec::new();
 74    let mut uniform_bytes = [0u8; 32];
 75    for _ in 0..n {
 76      reader.read_exact(&mut uniform_bytes).unwrap();
 77      let hash = Ep::hash_to_curve("from_uniform_bytes");
 78      gens.push(hash(&uniform_bytes).to_affine());
 79    }
 80    gens
 81  }
 82
 83  fn to_coordinates(&self) -> (Self::Base, Self::Base, bool) {
 84    let coordinates = self.to_affine().coordinates();
 85    if coordinates.is_some().unwrap_u8() == 1 {
 86      (*coordinates.unwrap().x(), *coordinates.unwrap().y(), false)
 87    } else {
 88      (Self::Base::zero(), Self::Base::zero(), true)
 89    }
 90  }
 91
 92  fn get_order() -> BigInt {
 93    BigInt::from_str_radix(
 94      "40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001",
 95      16,
 96    )
 97    .unwrap()
 98  }
 99}
100
101impl ChallengeTrait for pallas::Scalar {
102  fn challenge(label: &'static [u8], transcript: &mut Transcript) -> Self {
103    let mut key: <ChaCha20Rng as SeedableRng>::Seed = Default::default();
104    transcript.challenge_bytes(label, &mut key);
105    let mut rng = ChaCha20Rng::from_seed(key);
106    pallas::Scalar::random(&mut rng)
107  }
108}
109
110impl CompressedGroup for PallasCompressedElementWrapper {
111  type GroupElement = pallas::Point;
112
113  fn decompress(&self) -> Option<pallas::Point> {
114    Some(Ep::from_bytes(&self.repr).unwrap())
115  }
116  fn as_bytes(&self) -> &[u8] {
117    &self.repr
118  }
119}
120
121//////////////////////////////////////Vesta////////////////////////////////////////////////
122
123/// A wrapper for compressed group elements that come from the vesta curve
124#[derive(Clone, Copy, Debug, Eq, PartialEq)]
125pub struct VestaCompressedElementWrapper {
126  repr: [u8; 32],
127}
128
129impl VestaCompressedElementWrapper {
130  /// Wraps repr into the wrapper
131  pub fn new(repr: [u8; 32]) -> Self {
132    Self { repr }
133  }
134}
135
136impl Group for vesta::Point {
137  type Base = vesta::Base;
138  type Scalar = vesta::Scalar;
139  type CompressedGroupElement = VestaCompressedElementWrapper;
140  type PreprocessedGroupElement = vesta::Affine;
141  type HashFunc = PoseidonHashFunc<Self::Base, Self::Scalar>;
142  type HashFuncCircuit = PoseidonHashFuncCircuit<Self::Base>;
143
144  fn vartime_multiscalar_mul(
145    scalars: &[Self::Scalar],
146    bases: &[Self::PreprocessedGroupElement],
147  ) -> Self {
148    if scalars.len() >= 128 {
149      pasta_msm::vesta(bases, scalars)
150    } else {
151      scalars
152        .par_iter()
153        .zip(bases)
154        .map(|(scalar, base)| base.mul(scalar))
155        .reduce(Eq::group_zero, |x, y| x + y)
156    }
157  }
158
159  fn compress(&self) -> Self::CompressedGroupElement {
160    VestaCompressedElementWrapper::new(self.to_bytes())
161  }
162
163  fn preprocessed(&self) -> Self::PreprocessedGroupElement {
164    self.to_affine()
165  }
166
167  fn from_label(label: &'static [u8], n: usize) -> Vec<Self::PreprocessedGroupElement> {
168    let mut shake = Shake256::default();
169    shake.input(label);
170    let mut reader = shake.xof_result();
171    let mut gens: Vec<Self::PreprocessedGroupElement> = Vec::new();
172    let mut uniform_bytes = [0u8; 32];
173    for _ in 0..n {
174      reader.read_exact(&mut uniform_bytes).unwrap();
175      let hash = Eq::hash_to_curve("from_uniform_bytes");
176      gens.push(hash(&uniform_bytes).to_affine());
177    }
178    gens
179  }
180
181  fn to_coordinates(&self) -> (Self::Base, Self::Base, bool) {
182    let coordinates = self.to_affine().coordinates();
183    if coordinates.is_some().unwrap_u8() == 1 {
184      (*coordinates.unwrap().x(), *coordinates.unwrap().y(), false)
185    } else {
186      (Self::Base::zero(), Self::Base::zero(), true)
187    }
188  }
189
190  fn get_order() -> BigInt {
191    BigInt::from_str_radix(
192      "40000000000000000000000000000000224698fc094cf91b992d30ed00000001",
193      16,
194    )
195    .unwrap()
196  }
197}
198
199impl ChallengeTrait for vesta::Scalar {
200  fn challenge(label: &'static [u8], transcript: &mut Transcript) -> Self {
201    let mut key: <ChaCha20Rng as SeedableRng>::Seed = Default::default();
202    transcript.challenge_bytes(label, &mut key);
203    let mut rng = ChaCha20Rng::from_seed(key);
204    vesta::Scalar::random(&mut rng)
205  }
206}
207
208impl CompressedGroup for VestaCompressedElementWrapper {
209  type GroupElement = vesta::Point;
210
211  fn decompress(&self) -> Option<vesta::Point> {
212    Some(Eq::from_bytes(&self.repr).unwrap())
213  }
214  fn as_bytes(&self) -> &[u8] {
215    &self.repr
216  }
217}