solana_zk_token_sdk/encryption/
pedersen.rs1#[cfg(not(target_os = "solana"))]
4use rand::rngs::OsRng;
5use {
6 crate::{RISTRETTO_POINT_LEN, SCALAR_LEN},
7 core::ops::{Add, Mul, Sub},
8 curve25519_dalek::{
9 constants::{RISTRETTO_BASEPOINT_COMPRESSED, RISTRETTO_BASEPOINT_POINT},
10 ristretto::{CompressedRistretto, RistrettoPoint},
11 scalar::Scalar,
12 traits::MultiscalarMul,
13 },
14 serde::{Deserialize, Serialize},
15 sha3::Sha3_512,
16 std::convert::TryInto,
17 subtle::{Choice, ConstantTimeEq},
18 zeroize::Zeroize,
19};
20
21const PEDERSEN_OPENING_LEN: usize = SCALAR_LEN;
23
24pub(crate) const PEDERSEN_COMMITMENT_LEN: usize = RISTRETTO_POINT_LEN;
26
27lazy_static::lazy_static! {
28 pub static ref G: RistrettoPoint = RISTRETTO_BASEPOINT_POINT;
30 pub static ref H: RistrettoPoint =
32 RistrettoPoint::hash_from_bytes::<Sha3_512>(RISTRETTO_BASEPOINT_COMPRESSED.as_bytes());
33}
34
35pub struct Pedersen;
37impl Pedersen {
38 #[cfg(not(target_os = "solana"))]
43 #[allow(clippy::new_ret_no_self)]
44 pub fn new<T: Into<Scalar>>(amount: T) -> (PedersenCommitment, PedersenOpening) {
45 let opening = PedersenOpening::new_rand();
46 let commitment = Pedersen::with(amount, &opening);
47
48 (commitment, opening)
49 }
50
51 #[allow(non_snake_case)]
56 pub fn with<T: Into<Scalar>>(amount: T, opening: &PedersenOpening) -> PedersenCommitment {
57 let x: Scalar = amount.into();
58 let r = opening.get_scalar();
59
60 PedersenCommitment(RistrettoPoint::multiscalar_mul(&[x, *r], &[*G, *H]))
61 }
62
63 pub fn encode<T: Into<Scalar>>(amount: T) -> PedersenCommitment {
68 PedersenCommitment(amount.into() * &(*G))
69 }
70}
71
72#[derive(Clone, Debug, Default, Serialize, Deserialize, Zeroize)]
76#[zeroize(drop)]
77pub struct PedersenOpening(Scalar);
78impl PedersenOpening {
79 pub fn new(scalar: Scalar) -> Self {
80 Self(scalar)
81 }
82
83 pub fn get_scalar(&self) -> &Scalar {
84 &self.0
85 }
86
87 #[cfg(not(target_os = "solana"))]
88 pub fn new_rand() -> Self {
89 PedersenOpening(Scalar::random(&mut OsRng))
90 }
91
92 pub fn as_bytes(&self) -> &[u8; PEDERSEN_OPENING_LEN] {
93 self.0.as_bytes()
94 }
95
96 pub fn to_bytes(&self) -> [u8; PEDERSEN_OPENING_LEN] {
97 self.0.to_bytes()
98 }
99
100 pub fn from_bytes(bytes: &[u8]) -> Option<PedersenOpening> {
101 match bytes.try_into() {
102 Ok(bytes) => Scalar::from_canonical_bytes(bytes)
103 .map(PedersenOpening)
104 .into(),
105 _ => None,
106 }
107 }
108}
109impl Eq for PedersenOpening {}
110impl PartialEq for PedersenOpening {
111 fn eq(&self, other: &Self) -> bool {
112 self.ct_eq(other).unwrap_u8() == 1u8
113 }
114}
115impl ConstantTimeEq for PedersenOpening {
116 fn ct_eq(&self, other: &Self) -> Choice {
117 self.0.ct_eq(&other.0)
118 }
119}
120
121impl<'b> Add<&'b PedersenOpening> for &PedersenOpening {
122 type Output = PedersenOpening;
123
124 fn add(self, opening: &'b PedersenOpening) -> PedersenOpening {
125 PedersenOpening(&self.0 + &opening.0)
126 }
127}
128
129define_add_variants!(
130 LHS = PedersenOpening,
131 RHS = PedersenOpening,
132 Output = PedersenOpening
133);
134
135impl<'b> Sub<&'b PedersenOpening> for &PedersenOpening {
136 type Output = PedersenOpening;
137
138 fn sub(self, opening: &'b PedersenOpening) -> PedersenOpening {
139 PedersenOpening(&self.0 - &opening.0)
140 }
141}
142
143define_sub_variants!(
144 LHS = PedersenOpening,
145 RHS = PedersenOpening,
146 Output = PedersenOpening
147);
148
149impl<'b> Mul<&'b Scalar> for &PedersenOpening {
150 type Output = PedersenOpening;
151
152 fn mul(self, scalar: &'b Scalar) -> PedersenOpening {
153 PedersenOpening(&self.0 * scalar)
154 }
155}
156
157define_mul_variants!(
158 LHS = PedersenOpening,
159 RHS = Scalar,
160 Output = PedersenOpening
161);
162
163impl<'b> Mul<&'b PedersenOpening> for &Scalar {
164 type Output = PedersenOpening;
165
166 fn mul(self, opening: &'b PedersenOpening) -> PedersenOpening {
167 PedersenOpening(self * &opening.0)
168 }
169}
170
171define_mul_variants!(
172 LHS = Scalar,
173 RHS = PedersenOpening,
174 Output = PedersenOpening
175);
176
177#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
179pub struct PedersenCommitment(RistrettoPoint);
180impl PedersenCommitment {
181 pub fn new(point: RistrettoPoint) -> Self {
182 Self(point)
183 }
184
185 pub fn get_point(&self) -> &RistrettoPoint {
186 &self.0
187 }
188
189 pub fn to_bytes(&self) -> [u8; PEDERSEN_COMMITMENT_LEN] {
190 self.0.compress().to_bytes()
191 }
192
193 pub fn from_bytes(bytes: &[u8]) -> Option<PedersenCommitment> {
194 if bytes.len() != PEDERSEN_COMMITMENT_LEN {
195 return None;
196 }
197 let Ok(compressed_ristretto) = CompressedRistretto::from_slice(bytes) else {
198 return None;
199 };
200
201 compressed_ristretto.decompress().map(PedersenCommitment)
202 }
203}
204
205impl<'b> Add<&'b PedersenCommitment> for &PedersenCommitment {
206 type Output = PedersenCommitment;
207
208 fn add(self, commitment: &'b PedersenCommitment) -> PedersenCommitment {
209 PedersenCommitment(&self.0 + &commitment.0)
210 }
211}
212
213define_add_variants!(
214 LHS = PedersenCommitment,
215 RHS = PedersenCommitment,
216 Output = PedersenCommitment
217);
218
219impl<'b> Sub<&'b PedersenCommitment> for &PedersenCommitment {
220 type Output = PedersenCommitment;
221
222 fn sub(self, commitment: &'b PedersenCommitment) -> PedersenCommitment {
223 PedersenCommitment(&self.0 - &commitment.0)
224 }
225}
226
227define_sub_variants!(
228 LHS = PedersenCommitment,
229 RHS = PedersenCommitment,
230 Output = PedersenCommitment
231);
232
233impl<'b> Mul<&'b Scalar> for &PedersenCommitment {
234 type Output = PedersenCommitment;
235
236 fn mul(self, scalar: &'b Scalar) -> PedersenCommitment {
237 PedersenCommitment(scalar * &self.0)
238 }
239}
240
241define_mul_variants!(
242 LHS = PedersenCommitment,
243 RHS = Scalar,
244 Output = PedersenCommitment
245);
246
247impl<'b> Mul<&'b PedersenCommitment> for &Scalar {
248 type Output = PedersenCommitment;
249
250 fn mul(self, commitment: &'b PedersenCommitment) -> PedersenCommitment {
251 PedersenCommitment(self * &commitment.0)
252 }
253}
254
255define_mul_variants!(
256 LHS = Scalar,
257 RHS = PedersenCommitment,
258 Output = PedersenCommitment
259);
260
261#[cfg(test)]
262mod tests {
263 use super::*;
264
265 #[test]
266 fn test_pedersen_homomorphic_addition() {
267 let amount_0: u64 = 77;
268 let amount_1: u64 = 57;
269
270 let rng = &mut OsRng;
271 let opening_0 = PedersenOpening(Scalar::random(rng));
272 let opening_1 = PedersenOpening(Scalar::random(rng));
273
274 let commitment_0 = Pedersen::with(amount_0, &opening_0);
275 let commitment_1 = Pedersen::with(amount_1, &opening_1);
276 let commitment_addition = Pedersen::with(amount_0 + amount_1, &(opening_0 + opening_1));
277
278 assert_eq!(commitment_addition, commitment_0 + commitment_1);
279 }
280
281 #[test]
282 fn test_pedersen_homomorphic_subtraction() {
283 let amount_0: u64 = 77;
284 let amount_1: u64 = 57;
285
286 let rng = &mut OsRng;
287 let opening_0 = PedersenOpening(Scalar::random(rng));
288 let opening_1 = PedersenOpening(Scalar::random(rng));
289
290 let commitment_0 = Pedersen::with(amount_0, &opening_0);
291 let commitment_1 = Pedersen::with(amount_1, &opening_1);
292 let commitment_addition = Pedersen::with(amount_0 - amount_1, &(opening_0 - opening_1));
293
294 assert_eq!(commitment_addition, commitment_0 - commitment_1);
295 }
296
297 #[test]
298 fn test_pedersen_homomorphic_multiplication() {
299 let amount_0: u64 = 77;
300 let amount_1: u64 = 57;
301
302 let (commitment, opening) = Pedersen::new(amount_0);
303 let scalar = Scalar::from(amount_1);
304 let commitment_addition = Pedersen::with(amount_0 * amount_1, &(opening * scalar));
305
306 assert_eq!(commitment_addition, commitment * scalar);
307 assert_eq!(commitment_addition, scalar * commitment);
308 }
309
310 #[test]
311 fn test_pedersen_commitment_bytes() {
312 let amount: u64 = 77;
313 let (commitment, _) = Pedersen::new(amount);
314
315 let encoded = commitment.to_bytes();
316 let decoded = PedersenCommitment::from_bytes(&encoded).unwrap();
317
318 assert_eq!(commitment, decoded);
319
320 assert_eq!(PedersenCommitment::from_bytes(&[0; 33]), None);
322 }
323
324 #[test]
325 fn test_pedersen_opening_bytes() {
326 let opening = PedersenOpening(Scalar::random(&mut OsRng));
327
328 let encoded = opening.to_bytes();
329 let decoded = PedersenOpening::from_bytes(&encoded).unwrap();
330
331 assert_eq!(opening, decoded);
332
333 assert_eq!(PedersenOpening::from_bytes(&[0; 33]), None);
335 }
336
337 #[test]
338 fn test_serde_pedersen_commitment() {
339 let amount: u64 = 77;
340 let (commitment, _) = Pedersen::new(amount);
341
342 let encoded = bincode::serialize(&commitment).unwrap();
343 let decoded: PedersenCommitment = bincode::deserialize(&encoded).unwrap();
344
345 assert_eq!(commitment, decoded);
346 }
347
348 #[test]
349 fn test_serde_pedersen_opening() {
350 let opening = PedersenOpening(Scalar::random(&mut OsRng));
351
352 let encoded = bincode::serialize(&opening).unwrap();
353 let decoded: PedersenOpening = bincode::deserialize(&encoded).unwrap();
354
355 assert_eq!(opening, decoded);
356 }
357}