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