arnaucube
arnaucube/sonobePublic Self-hosted repository
Add initial CCS mod: (#6)

- port initial CCS structure with methods from multifolding-poc
- add R1CS helper methods, which will be used in Nova impl
arnaucube root@arnaucube.com
Wed, 23 Aug 2023 10:22:25 +0200
commit

240b916ddf9db55df21803138c55c66e8ddeeb7f

parent

bc81fbea592d4e495a8a16f2a24b814c340269d1

3 files changed, 203 insertions(+), 0 deletions(-)

jump to
A src/ccs/mod.rs

@@ -0,0 +1,123 @@

+use ark_ec::CurveGroup; +use ark_std::log2; +use ark_std::{One, Zero}; +use std::ops::Neg; + +use crate::utils::vec::*; +use crate::Error; + +pub mod r1cs; +use r1cs::R1CS; + +/// CCS represents the Customizable Constraint Systems structure defined in +/// https://eprint.iacr.org/2023/552 +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct CCS<C: CurveGroup> { + /// m: number of columns in M_i (such that M_i \in F^{m, n}) + pub m: usize, + /// n = |z|, number of rows in M_i + pub n: usize, + /// l = |io|, size of public input/output + pub l: usize, + /// t = |M|, number of matrices + pub t: usize, + /// q = |c| = |S|, number of multisets + pub q: usize, + /// d: max degree in each variable + pub d: usize, + /// s = log(m), dimension of x + pub s: usize, + /// s_prime = log(n), dimension of y + pub s_prime: usize, + + /// vector of matrices + pub M: Vec<SparseMatrix<C::ScalarField>>, + /// vector of multisets + pub S: Vec<Vec<usize>>, + /// vector of coefficients + pub c: Vec<C::ScalarField>, +} + +impl<C: CurveGroup> CCS<C> { + /// check that a CCS structure is satisfied by a z vector. Only for testing. + pub fn check_relation(&self, z: &[C::ScalarField]) -> Result<(), Error> { + let mut result = vec![C::ScalarField::zero(); self.m]; + + for i in 0..self.q { + // extract the needed M_j matrices out of S_i + let vec_M_j: Vec<&SparseMatrix<C::ScalarField>> = + self.S[i].iter().map(|j| &self.M[*j]).collect(); + + // complete the hadamard chain + let mut hadamard_result = vec![C::ScalarField::one(); self.m]; + for M_j in vec_M_j.into_iter() { + hadamard_result = hadamard(&hadamard_result, &mat_vec_mul_sparse(M_j, z)); + } + + // multiply by the coefficient of this step + let c_M_j_z = vec_scalar_mul(&hadamard_result, &self.c[i]); + + // add it to the final vector + result = vec_add(&result, &c_M_j_z); + } + + // make sure the final vector is all zeroes + for e in result { + if !e.is_zero() { + return Err(Error::NotSatisfied); + } + } + + Ok(()) + } +} + +impl<C: CurveGroup> CCS<C> { + pub fn from_r1cs(r1cs: R1CS<C::ScalarField>, io_len: usize) -> Self { + let m = r1cs.A.n_cols; + let n = r1cs.A.n_rows; + CCS { + m, + n, + l: io_len, + s: log2(m) as usize, + s_prime: log2(n) as usize, + t: 3, + q: 2, + d: 2, + + S: vec![vec![0, 1], vec![2]], + c: vec![C::ScalarField::one(), C::ScalarField::one().neg()], + M: vec![r1cs.A, r1cs.B, r1cs.C], + } + } + pub fn to_r1cs(self) -> R1CS<C::ScalarField> { + R1CS::<C::ScalarField> { + l: self.l, + A: self.M[0].clone(), + B: self.M[1].clone(), + C: self.M[2].clone(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::ccs::r1cs::tests::{get_test_r1cs, get_test_z}; + use ark_bls12_377::G1Projective; + + pub fn get_test_ccs<C: CurveGroup>() -> CCS<C> { + let r1cs = get_test_r1cs::<C::ScalarField>(); + CCS::<C>::from_r1cs(r1cs, 1) + } + + /// Test that a basic CCS relation can be satisfied + #[test] + fn test_ccs_relation() { + let ccs = get_test_ccs::<G1Projective>(); + let z = get_test_z(3); + + ccs.check_relation(&z).unwrap(); + } +}
A src/ccs/r1cs.rs

@@ -0,0 +1,79 @@

+use ark_ff::PrimeField; + +use crate::utils::vec::*; + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct R1CS<F: PrimeField> { + pub l: usize, // io len + pub A: SparseMatrix<F>, + pub B: SparseMatrix<F>, + pub C: SparseMatrix<F>, +} +impl<F: PrimeField> R1CS<F> { + /// returns a tuple containing (w, x) (witness and public inputs respectively) + pub fn split_z(&self, z: &[F]) -> (Vec<F>, Vec<F>) { + (z[self.l + 1..].to_vec(), z[1..self.l + 1].to_vec()) + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + + pub fn to_F_matrix<F: PrimeField>(M: Vec<Vec<usize>>) -> Vec<Vec<F>> { + let mut R: Vec<Vec<F>> = vec![Vec::new(); M.len()]; + for i in 0..M.len() { + R[i] = vec![F::zero(); M[i].len()]; + for j in 0..M[i].len() { + R[i][j] = F::from(M[i][j] as u64); + } + } + R + } + pub fn to_F_vec<F: PrimeField>(z: Vec<usize>) -> Vec<F> { + let mut r: Vec<F> = vec![F::zero(); z.len()]; + for i in 0..z.len() { + r[i] = F::from(z[i] as u64); + } + r + } + + pub fn get_test_r1cs<F: PrimeField>() -> R1CS<F> { + // R1CS for: x^3 + x + 5 = y (example from article + // https://www.vitalik.ca/general/2016/12/10/qap.html ) + let A = dense_matrix_to_sparse(to_F_matrix::<F>(vec![ + vec![0, 1, 0, 0, 0, 0], + vec![0, 0, 0, 1, 0, 0], + vec![0, 1, 0, 0, 1, 0], + vec![5, 0, 0, 0, 0, 1], + ])); + let B = dense_matrix_to_sparse(to_F_matrix::<F>(vec![ + vec![0, 1, 0, 0, 0, 0], + vec![0, 1, 0, 0, 0, 0], + vec![1, 0, 0, 0, 0, 0], + vec![1, 0, 0, 0, 0, 0], + ])); + let C = dense_matrix_to_sparse(to_F_matrix::<F>(vec![ + vec![0, 0, 0, 1, 0, 0], + vec![0, 0, 0, 0, 1, 0], + vec![0, 0, 0, 0, 0, 1], + vec![0, 0, 1, 0, 0, 0], + ])); + + R1CS::<F> { l: 1, A, B, C } + } + + pub fn get_test_z<F: PrimeField>(input: usize) -> Vec<F> { + // z = (1, io, w) + to_F_vec(vec![ + 1, + input, // io + input * input * input + input + 5, // x^3 + x + 5 + input * input, // x^2 + input * input * input, // x^2 * x + input * input * input + input, // x^3 + x + 0, // pad to pow of 2 + 0, + ]) + } +}
M src/lib.rs → src/lib.rs

@@ -8,6 +8,7 @@ use thiserror::Error;

pub mod transcript; use transcript::Transcript; +pub mod ccs; pub mod pedersen; pub mod utils;