sonobe/folding-schemes/src/frontend/utils.rs
folding-schemes/src/frontend/utils.rsRaw
1use ark_ff::PrimeField;
2use ark_r1cs_std::{alloc::AllocVar, fields::fp::FpVar};
3use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
4#[cfg(test)]
5use ark_std::marker::PhantomData;
6use ark_std::{fmt::Debug, Zero};
7
8use super::FCircuit;
9use crate::Error;
10
11/// DummyCircuit is a circuit that has dummy state and external inputs whose
12/// lengths are specified in the `state_len` and `external_inputs_len`
13/// parameters, without any constraints.
14#[derive(Clone, Debug)]
15pub struct DummyCircuit {
16 state_len: usize,
17 external_inputs_len: usize,
18}
19impl<F: PrimeField> FCircuit<F> for DummyCircuit {
20 type Params = (usize, usize);
21
22 fn new((state_len, external_inputs_len): Self::Params) -> Result<Self, Error> {
23 Ok(Self {
24 state_len,
25 external_inputs_len,
26 })
27 }
28 fn state_len(&self) -> usize {
29 self.state_len
30 }
31 fn external_inputs_len(&self) -> usize {
32 self.external_inputs_len
33 }
34 fn step_native(
35 &self,
36 _i: usize,
37 _z_i: Vec<F>,
38 _external_inputs: Vec<F>,
39 ) -> Result<Vec<F>, Error> {
40 Ok(vec![F::zero(); self.state_len])
41 }
42 fn generate_step_constraints(
43 &self,
44 cs: ConstraintSystemRef<F>,
45 _i: usize,
46 _z_i: Vec<FpVar<F>>,
47 _external_inputs: Vec<FpVar<F>>,
48 ) -> Result<Vec<FpVar<F>>, SynthesisError> {
49 Vec::new_witness(cs.clone(), || Ok(vec![Zero::zero(); self.state_len]))
50 }
51}
52
53/// CubicFCircuit is a struct that implements the FCircuit trait, for the R1CS example circuit
54/// from https://www.vitalik.ca/general/2016/12/10/qap.html, which checks `x^3 + x + 5 = y`.
55/// `z_i` is used as `x`, and `z_{i+1}` is used as `y`, and at the next step, `z_{i+1}` will be
56/// assigned to `z_i`, and a new `z+{i+1}` will be computted.
57#[cfg(test)]
58#[derive(Clone, Copy, Debug)]
59pub struct CubicFCircuit<F: PrimeField> {
60 _f: PhantomData<F>,
61}
62
63#[cfg(test)]
64impl<F: PrimeField> FCircuit<F> for CubicFCircuit<F> {
65 type Params = ();
66 fn new(_params: Self::Params) -> Result<Self, Error> {
67 Ok(Self { _f: PhantomData })
68 }
69 fn state_len(&self) -> usize {
70 1
71 }
72 fn external_inputs_len(&self) -> usize {
73 0
74 }
75 fn step_native(
76 &self,
77 _i: usize,
78 z_i: Vec<F>,
79 _external_inputs: Vec<F>,
80 ) -> Result<Vec<F>, Error> {
81 Ok(vec![z_i[0] * z_i[0] * z_i[0] + z_i[0] + F::from(5_u32)])
82 }
83 fn generate_step_constraints(
84 &self,
85 cs: ConstraintSystemRef<F>,
86 _i: usize,
87 z_i: Vec<FpVar<F>>,
88 _external_inputs: Vec<FpVar<F>>,
89 ) -> Result<Vec<FpVar<F>>, SynthesisError> {
90 let five = FpVar::<F>::new_constant(cs.clone(), F::from(5u32))?;
91 let z_i = z_i[0].clone();
92
93 Ok(vec![&z_i * &z_i * &z_i + &z_i + &five])
94 }
95}
96
97/// CustomFCircuit is a circuit that has the number of constraints specified in the
98/// `n_constraints` parameter. Note that the generated circuit will have very sparse matrices.
99#[cfg(test)]
100#[derive(Clone, Copy, Debug)]
101pub struct CustomFCircuit<F: PrimeField> {
102 _f: PhantomData<F>,
103 pub n_constraints: usize,
104}
105
106#[cfg(test)]
107impl<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#[cfg(test)]
156pub struct WrapperCircuit<F: PrimeField, FC: FCircuit<F>> {
157 pub FC: FC, // F circuit
158 pub z_i: Option<Vec<F>>,
159 pub z_i1: Option<Vec<F>>,
160}
161
162#[cfg(test)]
163impl<F, FC> ark_relations::r1cs::ConstraintSynthesizer<F> for WrapperCircuit<F, FC>
164where
165 F: PrimeField,
166 FC: FCircuit<F>,
167{
168 fn generate_constraints(self, cs: ConstraintSystemRef<F>) -> Result<(), SynthesisError> {
169 let z_i =
170 Vec::<FpVar<F>>::new_witness(cs.clone(), || Ok(self.z_i.unwrap_or(vec![F::zero()])))?;
171 let z_i1 =
172 Vec::<FpVar<F>>::new_input(cs.clone(), || Ok(self.z_i1.unwrap_or(vec![F::zero()])))?;
173 let computed_z_i1 =
174 self.FC
175 .generate_step_constraints(cs.clone(), 0, z_i.clone(), vec![])?;
176
177 use ark_r1cs_std::eq::EqGadget;
178 computed_z_i1.enforce_equal(&z_i1)?;
179 Ok(())
180 }
181}