mirror of
https://github.com/arnaucube/sonobe-docs.git
synced 2026-02-10 05:06:45 +01:00
chore: start updating doc on frontend
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
# sonobe docs
|
||||
|
||||
- [Introduction](README.md)
|
||||
- [Folding Schemes and Sonobe](folding-and-sonobe.md)
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
|
||||
Folding schemes are a family of SNARKs for iterative computations, allowing to prove that a function $F$ applied $n$ times to an initial input $z_0$ results in $z_n$.
|
||||
|
||||
<img src="imgs/folding-main-idea-diagram.png" style="width:70%;" />
|
||||
<p align="center">
|
||||
<img src="imgs/folding-main-idea-diagram.png" style="width:70%;" />
|
||||
</p>
|
||||
|
||||
Where $w_i$ are the external witnesses used at each iterative step.
|
||||
|
||||
@@ -12,25 +14,26 @@ In other words, it allows to prove efficiently that $z_n = F(...~F(F(F(F(z_0, w_
|
||||
|
||||
<br>
|
||||
|
||||
The next 3 videos provide a good overview on the folding shcmes ideas:
|
||||
The next 3 videos provide a good overview of folding schemes:
|
||||
- In [this presentation](https://www.youtube.com/watch?v=Jj19k2AXH2k) (5 min) Abhiram Kothapalli explains the main idea of Nova folding scheme.
|
||||
- In [this presentation](https://youtu.be/IzLTpKWt-yg?t=6367) (20 min) Carlos Pérez overviews the features of folding schemes and what can be build with them.
|
||||
- In [this presentation](https://www.youtube.com/watch?v=SwonTtOQzAk) (1h) Justin Drake explains the folding schemes and Nova concepts.
|
||||
|
||||
- In [this presentation](https://youtu.be/IzLTpKWt-yg?t=6367) (20 min) Carlos Pérez overviews the features of folding schemes and what can be built with them.
|
||||
- In [this presentation](https://www.youtube.com/watch?v=SwonTtOQzAk) (1h) Justin Drake explains what a folding scheme is and Nova-related concepts.
|
||||
|
||||
## Sonobe overview
|
||||
|
||||
Sonobe is a folding schemes modular library to fold R1CS instances in an Incremental Verifiable computation (IVC) style. It also provides the tools required to generate a zkSNARK out of an IVC proof and to verify it on Ethereum's EVM.
|
||||
Sonobe is a modular folding schemes library. It allows developers to fold R1CS instances in an Incremental Verifiable computation (IVC) style. It also provides tools required to generate a zkSNARK out of an IVC proof. Developers can configure sonobe so that those proofs can also be verified on Ethereum's EVM.
|
||||
|
||||
The development flow using Sonobe looks like:
|
||||
|
||||
1. Define a circuit to be folded
|
||||
2. Set which folding scheme to be used (eg. Nova)
|
||||
3. Set a final decider to generate the final proof (eg. Spartan over Pasta curves)
|
||||
4. Generate the the decider verifier
|
||||
1. Define a circuit to be folded. This is done using a frontend such as [`circom`](https://github.com/iden3/circom) or [arkworks](https://github.com/arkworks-rs/r1cs-std).
|
||||
2. Set which folding scheme to be used (eg. Nova).
|
||||
3. Set a final decider to generate the final proof (eg. Spartan over Pasta curves).
|
||||
4. Generate the decider verifier.
|
||||
|
||||

|
||||
<p align="center">
|
||||
<img src="imgs/sonobe-lib-pipeline.png" style="width:70%;" />
|
||||
</p>
|
||||
|
||||
The folding scheme and decider used can be swapped respectively with a few lines of code (eg. switching from a Decider that uses two Spartan proofs over a cycle of curves, to a Decider that uses a single Groth16 proof over the BN254 to be verified in an Ethereum smart contract).
|
||||
|
||||
Complete examples can be found at [sonobe/folding-schemes/examples](https://github.com/privacy-scaling-explorations/sonobe/tree/main/folding-schemes/examples)
|
||||
Complete examples can be found at [sonobe/folding-schemes/examples](https://github.com/privacy-scaling-explorations/sonobe/tree/main/folding-schemes/examples).
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
# Frontend
|
||||
|
||||
The frontend interface allows to define the circuit to be folded. The available frontends are arkworks are Circom.
|
||||
The 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`.
|
||||
|
||||
We just need to fulfill the [`FCircuit` trait](https://github.com/privacy-scaling-explorations/sonobe/blob/main/sonobe/src/frontend/mod.rs):
|
||||
# The `FCircuit` trait
|
||||
|
||||
To 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:
|
||||
|
||||
```rust
|
||||
/// FCircuit defines the trait of the circuit of the F function, which is the one being folded (ie.
|
||||
@@ -41,3 +43,117 @@ pub trait FCircuit<F: PrimeField>: Clone + Debug {
|
||||
}
|
||||
```
|
||||
|
||||
# Example
|
||||
|
||||
Let'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.
|
||||
|
||||
## Folding a simple circuit
|
||||
|
||||
The 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:
|
||||
|
||||
1. adding 4 to the first element
|
||||
2. adding 40 to the second element
|
||||
3. multiplying the third element by 4
|
||||
4. multiplying the fourth element by 40
|
||||
5. adding 100 to the fifth element
|
||||
|
||||
Let's implement this now:
|
||||
|
||||
```rust
|
||||
// Define a struct that will be our circuit. This struct will implement the FCircuit trait.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct MultiInputsFCircuit<F: PrimeField> {
|
||||
_f: PhantomData<F>,
|
||||
}
|
||||
|
||||
// Implement the FCircuit trait for the struct
|
||||
impl<F: PrimeField> FCircuit<F> for MultiInputsFCircuit<F> {
|
||||
type Params = ();
|
||||
|
||||
fn new(_params: Self::Params) -> Self {
|
||||
Self { _f: PhantomData }
|
||||
}
|
||||
|
||||
fn state_len(&self) -> usize {
|
||||
5 // This circuit has 5 inputs
|
||||
}
|
||||
|
||||
// Computes the next state values in place, assigning z_{i+1} into z_i, and computing the new z_{i+1}
|
||||
// We want the `step_native` method to implement the same logic as the `generate_step_constraints` method
|
||||
fn step_native(&self, _i: usize, z_i: Vec<F>) -> Result<Vec<F>, Error> {
|
||||
let a = z_i[0] + F::from(4_u32);
|
||||
let b = z_i[1] + F::from(40_u32);
|
||||
let c = z_i[2] * F::from(4_u32);
|
||||
let d = z_i[3] * F::from(40_u32);
|
||||
let e = z_i[4] + F::from(100_u32);
|
||||
|
||||
Ok(vec![a, b, c, d, e]) // The length of the returned vector should match `state_len`
|
||||
}
|
||||
|
||||
/// generates R1CS constraints for the step of F for the given z_i
|
||||
fn generate_step_constraints(
|
||||
&self,
|
||||
cs: ConstraintSystemRef<F>,
|
||||
_i: usize,
|
||||
z_i: Vec<FpVar<F>>,
|
||||
) -> Result<Vec<FpVar<F>>, SynthesisError> {
|
||||
// Implementing the circuit constraints
|
||||
let four = FpVar::<F>::new_constant(cs.clone(), F::from(4u32))?;
|
||||
let forty = FpVar::<F>::new_constant(cs.clone(), F::from(40u32))?;
|
||||
let onehundred = FpVar::<F>::new_constant(cs.clone(), F::from(100u32))?;
|
||||
let a = z_i[0].clone() + four.clone();
|
||||
let b = z_i[1].clone() + forty.clone();
|
||||
let c = z_i[2].clone() * four;
|
||||
let d = z_i[3].clone() * forty;
|
||||
let e = z_i[4].clone() + onehundred;
|
||||
|
||||
Ok(vec![a, b, c, d, e]) // The length of the returned vector should match `state_len`
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Folding a `Sha256` circuit
|
||||
|
||||
We 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.
|
||||
|
||||
```rust
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Sha256FCircuit<F: PrimeField> {
|
||||
_f: PhantomData<F>,
|
||||
}
|
||||
impl<F: PrimeField> FCircuit<F> for Sha256FCircuit<F> {
|
||||
type Params = ();
|
||||
|
||||
fn new(_params: Self::Params) -> Self {
|
||||
Self { _f: PhantomData }
|
||||
}
|
||||
fn state_len(&self) -> usize {
|
||||
1
|
||||
}
|
||||
|
||||
/// computes the next state values in place, assigning z_{i+1} into z_i, and computing the new
|
||||
/// z_{i+1}
|
||||
fn step_native(&self, _i: usize, z_i: Vec<F>) -> Result<Vec<F>, Error> {
|
||||
let out_bytes = Sha256::evaluate(&(), z_i[0].into_bigint().to_bytes_le()).unwrap();
|
||||
let out: Vec<F> = out_bytes.to_field_elements().unwrap();
|
||||
|
||||
Ok(vec![out[0]])
|
||||
}
|
||||
|
||||
/// generates the constraints for the step of F for the given z_i
|
||||
fn generate_step_constraints(
|
||||
&self,
|
||||
_cs: ConstraintSystemRef<F>,
|
||||
_i: usize,
|
||||
z_i: Vec<FpVar<F>>,
|
||||
) -> Result<Vec<FpVar<F>>, SynthesisError> {
|
||||
let unit_var = UnitVar::default();
|
||||
let out_bytes = Sha256Gadget::evaluate(&unit_var, &z_i[0].to_bytes()?)?;
|
||||
let out = out_bytes.0.to_constraint_field()?;
|
||||
Ok(vec![out[0].clone()])
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Folding a circuit with public and private inputs
|
||||
@@ -1,6 +1,7 @@
|
||||
# Usage
|
||||
|
||||
This section showcases how to use the Sonobe library to:
|
||||
- Define a circuit to be folded using the Frontend
|
||||
- Define a circuit to be folded using a frontend such as [`circom`](https://github.com/iden3/circom) or [arkworks](https://github.com/arkworks-rs/r1cs-std).
|
||||
- Fold the circuit using one of the folding schemes
|
||||
- Generate a final Decider proof
|
||||
- Verify the Decider proof, and in Ethereum case, generate a Solidity verifier
|
||||
|
||||
Reference in New Issue
Block a user