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