mirror of
https://github.com/arnaucube/ethdos-fold.git
synced 2026-01-11 16:31:34 +01:00
add WASM bindings
This commit is contained in:
7
README.md
Normal file
7
README.md
Normal file
@@ -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
|
||||
|
||||
@@ -83,7 +83,7 @@ where
|
||||
degree = degree.clone() + FpVar::<F>::one();
|
||||
|
||||
let pk_i1_xy = external_inputs.pk.to_constraint_field()?;
|
||||
Ok(vec![vec![pk_0_x, pk_0_y], pk_i1_xy, vec![degree]].concat())
|
||||
Ok([vec![pk_0_x, pk_0_y], pk_i1_xy, vec![degree]].concat())
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
|
||||
197
src/lib.rs
Normal file
197
src/lib.rs
Normal file
@@ -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()
|
||||
}
|
||||
@@ -33,10 +33,10 @@ impl<C: CurveGroup> Default for SigPk<C> {
|
||||
}
|
||||
}
|
||||
impl<C: CurveGroup> SigPk<C> {
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
pub fn to_bytes(self) -> Vec<u8> {
|
||||
let sig_bytes = self.sig.to_bytes();
|
||||
let pk_bytes = self.pk.to_bytes();
|
||||
vec![sig_bytes, pk_bytes].concat()
|
||||
[sig_bytes, pk_bytes].concat()
|
||||
}
|
||||
pub fn from_bytes(b: Vec<u8>) -> Self {
|
||||
let u_point_size = C::Affine::generator().serialized_size(ark_serialize::Compress::No);
|
||||
@@ -138,20 +138,13 @@ where
|
||||
let pk = sk.public_key();
|
||||
|
||||
// if prev_pk!=None, use it, else, set the new pk to it
|
||||
let prev_pk = if prev_pk.is_some() {
|
||||
prev_pk.unwrap()
|
||||
} else {
|
||||
*pk
|
||||
};
|
||||
let prev_pk = if let Some(v) = prev_pk { v } else { *pk };
|
||||
|
||||
let msg = hash_pk(poseidon_config, prev_pk);
|
||||
|
||||
let sig = sk
|
||||
.sign::<blake2::Blake2b512>(&poseidon_config, &msg)
|
||||
.sign::<blake2::Blake2b512>(poseidon_config, &msg)
|
||||
.unwrap();
|
||||
pk.verify(&poseidon_config, &msg, &sig).unwrap();
|
||||
SigPk {
|
||||
pk: pk.clone(),
|
||||
sig,
|
||||
}
|
||||
pk.verify(poseidon_config, &msg, &sig).unwrap();
|
||||
SigPk { pk: *pk, sig }
|
||||
}
|
||||
|
||||
57
src/utils.rs
Normal file
57
src/utils.rs
Normal file
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user