Browse Source

add WASM bindings

main
arnaucube 3 months ago
parent
commit
8d11ef2fcb
5 changed files with 268 additions and 14 deletions
  1. +7
    -0
      README.md
  2. +1
    -1
      src/fcircuit.rs
  3. +197
    -0
      src/lib.rs
  4. +6
    -13
      src/signature.rs
  5. +57
    -0
      src/utils.rs

+ 7
- 0
README.md

@ -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

+ 1
- 1
src/fcircuit.rs

@ -83,7 +83,7 @@ where
degree = degree.clone() + FpVar::<F>::one(); degree = degree.clone() + FpVar::<F>::one();
let pk_i1_xy = external_inputs.pk.to_constraint_field()?; 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)] #[cfg(test)]

+ 197
- 0
src/lib.rs

@ -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()
}

+ 6
- 13
src/signature.rs

@ -33,10 +33,10 @@ impl Default for SigPk {
} }
} }
impl<C: CurveGroup> 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 sig_bytes = self.sig.to_bytes();
let pk_bytes = self.pk.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 { pub fn from_bytes(b: Vec<u8>) -> Self {
let u_point_size = C::Affine::generator().serialized_size(ark_serialize::Compress::No); let u_point_size = C::Affine::generator().serialized_size(ark_serialize::Compress::No);
@ -138,20 +138,13 @@ where
let pk = sk.public_key(); let pk = sk.public_key();
// if prev_pk!=None, use it, else, set the new pk to it // 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 msg = hash_pk(poseidon_config, prev_pk);
let sig = sk let sig = sk
.sign::<blake2::Blake2b512>(&poseidon_config, &msg)
.sign::<blake2::Blake2b512>(poseidon_config, &msg)
.unwrap(); .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
- 0
src/utils.rs

@ -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
}

Loading…
Cancel
Save