mirror of https://github.com/status-im/rln.git
add wasm support
This commit is contained in:
parent
600775f069
commit
b7e2954a69
11
Cargo.toml
11
Cargo.toml
|
@ -7,11 +7,18 @@ edition = "2018"
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["sapling_multicore"]
|
||||||
|
wasm = ["sapling_wasm"]
|
||||||
|
bellman_wasm = ["bellman/wasm", "bellman/nolog"]
|
||||||
|
sapling_wasm = ["sapling-crypto/wasm"]
|
||||||
|
sapling_multicore = ["sapling-crypto/multicore"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rand = "0.4"
|
rand = "0.4"
|
||||||
blake2 = "0.8.1"
|
blake2 = "0.8.1"
|
||||||
sapling-crypto = { package = "sapling-crypto_ce", version = "0.1.2" }
|
sapling-crypto = { package = "sapling-crypto_ce", version = "0.1.3", default-features = false }
|
||||||
hex = "0.4.2"
|
bellman = { package = "bellman_ce", version = "0.3.4", default-features = false }
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
console_error_panic_hook = { version = "0.1.1" }
|
console_error_panic_hook = { version = "0.1.1" }
|
||||||
|
|
|
@ -89,26 +89,7 @@ where
|
||||||
let root = E::Fr::from_repr(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
let root = E::Fr::from_repr(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
buf.read_le(&mut reader)?;
|
buf.read_le(&mut reader)?;
|
||||||
let id_key = E::Fr::from_repr(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
let id_key = E::Fr::from_repr(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
let mut byte_buf = vec![0u8; 1];
|
let auth_path = Self::decode_auth_path(&mut reader)?;
|
||||||
let mut auth_path: Vec<Option<(E::Fr, bool)>> = vec![];
|
|
||||||
loop {
|
|
||||||
match reader.read_exact(&mut byte_buf) {
|
|
||||||
Ok(_) => match byte_buf[0] {
|
|
||||||
0u8 => auth_path.push(Some((E::Fr::zero(), false))),
|
|
||||||
1u8 => auth_path.push(Some((E::Fr::one(), true))),
|
|
||||||
_ => {
|
|
||||||
return Err(io::Error::new(
|
|
||||||
io::ErrorKind::InvalidInput,
|
|
||||||
"auth path element should be 1 or 0",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(ref err) if err.kind() == io::ErrorKind::UnexpectedEof => {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Err(err) => return Err(err),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Ok(RLNInputs {
|
Ok(RLNInputs {
|
||||||
share_x: Some(share_x),
|
share_x: Some(share_x),
|
||||||
share_y: Some(share_y),
|
share_y: Some(share_y),
|
||||||
|
@ -120,6 +101,17 @@ where
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||||
|
self.share_x.unwrap().into_repr().write_le(&mut writer).unwrap();
|
||||||
|
self.share_y.unwrap().into_repr().write_le(&mut writer).unwrap();
|
||||||
|
self.epoch.unwrap().into_repr().write_le(&mut writer).unwrap();
|
||||||
|
self.nullifier.unwrap().into_repr().write_le(&mut writer).unwrap();
|
||||||
|
self.root.unwrap().into_repr().write_le(&mut writer).unwrap();
|
||||||
|
self.id_key.unwrap().into_repr().write_le(&mut writer).unwrap();
|
||||||
|
Self::encode_auth_path(&mut writer, self.auth_path.clone()).unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn read_public_inputs<R: Read>(mut reader: R) -> io::Result<Vec<E::Fr>> {
|
pub fn read_public_inputs<R: Read>(mut reader: R) -> io::Result<Vec<E::Fr>> {
|
||||||
let mut buf = <E::Fr as PrimeField>::Repr::default();
|
let mut buf = <E::Fr as PrimeField>::Repr::default();
|
||||||
buf.read_le(&mut reader)?;
|
buf.read_le(&mut reader)?;
|
||||||
|
@ -134,6 +126,53 @@ where
|
||||||
let nullifier = E::Fr::from_repr(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
let nullifier = E::Fr::from_repr(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
Ok(vec![root, epoch, share_x, share_y, nullifier])
|
Ok(vec![root, epoch, share_x, share_y, nullifier])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write_public_inputs<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||||
|
self.root.unwrap().into_repr().write_le(&mut writer)?;
|
||||||
|
self.epoch.unwrap().into_repr().write_le(&mut writer)?;
|
||||||
|
self.share_x.unwrap().into_repr().write_le(&mut writer)?;
|
||||||
|
self.share_y.unwrap().into_repr().write_le(&mut writer)?;
|
||||||
|
self.nullifier.unwrap().into_repr().write_le(&mut writer)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode_auth_path<W: Write>(mut writer: W, auth_path: Vec<Option<(E::Fr, bool)>>) -> io::Result<()> {
|
||||||
|
let path_len = auth_path.len() as u8;
|
||||||
|
writer.write(&[path_len])?;
|
||||||
|
for el in auth_path.iter() {
|
||||||
|
let c = el.unwrap();
|
||||||
|
if c.1 {
|
||||||
|
writer.write(&[1])?;
|
||||||
|
} else {
|
||||||
|
writer.write(&[0])?;
|
||||||
|
}
|
||||||
|
c.0.into_repr().write_le(&mut writer).unwrap();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode_auth_path<R: Read>(mut reader: R) -> io::Result<Vec<Option<(E::Fr, bool)>>> {
|
||||||
|
let mut byte_buf = vec![0u8; 1];
|
||||||
|
let mut el_buf = <E::Fr as PrimeField>::Repr::default();
|
||||||
|
let mut auth_path: Vec<Option<(E::Fr, bool)>> = vec![];
|
||||||
|
reader.read_exact(&mut byte_buf)?;
|
||||||
|
let path_len = byte_buf[0];
|
||||||
|
if path_len < 2 {
|
||||||
|
return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid path length"));
|
||||||
|
}
|
||||||
|
for _ in 0..path_len {
|
||||||
|
reader.read_exact(&mut byte_buf)?;
|
||||||
|
let path_dir = match byte_buf[0] {
|
||||||
|
0u8 => false,
|
||||||
|
1u8 => true,
|
||||||
|
_ => return Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid path direction")),
|
||||||
|
};
|
||||||
|
el_buf.read_le(&mut reader)?;
|
||||||
|
let node = E::Fr::from_repr(el_buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
|
auth_path.push(Some((node, path_dir)));
|
||||||
|
}
|
||||||
|
Ok(auth_path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -471,24 +510,31 @@ mod test {
|
||||||
let nullifier = Fr::from_str("4").unwrap();
|
let nullifier = Fr::from_str("4").unwrap();
|
||||||
let root = Fr::from_str("5").unwrap();
|
let root = Fr::from_str("5").unwrap();
|
||||||
let id_key = Fr::from_str("6").unwrap();
|
let id_key = Fr::from_str("6").unwrap();
|
||||||
let mut writer: Vec<u8> = Vec::new();
|
let auth_path = vec![
|
||||||
share_x.into_repr().write_le(&mut writer).unwrap();
|
Some((Fr::from_str("20").unwrap(), false)),
|
||||||
share_y.into_repr().write_le(&mut writer).unwrap();
|
Some((Fr::from_str("21").unwrap(), true)),
|
||||||
epoch.into_repr().write_le(&mut writer).unwrap();
|
Some((Fr::from_str("22").unwrap(), true)),
|
||||||
nullifier.into_repr().write_le(&mut writer).unwrap();
|
Some((Fr::from_str("23").unwrap(), false)),
|
||||||
root.into_repr().write_le(&mut writer).unwrap();
|
];
|
||||||
id_key.into_repr().write_le(&mut writer).unwrap();
|
let input0 = RLNInputs::<Bn256> {
|
||||||
writer.push(1u8);
|
share_x: Some(share_x),
|
||||||
writer.push(1u8);
|
share_y: Some(share_y),
|
||||||
writer.push(1u8);
|
epoch: Some(epoch),
|
||||||
writer.push(1u8);
|
nullifier: Some(nullifier),
|
||||||
let inputs = RLNInputs::<Bn256>::read(writer.as_slice()).unwrap();
|
root: Some(root),
|
||||||
assert_eq!(inputs.share_x.unwrap().eq(&share_x), true);
|
id_key: Some(id_key),
|
||||||
assert_eq!(inputs.share_y.unwrap().eq(&share_y), true);
|
auth_path,
|
||||||
assert_eq!(inputs.epoch.unwrap().eq(&epoch), true);
|
};
|
||||||
assert_eq!(inputs.nullifier.unwrap().eq(&nullifier), true);
|
let mut raw_inputs: Vec<u8> = Vec::new();
|
||||||
assert_eq!(inputs.root.unwrap().eq(&root), true);
|
input0.write(&mut raw_inputs).unwrap();
|
||||||
assert_eq!(inputs.id_key.unwrap().eq(&id_key), true);
|
let mut reader = raw_inputs.as_slice();
|
||||||
assert_eq!(4, inputs.merkle_depth());
|
let input1 = RLNInputs::<Bn256>::read(&mut reader).unwrap();
|
||||||
|
assert_eq!(input0.share_x, input1.share_x);
|
||||||
|
assert_eq!(input0.share_y, input1.share_y);
|
||||||
|
assert_eq!(input0.epoch, input1.epoch);
|
||||||
|
assert_eq!(input0.nullifier, input1.nullifier);
|
||||||
|
assert_eq!(input0.root, input1.root);
|
||||||
|
assert_eq!(input0.id_key, input1.id_key);
|
||||||
|
assert_eq!(input0.auth_path, input1.auth_path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,75 +91,63 @@ mod test {
|
||||||
writer
|
writer
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen_test]
|
fn gen_valid_inputs(merkle_depth: usize) -> RLNInputs<Bn256> {
|
||||||
fn test_rln_wasm() {
|
|
||||||
let merkle_depth = 32usize;
|
|
||||||
let circuit_parameters = gen_circuit_parameters(merkle_depth);
|
|
||||||
//
|
|
||||||
// prepare inputs
|
|
||||||
|
|
||||||
let mut rng = XorShiftRng::from_seed([0x3dbe6258, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
let mut rng = XorShiftRng::from_seed([0x3dbe6258, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
|
||||||
let poseidon_params = PoseidonParams::<Bn256>::default();
|
let poseidon_params = PoseidonParams::<Bn256>::default();
|
||||||
let mut hasher = PoseidonHasher::new(poseidon_params.clone());
|
let mut hasher = PoseidonHasher::new(poseidon_params.clone());
|
||||||
let mut membership_tree = MerkleTree::empty(hasher.clone(), merkle_depth);
|
let mut membership_tree = MerkleTree::empty(hasher.clone(), merkle_depth);
|
||||||
|
|
||||||
// A. setup an identity
|
|
||||||
|
|
||||||
let id_key = Fr::rand(&mut rng);
|
let id_key = Fr::rand(&mut rng);
|
||||||
let id_comm = hasher.hash(vec![id_key.clone()]);
|
let id_comm = hasher.hash(vec![id_key.clone()]);
|
||||||
|
|
||||||
// B. insert to the membership tree
|
let id_index = 6;
|
||||||
|
|
||||||
let id_index = 6; // any number below 2^depth will work
|
|
||||||
membership_tree.update(id_index, id_comm);
|
membership_tree.update(id_index, id_comm);
|
||||||
|
|
||||||
// C.1 get membership witness
|
|
||||||
|
|
||||||
let auth_path = membership_tree.witness(id_index);
|
let auth_path = membership_tree.witness(id_index);
|
||||||
assert!(membership_tree.check_inclusion(auth_path.clone(), id_index, id_key.clone()));
|
assert!(membership_tree.check_inclusion(auth_path.clone(), id_index, id_key.clone()));
|
||||||
|
|
||||||
// C.2 prepare sss
|
|
||||||
|
|
||||||
// get current epoch
|
|
||||||
let epoch = Fr::rand(&mut rng);
|
let epoch = Fr::rand(&mut rng);
|
||||||
|
|
||||||
let signal_hash = Fr::rand(&mut rng);
|
let signal_hash = Fr::rand(&mut rng);
|
||||||
// evaluation point is the signal_hash
|
|
||||||
let share_x = signal_hash.clone();
|
let share_x = signal_hash.clone();
|
||||||
|
|
||||||
// calculate current line equation
|
|
||||||
let a_0 = id_key.clone();
|
let a_0 = id_key.clone();
|
||||||
let a_1 = hasher.hash(vec![a_0, epoch]);
|
let a_1 = hasher.hash(vec![a_0, epoch]);
|
||||||
|
|
||||||
// evaluate line equation
|
|
||||||
let mut share_y = a_1.clone();
|
let mut share_y = a_1.clone();
|
||||||
share_y.mul_assign(&share_x);
|
share_y.mul_assign(&share_x);
|
||||||
share_y.add_assign(&a_0);
|
share_y.add_assign(&a_0);
|
||||||
|
|
||||||
// calculate nullfier
|
|
||||||
let nullifier = hasher.hash(vec![a_1]);
|
let nullifier = hasher.hash(vec![a_1]);
|
||||||
|
|
||||||
let root = membership_tree.root();
|
let root = membership_tree.root();
|
||||||
|
|
||||||
//
|
let inputs = RLNInputs::<Bn256> {
|
||||||
// serialize input
|
share_x: Some(share_x),
|
||||||
|
share_y: Some(share_y),
|
||||||
|
epoch: Some(epoch),
|
||||||
|
nullifier: Some(nullifier),
|
||||||
|
root: Some(membership_tree.root()),
|
||||||
|
id_key: Some(id_key),
|
||||||
|
auth_path: auth_path.into_iter().map(|w| Some(w)).collect(),
|
||||||
|
};
|
||||||
|
|
||||||
let mut writer: Vec<u8> = Vec::new();
|
inputs
|
||||||
share_x.into_repr().write_le(&mut writer).unwrap();
|
|
||||||
share_y.into_repr().write_le(&mut writer).unwrap();
|
|
||||||
epoch.into_repr().write_le(&mut writer).unwrap();
|
|
||||||
nullifier.into_repr().write_le(&mut writer).unwrap();
|
|
||||||
root.into_repr().write_le(&mut writer).unwrap();
|
|
||||||
id_key.into_repr().write_le(&mut writer).unwrap();
|
|
||||||
for (e, _) in auth_path.iter() {
|
|
||||||
e.into_repr().write_le(&mut writer).unwrap();
|
|
||||||
}
|
}
|
||||||
let raw_circuit_parameters = writer.as_slice();
|
|
||||||
|
|
||||||
//
|
|
||||||
// call wasm
|
|
||||||
|
|
||||||
|
#[wasm_bindgen_test]
|
||||||
|
fn test_rln_wasm() {
|
||||||
|
let merkle_depth = 32usize;
|
||||||
|
let raw_circuit_parameters = gen_circuit_parameters(merkle_depth);
|
||||||
|
let inputs = gen_valid_inputs(merkle_depth);
|
||||||
|
let mut raw_inputs: Vec<u8> = Vec::new();
|
||||||
|
inputs.write(&mut raw_inputs);
|
||||||
use super::RLNWasm;
|
use super::RLNWasm;
|
||||||
// let rlnWasm = RLNWasm::new(merkle_depth, raw_circuit_parameters);
|
let rln_wasm = RLNWasm::new(merkle_depth, raw_circuit_parameters.as_slice());
|
||||||
|
let proof = rln_wasm.generate_proof(raw_inputs.as_slice());
|
||||||
|
let mut public_inputs: Vec<u8> = Vec::new();
|
||||||
|
inputs.write_public_inputs(&mut public_inputs);
|
||||||
|
let proof = proof.unwrap();
|
||||||
|
assert_eq!(rln_wasm.verify(proof.as_slice(), public_inputs.as_slice()), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue