arnaucube
arnaucube/sonobePublic Self-hosted repository
sonobe/folding-schemes/src/arith/r1cs.rs
folding-schemes/src/arith/r1cs.rsRaw
  1use crate::commitment::CommitmentScheme;
  2use crate::folding::nova::{CommittedInstance, Witness};
  3use crate::RngCore;
  4use ark_crypto_primitives::sponge::Absorb;
  5use ark_ec::{CurveGroup, Group};
  6use ark_ff::PrimeField;
  7use ark_relations::r1cs::ConstraintSystem;
  8use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
  9use ark_std::rand::Rng;
 10
 11use super::Arith;
 12use crate::utils::vec::{hadamard, mat_vec_mul, vec_add, vec_scalar_mul, vec_sub, SparseMatrix};
 13use crate::Error;
 14
 15#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
 16pub struct R1CS<F: PrimeField> {
 17    pub l: usize, // io len
 18    pub A: SparseMatrix<F>,
 19    pub B: SparseMatrix<F>,
 20    pub C: SparseMatrix<F>,
 21}
 22
 23impl<F: PrimeField> Arith<F> for R1CS<F> {
 24    /// check that a R1CS structure is satisfied by a z vector. Only for testing.
 25    fn check_relation(&self, z: &[F]) -> Result<(), Error> {
 26        let Az = mat_vec_mul(&self.A, z)?;
 27        let Bz = mat_vec_mul(&self.B, z)?;
 28        let Cz = mat_vec_mul(&self.C, z)?;
 29        let AzBz = hadamard(&Az, &Bz)?;
 30        if AzBz != Cz {
 31            return Err(Error::NotSatisfied);
 32        }
 33        Ok(())
 34    }
 35
 36    fn params_to_le_bytes(&self) -> Vec<u8> {
 37        [
 38            self.l.to_le_bytes(),
 39            self.A.n_rows.to_le_bytes(),
 40            self.A.n_cols.to_le_bytes(),
 41        ]
 42        .concat()
 43    }
 44}
 45
 46impl<F: PrimeField> R1CS<F> {
 47    pub fn empty() -> Self {
 48        R1CS {
 49            l: 0,
 50            A: SparseMatrix::empty(),
 51            B: SparseMatrix::empty(),
 52            C: SparseMatrix::empty(),
 53        }
 54    }
 55    pub fn rand<R: Rng>(rng: &mut R, n_rows: usize, n_cols: usize) -> Self {
 56        Self {
 57            l: 1,
 58            A: SparseMatrix::rand(rng, n_rows, n_cols),
 59            B: SparseMatrix::rand(rng, n_rows, n_cols),
 60            C: SparseMatrix::rand(rng, n_rows, n_cols),
 61        }
 62    }
 63
 64    /// returns a tuple containing (w, x) (witness and public inputs respectively)
 65    pub fn split_z(&self, z: &[F]) -> (Vec<F>, Vec<F>) {
 66        (z[self.l + 1..].to_vec(), z[1..self.l + 1].to_vec())
 67    }
 68
 69    /// converts the R1CS instance into a RelaxedR1CS as described in
 70    /// [Nova](https://eprint.iacr.org/2021/370.pdf) section 4.1.
 71    pub fn relax(self) -> RelaxedR1CS<F> {
 72        RelaxedR1CS::<F> {
 73            l: self.l,
 74            E: vec![F::zero(); self.A.n_rows],
 75            A: self.A,
 76            B: self.B,
 77            C: self.C,
 78            u: F::one(),
 79        }
 80    }
 81}
 82
 83#[derive(Debug, Clone, Eq, PartialEq)]
 84pub struct RelaxedR1CS<F: PrimeField> {
 85    pub l: usize, // io len
 86    pub A: SparseMatrix<F>,
 87    pub B: SparseMatrix<F>,
 88    pub C: SparseMatrix<F>,
 89    pub u: F,
 90    pub E: Vec<F>,
 91}
 92
 93impl<F: PrimeField> RelaxedR1CS<F> {
 94    /// check that a RelaxedR1CS structure is satisfied by a z vector. Only for testing.
 95    pub fn check_relation(&self, z: &[F]) -> Result<(), Error> {
 96        let Az = mat_vec_mul(&self.A, z)?;
 97        let Bz = mat_vec_mul(&self.B, z)?;
 98        let Cz = mat_vec_mul(&self.C, z)?;
 99        let uCz = vec_scalar_mul(&Cz, &self.u);
100        let uCzE = vec_add(&uCz, &self.E)?;
101        let AzBz = hadamard(&Az, &Bz)?;
102        if AzBz != uCzE {
103            return Err(Error::NotSatisfied);
104        }
105
106        Ok(())
107    }
108
109    // Computes the E term, given A, B, C, z, u
110    fn compute_E(
111        A: &SparseMatrix<F>,
112        B: &SparseMatrix<F>,
113        C: &SparseMatrix<F>,
114        z: &[F],
115        u: &F,
116    ) -> Result<Vec<F>, Error> {
117        let Az = mat_vec_mul(A, z)?;
118        let Bz = mat_vec_mul(B, z)?;
119        let AzBz = hadamard(&Az, &Bz)?;
120
121        let Cz = mat_vec_mul(C, z)?;
122        let uCz = vec_scalar_mul(&Cz, u);
123        vec_sub(&AzBz, &uCz)
124    }
125
126    pub fn check_sampled_relaxed_r1cs(&self, u: F, E: &[F], z: &[F]) -> bool {
127        let sampled = RelaxedR1CS {
128            l: self.l,
129            A: self.A.clone(),
130            B: self.B.clone(),
131            C: self.C.clone(),
132            u,
133            E: E.to_vec(),
134        };
135        sampled.check_relation(z).is_ok()
136    }
137
138    // Implements sampling a (committed) RelaxedR1CS
139    // See construction 5 in https://eprint.iacr.org/2023/573.pdf
140    pub fn sample<C, CS>(
141        &self,
142        params: &CS::ProverParams,
143        mut rng: impl RngCore,
144    ) -> Result<(CommittedInstance<C>, Witness<C>), Error>
145    where
146        C: CurveGroup,
147        C: CurveGroup<ScalarField = F>,
148        <C as Group>::ScalarField: Absorb,
149        CS: CommitmentScheme<C, true>,
150    {
151        let u = C::ScalarField::rand(&mut rng);
152        let rE = C::ScalarField::rand(&mut rng);
153        let rW = C::ScalarField::rand(&mut rng);
154
155        let W = (0..self.A.n_cols - self.l - 1)
156            .map(|_| F::rand(&mut rng))
157            .collect();
158        let x = (0..self.l).map(|_| F::rand(&mut rng)).collect::<Vec<F>>();
159        let mut z = vec![u];
160        z.extend(&x);
161        z.extend(&W);
162
163        let E = RelaxedR1CS::compute_E(&self.A, &self.B, &self.C, &z, &u)?;
164
165        debug_assert!(
166            z.len() == self.A.n_cols,
167            "Length of z is {}, while A has {} columns.",
168            z.len(),
169            self.A.n_cols
170        );
171
172        debug_assert!(
173            self.check_sampled_relaxed_r1cs(u, &E, &z),
174            "Sampled a non satisfiable relaxed R1CS, sampled u: {}, computed E: {:?}",
175            u,
176            E
177        );
178
179        let witness = Witness { E, rE, W, rW };
180        let mut cm_witness = witness.commit::<CS, true>(params, x)?;
181
182        // witness.commit() sets u to 1, we set it to the sampled u value
183        cm_witness.u = u;
184        Ok((cm_witness, witness))
185    }
186}
187
188/// extracts arkworks ConstraintSystem matrices into crate::utils::vec::SparseMatrix format as R1CS
189/// struct.
190pub fn extract_r1cs<F: PrimeField>(cs: &ConstraintSystem<F>) -> R1CS<F> {
191    let m = cs.to_matrices().unwrap();
192
193    let n_rows = cs.num_constraints;
194    let n_cols = cs.num_instance_variables + cs.num_witness_variables; // cs.num_instance_variables already counts the 1
195
196    let A = SparseMatrix::<F> {
197        n_rows,
198        n_cols,
199        coeffs: m.a,
200    };
201    let B = SparseMatrix::<F> {
202        n_rows,
203        n_cols,
204        coeffs: m.b,
205    };
206    let C = SparseMatrix::<F> {
207        n_rows,
208        n_cols,
209        coeffs: m.c,
210    };
211
212    R1CS::<F> {
213        l: cs.num_instance_variables - 1, // -1 to subtract the first '1'
214        A,
215        B,
216        C,
217    }
218}
219
220/// extracts the witness and the public inputs from arkworks ConstraintSystem.
221pub fn extract_w_x<F: PrimeField>(cs: &ConstraintSystem<F>) -> (Vec<F>, Vec<F>) {
222    (
223        cs.witness_assignment.clone(),
224        // skip the first element which is '1'
225        cs.instance_assignment[1..].to_vec(),
226    )
227}
228
229#[cfg(test)]
230pub mod tests {
231    use super::*;
232    use crate::{
233        commitment::pedersen::Pedersen,
234        utils::vec::tests::{to_F_matrix, to_F_vec},
235    };
236
237    use ark_pallas::{Fr, Projective};
238
239    #[test]
240    pub fn sample_relaxed_r1cs() {
241        let rng = rand::rngs::OsRng;
242        let r1cs = get_test_r1cs::<Fr>();
243        let (prover_params, _) = Pedersen::<Projective>::setup(rng, r1cs.A.n_rows).unwrap();
244
245        let relaxed_r1cs = r1cs.relax();
246        let sampled =
247            relaxed_r1cs.sample::<Projective, Pedersen<Projective, true>>(&prover_params, rng);
248        assert!(sampled.is_ok());
249    }
250
251    pub fn get_test_r1cs<F: PrimeField>() -> R1CS<F> {
252        // R1CS for: x^3 + x + 5 = y (example from article
253        // https://www.vitalik.ca/general/2016/12/10/qap.html )
254        let A = to_F_matrix::<F>(vec![
255            vec![0, 1, 0, 0, 0, 0],
256            vec![0, 0, 0, 1, 0, 0],
257            vec![0, 1, 0, 0, 1, 0],
258            vec![5, 0, 0, 0, 0, 1],
259        ]);
260        let B = to_F_matrix::<F>(vec![
261            vec![0, 1, 0, 0, 0, 0],
262            vec![0, 1, 0, 0, 0, 0],
263            vec![1, 0, 0, 0, 0, 0],
264            vec![1, 0, 0, 0, 0, 0],
265        ]);
266        let C = to_F_matrix::<F>(vec![
267            vec![0, 0, 0, 1, 0, 0],
268            vec![0, 0, 0, 0, 1, 0],
269            vec![0, 0, 0, 0, 0, 1],
270            vec![0, 0, 1, 0, 0, 0],
271        ]);
272
273        R1CS::<F> { l: 1, A, B, C }
274    }
275
276    pub fn get_test_z<F: PrimeField>(input: usize) -> Vec<F> {
277        // z = (1, io, w)
278        to_F_vec(vec![
279            1,
280            input,                             // io
281            input * input * input + input + 5, // x^3 + x + 5
282            input * input,                     // x^2
283            input * input * input,             // x^2 * x
284            input * input * input + input,     // x^3 + x
285        ])
286    }
287
288    pub fn get_test_z_split<F: PrimeField>(input: usize) -> (F, Vec<F>, Vec<F>) {
289        // z = (1, io, w)
290        (
291            F::one(),
292            to_F_vec(vec![
293                input, // io
294            ]),
295            to_F_vec(vec![
296                input * input * input + input + 5, // x^3 + x + 5
297                input * input,                     // x^2
298                input * input * input,             // x^2 * x
299                input * input * input + input,     // x^3 + x
300            ]),
301        )
302    }
303
304    #[test]
305    fn test_check_relation() {
306        let r1cs = get_test_r1cs::<Fr>();
307        let z = get_test_z(5);
308        r1cs.check_relation(&z).unwrap();
309        r1cs.relax().check_relation(&z).unwrap();
310    }
311}