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