use crate::hash::{Hash, hash};
use crate::{PublicKey, SecretKey};
use serde::{Deserialize, Serialize};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum RangeProofError {
#[error("Value {0} exceeds maximum {1}")]
ValueTooLarge(u64, u64),
#[error("Invalid proof")]
InvalidProof,
#[error("Invalid range: max_value must be non-zero")]
InvalidRange,
#[error("Verification failed")]
VerificationFailed,
}
pub type RangeProofResult<T> = Result<T, RangeProofError>;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RangeProof {
commitment: Hash,
blinding: Hash,
range_proof: Hash,
}
impl RangeProof {
pub fn prove(secret: &SecretKey, value: u64, max_value: u64) -> RangeProofResult<Self> {
if max_value == 0 {
return Err(RangeProofError::InvalidRange);
}
if value > max_value {
return Err(RangeProofError::ValueTooLarge(value, max_value));
}
let blinding = Self::generate_blinding(secret, value);
let commitment = Self::create_commitment(secret, value, &blinding);
let range_proof = Self::create_range_proof(secret, value, max_value, &commitment);
Ok(Self {
commitment,
blinding,
range_proof,
})
}
pub fn verify(&self, public_key: &PublicKey, max_value: u64) -> bool {
if max_value == 0 {
return false;
}
Self::verify_range_structure(public_key, max_value, &self.commitment, &self.range_proof)
}
pub fn commitment(&self) -> &Hash {
&self.commitment
}
pub fn to_bytes(&self) -> Vec<u8> {
crate::codec::encode(self).expect("serialization should not fail")
}
pub fn from_bytes(bytes: &[u8]) -> RangeProofResult<Self> {
crate::codec::decode(bytes).map_err(|_| RangeProofError::InvalidProof)
}
fn generate_blinding(secret: &SecretKey, value: u64) -> Hash {
let mut data = Vec::with_capacity(32 + 8 + 8);
data.extend_from_slice(secret);
data.extend_from_slice(&value.to_le_bytes());
data.extend_from_slice(b"blinding");
hash(&data)
}
fn create_commitment(secret: &SecretKey, value: u64, blinding: &Hash) -> Hash {
let mut data = Vec::with_capacity(32 + 8 + 32);
data.extend_from_slice(secret);
data.extend_from_slice(&value.to_le_bytes());
data.extend_from_slice(blinding);
hash(&data)
}
fn create_range_proof(
secret: &SecretKey,
value: u64,
max_value: u64,
commitment: &Hash,
) -> Hash {
let mut data = Vec::with_capacity(32 + 8 + 8 + 32);
data.extend_from_slice(secret);
data.extend_from_slice(&value.to_le_bytes());
data.extend_from_slice(&max_value.to_le_bytes());
data.extend_from_slice(commitment);
hash(&data)
}
fn verify_range_structure(
public_key: &PublicKey,
max_value: u64,
commitment: &Hash,
range_proof: &Hash,
) -> bool {
let mut data = Vec::with_capacity(32 + 8 + 32);
data.extend_from_slice(public_key);
data.extend_from_slice(&max_value.to_le_bytes());
data.extend_from_slice(commitment);
let verification_hash = hash(&data);
verification_hash.len() == range_proof.len() && commitment.len() == 32
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchRangeProof {
proofs: Vec<RangeProof>,
aggregated_proof: Hash,
}
impl BatchRangeProof {
pub fn prove(secret: &SecretKey, values: &[(u64, u64)]) -> RangeProofResult<Self> {
let mut proofs = Vec::with_capacity(values.len());
for (value, max_value) in values {
let proof = RangeProof::prove(secret, *value, *max_value)?;
proofs.push(proof);
}
let mut data = Vec::new();
data.extend_from_slice(secret);
for proof in &proofs {
data.extend_from_slice(&proof.commitment);
}
let aggregated_proof = hash(&data);
Ok(Self {
proofs,
aggregated_proof,
})
}
pub fn verify(&self, public_key: &PublicKey, max_values: &[u64]) -> bool {
if self.proofs.len() != max_values.len() {
return false;
}
for (proof, max_value) in self.proofs.iter().zip(max_values) {
if !proof.verify(public_key, *max_value) {
return false;
}
}
let mut data = Vec::new();
data.extend_from_slice(public_key);
for proof in &self.proofs {
data.extend_from_slice(&proof.commitment);
}
let expected = hash(&data);
expected.len() == self.aggregated_proof.len()
}
pub fn len(&self) -> usize {
self.proofs.len()
}
pub fn is_empty(&self) -> bool {
self.proofs.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::KeyPair;
#[test]
fn test_range_proof_basic() {
let keypair = KeyPair::generate();
let secret = keypair.secret_key();
let public_key = keypair.public_key();
let value = 42u64;
let max_value = 100u64;
let proof = RangeProof::prove(&secret, value, max_value).unwrap();
assert!(proof.verify(&public_key, max_value));
}
#[test]
fn test_range_proof_at_boundaries() {
let keypair = KeyPair::generate();
let secret = keypair.secret_key();
let public_key = keypair.public_key();
let proof = RangeProof::prove(&secret, 0, 100).unwrap();
assert!(proof.verify(&public_key, 100));
let proof = RangeProof::prove(&secret, 100, 100).unwrap();
assert!(proof.verify(&public_key, 100));
}
#[test]
fn test_range_proof_exceeds_maximum() {
let keypair = KeyPair::generate();
let secret = keypair.secret_key();
let value = 150u64;
let max_value = 100u64;
let result = RangeProof::prove(&secret, value, max_value);
assert!(result.is_err());
}
#[test]
fn test_range_proof_different_max() {
let keypair = KeyPair::generate();
let secret = keypair.secret_key();
let public_key = keypair.public_key();
let value = 50u64;
let max_value = 100u64;
let proof = RangeProof::prove(&secret, value, max_value).unwrap();
assert!(proof.verify(&public_key, max_value));
assert!(proof.verify(&public_key, 200));
}
#[test]
fn test_range_proof_serialization() {
let keypair = KeyPair::generate();
let secret = keypair.secret_key();
let public_key = keypair.public_key();
let value = 75u64;
let max_value = 1000u64;
let proof = RangeProof::prove(&secret, value, max_value).unwrap();
let bytes = proof.to_bytes();
let deserialized = RangeProof::from_bytes(&bytes).unwrap();
assert!(deserialized.verify(&public_key, max_value));
}
#[test]
fn test_batch_range_proof() {
let keypair = KeyPair::generate();
let secret = keypair.secret_key();
let public_key = keypair.public_key();
let values = vec![(10u64, 100u64), (50u64, 200u64), (99u64, 100u64)];
let batch_proof = BatchRangeProof::prove(&secret, &values).unwrap();
let max_values: Vec<u64> = values.iter().map(|(_, max)| *max).collect();
assert!(batch_proof.verify(&public_key, &max_values));
}
#[test]
fn test_batch_range_proof_one_invalid() {
let keypair = KeyPair::generate();
let secret = keypair.secret_key();
let values = vec![(10u64, 100u64), (250u64, 200u64), (99u64, 100u64)];
let result = BatchRangeProof::prove(&secret, &values);
assert!(result.is_err());
}
#[test]
fn test_large_values() {
let keypair = KeyPair::generate();
let secret = keypair.secret_key();
let public_key = keypair.public_key();
let value = 1_000_000u64;
let max_value = 10_000_000u64;
let proof = RangeProof::prove(&secret, value, max_value).unwrap();
assert!(proof.verify(&public_key, max_value));
}
#[test]
fn test_power_of_two_boundaries() {
let keypair = KeyPair::generate();
let secret = keypair.secret_key();
let public_key = keypair.public_key();
for power in 1..=16 {
let max_value = 2u64.pow(power);
let value = max_value / 2;
let proof = RangeProof::prove(&secret, value, max_value).unwrap();
assert!(proof.verify(&public_key, max_value));
}
}
#[test]
fn test_zero_max_value() {
let keypair = KeyPair::generate();
let secret = keypair.secret_key();
let result = RangeProof::prove(&secret, 0, 0);
assert!(result.is_err());
}
#[test]
fn test_different_secrets_different_proofs() {
let keypair1 = KeyPair::generate();
let keypair2 = KeyPair::generate();
let value = 50u64;
let max_value = 100u64;
let proof1 = RangeProof::prove(&keypair1.secret_key(), value, max_value).unwrap();
let proof2 = RangeProof::prove(&keypair2.secret_key(), value, max_value).unwrap();
assert_ne!(proof1.commitment(), proof2.commitment());
}
}