arnaucube
arnaucube/sonobe-docsPublic Self-hosted repository
sonobe-docs/src/usage/frontend-arkworks.md
src/usage/frontend-arkworks.mdRaw
  1# Arkworks frontend
  2
  3Let's walk through different simple examples implementing the `FCircuit` trait. By the end of this section, you will hopefully be familiar with how to integrate an `arkworks` circuit into Sonobe.
  4
  5You can find most of the following examples with the rest of code to run them at the [`examples`](https://github.com/privacy-scaling-explorations/sonobe/tree/main/examples) directory of the Sonobe repo.
  6
  7## Cubic circuit example
  8This first example implements the `FCircuit` trait for the R1CS example circuit from [Vitalik's post](https://www.vitalik.ca/general/2016/12/10/qap.html), which checks $x^3 + x + 5 == y$.
  9
 10$z_i$ is used as $x$, and $z_{i+1}$ is used as $y$, and at the next step, $z_{i+1}$ will be assigned to $z_i$, and a new $z_{i+1}$ will be computted.
 11
 12```rust
 13#[derive(Clone, Copy, Debug)]
 14pub struct CubicFCircuit<F: PrimeField> {
 15    _f: PhantomData<F>,
 16}
 17impl<F: PrimeField> FCircuit<F> for CubicFCircuit<F> {
 18    type Params = ();
 19    type ExternalInputs = ();
 20    type ExternalInputsVar = ();
 21
 22    fn new(_params: Self::Params) -> Result<Self, Error> {
 23        Ok(Self { _f: PhantomData })
 24    }
 25    fn state_len(&self) -> usize {
 26        1
 27    }
 28    fn generate_step_constraints(
 29        &self,
 30        cs: ConstraintSystemRef<F>,
 31        _i: usize,
 32        z_i: Vec<FpVar<F>>,
 33        _external_inputs: Self::ExternalInputsVar,
 34    ) -> Result<Vec<FpVar<F>>, SynthesisError> {
 35        let five = FpVar::<F>::new_constant(cs.clone(), F::from(5u32))?;
 36        let z_i = z_i[0].clone();
 37
 38        Ok(vec![&z_i * &z_i * &z_i + &z_i + &five])
 39    }
 40}
 41```
 42
 43
 44## Multiple inputs circuit example
 45
 46The following example has a state of 5 public elements. At each step, we will want the circuit to compute the next state by:
 47
 481. adding 4 to the first element
 492. adding 40 to the second element
 503. multiplying the third element by 4
 514. multiplying the fourth element by 40
 525. adding 100 to the fifth element
 53
 54
 55```rust
 56// Define a struct that will be our circuit. This struct will implement the FCircuit trait.
 57#[derive(Clone, Copy, Debug)]
 58pub struct MultiInputsFCircuit<F: PrimeField> {
 59    _f: PhantomData<F>,
 60}
 61impl<F: PrimeField> FCircuit<F> for MultiInputsFCircuit<F> {
 62    type Params = ();
 63    type ExternalInputs = ();
 64    type ExternalInputsVar = ();
 65
 66    fn new(_params: Self::Params) -> Result<Self, Error> {
 67        Ok(Self { _f: PhantomData })
 68    }
 69    fn state_len(&self) -> usize {
 70        5
 71    }
 72    /// generates the constraints for the step of F for the given z_i
 73    fn generate_step_constraints(
 74        &self,
 75        cs: ConstraintSystemRef<F>,
 76        _i: usize,
 77        z_i: Vec<FpVar<F>>,
 78        _external_inputs: Self::ExternalInputsVar,
 79    ) -> Result<Vec<FpVar<F>>, SynthesisError> {
 80        let four = FpVar::<F>::new_constant(cs.clone(), F::from(4u32))?;
 81        let forty = FpVar::<F>::new_constant(cs.clone(), F::from(40u32))?;
 82        let onehundred = FpVar::<F>::new_constant(cs.clone(), F::from(100u32))?;
 83        let a = z_i[0].clone() + four.clone();
 84        let b = z_i[1].clone() + forty.clone();
 85        let c = z_i[2].clone() * four;
 86        let d = z_i[3].clone() * forty;
 87        let e = z_i[4].clone() + onehundred;
 88
 89        Ok(vec![a, b, c, d, e])
 90    }
 91}
 92```
 93
 94
 95## Using external inputs
 96
 97In this example we set the state to be the previous state together with an external input, and the new state is an array which contains the new state and a zero which will be ignored.
 98
 99This is useful for example if we want to fold multiple verifications of signatures, where the circuit F checks the signature and is folded for each of the signatures and public keys. To keep things simpler, the following example does not verify signatures but does a similar approach with a chain of hashes, where each iteration hashes the previous step output ($z_i$) together with an external input ($w_i$).
100
101```
102       w_1     w_2     w_3     w_4     
103       │       │       │       │      
104       ▼       ▼       ▼       ▼      
105      ┌─┐     ┌─┐     ┌─┐     ┌─┐     
106─────►│F├────►│F├────►│F├────►│F├────►
107 z_1  └─┘ z_2 └─┘ z_3 └─┘ z_4 └─┘ z_5
108
109
110where each F is:
111  w_i                                        
112   │     ┌────────────────────┐              
113   │     │FCircuit            │              
114   │     │                    │              
115   └────►│ h =Hash(z_i[0],w_i)│              
116────────►│ │                  ├───────►      
117 z_i     │ └──►z_{i+1}=[h]    │  z_{i+1}
118         │                    │              
119         └────────────────────┘
120```
121
122where each $w_i$ value is set at the `external_inputs` array.
123
124The last state $z_i$ is used together with the external input w_i as inputs to compute the new state $z_{i+1}$.
125
126```rust
127use ark_crypto_primitives::{
128    crh::{
129        poseidon::constraints::{CRHGadget, CRHParametersVar},
130        poseidon::CRH,
131        CRHScheme, CRHSchemeGadget,
132    },
133    sponge::{poseidon::PoseidonConfig, Absorb},
134};
135
136#[derive(Clone, Debug)]
137pub struct ExternalInputsCircuits<F: PrimeField>
138where
139    F: Absorb,
140{
141    _f: PhantomData<F>,
142    poseidon_config: PoseidonConfig<F>,
143}
144impl<F: PrimeField> FCircuit<F> for ExternalInputsCircuit<F>
145where
146    F: Absorb,
147{
148    type Params = PoseidonConfig<F>;
149    type ExternalInputs = [F; 1];
150    type ExternalInputsVar = [FpVar<F>; 1];
151
152    fn new(params: Self::Params) -> Result<Self, Error> {
153        Ok(Self {
154            _f: PhantomData,
155            poseidon_config: params,
156        })
157    }
158    fn state_len(&self) -> usize {
159        1
160    }
161    /// generates the constraints and returns the next state value for the step of F for the given
162    /// z_i and external_inputs
163    fn generate_step_constraints(
164        &self,
165        cs: ConstraintSystemRef<F>,
166        _i: usize,
167        z_i: Vec<FpVar<F>>,
168        external_inputs: Self::ExternalInputsVar,
169    ) -> Result<Vec<FpVar<F>>, SynthesisError> {
170        let crh_params =
171            CRHParametersVar::<F>::new_constant(cs.clone(), self.poseidon_config.clone())?;
172        let hash_input: [FpVar<F>; 2] = [z_i[0].clone(), external_inputs[0].clone()];
173        let h = CRHGadget::<F>::evaluate(&crh_params, &hash_input)?;
174        Ok(vec![h])
175    }
176}
177```
178
179<br><br>
180
181# Complete examples
182You can find the complete examples with all the imports at [sonobe/examples](https://github.com/privacy-scaling-explorations/sonobe/tree/main/examples).