sonobe-docs/src/usage/experimental-frontends.md
src/usage/experimental-frontends.mdRaw
1# Experimental frontends
2> Warning: the following frontends are experimental and some computational and time overhead is expected when using them compared to directly using the [arkworks frontend](frontend-arkworks.md).
3
4
5This section overviews how to use the various experimental frontends:
6- [implementing new frontends](#implementing-new-frontends)
7- [Circom frontend](#circom-frontend)
8- [Noname frontend](#noname-frontend)
9- [Noir frontend](#noir-frontend)
10
11
12## Implementing new frontends
13
14Support for new frontends can be added (even from outside this repo) by implementing the [`FCircuit` trait](https://github.com/privacy-scaling-explorations/sonobe/blob/main/folding-schemes/src/frontend/mod.rs).
15
16
17## Circom frontend
18 > **Note**: Circom frontend will be significantly slower than the Arkworks frontend. We explain below how to implement a custom `step_native` function with your circom circuits to speed things up!
19
20Experimental frontend using [arkworks/circom-compat](https://github.com/arkworks-rs/circom-compat).
21
22We can define the circuit to be folded in Circom. The only interface that we need to fit in is:
23
24```c
25template Example(ivc_state_len, aux_inputs_len) {
26 signal input ivc_input[ivc_state_len]; // IVC state
27 signal input external_inputs[aux_inputs_len]; // external inputs, not part of the folding state
28
29 signal output ivc_output[ivc_state_len]; // next IVC state
30
31 // here it goes the Circom circuit logic
32}
33component main {public [ivc_input]} = Example();
34```
35
36The `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. Both need to be of the same size. The `external_inputs` array expects auxiliary input values.
37
38
39In the following image, the `ivc_input`=$z_i$, the `external_inputs`=$w_i$, and the `ivc_output`=$z_{i+1}$, and $F$ is the logic of our Circom circuit:
40<p align="center">
41 <img src="../imgs/folding-main-idea-diagram.png" style="width:70%;" />
42</p>
43
44<br>
45
46So for example, the following circuit proves (at each folding step) knowledge of $x$ such that $y==x^3 + e_0x + e_1$ for a known $y$ ($e_i$ are the `external_inputs[i]`):
47
48```c
49pragma circom 2.0.3;
50
51template CubicCircuit() {
52 signal input ivc_input[1]; // IVC state
53 signal input external_inputs[2]; // not part of the state
54
55 signal output ivc_output[1]; // next IVC state
56
57 signal temp1;
58 signal temp2;
59
60 temp1 <== ivc_input[0] * ivc_input[0];
61 temp2 <== ivc_input[0] * external_inputs[0];
62 ivc_output[0] <== temp1 * ivc_input[0] + temp2 + external_inputs[1];
63}
64
65component main {public [ivc_input]} = CubicCircuit();
66```
67
68<br><br>
69
70Once your `circom` circuit is ready, you can instantiate it with Sonobe. To do this, you will need the `struct CircomFCircuit`.
71
72```rust
73// we load our circom compiled R1CS, along with the witness wasm calculator
74let r1cs_path = PathBuf::from("./src/frontend/circom/test_folder/cubic_circuit.r1cs");
75let wasm_path =
76 PathBuf::from("./src/frontend/circom/test_folder/cubic_circuit_js/cubic_circuit.wasm");
77let f_circuit_params = (r1cs_path, wasm_path, 1, 2); // state_len:1, external_inputs_len:2
78let f_circuit = CircomFCircuit::<Fr>::new(f_circuit_params).unwrap();
79
80
81// [optional] to speed things up, you can define a custom step function to avoid
82// defaulting to the snarkjs witness calculator for the native computations,
83// which would be slower than rust native operations
84circom_fcircuit.set_custom_step_native(Rc::new(|_i, z_i, _external| {
85 let z = z_i[0];
86 Ok(vec![z * z * z + z + Fr::from(5)])
87 }));
88
89pub type N =
90 Nova<G1, GVar, G2, GVar2, CircomFCircuit<Fr>, KZG<'static, Bn254>, Pedersen<G2>, false>;
91pub type D = DeciderEth<
92 G1,
93 GVar,
94 G2,
95 GVar2,
96 CircomFCircuit<Fr>,
97 KZG<'static, Bn254>,
98 Pedersen<G2>,
99 Groth16<Bn254>,
100 N,
101>;
102
103let poseidon_config = poseidon_canonical_config::<Fr>();
104let mut rng = rand::rngs::OsRng;
105
106// prepare the Nova prover & verifier params
107let nova_preprocess_params = PreprocessorParam::new(poseidon_config, f_circuit.clone());
108let nova_params = N::preprocess(&mut rng, &nova_preprocess_params).unwrap();
109
110// initialize the folding scheme engine, in this case we use Nova
111let mut nova = N::init(&nova_params, f_circuit.clone(), z_0).unwrap();
112
113// run n steps of the folding iteration
114for (i, external_inputs_at_step) in external_inputs.iter().enumerate() {
115 let start = Instant::now();
116 // the last parameter at 'nova.prove_step()' is for schemes that support
117 // folding more than 2 instances at each fold, such as HyperNova. Since
118 // we're using Nova, we just set it to 'None'
119 nova.prove_step(rng, external_inputs_at_step.clone(), None)
120 .unwrap();
121 println!("Nova::prove_step {}: {:?}", i, start.elapsed());
122}
123
124```
125You can find a full example using Nova to fold a Circom circuit at [sonobe/examples/circom_full_flow.rs](https://github.com/privacy-scaling-explorations/sonobe/blob/main/examples/circom_full_flow.rs).
126
127
128## Noname frontend
129
130Experimental [Noname](https://github.com/zksecurity/noname/). Under the hood, we [bridge](https://github.com/dmpierre/ark-noname/tree/feat/sonobe-integration) compiled Noname circuits to arkworks R1CS. Our Noname integration does not support Noname's standard library for now.
131
132Using Noname with sonobe is similar to using any other frontend. Sonobe expects that the length of your public and private (external) inputs match what the Noname circuit expects. Note that sonobe does not expect your public inputs to follow some specific naming convention when using Noname: it will assume that whatever public input variable you have is the IVC state.
133
134This [example](https://github.com/privacy-scaling-explorations/sonobe/blob/main/examples/noname_full_flow.rs) shows how to fold a simple Noname circuit having both public and external, private inputs.
135
136
137## Noir frontend
138
139Experimental [Noir](https://noir-lang.org/) frontend. Under the hood, we [bridge](https://github.com/dmpierre/arkworks_backend) compiled Noir circuits to arkworks R1CS. Beware that sonobe assumes that the compiled Noir circuit that is being folded does not use any other opcode than an arithmetic gate: you can not fold circuits calling [oracles](https://noir-lang.org/docs/noir/concepts/oracles) or using [unconstrained](https://noir-lang.org/docs/noir/concepts/unconstrained/) functions. You should be able to use Noir's standard library though.
140
141Using Noir with sonobe is similar to using any other frontend. Sonobe expects that the length of your public and private (external) inputs match what the Noir circuit expects. Note that sonobe does not expect your public inputs to follow some specific naming convention when using Noir: it will assume that whatever public input variable you have is the IVC state.
142
143This [example](https://github.com/privacy-scaling-explorations/sonobe/blob/main/examples/noir_full_flow.rs) shows how to fold a poseidon circuit from the Noir standard library.