- port initial CCS structure with methods from multifolding-poc - add R1CS helper methods, which will be used in Nova implupdate-nifs-interface
@ -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();
|
|||
}
|
|||
}
|
@ -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,
|
|||
])
|
|||
}
|
|||
}
|