si_commitment_scheme/
pedersen.rs1use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT;
7use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
8use curve25519_dalek::scalar::Scalar;
9use curve25519_dalek::traits::Identity;
10use rand::rngs::OsRng;
11
12#[derive(Debug, Clone)]
14pub struct PedersenParams {
15 pub g: RistrettoPoint,
17 pub h: RistrettoPoint,
19}
20
21impl PedersenParams {
22 pub fn default_params() -> Self {
24 let g = RISTRETTO_BASEPOINT_POINT;
25 let g_bytes = g.compress().to_bytes();
27 let h_bytes = blake3::hash(&g_bytes);
28 let h_scalar = Scalar::from_bytes_mod_order(*h_bytes.as_bytes());
30 let h = h_scalar * g;
31 Self { g, h }
32 }
33
34 pub fn new(g: RistrettoPoint, h: RistrettoPoint) -> Self {
36 Self { g, h }
37 }
38}
39
40impl Default for PedersenParams {
41 fn default() -> Self {
42 Self::default_params()
43 }
44}
45
46#[derive(Debug, Clone, PartialEq, Eq)]
48pub struct PedersenCommitment {
49 pub point: RistrettoPoint,
51 pub compressed: CompressedRistretto,
53}
54
55#[derive(Debug, Clone)]
57pub struct PedersenOpening {
58 pub value: Scalar,
60 pub randomness: Scalar,
62}
63
64impl PedersenCommitment {
65 pub fn commit(params: &PedersenParams, value: &Scalar) -> (Self, PedersenOpening) {
67 let randomness = Scalar::random(&mut OsRng);
68 let commitment = Self::commit_with_randomness(params, value, &randomness);
69 let opening = PedersenOpening {
70 value: *value,
71 randomness,
72 };
73 (commitment, opening)
74 }
75
76 pub fn commit_with_randomness(
78 params: &PedersenParams,
79 value: &Scalar,
80 randomness: &Scalar,
81 ) -> Self {
82 let point = randomness * params.g + value * params.h;
83 let compressed = point.compress();
84 Self { point, compressed }
85 }
86
87 pub fn verify(
89 params: &PedersenParams,
90 opening: &PedersenOpening,
91 commitment: &PedersenCommitment,
92 ) -> bool {
93 let expected = opening.randomness * params.g + opening.value * params.h;
94 expected == commitment.point
95 }
96
97 pub fn commit_u64(params: &PedersenParams, value: u64) -> (Self, PedersenOpening) {
99 Self::commit(params, &Scalar::from(value))
100 }
101
102 pub fn identity() -> Self {
104 let point = RistrettoPoint::identity();
105 Self {
106 point,
107 compressed: point.compress(),
108 }
109 }
110
111 pub fn add(&self, other: &PedersenCommitment) -> PedersenCommitment {
113 let point = self.point + other.point;
114 PedersenCommitment {
115 point,
116 compressed: point.compress(),
117 }
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn commit_and_verify() {
127 let params = PedersenParams::default();
128 let value = Scalar::from(42u64);
129 let (commitment, opening) = PedersenCommitment::commit(¶ms, &value);
130 assert!(PedersenCommitment::verify(¶ms, &opening, &commitment));
131 }
132
133 #[test]
134 fn commit_u64_and_verify() {
135 let params = PedersenParams::default();
136 let (commitment, opening) = PedersenCommitment::commit_u64(¶ms, 12345);
137 assert!(PedersenCommitment::verify(¶ms, &opening, &commitment));
138 }
139
140 #[test]
141 fn wrong_value_fails() {
142 let params = PedersenParams::default();
143 let (commitment, mut opening) = PedersenCommitment::commit_u64(¶ms, 42);
144 opening.value = Scalar::from(99u64);
145 assert!(!PedersenCommitment::verify(¶ms, &opening, &commitment));
146 }
147
148 #[test]
149 fn wrong_randomness_fails() {
150 let params = PedersenParams::default();
151 let (commitment, mut opening) = PedersenCommitment::commit_u64(¶ms, 42);
152 opening.randomness = Scalar::random(&mut OsRng);
153 assert!(!PedersenCommitment::verify(¶ms, &opening, &commitment));
154 }
155
156 #[test]
157 fn homomorphic_addition() {
158 let params = PedersenParams::default();
159 let v1 = Scalar::from(10u64);
160 let v2 = Scalar::from(20u64);
161 let (c1, o1) = PedersenCommitment::commit(¶ms, &v1);
162 let (c2, o2) = PedersenCommitment::commit(¶ms, &v2);
163 let c_sum = c1.add(&c2);
164 let combined_value = v1 + v2;
165 let combined_randomness = o1.randomness + o2.randomness;
166 let expected =
167 PedersenCommitment::commit_with_randomness(¶ms, &combined_value, &combined_randomness);
168 assert_eq!(c_sum.point, expected.point);
169 }
170
171 #[test]
172 fn different_randomness_different_commitment() {
173 let params = PedersenParams::default();
174 let value = Scalar::from(42u64);
175 let (c1, _) = PedersenCommitment::commit(¶ms, &value);
176 let (c2, _) = PedersenCommitment::commit(¶ms, &value);
177 assert_ne!(c1.compressed, c2.compressed);
178 }
179
180 #[test]
181 fn deterministic_with_same_randomness() {
182 let params = PedersenParams::default();
183 let value = Scalar::from(42u64);
184 let r = Scalar::from(999u64);
185 let c1 = PedersenCommitment::commit_with_randomness(¶ms, &value, &r);
186 let c2 = PedersenCommitment::commit_with_randomness(¶ms, &value, &r);
187 assert_eq!(c1.compressed, c2.compressed);
188 }
189}