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