let out_bytes = Sha256::evaluate(&(), z_i[0].into_bigint().to_bytes_le()).unwrap();
let out: Vec<F> = out_bytes.to_field_elements().unwrap();
@ -145,6 +156,7 @@ impl FCircuit for Sha256FCircuit {
_cs: ConstraintSystemRef<F>,
_i: usize,
z_i: Vec<FpVar<F>>,
_external_inputs: 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()?)?;
@ -172,22 +184,20 @@ This is useful for example if we want to fold multiple verifications of signatur
where each F is:
w_i
│ ┌────────────────────┐
│ │FCircuit │
│ │ │
└────►│ h =Hash(z_i[0],w_i)│
│ │ =Hash(v, w_i) │
────────►│ │ ├───────►
z_i=[v,0] │ └──►z_{i+1}=[h, 0] │ z_{i+1}=[h,0]
│ │
└────────────────────┘
w_i
│ ┌────────────────────┐
│ │FCircuit │
│ │ │
└────►│ h =Hash(z_i[0],w_i)│
────────►│ │ ├───────►
z_i │ └──►z_{i+1}=[h] │ z_{i+1}
│ │
└────────────────────┘
```
where each $w_i$ value is set at the `external_inputs` array.
The last state $z_i$ is used together with the external input w_i as inputs to compute the new state $z_{i+1}$.
The function F will output the new state in an array of two elements, where the second element is a 0. In other words, $z_{i+1} = [F([z_i, w_i]), 0]$, and the 0 will be replaced by $w_{i+1}$ in the next iteration, so $z_{i+2} = [F([z_{i+1}, w_{i+1}]), 0]$.
```rust
#[derive(Clone, Debug)]
@ -197,47 +207,48 @@ where
{
_f: PhantomData<F>,
poseidon_config: PoseidonConfig<F>,
external_inputs: Vec<F>,
}
impl<F:PrimeField> FCircuit<F> for ExternalInputsCircuits<F>
where
F: Absorb,
{
type Params = (PoseidonConfig<F>, Vec<F>); // where Vec<F> contains the external inputs
type Params = (PoseidonConfig<F>);
fn new(params: Self::Params) -> Self {
Self {
_f: PhantomData,
poseidon_config: params.0,
external_inputs: params.1,
}
}
fn state_len(&self) -> usize {
2
1
}
fn external_inputs_len(&self) -> usize {
1
}
/// computes the next state values in place, assigning z_{i+1} into z_i, and computing the new
/// computes the next state value for the step of F for the given z_i and external_inputs
@ -5,8 +5,11 @@ We can define the circuit to be folded in Circom. The only interface that we nee
```c
template FCircuit() {
signal input ivc_input[1];
signal output ivc_output[1];
signal input ivc_input[1]; // IVC state
signal input external_inputs[2]; // not state
signal output ivc_output[1]; // next IVC state
// [...]
}
component main {public [ivc_input]} = Example();
@ -14,18 +17,23 @@ component main {public [ivc_input]} = Example();
The `ivc_input` is the array that defines the initial state, and the `ivc_output` is the array that defines the output state after the step.
So for example, the following circuit does the traditional example at each step, which proves knowledge of $x$ such that $y==x^3 + x + 5$ for a known $y$:
So for example, the following circuit does the traditional example at each step, which proves knowledge of $x$ such that $y==x^3 + x + e_0 + e_1$ for a known $y$ ($e_i$ are the `external_inputs[i]`):