improve the kzg commitment

This commit is contained in:
M Alghazwi 2025-07-03 11:39:32 +02:00
parent 6daf47889f
commit 7242c37107
5 changed files with 420 additions and 303 deletions

View File

@ -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 = <E as Pairing>::ScalarField;
pub type UniPoly381 = DensePolynomial<F>;
pub type PCS = MarlinKZG10<E, UniPoly381>;
pub struct KZGSRS{
pub ploycommit_domain: GeneralEvaluationDomain<F>,
pub pp: UniversalParams<E>
}
pub struct KZGPolyComm{
params: Params,
}
impl PolynomialCommitmentScheme for KZGPolyComm {
type Params = Params;
type Field = F;
type FieldMatrix<F> = Matrix<Self::Field>;
type SRS = KZGSRS;
type Commitment = KZGCommitments;
type Proof = Proof<E>;
fn new(params: Params) -> Self{
Self{
params,
}
}
fn setup(&self) -> Result<Self::SRS> {
let rng = &mut test_rng();
let pp = PCS::setup(self.params.m,None, rng)?;
let ploycommit_domain = EvaluationDomain::<F>::new(self.params.m).ok_or(anyhow!("polycommit domain error"))?;
Ok(KZGSRS{
ploycommit_domain,
pp,
})
}
fn commit(&self, srs: &Self::SRS, matrix: &Self::FieldMatrix<F>) -> Result<Self::Commitment> {
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<Self::Proof> {
// 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::<F>();
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<usize>, _cols: Vec<usize>) -> Result<Vec<Self::Proof>> {
todo!()
}
fn verify(
comms: &Self::Commitment,
srs: &Self::SRS,
row: usize,
col: usize,
value: F,
proof: &Self::Proof,
) -> Result<bool> {
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::<F>();
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<usize>, _cols: Vec<usize>, _values: Vec<F>, _proof: &Vec<Self::Proof>) -> Result<bool> {
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<F> = old_row.iter()
.zip(new_row.iter())
.map(|(o, n)| *n - *o)
.collect();
let delta_poly: DensePolynomial<F> =
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<F: PrimeField>() -> PoseidonSponge<F> {
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<LabeledPolynomial<F, UniPoly381>>,
pub comm: Vec<LabeledCommitment<Commitment<E>>>,
pub rand: Vec<Randomness<F, UniPoly381>>,
}
impl KZGCommitments {
pub fn new(
poly: Vec<LabeledPolynomial<F, UniPoly381>>,
comm: Vec<LabeledCommitment<Commitment<E>>>,
rand: Vec<Randomness<F, UniPoly381>>,
) -> Self{
Self{
poly,
comm,
rand,
}
}
pub fn get_refs(&self) ->(
&Vec<LabeledPolynomial<F, UniPoly381>>,
&Vec<LabeledCommitment<Commitment<E>>>,
&Vec<Randomness<F, UniPoly381>>,
){
(&self.poly, &self.comm, &self.rand)
}
}

233
src/kzg10.rs Normal file
View File

@ -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 = <E as Pairing>::ScalarField;
pub type UniPoly381 = DensePolynomial<F>;
pub type PCS = KZG10<E, UniPoly381>;
pub struct KZG10SRS {
pub poly_domain: GeneralEvaluationDomain<F>,
pub pp: UniversalParams<E>
}
impl SRSTrait<F> for KZG10SRS{
type PP = UniversalParams<E>;
type Domain = GeneralEvaluationDomain<F>;
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<F, UniPoly381>,
pub comm: Commitment<E>,
pub rand: Randomness<F, UniPoly381>,
}
impl KZG10CommitOutput {
pub fn new(
poly: LabeledPolynomial<F, UniPoly381>,
comm: Commitment<E>,
rand: Randomness<F, UniPoly381>,
) -> Self{
Self{
poly,
comm,
rand,
}
}
}
impl CommitOutputTrait for KZG10CommitOutput {
type Poly = LabeledPolynomial<F, UniPoly381>;
type Comm = Commitment<E>;
type Rand = Randomness<F, UniPoly381>;
fn get_poly(&self) -> &LabeledPolynomial<F, UniPoly381>{
&self.poly
}
fn get_comm(&self) -> &Commitment<E>{
&self.comm
}
fn get_rand(&self) -> &Randomness<F, UniPoly381>{
&self.rand
}
}
impl KZG10PolyComm{
fn commit_single(srs: &KZG10SRS, input: F, index: usize) -> Result<Commitment<E>> {
let power = &srs.pp.powers_of_g[index];
let c = power.mul_bigint(input.into_bigint());
Ok(
Commitment::<E>(c.into_affine())
)
}
}
impl PolyCommScheme<F> for KZG10PolyComm {
type SRS = KZG10SRS;
type VK = VerifierKey<E>;
type CommitOutput = KZG10CommitOutput;
type Comm = Commitment<E>;
type Proof = Proof<E>;
fn setup(degree: usize) -> Result<Self::SRS> {
let rng = &mut test_rng();
let pp = PCS::setup(degree,false, rng)?;
let poly_domain = EvaluationDomain::<F>::new(degree).ok_or(anyhow!("polycommit domain error"))?;
Ok(KZG10SRS {
poly_domain,
pp,
})
}
fn commit(srs: &Self::SRS, input: Vec<F>) -> Result<Self::CommitOutput> {
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::<F>::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<Self::Proof> {
// 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<bool> {
Ok( PCS::check(
&vk,
comm,
point,
value,
proof,
)? )
}
}
// --------------- Utils -----------------
/// get `degree` number of powers from the universal params
fn get_powers(
pp: &UniversalParams<E>,
degree: usize,
) -> Result<Powers<E>> {
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<E>,
) -> Result<VerifierKey<E>> {
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)
}

View File

@ -1,6 +1,7 @@
pub mod byte_data;
pub mod kzg;
pub mod field_matrix;
pub mod test;
pub mod traits;
pub mod encoder;
pub mod kzg10;
pub mod matrix_commit;

87
src/matrix_commit.rs Normal file
View File

@ -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<F, P: PolyCommScheme<F>> {
phantom_data: PhantomData<(F,P)>
}
impl<F: Field + Clone, P: PolyCommScheme<F>> MatrixPolyCommScheme<F, P> for MatrixPolyComm<F, P> {
type FieldMatrix = Matrix<F>;
/// setup takes `m`=`number of columns` in the matrix
fn setup(m: usize) -> Result<P::SRS> {
P::setup(m)
}
fn commit(srs: &P::SRS, matrix: &Self::FieldMatrix) -> Result<MatrixCommitOutput<F, P>> {
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<F, P>,
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<F, P>, srs: &P::SRS, row: usize, col: usize) -> Result<P::Proof> {
// 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<bool> {
Ok( P::verify(
&vk,
comm,
point,
value,
proof,
)? )
}
}

View File

@ -1,3 +1,4 @@
use std::marker::PhantomData;
use anyhow::Result;
use crate::byte_data::Params;
@ -26,51 +27,109 @@ pub trait Encoder<T>{
fn reconstruct(params: Params, matrix_opts: &mut Vec<Option<Vec<T>>>) -> 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<F>{
// 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<F>;
type SRS;
type Commitment;
pub trait PolyCommScheme<F>{
type SRS: SRSTrait<F>;
type VK;
type CommitOutput: CommitOutputTrait;
type Comm;
type Proof;
fn new(_params: Self::Params) -> Self;
fn setup(&self) -> Result<Self::SRS>;
fn commit(&self, _srs: &Self::SRS, _matrix:&Self::FieldMatrix<Self::Field>) -> Result<Self::Commitment>;
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<Self::SRS>;
fn commit(srs: &Self::SRS, input:Vec<F>) -> Result<Self::CommitOutput>;
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<Self::Proof>;
fn batch_open(
_: &Self::Commitment,
_: &Self::SRS,
_rows: Vec<usize>,
_cols: Vec<usize>,
) -> Result<Vec<Self::Proof>>;
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<bool>;
fn batch_verify(
comms: &Self::Commitment,
srs: &Self::SRS,
rows: Vec<usize>,
cols: Vec<usize>,
values: Vec<Self::Field>,
proof: &Vec<Self::Proof>,
}
/// Polynomial Commitment scheme for a field Matrix
/// it commits to the rows of the Matrix
/// and allows updating the row commitments
pub trait MatrixPolyCommScheme<F, P:PolyCommScheme<F>>{
type FieldMatrix: DataMatrix<F>;
fn setup(m: usize) -> Result<P::SRS>;
fn commit(srs: &P::SRS, matrix:&Self::FieldMatrix) -> Result<MatrixCommitOutput<F, P>>;
fn update_commitments(
srs: &P::SRS,
comm: &mut MatrixCommitOutput<F, P>,
col_idx: usize,
old_col: &[F],
new_col: &[F],
) -> Result<()>;
fn open(
comm: &MatrixCommitOutput<F, P>,
srs: &P::SRS,
row: usize,
col: usize,
) -> Result<P::Proof>;
fn verify(
vk: &P::VK,
comm: &P::Comm,
point: F,
value: F,
proof: &P::Proof,
) -> Result<bool>;
}
pub struct MatrixCommitOutput<F, P: PolyCommScheme<F>> {
pub comm_output: Vec<P::CommitOutput>,
phantom_data: PhantomData<F>
}
impl<F, P: PolyCommScheme<F>> MatrixCommitOutput<F, P> {
pub fn new(
comm_output: Vec<P::CommitOutput>
) -> Self{
Self{
comm_output,
phantom_data:PhantomData::default(),
}
}
pub fn get_poly(&self, idx: usize) -> &<P::CommitOutput as CommitOutputTrait>::Poly{
&self.comm_output[idx].get_poly()
}
pub fn get_comm(&self, idx: usize) -> &<P::CommitOutput as CommitOutputTrait>::Comm{
&self.comm_output[idx].get_comm()
}
pub fn get_rand(&self, idx: usize) -> &<P::CommitOutput as CommitOutputTrait>::Rand{
&self.comm_output[idx].get_rand()
}
}