mirror of
https://github.com/arnaucube/ethdos-fold.git
synced 2026-01-11 16:31:34 +01:00
document, add circuit diagram
This commit is contained in:
11
.github/workflows/ci.yml
vendored
11
.github/workflows/ci.yml
vendored
@@ -18,3 +18,14 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Clippy
|
||||
run: cargo clippy --all-targets --all-features
|
||||
typos:
|
||||
if: github.event.pull_request.draft == false
|
||||
name: Spell Check with Typos
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use typos with config file
|
||||
uses: crate-ci/typos@master
|
||||
with:
|
||||
config: .github/workflows/typos.toml
|
||||
|
||||
|
||||
2
.github/workflows/typos.toml
vendored
Normal file
2
.github/workflows/typos.toml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
[default.extend-words]
|
||||
groth = "groth"
|
||||
@@ -26,9 +26,7 @@ rand_core = {version = "0.6", default-features = false}
|
||||
base64 = "0.22.1"
|
||||
|
||||
folding-schemes = { git = "https://github.com/privacy-scaling-explorations/sonobe", package = "folding-schemes", features=["light-test"], rev="c6f1a246e0705582a75de6becf4ad21f325fa5a1"}
|
||||
# folding-schemes = { path = "../sonobe/sonobe_FCIRCUIT-EXTERNALINPUTS-TO-VEC/folding-schemes", package = "folding-schemes", features=["light-test"]}
|
||||
arkeddsa = { git = "https://github.com/arnaucube/arkeddsa", features=["r1cs"], rev="0a9ea7ac1df07363af0fda723e313e775563b9f4"}
|
||||
# arkeddsa = { path = "../arkeddsa_TE-to-C", features=["r1cs"]}
|
||||
blake2 = "0.10"
|
||||
|
||||
|
||||
|
||||
39
README.md
39
README.md
@@ -24,22 +24,51 @@ So for example, in the previous diagram:
|
||||
- $pk_3$ is 3 degrees of distance from $pk_0$
|
||||
- $pk_3$ has signed $pk_2$, who has signed $pk_1$, who has signed $pk_0$
|
||||
- $pk_B$ is 4 degrees of distance from $pk_0$
|
||||
- $pk_B$ has signed $pk_A$, who has signed $pk_2$, who has signed $pk_2$, who has signed $pk_1$
|
||||
- $pk_B$ has signed $pk_A$, who has signed $pk_2$, who has signed $pk_1$, who has signed $pk_0$
|
||||
- $pk_\beta$ is 3 degrees of distance from $pk_0$
|
||||
- $pk_\beta$ has signed $pk_\alpha$, who has signed $pk_1$, who has signed $pk_0$
|
||||
|
||||
With folding schemes, we can map those relations into an IVC model, where at each recursive step we're proving the [`FCircuit` relation](https://github.com/arnaucube/ethdos-fold/blob/main/src/fcircuit.rs) (key part: the method `EthDosCircuit.generate_step_constraints`).
|
||||
With folding schemes, we can map those relations into an IVC model, where at each recursive step we're proving the [`FCircuit` relation](https://github.com/arnaucube/ethdos-fold/blob/main/src/fcircuit.rs) (key part: the method `EthDosCircuit.generate_step_constraints`), which ensures that the new state ($s_{i+1}$) comes from the previous state ($s_i$) with the verification of the new signature of the $pk_{i+1}$ over the previous public key $pk_i$.
|
||||
|
||||
The *state* of the IVC is $s_i = [pk_0, pk_i, i]$, where $pk_i$ is the public key $i$ degrees of distance from $pk_0$. At each step $i$ we have the IVC proof $\pi_i$, which proves this relation.
|
||||

|
||||
|
||||
The *state* of the IVC is $s_{i+1} = [pk_0, pk_{i+1}, i+1]$, where $pk_i$ is the public key $i$ degrees of distance from $pk_0$, and $pk_{i+1}$ is $i+1$ degrees of distance from $pk_0$. At each step $i$ we have the IVC proof $\pi_i$, which proves this relation.
|
||||
|
||||
The following diagram shows the relation between the states and signatures at each folding step, showing also divergent paths.
|
||||
|
||||

|
||||
|
||||
Each new folding step, only needs to have the previous step's state ($s_i = [pk_0, pk_i, i]$) and the respective IVC proof ($\pi_i$), which proves that the given public key $pk_i$ is $i$ degrees of distance from the public key $pk_0$.
|
||||
A new recursive step is done from the $\pi_i$ and the $s_i$, and by inputing the new signature $sig_{pk_{i+1}}(pk_i)$, which is at degree of distance $i+1$ from $pk_0$.
|
||||
A new recursive step is done from the $\pi_i$ and the $s_i$, and by inputting the new signature $sig_{pk_{i+1}}(pk_i)$, which is at degree of distance $i+1$ from $pk_0$.
|
||||
|
||||
Notice that in order to generate the proof of relations between different public keys, it is not necessary to know any of their private keys, but just by knowing their public keys and having their signatures suffices to generate the proofs.
|
||||
Notice that in order to generate the proof of relations between different public keys, it is not necessary to know any of their private keys, but just by knowing their public keys and having their signatures suffices to generate the proofs. So for example the signatures could be publicly accessible, and any user could just fetch them to generate their specific proofs of degrees of distance from other keys.
|
||||
|
||||
|
||||
## Code structure
|
||||
|
||||
As you can see, thanks to the simplicity & modularity of Sonobe and arkworks, this whole implementation reduces to defining the [`FCircuit` trait](https://github.com/arnaucube/ethdos-fold/blob/main/src/fcircuit.rs), which takes less than 70 lines of code, the key part being the method `generate_step_constraints` which takes <40 lines of code.
|
||||
|
||||
Additionally, we can swap between folding schemes:
|
||||
|
||||
With Sonobe we define the Folding Scheme being used at the line (file `src/lib.rs`):
|
||||
```rust
|
||||
type FS = Nova<G1, G2, FC, Pedersen<G1>, Pedersen<G2>>;
|
||||
```
|
||||
which we could switch it to use HyperNova, is as simple as updating the previous line to:
|
||||
|
||||
```rust
|
||||
type FS = HyperNova< G1, G2, FC, Pedersen<G1>, Pedersen<G2>, 1, 1>;
|
||||
```
|
||||
|
||||
similarly we can switch to using ProtoGalaxy folding scheme:
|
||||
```rust
|
||||
type FS = ProtoGalaxy<G1, G2, FC, Pedersen<G1>, Pedersen<G2>>;
|
||||
```
|
||||
|
||||
And the rest of the code would remain the same, while using a completely different folding scheme.
|
||||
|
||||
We can also use any arkworks available cycle of curves at the `G1` and `G2`, the current implementation uses BN254 and Grumpkin curves, since we're verifying EdDSA signatures over the BabyJubJub curve.
|
||||
|
||||
|
||||
## Some numbers
|
||||
> Current numbers using the Sonobe version at commit `c6f1a246e0705582a75de6becf4ad21f325fa5a1`.
|
||||
|
||||
BIN
img/ethdos-fcircuit.png
Normal file
BIN
img/ethdos-fcircuit.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
@@ -5,7 +5,6 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>ETHdos fold</title>
|
||||
|
||||
<!-- Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
@@ -69,29 +68,26 @@
|
||||
</div>
|
||||
<div class="col-md-6 box">
|
||||
<h2 class="text-center mb-4">ETHdos fold</h2>
|
||||
<p style="font-style:italic;font-size:80%;" class="text-center">(simple browser demo)</p>
|
||||
<p>Follows the ideas of ETHdos (<a target="_blank" href="https://ethdos.xyz/blog">https://ethdos.xyz/blog</a>), but using Folding Schemes.</p>
|
||||
<p style="font-size:90%;">It uses <a target="_blank" href="https://github.com/privacy-scaling-explorations/sonobe">Sonobe</a> under the hood, compiled to WASM.</a>
|
||||
<p style="font-style:italic;font-size:80%;">
|
||||
Current version does not parallelize in wasm. Same execution can be run natively (no wasm), instructions <a target="_blank" href="https://github.com/arnaucube/ethdos-fold">in the repo</a>.<br>
|
||||
In the same laptop, natively takes ~290ms per step, in-browser takes ~1700ms per step.
|
||||
Current version does not parallelize in wasm. Same execution can be run natively (no wasm), instructions <a target="_blank" href="https://github.com/arnaucube/ethdos-fold">in the ethdos-fold repo</a>.<br>
|
||||
</p>
|
||||
|
||||
<button id="btn_gen_params" class="btn btn-primary">1. gen_params</button>
|
||||
|
||||
<div class="mb-3">
|
||||
<!-- <label for="params" class="form-label">params:</label> -->
|
||||
<textarea id="params" class="form-control" rows="3">params</textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<button id="btn_gen_sigs" class="btn btn-primary">2. gen_sigs</button><br>
|
||||
<!-- <label for="sigs" class="form-label">sigs and pks:</label> -->
|
||||
<textarea id="sigs" class="form-control" rows="3">sigs and pks</textarea>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<button id="btn_fold_sigs" class="btn btn-primary">3. fold_sigs</button><br>
|
||||
<!-- <label for="ivc_proof" class="form-label">ivc proof:</label> -->
|
||||
<textarea id="ivc_proof" class="form-control" rows="3">ivc proof</textarea>
|
||||
</div>
|
||||
<button id="btn_verify_proof" class="btn btn-primary">4. verify_proof</button>
|
||||
@@ -108,7 +104,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<script type="module">
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//! This file contains the FCircuit (Sonobe's trait) implementation for the ETHdos logic.
|
||||
use ark_crypto_primitives::sponge::{
|
||||
constraints::CryptographicSpongeVar,
|
||||
poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig},
|
||||
@@ -92,6 +93,7 @@ where
|
||||
Ok([vec![pk_0_x, pk_0_y], pk_i1_xy, vec![degree]].concat())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ark_bn254::{Fr, G1Projective as G1};
|
||||
use ark_ec::AffineRepr;
|
||||
use ark_grumpkin::Projective as G2;
|
||||
use ark_std::Zero;
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
use arkeddsa::ed_on_bn254_twist::{constraints::EdwardsVar, EdwardsProjective};
|
||||
|
||||
use folding_schemes::{
|
||||
commitment::pedersen::Pedersen,
|
||||
folding::nova::{Nova, PreprocessorParam},
|
||||
frontend::FCircuit,
|
||||
transcript::poseidon::poseidon_canonical_config,
|
||||
FoldingScheme,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
fcircuit::EthDosCircuit,
|
||||
signature::gen_signatures,
|
||||
utils::{dbg, elapsed, get_time},
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn full_flow() {
|
||||
// set how many steps of folding we want to compute
|
||||
const N_STEPS: usize = 10;
|
||||
dbg(format!(
|
||||
"running Nova folding scheme on EthDosCircuit, with N_STEPS={}",
|
||||
N_STEPS
|
||||
));
|
||||
|
||||
let mut rng = OsRng;
|
||||
let poseidon_config = poseidon_canonical_config::<Fr>();
|
||||
|
||||
let pks_sigs =
|
||||
gen_signatures::<OsRng, EdwardsProjective>(&mut rng, &poseidon_config, N_STEPS);
|
||||
|
||||
// set the initial state
|
||||
let xy = pks_sigs[0].pk.0.xy().unwrap();
|
||||
let pk0 = vec![xy.0, xy.1];
|
||||
let z_0: Vec<Fr> = vec![pk0.clone(), pk0, vec![Fr::zero()]].concat();
|
||||
|
||||
type FC = EthDosCircuit<Fr, EdwardsProjective, EdwardsVar>;
|
||||
let f_circuit = FC::new(poseidon_config.clone()).unwrap();
|
||||
|
||||
// define type aliases for the FoldingScheme (FS) and Decider (D), to avoid writting the
|
||||
// whole type each time
|
||||
pub type FS = Nova<G1, G2, FC, Pedersen<G1>, Pedersen<G2>, false>;
|
||||
|
||||
// prepare the Nova prover & verifier params
|
||||
let nova_preprocess_params =
|
||||
PreprocessorParam::new(poseidon_config.clone(), f_circuit.clone());
|
||||
let start = get_time();
|
||||
let nova_params = FS::preprocess(&mut rng, &nova_preprocess_params).unwrap();
|
||||
dbg(format!("Nova params generated: {:?}", elapsed(start)));
|
||||
|
||||
// initialize the folding scheme engine, in our case we use Nova
|
||||
let mut nova = FS::init(&nova_params, f_circuit, z_0.clone()).unwrap();
|
||||
|
||||
// run n steps of the folding iteration
|
||||
let start_full = get_time();
|
||||
for i in 0..N_STEPS {
|
||||
let start = get_time();
|
||||
nova.prove_step(rng, pks_sigs[i].clone(), None).unwrap();
|
||||
dbg(format!("Nova::prove_step {}: {:?}", nova.i, elapsed(start)));
|
||||
}
|
||||
dbg(format!(
|
||||
"Nova's all {} steps time: {:?}",
|
||||
N_STEPS,
|
||||
elapsed(start_full)
|
||||
));
|
||||
|
||||
// verify the last IVC proof
|
||||
let ivc_proof = nova.ivc_proof();
|
||||
dbg!(&ivc_proof.z_i);
|
||||
FS::verify(
|
||||
nova_params.1.clone(), // Nova's verifier params
|
||||
ivc_proof,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
95
src/lib.rs
95
src/lib.rs
@@ -1,4 +1,4 @@
|
||||
//! This file contains the WASM bindings.
|
||||
//! This file contains the WASM bindings, and at the bottom a test running the full flow.
|
||||
//!
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(dead_code)]
|
||||
@@ -28,16 +28,15 @@ use crate::signature::{gen_signatures, SigPk};
|
||||
use crate::utils::{dbg, elapsed, get_time};
|
||||
|
||||
mod fcircuit;
|
||||
mod fold_ethdos;
|
||||
mod signature;
|
||||
mod utils;
|
||||
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
// define type aliases for the FCircuit (FC) and the FoldingScheme (FS), to avoid writting the
|
||||
// whole type each time.
|
||||
// define type aliases for the FCircuit (FC) and the FoldingScheme (FS), to avoid writing the whole
|
||||
// type each time.
|
||||
type FC = EthDosCircuit<Fr, EdwardsProjective, EdwardsVar>;
|
||||
type FS = Nova<G1, G2, FC, Pedersen<G1>, Pedersen<G2>, false>;
|
||||
type FS = Nova<G1, G2, FC, Pedersen<G1>, Pedersen<G2>>;
|
||||
|
||||
#[wasm_bindgen]
|
||||
extern "C" {
|
||||
@@ -207,3 +206,89 @@ pub fn verify_proof(verifier_params: String, ivc_proof: String) -> String {
|
||||
.unwrap();
|
||||
"verified".to_string()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ark_bn254::{Fr, G1Projective as G1};
|
||||
use ark_ec::AffineRepr;
|
||||
use ark_grumpkin::Projective as G2;
|
||||
use ark_std::Zero;
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
use arkeddsa::ed_on_bn254_twist::{constraints::EdwardsVar, EdwardsProjective};
|
||||
|
||||
use folding_schemes::{
|
||||
commitment::pedersen::Pedersen,
|
||||
folding::nova::{Nova, PreprocessorParam},
|
||||
frontend::FCircuit,
|
||||
transcript::poseidon::poseidon_canonical_config,
|
||||
FoldingScheme,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
fcircuit::EthDosCircuit,
|
||||
signature::gen_signatures,
|
||||
utils::{dbg, elapsed, get_time},
|
||||
};
|
||||
|
||||
// test showing a full-execution example.
|
||||
#[test]
|
||||
fn test_full_flow() {
|
||||
// set how many steps of folding we want to compute
|
||||
const N_STEPS: usize = 10;
|
||||
dbg(format!(
|
||||
"running Nova folding scheme on EthDosCircuit, with N_STEPS={}",
|
||||
N_STEPS
|
||||
));
|
||||
|
||||
let mut rng = OsRng;
|
||||
let poseidon_config = poseidon_canonical_config::<Fr>();
|
||||
|
||||
let pks_sigs =
|
||||
gen_signatures::<OsRng, EdwardsProjective>(&mut rng, &poseidon_config, N_STEPS);
|
||||
|
||||
// set the initial state
|
||||
let xy = pks_sigs[0].pk.0.xy().unwrap();
|
||||
let pk0 = vec![xy.0, xy.1];
|
||||
let z_0: Vec<Fr> = vec![pk0.clone(), pk0, vec![Fr::zero()]].concat();
|
||||
|
||||
type FC = EthDosCircuit<Fr, EdwardsProjective, EdwardsVar>;
|
||||
let f_circuit = FC::new(poseidon_config.clone()).unwrap();
|
||||
|
||||
// define type aliases for the FoldingScheme (FS) and Decider (D), to avoid writing the
|
||||
// whole type each time
|
||||
pub type FS = Nova<G1, G2, FC, Pedersen<G1>, Pedersen<G2>, false>;
|
||||
|
||||
// prepare the Nova prover & verifier params
|
||||
let nova_preprocess_params =
|
||||
PreprocessorParam::new(poseidon_config.clone(), f_circuit.clone());
|
||||
let start = get_time();
|
||||
let nova_params = FS::preprocess(&mut rng, &nova_preprocess_params).unwrap();
|
||||
dbg(format!("Nova params generated: {:?}", elapsed(start)));
|
||||
|
||||
// initialize the folding scheme engine, in our case we use Nova
|
||||
let mut nova = FS::init(&nova_params, f_circuit, z_0.clone()).unwrap();
|
||||
|
||||
// run n steps of the folding iteration
|
||||
let start_full = get_time();
|
||||
for i in 0..N_STEPS {
|
||||
let start = get_time();
|
||||
nova.prove_step(rng, pks_sigs[i].clone(), None).unwrap();
|
||||
dbg(format!("Nova::prove_step {}: {:?}", nova.i, elapsed(start)));
|
||||
}
|
||||
dbg(format!(
|
||||
"Nova's all {} steps time: {:?}",
|
||||
N_STEPS,
|
||||
elapsed(start_full)
|
||||
));
|
||||
|
||||
// verify the last IVC proof
|
||||
let ivc_proof = nova.ivc_proof();
|
||||
dbg!(&ivc_proof.z_i);
|
||||
FS::verify(
|
||||
nova_params.1.clone(), // Nova's verifier params
|
||||
ivc_proof,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! This file is just some helper methods on top of https://github.com/kilic/arkeddsa in order to
|
||||
//! use the Signature and PublicKey as ExternalInputs in the FCircuit.
|
||||
use ark_crypto_primitives::sponge::{
|
||||
poseidon::{PoseidonConfig, PoseidonSponge},
|
||||
Absorb, CryptographicSponge,
|
||||
|
||||
26
src/utils.rs
26
src/utils.rs
@@ -1,17 +1,6 @@
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use web_sys::console;
|
||||
|
||||
pub fn set_panic_hook() {
|
||||
// When the `console_error_panic_hook` feature is enabled, we can call the
|
||||
// `set_panic_hook` function at least once during initialization, and then
|
||||
// we will get better error messages if our code ever panics.
|
||||
//
|
||||
// For more details see
|
||||
// https://github.com/rustwasm/console_error_panic_hook#readme
|
||||
#[cfg(feature = "console_error_panic_hook")]
|
||||
console_error_panic_hook::set_once();
|
||||
}
|
||||
|
||||
pub fn dbg(s: String) {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
console::log_1(&s.into());
|
||||
@@ -49,9 +38,20 @@ pub fn elapsed(start: u64) -> u64 {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
fn get_wasm_time() -> u64 {
|
||||
use web_sys::window;
|
||||
let window = window().expect("should have a window in this context");
|
||||
let window = window().expect("no window");
|
||||
let performance = window
|
||||
.performance()
|
||||
.expect("performance should be available");
|
||||
.expect("window.performance() not found");
|
||||
performance.now() as u64
|
||||
}
|
||||
|
||||
pub fn set_panic_hook() {
|
||||
// When the `console_error_panic_hook` feature is enabled, we can call the
|
||||
// `set_panic_hook` function at least once during initialization, and then
|
||||
// we will get better error messages if our code ever panics.
|
||||
//
|
||||
// For more details see
|
||||
// https://github.com/rustwasm/console_error_panic_hook#readme
|
||||
#[cfg(feature = "console_error_panic_hook")]
|
||||
console_error_panic_hook::set_once();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user