arnaucube
arnaucube/sonobePublic Self-hosted repository
sonobe/folding-schemes/src/frontend/mod.rs
folding-schemes/src/frontend/mod.rsRaw
  1use crate::Error;
  2use ark_ff::PrimeField;
  3use ark_r1cs_std::fields::fp::FpVar;
  4use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
  5use ark_std::fmt::Debug;
  6
  7pub mod circom;
  8
  9/// FCircuit defines the trait of the circuit of the F function, which is the one being folded (ie.
 10/// inside the agmented F' function).
 11/// The parameter z_i denotes the current state, and z_{i+1} denotes the next state after applying
 12/// the step.
 13pub trait FCircuit<F: PrimeField>: Clone + Debug {
 14    type Params: Debug;
 15
 16    /// returns a new FCircuit instance
 17    fn new(params: Self::Params) -> Self;
 18
 19    /// returns the number of elements in the state of the FCircuit, which corresponds to the
 20    /// FCircuit inputs.
 21    fn state_len(&self) -> usize;
 22
 23    /// computes the next state values in place, assigning z_{i+1} into z_i, and computing the new
 24    /// z_{i+1}
 25    fn step_native(
 26        // this method uses self, so that each FCircuit implementation (and different frontends)
 27        // can hold a state if needed to store data to compute the next state.
 28        &self,
 29        i: usize,
 30        z_i: Vec<F>,
 31    ) -> Result<Vec<F>, Error>;
 32
 33    /// generates the constraints for the step of F for the given z_i
 34    fn generate_step_constraints(
 35        // this method uses self, so that each FCircuit implementation (and different frontends)
 36        // can hold a state if needed to store data to generate the constraints.
 37        &self,
 38        cs: ConstraintSystemRef<F>,
 39        i: usize,
 40        z_i: Vec<FpVar<F>>,
 41    ) -> Result<Vec<FpVar<F>>, SynthesisError>;
 42}
 43
 44#[cfg(test)]
 45pub mod tests {
 46    use super::*;
 47    use ark_bn254::Fr;
 48    use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget};
 49    use ark_relations::r1cs::{
 50        ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, SynthesisError,
 51    };
 52    use core::marker::PhantomData;
 53
 54    /// CubicFCircuit is a struct that implements the FCircuit trait, for the R1CS example circuit
 55    /// from https://www.vitalik.ca/general/2016/12/10/qap.html, which checks `x^3 + x + 5 = y`.
 56    /// `z_i` is used as `x`, and `z_{i+1}` is used as `y`, and at the next step, `z_{i+1}` will be
 57    /// assigned to `z_i`, and a new `z+{i+1}` will be computted.
 58    #[derive(Clone, Copy, Debug)]
 59    pub struct CubicFCircuit<F: PrimeField> {
 60        _f: PhantomData<F>,
 61    }
 62    impl<F: PrimeField> FCircuit<F> for CubicFCircuit<F> {
 63        type Params = ();
 64        fn new(_params: Self::Params) -> Self {
 65            Self { _f: PhantomData }
 66        }
 67        fn state_len(&self) -> usize {
 68            1
 69        }
 70        fn step_native(&self, _i: usize, z_i: Vec<F>) -> Result<Vec<F>, Error> {
 71            Ok(vec![z_i[0] * z_i[0] * z_i[0] + z_i[0] + F::from(5_u32)])
 72        }
 73        fn generate_step_constraints(
 74            &self,
 75            cs: ConstraintSystemRef<F>,
 76            _i: usize,
 77            z_i: Vec<FpVar<F>>,
 78        ) -> Result<Vec<FpVar<F>>, SynthesisError> {
 79            let five = FpVar::<F>::new_constant(cs.clone(), F::from(5u32))?;
 80            let z_i = z_i[0].clone();
 81
 82            Ok(vec![&z_i * &z_i * &z_i + &z_i + &five])
 83        }
 84    }
 85
 86    /// CustomFCircuit is a circuit that has the number of constraints specified in the
 87    /// `n_constraints` parameter. Note that the generated circuit will have very sparse matrices.
 88    #[derive(Clone, Copy, Debug)]
 89    pub struct CustomFCircuit<F: PrimeField> {
 90        _f: PhantomData<F>,
 91        pub n_constraints: usize,
 92    }
 93    impl<F: PrimeField> FCircuit<F> for CustomFCircuit<F> {
 94        type Params = usize;
 95
 96        fn new(params: Self::Params) -> Self {
 97            Self {
 98                _f: PhantomData,
 99                n_constraints: params,
100            }
101        }
102        fn state_len(&self) -> usize {
103            1
104        }
105        fn step_native(&self, _i: usize, z_i: Vec<F>) -> Result<Vec<F>, Error> {
106            let mut z_i1 = F::one();
107            for _ in 0..self.n_constraints - 1 {
108                z_i1 *= z_i[0];
109            }
110            Ok(vec![z_i1])
111        }
112        fn generate_step_constraints(
113            &self,
114            cs: ConstraintSystemRef<F>,
115            _i: usize,
116            z_i: Vec<FpVar<F>>,
117        ) -> Result<Vec<FpVar<F>>, SynthesisError> {
118            let mut z_i1 = FpVar::<F>::new_witness(cs.clone(), || Ok(F::one()))?;
119            for _ in 0..self.n_constraints - 1 {
120                z_i1 *= z_i[0].clone();
121            }
122
123            Ok(vec![z_i1])
124        }
125    }
126
127    /// WrapperCircuit is a circuit that wraps any circuit that implements the FCircuit trait. This
128    /// is used to test the `FCircuit.generate_step_constraints` method. This is a similar wrapping
129    /// than the one done in the `AugmentedFCircuit`, but without adding all the extra constraints
130    /// of the AugmentedF circuit logic, in order to run lighter tests when we're not interested in
131    /// the the AugmentedF logic but in the wrapping of the circuits.
132    pub struct WrapperCircuit<F: PrimeField, FC: FCircuit<F>> {
133        pub FC: FC, // F circuit
134        pub z_i: Option<Vec<F>>,
135        pub z_i1: Option<Vec<F>>,
136    }
137    impl<F, FC> ConstraintSynthesizer<F> for WrapperCircuit<F, FC>
138    where
139        F: PrimeField,
140        FC: FCircuit<F>,
141    {
142        fn generate_constraints(self, cs: ConstraintSystemRef<F>) -> Result<(), SynthesisError> {
143            let z_i = Vec::<FpVar<F>>::new_witness(cs.clone(), || {
144                Ok(self.z_i.unwrap_or(vec![F::zero()]))
145            })?;
146            let z_i1 = Vec::<FpVar<F>>::new_input(cs.clone(), || {
147                Ok(self.z_i1.unwrap_or(vec![F::zero()]))
148            })?;
149            let computed_z_i1 = self
150                .FC
151                .generate_step_constraints(cs.clone(), 0, z_i.clone())?;
152
153            computed_z_i1.enforce_equal(&z_i1)?;
154            Ok(())
155        }
156    }
157
158    #[test]
159    fn test_testfcircuit() {
160        let cs = ConstraintSystem::<Fr>::new_ref();
161        let F_circuit = CubicFCircuit::<Fr>::new(());
162
163        let wrapper_circuit = WrapperCircuit::<Fr, CubicFCircuit<Fr>> {
164            FC: F_circuit,
165            z_i: Some(vec![Fr::from(3_u32)]),
166            z_i1: Some(vec![Fr::from(35_u32)]),
167        };
168        wrapper_circuit.generate_constraints(cs.clone()).unwrap();
169        assert_eq!(cs.num_constraints(), 3);
170    }
171
172    #[test]
173    fn test_customtestfcircuit() {
174        let cs = ConstraintSystem::<Fr>::new_ref();
175        let n_constraints = 1000;
176        let custom_circuit = CustomFCircuit::<Fr>::new(n_constraints);
177        let z_i = vec![Fr::from(5_u32)];
178        let wrapper_circuit = WrapperCircuit::<Fr, CustomFCircuit<Fr>> {
179            FC: custom_circuit,
180            z_i: Some(z_i.clone()),
181            z_i1: Some(custom_circuit.step_native(0, z_i).unwrap()),
182        };
183        wrapper_circuit.generate_constraints(cs.clone()).unwrap();
184        assert_eq!(cs.num_constraints(), n_constraints);
185    }
186}