arnaucube
arnaucube/sonobePublic Self-hosted repository
sonobe/folding-schemes/src/ccs/r1cs.rs
folding-schemes/src/ccs/r1cs.rsRaw
  1use ark_ff::PrimeField;
  2use ark_relations::r1cs::ConstraintSystem;
  3use ark_std::rand::Rng;
  4
  5use crate::utils::vec::{hadamard, mat_vec_mul, vec_add, vec_scalar_mul, SparseMatrix};
  6use crate::Error;
  7use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
  8
  9#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
 10pub struct R1CS<F: PrimeField> {
 11    pub l: usize, // io len
 12    pub A: SparseMatrix<F>,
 13    pub B: SparseMatrix<F>,
 14    pub C: SparseMatrix<F>,
 15}
 16
 17impl<F: PrimeField> R1CS<F> {
 18    pub fn rand<R: Rng>(rng: &mut R, n_rows: usize, n_cols: usize) -> Self {
 19        Self {
 20            l: 1,
 21            A: SparseMatrix::rand(rng, n_rows, n_cols),
 22            B: SparseMatrix::rand(rng, n_rows, n_cols),
 23            C: SparseMatrix::rand(rng, n_rows, n_cols),
 24        }
 25    }
 26
 27    /// returns a tuple containing (w, x) (witness and public inputs respectively)
 28    pub fn split_z(&self, z: &[F]) -> (Vec<F>, Vec<F>) {
 29        (z[self.l + 1..].to_vec(), z[1..self.l + 1].to_vec())
 30    }
 31
 32    /// check that a R1CS structure is satisfied by a z vector. Only for testing.
 33    pub fn check_relation(&self, z: &[F]) -> Result<(), Error> {
 34        let Az = mat_vec_mul(&self.A, z)?;
 35        let Bz = mat_vec_mul(&self.B, z)?;
 36        let Cz = mat_vec_mul(&self.C, z)?;
 37        let AzBz = hadamard(&Az, &Bz)?;
 38        if AzBz != Cz {
 39            return Err(Error::NotSatisfied);
 40        }
 41
 42        Ok(())
 43    }
 44
 45    /// converts the R1CS instance into a RelaxedR1CS as described in
 46    /// [Nova](https://eprint.iacr.org/2021/370.pdf) section 4.1.
 47    pub fn relax(self) -> RelaxedR1CS<F> {
 48        RelaxedR1CS::<F> {
 49            l: self.l,
 50            E: vec![F::zero(); self.A.n_rows],
 51            A: self.A,
 52            B: self.B,
 53            C: self.C,
 54            u: F::one(),
 55        }
 56    }
 57}
 58
 59#[derive(Debug, Clone, Eq, PartialEq)]
 60pub struct RelaxedR1CS<F: PrimeField> {
 61    pub l: usize, // io len
 62    pub A: SparseMatrix<F>,
 63    pub B: SparseMatrix<F>,
 64    pub C: SparseMatrix<F>,
 65    pub u: F,
 66    pub E: Vec<F>,
 67}
 68
 69impl<F: PrimeField> RelaxedR1CS<F> {
 70    /// check that a RelaxedR1CS structure is satisfied by a z vector. Only for testing.
 71    pub fn check_relation(&self, z: &[F]) -> Result<(), Error> {
 72        let Az = mat_vec_mul(&self.A, z)?;
 73        let Bz = mat_vec_mul(&self.B, z)?;
 74        let Cz = mat_vec_mul(&self.C, z)?;
 75        let uCz = vec_scalar_mul(&Cz, &self.u);
 76        let uCzE = vec_add(&uCz, &self.E)?;
 77        let AzBz = hadamard(&Az, &Bz)?;
 78        if AzBz != uCzE {
 79            return Err(Error::NotSatisfied);
 80        }
 81
 82        Ok(())
 83    }
 84}
 85
 86/// extracts arkworks ConstraintSystem matrices into crate::utils::vec::SparseMatrix format as R1CS
 87/// struct.
 88pub fn extract_r1cs<F: PrimeField>(cs: &ConstraintSystem<F>) -> R1CS<F> {
 89    let m = cs.to_matrices().unwrap();
 90
 91    let n_rows = cs.num_constraints;
 92    let n_cols = cs.num_instance_variables + cs.num_witness_variables; // cs.num_instance_variables already counts the 1
 93
 94    let A = SparseMatrix::<F> {
 95        n_rows,
 96        n_cols,
 97        coeffs: m.a,
 98    };
 99    let B = SparseMatrix::<F> {
100        n_rows,
101        n_cols,
102        coeffs: m.b,
103    };
104    let C = SparseMatrix::<F> {
105        n_rows,
106        n_cols,
107        coeffs: m.c,
108    };
109
110    R1CS::<F> {
111        l: cs.num_instance_variables - 1, // -1 to subtract the first '1'
112        A,
113        B,
114        C,
115    }
116}
117
118/// extracts the witness and the public inputs from arkworks ConstraintSystem.
119pub fn extract_w_x<F: PrimeField>(cs: &ConstraintSystem<F>) -> (Vec<F>, Vec<F>) {
120    (
121        cs.witness_assignment.clone(),
122        // skip the first element which is '1'
123        cs.instance_assignment[1..].to_vec(),
124    )
125}
126
127#[cfg(test)]
128pub mod tests {
129    use super::*;
130    use crate::utils::vec::tests::{to_F_matrix, to_F_vec};
131
132    use ark_ff::PrimeField;
133    use ark_pallas::Fr;
134
135    pub fn get_test_r1cs<F: PrimeField>() -> R1CS<F> {
136        // R1CS for: x^3 + x + 5 = y (example from article
137        // https://www.vitalik.ca/general/2016/12/10/qap.html )
138        let A = to_F_matrix::<F>(vec![
139            vec![0, 1, 0, 0, 0, 0],
140            vec![0, 0, 0, 1, 0, 0],
141            vec![0, 1, 0, 0, 1, 0],
142            vec![5, 0, 0, 0, 0, 1],
143        ]);
144        let B = to_F_matrix::<F>(vec![
145            vec![0, 1, 0, 0, 0, 0],
146            vec![0, 1, 0, 0, 0, 0],
147            vec![1, 0, 0, 0, 0, 0],
148            vec![1, 0, 0, 0, 0, 0],
149        ]);
150        let C = to_F_matrix::<F>(vec![
151            vec![0, 0, 0, 1, 0, 0],
152            vec![0, 0, 0, 0, 1, 0],
153            vec![0, 0, 0, 0, 0, 1],
154            vec![0, 0, 1, 0, 0, 0],
155        ]);
156
157        R1CS::<F> { l: 1, A, B, C }
158    }
159
160    pub fn get_test_z<F: PrimeField>(input: usize) -> Vec<F> {
161        // z = (1, io, w)
162        to_F_vec(vec![
163            1,
164            input,                             // io
165            input * input * input + input + 5, // x^3 + x + 5
166            input * input,                     // x^2
167            input * input * input,             // x^2 * x
168            input * input * input + input,     // x^3 + x
169        ])
170    }
171
172    #[test]
173    fn test_check_relation() {
174        let r1cs = get_test_r1cs::<Fr>();
175        let z = get_test_z(5);
176        r1cs.check_relation(&z).unwrap();
177        r1cs.relax().check_relation(&z).unwrap();
178    }
179}