1use ffi::CPtr;
2
3use crate::ffi::RANGEPROOF_MAX_LENGTH;
4use crate::from_hex;
5use crate::Error;
6use crate::Generator;
7use crate::PedersenCommitment;
8use crate::Verification;
9use crate::{ffi, Secp256k1, SecretKey, Signing, Tweak};
10use std::ops::Range;
11use std::str;
12
13#[derive(Debug, PartialEq, Clone, Eq, Hash, PartialOrd, Ord)]
17pub struct RangeProof {
18 inner: ffi::RangeProof,
19}
20
21impl RangeProof {
22 pub fn serialize(&self) -> Vec<u8> {
24 self.inner.to_bytes()
25 }
26
27 pub fn from_slice(bytes: &[u8]) -> Result<Self, Error> {
31 let mut exp = 0;
32 let mut mantissa = 0;
33 let mut min_value = 0;
34 let mut max_value = 0;
35
36 let ret = unsafe {
37 ffi::secp256k1_rangeproof_info(
38 ffi::secp256k1_context_no_precomp,
39 &mut exp,
40 &mut mantissa,
41 &mut min_value,
42 &mut max_value,
43 bytes.as_ptr(),
44 bytes.len(),
45 )
46 };
47
48 if ret == 0 {
49 return Err(Error::InvalidRangeProof);
50 }
51
52 Ok(RangeProof {
53 inner: ffi::RangeProof::new(bytes),
54 })
55 }
56
57 pub fn len(&self) -> usize {
59 self.inner.len()
60 }
61
62 pub fn is_empty(&self) -> bool {
64 self.inner.is_empty()
65 }
66
67 #[allow(clippy::too_many_arguments)]
69 pub fn new<C: Signing>(
70 secp: &Secp256k1<C>,
71 min_value: u64,
72 commitment: PedersenCommitment,
73 value: u64,
74 commitment_blinding: Tweak,
75 message: &[u8],
76 additional_commitment: &[u8],
77 sk: SecretKey,
78 exp: i32,
79 min_bits: u8,
80 additional_generator: Generator,
81 ) -> Result<RangeProof, Error> {
82 let mut proof = [0u8; RANGEPROOF_MAX_LENGTH];
83 let mut proof_length = RANGEPROOF_MAX_LENGTH;
84
85 let ret = unsafe {
86 ffi::secp256k1_rangeproof_sign(
87 secp.ctx().as_ptr(),
88 proof.as_mut_ptr(),
89 &mut proof_length,
90 min_value,
91 commitment.as_inner(),
92 commitment_blinding.as_c_ptr(),
93 sk.as_c_ptr(),
94 exp,
95 min_bits as i32,
96 value,
97 message.as_ptr(),
98 message.len(),
99 additional_commitment.as_ptr(),
100 additional_commitment.len(),
101 additional_generator.as_inner(),
102 )
103 };
104
105 if ret == 0 {
106 return Err(Error::CannotMakeRangeProof);
107 }
108
109 Ok(RangeProof {
110 inner: ffi::RangeProof::new(&proof[..proof_length]),
111 })
112 }
113
114 pub fn verify<C: Verification>(
118 &self,
119 secp: &Secp256k1<C>,
120 commitment: PedersenCommitment,
121 additional_commitment: &[u8],
122 additional_generator: Generator,
123 ) -> Result<Range<u64>, Error> {
124 let mut min_value = 0u64;
125 let mut max_value = 0u64;
126
127 let ret = unsafe {
128 ffi::secp256k1_rangeproof_verify(
129 secp.ctx().as_ptr(),
130 &mut min_value,
131 &mut max_value,
132 commitment.as_inner(),
133 self.inner.as_ptr(),
134 self.inner.len(),
135 additional_commitment.as_ptr(),
136 additional_commitment.len(),
137 additional_generator.as_inner(),
138 )
139 };
140
141 if ret == 0 {
142 return Err(Error::InvalidRangeProof);
143 }
144
145 Ok(Range {
146 start: min_value,
147 end: max_value + 1,
148 })
149 }
150
151 pub fn rewind<C: Verification>(
153 &self,
154 secp: &Secp256k1<C>,
155 commitment: PedersenCommitment,
156 sk: SecretKey,
157 additional_commitment: &[u8],
158 additional_generator: Generator,
159 ) -> Result<(Opening, Range<u64>), Error> {
160 let mut min_value = 0u64;
161 let mut max_value = 0u64;
162
163 let mut blinding_factor = [0u8; 32];
164 let mut value = 0u64;
165 let mut message = [0u8; 4096];
166 let mut message_length = 4096usize;
167
168 let ret = unsafe {
169 ffi::secp256k1_rangeproof_rewind(
170 secp.ctx().as_ptr(),
171 blinding_factor.as_mut_ptr(),
172 &mut value,
173 message.as_mut_ptr(),
174 &mut message_length,
175 sk.as_c_ptr(),
176 &mut min_value,
177 &mut max_value,
178 commitment.as_inner(),
179 self.inner.as_ptr(),
180 self.inner.len(),
181 additional_commitment.as_ptr(),
182 additional_commitment.len(),
183 additional_generator.as_inner(),
184 )
185 };
186
187 if ret == 0 {
188 return Err(Error::InvalidRangeProof);
189 }
190
191 let opening = Opening {
192 value,
193 blinding_factor: Tweak::from_slice(&blinding_factor)?,
194 message: message[..message_length].into(),
195 };
196
197 let range = Range {
198 start: min_value,
199 end: max_value + 1,
200 };
201
202 Ok((opening, range))
203 }
204}
205
206#[cfg(feature = "hashes")]
207impl ::core::fmt::Display for RangeProof {
208 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
209 use internals::hex::display::DisplayHex;
210
211 write!(f, "{:x}", &self.serialize().as_slice().as_hex())
212 }
213}
214
215impl str::FromStr for RangeProof {
216 type Err = Error;
217 fn from_str(s: &str) -> Result<RangeProof, Error> {
218 let mut res = vec![0u8; s.len() / 2];
219 match from_hex(s, &mut res) {
220 Ok(_) => RangeProof::from_slice(&res),
221 _ => Err(Error::InvalidRangeProof),
222 }
223 }
224}
225
226#[cfg(all(feature = "serde", feature = "hashes"))]
227impl ::serde::Serialize for RangeProof {
228 fn serialize<S: ::serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
229 if s.is_human_readable() {
230 s.collect_str(&self)
231 } else {
232 s.serialize_bytes(&self.serialize())
233 }
234 }
235}
236
237#[cfg(all(feature = "serde", feature = "hashes"))]
238impl<'de> ::serde::Deserialize<'de> for RangeProof {
239 fn deserialize<D: ::serde::Deserializer<'de>>(d: D) -> Result<RangeProof, D::Error> {
240 use crate::serde_util;
241
242 if d.is_human_readable() {
243 d.deserialize_str(serde_util::FromStrVisitor::new("an ASCII hex string"))
244 } else {
245 d.deserialize_bytes(serde_util::BytesVisitor::new(
246 "a bytestring",
247 RangeProof::from_slice,
248 ))
249 }
250 }
251}
252
253pub struct Opening {
257 pub value: u64,
259 pub blinding_factor: Tweak,
261 pub message: Box<[u8]>,
263}
264
265#[cfg(all(test, feature = "global-context"))] mod tests {
267 use super::*;
268 use crate::{CommitmentSecrets, Tag, SECP256K1};
269 use rand::thread_rng;
270
271 #[cfg(target_arch = "wasm32")]
272 use wasm_bindgen_test::wasm_bindgen_test as test;
273
274 #[test]
275 fn create_and_verify_range_proof() {
276 let value = 1_000;
277 let commitment_secrets = CommitmentSecrets::random(value);
278 let tag = Tag::random();
279 let commitment = commitment_secrets.commit(tag);
280
281 let message = b"foo";
282 let additional_commitment = b"bar";
283
284 let sk = SecretKey::new(&mut thread_rng());
285 let additional_generator =
286 Generator::new_blinded(SECP256K1, tag, commitment_secrets.generator_blinding_factor);
287
288 let proof = RangeProof::new(
289 SECP256K1,
290 1,
291 commitment,
292 value,
293 commitment_secrets.value_blinding_factor,
294 message,
295 additional_commitment,
296 sk,
297 0,
298 52,
299 additional_generator,
300 )
301 .unwrap();
302
303 proof
304 .verify(
305 SECP256K1,
306 commitment,
307 additional_commitment,
308 additional_generator,
309 )
310 .unwrap();
311
312 #[cfg(feature = "hashes")]
313 {
314 use std::str::FromStr;
315 use std::string::ToString;
316 let proof_str = proof.to_string();
317 assert_eq!(proof, RangeProof::from_str(&proof_str).unwrap());
318 }
319 }
320
321 #[test]
322 fn rewind_range_proof() {
323 let value = 1_000;
324 let commitment_secrets = CommitmentSecrets::random(value);
325 let tag = Tag::random();
326 let commitment = commitment_secrets.commit(tag);
327
328 let message = b"foo";
329 let additional_commitment = b"bar";
330
331 let sk = SecretKey::new(&mut thread_rng());
332 let additional_generator =
333 Generator::new_blinded(SECP256K1, tag, commitment_secrets.generator_blinding_factor);
334
335 let proof = RangeProof::new(
336 SECP256K1,
337 1,
338 commitment,
339 value,
340 commitment_secrets.value_blinding_factor,
341 message,
342 additional_commitment,
343 sk,
344 0,
345 52,
346 additional_generator,
347 )
348 .unwrap();
349
350 let (opening, _range) = proof
351 .rewind(
352 SECP256K1,
353 commitment,
354 sk,
355 additional_commitment,
356 additional_generator,
357 )
358 .unwrap();
359
360 assert_eq!(opening.value, commitment_secrets.value);
361 assert_eq!(
362 opening.blinding_factor,
363 commitment_secrets.value_blinding_factor
364 );
365
366 assert!(opening.message.starts_with(message));
367 assert!(opening
368 .message
369 .ends_with(&vec![0; opening.message.len() - message.len()]));
370 }
371}