1use crate::{Result, VeilCryptoError};
25use crate::borromean::borromean_sign;
26use crate::pedersen::{get_generator_h_point, pedersen_ecmult_point};
27use k256::{
28 elliptic_curve::{
29 group::Group,
30 sec1::{ToEncodedPoint, FromEncodedPoint},
31 ops::MulByGenerator,
32 PrimeField,
33 },
34 AffinePoint, ProjectivePoint, Scalar,
35};
36use sha2::{Sha256, Digest};
37use hmac::{Hmac, Mac};
38
39pub const MAX_PROOF_SIZE: usize = 5134;
41
42pub const MAX_RINGS: usize = 32;
44
45pub const MAX_NPUB: usize = 128;
47
48pub const MAX_MESSAGE_SIZE: usize = 256;
50
51#[derive(Debug, Clone)]
57struct RangeProofParams {
58 rings: usize, rsizes: Vec<usize>, secidx: Vec<usize>, npub: usize, mantissa: usize, #[allow(dead_code)]
64 min_value: u64, scale: u64, v: u64, }
68
69#[derive(Debug, Clone)]
71pub struct RangeProofSignResult {
72 pub proof: Vec<u8>,
74 pub commitment: Vec<u8>,
76 pub blind: Vec<u8>,
78 pub nonce: Vec<u8>,
80}
81
82#[derive(Debug, Clone)]
84pub struct RangeProofVerifyResult {
85 pub min_value: u64,
87 pub max_value: u64,
89}
90
91#[derive(Debug, Clone)]
93pub struct RangeProofRewindResult {
94 pub blind: Vec<u8>,
96 pub value: u64,
98 pub min_value: u64,
100 pub max_value: u64,
102 pub message: Vec<u8>,
104}
105
106#[derive(Debug, Clone)]
108pub struct RangeProofInfo {
109 pub exp: i32,
111 pub mantissa: usize,
113 pub min_value: u64,
115 pub max_value: u64,
117}
118
119fn serialize_point(point: &AffinePoint) -> [u8; 33] {
131 use k256::elliptic_curve::group::prime::PrimeCurveAffine;
132
133 let encoded = point.to_encoded_point(true);
134 let bytes = encoded.as_bytes();
135
136 if bytes.len() != 33 {
138 eprintln!("\n========== SERIALIZE_POINT ERROR ==========");
139 eprintln!("ERROR: serialize_point got {} bytes instead of 33", bytes.len());
140 eprintln!("Point is identity: {}", bool::from(point.is_identity()));
141 eprintln!("Encoded bytes: {:02x?}", bytes);
142 eprintln!("==========================================\n");
143 panic!("Invalid point serialization: expected 33 bytes, got {}", bytes.len());
144 }
145
146 let mut result = [0u8; 33];
147 result.copy_from_slice(bytes);
148 result
149}
150
151fn serialize_point_custom(point: &AffinePoint) -> [u8; 33] {
160 use k256::FieldElement;
161
162 let mut result = [0u8; 33];
163
164 let encoded = point.to_encoded_point(false); let bytes = encoded.as_bytes();
168
169 let y_bytes: [u8; 32] = bytes[33..65].try_into().unwrap();
171 let y = FieldElement::from_bytes(&y_bytes.into()).unwrap();
172
173 let y_has_sqrt = y.sqrt().is_some();
176 let y_is_qr: bool = y_has_sqrt.into();
177
178 result[0] = if y_is_qr { 0x00 } else { 0x01 };
181
182 result[1..33].copy_from_slice(&bytes[1..33]);
184
185 result
186}
187
188fn clz64(x: u64) -> usize {
190 if x == 0 {
191 64
192 } else {
193 x.leading_zeros() as usize
194 }
195}
196
197pub struct HmacDrbg {
201 k: [u8; 32],
202 v: [u8; 32],
203 retry: bool, }
205
206impl HmacDrbg {
207 pub fn new(seed: &[u8]) -> Self {
211 let mut k = [0u8; 32]; let mut v = [1u8; 32]; eprintln!("\n=== HMAC-DRBG Initialization ===");
215 eprintln!("Initial K: {}", hex::encode(&k));
216 eprintln!("Initial V: {}", hex::encode(&v));
217 eprintln!("Seed ({} bytes): {}", seed.len(), hex::encode(seed));
218
219 type HmacSha256 = Hmac<Sha256>;
220
221 let mut mac = HmacSha256::new_from_slice(&k).expect("HMAC init");
223 mac.update(&v);
224 mac.update(&[0x00]);
225 mac.update(seed);
226 k.copy_from_slice(&mac.finalize().into_bytes());
227 eprintln!("After step 3.2.d - K: {}", hex::encode(&k));
228
229 let mut mac = HmacSha256::new_from_slice(&k).expect("HMAC init");
231 mac.update(&v);
232 v.copy_from_slice(&mac.finalize().into_bytes());
233 eprintln!("After step 3.2.e - V: {}", hex::encode(&v));
234
235 let mut mac = HmacSha256::new_from_slice(&k).expect("HMAC init");
237 mac.update(&v);
238 mac.update(&[0x01]);
239 mac.update(seed);
240 k.copy_from_slice(&mac.finalize().into_bytes());
241 eprintln!("After step 3.2.f - K: {}", hex::encode(&k));
242
243 let mut mac = HmacSha256::new_from_slice(&k).expect("HMAC init");
245 mac.update(&v);
246 v.copy_from_slice(&mac.finalize().into_bytes());
247 eprintln!("After step 3.2.g - V: {}", hex::encode(&v));
248 eprintln!("=================================\n");
249
250 Self { k, v, retry: false } }
252
253 pub fn generate(&mut self) -> [u8; 32] {
258 static mut GENERATE_CALL_COUNT: usize = 0;
259 unsafe { GENERATE_CALL_COUNT += 1; }
260
261 type HmacSha256 = Hmac<Sha256>;
262
263 eprintln!("\n--- generate() call #{} ---", unsafe { GENERATE_CALL_COUNT });
264 eprintln!("Before: retry={}, K={}, V={}",
265 self.retry, hex::encode(&self.k[..8]), hex::encode(&self.v[..8]));
266
267 if self.retry {
269 eprintln!(" Retry=true, updating K and V first");
270 let mut mac = HmacSha256::new_from_slice(&self.k).expect("HMAC init");
272 mac.update(&self.v);
273 mac.update(&[0x00]);
274 self.k.copy_from_slice(&mac.finalize().into_bytes());
275 eprintln!(" After K update: K={}", hex::encode(&self.k[..8]));
276
277 let mut mac = HmacSha256::new_from_slice(&self.k).expect("HMAC init");
279 mac.update(&self.v);
280 self.v.copy_from_slice(&mac.finalize().into_bytes());
281 eprintln!(" After V update: V={}", hex::encode(&self.v[..8]));
282 }
283
284 let mut mac = HmacSha256::new_from_slice(&self.k).expect("HMAC init");
286 mac.update(&self.v);
287 self.v.copy_from_slice(&mac.finalize().into_bytes());
288
289 self.retry = true;
291
292 eprintln!("After: V={} (output)", hex::encode(&self.v[..8]));
293 eprintln!("Full output: {}", hex::encode(&self.v));
294
295 self.v
296 }
297}
298
299fn rangeproof_genrand(
308 sec: &mut [Scalar],
309 s: &mut [Scalar],
310 mut message: Option<&mut [u8]>,
311 rsizes: &[usize],
312 rings: usize,
313 nonce: &[u8],
314 commit_point: &AffinePoint,
315 genp: &AffinePoint,
316 proof_header: &[u8],
317) -> Result<bool> {
318 let mut seed = Vec::with_capacity(32 + 33 + 33 + proof_header.len());
321 seed.extend_from_slice(nonce);
322
323 let commit_serialized = serialize_point_custom(commit_point);
324 let genp_serialized = serialize_point_custom(genp);
325
326 eprintln!("\n=== HMAC-DRBG Seed Construction ===");
328 eprintln!("nonce ({} bytes): {}", nonce.len(), hex::encode(nonce));
329 eprintln!("commit_point: {:?}", commit_point);
330 eprintln!("commit_serialized ({} bytes): {}", commit_serialized.len(), hex::encode(&commit_serialized));
331 eprintln!("genp: {:?}", genp);
332 eprintln!("genp_serialized ({} bytes): {}", genp_serialized.len(), hex::encode(&genp_serialized));
333 eprintln!("proof_header ({} bytes): {}", proof_header.len(), hex::encode(proof_header));
334
335 seed.extend_from_slice(&commit_serialized);
336 seed.extend_from_slice(&genp_serialized);
337 seed.extend_from_slice(proof_header);
338
339 eprintln!("Total seed ({} bytes): {}", seed.len(), hex::encode(&seed));
340 eprintln!("=================================\n");
341
342 let mut drbg = HmacDrbg::new(&seed);
343 let mut acc = Scalar::ZERO;
344 let mut npub = 0;
345 let mut ret = true;
346
347 for i in 0..rings {
348 if i < rings - 1 {
349 let _ = drbg.generate();
351 eprintln!("Ring {}: Discarded first generate() call", i);
352
353 loop {
355 let bytes = drbg.generate();
356 eprintln!("Ring {}: Generated bytes: {}", i, hex::encode(&bytes));
357 let scalar_option = Option::<Scalar>::from(Scalar::from_repr(bytes.into()));
358 if let Some(scalar) = scalar_option {
359 if !bool::from(scalar.is_zero()) {
360 sec[i] = scalar;
361 acc += scalar;
362 eprintln!("Ring {}: Accepted scalar: {}", i, hex::encode(scalar.to_bytes()));
363 break;
364 }
365 }
366 eprintln!("Ring {}: Rejected (zero or overflow), retrying", i);
367 }
368 } else {
369 sec[i] = -acc;
371 eprintln!("Ring {} (last): sec = -acc = {}", i, hex::encode(sec[i].to_bytes()));
372 }
373
374 for j in 0..rsizes[i] {
376 let mut bytes = drbg.generate();
377
378 if let Some(ref mut msg) = message {
380 let offset = (i * 4 + j) * 32;
381 if offset + 32 <= msg.len() {
382 for b in 0..32 {
383 bytes[b] ^= msg[offset + b];
384 msg[offset + b] = bytes[b];
385 }
386 }
387 }
388
389 let scalar_option = Option::<Scalar>::from(Scalar::from_repr(bytes.into()));
391 if let Some(scalar) = scalar_option {
392 if bool::from(scalar.is_zero()) {
393 ret = false;
394 }
395 s[npub] = scalar;
396 } else {
397 ret = false;
398 }
399 npub += 1;
400 }
401 }
402
403 Ok(ret)
404}
405
406fn rangeproof_pub_expand(
412 pubs: &mut [ProjectivePoint],
413 exp: i32,
414 rsizes: &[usize],
415 rings: usize,
416 genp: &AffinePoint,
417) {
418 let mut base = ProjectivePoint::from(genp);
419 base = -base; for _ in 0..exp {
423 let tmp = base.double();
425 base = tmp.double();
426 base = base.double();
427 base += tmp;
428 }
429
430 let mut npub = 0;
431 for i in 0..rings {
432 for j in 1..rsizes[i] {
434 pubs[npub + j] = pubs[npub + j - 1] + base;
435 }
436
437 if i < rings - 1 {
439 base = base.double();
440 base = base.double();
441 }
442 npub += rsizes[i];
443 }
444}
445
446fn range_proveparams(
454 min_value: &mut u64,
455 exp: &mut i32,
456 min_bits: &mut i32,
457 value: u64,
458) -> Result<RangeProofParams> {
459 let rings;
460 let mut rsizes;
461 let mut secidx;
462 let mut scale: u64 = 1;
463 let mut mantissa: usize;
464 let mut npub: usize = 0;
465
466 if *min_value == u64::MAX {
468 *exp = -1;
469 }
470
471 if *exp >= 0 {
472 let mut v2: u64;
473
474 if (*min_value > 0 && value > i64::MAX as u64)
476 || (value > 0 && *min_value >= i64::MAX as u64)
477 {
478 return Err(VeilCryptoError::Other(
479 "Value or min_value too large (>= 2^63)".to_string(),
480 ));
481 }
482
483 let max_bits = if *min_value > 0 {
485 clz64(*min_value)
486 } else {
487 64
488 };
489
490 if *min_bits > max_bits as i32 {
491 *min_bits = max_bits as i32;
492 }
493
494 if *min_bits > 61 || value > i64::MAX as u64 {
496 *exp = 0;
497 }
498
499 let mut v = value.saturating_sub(*min_value);
501
502 v2 = if *min_bits > 0 {
504 u64::MAX >> (64 - *min_bits)
505 } else {
506 0
507 };
508
509 let mut i = 0;
510 while i < *exp as usize && v2 <= u64::MAX / 10 {
511 v /= 10;
512 v2 *= 10;
513 i += 1;
514 }
515 *exp = i as i32;
516
517 v2 = v;
518 for _ in 0..*exp {
519 v2 *= 10;
520 scale *= 10;
521 }
522
523 *min_value = value - v2;
525
526 mantissa = if v > 0 { 64 - clz64(v) } else { 1 };
528
529 if (*min_bits as usize) > mantissa {
530 mantissa = *min_bits as usize;
531 }
532
533 rings = (mantissa + 1) >> 1;
535 rsizes = Vec::with_capacity(rings);
536 secidx = Vec::with_capacity(rings);
537
538 for i in 0..rings {
539 let ring_size = if i < rings - 1 || (mantissa & 1) == 0 {
541 4
542 } else {
543 2
544 };
545 rsizes.push(ring_size);
546 npub += ring_size;
547
548 secidx.push(((v >> (i * 2)) & 3) as usize);
550 }
551
552 if v & !(u64::MAX >> (64 - mantissa)) != 0 {
554 return Err(VeilCryptoError::Other(
555 "Value encoding error: mantissa mismatch".to_string(),
556 ));
557 }
558
559 Ok(RangeProofParams {
560 rings,
561 rsizes,
562 secidx,
563 npub,
564 mantissa,
565 min_value: *min_value,
566 scale,
567 v,
568 })
569 } else {
570 *exp = 0;
572 *min_value = value;
573
574 Ok(RangeProofParams {
575 rings: 1,
576 rsizes: vec![1],
577 secidx: vec![0],
578 npub: 1, mantissa: 0,
580 min_value: value,
581 scale: 1,
582 v: 0,
583 })
584 }
585}
586
587pub fn rangeproof_sign(
598 commitment: &[u8],
599 value: u64,
600 blind: &[u8],
601 nonce: &[u8],
602 message: Option<&[u8]>,
603 min_value: u64,
604 exp: i32,
605 min_bits: i32,
606) -> Result<RangeProofSignResult> {
607 use sha2::{Sha256, Digest};
608
609 if commitment.len() != 33 {
611 return Err(VeilCryptoError::Other(
612 "commitment must be 33 bytes".to_string(),
613 ));
614 }
615 if blind.len() != 32 {
616 return Err(VeilCryptoError::InvalidSecretKey);
617 }
618 if nonce.len() != 32 && nonce.len() != 33 {
619 return Err(VeilCryptoError::Other(
620 "nonce must be 32 or 33 bytes".to_string(),
621 ));
622 }
623 if exp < -1 || exp > 18 {
624 return Err(VeilCryptoError::Other(
625 "exp must be in range [-1, 18]".to_string(),
626 ));
627 }
628 if min_bits < 0 || min_bits > 64 {
629 return Err(VeilCryptoError::Other(
630 "min_bits must be in range [0, 64]".to_string(),
631 ));
632 }
633 if min_value > value {
634 return Err(VeilCryptoError::Other(
635 "min_value cannot be greater than value".to_string(),
636 ));
637 }
638
639 let mut min_val_mut = min_value;
641 let mut exp_mut = exp;
642 let mut min_bits_mut = min_bits;
643
644 let params = range_proveparams(&mut min_val_mut, &mut exp_mut, &mut min_bits_mut, value)?;
645
646 let commit_point = crate::mlsag::pedersen_commitment_load(commitment)?;
649 let commit_affine = commit_point.to_affine();
650
651 let genp_proj = get_generator_h_point()?; let genp_affine = genp_proj.to_affine();
654
655 eprintln!("\n=== Generator H ===");
656 let genp_sec1 = serialize_point(&genp_affine);
657 let genp_custom = serialize_point_custom(&genp_affine);
658 eprintln!("SEC1 format: {}", hex::encode(&genp_sec1));
659 eprintln!("Custom format: {}", hex::encode(&genp_custom));
660
661 let mut proof = Vec::with_capacity(MAX_PROOF_SIZE);
665
666 let has_mantissa = params.rsizes[0] > 1;
668 let has_min_value = min_val_mut > 0;
669 let flags = (if has_mantissa { 64 | exp_mut as u8 } else { 0 })
670 | (if has_min_value { 32 } else { 0 });
671 proof.push(flags);
672
673 if has_mantissa {
675 proof.push((params.mantissa - 1) as u8);
676 }
677
678 if has_min_value {
680 for i in 0..8 {
681 proof.push((min_val_mut >> ((7 - i) * 8)) as u8);
682 }
683 }
684
685 let msg_len = message.as_ref().map(|m| m.len()).unwrap_or(0);
689
690 if msg_len > 0 && msg_len > 128 * (params.rings - 1) {
692 return Err(VeilCryptoError::Other(
693 format!("Message too large: {} bytes, max {} bytes",
694 msg_len, 128 * (params.rings - 1))
695 ));
696 }
697
698 let mut prep = vec![0u8; 4096];
700 if let Some(msg) = message {
701 prep[..msg.len()].copy_from_slice(msg);
702 }
703
704 if params.rsizes[params.rings - 1] > 1 {
707 let mut idx = params.rsizes[params.rings - 1] - 1;
708 if params.secidx[params.rings - 1] == idx {
710 idx -= 1;
711 }
712 let base_idx = ((params.rings - 1) * 4 + idx) * 32;
713
714 for i in 0..8 {
716 let byte = (params.v >> (56 - i * 8)) as u8;
717 prep[base_idx + 8 + i] = byte;
718 prep[base_idx + 16 + i] = byte;
719 prep[base_idx + 24 + i] = byte;
720 prep[base_idx + i] = 0;
721 }
722 prep[base_idx] = 128;
723 }
724
725 let mut sec = vec![Scalar::ZERO; params.rings];
729 let mut s = vec![Scalar::ZERO; params.npub];
730
731 let nonce_32 = if nonce.len() == 33 {
733 &nonce[..32]
734 } else {
735 nonce
736 };
737
738 let genrand_success = rangeproof_genrand(
739 &mut sec,
740 &mut s,
741 Some(&mut prep[..]),
742 ¶ms.rsizes,
743 params.rings,
744 nonce_32,
745 &commit_affine,
746 &genp_affine,
747 &proof,
748 )?;
749
750 if !genrand_success {
751 return Err(VeilCryptoError::Other(
752 "rangeproof_genrand failed: invalid scalar generated".to_string()
753 ));
754 }
755
756 let mut k = vec![Scalar::ZERO; params.rings];
761 for i in 0..params.rings {
762 let idx = i * 4 + params.secidx[i];
763 k[i] = s[idx];
764 s[idx] = Scalar::ZERO; }
766
767 let mut blind_bytes = [0u8; 32];
770 blind_bytes.copy_from_slice(blind);
771 let blind_scalar = Option::<Scalar>::from(Scalar::from_repr(blind_bytes.into()))
772 .ok_or_else(|| VeilCryptoError::InvalidSecretKey)?;
773
774 sec[params.rings - 1] += blind_scalar;
775
776 if bool::from(sec[params.rings - 1].is_zero()) {
777 return Err(VeilCryptoError::Other(
778 "Blinding factor resulted in zero secret".to_string()
779 ));
780 }
781
782 let signs_offset = proof.len();
786 let signs_len = (params.rings + 6) >> 3; for _ in 0..signs_len {
788 proof.push(0);
789 }
790
791 let mut sha256_m = Sha256::new();
795
796 sha256_m.update(&serialize_point_custom(&commit_affine));
798 sha256_m.update(&serialize_point_custom(&genp_affine));
799
800 sha256_m.update(&proof[..signs_offset]);
802
803 let mut pubs = vec![ProjectivePoint::IDENTITY; params.npub];
804 let mut npub_idx = 0;
805
806 eprintln!("\n=== Building Commitments ===");
807 eprintln!("scale={}, rings={}", params.scale, params.rings);
808 eprintln!("secidx={:?}", params.secidx);
809
810 for i in 0..params.rings {
811 let value_part = (params.secidx[i] as u64 * params.scale) << (i * 2);
813
814 if i == 0 {
815 eprintln!("\nRing 0 commitment details:");
816 eprintln!(" sec[0] = {}", hex::encode(sec[0].to_bytes()));
817 eprintln!(" secidx[0] = {}", params.secidx[0]);
818 eprintln!(" value_part = {}", value_part);
819 }
820
821 pubs[npub_idx] = pedersen_ecmult_point(&sec[i], value_part, &genp_proj)?;
822
823 if bool::from(pubs[npub_idx].is_identity()) {
824 return Err(VeilCryptoError::Other(
825 format!("Commitment at ring {} is infinity", i)
826 ));
827 }
828
829 if i < params.rings - 1 {
831 eprintln!("\n=== Ring {} commitment serialization ===", i);
832 eprintln!("npub_idx={}, pubs.len()={}", npub_idx, pubs.len());
833 eprintln!("pubs[{}] is_identity={}", npub_idx, bool::from(pubs[npub_idx].is_identity()));
834
835 let c_affine = pubs[npub_idx].to_affine();
836 let serialized = serialize_point_custom(&c_affine);
838 let quadness = serialized[0]; eprintln!("Ring {}: commitment quadness={:#04x}, value_part={}",
841 i, quadness, value_part);
842
843 sha256_m.update(&serialized);
846
847 let sign_byte_idx = i >> 3;
851 let sign_bit_idx = i & 7;
852 proof[signs_offset + sign_byte_idx] |= (quadness & 1) << sign_bit_idx;
853
854 eprintln!("Ring {}: quadness={:#04x}, storing bit {} at signs[{}]",
855 i, quadness, quadness & 1, i);
856
857 eprintln!("Ring {}: Writing x-coord at offset {} (proof.len()={}), x={}",
859 i, proof.len(), proof.len(), hex::encode(&serialized[1..9]));
860 proof.extend_from_slice(&serialized[1..]);
861 }
862
863 npub_idx += params.rsizes[i];
864 }
865
866 eprintln!("\n=== SIGN: PUBS BEFORE EXPANSION ===");
870 let mut npub_check = 0;
871 for i in 0..params.rings {
872 let affine = pubs[npub_check].to_affine();
873 let encoded = affine.to_encoded_point(true);
874 eprintln!(" pubs[{}] (ring {}): {}", npub_check, i, hex::encode(encoded.as_bytes()));
875 npub_check += params.rsizes[i];
876 }
877
878 rangeproof_pub_expand(&mut pubs, exp_mut, ¶ms.rsizes, params.rings, &genp_affine);
879
880 let message_hash = sha256_m.finalize();
884 eprintln!("\n=== SIGN: Message hash for Borromean ===");
885 eprintln!("message_hash: {}", hex::encode(&message_hash));
886
887 eprintln!("\n========== DEBUG: PUBS BEFORE BORROMEAN SIGN ==========");
888 eprintln!("Total pubs elements: {}", pubs.len());
889 for (idx, p) in pubs.iter().enumerate().take(3) {
890 let affine = p.to_affine();
891 let encoded = affine.to_encoded_point(true);
892 eprintln!(" pubs[{}]: {}", idx, hex::encode(encoded.as_bytes()));
893 }
894 eprintln!("=====================================================\n");
895
896 let pubs_bytes: Vec<[u8; 33]> = pubs.iter()
901 .map(|p| serialize_point(&p.to_affine()))
902 .collect();
903
904 let k_bytes: Vec<[u8; 32]> = k.iter()
905 .map(|scalar| {
906 let mut bytes = [0u8; 32];
907 bytes.copy_from_slice(&scalar.to_bytes());
908 bytes
909 })
910 .collect();
911
912 let sec_bytes: Vec<[u8; 32]> = sec.iter()
913 .map(|scalar| {
914 let mut bytes = [0u8; 32];
915 bytes.copy_from_slice(&scalar.to_bytes());
916 bytes
917 })
918 .collect();
919
920 let s_bytes: Vec<[u8; 32]> = s.iter()
921 .map(|scalar| {
922 let mut bytes = [0u8; 32];
923 bytes.copy_from_slice(&scalar.to_bytes());
924 bytes
925 })
926 .collect();
927
928 let (e0, s_result) = borromean_sign(
929 &pubs_bytes,
930 &k_bytes,
931 &sec_bytes,
932 &s_bytes,
933 ¶ms.rsizes,
934 ¶ms.secidx,
935 &message_hash,
936 )?;
937
938 proof.extend_from_slice(&e0);
943
944 for s_val in s_result {
946 proof.extend_from_slice(&s_val);
947 }
948
949 Ok(RangeProofSignResult {
951 proof: proof.clone(),
952 commitment: commitment.to_vec(),
953 blind: blind.to_vec(),
954 nonce: nonce.to_vec(),
955 })
956}
957
958fn parse_proof_header(proof: &[u8]) -> Result<(usize, i32, usize, u64, u64, u64)> {
962 let mut offset = 0usize;
963
964 if proof.len() < 65 {
966 return Err(VeilCryptoError::Other("Proof too short".to_string()));
967 }
968
969 if (proof[offset] & 128) != 0 {
971 return Err(VeilCryptoError::Other("Invalid proof flags".to_string()));
972 }
973
974 let has_nz_range = (proof[offset] & 64) != 0;
975 let has_min = (proof[offset] & 32) != 0;
976
977 let mut exp = -1i32;
978 let mut mantissa = 0usize;
979 let mut max_value = 0u64;
980
981 if has_nz_range {
982 exp = (proof[offset] & 31) as i32;
983 offset += 1;
984
985 if exp > 18 {
986 return Err(VeilCryptoError::Other("Invalid exponent".to_string()));
987 }
988
989 if offset >= proof.len() {
990 return Err(VeilCryptoError::Other("Proof truncated at mantissa".to_string()));
991 }
992
993 mantissa = proof[offset] as usize + 1;
994 if mantissa > 64 {
995 return Err(VeilCryptoError::Other("Invalid mantissa".to_string()));
996 }
997
998 max_value = u64::MAX >> (64 - mantissa);
999 }
1000
1001 offset += 1;
1002
1003 let mut scale = 1u64;
1005 for _ in 0..exp {
1006 if max_value > u64::MAX / 10 {
1007 return Err(VeilCryptoError::Other("Max value overflow".to_string()));
1008 }
1009 max_value = max_value.wrapping_mul(10);
1010 scale = scale.wrapping_mul(10);
1011 }
1012
1013 let mut min_value = 0u64;
1015 if has_min {
1016 if proof.len() - offset < 8 {
1017 return Err(VeilCryptoError::Other("Proof truncated at min_value".to_string()));
1018 }
1019
1020 for i in 0..8 {
1021 min_value = (min_value << 8) | (proof[offset + i] as u64);
1022 }
1023 offset += 8;
1024 }
1025
1026 if max_value > u64::MAX - min_value {
1028 return Err(VeilCryptoError::Other("Value range overflow".to_string()));
1029 }
1030 max_value += min_value;
1031
1032 Ok((offset, exp, mantissa, scale, min_value, max_value))
1033}
1034
1035pub fn rangeproof_verify(commitment: &[u8], proof: &[u8]) -> Result<RangeProofVerifyResult> {
1040 use sha2::{Sha256, Digest};
1041 use k256::elliptic_curve::sec1::FromEncodedPoint;
1042
1043 if commitment.len() != 33 {
1045 return Err(VeilCryptoError::Other("commitment must be 33 bytes".to_string()));
1046 }
1047
1048 let commit_point = crate::mlsag::pedersen_commitment_load(commitment)?;
1051 let commit_affine = commit_point.to_affine();
1052
1053 let genp_proj = get_generator_h_point()?; let genp_affine = genp_proj.to_affine();
1056
1057 let (mut offset, exp, mantissa, _scale, min_value, max_value) = parse_proof_header(proof)?;
1061 let _offset_post_header = offset;
1062
1063 let mut rings = 1usize;
1067 let mut rsizes = vec![1usize];
1068 let mut npub = 1usize;
1069
1070 if mantissa != 0 {
1071 rings = mantissa >> 1; rsizes = vec![4; rings];
1073 npub = rings << 2; if (mantissa & 1) != 0 {
1077 rsizes.push(2);
1078 npub += 2;
1079 rings += 1;
1080 }
1081 }
1082
1083 if rings > MAX_RINGS {
1084 return Err(VeilCryptoError::Other("Too many rings".to_string()));
1085 }
1086
1087 let signs_len = (rings + 6) >> 3; let required_len = offset + signs_len + 32 * (npub + rings - 1) + 32;
1092 if proof.len() < required_len {
1093 return Err(VeilCryptoError::Other(
1094 format!("Proof too short: {} < {}", proof.len(), required_len)
1095 ));
1096 }
1097
1098 let mut sha256_m = Sha256::new();
1102 sha256_m.update(&serialize_point_custom(&commit_affine));
1104 sha256_m.update(&serialize_point_custom(&genp_affine));
1105 sha256_m.update(&proof[..offset]);
1106
1107 let mut signs = vec![false; rings];
1111 for i in 0..(rings - 1) {
1112 let byte_idx = i >> 3;
1113 let bit_idx = i & 7;
1114 signs[i] = (proof[offset + byte_idx] & (1 << bit_idx)) != 0;
1115 }
1116
1117 eprintln!("\n=== VERIFY: Signs array ===");
1118 for i in 0..rings.min(4) {
1119 eprintln!(" signs[{}] = {}", i, signs[i]);
1120 }
1121
1122 offset += signs_len;
1123
1124 if ((rings - 1) & 7) != 0 {
1126 let last_used_bit = (rings - 1) & 7;
1127 let unused_mask = 0xffu8 << last_used_bit;
1128 if (proof[offset - 1] & unused_mask) != 0 {
1129 return Err(VeilCryptoError::Other("Invalid unused sign bits".to_string()));
1130 }
1131 }
1132
1133 let mut pubs = vec![ProjectivePoint::IDENTITY; npub];
1137 let mut accj = ProjectivePoint::IDENTITY;
1138
1139 if min_value > 0 {
1141 accj = genp_proj * Scalar::from(min_value);
1142 }
1143
1144 let mut npub_idx = 0usize;
1146 for i in 0..(rings - 1) {
1147 if offset + 32 > proof.len() {
1149 return Err(VeilCryptoError::Other("Proof truncated at commitment".to_string()));
1150 }
1151
1152 let mut x_bytes = [0u8; 32];
1153 x_bytes.copy_from_slice(&proof[offset..offset + 32]);
1154
1155 use k256::FieldElement;
1163 let x = FieldElement::from_bytes(&x_bytes.into())
1164 .into_option()
1165 .ok_or_else(|| VeilCryptoError::Other("Invalid x-coordinate".to_string()))?;
1166
1167 let x_cubed = x * x * x;
1169 let b = FieldElement::from(7u64);
1170 let y_squared = x_cubed + b;
1171
1172 let mut y = y_squared.sqrt()
1174 .into_option()
1175 .ok_or_else(|| VeilCryptoError::Other("Point not on curve".to_string()))?;
1176
1177 let y_has_sqrt = y.sqrt().is_some();
1183 let y_is_qr: bool = y_has_sqrt.into();
1184
1185 if !y_is_qr {
1188 y = -y;
1189 }
1190
1191 eprintln!("\n=== VERIFY: Ring {} sqrt selection ===", i);
1192 eprintln!(" x = {}", hex::encode(x.to_bytes()));
1193 eprintln!(" y (QR root) = {}", hex::encode(y.to_bytes()));
1194 eprintln!(" y_is_qr check = {}", y_is_qr);
1195
1196 let y_bytes = y.to_bytes();
1198 let mut point_bytes = [0u8; 65];
1199 point_bytes[0] = 0x04; point_bytes[1..33].copy_from_slice(&x_bytes);
1201 point_bytes[33..65].copy_from_slice(&y_bytes);
1202
1203 let encoded = k256::EncodedPoint::from_bytes(&point_bytes)
1204 .map_err(|_| VeilCryptoError::Other("Invalid point encoding".to_string()))?;
1205 let mut point_affine = AffinePoint::from_encoded_point(&encoded)
1206 .into_option()
1207 .ok_or_else(|| VeilCryptoError::Other("Invalid point".to_string()))?;
1208
1209 eprintln!("\n=== VERIFY: Ring {} reconstruction ===", i);
1213 eprintln!(" signs[{}] = {}", i, signs[i]);
1214 eprintln!(" Before negate: {}", hex::encode(point_affine.to_encoded_point(true).as_bytes()));
1215 if signs[i] {
1216 point_affine = -point_affine;
1217 }
1218 eprintln!(" After negate: {}", hex::encode(point_affine.to_encoded_point(true).as_bytes()));
1219
1220 sha256_m.update(&[if signs[i] { 1u8 } else { 0u8 }]);
1222 sha256_m.update(&x_bytes);
1223
1224 pubs[npub_idx] = ProjectivePoint::from(point_affine);
1225 accj += point_affine;
1226
1227 offset += 32;
1228 npub_idx += rsizes[i];
1229 }
1230
1231 accj = -accj;
1233 accj += commit_affine;
1234
1235 if bool::from(accj.is_identity()) {
1236 return Err(VeilCryptoError::Other("Last commitment is identity".to_string()));
1237 }
1238
1239 pubs[npub_idx] = accj;
1240
1241 eprintln!("\n=== VERIFY: PUBS BEFORE EXPANSION ===");
1245 let mut npub_check = 0;
1246 for i in 0..rings {
1247 let affine = pubs[npub_check].to_affine();
1248 let encoded = affine.to_encoded_point(true);
1249 eprintln!(" pubs[{}] (ring {}): {}", npub_check, i, hex::encode(encoded.as_bytes()));
1250 npub_check += rsizes[i];
1251 }
1252
1253 rangeproof_pub_expand(&mut pubs, exp, &rsizes, rings, &genp_affine);
1254
1255 eprintln!("\n========== DEBUG: PUBS BEFORE BORROMEAN VERIFY ==========");
1256 eprintln!("Total pubs elements: {}", pubs.len());
1257 for (idx, p) in pubs.iter().enumerate().take(3) {
1258 let affine = p.to_affine();
1259 let encoded = affine.to_encoded_point(true);
1260 eprintln!(" pubs[{}]: {}", idx, hex::encode(encoded.as_bytes()));
1261 }
1262 eprintln!("=====================================================\n");
1263
1264 if offset + 32 + npub * 32 > proof.len() {
1268 return Err(VeilCryptoError::Other("Proof truncated at signatures".to_string()));
1269 }
1270
1271 let mut e0 = [0u8; 32];
1272 e0.copy_from_slice(&proof[offset..offset + 32]);
1273 offset += 32;
1274
1275 let mut s_values = Vec::with_capacity(npub);
1276 for _ in 0..npub {
1277 let mut s_bytes = [0u8; 32];
1278 s_bytes.copy_from_slice(&proof[offset..offset + 32]);
1279 s_values.push(s_bytes);
1280 offset += 32;
1281 }
1282
1283 if offset != proof.len() {
1285 return Err(VeilCryptoError::Other("Extra data in proof".to_string()));
1286 }
1287
1288 let message_hash = sha256_m.finalize();
1292 eprintln!("\n=== VERIFY: Message hash for Borromean ===");
1293 eprintln!("message_hash: {}", hex::encode(&message_hash));
1294
1295 let pubs_bytes: Vec<[u8; 33]> = pubs.iter()
1300 .map(|p| serialize_point(&p.to_affine()))
1301 .collect();
1302
1303 use crate::borromean::borromean_verify;
1305 let verify_result = borromean_verify(
1306 &e0,
1307 &s_values,
1308 &pubs_bytes,
1309 &rsizes,
1310 &message_hash,
1311 )?;
1312
1313 if !verify_result {
1314 return Err(VeilCryptoError::Other("Borromean signature verification failed".to_string()));
1315 }
1316
1317 Ok(RangeProofVerifyResult {
1321 min_value,
1322 max_value,
1323 })
1324}
1325
1326fn rangeproof_recover_x(k: &Scalar, e: &Scalar, s: &Scalar) -> Scalar {
1338 let k_minus_s = *k - s;
1340 let e_inv = e.invert().unwrap(); k_minus_s * e_inv
1342}
1343
1344fn rangeproof_recover_k(x: &Scalar, e: &Scalar, s: &Scalar) -> Scalar {
1354 s + (x * e)
1356}
1357
1358fn xor_32(a: &mut [u8; 32], b: &[u8; 32]) {
1360 for i in 0..32 {
1361 a[i] ^= b[i];
1362 }
1363}
1364
1365pub fn rangeproof_rewind(
1374 nonce: &[u8],
1375 commitment: &[u8],
1376 proof: &[u8],
1377) -> Result<RangeProofRewindResult> {
1378 if nonce.len() != 32 {
1380 return Err(VeilCryptoError::Other("Nonce must be 32 bytes".to_string()));
1381 }
1382 if commitment.len() != 33 {
1383 return Err(VeilCryptoError::Other("Commitment must be 33 bytes".to_string()));
1384 }
1385 if proof.len() < 11 {
1386 return Err(VeilCryptoError::InvalidRangeProof);
1387 }
1388
1389 let (offset, exp, mantissa, scale, min_value, max_value) = parse_proof_header(proof)?;
1391 let offset_post_header = offset;
1392
1393 let mut rings = 1usize;
1395 let mut rsizes = vec![1usize];
1396
1397 if mantissa != 0 {
1398 rings = mantissa >> 1; rsizes = vec![4; rings];
1400
1401 if (mantissa & 1) != 0 {
1403 rsizes.push(2);
1404 rings += 1;
1405 }
1406 }
1407
1408 let genp = crate::pedersen::get_generator_h_point()?.to_affine();
1410 eprintln!("DEBUG rewind: Using Veil H generator (0x11 format)");
1411 eprintln!("DEBUG rewind: H generator point: {:?}", genp);
1412
1413 eprintln!("DEBUG rewind: commitment[0]={:#04x}, len={}", commitment[0], commitment.len());
1415 let commit_proj = crate::mlsag::pedersen_commitment_load(commitment)?;
1416 let commit_point = commit_proj.to_affine();
1417
1418 let npub_total = rsizes.iter().sum();
1420 let mut sec = vec![Scalar::ZERO; rings];
1421 let mut s_orig = vec![Scalar::ZERO; npub_total];
1422 let mut message_buf = vec![0u8; 4096]; let proof_header = &proof[0..offset_post_header];
1426 rangeproof_genrand(
1427 &mut sec,
1428 &mut s_orig,
1429 Some(&mut message_buf),
1430 &rsizes,
1431 rings,
1432 nonce,
1433 &commit_point,
1434 &genp,
1435 proof_header,
1436 )?;
1437
1438 let mut offset = offset_post_header;
1443
1444 let signs_len = (rings + 6) >> 3;
1446 let required_len = offset + signs_len + 32 * (npub_total + rings - 1) + 32;
1447
1448 eprintln!("DEBUG: offset_post_header={}, signs_len={}, rings={}, npub_total={}",
1449 offset_post_header, signs_len, rings, npub_total);
1450 if proof.len() < required_len {
1451 return Err(VeilCryptoError::Other(
1452 format!("Proof too short for rewind: {} < {}", proof.len(), required_len)
1453 ));
1454 }
1455
1456 let mut sha256_m = Sha256::new();
1458 sha256_m.update(&serialize_point_custom(&commit_point));
1459 sha256_m.update(&serialize_point_custom(&genp));
1460 sha256_m.update(&proof[..offset_post_header]);
1461
1462 let mut signs = vec![false; rings];
1464 for i in 0..(rings - 1) {
1465 let byte_idx = i >> 3;
1466 let bit_idx = i & 7;
1467 signs[i] = (proof[offset + byte_idx] & (1 << bit_idx)) != 0;
1468 }
1469 offset += signs_len;
1470
1471 let mut pubs = vec![ProjectivePoint::IDENTITY; npub_total];
1473 let mut accj = ProjectivePoint::IDENTITY;
1474
1475 if min_value > 0 {
1477 accj = ProjectivePoint::from(&genp) * Scalar::from(min_value);
1478 }
1479
1480 let mut npub_idx = 0usize;
1482 for i in 0..(rings - 1) {
1483 if offset + 32 > proof.len() {
1485 return Err(VeilCryptoError::Other("Proof truncated at commitment".to_string()));
1486 }
1487
1488 let mut x_bytes = [0u8; 32];
1489 x_bytes.copy_from_slice(&proof[offset..offset + 32]);
1490
1491 eprintln!("Ring {}: offset={}, signs[{}]={}, x_bytes={}",
1492 i, offset, i, signs[i], hex::encode(&x_bytes[..8]));
1493
1494 use k256::FieldElement;
1496 let x = FieldElement::from_bytes(&x_bytes.into())
1497 .into_option()
1498 .ok_or_else(|| VeilCryptoError::Other("Invalid x-coordinate".to_string()))?;
1499
1500 let x_cubed = x * x * x;
1502 let b = FieldElement::from(7u64);
1503 let y_squared = x_cubed + b;
1504
1505 let mut y = y_squared.sqrt()
1507 .into_option()
1508 .ok_or_else(|| VeilCryptoError::Other("Point not on curve".to_string()))?;
1509
1510 let y_has_sqrt = y.sqrt().is_some();
1512 let y_is_qr: bool = y_has_sqrt.into();
1513 if !y_is_qr {
1514 y = -y;
1515 }
1516
1517 let y_bytes = y.to_bytes();
1519 let mut point_bytes = [0u8; 65];
1520 point_bytes[0] = 0x04; point_bytes[1..33].copy_from_slice(&x_bytes);
1522 point_bytes[33..65].copy_from_slice(&y_bytes);
1523
1524 let point_encoded = k256::EncodedPoint::from_bytes(&point_bytes)
1525 .map_err(|_| VeilCryptoError::Other("Invalid point encoding".to_string()))?;
1526 let mut point_affine = AffinePoint::from_encoded_point(&point_encoded)
1527 .into_option()
1528 .ok_or_else(|| VeilCryptoError::Other(format!("Invalid commitment point at ring {}, offset {}", i, offset)))?;
1529
1530 if signs[i] {
1532 point_affine = -point_affine;
1533 }
1534
1535 eprintln!("Ring {}: Reconstructed point={}", i, hex::encode(point_affine.to_encoded_point(true).as_bytes()));
1536
1537 sha256_m.update(&[if signs[i] { 1u8 } else { 0u8 }]);
1539 sha256_m.update(&x_bytes);
1540
1541 pubs[npub_idx] = ProjectivePoint::from(point_affine);
1542 accj += point_affine;
1543
1544 offset += 32;
1545 npub_idx += rsizes[i];
1546 }
1547
1548 accj = -accj;
1550 accj += commit_point;
1551
1552 if bool::from(accj.is_identity()) {
1553 return Err(VeilCryptoError::Other("Last commitment is identity".to_string()));
1554 }
1555
1556 pubs[npub_idx] = accj;
1557
1558 rangeproof_pub_expand(&mut pubs, exp, &rsizes, rings, &genp);
1560
1561 if offset + 32 + npub_total * 32 > proof.len() {
1563 return Err(VeilCryptoError::Other("Proof truncated at signatures".to_string()));
1564 }
1565
1566 let mut e0 = [0u8; 32];
1567 e0.copy_from_slice(&proof[offset..offset + 32]);
1568 offset += 32;
1569
1570 let mut s = Vec::with_capacity(npub_total);
1571 for _ in 0..npub_total {
1572 let mut s_bytes = [0u8; 32];
1573 s_bytes.copy_from_slice(&proof[offset..offset + 32]);
1574 let s_scalar = Scalar::from_repr(s_bytes.into())
1575 .into_option()
1576 .ok_or_else(|| VeilCryptoError::Other("Invalid s value".to_string()))?;
1577 s.push(s_scalar);
1578 offset += 32;
1579 }
1580
1581 let value: u64;
1587 let skip1: usize;
1588 let skip2: usize;
1589
1590 if rings == 1 && rsizes[0] == 1 {
1591 value = min_value;
1593 skip1 = 0; skip2 = 0; } else {
1596 let npub_base = (rings - 1) * 4;
1598 let mut value_temp = u64::MAX;
1599 let mut skip1_temp = 0usize;
1600
1601 for j in 0..2 {
1602 let idx = npub_base + rsizes[rings - 1] - 1 - j;
1603 if idx >= s.len() {
1604 continue;
1605 }
1606
1607 let s_bytes = s[idx].to_bytes();
1609 let mut tmp: [u8; 32] = s_bytes.into();
1610
1611 let prep_slice: [u8; 32] = message_buf[idx * 32..(idx + 1) * 32]
1613 .try_into()
1614 .unwrap();
1615 xor_32(&mut tmp, &prep_slice);
1616
1617 if (tmp[0] & 0x80) != 0
1620 && tmp[16..24] == tmp[24..32]
1621 && tmp[8..16] == tmp[16..24]
1622 {
1623 value_temp = u64::from_be_bytes(tmp[24..32].try_into().unwrap());
1625 skip1_temp = rsizes[rings - 1] - 1 - j;
1626 break;
1627 }
1628 }
1629
1630 if value_temp == u64::MAX {
1631 return Err(VeilCryptoError::Other(
1632 "Could not extract value from proof".to_string()
1633 ));
1634 }
1635
1636 value = value_temp;
1637 skip1 = skip1_temp;
1638
1639 skip2 = ((value >> ((rings - 1) * 2)) & 3) as usize;
1641 if skip1 == skip2 {
1642 return Err(VeilCryptoError::Other(
1643 "Value is in wrong position".to_string()
1644 ));
1645 }
1646 }
1647
1648 let message_hash = sha256_m.finalize();
1650
1651 let mut ev = Vec::with_capacity(npub_total);
1653 let mut count = 0;
1654
1655 for (i, &ring_size) in rsizes.iter().enumerate() {
1656 let hash = crate::borromean::borromean_hash(&e0, &message_hash, i, 0);
1658 let mut en = Scalar::from_repr(hash.into())
1659 .into_option()
1660 .ok_or_else(|| VeilCryptoError::Other("Invalid scalar from hash".to_string()))?;
1661
1662 for j in 0..ring_size {
1664 ev.push(en);
1666
1667 let s_times_g = ProjectivePoint::mul_by_generator(&s[count]);
1669 let en_times_pub = pubs[count] * en;
1670 let r = (s_times_g + en_times_pub).to_affine();
1671
1672 let r_encoded = r.to_encoded_point(true);
1675 let r_serialized = r_encoded.as_bytes();
1676
1677 let hash = crate::borromean::borromean_hash(r_serialized, &message_hash, i, j + 1);
1679 en = Scalar::from_repr(hash.into())
1680 .into_option()
1681 .ok_or_else(|| VeilCryptoError::Other("Invalid scalar from hash".to_string()))?;
1682
1683 count += 1;
1684 }
1685 }
1686
1687 let last_ring_start = npub_total - rsizes[rings - 1];
1692 let blind_idx = last_ring_start + skip2;
1693
1694 let stmp = rangeproof_recover_x(&s_orig[blind_idx], &ev[blind_idx], &s[blind_idx]);
1696
1697 let sec_last_neg = -sec[rings - 1];
1699 let blind_scalar = stmp + sec_last_neg;
1700 let blind_bytes = blind_scalar.to_bytes();
1701
1702 let mut message_out = Vec::new();
1706 let mut npub_idx2 = 0;
1707
1708 for i in 0..rings {
1709 let idx = ((value >> (i * 2)) & 3) as usize;
1710 for j in 0..rsizes[i] {
1711 if npub_idx2 == blind_idx || npub_idx2 == last_ring_start + skip1 {
1712 npub_idx2 += 1;
1714 continue;
1715 }
1716
1717 let stmp = if idx == j {
1718 rangeproof_recover_k(&sec[i], &ev[npub_idx2], &s[npub_idx2])
1720 } else {
1721 s_orig[npub_idx2]
1723 };
1724
1725 let mut tmp: [u8; 32] = stmp.to_bytes().into();
1727 let prep_slice: [u8; 32] = message_buf[npub_idx2 * 32..(npub_idx2 + 1) * 32]
1728 .try_into()
1729 .unwrap();
1730 xor_32(&mut tmp, &prep_slice);
1731 message_out.extend_from_slice(&tmp);
1732 npub_idx2 += 1;
1733 }
1734 }
1735
1736 let actual_value = if rings == 1 && rsizes[0] == 1 {
1740 value } else {
1742 value * scale + min_value };
1744
1745 Ok(RangeProofRewindResult {
1746 blind: blind_bytes.to_vec(),
1747 value: actual_value,
1748 min_value,
1749 max_value,
1750 message: message_out,
1751 })
1752}
1753
1754pub fn rangeproof_info(proof: &[u8]) -> Result<RangeProofInfo> {
1768 let (_offset, exp, mantissa, _scale, min_value, max_value) = parse_proof_header(proof)?;
1770
1771 Ok(RangeProofInfo {
1772 exp,
1773 mantissa,
1774 min_value,
1775 max_value,
1776 })
1777}
1778
1779#[cfg(test)]
1784mod tests {
1785 use super::*;
1786 use hex;
1787
1788 #[test]
1789 fn test_hmac_drbg_simple() {
1790 let seed = b"test seed";
1792 let mut drbg = HmacDrbg::new(seed);
1793
1794 let output1 = drbg.generate();
1795 let output2 = drbg.generate();
1796
1797 eprintln!("Simple HMAC-DRBG test:");
1798 eprintln!("Seed: {:?}", std::str::from_utf8(seed));
1799 eprintln!("Output 1: {}", hex::encode(&output1));
1800 eprintln!("Output 2: {}", hex::encode(&output2));
1801 }
1802
1803 #[test]
1804 fn test_range_proveparams_basic() {
1805 let value = 12345u64;
1806 let mut min_value = 0u64;
1807 let mut exp = 2i32;
1808 let mut min_bits = 32i32;
1809
1810 let params = range_proveparams(&mut min_value, &mut exp, &mut min_bits, value).unwrap();
1811
1812 println!("Test value: {}", value);
1813 println!("Params: rings={}, npub={}, mantissa={}", params.rings, params.npub, params.mantissa);
1814 println!("rsizes: {:?}", params.rsizes);
1815 println!("secidx: {:?}", params.secidx);
1816 println!("scale: {}, v: {}", params.scale, params.v);
1817
1818 assert!(params.rings > 0);
1820 assert!(params.rings <= MAX_RINGS);
1821 assert_eq!(params.rsizes.len(), params.rings);
1822 assert_eq!(params.secidx.len(), params.rings);
1823 assert!(params.npub <= MAX_NPUB);
1824 }
1825
1826 #[test]
1827 fn test_range_proveparams_small_value() {
1828 let value = 100u64;
1829 let mut min_value = 0u64;
1830 let mut exp = 2i32;
1831 let mut min_bits = 32i32;
1832
1833 let params = range_proveparams(&mut min_value, &mut exp, &mut min_bits, value).unwrap();
1834
1835 assert!(params.rings > 0);
1836 assert!(params.npub > 0);
1837 }
1838
1839 #[test]
1840 fn test_range_proveparams_large_value() {
1841 let value = 1000000000u64;
1842 let mut min_value = 0u64;
1843 let mut exp = 2i32;
1844 let mut min_bits = 32i32;
1845
1846 let params = range_proveparams(&mut min_value, &mut exp, &mut min_bits, value).unwrap();
1847
1848 assert!(params.rings > 0);
1849 assert!(params.npub > 0);
1850 }
1851
1852 #[test]
1853 fn test_serialize_point() {
1854 use k256::ProjectivePoint;
1855
1856 let g = ProjectivePoint::GENERATOR;
1858 let g_affine = g.to_affine();
1859 let serialized = serialize_point(&g_affine);
1860
1861 assert_eq!(serialized.len(), 33);
1863
1864 assert!(serialized[0] == 0x02 || serialized[0] == 0x03);
1866 }
1867
1868 #[test]
1869 fn test_rangeproof_sign_basic() {
1870 use crate::pedersen::pedersen_commit;
1871
1872 let value = 12345u64;
1874 let blind_hex = "1111111111111111111111111111111111111111111111111111111111111111";
1875 let nonce_hex = "2222222222222222222222222222222222222222222222222222222222222222";
1876
1877 let blind = hex::decode(blind_hex).unwrap();
1878 let nonce = hex::decode(nonce_hex).unwrap();
1879
1880 let commitment_veil = pedersen_commit(value, &blind).unwrap();
1882
1883 let mut commitment = commitment_veil.clone();
1887 commitment[0] = if commitment_veil[0] == 0x08 { 0x02 } else { 0x03 };
1888
1889 println!("Testing rangeproof_sign with value={}", value);
1890 println!("Commitment (SEC1): {}", hex::encode(&commitment[..8]));
1891
1892 let result = rangeproof_sign(
1894 &commitment,
1895 value,
1896 &blind,
1897 &nonce,
1898 None, 0, 2, 32, );
1903
1904 match result {
1905 Ok(proof_result) => {
1906 println!("✅ Range proof generated successfully!");
1907 println!("Proof length: {} bytes", proof_result.proof.len());
1908 println!("First 32 bytes: {}", hex::encode(&proof_result.proof[..32.min(proof_result.proof.len())]));
1909 assert!(proof_result.proof.len() > 0);
1910 assert!(proof_result.proof.len() <= MAX_PROOF_SIZE);
1911 }
1912 Err(e) => {
1913 println!("❌ Range proof generation failed: {:?}", e);
1914 panic!("Range proof generation failed: {:?}", e);
1915 }
1916 }
1917 }
1918
1919 #[test]
1920 fn test_rangeproof_sign_large_value() {
1921 use crate::pedersen::pedersen_commit;
1922
1923 let value = 1000000000u64;
1925 let blind_hex = "1111111111111111111111111111111111111111111111111111111111111111";
1926 let nonce_hex = "2222222222222222222222222222222222222222222222222222222222222222";
1927
1928 let blind = hex::decode(blind_hex).unwrap();
1929 let nonce = hex::decode(nonce_hex).unwrap();
1930
1931 let commitment_veil = pedersen_commit(value, &blind).unwrap();
1932 let mut commitment = commitment_veil.clone();
1933 commitment[0] = if commitment_veil[0] == 0x08 { 0x02 } else { 0x03 };
1934
1935 println!("Testing rangeproof_sign with large value={}", value);
1936
1937 let result = rangeproof_sign(
1938 &commitment,
1939 value,
1940 &blind,
1941 &nonce,
1942 Some(b"large value test"),
1943 0,
1944 2,
1945 32,
1946 );
1947
1948 assert!(result.is_ok(), "Large value proof failed: {:?}", result.err());
1949 let proof_result = result.unwrap();
1950 println!("✅ Large value proof: {} bytes", proof_result.proof.len());
1951 assert!(proof_result.proof.len() <= MAX_PROOF_SIZE);
1952 }
1953
1954 #[test]
1955 fn test_rangeproof_sign_small_value() {
1956 use crate::pedersen::pedersen_commit;
1957
1958 let value = 100u64;
1960 let blind_hex = "abababababababababababababababababababababababababababababababab";
1961 let nonce_hex = "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd";
1962
1963 let blind = hex::decode(blind_hex).unwrap();
1964 let nonce = hex::decode(nonce_hex).unwrap();
1965
1966 let commitment_veil = pedersen_commit(value, &blind).unwrap();
1967 let mut commitment = commitment_veil.clone();
1968 commitment[0] = if commitment_veil[0] == 0x08 { 0x02 } else { 0x03 };
1969
1970 println!("Testing rangeproof_sign with small value={}", value);
1971
1972 let result = rangeproof_sign(
1973 &commitment,
1974 value,
1975 &blind,
1976 &nonce,
1977 Some(b"small"),
1978 0,
1979 2,
1980 32,
1981 );
1982
1983 assert!(result.is_ok(), "Small value proof failed: {:?}", result.err());
1984 let proof_result = result.unwrap();
1985 println!("✅ Small value proof: {} bytes", proof_result.proof.len());
1986 assert!(proof_result.proof.len() <= MAX_PROOF_SIZE);
1987 }
1988
1989 #[test]
1990 fn test_rangeproof_sign_medium_value() {
1991 use crate::pedersen::pedersen_commit;
1992
1993 let value = 500000u64;
1995 let blind_hex = "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd";
1996 let nonce_hex = "efefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefef";
1997
1998 let blind = hex::decode(blind_hex).unwrap();
1999 let nonce = hex::decode(nonce_hex).unwrap();
2000
2001 let commitment_veil = pedersen_commit(value, &blind).unwrap();
2002 let mut commitment = commitment_veil.clone();
2003 commitment[0] = if commitment_veil[0] == 0x08 { 0x02 } else { 0x03 };
2004
2005 println!("Testing rangeproof_sign with medium value={}", value);
2006
2007 let result = rangeproof_sign(
2008 &commitment,
2009 value,
2010 &blind,
2011 &nonce,
2012 Some(b"medium value"),
2013 0,
2014 2,
2015 32,
2016 );
2017
2018 assert!(result.is_ok(), "Medium value proof failed: {:?}", result.err());
2019 let proof_result = result.unwrap();
2020 println!("✅ Medium value proof: {} bytes", proof_result.proof.len());
2021 assert!(proof_result.proof.len() <= MAX_PROOF_SIZE);
2022 }
2023
2024 #[test]
2025 fn test_rangeproof_sign_minimal_value() {
2026 use crate::pedersen::pedersen_commit;
2027
2028 let value = 1u64;
2030 let blind_hex = "0101010101010101010101010101010101010101010101010101010101010101";
2031 let nonce_hex = "0202020202020202020202020202020202020202020202020202020202020202";
2032
2033 let blind = hex::decode(blind_hex).unwrap();
2034 let nonce = hex::decode(nonce_hex).unwrap();
2035
2036 let commitment_veil = pedersen_commit(value, &blind).unwrap();
2037 let mut commitment = commitment_veil.clone();
2038 commitment[0] = if commitment_veil[0] == 0x08 { 0x02 } else { 0x03 };
2039
2040 println!("Testing rangeproof_sign with minimal value={}", value);
2041
2042 let result = rangeproof_sign(
2043 &commitment,
2044 value,
2045 &blind,
2046 &nonce,
2047 Some(b"one"),
2048 0,
2049 2,
2050 32,
2051 );
2052
2053 assert!(result.is_ok(), "Minimal value proof failed: {:?}", result.err());
2054 let proof_result = result.unwrap();
2055 println!("✅ Minimal value proof: {} bytes", proof_result.proof.len());
2056 assert!(proof_result.proof.len() <= MAX_PROOF_SIZE);
2057 }
2058
2059 #[test]
2060 fn test_rangeproof_sign_no_message() {
2061 use crate::pedersen::pedersen_commit;
2062
2063 let value = 99999u64;
2065 let blind_hex = "9999999999999999999999999999999999999999999999999999999999999999";
2066 let nonce_hex = "8888888888888888888888888888888888888888888888888888888888888888";
2067
2068 let blind = hex::decode(blind_hex).unwrap();
2069 let nonce = hex::decode(nonce_hex).unwrap();
2070
2071 let commitment_veil = pedersen_commit(value, &blind).unwrap();
2072 let mut commitment = commitment_veil.clone();
2073 commitment[0] = if commitment_veil[0] == 0x08 { 0x02 } else { 0x03 };
2074
2075 println!("Testing rangeproof_sign without message, value={}", value);
2076
2077 let result = rangeproof_sign(
2078 &commitment,
2079 value,
2080 &blind,
2081 &nonce,
2082 None, 0,
2084 2,
2085 32,
2086 );
2087
2088 assert!(result.is_ok(), "No-message proof failed: {:?}", result.err());
2089 let proof_result = result.unwrap();
2090 println!("✅ No-message proof: {} bytes", proof_result.proof.len());
2091 assert!(proof_result.proof.len() <= MAX_PROOF_SIZE);
2092 }
2093
2094 #[test]
2095 fn test_rangeproof_verify_basic() {
2096 use crate::pedersen::pedersen_commit;
2097
2098 let value = 12345u64;
2100 let blind_hex = "1111111111111111111111111111111111111111111111111111111111111111";
2101 let nonce_hex = "2222222222222222222222222222222222222222222222222222222222222222";
2102
2103 let blind = hex::decode(blind_hex).unwrap();
2104 let nonce = hex::decode(nonce_hex).unwrap();
2105
2106 let commitment = pedersen_commit(value, &blind).unwrap();
2107 let sign_result = rangeproof_sign(
2110 &commitment,
2111 value,
2112 &blind,
2113 &nonce,
2114 None,
2115 0,
2116 2,
2117 32,
2118 ).unwrap();
2119
2120 println!("Verifying proof for value={}", value);
2121
2122 let verify_result = rangeproof_verify(&commitment, &sign_result.proof);
2124
2125 assert!(verify_result.is_ok(), "Verification failed: {:?}", verify_result.err());
2126 let result = verify_result.unwrap();
2127
2128 println!("✅ Proof verified! min={}, max={}", result.min_value, result.max_value);
2129
2130 assert!(value >= result.min_value, "value {} < min_value {}", value, result.min_value);
2132 assert!(value <= result.max_value, "value {} > max_value {}", value, result.max_value);
2133 }
2134
2135 #[test]
2136 fn test_rangeproof_verify_large_value() {
2137 use crate::pedersen::pedersen_commit;
2138
2139 let value = 1000000000u64;
2140 let blind_hex = "1111111111111111111111111111111111111111111111111111111111111111";
2141 let nonce_hex = "2222222222222222222222222222222222222222222222222222222222222222";
2142
2143 let blind = hex::decode(blind_hex).unwrap();
2144 let nonce = hex::decode(nonce_hex).unwrap();
2145
2146 let commitment = pedersen_commit(value, &blind).unwrap();
2147 let sign_result = rangeproof_sign(
2150 &commitment,
2151 value,
2152 &blind,
2153 &nonce,
2154 Some(b"large value test"),
2155 0,
2156 2,
2157 32,
2158 ).unwrap();
2159
2160 let verify_result = rangeproof_verify(&commitment, &sign_result.proof).unwrap();
2161
2162 println!("✅ Large value verified! min={}, max={}", verify_result.min_value, verify_result.max_value);
2163
2164 assert!(value >= verify_result.min_value, "value {} < min_value {}", value, verify_result.min_value);
2166 assert!(value <= verify_result.max_value, "value {} > max_value {}", value, verify_result.max_value);
2167 }
2168
2169 #[test]
2170 fn test_rangeproof_verify_minimal_value() {
2171 use crate::pedersen::pedersen_commit;
2172
2173 let value = 1u64;
2174 let blind_hex = "0101010101010101010101010101010101010101010101010101010101010101";
2175 let nonce_hex = "0202020202020202020202020202020202020202020202020202020202020202";
2176
2177 let blind = hex::decode(blind_hex).unwrap();
2178 let nonce = hex::decode(nonce_hex).unwrap();
2179
2180 let commitment = pedersen_commit(value, &blind).unwrap();
2181 let sign_result = rangeproof_sign(
2184 &commitment,
2185 value,
2186 &blind,
2187 &nonce,
2188 Some(b"one"),
2189 0,
2190 2,
2191 32,
2192 ).unwrap();
2193
2194 let verify_result = rangeproof_verify(&commitment, &sign_result.proof).unwrap();
2195
2196 println!("✅ Minimal value verified! min={}, max={}", verify_result.min_value, verify_result.max_value);
2197
2198 assert!(value >= verify_result.min_value);
2199 assert!(value <= verify_result.max_value);
2200 }
2201
2202 #[test]
2203 fn test_rangeproof_verify_tampered_proof_fails() {
2204 use crate::pedersen::pedersen_commit;
2205
2206 let value = 12345u64;
2207 let blind_hex = "1111111111111111111111111111111111111111111111111111111111111111";
2208 let nonce_hex = "2222222222222222222222222222222222222222222222222222222222222222";
2209
2210 let blind = hex::decode(blind_hex).unwrap();
2211 let nonce = hex::decode(nonce_hex).unwrap();
2212
2213 let commitment = pedersen_commit(value, &blind).unwrap();
2214 let sign_result = rangeproof_sign(
2217 &commitment,
2218 value,
2219 &blind,
2220 &nonce,
2221 None,
2222 0,
2223 2,
2224 32,
2225 ).unwrap();
2226
2227 let mut tampered_proof = sign_result.proof.clone();
2229 tampered_proof[100] ^= 0xff; let verify_result = rangeproof_verify(&commitment, &tampered_proof);
2232
2233 println!("Tampered proof result: {:?}", verify_result);
2234 assert!(verify_result.is_err(), "Tampered proof should fail verification!");
2235 }
2236
2237 #[test]
2238 fn test_rangeproof_verify_wrong_commitment_fails() {
2239 use crate::pedersen::pedersen_commit;
2240
2241 let value = 12345u64;
2242 let blind_hex = "1111111111111111111111111111111111111111111111111111111111111111";
2243 let nonce_hex = "2222222222222222222222222222222222222222222222222222222222222222";
2244
2245 let blind = hex::decode(blind_hex).unwrap();
2246 let nonce = hex::decode(nonce_hex).unwrap();
2247
2248 let commitment = pedersen_commit(value, &blind).unwrap();
2249 let sign_result = rangeproof_sign(
2252 &commitment,
2253 value,
2254 &blind,
2255 &nonce,
2256 None,
2257 0,
2258 2,
2259 32,
2260 ).unwrap();
2261
2262 let wrong_value = 99999u64;
2264 let wrong_commitment_veil = pedersen_commit(wrong_value, &blind).unwrap();
2265 let mut wrong_commitment = wrong_commitment_veil.clone();
2266 wrong_commitment[0] = if wrong_commitment_veil[0] == 0x08 { 0x02 } else { 0x03 };
2267
2268 let verify_result = rangeproof_verify(&wrong_commitment, &sign_result.proof);
2269
2270 println!("Wrong commitment result: {:?}", verify_result);
2271 assert!(verify_result.is_err(), "Wrong commitment should fail verification!");
2272 }
2273
2274 #[test]
2275 fn test_rangeproof_sign_max_value_32bit() {
2276 use crate::pedersen::pedersen_commit;
2277
2278 let value = 4294967295u64; let blind_hex = "fefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefefe";
2281 let nonce_hex = "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
2282
2283 let blind = hex::decode(blind_hex).unwrap();
2284 let nonce = hex::decode(nonce_hex).unwrap();
2285
2286 let commitment_veil = pedersen_commit(value, &blind).unwrap();
2287 let mut commitment = commitment_veil.clone();
2288 commitment[0] = if commitment_veil[0] == 0x08 { 0x02 } else { 0x03 };
2289
2290 println!("Testing rangeproof_sign with max 32-bit value={}", value);
2291
2292 let result = rangeproof_sign(
2293 &commitment,
2294 value,
2295 &blind,
2296 &nonce,
2297 Some(b"max32"),
2298 0,
2299 2,
2300 32,
2301 );
2302
2303 assert!(result.is_ok(), "Max 32-bit value proof failed: {:?}", result.err());
2304 let proof_result = result.unwrap();
2305 println!("✅ Max 32-bit value proof: {} bytes", proof_result.proof.len());
2306 assert!(proof_result.proof.len() <= MAX_PROOF_SIZE);
2307 }
2308
2309 #[test]
2310 fn test_rangeproof_sign_and_rewind() {
2311 use crate::pedersen::pedersen_commit;
2312
2313 println!("\n=== Test: Sign and Rewind ===");
2314
2315 let value = 12345u64;
2317 let blind = [0x42u8; 32];
2318 let nonce = [0x01u8; 32];
2319 let message = b"Hello, Veil!";
2320
2321 println!("Original value: {}", value);
2322 println!("Original blind: {}", hex::encode(&blind));
2323 println!("Nonce: {}", hex::encode(&nonce));
2324 println!("Message: {:?}", std::str::from_utf8(message).unwrap());
2325
2326 let commitment_veil = pedersen_commit(value, &blind).unwrap();
2328 let mut commitment = commitment_veil.clone();
2329 commitment[0] = if commitment_veil[0] == 0x08 { 0x02 } else { 0x03 };
2330
2331 let sign_result = rangeproof_sign(
2333 &commitment,
2334 value,
2335 &blind,
2336 &nonce,
2337 Some(message),
2338 0, 2, 32, );
2342
2343 assert!(sign_result.is_ok(), "Signing failed: {:?}", sign_result.err());
2344 let proof_data = sign_result.unwrap();
2345
2346 println!("Proof size: {} bytes", proof_data.proof.len());
2347 println!("Commitment: {}", hex::encode(&proof_data.commitment));
2348
2349 let rewind_result = rangeproof_rewind(
2351 &nonce,
2352 &proof_data.commitment,
2353 &proof_data.proof,
2354 );
2355
2356 assert!(rewind_result.is_ok(), "Rewind failed: {:?}", rewind_result.err());
2357 let rewind_data = rewind_result.unwrap();
2358
2359 println!("\n=== Rewind Results ===");
2360 println!("Recovered value: {}", rewind_data.value);
2361 println!("Recovered blind: {}", hex::encode(&rewind_data.blind));
2362 println!("Value range: [{}, {}]", rewind_data.min_value, rewind_data.max_value);
2363 println!("Message length: {} bytes", rewind_data.message.len());
2364
2365 assert_eq!(rewind_data.value, value, "Value mismatch!");
2367
2368 assert_eq!(rewind_data.blind.len(), 32, "Blind should be 32 bytes");
2370 assert_eq!(&rewind_data.blind[..], &blind[..], "Blind mismatch!");
2371
2372 assert!(
2374 rewind_data.value >= rewind_data.min_value,
2375 "Value {} below min {}",
2376 rewind_data.value,
2377 rewind_data.min_value
2378 );
2379 assert!(
2380 rewind_data.value <= rewind_data.max_value,
2381 "Value {} above max {}",
2382 rewind_data.value,
2383 rewind_data.max_value
2384 );
2385
2386 assert!(
2388 rewind_data.message.len() >= message.len(),
2389 "Message too short"
2390 );
2391
2392 let message_found = rewind_data.message
2394 .windows(message.len())
2395 .any(|window| window == message);
2396
2397 if !message_found {
2398 println!("Warning: Message not found in recovered data");
2399 println!("Expected: {:?}", std::str::from_utf8(message).unwrap());
2400 println!("Got first {} bytes: {:?}",
2401 message.len(),
2402 std::str::from_utf8(&rewind_data.message[..message.len().min(rewind_data.message.len())])
2403 );
2404 }
2405
2406 println!("✅ Sign and rewind test passed!");
2407 }
2408
2409 #[test]
2410 fn test_rangeproof_sign_and_rewind_no_message() {
2411 use crate::pedersen::pedersen_commit;
2412
2413 println!("\n=== Test: Sign and Rewind (No Message) ===");
2414
2415 let value = 99999u64;
2416 let blind = [0xAAu8; 32];
2417 let nonce = [0xBBu8; 32];
2418
2419 let commitment_veil = pedersen_commit(value, &blind).unwrap();
2421 let mut commitment = commitment_veil.clone();
2422 commitment[0] = if commitment_veil[0] == 0x08 { 0x02 } else { 0x03 };
2423
2424 let sign_result = rangeproof_sign(
2426 &commitment,
2427 value,
2428 &blind,
2429 &nonce,
2430 None, 0,
2432 2,
2433 32,
2434 );
2435
2436 assert!(sign_result.is_ok());
2437 let proof_data = sign_result.unwrap();
2438
2439 let rewind_result = rangeproof_rewind(
2441 &nonce,
2442 &proof_data.commitment,
2443 &proof_data.proof,
2444 );
2445
2446 assert!(rewind_result.is_ok());
2447 let rewind_data = rewind_result.unwrap();
2448
2449 assert_eq!(rewind_data.value, value);
2451 assert_eq!(&rewind_data.blind[..], &blind[..]);
2452
2453 println!("✅ No-message rewind test passed!");
2454 }
2455
2456 #[test]
2457 fn test_rangeproof_rewind_wrong_nonce_fails() {
2458 use crate::pedersen::pedersen_commit;
2459
2460 println!("\n=== Test: Rewind with Wrong Nonce ===");
2461
2462 let value = 54321u64;
2463 let blind = [0x77u8; 32];
2464 let nonce = [0x11u8; 32];
2465 let wrong_nonce = [0x22u8; 32];
2466
2467 let commitment_veil = pedersen_commit(value, &blind).unwrap();
2469 let mut commitment = commitment_veil.clone();
2470 commitment[0] = if commitment_veil[0] == 0x08 { 0x02 } else { 0x03 };
2471
2472 let sign_result = rangeproof_sign(
2474 &commitment,
2475 value,
2476 &blind,
2477 &nonce,
2478 Some(b"secret"),
2479 0,
2480 2,
2481 32,
2482 );
2483
2484 assert!(sign_result.is_ok());
2485 let proof_data = sign_result.unwrap();
2486
2487 let rewind_result = rangeproof_rewind(
2489 &wrong_nonce, &proof_data.commitment,
2491 &proof_data.proof,
2492 );
2493
2494 if let Ok(rewind_data) = rewind_result {
2496 assert_ne!(rewind_data.value, value, "Should not recover correct value with wrong nonce");
2498 assert_ne!(&rewind_data.blind[..], &blind[..], "Should not recover correct blind with wrong nonce");
2499 println!("✅ Wrong nonce produces wrong values as expected");
2500 } else {
2501 println!("✅ Wrong nonce causes rewind to fail (also acceptable)");
2502 }
2503 }
2504
2505 #[test]
2506 fn test_rangeproof_info() {
2507 use crate::pedersen::pedersen_commit;
2508
2509 println!("\n=== Test: Rangeproof Info ===");
2510
2511 let value = 54321u64;
2512 let blind = [0x88u8; 32];
2513 let nonce = [0x99u8; 32];
2514
2515 let commitment_veil = pedersen_commit(value, &blind).unwrap();
2517 let mut commitment = commitment_veil.clone();
2518 commitment[0] = if commitment_veil[0] == 0x08 { 0x02 } else { 0x03 };
2519
2520 let sign_result = rangeproof_sign(
2522 &commitment,
2523 value,
2524 &blind,
2525 &nonce,
2526 Some(b"info test"),
2527 0,
2528 2,
2529 32,
2530 );
2531
2532 assert!(sign_result.is_ok());
2533 let proof_data = sign_result.unwrap();
2534
2535 println!("Proof size: {} bytes", proof_data.proof.len());
2536
2537 let info_result = rangeproof_info(&proof_data.proof);
2539
2540 assert!(info_result.is_ok(), "Failed to extract info: {:?}", info_result.err());
2541 let info = info_result.unwrap();
2542
2543 println!("Proof info:");
2544 println!(" exp: {}", info.exp);
2545 println!(" mantissa: {}", info.mantissa);
2546 println!(" min_value: {}", info.min_value);
2547 println!(" max_value: {}", info.max_value);
2548
2549 assert!(
2551 value >= info.min_value,
2552 "Value {} below min {}",
2553 value,
2554 info.min_value
2555 );
2556 assert!(
2557 value <= info.max_value,
2558 "Value {} above max {}",
2559 value,
2560 info.max_value
2561 );
2562
2563 assert!(info.exp >= -1 && info.exp <= 18, "Invalid exp: {}", info.exp);
2565
2566 assert!(info.mantissa <= 64, "Invalid mantissa: {}", info.mantissa);
2568
2569 println!("✅ Rangeproof info test passed!");
2570 }
2571}