mirror of https://github.com/vacp2p/zerokit.git
chore: remove code surface area for maintainability
This commit is contained in:
parent
aaa12db70d
commit
7826e749f0
File diff suppressed because it is too large
Load Diff
13
Cargo.toml
13
Cargo.toml
|
@ -1,13 +1,5 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = ["rln", "rln-cli", "rln-wasm", "utils"]
|
||||||
"multiplier",
|
|
||||||
"private-settlement",
|
|
||||||
"semaphore",
|
|
||||||
"rln",
|
|
||||||
"rln-cli",
|
|
||||||
"rln-wasm",
|
|
||||||
"utils",
|
|
||||||
]
|
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
# Compilation profile for any non-workspace member.
|
# Compilation profile for any non-workspace member.
|
||||||
|
@ -19,6 +11,3 @@ opt-level = 3
|
||||||
[profile.release.package."rln-wasm"]
|
[profile.release.package."rln-wasm"]
|
||||||
# Tell `rustc` to optimize for small code size.
|
# Tell `rustc` to optimize for small code size.
|
||||||
opt-level = "s"
|
opt-level = "s"
|
||||||
|
|
||||||
[profile.release.package."semaphore"]
|
|
||||||
codegen-units = 1
|
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "multiplier"
|
|
||||||
version = "0.3.0"
|
|
||||||
edition = "2018"
|
|
||||||
license = "MIT OR Apache-2.0"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
|
|
||||||
# WASM operations
|
|
||||||
# wasmer = { version = "2.0" }
|
|
||||||
# fnv = { version = "1.0.3", default-features = false }
|
|
||||||
# num = { version = "0.4.0" }
|
|
||||||
# num-traits = { version = "0.2.0", default-features = false }
|
|
||||||
|
|
||||||
# ZKP Generation
|
|
||||||
# ark-ff = { version = "0.3.0", default-features = false, features = ["parallel", "asm"] }
|
|
||||||
ark-std = { version = "0.3.0", default-features = false, features = ["parallel"] }
|
|
||||||
ark-bn254 = { version = "0.3.0" }
|
|
||||||
ark-groth16 = { git = "https://github.com/arkworks-rs/groth16", rev = "765817f", features = ["parallel"] }
|
|
||||||
# ark-poly = { version = "^0.3.0", default-features = false, features = ["parallel"] }
|
|
||||||
ark-serialize = { version = "0.3.0", default-features = false }
|
|
||||||
|
|
||||||
ark-circom = { git = "https://github.com/gakonst/ark-circom", features = ["circom-2"], rev = "35ce5a9" }
|
|
||||||
|
|
||||||
# error handling
|
|
||||||
color-eyre = "0.6.1"
|
|
||||||
|
|
||||||
# decoding of data
|
|
||||||
# hex = "0.4.3"
|
|
||||||
# byteorder = "1.4.3"
|
|
|
@ -1,7 +0,0 @@
|
||||||
[tasks.build]
|
|
||||||
command = "cargo"
|
|
||||||
args = ["build", "--release"]
|
|
||||||
|
|
||||||
[tasks.test]
|
|
||||||
command = "cargo"
|
|
||||||
args = ["test", "--release"]
|
|
|
@ -1,21 +0,0 @@
|
||||||
# Multiplier example
|
|
||||||
|
|
||||||
Example wrapper around a basic Circom circuit to test Circom 2 integration
|
|
||||||
through ark-circom and FFI.
|
|
||||||
|
|
||||||
## Build and Test
|
|
||||||
|
|
||||||
To build and test, run the following commands within the module folder
|
|
||||||
```bash
|
|
||||||
cargo make build
|
|
||||||
cargo make test
|
|
||||||
```
|
|
||||||
|
|
||||||
## FFI
|
|
||||||
|
|
||||||
To generate C or Nim bindings from Rust FFI, use `cbindgen` or `nbindgen`:
|
|
||||||
|
|
||||||
```
|
|
||||||
cbindgen . -o target/multiplier.h
|
|
||||||
nbindgen . -o target/multiplier.nim
|
|
||||||
```
|
|
Binary file not shown.
Binary file not shown.
|
@ -1,77 +0,0 @@
|
||||||
use crate::public::Multiplier;
|
|
||||||
use std::slice;
|
|
||||||
|
|
||||||
/// Buffer struct is taken from
|
|
||||||
/// https://github.com/celo-org/celo-threshold-bls-rs/blob/master/crates/threshold-bls-ffi/src/ffi.rs
|
|
||||||
///
|
|
||||||
/// Also heavily inspired by https://github.com/kilic/rln/blob/master/src/ffi.rs
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub struct Buffer {
|
|
||||||
pub ptr: *const u8,
|
|
||||||
pub len: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&[u8]> for Buffer {
|
|
||||||
fn from(src: &[u8]) -> Self {
|
|
||||||
Self {
|
|
||||||
ptr: &src[0] as *const u8,
|
|
||||||
len: src.len(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&Buffer> for &'a [u8] {
|
|
||||||
fn from(src: &Buffer) -> &'a [u8] {
|
|
||||||
unsafe { slice::from_raw_parts(src.ptr, src.len) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn new_circuit(ctx: *mut *mut Multiplier) -> bool {
|
|
||||||
if let Ok(mul) = Multiplier::new() {
|
|
||||||
unsafe { *ctx = Box::into_raw(Box::new(mul)) };
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn prove(ctx: *const Multiplier, output_buffer: *mut Buffer) -> bool {
|
|
||||||
println!("multiplier ffi: prove");
|
|
||||||
let mul = unsafe { &*ctx };
|
|
||||||
let mut output_data: Vec<u8> = Vec::new();
|
|
||||||
|
|
||||||
match mul.prove(&mut output_data) {
|
|
||||||
Ok(proof_data) => proof_data,
|
|
||||||
Err(_) => return false,
|
|
||||||
};
|
|
||||||
unsafe { *output_buffer = Buffer::from(&output_data[..]) };
|
|
||||||
std::mem::forget(output_data);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn verify(
|
|
||||||
ctx: *const Multiplier,
|
|
||||||
proof_buffer: *const Buffer,
|
|
||||||
result_ptr: *mut u32,
|
|
||||||
) -> bool {
|
|
||||||
println!("multiplier ffi: verify");
|
|
||||||
let mul = unsafe { &*ctx };
|
|
||||||
let proof_data = <&[u8]>::from(unsafe { &*proof_buffer });
|
|
||||||
if match mul.verify(proof_data) {
|
|
||||||
Ok(verified) => verified,
|
|
||||||
Err(_) => return false,
|
|
||||||
} {
|
|
||||||
unsafe { *result_ptr = 0 };
|
|
||||||
} else {
|
|
||||||
unsafe { *result_ptr = 1 };
|
|
||||||
};
|
|
||||||
true
|
|
||||||
}
|
|
|
@ -1,2 +0,0 @@
|
||||||
pub mod ffi;
|
|
||||||
pub mod public;
|
|
|
@ -1,49 +0,0 @@
|
||||||
use ark_circom::{CircomBuilder, CircomConfig};
|
|
||||||
use ark_std::rand::thread_rng;
|
|
||||||
use color_eyre::{Report, Result};
|
|
||||||
|
|
||||||
use ark_bn254::Bn254;
|
|
||||||
use ark_groth16::{
|
|
||||||
create_random_proof as prove, generate_random_parameters, prepare_verifying_key, verify_proof,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn groth16_proof_example() -> Result<()> {
|
|
||||||
let cfg = CircomConfig::<Bn254>::new(
|
|
||||||
"./resources/circom2_multiplier2.wasm",
|
|
||||||
"./resources/circom2_multiplier2.r1cs",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let mut builder = CircomBuilder::new(cfg);
|
|
||||||
builder.push_input("a", 3);
|
|
||||||
builder.push_input("b", 11);
|
|
||||||
|
|
||||||
// create an empty instance for setting it up
|
|
||||||
let circom = builder.setup();
|
|
||||||
|
|
||||||
let mut rng = thread_rng();
|
|
||||||
let params = generate_random_parameters::<Bn254, _, _>(circom, &mut rng)?;
|
|
||||||
|
|
||||||
let circom = builder.build()?;
|
|
||||||
|
|
||||||
let inputs = circom
|
|
||||||
.get_public_inputs()
|
|
||||||
.ok_or(Report::msg("no public inputs"))?;
|
|
||||||
|
|
||||||
let proof = prove(circom, ¶ms, &mut rng)?;
|
|
||||||
|
|
||||||
let pvk = prepare_verifying_key(¶ms.vk);
|
|
||||||
|
|
||||||
match verify_proof(&pvk, &proof, &inputs) {
|
|
||||||
Ok(_) => Ok(()),
|
|
||||||
Err(_) => Err(Report::msg("not verified")),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
println!("Hello, world!");
|
|
||||||
|
|
||||||
match groth16_proof_example() {
|
|
||||||
Ok(_) => println!("Success"),
|
|
||||||
Err(_) => println!("Error"),
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
use ark_circom::{CircomBuilder, CircomCircuit, CircomConfig};
|
|
||||||
use ark_std::rand::thread_rng;
|
|
||||||
|
|
||||||
use ark_bn254::Bn254;
|
|
||||||
use ark_groth16::{
|
|
||||||
create_random_proof as prove, generate_random_parameters, prepare_verifying_key, verify_proof,
|
|
||||||
Proof, ProvingKey,
|
|
||||||
};
|
|
||||||
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
|
|
||||||
use color_eyre::{Report, Result};
|
|
||||||
use std::io::{Read, Write};
|
|
||||||
|
|
||||||
pub struct Multiplier {
|
|
||||||
circom: CircomCircuit<Bn254>,
|
|
||||||
params: ProvingKey<Bn254>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Multiplier {
|
|
||||||
// TODO Break this apart here
|
|
||||||
pub fn new() -> Result<Multiplier> {
|
|
||||||
let cfg = CircomConfig::<Bn254>::new(
|
|
||||||
"./resources/circom2_multiplier2.wasm",
|
|
||||||
"./resources/circom2_multiplier2.r1cs",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let mut builder = CircomBuilder::new(cfg);
|
|
||||||
builder.push_input("a", 3);
|
|
||||||
builder.push_input("b", 11);
|
|
||||||
|
|
||||||
// create an empty instance for setting it up
|
|
||||||
let circom = builder.setup();
|
|
||||||
|
|
||||||
let mut rng = thread_rng();
|
|
||||||
|
|
||||||
let params = generate_random_parameters::<Bn254, _, _>(circom, &mut rng)?;
|
|
||||||
|
|
||||||
let circom = builder.build()?;
|
|
||||||
|
|
||||||
Ok(Multiplier { circom, params })
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Input Read
|
|
||||||
pub fn prove<W: Write>(&self, result_data: W) -> Result<()> {
|
|
||||||
let mut rng = thread_rng();
|
|
||||||
|
|
||||||
// XXX: There's probably a better way to do this
|
|
||||||
let circom = self.circom.clone();
|
|
||||||
let params = self.params.clone();
|
|
||||||
|
|
||||||
let proof = prove(circom, ¶ms, &mut rng)?;
|
|
||||||
|
|
||||||
// XXX: Unclear if this is different from other serialization(s)
|
|
||||||
proof.serialize(result_data)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn verify<R: Read>(&self, input_data: R) -> Result<bool> {
|
|
||||||
let proof = Proof::deserialize(input_data)?;
|
|
||||||
|
|
||||||
let pvk = prepare_verifying_key(&self.params.vk);
|
|
||||||
|
|
||||||
// XXX Part of input data?
|
|
||||||
let inputs = self
|
|
||||||
.circom
|
|
||||||
.get_public_inputs()
|
|
||||||
.ok_or(Report::msg("no public inputs"))?;
|
|
||||||
|
|
||||||
let verified = verify_proof(&pvk, &proof, &inputs)?;
|
|
||||||
|
|
||||||
Ok(verified)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Multiplier {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new().unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use multiplier::public::Multiplier;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn multiplier_proof() {
|
|
||||||
let mul = Multiplier::new().unwrap();
|
|
||||||
|
|
||||||
let mut output_data: Vec<u8> = Vec::new();
|
|
||||||
let _ = mul.prove(&mut output_data);
|
|
||||||
|
|
||||||
let proof_data = &output_data[..];
|
|
||||||
|
|
||||||
// XXX Pass as arg?
|
|
||||||
//let pvk = prepare_verifying_key(&mul.params.vk);
|
|
||||||
|
|
||||||
let verified = mul.verify(proof_data).unwrap();
|
|
||||||
|
|
||||||
assert!(verified);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "private-settlement"
|
|
||||||
version = "0.3.0"
|
|
||||||
edition = "2021"
|
|
||||||
license = "MIT OR Apache-2.0"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
|
@ -1,7 +0,0 @@
|
||||||
[tasks.build]
|
|
||||||
command = "cargo"
|
|
||||||
args = ["build", "--release"]
|
|
||||||
|
|
||||||
[tasks.test]
|
|
||||||
command = "cargo"
|
|
||||||
args = ["test", "--release"]
|
|
|
@ -1,11 +0,0 @@
|
||||||
# Private Settlement Module
|
|
||||||
|
|
||||||
This module is to provide APIs to manage, compute and verify [Private Settlement](https://rfc.vac.dev/spec/44/) zkSNARK proofs and primitives.
|
|
||||||
|
|
||||||
## Build and Test
|
|
||||||
|
|
||||||
To build and test, run the following commands within the module folder
|
|
||||||
```bash
|
|
||||||
cargo make build
|
|
||||||
cargo make test
|
|
||||||
```
|
|
|
@ -1 +0,0 @@
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
#[test]
|
|
||||||
fn it_works() {
|
|
||||||
let result = 2 + 2;
|
|
||||||
assert_eq!(result, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "semaphore-wrapper"
|
|
||||||
version = "0.3.0"
|
|
||||||
edition = "2021"
|
|
||||||
license = "MIT OR Apache-2.0"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = []
|
|
||||||
dylib = [ "wasmer/dylib", "wasmer-engine-dylib", "wasmer-compiler-cranelift" ]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
ark-bn254 = { version = "0.3.0" }
|
|
||||||
ark-circom = { git = "https://github.com/gakonst/ark-circom", features=["circom-2"], rev = "35ce5a9" }
|
|
||||||
ark-ec = { version = "0.3.0", default-features = false, features = ["parallel"] }
|
|
||||||
ark-groth16 = { git = "https://github.com/arkworks-rs/groth16", rev = "765817f", features = ["parallel"] }
|
|
||||||
ark-relations = { version = "0.3.0", default-features = false }
|
|
||||||
ark-std = { version = "0.3.0", default-features = false, features = ["parallel"] }
|
|
||||||
color-eyre = "0.6.2"
|
|
||||||
once_cell = "1.17.1"
|
|
||||||
rand = "0.8.5"
|
|
||||||
semaphore = { git = "https://github.com/worldcoin/semaphore-rs", rev = "ee658c2"}
|
|
||||||
ethers-core = { version = "2.0.10", default-features = false }
|
|
||||||
ruint = { version = "1.10.0", features = [ "serde", "num-bigint", "ark-ff" ] }
|
|
||||||
serde = "1.0"
|
|
||||||
thiserror = "1.0.39"
|
|
||||||
wasmer = { version = "2.3" }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
rand_chacha = "0.3.1"
|
|
||||||
serde_json = "1.0.96"
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
color-eyre = "0.6.2"
|
|
||||||
wasmer = { version = "2.3" }
|
|
||||||
wasmer-engine-dylib = { version = "2.3.0", optional = true }
|
|
||||||
wasmer-compiler-cranelift = { version = "3.3.0", optional = true }
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
codegen-units = 1
|
|
||||||
lto = true
|
|
||||||
panic = "abort"
|
|
||||||
opt-level = 3
|
|
||||||
|
|
||||||
# Compilation profile for any non-workspace member.
|
|
||||||
# Dependencies are optimized, even in a dev build. This improves dev performance
|
|
||||||
# while having neglible impact on incremental build times.
|
|
||||||
[profile.dev.package."*"]
|
|
||||||
opt-level = 3
|
|
|
@ -1,7 +0,0 @@
|
||||||
[tasks.build]
|
|
||||||
command = "cargo"
|
|
||||||
args = ["build", "--release"]
|
|
||||||
|
|
||||||
[tasks.test]
|
|
||||||
command = "cargo"
|
|
||||||
args = ["test", "--release"]
|
|
|
@ -1,18 +0,0 @@
|
||||||
# Semaphore example package
|
|
||||||
|
|
||||||
This is basically a wrapper around/copy of
|
|
||||||
https://github.com/worldcoin/semaphore-rs to illustrate how e.g. RLN package
|
|
||||||
can be structured like.
|
|
||||||
|
|
||||||
Goal is also to provide a basic FFI around protocol.rs, which is currently not
|
|
||||||
in scope for that project.
|
|
||||||
|
|
||||||
See that project for more information.
|
|
||||||
|
|
||||||
## Build and Test
|
|
||||||
|
|
||||||
To build and test, run the following commands within the module folder
|
|
||||||
```bash
|
|
||||||
cargo make build
|
|
||||||
cargo make test
|
|
||||||
```
|
|
|
@ -1,111 +0,0 @@
|
||||||
// Adapted from semaphore-rs/build.rs
|
|
||||||
use color_eyre::eyre::{eyre, Result};
|
|
||||||
use std::{
|
|
||||||
path::{Component, Path, PathBuf},
|
|
||||||
process::Command,
|
|
||||||
};
|
|
||||||
|
|
||||||
const ZKEY_FILE: &str = "./vendor/semaphore/build/snark/semaphore_final.zkey";
|
|
||||||
const WASM_FILE: &str = "./vendor/semaphore/build/snark/semaphore.wasm";
|
|
||||||
|
|
||||||
// See <https://internals.rust-lang.org/t/path-to-lexical-absolute/14940>
|
|
||||||
fn absolute(path: &str) -> Result<PathBuf> {
|
|
||||||
let path = Path::new(path);
|
|
||||||
let mut absolute = if path.is_absolute() {
|
|
||||||
PathBuf::new()
|
|
||||||
} else {
|
|
||||||
std::env::current_dir()?
|
|
||||||
};
|
|
||||||
for component in path.components() {
|
|
||||||
match component {
|
|
||||||
Component::CurDir => {}
|
|
||||||
Component::ParentDir => {
|
|
||||||
absolute.pop();
|
|
||||||
}
|
|
||||||
component => absolute.push(component.as_os_str()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(absolute)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_circuit() -> Result<()> {
|
|
||||||
println!("cargo:rerun-if-changed=./vendor/semaphore");
|
|
||||||
let run = |cmd: &[&str]| -> Result<()> {
|
|
||||||
// TODO: Use ExitCode::exit_ok() when stable.
|
|
||||||
Command::new(cmd[0])
|
|
||||||
.args(cmd[1..].iter())
|
|
||||||
.current_dir("./vendor/semaphore")
|
|
||||||
.status()?
|
|
||||||
.success()
|
|
||||||
.then_some(())
|
|
||||||
.ok_or(eyre!("procees returned failure"))?;
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
|
|
||||||
// Compute absolute paths
|
|
||||||
let zkey_file = absolute(ZKEY_FILE)?;
|
|
||||||
let wasm_file = absolute(WASM_FILE)?;
|
|
||||||
|
|
||||||
// Build circuits if not exists
|
|
||||||
// TODO: This does not rebuild if the semaphore submodule is changed.
|
|
||||||
// NOTE: This requires npm / nodejs to be installed.
|
|
||||||
if !(zkey_file.exists() && wasm_file.exists()) {
|
|
||||||
run(&["npm", "install"])?;
|
|
||||||
run(&["npm", "exec", "ts-node", "./scripts/compile-circuits.ts"])?;
|
|
||||||
}
|
|
||||||
assert!(zkey_file.exists());
|
|
||||||
assert!(wasm_file.exists());
|
|
||||||
|
|
||||||
// Export generated paths
|
|
||||||
println!("cargo:rustc-env=BUILD_RS_ZKEY_FILE={}", zkey_file.display());
|
|
||||||
println!("cargo:rustc-env=BUILD_RS_WASM_FILE={}", wasm_file.display());
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "dylib")]
|
|
||||||
fn build_dylib() -> Result<()> {
|
|
||||||
use enumset::enum_set;
|
|
||||||
use std::{env, str::FromStr};
|
|
||||||
use wasmer::{Module, Store, Target, Triple};
|
|
||||||
use wasmer_compiler_cranelift::Cranelift;
|
|
||||||
use wasmer_engine_dylib::Dylib;
|
|
||||||
|
|
||||||
let wasm_file = absolute(WASM_FILE)?;
|
|
||||||
assert!(wasm_file.exists());
|
|
||||||
|
|
||||||
let out_dir = env::var("OUT_DIR")?;
|
|
||||||
let out_dir = Path::new(&out_dir).to_path_buf();
|
|
||||||
let dylib_file = out_dir.join("semaphore.dylib");
|
|
||||||
println!(
|
|
||||||
"cargo:rustc-env=CIRCUIT_WASM_DYLIB={}",
|
|
||||||
dylib_file.display()
|
|
||||||
);
|
|
||||||
|
|
||||||
if dylib_file.exists() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a WASM engine for the target that can compile
|
|
||||||
let triple = Triple::from_str(&env::var("TARGET")?).map_err(|e| eyre!(e))?;
|
|
||||||
let cpu_features = enum_set!();
|
|
||||||
let target = Target::new(triple, cpu_features);
|
|
||||||
let compiler_config = Cranelift::default();
|
|
||||||
let engine = Dylib::new(compiler_config).target(target).engine();
|
|
||||||
|
|
||||||
// Compile the WASM module
|
|
||||||
let store = Store::new(&engine);
|
|
||||||
let module = Module::from_file(&store, &wasm_file)?;
|
|
||||||
module.serialize_to_file(&dylib_file)?;
|
|
||||||
assert!(dylib_file.exists());
|
|
||||||
println!("cargo:warning=Circuit dylib is in {}", dylib_file.display());
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
|
||||||
build_circuit()?;
|
|
||||||
#[cfg(feature = "dylib")]
|
|
||||||
build_dylib()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
// Adapted from semaphore-rs/src/circuit.rs
|
|
||||||
use ark_bn254::{Bn254, Fr};
|
|
||||||
use ark_circom::{read_zkey, WitnessCalculator};
|
|
||||||
use ark_groth16::ProvingKey;
|
|
||||||
use ark_relations::r1cs::ConstraintMatrices;
|
|
||||||
use core::include_bytes;
|
|
||||||
use once_cell::sync::{Lazy, OnceCell};
|
|
||||||
use std::{io::Cursor, sync::Mutex};
|
|
||||||
use wasmer::{Module, Store};
|
|
||||||
|
|
||||||
#[cfg(feature = "dylib")]
|
|
||||||
use std::{env, path::Path};
|
|
||||||
#[cfg(feature = "dylib")]
|
|
||||||
use wasmer::Dylib;
|
|
||||||
|
|
||||||
const ZKEY_BYTES: &[u8] = include_bytes!(env!("BUILD_RS_ZKEY_FILE"));
|
|
||||||
|
|
||||||
#[cfg(not(feature = "dylib"))]
|
|
||||||
const WASM: &[u8] = include_bytes!(env!("BUILD_RS_WASM_FILE"));
|
|
||||||
|
|
||||||
static ZKEY: Lazy<(ProvingKey<Bn254>, ConstraintMatrices<Fr>)> = Lazy::new(|| {
|
|
||||||
let mut reader = Cursor::new(ZKEY_BYTES);
|
|
||||||
read_zkey(&mut reader).expect("zkey should be valid")
|
|
||||||
});
|
|
||||||
|
|
||||||
static WITNESS_CALCULATOR: OnceCell<Mutex<WitnessCalculator>> = OnceCell::new();
|
|
||||||
|
|
||||||
/// Initialize the library.
|
|
||||||
#[cfg(feature = "dylib")]
|
|
||||||
pub fn initialize(dylib_path: &Path) {
|
|
||||||
WITNESS_CALCULATOR
|
|
||||||
.set(from_dylib(dylib_path))
|
|
||||||
.expect("Failed to initialize witness calculator");
|
|
||||||
|
|
||||||
// Force init of ZKEY
|
|
||||||
Lazy::force(&ZKEY);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "dylib")]
|
|
||||||
fn from_dylib(path: &Path) -> Mutex<WitnessCalculator> {
|
|
||||||
let store = Store::new(&Dylib::headless().engine());
|
|
||||||
// The module must be exported using [`Module::serialize`].
|
|
||||||
let module = unsafe {
|
|
||||||
Module::deserialize_from_file(&store, path).expect("Failed to load wasm dylib module")
|
|
||||||
};
|
|
||||||
let result =
|
|
||||||
WitnessCalculator::from_module(module).expect("Failed to create witness calculator");
|
|
||||||
Mutex::new(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn zkey() -> &'static (ProvingKey<Bn254>, ConstraintMatrices<Fr>) {
|
|
||||||
&ZKEY
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "dylib")]
|
|
||||||
#[must_use]
|
|
||||||
pub fn witness_calculator() -> &'static Mutex<WitnessCalculator> {
|
|
||||||
WITNESS_CALCULATOR.get_or_init(|| {
|
|
||||||
let path = env::var("CIRCUIT_WASM_DYLIB").expect(
|
|
||||||
"Semaphore-rs is not initialized. The library needs to be initialized before use when \
|
|
||||||
build with the `cdylib` feature. You can initialize by calling `initialize` or \
|
|
||||||
seting the `CIRCUIT_WASM_DYLIB` environment variable.",
|
|
||||||
);
|
|
||||||
from_dylib(Path::new(&path))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "dylib"))]
|
|
||||||
#[must_use]
|
|
||||||
pub fn witness_calculator() -> &'static Mutex<WitnessCalculator> {
|
|
||||||
WITNESS_CALCULATOR.get_or_init(|| {
|
|
||||||
let store = Store::default();
|
|
||||||
let module = Module::from_binary(&store, WASM).expect("wasm should be valid");
|
|
||||||
let result =
|
|
||||||
WitnessCalculator::from_module(module).expect("Failed to create witness calculator");
|
|
||||||
Mutex::new(result)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
#![allow(clippy::multiple_crate_versions)]
|
|
||||||
|
|
||||||
pub mod circuit;
|
|
||||||
pub mod protocol;
|
|
||||||
|
|
||||||
#[cfg(feature = "dylib")]
|
|
||||||
pub use circuit::initialize;
|
|
|
@ -1,215 +0,0 @@
|
||||||
// Adapted from semaphore-rs/src/protocol.rs
|
|
||||||
// For illustration purposes only as an example protocol
|
|
||||||
|
|
||||||
// Private module
|
|
||||||
use crate::circuit::{witness_calculator, zkey};
|
|
||||||
|
|
||||||
use ark_bn254::{Bn254, Parameters};
|
|
||||||
use ark_circom::CircomReduction;
|
|
||||||
use ark_ec::bn::Bn;
|
|
||||||
use ark_groth16::{
|
|
||||||
create_proof_with_reduction_and_matrices, prepare_verifying_key, Proof as ArkProof,
|
|
||||||
};
|
|
||||||
use ark_relations::r1cs::SynthesisError;
|
|
||||||
use ark_std::UniformRand;
|
|
||||||
use color_eyre::{Report, Result};
|
|
||||||
use ethers_core::types::U256;
|
|
||||||
use rand::{thread_rng, Rng};
|
|
||||||
use semaphore::{
|
|
||||||
identity::Identity,
|
|
||||||
merkle_tree::{self, Branch},
|
|
||||||
poseidon,
|
|
||||||
poseidon_tree::PoseidonHash,
|
|
||||||
Field,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::time::Instant;
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
// Matches the private G1Tup type in ark-circom.
|
|
||||||
pub type G1 = (U256, U256);
|
|
||||||
|
|
||||||
// Matches the private G2Tup type in ark-circom.
|
|
||||||
pub type G2 = ([U256; 2], [U256; 2]);
|
|
||||||
|
|
||||||
/// Wrap a proof object so we have serde support
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
pub struct Proof(G1, G2, G1);
|
|
||||||
|
|
||||||
impl From<ArkProof<Bn<Parameters>>> for Proof {
|
|
||||||
fn from(proof: ArkProof<Bn<Parameters>>) -> Self {
|
|
||||||
let proof = ark_circom::ethereum::Proof::from(proof);
|
|
||||||
let (a, b, c) = proof.as_tuple();
|
|
||||||
Self(a, b, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Proof> for ArkProof<Bn<Parameters>> {
|
|
||||||
fn from(proof: Proof) -> Self {
|
|
||||||
let eth_proof = ark_circom::ethereum::Proof {
|
|
||||||
a: ark_circom::ethereum::G1 {
|
|
||||||
x: proof.0 .0,
|
|
||||||
y: proof.0 .1,
|
|
||||||
},
|
|
||||||
#[rustfmt::skip] // Rustfmt inserts some confusing spaces
|
|
||||||
b: ark_circom::ethereum::G2 {
|
|
||||||
// The order of coefficients is flipped.
|
|
||||||
x: [proof.1.0[1], proof.1.0[0]],
|
|
||||||
y: [proof.1.1[1], proof.1.1[0]],
|
|
||||||
},
|
|
||||||
c: ark_circom::ethereum::G1 {
|
|
||||||
x: proof.2 .0,
|
|
||||||
y: proof.2 .1,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
eth_proof.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Helper to merkle proof into a bigint vector
|
|
||||||
/// TODO: we should create a From trait for this
|
|
||||||
fn merkle_proof_to_vec(proof: &merkle_tree::Proof<PoseidonHash>) -> Vec<Field> {
|
|
||||||
proof
|
|
||||||
.0
|
|
||||||
.iter()
|
|
||||||
.map(|x| match x {
|
|
||||||
Branch::Left(value) | Branch::Right(value) => *value,
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates the nullifier hash
|
|
||||||
#[must_use]
|
|
||||||
pub fn generate_nullifier_hash(identity: &Identity, external_nullifier: Field) -> Field {
|
|
||||||
poseidon::hash2(external_nullifier, identity.nullifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum ProofError {
|
|
||||||
#[error("Error reading circuit key: {0}")]
|
|
||||||
CircuitKeyError(#[from] std::io::Error),
|
|
||||||
#[error("Error producing witness: {0}")]
|
|
||||||
WitnessError(Report),
|
|
||||||
#[error("Error producing proof: {0}")]
|
|
||||||
SynthesisError(#[from] SynthesisError),
|
|
||||||
#[error("Error converting public input: {0}")]
|
|
||||||
ToFieldError(#[from] ruint::ToFieldError),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates a semaphore proof
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns a [`ProofError`] if proving fails.
|
|
||||||
pub fn generate_proof(
|
|
||||||
identity: &Identity,
|
|
||||||
merkle_proof: &merkle_tree::Proof<PoseidonHash>,
|
|
||||||
external_nullifier_hash: Field,
|
|
||||||
signal_hash: Field,
|
|
||||||
) -> Result<Proof, ProofError> {
|
|
||||||
generate_proof_rng(
|
|
||||||
identity,
|
|
||||||
merkle_proof,
|
|
||||||
external_nullifier_hash,
|
|
||||||
signal_hash,
|
|
||||||
&mut thread_rng(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates a semaphore proof from entropy
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns a [`ProofError`] if proving fails.
|
|
||||||
pub fn generate_proof_rng(
|
|
||||||
identity: &Identity,
|
|
||||||
merkle_proof: &merkle_tree::Proof<PoseidonHash>,
|
|
||||||
external_nullifier_hash: Field,
|
|
||||||
signal_hash: Field,
|
|
||||||
rng: &mut impl Rng,
|
|
||||||
) -> Result<Proof, ProofError> {
|
|
||||||
generate_proof_rs(
|
|
||||||
identity,
|
|
||||||
merkle_proof,
|
|
||||||
external_nullifier_hash,
|
|
||||||
signal_hash,
|
|
||||||
ark_bn254::Fr::rand(rng),
|
|
||||||
ark_bn254::Fr::rand(rng),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn generate_proof_rs(
|
|
||||||
identity: &Identity,
|
|
||||||
merkle_proof: &merkle_tree::Proof<PoseidonHash>,
|
|
||||||
external_nullifier_hash: Field,
|
|
||||||
signal_hash: Field,
|
|
||||||
r: ark_bn254::Fr,
|
|
||||||
s: ark_bn254::Fr,
|
|
||||||
) -> Result<Proof, ProofError> {
|
|
||||||
let inputs = [
|
|
||||||
("identityNullifier", vec![identity.nullifier]),
|
|
||||||
("identityTrapdoor", vec![identity.trapdoor]),
|
|
||||||
("treePathIndices", merkle_proof.path_index()),
|
|
||||||
("treeSiblings", merkle_proof_to_vec(merkle_proof)),
|
|
||||||
("externalNullifier", vec![external_nullifier_hash]),
|
|
||||||
("signalHash", vec![signal_hash]),
|
|
||||||
];
|
|
||||||
let inputs = inputs.into_iter().map(|(name, values)| {
|
|
||||||
(
|
|
||||||
name.to_string(),
|
|
||||||
values.iter().copied().map(Into::into).collect::<Vec<_>>(),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let now = Instant::now();
|
|
||||||
|
|
||||||
let full_assignment = witness_calculator()
|
|
||||||
.lock()
|
|
||||||
.expect("witness_calculator mutex should not get poisoned")
|
|
||||||
.calculate_witness_element::<Bn254, _>(inputs, false)
|
|
||||||
.map_err(ProofError::WitnessError)?;
|
|
||||||
|
|
||||||
println!("witness generation took: {:.2?}", now.elapsed());
|
|
||||||
|
|
||||||
let now = Instant::now();
|
|
||||||
let zkey = zkey();
|
|
||||||
let ark_proof = create_proof_with_reduction_and_matrices::<_, CircomReduction>(
|
|
||||||
&zkey.0,
|
|
||||||
r,
|
|
||||||
s,
|
|
||||||
&zkey.1,
|
|
||||||
zkey.1.num_instance_variables,
|
|
||||||
zkey.1.num_constraints,
|
|
||||||
full_assignment.as_slice(),
|
|
||||||
)?;
|
|
||||||
let proof = ark_proof.into();
|
|
||||||
println!("proof generation took: {:.2?}", now.elapsed());
|
|
||||||
|
|
||||||
Ok(proof)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verifies a given semaphore proof
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns a [`ProofError`] if verifying fails. Verification failure does not
|
|
||||||
/// necessarily mean the proof is incorrect.
|
|
||||||
pub fn verify_proof(
|
|
||||||
root: Field,
|
|
||||||
nullifier_hash: Field,
|
|
||||||
signal_hash: Field,
|
|
||||||
external_nullifier_hash: Field,
|
|
||||||
proof: &Proof,
|
|
||||||
) -> Result<bool, ProofError> {
|
|
||||||
let zkey = zkey();
|
|
||||||
let pvk = prepare_verifying_key(&zkey.0.vk);
|
|
||||||
|
|
||||||
let public_inputs = [root, nullifier_hash, signal_hash, external_nullifier_hash]
|
|
||||||
.iter()
|
|
||||||
.map(ark_bn254::Fr::try_from)
|
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
|
||||||
|
|
||||||
let ark_proof = (*proof).into();
|
|
||||||
let result = ark_groth16::verify_proof(&pvk, &ark_proof, &public_inputs[..])?;
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
|
@ -1,115 +0,0 @@
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use ark_bn254::Parameters;
|
|
||||||
use ark_ec::bn::Bn;
|
|
||||||
use ark_groth16::Proof as ArkProof;
|
|
||||||
use rand::{Rng, SeedableRng as _};
|
|
||||||
use rand_chacha::ChaChaRng;
|
|
||||||
use semaphore::{hash_to_field, identity::Identity, poseidon_tree::PoseidonTree, Field};
|
|
||||||
use semaphore_wrapper::protocol::{
|
|
||||||
generate_nullifier_hash, generate_proof, generate_proof_rng, verify_proof, Proof,
|
|
||||||
};
|
|
||||||
use serde_json::json;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_semaphore() {
|
|
||||||
// generate identity
|
|
||||||
let id = Identity::from_seed(b"secret");
|
|
||||||
|
|
||||||
// generate merkle tree
|
|
||||||
let leaf = Field::from(0);
|
|
||||||
let mut tree = PoseidonTree::new(21, leaf);
|
|
||||||
tree.set(0, id.commitment());
|
|
||||||
|
|
||||||
let merkle_proof = tree.proof(0).expect("proof should exist");
|
|
||||||
let root = tree.root().into();
|
|
||||||
|
|
||||||
// change signal and external_nullifier here
|
|
||||||
let signal_hash = hash_to_field(b"xxx");
|
|
||||||
let external_nullifier_hash = hash_to_field(b"appId");
|
|
||||||
|
|
||||||
let nullifier_hash = generate_nullifier_hash(&id, external_nullifier_hash);
|
|
||||||
|
|
||||||
let proof =
|
|
||||||
generate_proof(&id, &merkle_proof, external_nullifier_hash, signal_hash).unwrap();
|
|
||||||
|
|
||||||
let success = verify_proof(
|
|
||||||
root,
|
|
||||||
nullifier_hash,
|
|
||||||
signal_hash,
|
|
||||||
external_nullifier_hash,
|
|
||||||
&proof,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert!(success);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn arb_proof(seed: u64) -> Proof {
|
|
||||||
// Deterministic randomness for testing
|
|
||||||
let mut rng = ChaChaRng::seed_from_u64(seed);
|
|
||||||
|
|
||||||
// generate identity
|
|
||||||
let seed: [u8; 16] = rng.gen();
|
|
||||||
let id = Identity::from_seed(&seed);
|
|
||||||
|
|
||||||
// generate merkle tree
|
|
||||||
let leaf = Field::from(0);
|
|
||||||
let mut tree = PoseidonTree::new(21, leaf);
|
|
||||||
tree.set(0, id.commitment());
|
|
||||||
|
|
||||||
let merkle_proof = tree.proof(0).expect("proof should exist");
|
|
||||||
|
|
||||||
let external_nullifier: [u8; 16] = rng.gen();
|
|
||||||
let external_nullifier_hash = hash_to_field(&external_nullifier);
|
|
||||||
|
|
||||||
let signal: [u8; 16] = rng.gen();
|
|
||||||
let signal_hash = hash_to_field(&signal);
|
|
||||||
|
|
||||||
generate_proof_rng(
|
|
||||||
&id,
|
|
||||||
&merkle_proof,
|
|
||||||
external_nullifier_hash,
|
|
||||||
signal_hash,
|
|
||||||
&mut rng,
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_proof_cast_roundtrip() {
|
|
||||||
let proof = arb_proof(123);
|
|
||||||
let ark_proof: ArkProof<Bn<Parameters>> = proof.into();
|
|
||||||
let result: Proof = ark_proof.into();
|
|
||||||
assert_eq!(proof, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_proof_serialize() {
|
|
||||||
let proof = arb_proof(456);
|
|
||||||
let json = serde_json::to_value(&proof).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
json,
|
|
||||||
json!([
|
|
||||||
[
|
|
||||||
"0x249ae469686987ee9368da60dd177a8c42891c02f5760e955e590c79d55cfab2",
|
|
||||||
"0xf22e25870f49388459d388afb24dcf6ec11bb2d4def1e2ec26d6e42f373aad8"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
[
|
|
||||||
"0x17bd25dbd7436c30ea5b8a3a47aadf11ed646c4b25cc14a84ff8cbe0252ff1f8",
|
|
||||||
"0x1c140668c56688367416534d57b4a14e5a825efdd5e121a6a2099f6dc4cd277b"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"0x26a8524759d969ea0682a092cf7a551697d81962d6c998f543f81e52d83e05e1",
|
|
||||||
"0x273eb3f796fd1807b9df9c6d769d983e3dabdc61677b75d48bb7691303b2c8dd"
|
|
||||||
]
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"0x62715c53a0eb4c46dbb5f73f1fd7449b9c63d37c1ece65debc39b472065a90f",
|
|
||||||
"0x114f7becc66f1cd7a8b01c89db8233622372fc0b6fc037c4313bca41e2377fd9"
|
|
||||||
]
|
|
||||||
])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 5186a940ff495ff163bd5779631a716d0bf96507
|
|
Loading…
Reference in New Issue