arnaucube
arnaucube/sonobe-docsPublic Self-hosted repository
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.