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,
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 = PoseidonRO<Self::Base, Self::Scalar>;
44
45 fn vartime_multiscalar_mul(
46 scalars: &[Self::Scalar],
47 bases: &[Self::PreprocessedGroupElement],
48 ) -> Self {
49 if scalars.len() >= 128 {
50 pasta_msm::pallas(bases, scalars)
51 } else {
52 scalars
53 .par_iter()
54 .zip(bases)
55 .map(|(scalar, base)| base.mul(scalar))
56 .reduce(Ep::group_zero, |x, y| x + y)
57 }
58 }
59
60 fn preprocessed(&self) -> Self::PreprocessedGroupElement {
61 self.to_affine()
62 }
63
64 fn compress(&self) -> Self::CompressedGroupElement {
65 PallasCompressedElementWrapper::new(self.to_bytes())
66 }
67
68 fn from_label(label: &'static [u8], n: usize) -> Vec<Self::PreprocessedGroupElement> {
69 let mut shake = Shake256::default();
70 shake.input(label);
71 let mut reader = shake.xof_result();
72 let mut gens: Vec<Self::PreprocessedGroupElement> = Vec::new();
73 let mut uniform_bytes = [0u8; 32];
74 for _ in 0..n {
75 reader.read_exact(&mut uniform_bytes).unwrap();
76 let hash = Ep::hash_to_curve("from_uniform_bytes");
77 gens.push(hash(&uniform_bytes).to_affine());
78 }
79 gens
80 }
81
82 fn to_coordinates(&self) -> (Self::Base, Self::Base, bool) {
83 let coordinates = self.to_affine().coordinates();
84 if coordinates.is_some().unwrap_u8() == 1 {
85 (*coordinates.unwrap().x(), *coordinates.unwrap().y(), false)
86 } else {
87 (Self::Base::zero(), Self::Base::zero(), true)
88 }
89 }
90
91 fn get_order() -> BigInt {
92 BigInt::from_str_radix(
93 "40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001",
94 16,
95 )
96 .unwrap()
97 }
98}
99
100impl ChallengeTrait for pallas::Scalar {
101 fn challenge(label: &'static [u8], transcript: &mut Transcript) -> Self {
102 let mut key: <ChaCha20Rng as SeedableRng>::Seed = Default::default();
103 transcript.challenge_bytes(label, &mut key);
104 let mut rng = ChaCha20Rng::from_seed(key);
105 pallas::Scalar::random(&mut rng)
106 }
107}
108
109impl CompressedGroup for PallasCompressedElementWrapper {
110 type GroupElement = pallas::Point;
111
112 fn decompress(&self) -> Option<pallas::Point> {
113 Some(Ep::from_bytes(&self.repr).unwrap())
114 }
115 fn as_bytes(&self) -> &[u8] {
116 &self.repr
117 }
118}
119
120//////////////////////////////////////Vesta////////////////////////////////////////////////
121
122/// A wrapper for compressed group elements that come from the vesta curve
123#[derive(Clone, Copy, Debug, Eq, PartialEq)]
124pub struct VestaCompressedElementWrapper {
125 repr: [u8; 32],
126}
127
128impl VestaCompressedElementWrapper {
129 /// Wraps repr into the wrapper
130 pub fn new(repr: [u8; 32]) -> Self {
131 Self { repr }
132 }
133}
134
135impl Group for vesta::Point {
136 type Base = vesta::Base;
137 type Scalar = vesta::Scalar;
138 type CompressedGroupElement = VestaCompressedElementWrapper;
139 type PreprocessedGroupElement = vesta::Affine;
140 type HashFunc = PoseidonRO<Self::Base, Self::Scalar>;
141
142 fn vartime_multiscalar_mul(
143 scalars: &[Self::Scalar],
144 bases: &[Self::PreprocessedGroupElement],
145 ) -> Self {
146 if scalars.len() >= 128 {
147 pasta_msm::vesta(bases, scalars)
148 } else {
149 scalars
150 .par_iter()
151 .zip(bases)
152 .map(|(scalar, base)| base.mul(scalar))
153 .reduce(Eq::group_zero, |x, y| x + y)
154 }
155 }
156
157 fn compress(&self) -> Self::CompressedGroupElement {
158 VestaCompressedElementWrapper::new(self.to_bytes())
159 }
160
161 fn preprocessed(&self) -> Self::PreprocessedGroupElement {
162 self.to_affine()
163 }
164
165 fn from_label(label: &'static [u8], n: usize) -> Vec<Self::PreprocessedGroupElement> {
166 let mut shake = Shake256::default();
167 shake.input(label);
168 let mut reader = shake.xof_result();
169 let mut gens: Vec<Self::PreprocessedGroupElement> = Vec::new();
170 let mut uniform_bytes = [0u8; 32];
171 for _ in 0..n {
172 reader.read_exact(&mut uniform_bytes).unwrap();
173 let hash = Eq::hash_to_curve("from_uniform_bytes");
174 gens.push(hash(&uniform_bytes).to_affine());
175 }
176 gens
177 }
178
179 fn to_coordinates(&self) -> (Self::Base, Self::Base, bool) {
180 let coordinates = self.to_affine().coordinates();
181 if coordinates.is_some().unwrap_u8() == 1 {
182 (*coordinates.unwrap().x(), *coordinates.unwrap().y(), false)
183 } else {
184 (Self::Base::zero(), Self::Base::zero(), true)
185 }
186 }
187
188 fn get_order() -> BigInt {
189 BigInt::from_str_radix(
190 "40000000000000000000000000000000224698fc094cf91b992d30ed00000001",
191 16,
192 )
193 .unwrap()
194 }
195}
196
197impl ChallengeTrait for vesta::Scalar {
198 fn challenge(label: &'static [u8], transcript: &mut Transcript) -> Self {
199 let mut key: <ChaCha20Rng as SeedableRng>::Seed = Default::default();
200 transcript.challenge_bytes(label, &mut key);
201 let mut rng = ChaCha20Rng::from_seed(key);
202 vesta::Scalar::random(&mut rng)
203 }
204}
205
206impl CompressedGroup for VestaCompressedElementWrapper {
207 type GroupElement = vesta::Point;
208
209 fn decompress(&self) -> Option<vesta::Point> {
210 Some(Eq::from_bytes(&self.repr).unwrap())
211 }
212 fn as_bytes(&self) -> &[u8] {
213 &self.repr
214 }
215}