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 core::ops::Mul;
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 Grp},
14 group::{Curve, GroupEncoding},
15 pallas, vesta, Ep, Eq,
16};
17use rand::SeedableRng;
18use rand_chacha::ChaCha20Rng;
19
20//////////////////////////////////////Pallas///////////////////////////////////////////////
21
22/// A wrapper for compressed group elements that come from the pallas curve
23#[derive(Clone, Copy, Debug, Eq, PartialEq)]
24pub struct PallasCompressedElementWrapper {
25 repr: [u8; 32],
26}
27
28impl PallasCompressedElementWrapper {
29 /// Wraps repr into the wrapper
30 pub fn new(repr: [u8; 32]) -> Self {
31 Self { repr }
32 }
33}
34
35impl Group for pallas::Point {
36 type Base = pallas::Base;
37 type Scalar = pallas::Scalar;
38 type CompressedGroupElement = PallasCompressedElementWrapper;
39 type PreprocessedGroupElement = pallas::Affine;
40 type HashFunc = PoseidonRO<Self::Base, Self::Scalar>;
41
42 fn vartime_multiscalar_mul(
43 scalars: &[Self::Scalar],
44 bases: &[Self::PreprocessedGroupElement],
45 ) -> Self {
46 // Unoptimized.
47 scalars
48 .iter()
49 .zip(bases)
50 .map(|(scalar, base)| base.mul(scalar))
51 .fold(Ep::group_zero(), |acc, x| acc + x)
52 }
53
54 fn compress(&self) -> Self::CompressedGroupElement {
55 PallasCompressedElementWrapper::new(self.to_bytes())
56 }
57
58 fn from_uniform_bytes(bytes: &[u8]) -> Option<Self::PreprocessedGroupElement> {
59 if bytes.len() != 64 {
60 None
61 } else {
62 let mut arr = [0; 32];
63 arr.copy_from_slice(&bytes[0..32]);
64
65 let hash = Ep::hash_to_curve("from_uniform_bytes");
66 Some(hash(&arr).to_affine())
67 }
68 }
69
70 fn to_coordinates(&self) -> (Self::Base, Self::Base, bool) {
71 let coordinates = self.to_affine().coordinates();
72 if coordinates.is_some().unwrap_u8() == 1 {
73 (*coordinates.unwrap().x(), *coordinates.unwrap().y(), false)
74 } else {
75 (Self::Base::zero(), Self::Base::zero(), true)
76 }
77 }
78
79 fn get_order() -> BigInt {
80 BigInt::from_str_radix(
81 "40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001",
82 16,
83 )
84 .unwrap()
85 }
86}
87
88impl ChallengeTrait for pallas::Scalar {
89 fn challenge(label: &'static [u8], transcript: &mut Transcript) -> Self {
90 let mut key: <ChaCha20Rng as SeedableRng>::Seed = Default::default();
91 transcript.challenge_bytes(label, &mut key);
92 let mut rng = ChaCha20Rng::from_seed(key);
93 pallas::Scalar::random(&mut rng)
94 }
95}
96
97impl CompressedGroup for PallasCompressedElementWrapper {
98 type GroupElement = pallas::Point;
99
100 fn decompress(&self) -> Option<pallas::Point> {
101 Some(Ep::from_bytes(&self.repr).unwrap())
102 }
103 fn as_bytes(&self) -> &[u8] {
104 &self.repr
105 }
106}
107
108//////////////////////////////////////Vesta////////////////////////////////////////////////
109
110/// A wrapper for compressed group elements that come from the vesta curve
111#[derive(Clone, Copy, Debug, Eq, PartialEq)]
112pub struct VestaCompressedElementWrapper {
113 repr: [u8; 32],
114}
115
116impl VestaCompressedElementWrapper {
117 /// Wraps repr into the wrapper
118 pub fn new(repr: [u8; 32]) -> Self {
119 Self { repr }
120 }
121}
122
123impl Group for vesta::Point {
124 type Base = vesta::Base;
125 type Scalar = vesta::Scalar;
126 type CompressedGroupElement = VestaCompressedElementWrapper;
127 type PreprocessedGroupElement = vesta::Affine;
128 type HashFunc = PoseidonRO<Self::Base, Self::Scalar>;
129
130 fn vartime_multiscalar_mul(
131 scalars: &[Self::Scalar],
132 bases: &[Self::PreprocessedGroupElement],
133 ) -> Self {
134 // Unoptimized.
135 scalars
136 .iter()
137 .zip(bases)
138 .map(|(scalar, base)| base.mul(scalar))
139 .fold(Eq::group_zero(), |acc, x| acc + x)
140 }
141
142 fn compress(&self) -> Self::CompressedGroupElement {
143 VestaCompressedElementWrapper::new(self.to_bytes())
144 }
145
146 fn from_uniform_bytes(bytes: &[u8]) -> Option<Self::PreprocessedGroupElement> {
147 if bytes.len() != 64 {
148 None
149 } else {
150 let mut arr = [0; 32];
151 arr.copy_from_slice(&bytes[0..32]);
152
153 let hash = Eq::hash_to_curve("from_uniform_bytes");
154 Some(hash(&arr).to_affine())
155 }
156 }
157
158 fn to_coordinates(&self) -> (Self::Base, Self::Base, bool) {
159 let coordinates = self.to_affine().coordinates();
160 if coordinates.is_some().unwrap_u8() == 1 {
161 (*coordinates.unwrap().x(), *coordinates.unwrap().y(), false)
162 } else {
163 (Self::Base::zero(), Self::Base::zero(), true)
164 }
165 }
166
167 fn get_order() -> BigInt {
168 BigInt::from_str_radix(
169 "40000000000000000000000000000000224698fc094cf91b992d30ed00000001",
170 16,
171 )
172 .unwrap()
173 }
174}
175
176impl ChallengeTrait for vesta::Scalar {
177 fn challenge(label: &'static [u8], transcript: &mut Transcript) -> Self {
178 let mut key: <ChaCha20Rng as SeedableRng>::Seed = Default::default();
179 transcript.challenge_bytes(label, &mut key);
180 let mut rng = ChaCha20Rng::from_seed(key);
181 vesta::Scalar::random(&mut rng)
182 }
183}
184
185impl CompressedGroup for VestaCompressedElementWrapper {
186 type GroupElement = vesta::Point;
187
188 fn decompress(&self) -> Option<vesta::Point> {
189 Some(Eq::from_bytes(&self.repr).unwrap())
190 }
191 fn as_bytes(&self) -> &[u8] {
192 &self.repr
193 }
194}