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