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::{PoseidonRO, PoseidonROCircuit},
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, Group as AnotherGroup, 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 RO = PoseidonRO<Self::Base, Self::Scalar>;
44 type ROCircuit = PoseidonROCircuit<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_curve_params() -> (Self::Base, Self::Base, BigInt) {
93 let A = Self::Base::zero();
94 let B = Self::Base::from(5);
95 let order = BigInt::from_str_radix(
96 "40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001",
97 16,
98 )
99 .unwrap();
100
101 (A, B, order)
102 }
103
104 fn zero() -> Self {
105 pallas::Point::group_zero()
106 }
107
108 fn get_generator() -> Self {
109 pallas::Point::generator()
110 }
111}
112
113impl ChallengeTrait for pallas::Scalar {
114 fn challenge(label: &'static [u8], transcript: &mut Transcript) -> Self {
115 let mut key: <ChaCha20Rng as SeedableRng>::Seed = Default::default();
116 transcript.challenge_bytes(label, &mut key);
117 let mut rng = ChaCha20Rng::from_seed(key);
118 pallas::Scalar::random(&mut rng)
119 }
120}
121
122impl CompressedGroup for PallasCompressedElementWrapper {
123 type GroupElement = pallas::Point;
124
125 fn decompress(&self) -> Option<pallas::Point> {
126 Some(Ep::from_bytes(&self.repr).unwrap())
127 }
128 fn as_bytes(&self) -> &[u8] {
129 &self.repr
130 }
131}
132
133//////////////////////////////////////Vesta////////////////////////////////////////////////
134
135/// A wrapper for compressed group elements that come from the vesta curve
136#[derive(Clone, Copy, Debug, Eq, PartialEq)]
137pub struct VestaCompressedElementWrapper {
138 repr: [u8; 32],
139}
140
141impl VestaCompressedElementWrapper {
142 /// Wraps repr into the wrapper
143 pub fn new(repr: [u8; 32]) -> Self {
144 Self { repr }
145 }
146}
147
148impl Group for vesta::Point {
149 type Base = vesta::Base;
150 type Scalar = vesta::Scalar;
151 type CompressedGroupElement = VestaCompressedElementWrapper;
152 type PreprocessedGroupElement = vesta::Affine;
153 type RO = PoseidonRO<Self::Base, Self::Scalar>;
154 type ROCircuit = PoseidonROCircuit<Self::Base>;
155
156 fn vartime_multiscalar_mul(
157 scalars: &[Self::Scalar],
158 bases: &[Self::PreprocessedGroupElement],
159 ) -> Self {
160 if scalars.len() >= 128 {
161 pasta_msm::vesta(bases, scalars)
162 } else {
163 scalars
164 .par_iter()
165 .zip(bases)
166 .map(|(scalar, base)| base.mul(scalar))
167 .reduce(Eq::group_zero, |x, y| x + y)
168 }
169 }
170
171 fn compress(&self) -> Self::CompressedGroupElement {
172 VestaCompressedElementWrapper::new(self.to_bytes())
173 }
174
175 fn preprocessed(&self) -> Self::PreprocessedGroupElement {
176 self.to_affine()
177 }
178
179 fn from_label(label: &'static [u8], n: usize) -> Vec<Self::PreprocessedGroupElement> {
180 let mut shake = Shake256::default();
181 shake.input(label);
182 let mut reader = shake.xof_result();
183 let mut gens: Vec<Self::PreprocessedGroupElement> = Vec::new();
184 let mut uniform_bytes = [0u8; 32];
185 for _ in 0..n {
186 reader.read_exact(&mut uniform_bytes).unwrap();
187 let hash = Eq::hash_to_curve("from_uniform_bytes");
188 gens.push(hash(&uniform_bytes).to_affine());
189 }
190 gens
191 }
192
193 fn to_coordinates(&self) -> (Self::Base, Self::Base, bool) {
194 let coordinates = self.to_affine().coordinates();
195 if coordinates.is_some().unwrap_u8() == 1 {
196 (*coordinates.unwrap().x(), *coordinates.unwrap().y(), false)
197 } else {
198 (Self::Base::zero(), Self::Base::zero(), true)
199 }
200 }
201
202 fn get_curve_params() -> (Self::Base, Self::Base, BigInt) {
203 let A = Self::Base::zero();
204 let B = Self::Base::from(5);
205 let order = BigInt::from_str_radix(
206 "40000000000000000000000000000000224698fc094cf91b992d30ed00000001",
207 16,
208 )
209 .unwrap();
210
211 (A, B, order)
212 }
213
214 fn zero() -> Self {
215 vesta::Point::group_zero()
216 }
217
218 fn get_generator() -> Self {
219 vesta::Point::generator()
220 }
221}
222
223impl ChallengeTrait for vesta::Scalar {
224 fn challenge(label: &'static [u8], transcript: &mut Transcript) -> Self {
225 let mut key: <ChaCha20Rng as SeedableRng>::Seed = Default::default();
226 transcript.challenge_bytes(label, &mut key);
227 let mut rng = ChaCha20Rng::from_seed(key);
228 vesta::Scalar::random(&mut rng)
229 }
230}
231
232impl CompressedGroup for VestaCompressedElementWrapper {
233 type GroupElement = vesta::Point;
234
235 fn decompress(&self) -> Option<vesta::Point> {
236 Some(Eq::from_bytes(&self.repr).unwrap())
237 }
238 fn as_bytes(&self) -> &[u8] {
239 &self.repr
240 }
241}