1use ffi::CPtr;
2
3use crate::ffi;
4use crate::{from_hex, Error, Generator, Secp256k1, Signing, Tweak, ZERO_TWEAK};
5use core::{fmt, slice, str};
6
7#[derive(Debug, PartialEq, Clone, Copy, Eq, Hash, PartialOrd, Ord)]
9pub struct PedersenCommitment(ffi::PedersenCommitment);
10
11impl PedersenCommitment {
12 pub fn serialize(&self) -> [u8; 33] {
16 let mut bytes = [0u8; 33];
17
18 let ret = unsafe {
19 ffi::secp256k1_pedersen_commitment_serialize(
20 ffi::secp256k1_context_no_precomp,
21 bytes.as_mut_ptr(),
22 &self.0,
23 )
24 };
25 assert_eq!(ret, 1, "failed to serialize pedersen commitment");
26
27 bytes
28 }
29
30 pub fn from_slice(bytes: &[u8]) -> Result<Self, Error> {
32 let mut commitment = ffi::PedersenCommitment::default();
33
34 let ret = unsafe {
35 ffi::secp256k1_pedersen_commitment_parse(
36 ffi::secp256k1_context_no_precomp,
37 &mut commitment,
38 bytes.as_ptr(),
39 )
40 };
41
42 if ret != 1 {
43 return Err(Error::InvalidPedersenCommitment);
44 }
45
46 Ok(PedersenCommitment(commitment))
47 }
48
49 pub fn new<C: Signing>(
54 secp: &Secp256k1<C>,
55 value: u64,
56 blinding_factor: Tweak,
57 generator: Generator,
58 ) -> Self {
59 let mut commitment = ffi::PedersenCommitment::default();
60
61 let ret = unsafe {
62 ffi::secp256k1_pedersen_commit(
63 secp.ctx().as_ptr(),
64 &mut commitment,
65 blinding_factor.as_c_ptr(),
66 value,
67 generator.as_inner(),
68 )
69 };
70 assert_eq!(
71 ret, 1,
72 "failed to create pedersen commitment, likely a bad blinding factor"
73 );
74
75 PedersenCommitment(commitment)
76 }
77
78 pub fn new_unblinded<C: Signing>(
81 secp: &Secp256k1<C>,
82 value: u64,
83 generator: Generator,
84 ) -> Self {
85 PedersenCommitment::new(secp, value, ZERO_TWEAK, generator)
86 }
87
88 pub(crate) fn as_inner(&self) -> &ffi::PedersenCommitment {
89 &self.0
90 }
91}
92
93#[derive(Debug)]
101pub struct CommitmentSecrets {
102 pub value: u64,
104 pub value_blinding_factor: Tweak,
106 pub generator_blinding_factor: Tweak,
108}
109
110impl CommitmentSecrets {
111 pub fn new(value: u64, value_blinding_factor: Tweak, generator_blinding_factor: Tweak) -> Self {
113 CommitmentSecrets {
114 value,
115 value_blinding_factor,
116 generator_blinding_factor,
117 }
118 }
119}
120
121pub fn compute_adaptive_blinding_factor<C: Signing>(
123 secp: &Secp256k1<C>,
124 value: u64,
125 generator_blinding_factor: Tweak,
126 set_a: &[CommitmentSecrets],
127 set_b: &[CommitmentSecrets],
128) -> Tweak {
129 let value_blinding_factor_placeholder = ZERO_TWEAK; let (mut values, mut secrets) = set_a
132 .iter()
133 .chain(set_b.iter())
134 .map(|c| {
135 (
136 c.value,
137 (c.value_blinding_factor, c.generator_blinding_factor),
138 )
139 })
140 .unzip::<_, _, Vec<_>, Vec<_>>();
141 values.push(value);
142 secrets.push((value_blinding_factor_placeholder, generator_blinding_factor));
143
144 let (vbf, gbf) = secrets
145 .iter_mut()
146 .map(|(s_v, s_g)| (s_v.as_mut_c_ptr(), s_g.as_c_ptr()))
147 .unzip::<_, _, Vec<_>, Vec<_>>();
148
149 let ret = unsafe {
150 ffi::secp256k1_pedersen_blind_generator_blind_sum(
151 secp.ctx().as_ptr(),
152 values.as_ptr(),
153 gbf.as_ptr(),
154 vbf.as_ptr(),
155 set_a.len() + set_b.len() + 1,
156 set_a.len(),
157 )
158 };
159 assert_eq!(1, ret, "failed to compute blinding factor");
160
161 let last = vbf.last().expect("this vector is never empty");
162 let slice = unsafe { slice::from_raw_parts(*last, 32) };
163 Tweak::from_slice(slice).expect("data is always 32 bytes")
164}
165
166#[must_use]
168pub fn verify_commitments_sum_to_equal<C: Signing>(
169 secp: &Secp256k1<C>,
170 a: &[PedersenCommitment],
171 b: &[PedersenCommitment],
172) -> bool {
173 let a = a.iter().map(|c| &c.0).collect::<Vec<_>>();
174 let b = b.iter().map(|c| &c.0).collect::<Vec<_>>();
175
176 let ret = unsafe {
177 ffi::secp256k1_pedersen_verify_tally(
178 secp.ctx().as_ptr(),
179 a.as_ptr(),
180 a.len(),
181 b.as_ptr(),
182 b.len(),
183 )
184 };
185
186 ret == 1
187}
188
189impl fmt::LowerHex for PedersenCommitment {
190 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
191 let ser = self.serialize();
192 for ch in &ser[..] {
193 write!(f, "{:02x}", *ch)?;
194 }
195 Ok(())
196 }
197}
198
199impl fmt::Display for PedersenCommitment {
200 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
201 fmt::LowerHex::fmt(self, f)
202 }
203}
204
205impl str::FromStr for PedersenCommitment {
206 type Err = Error;
207 fn from_str(s: &str) -> Result<Self, Error> {
208 let mut res = [0; 33];
209 match from_hex(s, &mut res) {
210 Ok(33) => Self::from_slice(&res[0..33]),
211 _ => Err(Error::InvalidPedersenCommitment),
212 }
213 }
214}
215
216#[cfg(feature = "serde")]
217impl ::serde::Serialize for PedersenCommitment {
218 fn serialize<S: ::serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
219 if s.is_human_readable() {
220 s.collect_str(self)
221 } else {
222 s.serialize_bytes(&self.serialize())
223 }
224 }
225}
226
227#[cfg(feature = "serde")]
228impl<'de> ::serde::Deserialize<'de> for PedersenCommitment {
229 fn deserialize<D: ::serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
230 use crate::serde_util;
231
232 if d.is_human_readable() {
233 d.deserialize_str(serde_util::FromStrVisitor::new("an ASCII hex string"))
234 } else {
235 d.deserialize_bytes(serde_util::BytesVisitor::new(
236 "a bytestring",
237 PedersenCommitment::from_slice,
238 ))
239 }
240 }
241}
242
243#[cfg(all(test, feature = "global-context"))]
244mod tests {
245 use std::str::FromStr;
246
247 use super::*;
248 use crate::{Tag, SECP256K1};
249 use rand::thread_rng;
250
251 #[cfg(target_arch = "wasm32")]
252 use wasm_bindgen_test::wasm_bindgen_test as test;
253
254 impl CommitmentSecrets {
255 pub fn random(value: u64) -> Self {
256 Self {
257 value,
258 value_blinding_factor: Tweak::new(&mut thread_rng()),
259 generator_blinding_factor: Tweak::new(&mut thread_rng()),
260 }
261 }
262
263 pub fn commit(&self, tag: Tag) -> PedersenCommitment {
264 let generator = Generator::new_blinded(SECP256K1, tag, self.generator_blinding_factor);
265
266 PedersenCommitment::new(SECP256K1, self.value, self.value_blinding_factor, generator)
267 }
268 }
269
270 #[test]
271 fn test_unblinded_pedersen_commitments() {
272 let tag = Tag::random();
273 let unblinded_gen = Generator::new_unblinded(SECP256K1, tag);
274 let one_comm = PedersenCommitment::new_unblinded(SECP256K1, 1, unblinded_gen); let two_comm = PedersenCommitment::new_unblinded(SECP256K1, 2, unblinded_gen); let three_comm = PedersenCommitment::new_unblinded(SECP256K1, 3, unblinded_gen); let six_comm = PedersenCommitment::new_unblinded(SECP256K1, 6, unblinded_gen); let commitment_sums_are_equal = verify_commitments_sum_to_equal(
280 SECP256K1,
281 &[one_comm, two_comm, three_comm],
282 &[six_comm],
283 );
284
285 assert!(commitment_sums_are_equal);
286 }
287
288 #[test]
289 fn test_serialize_and_parse_pedersen_commitment() {
290 let commitment = CommitmentSecrets::random(1000).commit(Tag::random());
291
292 let bytes = commitment.serialize();
293 let got = PedersenCommitment::from_slice(&bytes).unwrap();
294
295 assert_eq!(got, commitment);
296 }
297
298 #[test]
299 fn test_equal_sum_of_commitments() {
300 let tag_1 = Tag::random();
301 let tag_2 = Tag::random();
302
303 let secrets_1 = CommitmentSecrets::random(1000);
304 let commitment_1 = secrets_1.commit(tag_1);
305 let secrets_2 = CommitmentSecrets::random(1000);
306 let commitment_2 = secrets_2.commit(tag_2);
307
308 let secrets_3 = CommitmentSecrets::random(1000);
309 let commitment_3 = secrets_3.commit(tag_1);
310
311 let tbf_4 = Tweak::new(&mut thread_rng());
312 let blinded_tag_4 = Generator::new_blinded(SECP256K1, tag_2, tbf_4);
313 let vbf_4 = compute_adaptive_blinding_factor(
314 SECP256K1,
315 1000,
316 tbf_4,
317 &[secrets_1, secrets_2],
318 &[secrets_3],
319 );
320 let commitment_4 = PedersenCommitment::new(SECP256K1, 1000, vbf_4, blinded_tag_4);
321
322 let commitment_sums_are_equal = verify_commitments_sum_to_equal(
323 SECP256K1,
324 &[commitment_1, commitment_2],
325 &[commitment_3, commitment_4],
326 );
327
328 assert!(commitment_sums_are_equal);
329 }
330
331 #[test]
332 fn test_pedersen_from_str() {
333 let commitment = CommitmentSecrets::random(1000).commit(Tag::random());
334
335 let string = commitment.to_string();
336 let from_str = PedersenCommitment::from_str(&string);
337
338 assert_eq!(Ok(commitment), from_str)
339 }
340
341 #[cfg(feature = "serde")]
342 #[test]
343 fn test_pedersen_de_serialization() {
344 use serde_test::Configure;
345 use serde_test::{assert_tokens, Token};
346
347 let commitment = PedersenCommitment::from_slice(&[
348 9, 7, 166, 63, 171, 227, 228, 157, 87, 19, 233, 218, 252, 171, 254, 202, 228, 138, 19,
349 124, 26, 29, 131, 42, 33, 212, 151, 151, 89, 0, 135, 201, 254,
350 ])
351 .unwrap();
352
353 assert_tokens(
354 &commitment.readable(),
355 &[Token::Str(
356 "0907a63fabe3e49d5713e9dafcabfecae48a137c1a1d832a21d49797590087c9fe",
357 )],
358 );
359
360 assert_tokens(
361 &commitment.compact(),
362 &[Token::Bytes(&[
363 9, 7, 166, 63, 171, 227, 228, 157, 87, 19, 233, 218, 252, 171, 254, 202, 228, 138,
364 19, 124, 26, 29, 131, 42, 33, 212, 151, 151, 89, 0, 135, 201, 254,
365 ])],
366 );
367 }
368
369 }