@ -0,0 +1,7 @@ |
|||
# ethdos-fold |
|||
|
|||
- run native tests: `cargo test --release -- --nocapture` |
|||
- build wasm: `wasm-pack build --target web` |
|||
- serve the web: `python -m http.server 8080` |
|||
- go to http://127.0.0.1:8080/index.html |
|||
|
@ -0,0 +1,197 @@ |
|||
//! This file contains the WASM bindings.
|
|||
//!
|
|||
#![allow(non_snake_case)]
|
|||
#![allow(dead_code)]
|
|||
#![allow(non_camel_case_types)]
|
|||
#![allow(clippy::upper_case_acronyms)]
|
|||
|
|||
use ark_bn254::{Fr, G1Projective as G1};
|
|||
use ark_ec::AffineRepr;
|
|||
use ark_grumpkin::Projective as G2;
|
|||
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
|
|||
use ark_std::Zero;
|
|||
use base64::{engine::general_purpose::STANDARD as b64, Engine as _};
|
|||
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;
|
|||
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.
|
|||
type FC = EthDosCircuit<Fr, EdwardsProjective, EdwardsVar>;
|
|||
type FS = Nova<G1, G2, FC, Pedersen<G1>, Pedersen<G2>, false>;
|
|||
|
|||
#[wasm_bindgen]
|
|||
extern "C" {
|
|||
fn alert(s: &str);
|
|||
}
|
|||
|
|||
#[wasm_bindgen]
|
|||
pub fn gen_params() -> Vec<String> {
|
|||
let mut rng = OsRng;
|
|||
let poseidon_config = poseidon_canonical_config::<Fr>();
|
|||
|
|||
let f_circuit = FC::new(poseidon_config.clone()).unwrap();
|
|||
|
|||
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: {:?}ms", elapsed(start)));
|
|||
|
|||
// serialize
|
|||
let start = get_time();
|
|||
let mut prover_params_serialized = vec![];
|
|||
nova_params
|
|||
.0
|
|||
.serialize_compressed(&mut prover_params_serialized)
|
|||
.unwrap();
|
|||
dbg(format!(
|
|||
"Nova prover params serialized: {:?}ms",
|
|||
elapsed(start)
|
|||
));
|
|||
|
|||
let start = get_time();
|
|||
let mut verifier_params_serialized = vec![];
|
|||
nova_params
|
|||
.1
|
|||
.serialize_compressed(&mut verifier_params_serialized)
|
|||
.unwrap();
|
|||
dbg(format!(
|
|||
"Nova prover params serialized: {:?}ms",
|
|||
elapsed(start)
|
|||
));
|
|||
|
|||
vec![
|
|||
b64.encode(&prover_params_serialized),
|
|||
b64.encode(&prover_params_serialized),
|
|||
]
|
|||
}
|
|||
|
|||
#[wasm_bindgen]
|
|||
pub fn gen_sigs(n_steps: usize) -> Vec<String> {
|
|||
let mut rng = OsRng;
|
|||
let poseidon_config = poseidon_canonical_config::<Fr>();
|
|||
|
|||
let sigs: Vec<SigPk<EdwardsProjective>> = gen_signatures(&mut rng, &poseidon_config, n_steps);
|
|||
let b: Vec<Vec<u8>> = sigs.iter().map(|&s| s.to_bytes()).collect();
|
|||
b.iter().map(|s| b64.encode(s)).collect::<Vec<String>>()
|
|||
}
|
|||
|
|||
#[wasm_bindgen]
|
|||
pub fn fold_sigs(params: Vec<String>, sigs_pks: Vec<String>) -> String {
|
|||
dbg("starting fold_sigs (rust)".to_string());
|
|||
dbg(format!("received sigs: {:?}", sigs_pks));
|
|||
|
|||
let poseidon_config = poseidon_canonical_config::<Fr>();
|
|||
|
|||
// parse sigs_pks
|
|||
let b: Vec<Vec<u8>> = sigs_pks.iter().map(|s| b64.decode(s).unwrap()).collect();
|
|||
let pks_sigs: Vec<SigPk<EdwardsProjective>> =
|
|||
b.iter().map(|s| SigPk::from_bytes(s.clone())).collect();
|
|||
|
|||
// parse params
|
|||
let start = get_time();
|
|||
let pp = FS::pp_deserialize_with_mode(
|
|||
&mut b64.decode(params[0].clone()).unwrap().as_slice(),
|
|||
ark_serialize::Compress::Yes,
|
|||
ark_serialize::Validate::Yes,
|
|||
poseidon_config.clone(), // fcircuit_params
|
|||
)
|
|||
.unwrap();
|
|||
let vp = FS::vp_deserialize_with_mode(
|
|||
&mut b64.decode(params[1].clone()).unwrap().as_slice(),
|
|||
ark_serialize::Compress::Yes,
|
|||
ark_serialize::Validate::Yes,
|
|||
poseidon_config.clone(), // fcircuit_params
|
|||
)
|
|||
.unwrap();
|
|||
let fs_params = (pp, vp);
|
|||
dbg(format!(
|
|||
"params (prover & verifier) deserialization: {:?}ms",
|
|||
elapsed(start)
|
|||
));
|
|||
|
|||
// 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> = [pk0.clone(), pk0, vec![Fr::zero()]].concat();
|
|||
|
|||
type FC = EthDosCircuit<Fr, EdwardsProjective, EdwardsVar>;
|
|||
let f_circuit = FC::new(poseidon_config.clone()).unwrap();
|
|||
|
|||
// initialize the folding scheme engine, in our case we use Nova
|
|||
let mut nova = FS::init(&fs_params, f_circuit, z_0.clone()).unwrap();
|
|||
let rng = OsRng;
|
|||
let n_steps = sigs_pks.len();
|
|||
|
|||
let start_full = get_time();
|
|||
#[allow(clippy::needless_range_loop)]
|
|||
for i in 0..n_steps {
|
|||
let start = get_time();
|
|||
nova.prove_step(rng, pks_sigs[i], None).unwrap();
|
|||
dbg(format!(
|
|||
"Nova::prove_step {}: {:?}ms",
|
|||
nova.i,
|
|||
elapsed(start)
|
|||
));
|
|||
}
|
|||
dbg(format!(
|
|||
"Nova's all {} steps time: {:?}ms",
|
|||
n_steps,
|
|||
elapsed(start_full)
|
|||
));
|
|||
|
|||
let ivc_proof = nova.ivc_proof();
|
|||
let mut ivc_proof_bytes = vec![];
|
|||
ivc_proof
|
|||
.serialize_compressed(&mut ivc_proof_bytes)
|
|||
.unwrap();
|
|||
|
|||
b64.encode(ivc_proof_bytes)
|
|||
}
|
|||
|
|||
#[wasm_bindgen]
|
|||
pub fn verify_proof(verifier_params: String, ivc_proof: String) -> String {
|
|||
let poseidon_config = poseidon_canonical_config::<Fr>();
|
|||
|
|||
let vp = FS::vp_deserialize_with_mode(
|
|||
&mut b64.decode(verifier_params.clone()).unwrap().as_slice(),
|
|||
ark_serialize::Compress::Yes,
|
|||
ark_serialize::Validate::Yes,
|
|||
poseidon_config.clone(), // fcircuit_params
|
|||
)
|
|||
.unwrap();
|
|||
// let proof =
|
|||
// FS::IVCProof::deserialize_compressed(b64.decode(ivc_proof).unwrap().as_slice()).unwrap();
|
|||
let proof = <Nova<G1, G2, FC, Pedersen<G1>, Pedersen<G2>, false> as FoldingScheme<
|
|||
G1,
|
|||
G2,
|
|||
FC,
|
|||
>>::IVCProof::deserialize_compressed(b64.decode(ivc_proof).unwrap().as_slice())
|
|||
.unwrap();
|
|||
|
|||
FS::verify(
|
|||
vp, // Nova's verifier params
|
|||
proof,
|
|||
)
|
|||
.unwrap();
|
|||
"verified".to_string()
|
|||
}
|
@ -0,0 +1,57 @@ |
|||
#[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());
|
|||
|
|||
#[cfg(not(target_arch = "wasm32"))]
|
|||
println!("{}", s);
|
|||
}
|
|||
|
|||
pub fn get_time() -> u64 {
|
|||
#[cfg(target_arch = "wasm32")]
|
|||
let start = get_wasm_time() as u64;
|
|||
|
|||
#[cfg(not(target_arch = "wasm32"))]
|
|||
let start = std::time::SystemTime::now()
|
|||
.duration_since(std::time::UNIX_EPOCH)
|
|||
.unwrap()
|
|||
.as_millis() as u64;
|
|||
|
|||
start
|
|||
}
|
|||
|
|||
pub fn elapsed(start: u64) -> u64 {
|
|||
#[cfg(target_arch = "wasm32")]
|
|||
let end = get_wasm_time() as u64;
|
|||
|
|||
#[cfg(not(target_arch = "wasm32"))]
|
|||
let end = std::time::SystemTime::now()
|
|||
.duration_since(std::time::UNIX_EPOCH)
|
|||
.unwrap()
|
|||
.as_millis() as u64;
|
|||
|
|||
end - start
|
|||
}
|
|||
|
|||
#[cfg(target_arch = "wasm32")]
|
|||
fn get_wasm_time() -> u64 {
|
|||
use web_sys::{window, Performance};
|
|||
let window = window().expect("should have a window in this context");
|
|||
let performance = window
|
|||
.performance()
|
|||
.expect("performance should be available");
|
|||
performance.now() as u64
|
|||
}
|