use blake3::Hasher;
use std::vec::Vec;
#[derive(Debug, Clone, PartialEq)]
pub struct Polynomial {
pub coeffs: Vec<u64>,
}
impl Polynomial {
pub fn new(coeffs: Vec<u64>) -> Self {
Self { coeffs }
}
pub fn zero() -> Self {
Self { coeffs: vec![0] }
}
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
}
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
}
pub fn len(&self) -> usize {
self.coeffs.len()
}
pub fn is_empty(&self) -> bool {
self.coeffs.is_empty()
}
}
#[derive(Debug, Clone)]
pub struct PolynomialCommitment {
pub hash: [u8; 32],
}
impl PolynomialCommitment {
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 }
}
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,
}
}
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
}
}
#[derive(Debug, Clone)]
pub struct EvalProof {
pub x: u64,
pub y: u64,
pub proof: [u8; 32],
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn polynomial_evaluation() {
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));
}
}