From 240b916ddf9db55df21803138c55c66e8ddeeb7f Mon Sep 17 00:00:00 2001 From: arnaucube Date: Wed, 23 Aug 2023 10:22:25 +0200 Subject: [PATCH] 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 --- src/ccs/mod.rs | 123 ++++++++++++++++++++++++++++++++++++++++++++++++ src/ccs/r1cs.rs | 79 +++++++++++++++++++++++++++++++ src/lib.rs | 1 + 3 files changed, 203 insertions(+) create mode 100644 src/ccs/mod.rs create mode 100644 src/ccs/r1cs.rs diff --git a/src/ccs/mod.rs b/src/ccs/mod.rs new file mode 100644 index 0000000..9a052c1 --- /dev/null +++ b/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 { + /// 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>, + /// vector of multisets + pub S: Vec>, + /// vector of coefficients + pub c: Vec, +} + +impl CCS { + /// 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> = + 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 CCS { + pub fn from_r1cs(r1cs: R1CS, 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 { + R1CS:: { + 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() -> CCS { + let r1cs = get_test_r1cs::(); + CCS::::from_r1cs(r1cs, 1) + } + + /// Test that a basic CCS relation can be satisfied + #[test] + fn test_ccs_relation() { + let ccs = get_test_ccs::(); + let z = get_test_z(3); + + ccs.check_relation(&z).unwrap(); + } +} diff --git a/src/ccs/r1cs.rs b/src/ccs/r1cs.rs new file mode 100644 index 0000000..8384ba4 --- /dev/null +++ b/src/ccs/r1cs.rs @@ -0,0 +1,79 @@ +use ark_ff::PrimeField; + +use crate::utils::vec::*; + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct R1CS { + pub l: usize, // io len + pub A: SparseMatrix, + pub B: SparseMatrix, + pub C: SparseMatrix, +} +impl R1CS { + /// returns a tuple containing (w, x) (witness and public inputs respectively) + pub fn split_z(&self, z: &[F]) -> (Vec, Vec) { + (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(M: Vec>) -> Vec> { + let mut R: Vec> = 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(z: Vec) -> Vec { + let mut r: Vec = 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() -> R1CS { + // 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::(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::(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::(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:: { l: 1, A, B, C } + } + + pub fn get_test_z(input: usize) -> Vec { + // 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, + ]) + } +} diff --git a/src/lib.rs b/src/lib.rs index de53ca6..335d233 100644 --- a/src/lib.rs +++ b/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;