zebra_chain/orchard/
commitment.rs
1use std::{fmt, io};
4
5use group::{
6 ff::{FromUniformBytes, PrimeField},
7 prime::PrimeCurveAffine,
8 GroupEncoding,
9};
10use halo2::{
11 arithmetic::{Coordinates, CurveAffine},
12 pasta::pallas,
13};
14use lazy_static::lazy_static;
15use rand_core::{CryptoRng, RngCore};
16
17use crate::{
18 amount::Amount,
19 error::RandError,
20 serialization::{
21 serde_helpers, ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize,
22 },
23};
24
25use super::sinsemilla::*;
26
27pub fn generate_trapdoor<T>(csprng: &mut T) -> Result<pallas::Scalar, RandError>
31where
32 T: RngCore + CryptoRng,
33{
34 let mut bytes = [0u8; 64];
35 csprng
36 .try_fill_bytes(&mut bytes)
37 .map_err(|_| RandError::FillBytes)?;
38 Ok(pallas::Scalar::from_uniform_bytes(&bytes))
40}
41
42#[derive(Copy, Clone, Debug, PartialEq, Eq)]
44pub struct CommitmentRandomness(pallas::Scalar);
45
46#[derive(Clone, Copy, Deserialize, PartialEq, Eq, Serialize)]
48pub struct NoteCommitment(#[serde(with = "serde_helpers::Affine")] pub pallas::Affine);
49
50impl fmt::Debug for NoteCommitment {
51 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
52 let mut d = f.debug_struct("NoteCommitment");
53
54 let option: Option<Coordinates<pallas::Affine>> = self.0.coordinates().into();
55
56 match option {
57 Some(coordinates) => d
58 .field("x", &hex::encode(coordinates.x().to_repr()))
59 .field("y", &hex::encode(coordinates.y().to_repr()))
60 .finish(),
61 None => d
62 .field("x", &hex::encode(pallas::Base::zero().to_repr()))
63 .field("y", &hex::encode(pallas::Base::zero().to_repr()))
64 .finish(),
65 }
66 }
67}
68
69impl From<pallas::Point> for NoteCommitment {
70 fn from(projective_point: pallas::Point) -> Self {
71 Self(pallas::Affine::from(projective_point))
72 }
73}
74
75impl From<NoteCommitment> for [u8; 32] {
76 fn from(cm: NoteCommitment) -> [u8; 32] {
77 cm.0.to_bytes()
78 }
79}
80
81impl TryFrom<[u8; 32]> for NoteCommitment {
82 type Error = &'static str;
83
84 fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
85 let possible_point = pallas::Affine::from_bytes(&bytes);
86
87 if possible_point.is_some().into() {
88 Ok(Self(possible_point.unwrap()))
89 } else {
90 Err("Invalid pallas::Affine value")
91 }
92 }
93}
94
95impl NoteCommitment {
96 pub fn extract_x(&self) -> pallas::Base {
98 extract_p(self.0.into())
99 }
100}
101
102#[derive(Clone, Copy, Deserialize, PartialEq, Eq, Serialize)]
107pub struct ValueCommitment(#[serde(with = "serde_helpers::Affine")] pub pallas::Affine);
108
109impl<'a> std::ops::Add<&'a ValueCommitment> for ValueCommitment {
110 type Output = Self;
111
112 fn add(self, rhs: &'a ValueCommitment) -> Self::Output {
113 self + *rhs
114 }
115}
116
117impl std::ops::Add<ValueCommitment> for ValueCommitment {
118 type Output = Self;
119
120 fn add(self, rhs: ValueCommitment) -> Self::Output {
121 ValueCommitment((self.0 + rhs.0).into())
122 }
123}
124
125impl std::ops::AddAssign<ValueCommitment> for ValueCommitment {
126 fn add_assign(&mut self, rhs: ValueCommitment) {
127 *self = *self + rhs
128 }
129}
130
131impl fmt::Debug for ValueCommitment {
132 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
133 let mut d = f.debug_struct("ValueCommitment");
134
135 let option: Option<Coordinates<pallas::Affine>> = self.0.coordinates().into();
136
137 match option {
138 Some(coordinates) => d
139 .field("x", &hex::encode(coordinates.x().to_repr()))
140 .field("y", &hex::encode(coordinates.y().to_repr()))
141 .finish(),
142 None => d
143 .field("x", &hex::encode(pallas::Base::zero().to_repr()))
144 .field("y", &hex::encode(pallas::Base::zero().to_repr()))
145 .finish(),
146 }
147 }
148}
149
150impl From<pallas::Point> for ValueCommitment {
151 fn from(projective_point: pallas::Point) -> Self {
152 Self(pallas::Affine::from(projective_point))
153 }
154}
155
156impl From<ValueCommitment> for [u8; 32] {
160 fn from(cm: ValueCommitment) -> [u8; 32] {
161 cm.0.to_bytes()
162 }
163}
164
165impl<'a> std::ops::Sub<&'a ValueCommitment> for ValueCommitment {
166 type Output = Self;
167
168 fn sub(self, rhs: &'a ValueCommitment) -> Self::Output {
169 self - *rhs
170 }
171}
172
173impl std::ops::Sub<ValueCommitment> for ValueCommitment {
174 type Output = Self;
175
176 fn sub(self, rhs: ValueCommitment) -> Self::Output {
177 ValueCommitment((self.0 - rhs.0).into())
178 }
179}
180
181impl std::ops::SubAssign<ValueCommitment> for ValueCommitment {
182 fn sub_assign(&mut self, rhs: ValueCommitment) {
183 *self = *self - rhs;
184 }
185}
186
187impl std::iter::Sum for ValueCommitment {
188 fn sum<I>(iter: I) -> Self
189 where
190 I: Iterator<Item = Self>,
191 {
192 iter.fold(
193 ValueCommitment(pallas::Affine::identity()),
194 std::ops::Add::add,
195 )
196 }
197}
198
199impl TryFrom<[u8; 32]> for ValueCommitment {
203 type Error = &'static str;
204
205 fn try_from(bytes: [u8; 32]) -> Result<Self, Self::Error> {
206 let possible_point = pallas::Affine::from_bytes(&bytes);
207
208 if possible_point.is_some().into() {
209 Ok(Self(possible_point.unwrap()))
210 } else {
211 Err("Invalid pallas::Affine value")
212 }
213 }
214}
215
216impl ZcashSerialize for ValueCommitment {
217 fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
218 writer.write_all(&<[u8; 32]>::from(*self)[..])?;
219 Ok(())
220 }
221}
222
223impl ZcashDeserialize for ValueCommitment {
224 fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
225 Self::try_from(reader.read_32_bytes()?).map_err(SerializationError::Parse)
226 }
227}
228
229impl ValueCommitment {
230 pub fn randomized<T>(csprng: &mut T, value: Amount) -> Result<Self, RandError>
234 where
235 T: RngCore + CryptoRng,
236 {
237 let rcv = generate_trapdoor(csprng)?;
238
239 Ok(Self::new(rcv, value))
240 }
241
242 #[allow(non_snake_case)]
248 pub fn new(rcv: pallas::Scalar, value: Amount) -> Self {
249 let v = pallas::Scalar::from(value);
250 Self::from(*V * v + *R * rcv)
251 }
252}
253
254lazy_static! {
255 static ref V: pallas::Point = pallas_group_hash(b"z.cash:Orchard-cv", b"v");
256 static ref R: pallas::Point = pallas_group_hash(b"z.cash:Orchard-cv", b"r");
257}
258
259#[cfg(test)]
260mod tests {
261
262 use std::ops::Neg;
263
264 use group::Group;
265
266 use super::*;
267
268 #[test]
269 fn add() {
270 let _init_guard = zebra_test::init();
271
272 let identity = ValueCommitment(pallas::Affine::identity());
273
274 let g = ValueCommitment(pallas::Affine::generator());
275
276 assert_eq!(identity + g, g);
277 }
278
279 #[test]
280 fn add_assign() {
281 let _init_guard = zebra_test::init();
282
283 let mut identity = ValueCommitment(pallas::Affine::identity());
284
285 let g = ValueCommitment(pallas::Affine::generator());
286
287 identity += g;
288 let new_g = identity;
289
290 assert_eq!(new_g, g);
291 }
292
293 #[test]
294 fn sub() {
295 let _init_guard = zebra_test::init();
296
297 let g_point = pallas::Affine::generator();
298
299 let identity = ValueCommitment(pallas::Affine::identity());
300
301 let g = ValueCommitment(g_point);
302
303 assert_eq!(identity - g, ValueCommitment(g_point.neg()));
304 }
305
306 #[test]
307 fn sub_assign() {
308 let _init_guard = zebra_test::init();
309
310 let g_point = pallas::Affine::generator();
311
312 let mut identity = ValueCommitment(pallas::Affine::identity());
313
314 let g = ValueCommitment(g_point);
315
316 identity -= g;
317 let new_g = identity;
318
319 assert_eq!(new_g, ValueCommitment(g_point.neg()));
320 }
321
322 #[test]
323 fn sum() {
324 let _init_guard = zebra_test::init();
325
326 let g_point = pallas::Affine::generator();
327
328 let g = ValueCommitment(g_point);
329 let other_g = ValueCommitment(g_point);
330
331 let sum: ValueCommitment = vec![g, other_g].into_iter().sum();
332
333 let doubled_g = ValueCommitment(g_point.to_curve().double().into());
334
335 assert_eq!(sum, doubled_g);
336 }
337}