From 1fa63c9d3d9228f6e1f2c41bc50bafdfdb071c74 Mon Sep 17 00:00:00 2001 From: poma Date: Mon, 13 Jan 2020 13:10:00 +0700 Subject: [PATCH] WebAssembly compatibility --- phase2/Cargo.toml | 23 ++++++++++++++---- phase2/README.md | 22 +++++++++++++++++ phase2/src/lib.rs | 52 ++++++++++++++++++++++++++++++++++++---- phase2/src/parameters.rs | 29 ++++++++++++++++++++-- 4 files changed, 115 insertions(+), 11 deletions(-) diff --git a/phase2/Cargo.toml b/phase2/Cargo.toml index 251bae4..9acb08c 100644 --- a/phase2/Cargo.toml +++ b/phase2/Cargo.toml @@ -8,13 +8,13 @@ homepage = "https://github.com/ebfull/phase2" license = "MIT/Apache-2.0" repository = "https://github.com/ebfull/phase2" +[lib] +crate-type = ["cdylib", "lib"] + [dependencies] rand = "0.4" -bellman_ce = { path = "../bellman" } byteorder = "1" exitcode = "1.1.2" -num_cpus = "1" -crossbeam = "0.3" blake2-rfc = "0.2" blake2 = "0.6.1" serde = { version = "1.0", features = ["derive"] } @@ -22,5 +22,20 @@ serde_json = "1.0" num-bigint = "0.2.3" num-traits = "0.2.8" itertools = "0.8.1" -rust-crypto = "0.2" hex = "0.4.0" +cfg-if = "0.1.10" +bellman_ce = { path = "../bellman", default-features = false } # active features depend on build type + +# needed for native only but don't break wasm if present +num_cpus = "1" +crossbeam = "0.3" + +# needed for wasm only +wasm-bindgen = { version = "0.2.58", optional = true } +js-sys = { version = "0.3.35", optional = true } +web-sys = { version = "0.3.35", features = ["console"], optional = true } +console_error_panic_hook = { version = "0.1.6", optional = true } + +[features] +default = ["bellman_ce/multicore"] +wasm = ["wasm-bindgen", "js-sys", "web-sys", "console_error_panic_hook", "bellman_ce/wasm"] diff --git a/phase2/README.md b/phase2/README.md index 828eb5c..6b42540 100644 --- a/phase2/README.md +++ b/phase2/README.md @@ -2,6 +2,28 @@ This library is still under development. +## WebAssembly how-to + +Build wasm package using `wasm-pack build --release -- --no-default-features --features wasm` + +this will generate `./pkg` directory with wasm file and js bindings. After that you +can use this package in your browser application like so: + +```js +async function main() { + const phase2 = await import("./pkg/phase2.js") + let data = await fetch('params') + data = await data.arrayBuffer() + data = new Uint8Array(data) + console.log('Source params', data) + const result = phase2.contribute(data) + console.log('Updated params', result) + // upload updated params +} + +main().catch(console.error) +``` + ## [Documentation](https://docs.rs/phase2/) ## Security Warnings diff --git a/phase2/src/lib.rs b/phase2/src/lib.rs index 848b96d..28c52a0 100644 --- a/phase2/src/lib.rs +++ b/phase2/src/lib.rs @@ -1,19 +1,61 @@ #![allow(unused_imports)] +#[macro_use] +extern crate serde; extern crate bellman_ce; extern crate rand; extern crate byteorder; extern crate blake2_rfc; extern crate num_cpus; extern crate crossbeam; - -#[macro_use] -extern crate serde; -extern crate serde_json; +extern crate cfg_if; +use cfg_if::cfg_if; pub mod keypair; pub mod keypair_assembly; pub mod hash_writer; pub mod parameters; pub mod utils; -pub mod circom_circuit; \ No newline at end of file +pub mod circom_circuit; + +cfg_if! { + if #[cfg(feature = "wasm")] { + extern crate serde_json; + extern crate js_sys; + extern crate web_sys; + extern crate wasm_bindgen; + extern crate console_error_panic_hook; + extern crate itertools; + + use wasm_bindgen::prelude::*; + use itertools::Itertools; + use parameters::MPCParameters; + use std::io::{ + Read, + Write, + }; + + macro_rules! log { + ($($t:tt)*) => (web_sys::console::log_1(&format_args!($($t)*).to_string().into())) + } + + #[wasm_bindgen] + pub fn contribute(params: Vec) -> Result, JsValue> { + console_error_panic_hook::set_once(); + let disallow_points_at_infinity = false; + + log!("Initializing phase2"); + let mut rng = &mut rand::XorShiftRng::new_unseeded(); // TODO: change this unsafe unseeded random (!) + let mut params = MPCParameters::read(&*params, disallow_points_at_infinity, true).expect("unable to read params"); + + log!("Contributing..."); + let hash = params.contribute(&mut rng); + log!("Contribution hash: 0x{:02x}", hash.iter().format("")); + + let mut output: Vec = vec![]; + params.write(&mut output).expect("failed to write updated parameters"); + log!("Returning parameters"); + Ok(output) + } + } +} diff --git a/phase2/src/parameters.rs b/phase2/src/parameters.rs index 963d0c9..db380ef 100644 --- a/phase2/src/parameters.rs +++ b/phase2/src/parameters.rs @@ -4,6 +4,11 @@ extern crate byteorder; extern crate num_cpus; extern crate crossbeam; +#[cfg(feature = "wasm")] +use bellman_ce::singlecore::Worker; +#[cfg(not(feature = "wasm"))] +use bellman_ce::multicore::Worker; + use byteorder::{ BigEndian, ReadBytesExt, @@ -46,8 +51,6 @@ use bellman_ce::pairing::{ } }; -pub use bellman_ce::multicore::*; - use bellman_ce::{ Circuit, SynthesisError, @@ -415,6 +418,7 @@ impl MPCParameters { // Generate a keypair let (pubkey, privkey) = keypair(rng, self); + #[cfg(not(feature = "wasm"))] fn batch_exp(bases: &mut [C], coeff: C::Scalar) { let coeff = coeff.into_repr(); @@ -459,6 +463,27 @@ impl MPCParameters { } } + #[cfg(feature = "wasm")] + fn batch_exp(bases: &mut [C], coeff: C::Scalar) { + let coeff = coeff.into_repr(); + + let mut projective = vec![C::Projective::zero(); bases.len()]; + + // Perform wNAF, placing results into `projective`. + let mut wnaf = Wnaf::new(); + for (base, projective) in bases.iter_mut().zip(projective.iter_mut()) { + *projective = wnaf.base(base.into_projective(), 1).scalar(coeff); + } + + // Perform batch normalization + C::Projective::batch_normalization(&mut projective); + + // Turn it all back into affine points + for (projective, affine) in projective.iter().zip(bases.iter_mut()) { + *affine = projective.into_affine(); + } + } + let delta_inv = privkey.delta.inverse().expect("nonzero"); let mut l = (&self.params.l[..]).to_vec(); let mut h = (&self.params.h[..]).to_vec();