-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- port initial CCS structure with methods from multifolding-poc - add R1CS helper methods, which will be used in Nova impl
- Loading branch information
Showing
3 changed files
with
203 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, | ||
]) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters