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