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},
14 group::{Curve, GroupEncoding},
15 pallas, vesta, Ep, Eq,
16};
17use rand::SeedableRng;
18use rand_chacha::ChaCha20Rng;
19use sha3::Shake256;
20use std::io::Read;
21
22//////////////////////////////////////Pallas///////////////////////////////////////////////
23
24/// A wrapper for compressed group elements that come from the pallas curve
25#[derive(Clone, Copy, Debug, Eq, PartialEq)]
26pub struct PallasCompressedElementWrapper {
27 repr: [u8; 32],
28}
29
30impl PallasCompressedElementWrapper {
31 /// Wraps repr into the wrapper
32 pub fn new(repr: [u8; 32]) -> Self {
33 Self { repr }
34 }
35}
36
37impl Group for pallas::Point {
38 type Base = pallas::Base;
39 type Scalar = pallas::Scalar;
40 type CompressedGroupElement = PallasCompressedElementWrapper;
41 type PreprocessedGroupElement = pallas::Affine;
42 type HashFunc = PoseidonRO<Self::Base, Self::Scalar>;
43
44 fn vartime_multiscalar_mul(
45 scalars: &[Self::Scalar],
46 bases: &[Self::PreprocessedGroupElement],
47 ) -> Self {
48 pasta_msm::pallas(bases, scalars)
49 }
50
51 fn compress(&self) -> Self::CompressedGroupElement {
52 PallasCompressedElementWrapper::new(self.to_bytes())
53 }
54
55 fn from_label(label: &'static [u8], n: usize) -> Vec<Self::PreprocessedGroupElement> {
56 let mut shake = Shake256::default();
57 shake.input(label);
58 let mut reader = shake.xof_result();
59 let mut gens: Vec<Self::PreprocessedGroupElement> = Vec::new();
60 let mut uniform_bytes = [0u8; 32];
61 for _ in 0..n {
62 reader.read_exact(&mut uniform_bytes).unwrap();
63 let hash = Ep::hash_to_curve("from_uniform_bytes");
64 gens.push(hash(&uniform_bytes).to_affine());
65 }
66 gens
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() -> BigInt {
79 BigInt::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 pasta_msm::vesta(bases, scalars)
134 }
135
136 fn compress(&self) -> Self::CompressedGroupElement {
137 VestaCompressedElementWrapper::new(self.to_bytes())
138 }
139
140 fn from_label(label: &'static [u8], n: usize) -> Vec<Self::PreprocessedGroupElement> {
141 let mut shake = Shake256::default();
142 shake.input(label);
143 let mut reader = shake.xof_result();
144 let mut gens: Vec<Self::PreprocessedGroupElement> = Vec::new();
145 let mut uniform_bytes = [0u8; 32];
146 for _ in 0..n {
147 reader.read_exact(&mut uniform_bytes).unwrap();
148 let hash = Eq::hash_to_curve("from_uniform_bytes");
149 gens.push(hash(&uniform_bytes).to_affine());
150 }
151 gens
152 }
153
154 fn to_coordinates(&self) -> (Self::Base, Self::Base, bool) {
155 let coordinates = self.to_affine().coordinates();
156 if coordinates.is_some().unwrap_u8() == 1 {
157 (*coordinates.unwrap().x(), *coordinates.unwrap().y(), false)
158 } else {
159 (Self::Base::zero(), Self::Base::zero(), true)
160 }
161 }
162
163 fn get_order() -> BigInt {
164 BigInt::from_str_radix(
165 "40000000000000000000000000000000224698fc094cf91b992d30ed00000001",
166 16,
167 )
168 .unwrap()
169 }
170}
171
172impl ChallengeTrait for vesta::Scalar {
173 fn challenge(label: &'static [u8], transcript: &mut Transcript) -> Self {
174 let mut key: <ChaCha20Rng as SeedableRng>::Seed = Default::default();
175 transcript.challenge_bytes(label, &mut key);
176 let mut rng = ChaCha20Rng::from_seed(key);
177 vesta::Scalar::random(&mut rng)
178 }
179}
180
181impl CompressedGroup for VestaCompressedElementWrapper {
182 type GroupElement = vesta::Point;
183
184 fn decompress(&self) -> Option<vesta::Point> {
185 Some(Eq::from_bytes(&self.repr).unwrap())
186 }
187 fn as_bytes(&self) -> &[u8] {
188 &self.repr
189 }
190}