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