arnaucube
arnaucube/sonobe-docsPublic Self-hosted repository
sonobe-docs/src/usage/frontend.md
src/usage/frontend.mdRaw
  1# Frontend
  2
  3The frontend interface allows to define the circuit to be folded. The available frontends are [`circom`](https://github.com/iden3/circom) or [arkworks](https://github.com/arkworks-rs/r1cs-std). We will show here how to define a circuit using `arkworks`.
  4
  5# The `FCircuit` trait
  6
  7To be folded with sonobe, a circuit needs to implement the [`FCircuit` trait](https://github.com/privacy-scaling-explorations/sonobe/blob/main/sonobe/src/frontend/mod.rs). This trait defines the methods that sonobe expects from the circuit to be folded. It corresponds to the $F$ function that is being folded. The trait has the following methods:
  8
  9```rust
 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) -> Self;
 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    /// computes the next state values in place, assigning z_{i+1} into z_i, and computing the new
 25    /// z_{i+1}
 26    fn step_native(
 27        // this method uses self, so that each FCircuit implementation (and different frontends)
 28        // can hold a state if needed to store data to compute the next state.
 29        &self,
 30        i: usize,
 31        z_i: Vec<F>,
 32    ) -> Result<Vec<F>, Error>;
 33
 34    /// generates the constraints for the step of F for the given z_i
 35    fn generate_step_constraints(
 36        // this method uses self, so that each FCircuit implementation (and different frontends)
 37        // can hold a state if needed to store data to generate the constraints.
 38        &self,
 39        cs: ConstraintSystemRef<F>,
 40        i: usize,
 41        z_i: Vec<FpVar<F>>,
 42    ) -> Result<Vec<FpVar<F>>, SynthesisError>;
 43}
 44```
 45
 46# Example
 47
 48Let's walk through different simple examples implementing the `FCircuit` trait. By the end, you will hopefully be familiar with how to integrate an `arkworks` circuit into sonobe.
 49
 50## Folding a simple circuit
 51
 52The circuit we will fold has a state of 5 public elements. At each step, we will want the circuit to compute the next state by:
 53
 541. adding 4 to the first element
 552. adding 40 to the second element
 563. multiplying the third element by 4
 574. multiplying the fourth element by 40
 585. adding 100 to the fifth element
 59
 60Let's implement this now:
 61
 62```rust
 63// Define a struct that will be our circuit. This struct will implement the FCircuit trait.
 64#[derive(Clone, Copy, Debug)]
 65pub struct MultiInputsFCircuit<F: PrimeField> {
 66    _f: PhantomData<F>,
 67}
 68
 69// Implement the FCircuit trait for the struct
 70impl<F: PrimeField> FCircuit<F> for MultiInputsFCircuit<F> {
 71    type Params = ();
 72
 73    fn new(_params: Self::Params) -> Self {
 74        Self { _f: PhantomData }
 75    }
 76
 77    fn state_len(&self) -> usize {
 78        5 // This circuit has 5 inputs
 79    }
 80
 81    // Computes the next state values in place, assigning z_{i+1} into z_i, and computing the new z_{i+1}
 82    // We want the `step_native` method to implement the same logic as the `generate_step_constraints` method
 83    fn step_native(&self, _i: usize, z_i: Vec<F>) -> Result<Vec<F>, Error> {
 84        let a = z_i[0] + F::from(4_u32);
 85        let b = z_i[1] + F::from(40_u32);
 86        let c = z_i[2] * F::from(4_u32);
 87        let d = z_i[3] * F::from(40_u32);
 88        let e = z_i[4] + F::from(100_u32);
 89
 90        Ok(vec![a, b, c, d, e]) // The length of the returned vector should match `state_len`
 91    }
 92
 93    /// generates R1CS constraints for the step of F for the given z_i
 94    fn generate_step_constraints(
 95        &self,
 96        cs: ConstraintSystemRef<F>,
 97        _i: usize,
 98        z_i: Vec<FpVar<F>>,
 99    ) -> Result<Vec<FpVar<F>>, SynthesisError> {
100        // Implementing the circuit constraints
101        let four = FpVar::<F>::new_constant(cs.clone(), F::from(4u32))?;
102        let forty = FpVar::<F>::new_constant(cs.clone(), F::from(40u32))?;
103        let onehundred = FpVar::<F>::new_constant(cs.clone(), F::from(100u32))?;
104        let a = z_i[0].clone() + four.clone();
105        let b = z_i[1].clone() + forty.clone();
106        let c = z_i[2].clone() * four;
107        let d = z_i[3].clone() * forty;
108        let e = z_i[4].clone() + onehundred;
109
110        Ok(vec![a, b, c, d, e]) // The length of the returned vector should match `state_len`
111    }
112}
113
114```
115
116## Folding a `Sha256` circuit
117
118We will fold a simple `Sha256` circuit. The circuit has a state of 1 public element. At each step, we will want the circuit to compute the next state by applying the `Sha256` function to the current state.
119
120```rust
121#[derive(Clone, Copy, Debug)]
122pub struct Sha256FCircuit<F: PrimeField> {
123    _f: PhantomData<F>,
124}
125impl<F: PrimeField> FCircuit<F> for Sha256FCircuit<F> {
126    type Params = ();
127
128    fn new(_params: Self::Params) -> Self {
129        Self { _f: PhantomData }
130    }
131    fn state_len(&self) -> usize {
132        1
133    }
134
135    /// computes the next state values in place, assigning z_{i+1} into z_i, and computing the new
136    /// z_{i+1}
137    fn step_native(&self, _i: usize, z_i: Vec<F>) -> Result<Vec<F>, Error> {
138        let out_bytes = Sha256::evaluate(&(), z_i[0].into_bigint().to_bytes_le()).unwrap();
139        let out: Vec<F> = out_bytes.to_field_elements().unwrap();
140
141        Ok(vec![out[0]])
142    }
143
144    /// generates the constraints for the step of F for the given z_i
145    fn generate_step_constraints(
146        &self,
147        _cs: ConstraintSystemRef<F>,
148        _i: usize,
149        z_i: Vec<FpVar<F>>,
150    ) -> Result<Vec<FpVar<F>>, SynthesisError> {
151        let unit_var = UnitVar::default();
152        let out_bytes = Sha256Gadget::evaluate(&unit_var, &z_i[0].to_bytes()?)?;
153        let out = out_bytes.0.to_constraint_field()?;
154        Ok(vec![out[0].clone()])
155    }
156}
157```
158
159## Folding a circuit with public and private inputs