arnaucube
arnaucube/sonobe-docsPublic Self-hosted repository
chore: start updating doc on frontend
dmpierre pdaixmoreux@gmail.com
Fri, 12 Apr 2024 09:31:28 +0200
commit

aa47ac1d54cfd603dbb91bd9e7c76d03e5820ece

parent

e1924215d6358b6a1f5895ea310a2204b8ccc018

4 files changed, 136 insertions(+), 15 deletions(-)

jump to
M src/SUMMARY.md → src/SUMMARY.md

@@ -1,4 +1,5 @@

# sonobe docs + - [Introduction](README.md) - [Folding Schemes and Sonobe](folding-and-sonobe.md)
M src/folding-and-sonobe.md → src/folding-and-sonobe.md

@@ -4,7 +4,9 @@ ## Folding schemes overview

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_0), w_1), w_2), ...), w_{n-1})$.

<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. -![](imgs/sonobe-lib-pipeline.png) +<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).
M src/usage/frontend.md → src/usage/frontend.md

@@ -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 @@ ) -> Result<Vec<FpVar<F>>, SynthesisError>;

} ``` +# 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
M src/usage/overview.md → src/usage/overview.md

@@ -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