impl column commitment.

This commit is contained in:
M Alghazwi 2025-06-18 21:02:17 +02:00
parent 07f8c0a5e7
commit c314dacf08
5 changed files with 1371 additions and 0 deletions

1034
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -7,3 +7,14 @@ version = "1.0.0"
edition = "2024"
[dependencies]
rand = { version = "0.9.0", features = [ "std", "std_rng" ] }
itertools = "0.14.0"
clap = { version = "4.5.27", features = ["derive"] }
ark-std = "0.5.0"
ark-ff = "0.5.0"
ark-ec = "0.5.0"
ark-bls12-381 = "0.5.0"
ark-bn254 = "0.5.0"
ark-poly = "0.5.0"
ark-poly-commit = "0.5.0"
anyhow = "1.0.95"

117
src/dynamic_data.rs Normal file
View File

@ -0,0 +1,117 @@
use ark_bls12_381::Bls12_381;
use ark_ec::pairing::Pairing;
use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, Evaluations, EvaluationDomain, GeneralEvaluationDomain};
use ark_poly_commit::{Polynomial, marlin_pc::MarlinKZG10, LabeledPolynomial, PolynomialCommitment, QuerySet, LabeledCommitment};
use ark_poly_commit::marlin_pc::{Commitment, Randomness,};
use anyhow::{anyhow, Result};
use ark_poly_commit::sonic_pc::UniversalParams;
use ark_std::{end_timer, start_timer, test_rng};
use crate::matrix::Matrix;
type E = Bls12_381;
type F = <E as Pairing>::ScalarField;
type UniPoly381 = DensePolynomial<F>;
type PCS = MarlinKZG10<E, UniPoly381>;
// struct for the dynamic data scheme,
// contains the params and functions needed for the dynamic data scheme
pub struct DynamicData{
n: usize, // the row size of the data matrix - un-coded
k: usize, // the row size of the erasure coded data matrix
m: usize, // the column size of the matrix
ploycommit_domain: GeneralEvaluationDomain<F>,
encoding_domain: GeneralEvaluationDomain<F>,
pp: UniversalParams<Bls12_381>
}
impl DynamicData {
// setup the dynamic data scheme
pub fn setup(n: usize, k:usize, m:usize) -> Result<Self>{
let rng = &mut test_rng();
let pp = PCS::setup(m,None, rng)?;
let ploycommit_domain = EvaluationDomain::<F>::new(m).ok_or(anyhow!("polycommit domain error"))?;
let encoding_domain = EvaluationDomain::<F>::new(n).ok_or(anyhow!("encoding domain error"))?;
Ok(Self{
n,
k,
m,
ploycommit_domain,
encoding_domain,
pp,
})
}
pub fn commit_columns(&self, matrix: Matrix<F>) -> Result<(
Vec<LabeledPolynomial<F, UniPoly381>>,
Vec<LabeledCommitment<Commitment<E>>>,
Vec<Randomness<F, UniPoly381>>,
)>{
let rng = &mut test_rng();
let degree = self.m;
let (ck, vk) = PCS::trim(&self.pp, degree, 2, Some(&[degree])).unwrap();
let mut col_polynomials = vec![];
let timer = start_timer!(|| format!("Poly evaluations and interpolation for {} columns", degree));
for i in 0..matrix.cols(){
let poly_evals = Evaluations::from_vec_and_domain(matrix.column(i), self.ploycommit_domain.clone());
let col_poly = poly_evals.interpolate();
let label = String::from(format!("column_poly_{}", i));
let labeled_poly = LabeledPolynomial::new(
label,
col_poly,
Some(degree),
Some(2),
);
col_polynomials.push(labeled_poly);
}
let timer = start_timer!(|| format!("KZG commitment for {} columns", degree));
let (labeled_comms, states) = PCS::commit(&ck, &col_polynomials, Some(rng)).unwrap();
end_timer!(timer);
Ok((col_polynomials,labeled_comms, states))
}
}
#[cfg(test)]
mod tests {
use super::*;
use ark_std::test_rng;
#[test]
fn test_commit_columns_roundtrip() {
// dimensions: 3 rows, 2 columns
let n = 8;
let k = 4;
let m = 8;
// setup
let rng = &mut test_rng();
let dd = DynamicData::setup(n, k, m).expect("setup should succeed");
// make a random n×m matrix
let matrix = Matrix::new_random(n, m, rng);
// commit to its columns
let (col_polys, commitments, randomness) =
dd.commit_columns(matrix.clone()).expect("commit_columns should succeed");
// we produced exactly one polynomial, one comm, one rand per column
assert_eq!(col_polys.len(), m);
assert_eq!(commitments.len(), m);
assert_eq!(randomness.len(), m);
// check that each polynomial really interpolates its original column
for (i, poly) in col_polys.iter().enumerate() {
let col = matrix.column(i);
// evaluate poly at each domain point and collect
let evals: Vec<_> = dd
.ploycommit_domain
.elements()
.map(|x| poly.polynomial().evaluate(&x))
.collect();
assert_eq!(evals, col);
}
}
}

View File

@ -0,0 +1,2 @@
pub mod matrix;
pub mod dynamic_data;

207
src/matrix.rs Normal file
View File

@ -0,0 +1,207 @@
use ark_ff::Field;
use std::ops::{Index, IndexMut};
use ark_std::rand::Rng;
use ark_std::UniformRand;
/// a generic dense matrix stored in row-major order.
#[derive(Clone, Debug, PartialEq)]
pub struct Matrix<T: Field + Clone> {
rows: usize,
cols: usize,
data: Vec<T>,
}
impl<T: Field + Clone> Matrix<T> {
/// Creates a new matrix from raw data.
pub fn new(rows: usize, cols: usize, data: Vec<T>) -> Self {
assert!(data.len() == rows * cols, "Data length must equal rows*cols");
Matrix { rows, cols, data }
}
/// Generates a random matrix with given dimensions, uses given rng for randomness.
pub fn new_random<R: Rng + ?Sized>(rows: usize, cols: usize, rng: &mut R) -> Self
where
T: UniformRand,
{
let mut data = Vec::with_capacity(rows * cols);
for _ in 0..rows * cols {
data.push(T::rand(rng));
}
Matrix { rows, cols, data }
}
/// Creates a zero matrix (rows x cols).
pub fn zeros(rows: usize, cols: usize) -> Self {
Matrix { rows, cols, data: vec![T::zero(); rows * cols] }
}
/// Creates an identity matrix of size n x n.
pub fn identity(n: usize) -> Self {
let mut m = Self::zeros(n, n);
for i in 0..n {
m[(i, i)] = T::one();
}
m
}
/// Constructs from a nested Vec
pub fn from_nested_vec(nested: Vec<Vec<T>>) -> Self {
let rows = nested.len();
assert!(rows > 0, "must have at least one row");
let cols = nested[0].len();
let mut data = Vec::with_capacity(rows * cols);
for row in nested.into_iter() {
assert!(row.len() == cols, "all rows must have the same length");
data.extend(row);
}
Matrix { rows, cols, data }
}
/// Returns the number of rows.
#[inline]
pub fn rows(&self) -> usize { self.rows }
/// Returns the number of columns.
#[inline]
pub fn cols(&self) -> usize { self.cols }
/// Returns both dimensions (rows, cols).
#[inline]
pub fn dims(&self) -> (usize, usize) { (self.rows, self.cols) }
/// Returns a reference to the element at (row, col). Panics if out of bounds.
#[inline]
pub fn get(&self, row: usize, col: usize) -> &T {
assert!(row < self.rows && col < self.cols, "Index out of bounds");
&self.data[row * self.cols + col]
}
/// Returns a mutable reference to the element at (row, col).
#[inline]
pub fn get_mut(&mut self, row: usize, col: usize) -> &mut T {
assert!(row < self.rows && col < self.cols, "Index out of bounds");
&mut self.data[row * self.cols + col]
}
/// Returns a slice for the given row.
pub fn row(&self, row: usize) -> &[T] {
assert!(row < self.rows, "Row index out of bounds");
let start = row * self.cols;
&self.data[start..start + self.cols]
}
/// Returns a mutable slice for the given row.
pub fn row_mut(&mut self, row: usize) -> &mut [T] {
assert!(row < self.rows, "Row index out of bounds");
let start = row * self.cols;
&mut self.data[start..start + self.cols]
}
/// Swaps two rows in-place.
pub fn swap_rows(&mut self, i: usize, j: usize) {
assert!(i < self.rows && j < self.rows, "Row index out of bounds");
for col in 0..self.cols {
let a = i * self.cols + col;
let b = j * self.cols + col;
self.data.swap(a, b);
}
}
/// Horizontal concatenation: [self | other].
pub fn hcat(&self, other: &Self) -> Self {
assert!(self.rows == other.rows, "Row counts must match");
let mut result = Self::zeros(self.rows, self.cols + other.cols);
for r in 0..self.rows {
// copy self
let src = r * self.cols;
let dst = r * result.cols;
result.data[dst..dst + self.cols]
.copy_from_slice(&self.data[src..src + self.cols]);
// copy other
let src2 = r * other.cols;
result.data[dst + self.cols..dst + self.cols + other.cols]
.copy_from_slice(&other.data[src2..src2 + other.cols]);
}
result
}
/// Selects a subset of columns by index.
pub fn select_columns(&self, cols_idx: &[usize]) -> Self {
let mut result = Self::zeros(self.rows, cols_idx.len());
for r in 0..self.rows {
for (j, &c) in cols_idx.iter().enumerate() {
result.data[r * result.cols + j] = self.get(r, c).clone();
}
}
result
}
/// Returns a Vec of all elements in the given column.
pub fn column(&self, col: usize) -> Vec<T> {
assert!(col < self.cols, "Column index out of bounds");
let mut v = Vec::with_capacity(self.rows);
for r in 0..self.rows {
v.push(self.get(r, col).clone());
}
v
}
/// Computes the inverse via in-place GaussJordan; returns None if singular.
pub fn invert(&self) -> Option<Self> {
assert!(self.rows == self.cols, "Can only invert square matrices");
let n = self.rows;
let mut aug = self.hcat(&Self::identity(n));
for i in 0..n {
// pivot check and swap if zero
if aug[(i, i)].is_zero() {
if let Some(k) = (i + 1..n).find(|&k| !aug[(k, i)].is_zero()) {
aug.swap_rows(i, k);
} else {
return None;
}
}
// normalize pivot row
let inv_pivot = aug[(i, i)].inverse().unwrap();
for col in i..2 * n {
let idx = i * aug.cols + col;
aug.data[idx] = aug.data[idx].clone() * inv_pivot.clone();
}
// Clone pivot row slice
let pivot_start = i * aug.cols + i;
let pivot_len = 2 * n - i;
let pivot_row: Vec<T> = aug.data[pivot_start..pivot_start + pivot_len].to_vec();
// remove other rows
for r in 0..n {
if r != i {
let factor = aug[(r, i)].clone();
if !factor.is_zero() {
let row_offset = r * aug.cols;
for k in 0..pivot_len {
let idx = row_offset + i + k;
aug.data[idx] = aug.data[idx].clone() - factor.clone() * pivot_row[k].clone();
}
}
}
}
}
Some(aug.select_columns(&(n..2 * n).collect::<Vec<_>>()))
}
}
// indexing with (row, col)
impl<T: Field + Clone> Index<(usize, usize)> for Matrix<T> {
type Output = T;
fn index(&self, (row, col): (usize, usize)) -> &Self::Output {
self.get(row, col)
}
}
// mutable indexing with (row, col)
impl<T: Field + Clone> IndexMut<(usize, usize)> for Matrix<T> {
fn index_mut(&mut self, (row, col): (usize, usize)) -> &mut Self::Output {
self.get_mut(row, col)
}
}