From 7242c371075c46c433f71e06731cb29b40b705af Mon Sep 17 00:00:00 2001 From: M Alghazwi Date: Thu, 3 Jul 2025 11:39:32 +0200 Subject: [PATCH] improve the kzg commitment --- src/kzg.rs | 263 ------------------------------------------- src/kzg10.rs | 233 ++++++++++++++++++++++++++++++++++++++ src/lib.rs | 5 +- src/matrix_commit.rs | 87 ++++++++++++++ src/traits.rs | 135 +++++++++++++++------- 5 files changed, 420 insertions(+), 303 deletions(-) delete mode 100644 src/kzg.rs create mode 100644 src/kzg10.rs create mode 100644 src/matrix_commit.rs diff --git a/src/kzg.rs b/src/kzg.rs deleted file mode 100644 index 9b94c1d..0000000 --- a/src/kzg.rs +++ /dev/null @@ -1,263 +0,0 @@ -use ark_ff::{One, PrimeField}; -use std::ops::{Add, AddAssign}; -use ark_poly::univariate::DensePolynomial; -use ark_poly::{EvaluationDomain, Evaluations, GeneralEvaluationDomain}; -use ark_poly_commit::{ - PolynomialCommitment, - LabeledPolynomial, - LabeledCommitment, - marlin_pc::{ Commitment, Randomness}, -}; -use ark_std::{end_timer, start_timer, test_rng}; -use anyhow::{anyhow, Result}; -use ark_bls12_381::Bls12_381; -use ark_crypto_primitives::sponge::CryptographicSponge; -use ark_ec::CurveGroup; -use ark_ec::pairing::Pairing; -use ark_poly_commit::marlin_pc::MarlinKZG10; -use ark_poly_commit::sonic_pc::UniversalParams; -use crate::byte_data::Params; -use crate::field_matrix::Matrix; -use crate::traits::{PolynomialCommitmentScheme, DataMatrix}; -use ark_crypto_primitives::sponge::poseidon::{PoseidonSponge, PoseidonConfig}; -use ark_poly_commit::kzg10::Proof; - -pub type E = Bls12_381; -pub type F = ::ScalarField; -pub type UniPoly381 = DensePolynomial; -pub type PCS = MarlinKZG10; - -pub struct KZGSRS{ - pub ploycommit_domain: GeneralEvaluationDomain, - pub pp: UniversalParams -} - -pub struct KZGPolyComm{ - params: Params, -} - -impl PolynomialCommitmentScheme for KZGPolyComm { - type Params = Params; - type Field = F; - type FieldMatrix = Matrix; - type SRS = KZGSRS; - type Commitment = KZGCommitments; - type Proof = Proof; - - fn new(params: Params) -> Self{ - Self{ - params, - } - } - - fn setup(&self) -> Result { - let rng = &mut test_rng(); - let pp = PCS::setup(self.params.m,None, rng)?; - let ploycommit_domain = EvaluationDomain::::new(self.params.m).ok_or(anyhow!("polycommit domain error"))?; - Ok(KZGSRS{ - ploycommit_domain, - pp, - }) - } - - fn commit(&self, srs: &Self::SRS, matrix: &Self::FieldMatrix) -> Result { - let rng = &mut test_rng(); - let degree = self.params.m; - let (ck, _vk) = PCS::trim(&srs.pp, degree, degree, Some(&[degree]))?; - let mut row_polynomials = vec![]; - let timer = start_timer!(|| format!("Poly evaluations and interpolation for {} rows", degree)); - for i in 0..matrix.params.n{ - let row = matrix.get_row(i)?.to_vec(); - let poly_evals = Evaluations::from_vec_and_domain(row, srs.ploycommit_domain.clone()); - let row_poly = poly_evals.interpolate(); - let label = String::from(format!("row_poly_{}", i)); - let labeled_poly = LabeledPolynomial::new( - label, - row_poly, - Some(degree), - Some(degree), - ); - row_polynomials.push(labeled_poly); - } - end_timer!(timer); - let timer = start_timer!(|| format!("KZG commitment for {} columns", degree)); - let (labeled_comms, states) = PCS::commit(&ck, &row_polynomials, Some(rng)).unwrap(); - end_timer!(timer); - Ok( - KZGCommitments::new(row_polynomials, labeled_comms, states) - ) - } - - fn open(comms: &Self::Commitment, srs: &Self::SRS, row: usize, col: usize) -> Result { - // point - let z = srs.ploycommit_domain.element(col); - - // trim the srs - let m = srs.ploycommit_domain.size(); - let (ck, _vk) = PCS::trim(&srs.pp, m, m, Some(&[m]))?; - - let (polys, comms_vec, states) = comms.get_refs(); - let poly = &polys[row]; - let commit = &comms_vec[row]; - let state = &states[row]; - - let mut sponge = test_sponge::(); - - let proof = PCS::open( - &ck, - std::iter::once(poly), - std::iter::once(commit), - &z, - &mut sponge, - &mut std::iter::once(state), - None, - )?; - - Ok(proof) - } - - fn batch_open(_: &Self::Commitment, _: &Self::SRS, _rows: Vec, _cols: Vec) -> Result> { - todo!() - } - - fn verify( - comms: &Self::Commitment, - srs: &Self::SRS, - row: usize, - col: usize, - value: F, - proof: &Self::Proof, - ) -> Result { - let z = srs.ploycommit_domain.element(col); - - let m = srs.ploycommit_domain.size(); - let (_ck, vk) = PCS::trim(&srs.pp, m, m, Some(&[m]))?; - - // get labeled commitment - let (_polys, commits, _states) = comms.get_refs(); - let commit = &commits[row]; - - let mut sponge = test_sponge::(); - Ok( PCS::check( - &vk, - std::iter::once(commit), - &z, - std::iter::once(value), - proof, - &mut sponge, - None, - )? ) - } - - fn batch_verify(_comms: &Self::Commitment, _srs: &Self::SRS, _rows: Vec, _cols: Vec, _values: Vec, _proof: &Vec) -> Result { - todo!() - } - - fn update_commitments( - srs: &KZGSRS, - comm: &mut KZGCommitments, - row_idx: usize, - old_row: &[F], - new_row: &[F], - ) -> Result<()> { - - let n = comm.poly.len(); - let domain = &srs.ploycommit_domain; - let m = domain.size(); - let (ck, _vk) = PCS::trim(&srs.pp, m, 2, Some(&[m]))?; - // Bounds and length checks - assert!(row_idx < n, "row_idx {} out of bounds ({} rows)", row_idx, n); - assert_eq!(old_row.len(), m, "old_row must have length {}", m); - assert_eq!(new_row.len(), m, "new_row must have length {}", m); - - let deltas: Vec = old_row.iter() - .zip(new_row.iter()) - .map(|(o, n)| *n - *o) - .collect(); - - let delta_poly: DensePolynomial = - Evaluations::from_vec_and_domain(deltas, domain.clone()) - .interpolate(); - - let label = format!("row_diff_{}", row_idx); - let labeled = LabeledPolynomial::new(label, delta_poly.clone(), Some(m), None); - let rng = &mut test_rng(); - let (diff_comms, diff_rands) = PCS::commit(&ck, std::iter::once(&labeled), Some(rng))?; - let diff_comm = &diff_comms[0]; - let diff_rand = &diff_rands[0]; - - let f_row = comm.poly[row_idx].polynomial_mut(); - f_row.add_assign(&delta_poly); - - let mut cmt = comm.comm[row_idx].commitment().clone(); - let main_patch = diff_comm.commitment().comm.0; - cmt.comm.0 = cmt.comm.0.add(&main_patch).into_affine(); - if let (Some(mut shifted), Some(diff_shifted)) = ( - cmt.shifted_comm.clone(), - diff_comm.commitment().shifted_comm.clone(), - ) { - shifted.0 = shifted.0.add(&diff_shifted.0).into_affine(); - cmt.shifted_comm = Some(shifted); - } - let lbl = comm.comm[row_idx].label().to_string(); - let dgb = comm.comm[row_idx].degree_bound(); - comm.comm[row_idx] = LabeledCommitment::new(lbl, cmt, dgb); - - comm.rand[row_idx].add_assign((F::one(), diff_rand)); - - Ok(()) - } -} - -fn test_sponge() -> PoseidonSponge { - let full_rounds = 8; - let partial_rounds = 31; - let alpha = 17; - - let mds = vec![ - vec![F::one(), F::zero(), F::one()], - vec![F::one(), F::one(), F::zero()], - vec![F::zero(), F::one(), F::one()], - ]; - - let mut v = Vec::new(); - let mut ark_rng = test_rng(); - - for _ in 0..(full_rounds + partial_rounds) { - let mut res = Vec::new(); - - for _ in 0..3 { - res.push(F::rand(&mut ark_rng)); - } - v.push(res); - } - let config = PoseidonConfig::new(full_rounds, partial_rounds, alpha, mds, v, 2, 1); - PoseidonSponge::new(&config) -} - -pub struct KZGCommitments{ - pub poly: Vec>, - pub comm: Vec>>, - pub rand: Vec>, -} - -impl KZGCommitments { - pub fn new( - poly: Vec>, - comm: Vec>>, - rand: Vec>, - ) -> Self{ - Self{ - poly, - comm, - rand, - } - } - pub fn get_refs(&self) ->( - &Vec>, - &Vec>>, - &Vec>, - ){ - (&self.poly, &self.comm, &self.rand) - } -} \ No newline at end of file diff --git a/src/kzg10.rs b/src/kzg10.rs new file mode 100644 index 0000000..c62f285 --- /dev/null +++ b/src/kzg10.rs @@ -0,0 +1,233 @@ +use ark_poly::univariate::DensePolynomial; +use ark_poly::{DenseUVPolynomial, EvaluationDomain, GeneralEvaluationDomain}; +use ark_poly_commit::{ + LabeledPolynomial, +}; +use ark_std::test_rng; +use anyhow::{anyhow, Result}; +use ark_bls12_381::Bls12_381; +use ark_ec::pairing::Pairing; +use ark_ec::{AffineRepr, CurveGroup}; +use ark_ff::{PrimeField, Zero}; +use crate::traits::{CommitOutputTrait, PolyCommScheme, SRSTrait}; +use ark_poly_commit::kzg10::{KZG10, Proof, UniversalParams, Powers, VerifierKey, Commitment, Randomness}; + +pub type E = Bls12_381; +pub type F = ::ScalarField; +pub type UniPoly381 = DensePolynomial; +pub type PCS = KZG10; + +pub struct KZG10SRS { + pub poly_domain: GeneralEvaluationDomain, + pub pp: UniversalParams +} + +impl SRSTrait for KZG10SRS{ + type PP = UniversalParams; + type Domain = GeneralEvaluationDomain; + + fn get_pp(&self) -> &Self::PP { + &self.pp + } + + fn get_domain(&self) -> &Self::Domain { + &self.poly_domain + } + + fn get_domain_element(&self, idx:usize) -> F { + self.poly_domain.element(idx) + } + + fn get_domain_size(&self) -> usize{ + self.poly_domain.size() + } +} + +pub struct KZG10PolyComm {} + +pub struct KZG10CommitOutput { + pub poly: LabeledPolynomial, + pub comm: Commitment, + pub rand: Randomness, +} + +impl KZG10CommitOutput { + pub fn new( + poly: LabeledPolynomial, + comm: Commitment, + rand: Randomness, + ) -> Self{ + Self{ + poly, + comm, + rand, + } + } +} + + +impl CommitOutputTrait for KZG10CommitOutput { + type Poly = LabeledPolynomial; + type Comm = Commitment; + type Rand = Randomness; + + fn get_poly(&self) -> &LabeledPolynomial{ + &self.poly + } + + fn get_comm(&self) -> &Commitment{ + &self.comm + } + + fn get_rand(&self) -> &Randomness{ + &self.rand + } +} + +impl KZG10PolyComm{ + fn commit_single(srs: &KZG10SRS, input: F, index: usize) -> Result> { + let power = &srs.pp.powers_of_g[index]; + + let c = power.mul_bigint(input.into_bigint()); + + Ok( + Commitment::(c.into_affine()) + ) + } +} + +impl PolyCommScheme for KZG10PolyComm { + type SRS = KZG10SRS; + type VK = VerifierKey; + type CommitOutput = KZG10CommitOutput; + type Comm = Commitment; + type Proof = Proof; + + fn setup(degree: usize) -> Result { + let rng = &mut test_rng(); + let pp = PCS::setup(degree,false, rng)?; + let poly_domain = EvaluationDomain::::new(degree).ok_or(anyhow!("polycommit domain error"))?; + Ok(KZG10SRS { + poly_domain, + pp, + }) + } + + fn commit(srs: &Self::SRS, input: Vec) -> Result { + let rng = &mut test_rng(); + let degree = srs.poly_domain.size(); + let powers = get_powers(&srs.pp, degree)?; + + // input are poly coeffs + let input_poly = DensePolynomial::::from_coefficients_vec(input); + let label = String::from("row_poly"); + let labeled_poly = LabeledPolynomial::new( + label, + input_poly, + Some(degree), + None, + ); + + let (comm, rand) = PCS::commit(&powers, &labeled_poly, None, Some(rng))?; + + Ok( + KZG10CommitOutput::new(labeled_poly, comm, rand) + ) + } + + fn update_commitment(srs: &Self::SRS, original_comm: &mut Self::CommitOutput, original_cell: F, new_cell:F, index: usize) -> Result<()> { + // check if there is difference, + if new_cell.clone() - original_cell.clone() == F::zero() { + return Ok(()) + } + + // commit to original and new cells + let original_cell_comm = Self::commit_single(srs, original_cell, index)?; + let new_cell_comm = Self::commit_single(srs, new_cell.clone(), index)?; + + // compute delta + let delta_comm = (new_cell_comm.0-original_cell_comm.0).into_affine(); + // update the commitment + let mut tmp = original_comm.comm.0.clone().into_group(); + tmp += &delta_comm; + original_comm.comm.0 = tmp.into_affine(); + // update the poly + let original_poly = original_comm.poly.polynomial_mut(); + original_poly.coeffs[index] = new_cell; + // no update to rand because we assume it is empty i.e. no hiding + Ok(()) + } + + fn open( + comm: &KZG10CommitOutput, + srs: &KZG10SRS, + point: F, + ) -> Result { + + // powers from the srs + let m = srs.poly_domain.size(); + let powers= get_powers(&srs.pp, m)?; + + // get row poly and rand + let poly = &comm.poly; + let rand = &comm.rand; + + let proof = PCS::open( + &powers, + poly, + point, + rand, + )?; + + Ok(proof) + } + + fn verify( + vk: &Self::VK, + comm: &Self::Comm, + point: F, + value: F, + proof: &Self::Proof, + ) -> Result { + + Ok( PCS::check( + &vk, + comm, + point, + value, + proof, + )? ) + } +} + +// --------------- Utils ----------------- + +/// get `degree` number of powers from the universal params +fn get_powers( + pp: &UniversalParams, + degree: usize, +) -> Result> { + let powers_of_g = pp.powers_of_g[..=degree].to_vec(); + let powers_of_gamma_g = (0..=degree) + .map(|i| pp.powers_of_gamma_g[&i]) + .collect(); + let powers = Powers { + powers_of_g: ark_std::borrow::Cow::Owned(powers_of_g), + powers_of_gamma_g: ark_std::borrow::Cow::Owned(powers_of_gamma_g), + }; + Ok(powers) +} + +pub fn get_vk( + pp: &UniversalParams, +) -> Result> { + let vk = VerifierKey { + g: pp.powers_of_g[0], + gamma_g: pp.powers_of_gamma_g[&0], + h: pp.h, + beta_h: pp.beta_h, + prepared_h: pp.prepared_h.clone(), + prepared_beta_h: pp.prepared_beta_h.clone(), + }; + Ok(vk) +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index dae47a6..736a9ce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ pub mod byte_data; -pub mod kzg; pub mod field_matrix; pub mod test; pub mod traits; -pub mod encoder; \ No newline at end of file +pub mod encoder; +pub mod kzg10; +pub mod matrix_commit; \ No newline at end of file diff --git a/src/matrix_commit.rs b/src/matrix_commit.rs new file mode 100644 index 0000000..d2c418f --- /dev/null +++ b/src/matrix_commit.rs @@ -0,0 +1,87 @@ +use std::marker::PhantomData; +use anyhow::Result; +use ark_ff::Field; +use crate::field_matrix::Matrix; +use crate::traits::{MatrixPolyCommScheme, DataMatrix, PolyCommScheme, SRSTrait, MatrixCommitOutput}; + + +pub struct MatrixPolyComm> { + phantom_data: PhantomData<(F,P)> +} + +impl> MatrixPolyCommScheme for MatrixPolyComm { + type FieldMatrix = Matrix; + + /// setup takes `m`=`number of columns` in the matrix + fn setup(m: usize) -> Result { + P::setup(m) + } + + fn commit(srs: &P::SRS, matrix: &Self::FieldMatrix) -> Result> { + + let mut row_comm_output = vec![]; + for i in 0..matrix.params.n{ + let row = matrix.get_row(i)?; + let output = P::commit(srs,row)?; + row_comm_output.push(output); + } + + Ok( + MatrixCommitOutput::new(row_comm_output) + ) + } + + /// updates the row commitments after updating/modifying columns + /// since the data DataMatrix should only allow column updates + /// and since we commit to rows + /// this means we update all row commitments that are affected by the data matrix update + fn update_commitments( + srs: &P::SRS, + comm: &mut MatrixCommitOutput, + col_idx: usize, + old_col: &[F], + new_col: &[F], + ) -> Result<()> { + // check input is consistent + assert_eq!(old_col.len(), new_col.len(), "col sizes don't match"); + assert_eq!(srs.get_domain_size(), new_col.len(), "domain size is incorrect"); + + // loop through all new_col elements to see if there is an update at each cell + // if there is, then update the commitment + for r in 0..new_col.len(){ + let original_cell: F = old_col[r].clone(); + let new_cell: F = new_col[r].clone(); + P::update_commitment(srs, &mut comm.comm_output[r], original_cell, new_cell, col_idx)?; + } + + Ok(()) + } + + fn open(comm: &MatrixCommitOutput, srs: &P::SRS, row: usize, col: usize) -> Result { + // the point we want to open + let point = srs.get_domain_element(col); + + let proof = P::open(&comm.comm_output[row], srs, point)?; + + Ok(proof) + } + + fn verify( + vk: &P::VK, + comm: &P::Comm, + point: F, + value: F, + proof: &P::Proof, + ) -> Result { + + Ok( P::verify( + &vk, + comm, + point, + value, + proof, + )? ) + } + +} + diff --git a/src/traits.rs b/src/traits.rs index 3c3bea2..bb9e3ce 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,3 +1,4 @@ +use std::marker::PhantomData; use anyhow::Result; use crate::byte_data::Params; @@ -26,51 +27,109 @@ pub trait Encoder{ fn reconstruct(params: Params, matrix_opts: &mut Vec>>) -> Result<()>; } +pub trait CommitOutputTrait { + type Poly; + type Comm; + type Rand; + + fn get_poly(&self) -> &Self::Poly; + + fn get_comm(&self) -> &Self::Comm; + + fn get_rand(&self) -> &Self::Rand; +} + +pub trait SRSTrait{ + // public/universal params + type PP; + // domain type + type Domain; + + fn get_pp(&self) -> &Self::PP; + fn get_domain(&self) -> &Self::Domain; + fn get_domain_element(&self, idx: usize) -> F; + fn get_domain_size(&self) -> usize; +} + /// Polynomial Commitment scheme (e.g. KZG) trait -pub trait PolynomialCommitmentScheme{ - type Params; - type Field; - type FieldMatrix; - type SRS; - type Commitment; +pub trait PolyCommScheme{ + type SRS: SRSTrait; + type VK; + type CommitOutput: CommitOutputTrait; + type Comm; type Proof; - fn new(_params: Self::Params) -> Self; - fn setup(&self) -> Result; - fn commit(&self, _srs: &Self::SRS, _matrix:&Self::FieldMatrix) -> Result; - fn update_commitments( - srs: &Self::SRS, - comm: &mut Self::Commitment, - row_idx: usize, - old_row: &[Self::Field], - new_row: &[Self::Field], - ) -> Result<()>; + fn setup(degree: usize) -> Result; + fn commit(srs: &Self::SRS, input:Vec) -> Result; + fn update_commitment(srs: &Self::SRS, original_comm: &mut Self::CommitOutput, original_cell: F, new_cell:F, index: usize) -> Result<()>; fn open( - _: &Self::Commitment, - _: &Self::SRS, - _row: usize, - _col: usize, + comm: &Self::CommitOutput, + srs: &Self::SRS, + point: F ) -> Result; - fn batch_open( - _: &Self::Commitment, - _: &Self::SRS, - _rows: Vec, - _cols: Vec, - ) -> Result>; fn verify( - comms: &Self::Commitment, - srs: &Self::SRS, - row: usize, - col: usize, - value: Self::Field, + vk: &Self::VK, + comm: &Self::Comm, + point: F, + value: F, proof: &Self::Proof, ) -> Result; - fn batch_verify( - comms: &Self::Commitment, - srs: &Self::SRS, - rows: Vec, - cols: Vec, - values: Vec, - proof: &Vec, +} + +/// Polynomial Commitment scheme for a field Matrix +/// it commits to the rows of the Matrix +/// and allows updating the row commitments +pub trait MatrixPolyCommScheme>{ + type FieldMatrix: DataMatrix; + + fn setup(m: usize) -> Result; + fn commit(srs: &P::SRS, matrix:&Self::FieldMatrix) -> Result>; + fn update_commitments( + srs: &P::SRS, + comm: &mut MatrixCommitOutput, + col_idx: usize, + old_col: &[F], + new_col: &[F], + ) -> Result<()>; + fn open( + comm: &MatrixCommitOutput, + srs: &P::SRS, + row: usize, + col: usize, + ) -> Result; + fn verify( + vk: &P::VK, + comm: &P::Comm, + point: F, + value: F, + proof: &P::Proof, ) -> Result; +} + +pub struct MatrixCommitOutput> { + pub comm_output: Vec, + phantom_data: PhantomData +} + +impl> MatrixCommitOutput { + pub fn new( + comm_output: Vec + ) -> Self{ + Self{ + comm_output, + phantom_data:PhantomData::default(), + } + } + + pub fn get_poly(&self, idx: usize) -> &::Poly{ + &self.comm_output[idx].get_poly() + } + + pub fn get_comm(&self, idx: usize) -> &::Comm{ + &self.comm_output[idx].get_comm() + } + + pub fn get_rand(&self, idx: usize) -> &::Rand{ + &self.comm_output[idx].get_rand() + } } \ No newline at end of file