si-commitment-scheme 0.1.0

Pedersen commitments, Blake3-based polynomial commitments, batch opening/verification with binding and hiding properties
Documentation
//! Statistical hiding property verification.
//!
//! The hiding property ensures that a commitment reveals nothing about
//! the committed value. For Pedersen commitments with proper randomness,
//! the commitment distribution is uniform regardless of the value.

use crate::pedersen::{PedersenCommitment, PedersenParams};
use curve25519_dalek::scalar::Scalar;

/// Verifier for the hiding property of commitments.
#[derive(Debug, Clone)]
pub struct HidingVerifier {
    /// Number of commitment samples per value.
    pub samples: usize,
}

impl HidingVerifier {
    /// Create a new verifier with the given sample count.
    pub fn new(samples: usize) -> Self {
        Self { samples }
    }

    /// Test that commitments to the same value produce different outputs.
    ///
    /// This confirms that randomness is being used properly.
    pub fn test_unique_commitments(&self, params: &PedersenParams, value: u64) -> bool {
        let scalar = Scalar::from(value);
        let mut seen = std::collections::HashSet::new();
        for _ in 0..self.samples {
            let (comm, _) = PedersenCommitment::commit(params, &scalar);
            let bytes = comm.compressed.to_bytes();
            if seen.contains(&bytes) {
                return false;
            }
            seen.insert(bytes);
        }
        seen.len() == self.samples
    }

    /// Test that commitments to different values are indistinguishable.
    ///
    /// Verifies that the commitment set for value A overlaps with the
    /// commitment set for value B at most minimally (statistical check).
    pub fn test_indistinguishability(
        &self,
        params: &PedersenParams,
        value_a: u64,
        value_b: u64,
    ) -> bool {
        let scalar_a = Scalar::from(value_a);
        let scalar_b = Scalar::from(value_b);
        let mut set_a = std::collections::HashSet::new();
        for _ in 0..self.samples {
            let (comm, _) = PedersenCommitment::commit(params, &scalar_a);
            set_a.insert(comm.compressed.to_bytes());
        }
        // No commitment to B should match any commitment to A
        for _ in 0..self.samples {
            let (comm, _) = PedersenCommitment::commit(params, &scalar_b);
            if set_a.contains(&comm.compressed.to_bytes()) {
                return false;
            }
        }
        true
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn unique_commitments() {
        let params = PedersenParams::default();
        let verifier = HidingVerifier::new(50);
        assert!(verifier.test_unique_commitments(&params, 42));
    }

    #[test]
    fn indistinguishability() {
        let params = PedersenParams::default();
        let verifier = HidingVerifier::new(50);
        assert!(verifier.test_indistinguishability(&params, 0, 1));
    }

    #[test]
    fn hiding_different_values() {
        let params = PedersenParams::default();
        let verifier = HidingVerifier::new(30);
        assert!(verifier.test_indistinguishability(&params, 100, 200));
    }
}