arnaucube
arnaucube/sonobePublic Self-hosted repository
sonobe/folding-schemes/src/arith/r1cs/circuits.rs
folding-schemes/src/arith/r1cs/circuits.rsRaw
  1use crate::{
  2    arith::ArithGadget,
  3    utils::gadgets::{EquivalenceGadget, MatrixGadget, SparseMatrixVar, VectorGadget},
  4};
  5use ark_ff::PrimeField;
  6use ark_r1cs_std::alloc::{AllocVar, AllocationMode};
  7use ark_relations::r1cs::{Namespace, SynthesisError};
  8use ark_std::{borrow::Borrow, marker::PhantomData, One};
  9
 10use super::R1CS;
 11
 12/// An in-circuit representation of the `R1CS` struct.
 13///
 14/// `M` is for the modulo operation involved in the satisfiability check when
 15/// the underlying `FVar` is `NonNativeUintVar`.
 16#[derive(Debug, Clone)]
 17pub struct R1CSMatricesVar<M, FVar> {
 18    _m: PhantomData<M>,
 19    pub A: SparseMatrixVar<FVar>,
 20    pub B: SparseMatrixVar<FVar>,
 21    pub C: SparseMatrixVar<FVar>,
 22}
 23
 24impl<F: PrimeField, ConstraintF: PrimeField, FVar: AllocVar<F, ConstraintF>>
 25    AllocVar<R1CS<F>, ConstraintF> for R1CSMatricesVar<F, FVar>
 26{
 27    fn new_variable<T: Borrow<R1CS<F>>>(
 28        cs: impl Into<Namespace<ConstraintF>>,
 29        f: impl FnOnce() -> Result<T, SynthesisError>,
 30        _mode: AllocationMode,
 31    ) -> Result<Self, SynthesisError> {
 32        f().and_then(|val| {
 33            let cs = cs.into();
 34
 35            Ok(Self {
 36                _m: PhantomData,
 37                A: SparseMatrixVar::<FVar>::new_constant(cs.clone(), &val.borrow().A)?,
 38                B: SparseMatrixVar::<FVar>::new_constant(cs.clone(), &val.borrow().B)?,
 39                C: SparseMatrixVar::<FVar>::new_constant(cs.clone(), &val.borrow().C)?,
 40            })
 41        })
 42    }
 43}
 44
 45impl<M, FVar> R1CSMatricesVar<M, FVar>
 46where
 47    SparseMatrixVar<FVar>: MatrixGadget<FVar>,
 48    [FVar]: VectorGadget<FVar>,
 49{
 50    pub fn eval_at_z(&self, z: &[FVar]) -> Result<(Vec<FVar>, Vec<FVar>), SynthesisError> {
 51        // Multiply Cz by z[0] (u) here, allowing this method to be reused for
 52        // both relaxed and unrelaxed R1CS.
 53        let Az = self.A.mul_vector(z)?;
 54        let Bz = self.B.mul_vector(z)?;
 55        let Cz = self.C.mul_vector(z)?;
 56        let uCz = Cz.mul_scalar(&z[0])?;
 57        let AzBz = Az.hadamard(&Bz)?;
 58        Ok((AzBz, uCz))
 59    }
 60}
 61
 62impl<M, FVar, WVar: AsRef<[FVar]>, UVar: AsRef<[FVar]>> ArithGadget<WVar, UVar>
 63    for R1CSMatricesVar<M, FVar>
 64where
 65    SparseMatrixVar<FVar>: MatrixGadget<FVar>,
 66    [FVar]: VectorGadget<FVar> + EquivalenceGadget<M>,
 67    FVar: Clone + One,
 68{
 69    /// Evaluation is a tuple of two vectors (`AzBz` and `uCz`) instead of a
 70    /// single vector `AzBz - uCz`, because subtraction is not supported for
 71    /// `FVar = NonNativeUintVar`.
 72    type Evaluation = (Vec<FVar>, Vec<FVar>);
 73
 74    fn eval_relation(&self, w: &WVar, u: &UVar) -> Result<Self::Evaluation, SynthesisError> {
 75        self.eval_at_z(&[&[FVar::one()], u.as_ref(), w.as_ref()].concat())
 76    }
 77
 78    fn enforce_evaluation(
 79        _w: &WVar,
 80        _u: &UVar,
 81        (lhs, rhs): Self::Evaluation,
 82    ) -> Result<(), SynthesisError> {
 83        lhs.enforce_equivalent(&rhs)
 84    }
 85}
 86
 87#[cfg(test)]
 88pub mod tests {
 89    use std::cmp::max;
 90
 91    use ark_crypto_primitives::crh::{
 92        sha256::{
 93            constraints::{Sha256Gadget, UnitVar},
 94            Sha256,
 95        },
 96        CRHScheme, CRHSchemeGadget,
 97    };
 98    use ark_ec::CurveGroup;
 99    use ark_ff::BigInteger;
100    use ark_pallas::{Fq, Fr, Projective};
101    use ark_r1cs_std::{bits::uint8::UInt8, eq::EqGadget, fields::fp::FpVar};
102    use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef};
103    use ark_std::{
104        rand::{thread_rng, Rng},
105        One, UniformRand,
106    };
107    use ark_vesta::{constraints::GVar as GVar2, Projective as Projective2};
108
109    use super::*;
110    use crate::arith::{
111        r1cs::{
112            extract_r1cs, extract_w_x,
113            tests::{get_test_r1cs, get_test_z},
114        },
115        Arith,
116    };
117    use crate::commitment::{pedersen::Pedersen, CommitmentScheme};
118    use crate::folding::{
119        circuits::{
120            cyclefold::{CycleFoldCommittedInstanceVar, CycleFoldWitnessVar},
121            nonnative::uint::NonNativeUintVar,
122        },
123        nova::{
124            circuits::CommittedInstanceVar, decider_eth_circuit::WitnessVar, CommittedInstance,
125            Witness,
126        },
127    };
128    use crate::frontend::{
129        utils::{CubicFCircuit, CustomFCircuit, WrapperCircuit},
130        FCircuit,
131    };
132
133    fn prepare_instances<C: CurveGroup, CS: CommitmentScheme<C>, R: Rng>(
134        mut rng: R,
135        r1cs: &R1CS<C::ScalarField>,
136        z: &[C::ScalarField],
137    ) -> (Witness<C>, CommittedInstance<C>) {
138        let (w, x) = r1cs.split_z(z);
139
140        let (cs_pp, _) = CS::setup(&mut rng, max(w.len(), r1cs.A.n_rows)).unwrap();
141
142        let mut w = Witness::new::<false>(w, r1cs.A.n_rows, &mut rng);
143        w.E = r1cs.eval_at_z(z).unwrap();
144        let mut u = w.commit::<CS, false>(&cs_pp, x).unwrap();
145        u.u = z[0];
146
147        (w, u)
148    }
149
150    #[test]
151    fn test_relaxed_r1cs_small_gadget_handcrafted() {
152        let rng = &mut thread_rng();
153
154        let r1cs: R1CS<Fr> = get_test_r1cs();
155        let mut z = get_test_z(3);
156        z[0] = Fr::rand(rng);
157        let (w, u) = prepare_instances::<_, Pedersen<Projective>, _>(rng, &r1cs, &z);
158
159        let cs = ConstraintSystem::<Fr>::new_ref();
160
161        let wVar = WitnessVar::new_witness(cs.clone(), || Ok(w)).unwrap();
162        let uVar = CommittedInstanceVar::new_witness(cs.clone(), || Ok(u)).unwrap();
163        let r1csVar =
164            R1CSMatricesVar::<Fr, FpVar<Fr>>::new_witness(cs.clone(), || Ok(r1cs)).unwrap();
165
166        r1csVar.enforce_relation(&wVar, &uVar).unwrap();
167        assert!(cs.is_satisfied().unwrap());
168    }
169
170    // gets as input a circuit that implements the ConstraintSynthesizer trait, and that has been
171    // initialized.
172    fn test_relaxed_r1cs_gadget<CS: ConstraintSynthesizer<Fr>>(circuit: CS) {
173        let rng = &mut thread_rng();
174
175        let cs = ConstraintSystem::<Fr>::new_ref();
176
177        circuit.generate_constraints(cs.clone()).unwrap();
178        cs.finalize();
179        assert!(cs.is_satisfied().unwrap());
180
181        let cs = cs.into_inner().unwrap();
182
183        let r1cs = extract_r1cs::<Fr>(&cs).unwrap();
184        let (w, x) = extract_w_x::<Fr>(&cs);
185        r1cs.check_relation(&w, &x).unwrap();
186        let mut z = [vec![Fr::one()], x, w].concat();
187        z[0] = Fr::rand(rng);
188
189        let (w, u) = prepare_instances::<_, Pedersen<Projective>, _>(rng, &r1cs, &z);
190        r1cs.check_relation(&w, &u).unwrap();
191
192        // set new CS for the circuit that checks the RelaxedR1CS of our original circuit
193        let cs = ConstraintSystem::<Fr>::new_ref();
194        // prepare the inputs for our circuit
195        let wVar = WitnessVar::new_witness(cs.clone(), || Ok(w)).unwrap();
196        let uVar = CommittedInstanceVar::new_witness(cs.clone(), || Ok(u)).unwrap();
197        let r1csVar =
198            R1CSMatricesVar::<Fr, FpVar<Fr>>::new_witness(cs.clone(), || Ok(r1cs)).unwrap();
199
200        r1csVar.enforce_relation(&wVar, &uVar).unwrap();
201        assert!(cs.is_satisfied().unwrap());
202    }
203
204    #[test]
205    fn test_relaxed_r1cs_small_gadget_arkworks() {
206        let z_i = vec![Fr::from(3_u32)];
207        let cubic_circuit = CubicFCircuit::<Fr>::new(()).unwrap();
208        let circuit = WrapperCircuit::<Fr, CubicFCircuit<Fr>> {
209            FC: cubic_circuit,
210            z_i: Some(z_i.clone()),
211            z_i1: Some(cubic_circuit.step_native(0, z_i, vec![]).unwrap()),
212        };
213
214        test_relaxed_r1cs_gadget(circuit);
215    }
216
217    struct Sha256TestCircuit<F: PrimeField> {
218        _f: PhantomData<F>,
219        pub x: Vec<u8>,
220        pub y: Vec<u8>,
221    }
222    impl<F: PrimeField> ConstraintSynthesizer<F> for Sha256TestCircuit<F> {
223        fn generate_constraints(self, cs: ConstraintSystemRef<F>) -> Result<(), SynthesisError> {
224            let x = Vec::<UInt8<F>>::new_witness(cs.clone(), || Ok(self.x))?;
225            let y = Vec::<UInt8<F>>::new_input(cs.clone(), || Ok(self.y))?;
226
227            let unitVar = UnitVar::default();
228            let comp_y = <Sha256Gadget<F> as CRHSchemeGadget<Sha256, F>>::evaluate(&unitVar, &x)?;
229            comp_y.0.enforce_equal(&y)?;
230            Ok(())
231        }
232    }
233    #[test]
234    fn test_relaxed_r1cs_medium_gadget_arkworks() {
235        let x = Fr::from(5_u32).into_bigint().to_bytes_le();
236        let y = <Sha256 as CRHScheme>::evaluate(&(), x.clone()).unwrap();
237
238        let circuit = Sha256TestCircuit::<Fr> {
239            _f: PhantomData,
240            x,
241            y,
242        };
243        test_relaxed_r1cs_gadget(circuit);
244    }
245
246    #[test]
247    fn test_relaxed_r1cs_custom_circuit() {
248        let n_constraints = 10_000;
249        let custom_circuit = CustomFCircuit::<Fr>::new(n_constraints).unwrap();
250        let z_i = vec![Fr::from(5_u32)];
251        let circuit = WrapperCircuit::<Fr, CustomFCircuit<Fr>> {
252            FC: custom_circuit,
253            z_i: Some(z_i.clone()),
254            z_i1: Some(custom_circuit.step_native(0, z_i, vec![]).unwrap()),
255        };
256        test_relaxed_r1cs_gadget(circuit);
257    }
258
259    #[test]
260    fn test_relaxed_r1cs_nonnative_circuit() {
261        let rng = &mut thread_rng();
262
263        let cs = ConstraintSystem::<Fq>::new_ref();
264        // in practice we would use CycleFoldCircuit, but is a very big circuit (when computed
265        // non-natively inside the RelaxedR1CS circuit), so in order to have a short test we use a
266        // custom circuit.
267        let custom_circuit = CustomFCircuit::<Fq>::new(10).unwrap();
268        let z_i = vec![Fq::from(5_u32)];
269        let circuit = WrapperCircuit::<Fq, CustomFCircuit<Fq>> {
270            FC: custom_circuit,
271            z_i: Some(z_i.clone()),
272            z_i1: Some(custom_circuit.step_native(0, z_i, vec![]).unwrap()),
273        };
274        circuit.generate_constraints(cs.clone()).unwrap();
275        cs.finalize();
276        let cs = cs.into_inner().unwrap();
277        let r1cs = extract_r1cs::<Fq>(&cs).unwrap();
278        let (w, x) = extract_w_x::<Fq>(&cs);
279        let z = [vec![Fq::rand(rng)], x, w].concat();
280
281        let (w, u) = prepare_instances::<_, Pedersen<Projective2>, _>(rng, &r1cs, &z);
282
283        // natively
284        let cs = ConstraintSystem::<Fq>::new_ref();
285        let wVar = WitnessVar::new_witness(cs.clone(), || Ok(&w)).unwrap();
286        let uVar = CommittedInstanceVar::new_witness(cs.clone(), || Ok(&u)).unwrap();
287        let r1csVar =
288            R1CSMatricesVar::<Fq, FpVar<Fq>>::new_witness(cs.clone(), || Ok(&r1cs)).unwrap();
289        r1csVar.enforce_relation(&wVar, &uVar).unwrap();
290
291        // non-natively
292        let cs = ConstraintSystem::<Fr>::new_ref();
293        let wVar = CycleFoldWitnessVar::new_witness(cs.clone(), || Ok(w)).unwrap();
294        let uVar =
295            CycleFoldCommittedInstanceVar::<_, GVar2>::new_witness(cs.clone(), || Ok(u)).unwrap();
296        let r1csVar =
297            R1CSMatricesVar::<Fq, NonNativeUintVar<Fr>>::new_witness(cs.clone(), || Ok(r1cs))
298                .unwrap();
299        r1csVar.enforce_relation(&wVar, &uVar).unwrap();
300    }
301}