Browse Source

chore: add example with private state

main
dmpierre 1 year ago
parent
commit
a8c5153441
1 changed files with 75 additions and 12 deletions
  1. +75
    -12
      src/usage/frontend.md

+ 75
- 12
src/usage/frontend.md

@ -1,6 +1,6 @@
# Frontend
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`.
The frontend interface allows to define the circuit to be folded. The currently 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`.
# The `FCircuit` trait
@ -14,14 +14,14 @@ To be folded with sonobe, a circuit needs to implement the [`FCircuit` trait](ht
pub trait FCircuit<F: PrimeField>: Clone + Debug {
type Params: Debug;
/// returns a new FCircuit instance
/// Returns a new FCircuit instance
fn new(params: Self::Params) -> Self;
/// returns the number of elements in the state of the FCircuit, which corresponds to the
/// Returns the number of elements in the state of the FCircuit, which corresponds to the
/// FCircuit inputs.
fn state_len(&self) -> usize;
/// computes the next state values in place, assigning z_{i+1} into z_i, and computing the new
/// Computes the next state values in place, assigning z_{i+1} into z_i, and computing the new
/// z_{i+1}
fn step_native(
// this method uses self, so that each FCircuit implementation (and different frontends)
@ -31,7 +31,7 @@ pub trait FCircuit: Clone + Debug {
z_i: Vec<F>,
) -> Result<Vec<F>, Error>;
/// generates the constraints for the step of F for the given z_i
/// Generates the constraints for the step of F for the given z_i
fn generate_step_constraints(
// this method uses self, so that each FCircuit implementation (and different frontends)
// can hold a state if needed to store data to generate the constraints.
@ -45,7 +45,7 @@ pub trait FCircuit: 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.
Let'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.
## Folding a simple circuit
@ -90,7 +90,7 @@ impl FCircuit for MultiInputsFCircuit {
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
/// Generates R1CS constraints for the step of F for the given z_i
fn generate_step_constraints(
&self,
cs: ConstraintSystemRef<F>,
@ -110,18 +110,21 @@ impl FCircuit for MultiInputsFCircuit {
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.
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.
Note that the logic here is also very similar to the previous example: write a struct that will hold the circuit, implement the `FCircuit` trait for the struct, ensure that the length of the state is correct, and implement the `step_native` and `generate_step_constraints` methods.
```rust
// Define a struct that will be our circuit. This struct will implement the FCircuit trait.
#[derive(Clone, Copy, Debug)]
pub struct Sha256FCircuit<F: PrimeField> {
_f: PhantomData<F>,
}
impl<F: PrimeField> FCircuit<F> for Sha256FCircuit<F> {
type Params = ();
@ -132,7 +135,7 @@ impl FCircuit for Sha256FCircuit {
1
}
/// computes the next state values in place, assigning z_{i+1} into z_i, and computing the new
/// 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();
@ -141,7 +144,7 @@ impl FCircuit for Sha256FCircuit {
Ok(vec![out[0]])
}
/// generates the constraints for the step of F for the given z_i
/// Generates the constraints for the step of F for the given z_i
fn generate_step_constraints(
&self,
_cs: ConstraintSystemRef<F>,
@ -156,4 +159,64 @@ impl FCircuit for Sha256FCircuit {
}
```
## Folding a circuit with public and private inputs
## Folding a circuit with public and private inputs
Sometimes, the circuit to be folded will have private inputs. Let's see how we can setup such a circuit to be folded with sonobe. Again, the logic here is also very similar to our previous examples. The main difference is that the `struct` which will hold the circuit also holds a `Vec` of private inputs.
```rust
#[derive(Clone, Debug)]
pub struct ACircuitWithPrivateState<F: PrimeField> {
_f: PhantomData<F>,
private_state: Vec<F>, // private inputs, here a `Vec` of field elements, but you can specify whatever type you prefer here
}
impl<F: PrimeField> FCircuit<F> for ACircuitWithPrivateState<F> {
type Params = Vec<F>;
fn new(params: Self::Params) -> Self {
Self {
_f: PhantomData,
private_state: params,
}
}
fn state_len(&self) -> usize {
3 // the length of the state should match the size of the public inputs, not including the private inputs
}
fn step_native(&self, i: usize, z_i: Vec<F>) -> Result<Vec<F>, folding_schemes::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 = self.private_state[0] * a;
let e = self.private_state[1] * c;
Ok(vec![a, b, c])
Ok(new_z_i)
}
fn generate_step_constraints(
&self,
cs: ConstraintSystemRef<F>,
i: usize,
z_i: Vec<ark_r1cs_std::fields::fp::FpVar<F>>,
) -> Result<Vec<ark_r1cs_std::fields::fp::FpVar<F>>, SynthesisError> {
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))?;
// we still need to allocate our private state as witnesses
let priv_var_0 = FpVar::<F>::new_witness(cs.clone(), || Ok(self.private_state[0].clone()))?;
let priv_var_1 = FpVar::<F>::new_witness(cs.clone(), || Ok(self.private_state[1].clone()))?;
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 = priv_var_0 * a;
let e = priv_var_1 * c;
Ok(vec![a, b, c])
}
}
```

Loading…
Cancel
Save