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, 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
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 RO = PoseidonRO<Self::Base, Self::Scalar>;
142 type ROCircuit = PoseidonROCircuit<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}