si-commitment-scheme 0.1.0

Pedersen commitments, Blake3-based polynomial commitments, batch opening/verification with binding and hiding properties
Documentation
//! Blake3-based polynomial commitments.
//!
//! A simple polynomial commitment scheme using Blake3 to commit to
//! coefficient vectors, with evaluation proofs.

use blake3::Hasher;
use std::vec::Vec;

/// A polynomial represented by its coefficients (degree d has d+1 coefficients).
#[derive(Debug, Clone, PartialEq)]
pub struct Polynomial {
    /// Coefficients from lowest degree to highest.
    pub coeffs: Vec<u64>,
}

impl Polynomial {
    /// Create a new polynomial from coefficients.
    pub fn new(coeffs: Vec<u64>) -> Self {
        Self { coeffs }
    }

    /// Create the zero polynomial.
    pub fn zero() -> Self {
        Self { coeffs: vec![0] }
    }

    /// Evaluate the polynomial at a given point.
    pub fn evaluate(&self, x: u64) -> u64 {
        let mut result = 0u64;
        let mut power = 1u64;
        for &coeff in &self.coeffs {
            result = result.wrapping_add(coeff.wrapping_mul(power));
            power = power.wrapping_mul(x);
        }
        result
    }

    /// Degree of the polynomial.
    pub fn degree(&self) -> usize {
        if self.coeffs.is_empty() {
            return 0;
        }
        let mut deg = self.coeffs.len() - 1;
        while deg > 0 && self.coeffs[deg] == 0 {
            deg -= 1;
        }
        deg
    }

    /// Number of coefficients.
    pub fn len(&self) -> usize {
        self.coeffs.len()
    }

    /// Whether the polynomial is empty.
    pub fn is_empty(&self) -> bool {
        self.coeffs.is_empty()
    }
}

/// A Blake3-based polynomial commitment.
#[derive(Debug, Clone)]
pub struct PolynomialCommitment {
    /// The commitment hash (32 bytes).
    pub hash: [u8; 32],
}

impl PolynomialCommitment {
    /// Commit to a polynomial by hashing its coefficients.
    pub fn commit(poly: &Polynomial) -> Self {
        let mut hasher = Hasher::new();
        for &coeff in &poly.coeffs {
            hasher.update(&coeff.to_le_bytes());
        }
        let hash = *hasher.finalize().as_bytes();
        Self { hash }
    }

    /// Create an evaluation proof: hash of (commitment, x, y).
    pub fn create_eval_proof(&self, x: u64, y: u64) -> EvalProof {
        let mut hasher = Hasher::new();
        hasher.update(&self.hash);
        hasher.update(&x.to_le_bytes());
        hasher.update(&y.to_le_bytes());
        let proof_hash = *hasher.finalize().as_bytes();
        EvalProof {
            x,
            y,
            proof: proof_hash,
        }
    }

    /// Verify an evaluation proof.
    pub fn verify_eval(&self, proof: &EvalProof, poly: &Polynomial) -> bool {
        let expected_y = poly.evaluate(proof.x);
        if expected_y != proof.y {
            return false;
        }
        let expected_proof = self.create_eval_proof(proof.x, proof.y);
        expected_proof.proof == proof.proof
    }
}

/// An evaluation proof for a polynomial commitment.
#[derive(Debug, Clone)]
pub struct EvalProof {
    /// The evaluation point.
    pub x: u64,
    /// The claimed evaluation result.
    pub y: u64,
    /// The proof hash.
    pub proof: [u8; 32],
}

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

    #[test]
    fn polynomial_evaluation() {
        // p(x) = 3 + 2x + x^2
        let poly = Polynomial::new(vec![3, 2, 1]);
        assert_eq!(poly.evaluate(0), 3);
        assert_eq!(poly.evaluate(1), 6);
        assert_eq!(poly.evaluate(2), 11);
    }

    #[test]
    fn polynomial_degree() {
        let poly = Polynomial::new(vec![3, 2, 1]);
        assert_eq!(poly.degree(), 2);
        let zero = Polynomial::zero();
        assert_eq!(zero.degree(), 0);
    }

    #[test]
    fn commit_deterministic() {
        let poly = Polynomial::new(vec![1, 2, 3]);
        let c1 = PolynomialCommitment::commit(&poly);
        let c2 = PolynomialCommitment::commit(&poly);
        assert_eq!(c1.hash, c2.hash);
    }

    #[test]
    fn commit_different_polys() {
        let p1 = Polynomial::new(vec![1, 2, 3]);
        let p2 = Polynomial::new(vec![3, 2, 1]);
        let c1 = PolynomialCommitment::commit(&p1);
        let c2 = PolynomialCommitment::commit(&p2);
        assert_ne!(c1.hash, c2.hash);
    }

    #[test]
    fn eval_proof_verifies() {
        let poly = Polynomial::new(vec![3, 2, 1]);
        let comm = PolynomialCommitment::commit(&poly);
        let y = poly.evaluate(5);
        let proof = comm.create_eval_proof(5, y);
        assert!(comm.verify_eval(&proof, &poly));
    }

    #[test]
    fn eval_proof_wrong_y_fails() {
        let poly = Polynomial::new(vec![3, 2, 1]);
        let comm = PolynomialCommitment::commit(&poly);
        let proof = comm.create_eval_proof(5, 999);
        assert!(!comm.verify_eval(&proof, &poly));
    }
}