mirror of
https://github.com/vacp2p/zerokit.git
synced 2026-01-09 13:47:58 -05:00
Compare commits
3 Commits
v0.3.7
...
rln-v0.3.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
def5125d8f | ||
|
|
ebdad7b78c | ||
|
|
7aaac2b792 |
49
.github/workflows/ci.yml
vendored
49
.github/workflows/ci.yml
vendored
@@ -3,20 +3,27 @@ on:
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- "**.md"
|
||||
- "!.github/workflows/*.yml"
|
||||
- "!rln-wasm/**"
|
||||
- "!rln/src/**"
|
||||
- "!rln/resources/**"
|
||||
- "!utils/src/**"
|
||||
- '**.md'
|
||||
- '!.github/workflows/*.yml'
|
||||
- '!multiplier/src/**'
|
||||
- '!private-settlement/src/**'
|
||||
- '!rln-wasm/**'
|
||||
- '!rln/src/**'
|
||||
- '!rln/resources/**'
|
||||
- '!semaphore/src/**'
|
||||
- '!utils/src/**'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "**.md"
|
||||
- "!.github/workflows/*.yml"
|
||||
- "!rln-wasm/**"
|
||||
- "!rln/src/**"
|
||||
- "!rln/resources/**"
|
||||
- "!utils/src/**"
|
||||
- '**.md'
|
||||
- '!.github/workflows/*.yml'
|
||||
- '!multiplier/src/**'
|
||||
- '!private-settlement/src/**'
|
||||
- '!rln-wasm/**'
|
||||
- '!rln/src/**'
|
||||
- '!rln/resources/**'
|
||||
- '!semaphore/src/**'
|
||||
- '!utils/src/**'
|
||||
|
||||
|
||||
name: Tests
|
||||
|
||||
@@ -25,10 +32,10 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [ubuntu-latest, macos-latest]
|
||||
crate: [rln, utils]
|
||||
crate: [multiplier, semaphore, rln, utils]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
timeout-minutes: 60
|
||||
|
||||
|
||||
name: test - ${{ matrix.crate }} - ${{ matrix.platform }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
@@ -44,16 +51,16 @@ jobs:
|
||||
run: make installdeps
|
||||
- name: cargo-make test
|
||||
run: |
|
||||
cargo make test --release
|
||||
cargo make test --release
|
||||
working-directory: ${{ matrix.crate }}
|
||||
|
||||
|
||||
rln-wasm:
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [ubuntu-latest, macos-latest]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
timeout-minutes: 60
|
||||
|
||||
|
||||
name: test - rln-wasm - ${{ matrix.platform }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -78,11 +85,11 @@ jobs:
|
||||
matrix:
|
||||
# we run lint tests only on ubuntu
|
||||
platform: [ubuntu-latest]
|
||||
crate: [rln, utils]
|
||||
crate: [multiplier, semaphore, rln, utils]
|
||||
runs-on: ${{ matrix.platform }}
|
||||
timeout-minutes: 60
|
||||
|
||||
name: lint - ${{ matrix.crate }} - ${{ matrix.platform }}
|
||||
name: lint - ${{ matrix.crate }} - ${{ matrix.platform }}
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
@@ -103,7 +110,7 @@ jobs:
|
||||
- name: cargo clippy
|
||||
if: success() || failure()
|
||||
run: |
|
||||
cargo clippy --release -- -D warnings
|
||||
cargo clippy --release -- -D warnings
|
||||
working-directory: ${{ matrix.crate }}
|
||||
# We skip clippy on rln-wasm, since wasm target is managed by cargo make
|
||||
# Currently not treating warnings as error, too noisy
|
||||
@@ -127,4 +134,4 @@ jobs:
|
||||
- uses: boa-dev/criterion-compare-action@v3
|
||||
with:
|
||||
branchName: ${{ github.base_ref }}
|
||||
cwd: ${{ matrix.crate }}
|
||||
cwd: ${{ matrix.crate }}
|
||||
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -2,3 +2,7 @@
|
||||
path = rln/vendor/rln
|
||||
ignore = dirty
|
||||
url = https://github.com/Rate-Limiting-Nullifier/rln_circuits.git
|
||||
[submodule "semaphore/vendor/semaphore"]
|
||||
path = semaphore/vendor/semaphore
|
||||
ignore = dirty
|
||||
url = https://github.com/appliedzkp/semaphore.git
|
||||
|
||||
1607
Cargo.lock
generated
1607
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
13
Cargo.toml
13
Cargo.toml
@@ -1,5 +1,13 @@
|
||||
[workspace]
|
||||
members = ["rln", "rln-cli", "rln-wasm", "utils"]
|
||||
members = [
|
||||
"multiplier",
|
||||
"private-settlement",
|
||||
"semaphore",
|
||||
"rln",
|
||||
"rln-cli",
|
||||
"rln-wasm",
|
||||
"utils",
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
# Compilation profile for any non-workspace member.
|
||||
@@ -11,3 +19,6 @@ opt-level = 3
|
||||
[profile.release.package."rln-wasm"]
|
||||
# Tell `rustc` to optimize for small code size.
|
||||
opt-level = "s"
|
||||
|
||||
[profile.release.package."semaphore"]
|
||||
codegen-units = 1
|
||||
|
||||
@@ -29,7 +29,4 @@ image = "ghcr.io/cross-rs/mips64-unknown-linux-gnuabi64:latest"
|
||||
image = "ghcr.io/cross-rs/mips64el-unknown-linux-gnuabi64:latest"
|
||||
|
||||
[target.mipsel-unknown-linux-gnu]
|
||||
image = "ghcr.io/cross-rs/mipsel-unknown-linux-gnu:latest"
|
||||
|
||||
[target.aarch64-linux-android]
|
||||
image = "ghcr.io/cross-rs/aarch64-linux-android:edge"
|
||||
image = "ghcr.io/cross-rs/mipsel-unknown-linux-gnu:latest"
|
||||
32
multiplier/Cargo.toml
Normal file
32
multiplier/Cargo.toml
Normal file
@@ -0,0 +1,32 @@
|
||||
[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"
|
||||
7
multiplier/Makefile.toml
Normal file
7
multiplier/Makefile.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[tasks.build]
|
||||
command = "cargo"
|
||||
args = ["build", "--release"]
|
||||
|
||||
[tasks.test]
|
||||
command = "cargo"
|
||||
args = ["test", "--release"]
|
||||
21
multiplier/README.md
Normal file
21
multiplier/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# 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
|
||||
```
|
||||
BIN
multiplier/resources/circom2_multiplier2.r1cs
Normal file
BIN
multiplier/resources/circom2_multiplier2.r1cs
Normal file
Binary file not shown.
BIN
multiplier/resources/circom2_multiplier2.wasm
Normal file
BIN
multiplier/resources/circom2_multiplier2.wasm
Normal file
Binary file not shown.
77
multiplier/src/ffi.rs
Normal file
77
multiplier/src/ffi.rs
Normal file
@@ -0,0 +1,77 @@
|
||||
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
|
||||
}
|
||||
2
multiplier/src/lib.rs
Normal file
2
multiplier/src/lib.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod ffi;
|
||||
pub mod public;
|
||||
49
multiplier/src/main.rs
Normal file
49
multiplier/src/main.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
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"),
|
||||
}
|
||||
}
|
||||
79
multiplier/src/public.rs
Normal file
79
multiplier/src/public.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
21
multiplier/tests/public.rs
Normal file
21
multiplier/tests/public.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
#[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);
|
||||
}
|
||||
}
|
||||
9
private-settlement/Cargo.toml
Normal file
9
private-settlement/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[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]
|
||||
7
private-settlement/Makefile.toml
Normal file
7
private-settlement/Makefile.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[tasks.build]
|
||||
command = "cargo"
|
||||
args = ["build", "--release"]
|
||||
|
||||
[tasks.test]
|
||||
command = "cargo"
|
||||
args = ["test", "--release"]
|
||||
11
private-settlement/README.md
Normal file
11
private-settlement/README.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# 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
private-settlement/src/lib.rs
Normal file
1
private-settlement/src/lib.rs
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
11
private-settlement/tests/private-settlement.rs
Normal file
11
private-settlement/tests/private-settlement.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = 2 + 2;
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,6 @@ pub(crate) enum Commands {
|
||||
/// Sets a custom config file
|
||||
#[arg(short, long)]
|
||||
config: PathBuf,
|
||||
#[arg(short, long)]
|
||||
tree_config_input: PathBuf,
|
||||
},
|
||||
SetTree {
|
||||
tree_height: usize,
|
||||
|
||||
@@ -34,7 +34,6 @@ fn main() -> Result<()> {
|
||||
Some(Commands::NewWithParams {
|
||||
tree_height,
|
||||
config,
|
||||
tree_config_input,
|
||||
}) => {
|
||||
let mut resources: Vec<Vec<u8>> = Vec::new();
|
||||
for filename in ["rln.wasm", "rln_final.zkey", "verification_key.json"] {
|
||||
@@ -45,13 +44,11 @@ fn main() -> Result<()> {
|
||||
file.read_exact(&mut buffer)?;
|
||||
resources.push(buffer);
|
||||
}
|
||||
let tree_config_input_file = File::open(&tree_config_input)?;
|
||||
state.rln = Some(RLN::new_with_params(
|
||||
*tree_height,
|
||||
resources[0].clone(),
|
||||
resources[1].clone(),
|
||||
resources[2].clone(),
|
||||
tree_config_input_file,
|
||||
)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use color_eyre::Result;
|
||||
use rln::public::RLN;
|
||||
use std::fs::File;
|
||||
|
||||
use crate::config::{Config, InnerConfig};
|
||||
use crate::config::{InnerConfig, Config};
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct State<'a> {
|
||||
|
||||
@@ -250,7 +250,11 @@ pub fn wasm_set_metadata(ctx: *mut RLNWrapper, input: Uint8Array) -> Result<(),
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[wasm_bindgen(js_name = getMetadata)]
|
||||
pub fn wasm_get_metadata(ctx: *mut RLNWrapper) -> Result<Uint8Array, String> {
|
||||
call_with_output_and_error_msg!(ctx, get_metadata, "could not get metadata".to_string())
|
||||
call_with_output_and_error_msg!(
|
||||
ctx,
|
||||
get_metadata,
|
||||
"could not get metadata".to_string()
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rln"
|
||||
version = "0.3.7"
|
||||
version = "0.3.1"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "APIs to manage, compute and verify zkSNARK proofs and RLN primitives"
|
||||
@@ -19,19 +19,13 @@ doctest = false
|
||||
[dependencies]
|
||||
# ZKP Generation
|
||||
ark-ec = { version = "=0.4.1", default-features = false }
|
||||
ark-ff = { version = "=0.4.1", default-features = false, features = ["asm"] }
|
||||
ark-ff = { version = "=0.4.1", default-features = false, features = [ "asm"] }
|
||||
ark-std = { version = "=0.4.0", default-features = false }
|
||||
ark-bn254 = { version = "=0.4.0" }
|
||||
ark-groth16 = { version = "=0.4.0", features = [
|
||||
"parallel",
|
||||
], default-features = false }
|
||||
ark-relations = { version = "=0.4.0", default-features = false, features = [
|
||||
"std",
|
||||
] }
|
||||
ark-groth16 = { version = "=0.4.0", features = ["parallel"], default-features = false }
|
||||
ark-relations = { version = "=0.4.0", default-features = false, features = [ "std" ] }
|
||||
ark-serialize = { version = "=0.4.1", default-features = false }
|
||||
ark-circom = { version = "=0.1.0", default-features = false, features = [
|
||||
"circom-2",
|
||||
] }
|
||||
ark-circom = { version = "=0.1.0", default-features = false, features = ["circom-2"] }
|
||||
|
||||
# WASM
|
||||
wasmer = { version = "=2.3.0", default-features = false }
|
||||
@@ -42,15 +36,13 @@ thiserror = "=1.0.39"
|
||||
|
||||
# utilities
|
||||
cfg-if = "=1.0"
|
||||
num-bigint = { version = "=0.4.3", default-features = false, features = [
|
||||
"rand",
|
||||
] }
|
||||
num-bigint = { version = "=0.4.3", default-features = false, features = ["rand"] }
|
||||
num-traits = "=0.2.15"
|
||||
once_cell = "=1.17.1"
|
||||
rand = "=0.8.5"
|
||||
rand_chacha = "=0.3.1"
|
||||
tiny-keccak = { version = "=2.0.2", features = ["keccak"] }
|
||||
utils = { package = "zerokit_utils", version = "=0.3.3", path = "../utils/", default-features = false }
|
||||
utils = { package = "zerokit_utils", version = "=0.3.1", path = "../utils/", default-features = false }
|
||||
|
||||
# serialization
|
||||
serde_json = "=1.0.96"
|
||||
@@ -64,13 +56,7 @@ criterion = { version = "=0.4.0", features = ["html_reports"] }
|
||||
|
||||
[features]
|
||||
default = ["parallel", "wasmer/sys-default", "pmtree-ft"]
|
||||
parallel = [
|
||||
"ark-ec/parallel",
|
||||
"ark-ff/parallel",
|
||||
"ark-std/parallel",
|
||||
"ark-groth16/parallel",
|
||||
"utils/parallel",
|
||||
]
|
||||
parallel = ["ark-ec/parallel", "ark-ff/parallel", "ark-std/parallel", "ark-groth16/parallel", "utils/parallel"]
|
||||
wasm = ["wasmer/js", "wasmer/std"]
|
||||
fullmerkletree = ["default"]
|
||||
|
||||
|
||||
138
rln/src/ffi.rs
138
rln/src/ffi.rs
@@ -12,15 +12,7 @@ macro_rules! call {
|
||||
($instance:expr, $method:ident $(, $arg:expr)*) => {
|
||||
{
|
||||
let new_instance: &mut RLN = $instance.process();
|
||||
match new_instance.$method($($arg.process()),*) {
|
||||
Ok(()) => {
|
||||
true
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("execution error: {err}");
|
||||
false
|
||||
}
|
||||
}
|
||||
new_instance.$method($($arg.process()),*).is_ok()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,17 +30,13 @@ macro_rules! call_with_output_arg {
|
||||
{
|
||||
let mut output_data: Vec<u8> = Vec::new();
|
||||
let new_instance = $instance.process();
|
||||
match new_instance.$method(&mut output_data) {
|
||||
Ok(()) => {
|
||||
unsafe { *$output_arg = Buffer::from(&output_data[..]) };
|
||||
std::mem::forget(output_data);
|
||||
true
|
||||
}
|
||||
Err(err) => {
|
||||
std::mem::forget(output_data);
|
||||
eprintln!("execution error: {err}");
|
||||
false
|
||||
}
|
||||
if new_instance.$method(&mut output_data).is_ok() {
|
||||
unsafe { *$output_arg = Buffer::from(&output_data[..]) };
|
||||
std::mem::forget(output_data);
|
||||
true
|
||||
} else {
|
||||
std::mem::forget(output_data);
|
||||
false
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -56,17 +44,13 @@ macro_rules! call_with_output_arg {
|
||||
{
|
||||
let mut output_data: Vec<u8> = Vec::new();
|
||||
let new_instance = $instance.process();
|
||||
match new_instance.$method($($arg.process()),*, &mut output_data) {
|
||||
Ok(()) => {
|
||||
unsafe { *$output_arg = Buffer::from(&output_data[..]) };
|
||||
std::mem::forget(output_data);
|
||||
true
|
||||
}
|
||||
Err(err) => {
|
||||
std::mem::forget(output_data);
|
||||
eprintln!("execution error: {err}");
|
||||
false
|
||||
}
|
||||
if new_instance.$method($($arg.process()),*, &mut output_data).is_ok() {
|
||||
unsafe { *$output_arg = Buffer::from(&output_data[..]) };
|
||||
std::mem::forget(output_data);
|
||||
true
|
||||
} else {
|
||||
std::mem::forget(output_data);
|
||||
false
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -82,17 +66,13 @@ macro_rules! no_ctx_call_with_output_arg {
|
||||
($method:ident, $output_arg:expr, $( $arg:expr ),* ) => {
|
||||
{
|
||||
let mut output_data: Vec<u8> = Vec::new();
|
||||
match $method($($arg.process()),*, &mut output_data) {
|
||||
Ok(()) => {
|
||||
unsafe { *$output_arg = Buffer::from(&output_data[..]) };
|
||||
std::mem::forget(output_data);
|
||||
true
|
||||
}
|
||||
Err(err) => {
|
||||
std::mem::forget(output_data);
|
||||
eprintln!("execution error: {err}");
|
||||
false
|
||||
}
|
||||
if $method($($arg.process()),*, &mut output_data).is_ok() {
|
||||
unsafe { *$output_arg = Buffer::from(&output_data[..]) };
|
||||
std::mem::forget(output_data);
|
||||
true
|
||||
} else {
|
||||
std::mem::forget(output_data);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,11 +89,8 @@ macro_rules! call_with_bool_arg {
|
||||
{
|
||||
let new_instance = $instance.process();
|
||||
if match new_instance.$method($($arg.process()),*,) {
|
||||
Ok(result) => result,
|
||||
Err(err) => {
|
||||
eprintln!("execution error: {err}");
|
||||
return false
|
||||
},
|
||||
Ok(verified) => verified,
|
||||
Err(_) => return false,
|
||||
} {
|
||||
unsafe { *$bool_arg = true };
|
||||
} else {
|
||||
@@ -194,15 +171,11 @@ impl<'a> From<&Buffer> for &'a [u8] {
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn new(tree_height: usize, input_buffer: *const Buffer, ctx: *mut *mut RLN) -> bool {
|
||||
match RLN::new(tree_height, input_buffer.process()) {
|
||||
Ok(rln) => {
|
||||
unsafe { *ctx = Box::into_raw(Box::new(rln)) };
|
||||
true
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("could not instantiate rln: {err}");
|
||||
false
|
||||
}
|
||||
if let Ok(rln) = RLN::new(tree_height, input_buffer.process()) {
|
||||
unsafe { *ctx = Box::into_raw(Box::new(rln)) };
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,24 +186,18 @@ pub extern "C" fn new_with_params(
|
||||
circom_buffer: *const Buffer,
|
||||
zkey_buffer: *const Buffer,
|
||||
vk_buffer: *const Buffer,
|
||||
tree_config: *const Buffer,
|
||||
ctx: *mut *mut RLN,
|
||||
) -> bool {
|
||||
match RLN::new_with_params(
|
||||
if let Ok(rln) = RLN::new_with_params(
|
||||
tree_height,
|
||||
circom_buffer.process().to_vec(),
|
||||
zkey_buffer.process().to_vec(),
|
||||
vk_buffer.process().to_vec(),
|
||||
tree_config.process(),
|
||||
) {
|
||||
Ok(rln) => {
|
||||
unsafe { *ctx = Box::into_raw(Box::new(rln)) };
|
||||
true
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("could not instantiate rln: {err}");
|
||||
false
|
||||
}
|
||||
unsafe { *ctx = Box::into_raw(Box::new(rln)) };
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,12 +228,6 @@ pub extern "C" fn get_leaf(ctx: *mut RLN, index: usize, output_buffer: *mut Buff
|
||||
call_with_output_arg!(ctx, get_leaf, output_buffer, index)
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn leaves_set(ctx: *mut RLN) -> usize {
|
||||
ctx.process().leaves_set()
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn set_next_leaf(ctx: *mut RLN, input_buffer: *const Buffer) -> bool {
|
||||
@@ -300,22 +261,6 @@ pub extern "C" fn atomic_operation(
|
||||
call!(ctx, atomic_operation, index, leaves_buffer, indices_buffer)
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn seq_atomic_operation(
|
||||
ctx: *mut RLN,
|
||||
leaves_buffer: *const Buffer,
|
||||
indices_buffer: *const Buffer,
|
||||
) -> bool {
|
||||
call!(
|
||||
ctx,
|
||||
atomic_operation,
|
||||
ctx.process().leaves_set(),
|
||||
leaves_buffer,
|
||||
indices_buffer
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn get_root(ctx: *const RLN, output_buffer: *mut Buffer) -> bool {
|
||||
@@ -361,21 +306,6 @@ pub extern "C" fn generate_rln_proof(
|
||||
call_with_output_arg!(ctx, generate_rln_proof, output_buffer, input_buffer)
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn generate_rln_proof_with_witness(
|
||||
ctx: *mut RLN,
|
||||
input_buffer: *const Buffer,
|
||||
output_buffer: *mut Buffer,
|
||||
) -> bool {
|
||||
call_with_output_arg!(
|
||||
ctx,
|
||||
generate_rln_proof_with_witness,
|
||||
output_buffer,
|
||||
input_buffer
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::not_unsafe_ptr_arg_deref)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn verify_rln_proof(
|
||||
|
||||
@@ -197,13 +197,11 @@ impl ZerokitMerkleTree for PmTree {
|
||||
let mut indices = indices.into_iter().collect::<Vec<_>>();
|
||||
indices.sort();
|
||||
|
||||
match (leaves.len(), indices.len()) {
|
||||
(0, 0) => Err(Report::msg("no leaves or indices to be removed")),
|
||||
(1, 0) => self.set(start, leaves[0]),
|
||||
(0, 1) => self.delete(indices[0]),
|
||||
(_, 0) => self.set_range_with_leaves(start, leaves),
|
||||
(0, _) => self.remove_indices(&indices),
|
||||
(_, _) => self.remove_indices_and_set_leaves(start, leaves, &indices),
|
||||
match (leaves.is_empty(), indices.is_empty()) {
|
||||
(true, true) => Err(Report::msg("no leaves or indices to be removed")),
|
||||
(false, true) => self.set_range_with_leaves(start, leaves),
|
||||
(true, false) => self.remove_indices(indices),
|
||||
(false, false) => self.remove_indices_and_set_leaves(start, leaves, indices),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -246,8 +244,7 @@ impl ZerokitMerkleTree for PmTree {
|
||||
let data = self.tree.db.get(METADATA_KEY)?;
|
||||
|
||||
if data.is_none() {
|
||||
// send empty Metadata
|
||||
return Ok(Vec::new());
|
||||
return Err(Report::msg("metadata does not exist"));
|
||||
}
|
||||
Ok(data.unwrap())
|
||||
}
|
||||
@@ -267,11 +264,18 @@ impl PmTree {
|
||||
.map_err(|e| Report::msg(e.to_string()))
|
||||
}
|
||||
|
||||
fn remove_indices(&mut self, indices: &[usize]) -> Result<()> {
|
||||
fn remove_indices(&mut self, indices: Vec<usize>) -> Result<()> {
|
||||
let start = indices[0];
|
||||
let end = indices.last().unwrap() + 1;
|
||||
|
||||
let new_leaves = (start..end).map(|_| PmTreeHasher::default_leaf());
|
||||
let mut new_leaves: Vec<_> = (start..end)
|
||||
.map(|i| self.tree.get(i))
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
new_leaves
|
||||
.iter_mut()
|
||||
.take(indices.len())
|
||||
.for_each(|leaf| *leaf = PmTreeHasher::default_leaf());
|
||||
|
||||
self.tree
|
||||
.set_range(start, new_leaves)
|
||||
@@ -282,13 +286,16 @@ impl PmTree {
|
||||
&mut self,
|
||||
start: usize,
|
||||
leaves: Vec<FrOfPmTreeHasher>,
|
||||
indices: &[usize],
|
||||
indices: Vec<usize>,
|
||||
) -> Result<()> {
|
||||
let min_index = *indices.first().unwrap();
|
||||
let max_index = start + leaves.len();
|
||||
|
||||
// Generated a placeholder with the exact size needed,
|
||||
// Initiated with default values to be overridden throughout the method
|
||||
let mut set_values = vec![PmTreeHasher::default_leaf(); max_index - min_index];
|
||||
|
||||
// If the index is not in indices list, keep the original value
|
||||
for i in min_index..start {
|
||||
if !indices.contains(&i) {
|
||||
let value = self.tree.get(i)?;
|
||||
@@ -296,6 +303,7 @@ impl PmTree {
|
||||
}
|
||||
}
|
||||
|
||||
// Insert new leaves after 'start' position
|
||||
for (i, &leaf) in leaves.iter().enumerate() {
|
||||
set_values[start - min_index + i] = leaf;
|
||||
}
|
||||
|
||||
@@ -117,7 +117,6 @@ impl RLN<'_> {
|
||||
/// - `circom_vec`: a byte vector containing the ZK circuit (`rln.wasm`) as binary file
|
||||
/// - `zkey_vec`: a byte vector containing to the proving key (`rln_final.zkey`) as binary file
|
||||
/// - `vk_vec`: a byte vector containing to the verification key (`verification_key.json`) as binary file
|
||||
/// - `tree_config`: a reader for a string containing a json with the merkle tree configuration
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
@@ -135,8 +134,6 @@ impl RLN<'_> {
|
||||
/// let mut buffer = vec![0; metadata.len() as usize];
|
||||
/// file.read_exact(&mut buffer).expect("buffer overflow");
|
||||
/// resources.push(buffer);
|
||||
/// let tree_config = "{}".to_string();
|
||||
/// let tree_config_buffer = &Buffer::from(tree_config.as_bytes());
|
||||
/// }
|
||||
///
|
||||
/// let mut rln = RLN::new_with_params(
|
||||
@@ -144,51 +141,11 @@ impl RLN<'_> {
|
||||
/// resources[0].clone(),
|
||||
/// resources[1].clone(),
|
||||
/// resources[2].clone(),
|
||||
/// tree_config_buffer,
|
||||
/// );
|
||||
/// ```
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn new_with_params<R: Read>(
|
||||
tree_height: usize,
|
||||
circom_vec: Vec<u8>,
|
||||
zkey_vec: Vec<u8>,
|
||||
vk_vec: Vec<u8>,
|
||||
mut tree_config_input: R,
|
||||
) -> Result<RLN<'static>> {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let witness_calculator = circom_from_raw(circom_vec)?;
|
||||
|
||||
let proving_key = zkey_from_raw(&zkey_vec)?;
|
||||
let verification_key = vk_from_raw(&vk_vec, &zkey_vec)?;
|
||||
|
||||
let mut tree_config_vec: Vec<u8> = Vec::new();
|
||||
tree_config_input.read_to_end(&mut tree_config_vec)?;
|
||||
let tree_config_str = String::from_utf8(tree_config_vec)?;
|
||||
let tree_config: <PoseidonTree as ZerokitMerkleTree>::Config = if tree_config_str.is_empty()
|
||||
{
|
||||
<PoseidonTree as ZerokitMerkleTree>::Config::default()
|
||||
} else {
|
||||
<PoseidonTree as ZerokitMerkleTree>::Config::from_str(&tree_config_str)?
|
||||
};
|
||||
|
||||
// We compute a default empty tree
|
||||
let tree = PoseidonTree::new(
|
||||
tree_height,
|
||||
<PoseidonTree as ZerokitMerkleTree>::Hasher::default_leaf(),
|
||||
tree_config,
|
||||
)?;
|
||||
|
||||
Ok(RLN {
|
||||
witness_calculator,
|
||||
proving_key,
|
||||
verification_key,
|
||||
tree,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
pub fn new_with_params(
|
||||
tree_height: usize,
|
||||
#[cfg(not(target_arch = "wasm32"))] circom_vec: Vec<u8>,
|
||||
zkey_vec: Vec<u8>,
|
||||
vk_vec: Vec<u8>,
|
||||
) -> Result<RLN<'static>> {
|
||||
@@ -202,9 +159,12 @@ impl RLN<'_> {
|
||||
let tree = PoseidonTree::default(tree_height)?;
|
||||
|
||||
Ok(RLN {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
witness_calculator,
|
||||
proving_key,
|
||||
verification_key,
|
||||
tree,
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
_marker: PhantomData,
|
||||
})
|
||||
}
|
||||
@@ -406,10 +366,6 @@ impl RLN<'_> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn leaves_set(&mut self) -> usize {
|
||||
self.tree.leaves_set()
|
||||
}
|
||||
|
||||
/// Sets a leaf value at the next available never-set leaf index.
|
||||
///
|
||||
/// This function updates the internal Merkle tree `next_index` value indicating the next available index corresponding to a never-set leaf as `next_index = next_index + 1`.
|
||||
@@ -720,9 +676,9 @@ impl RLN<'_> {
|
||||
mut output_data: W,
|
||||
) -> Result<()> {
|
||||
// We read input RLN witness and we serialize_compressed it
|
||||
let mut input_byte: Vec<u8> = Vec::new();
|
||||
input_data.read_to_end(&mut input_byte)?;
|
||||
let (rln_witness, _) = proof_inputs_to_rln_witness(&mut self.tree, &input_byte)?;
|
||||
let mut witness_byte: Vec<u8> = Vec::new();
|
||||
input_data.read_to_end(&mut witness_byte)?;
|
||||
let (rln_witness, _) = proof_inputs_to_rln_witness(&mut self.tree, &witness_byte)?;
|
||||
let proof_values = proof_values_from_witness(&rln_witness);
|
||||
|
||||
let proof = generate_proof(self.witness_calculator, &self.proving_key, &rln_witness)?;
|
||||
@@ -735,33 +691,12 @@ impl RLN<'_> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: this function seems to use redundant witness (as bigint and serialized) and should be refactored
|
||||
// Generate RLN Proof using a witness calculated from outside zerokit
|
||||
//
|
||||
// output_data is [ proof<128> | root<32> | epoch<32> | share_x<32> | share_y<32> | nullifier<32> | rln_identifier<32> ]
|
||||
// we skip it from documentation for now
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub fn generate_rln_proof_with_witness<R: Read, W: Write>(
|
||||
&mut self,
|
||||
mut input_data: R,
|
||||
mut output_data: W,
|
||||
) -> Result<()> {
|
||||
let mut witness_byte: Vec<u8> = Vec::new();
|
||||
input_data.read_to_end(&mut witness_byte)?;
|
||||
let (rln_witness, _) = deserialize_witness(&witness_byte)?;
|
||||
let proof_values = proof_values_from_witness(&rln_witness);
|
||||
|
||||
let proof = generate_proof(self.witness_calculator, &self.proving_key, &rln_witness)?;
|
||||
|
||||
// Note: we export a serialization of ark-groth16::Proof not semaphore::Proof
|
||||
// This proof is compressed, i.e. 128 bytes long
|
||||
proof.serialize_compressed(&mut output_data)?;
|
||||
output_data.write_all(&serialize_proof_values(&proof_values))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Different in wasm since witness_calculator is not available
|
||||
// See: https://github.com/vacp2p/zerokit/blob/b903d8d740e0b8b82057bcc5377ddce05ae5676b/rln/src/public.rs#L47-L49
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[doc(hidden)]
|
||||
pub fn generate_rln_proof_with_witness<W: Write>(
|
||||
&mut self,
|
||||
calculated_witness: Vec<BigInt>,
|
||||
@@ -1821,10 +1756,13 @@ mod test {
|
||||
.collect();
|
||||
|
||||
// Generating the proof
|
||||
let mut input_buffer = Cursor::new(serialized_witness);
|
||||
let mut output_buffer = Cursor::new(Vec::<u8>::new());
|
||||
rln.generate_rln_proof_with_witness(&mut input_buffer, &mut output_buffer)
|
||||
.unwrap();
|
||||
rln.generate_rln_proof_with_witness(
|
||||
calculated_witness_vec,
|
||||
serialized_witness,
|
||||
&mut output_buffer,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// output_data is [ proof<128> | share_y<32> | nullifier<32> | root<32> | epoch<32> | share_x<32> | rln_identifier<32> ]
|
||||
let mut proof_data = output_buffer.into_inner();
|
||||
@@ -2108,19 +2046,4 @@ mod test {
|
||||
|
||||
assert_eq!(arbitrary_metadata, received_metadata);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_metadata() {
|
||||
let tree_height = TEST_TREE_HEIGHT;
|
||||
|
||||
let input_buffer =
|
||||
Cursor::new(json!({ "resources_folder": TEST_RESOURCES_FOLDER }).to_string());
|
||||
let rln = RLN::new(tree_height, input_buffer).unwrap();
|
||||
|
||||
let mut buffer = Cursor::new(Vec::<u8>::new());
|
||||
rln.get_metadata(&mut buffer).unwrap();
|
||||
let received_metadata = buffer.into_inner();
|
||||
|
||||
assert_eq!(received_metadata.len(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,8 +143,6 @@ mod test {
|
||||
assert!(success, "RLN object creation failed");
|
||||
let rln_pointer = unsafe { &mut *rln_pointer.assume_init() };
|
||||
|
||||
assert_eq!(rln_pointer.leaves_set(), 0);
|
||||
|
||||
// We generate a vector of random leaves
|
||||
let mut leaves: Vec<Fr> = Vec::new();
|
||||
let mut rng = thread_rng();
|
||||
@@ -161,7 +159,6 @@ mod test {
|
||||
let input_buffer = &Buffer::from(leaves_ser.as_ref());
|
||||
let success = init_tree_with_leaves(rln_pointer, input_buffer);
|
||||
assert!(success, "init tree with leaves call failed");
|
||||
assert_eq!(rln_pointer.leaves_set(), no_of_leaves);
|
||||
|
||||
// We get the root of the tree obtained adding leaves in batch
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
@@ -620,14 +617,11 @@ mod test {
|
||||
|
||||
// Creating a RLN instance passing the raw data
|
||||
let mut rln_pointer_raw_bytes = MaybeUninit::<*mut RLN>::uninit();
|
||||
let tree_config = "".to_string();
|
||||
let tree_config_buffer = &Buffer::from(tree_config.as_bytes());
|
||||
let success = new_with_params(
|
||||
tree_height,
|
||||
circom_data,
|
||||
zkey_data,
|
||||
vk_data,
|
||||
tree_config_buffer,
|
||||
rln_pointer_raw_bytes.as_mut_ptr(),
|
||||
);
|
||||
assert!(success, "RLN object creation failed");
|
||||
@@ -1209,7 +1203,7 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_valid_metadata() {
|
||||
fn test_metadata() {
|
||||
// We create a RLN instance
|
||||
let tree_height = TEST_TREE_HEIGHT;
|
||||
|
||||
@@ -1235,25 +1229,4 @@ mod test {
|
||||
|
||||
assert_eq!(result_data, seed_bytes.to_vec());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_metadata() {
|
||||
// We create a RLN instance
|
||||
let tree_height = TEST_TREE_HEIGHT;
|
||||
|
||||
let mut rln_pointer = MaybeUninit::<*mut RLN>::uninit();
|
||||
let input_config = json!({ "resources_folder": TEST_RESOURCES_FOLDER }).to_string();
|
||||
let input_buffer = &Buffer::from(input_config.as_bytes());
|
||||
let success = new(tree_height, input_buffer, rln_pointer.as_mut_ptr());
|
||||
assert!(success, "RLN object creation failed");
|
||||
let rln_pointer = unsafe { &mut *rln_pointer.assume_init() };
|
||||
|
||||
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
|
||||
let success = get_metadata(rln_pointer, output_buffer.as_mut_ptr());
|
||||
assert!(success, "get_metadata call failed");
|
||||
|
||||
let output_buffer = unsafe { output_buffer.assume_init() };
|
||||
|
||||
assert_eq!(output_buffer.len, 0);
|
||||
}
|
||||
}
|
||||
|
||||
50
semaphore/Cargo.toml
Normal file
50
semaphore/Cargo.toml
Normal file
@@ -0,0 +1,50 @@
|
||||
[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.1"
|
||||
once_cell = "1.8"
|
||||
rand = "0.8.4"
|
||||
semaphore = { git = "https://github.com/worldcoin/semaphore-rs", rev = "ee658c2"}
|
||||
ethers-core = { version = "2.0.8", default-features = false }
|
||||
ruint = { version = "1.2.0", features = [ "serde", "num-bigint", "ark-ff" ] }
|
||||
serde = "1.0"
|
||||
thiserror = "1.0.0"
|
||||
wasmer = { version = "2.0" }
|
||||
|
||||
[dev-dependencies]
|
||||
rand_chacha = "0.3.1"
|
||||
serde_json = "1.0.79"
|
||||
|
||||
[build-dependencies]
|
||||
color-eyre = "0.6.1"
|
||||
wasmer = { version = "2.0" }
|
||||
wasmer-engine-dylib = { version = "2.2.1", optional = true }
|
||||
wasmer-compiler-cranelift = { version = "3.1.1", 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
|
||||
7
semaphore/Makefile.toml
Normal file
7
semaphore/Makefile.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[tasks.build]
|
||||
command = "cargo"
|
||||
args = ["build", "--release"]
|
||||
|
||||
[tasks.test]
|
||||
command = "cargo"
|
||||
args = ["test", "--release"]
|
||||
18
semaphore/README.md
Normal file
18
semaphore/README.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# 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
|
||||
```
|
||||
111
semaphore/build.rs
Normal file
111
semaphore/build.rs
Normal file
@@ -0,0 +1,111 @@
|
||||
// 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(())
|
||||
}
|
||||
79
semaphore/src/circuit.rs
Normal file
79
semaphore/src/circuit.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
// 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)
|
||||
})
|
||||
}
|
||||
7
semaphore/src/lib.rs
Normal file
7
semaphore/src/lib.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
#![allow(clippy::multiple_crate_versions)]
|
||||
|
||||
pub mod circuit;
|
||||
pub mod protocol;
|
||||
|
||||
#[cfg(feature = "dylib")]
|
||||
pub use circuit::initialize;
|
||||
215
semaphore/src/protocol.rs
Normal file
215
semaphore/src/protocol.rs
Normal file
@@ -0,0 +1,215 @@
|
||||
// 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)
|
||||
}
|
||||
115
semaphore/tests/protocol.rs
Normal file
115
semaphore/tests/protocol.rs
Normal file
@@ -0,0 +1,115 @@
|
||||
#[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
semaphore/vendor/semaphore
vendored
Submodule
1
semaphore/vendor/semaphore
vendored
Submodule
Submodule semaphore/vendor/semaphore added at 5186a940ff
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "zerokit_utils"
|
||||
version = "0.3.3"
|
||||
version = "0.3.1"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
description = "Various utilities for Zerokit"
|
||||
@@ -13,11 +13,9 @@ bench = false
|
||||
|
||||
[dependencies]
|
||||
ark-ff = { version = "=0.4.1", default-features = false, features = ["asm"] }
|
||||
num-bigint = { version = "=0.4.3", default-features = false, features = [
|
||||
"rand",
|
||||
] }
|
||||
num-bigint = { version = "=0.4.3", default-features = false, features = ["rand"] }
|
||||
color-eyre = "=0.6.2"
|
||||
pmtree = { package = "pmtree", version = "=2.0.0", optional = true }
|
||||
pmtree = { package = "vacp2p_pmtree", version = "=1.0.0", optional = true}
|
||||
sled = "=0.34.7"
|
||||
serde = "=1.0.163"
|
||||
|
||||
|
||||
@@ -2,51 +2,29 @@ use pmtree::*;
|
||||
|
||||
use sled::Db as Sled;
|
||||
use std::collections::HashMap;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
pub struct SledDB(Sled);
|
||||
|
||||
impl SledDB {
|
||||
fn new_with_tries(config: <SledDB as Database>::Config, tries: u32) -> PmtreeResult<Self> {
|
||||
// If we've tried more than 10 times, we give up and return an error.
|
||||
if tries >= 10 {
|
||||
return Err(PmtreeErrorKind::DatabaseError(
|
||||
DatabaseErrorKind::CustomError(format!(
|
||||
"Cannot create database: exceeded maximum retry attempts. {config:#?}"
|
||||
)),
|
||||
));
|
||||
}
|
||||
match config.open() {
|
||||
Ok(db) => Ok(SledDB(db)),
|
||||
Err(e) if e.to_string().contains("WouldBlock") => {
|
||||
// try till the fd is freed
|
||||
// sleep for 10^tries milliseconds, then recursively try again
|
||||
thread::sleep(Duration::from_millis(10u64.pow(tries)));
|
||||
Self::new_with_tries(config, tries + 1)
|
||||
}
|
||||
Err(e) => {
|
||||
// On any other error, we return immediately.
|
||||
Err(PmtreeErrorKind::DatabaseError(
|
||||
DatabaseErrorKind::CustomError(format!(
|
||||
"Cannot create database: {e} {config:#?}"
|
||||
)),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Database for SledDB {
|
||||
type Config = sled::Config;
|
||||
|
||||
fn new(config: Self::Config) -> PmtreeResult<Self> {
|
||||
let db = Self::new_with_tries(config, 0)?;
|
||||
Ok(db)
|
||||
let db: Sled = match config.open() {
|
||||
Ok(db) => db,
|
||||
Err(e) => {
|
||||
return Err(PmtreeErrorKind::DatabaseError(
|
||||
DatabaseErrorKind::CustomError(format!(
|
||||
"Cannot create database: {e} {config:#?}",
|
||||
)),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(SledDB(db))
|
||||
}
|
||||
|
||||
fn load(config: Self::Config) -> PmtreeResult<Self> {
|
||||
let db = match config.open() {
|
||||
let db: Sled = match sled::open(&config.path) {
|
||||
Ok(db) => db,
|
||||
Err(e) => {
|
||||
return Err(PmtreeErrorKind::DatabaseError(
|
||||
|
||||
Reference in New Issue
Block a user