snark file config and ffi lib

This commit is contained in:
psippl 2022-02-08 15:57:21 -10:00
parent cc09d79f51
commit 0737ac5976
8 changed files with 356 additions and 45 deletions

50
Cargo.lock generated
View File

@ -2155,31 +2155,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "libsemaphore-rs"
version = "0.1.0"
dependencies = [
"ark-bn254",
"ark-circom",
"ark-ec",
"ark-ff",
"ark-groth16",
"ark-relations",
"ark-std",
"color-eyre",
"ethers",
"ff_ce",
"hex",
"hex-literal",
"num-bigint 0.4.3",
"once_cell",
"poseidon-rs",
"proptest",
"rayon",
"serde",
"sha2 0.10.1",
]
[[package]]
name = "lock_api"
version = "0.4.6"
@ -3340,6 +3315,31 @@ dependencies = [
"libc",
]
[[package]]
name = "semaphore"
version = "0.1.0"
dependencies = [
"ark-bn254",
"ark-circom",
"ark-ec",
"ark-ff",
"ark-groth16",
"ark-relations",
"ark-std",
"color-eyre",
"ethers",
"ff_ce",
"hex",
"hex-literal",
"num-bigint 0.4.3",
"once_cell",
"poseidon-rs",
"proptest",
"rayon",
"serde",
"sha2 0.10.1",
]
[[package]]
name = "semver"
version = "0.11.0"

View File

@ -5,8 +5,6 @@ edition = "2021"
authors = [
"Remco Bloemen <remco@worldcoin.org>",
"Philipp Sippl <philipp@worldcoin.org>"]
edition = "2021"
build = "build.rs"
homepage = "https://github.com/worldcoin/semaphore-rs"
repository = "https://github.com/worldcoin/semaphore-rs"
description = "Rust support library for Semaphore"

8
cbindgen.toml Normal file
View File

@ -0,0 +1,8 @@
language = "C"
autogen_warning = "// NOTE: Append the lines below to ios/Classes/GreeterPlugin.h"
#namespace = "ffi"
#include_guard = "CBINDGEN_BINDINGS_H"
[defines]
"target_os = ios" = "TARGET_OS_IOS"
"target_os = macos" = "TARGET_OS_MACOS"

73
libsemaphore.h Normal file
View File

@ -0,0 +1,73 @@
// NOTE: Append the lines below to ios/Classes/GreeterPlugin.h
typedef struct Identity Identity;
/**
* Merkle tree with all leaf and intermediate hashes stored
*/
typedef struct MerkleTree_PoseidonHash MerkleTree_PoseidonHash;
/**
* Merkle proof path, bottom to top.
*/
typedef struct Proof_Bn_Parameters Proof_Bn_Parameters;
/**
* Merkle proof path, bottom to top.
*/
typedef struct Proof_PoseidonHash Proof_PoseidonHash;
typedef struct MerkleTree_PoseidonHash PoseidonTree;
/**
* Creates a new idenity and returns the object
*/
struct Identity *new_identity(const char *seed);
/**
* Generates the identity commitment based on seed for identity
*/
char *generate_identity_commitment(struct Identity *identity);
/**
* Generates nullifier hash based on identity and external nullifier
*/
char *generate_nullifier_hash(struct Identity *identity, const char *external_nullifier);
/**
* Generates nullifier hash based on identity and external nullifier
*/
PoseidonTree *create_poseidon_tree(int depth);
/**
* Generates nullifier hash based on identity and external nullifier
*/
void insert_leaf(PoseidonTree *tree, struct Identity *identity);
/**
* Generates nullifier hash based on identity and external nullifier
*/
char *get_root(PoseidonTree *tree);
/**
* Generates nullifier hash based on identity and external nullifier
*/
struct Proof_PoseidonHash *get_merkle_proof(PoseidonTree *tree, int leaf_idx);
/**
* Generates nullifier hash based on identity and external nullifier
*/
struct Proof_Bn_Parameters *generate_proof(struct Identity *identity,
const char *external_nullifier,
const char *signal,
struct Proof_PoseidonHash *merkle_proof,
const char *zkey_path,
const char *wasm_path);
int verify_proof(const char *root,
const char *external_nullifier,
const char *signal,
const char *nullifier,
struct Proof_Bn_Parameters *proof,
const char *zkey_path,
const char *wasm_path);

View File

@ -5,13 +5,24 @@ mod poseidon_tree;
mod protocol;
mod util;
use ark_bn254::Parameters;
use ark_ec::bn::Bn;
use ark_groth16::Proof;
use hex_literal::hex;
use num_bigint::{BigInt};
use poseidon_tree::PoseidonHash;
use protocol::SnarkFileConfig;
use std::{
ffi::{CStr, CString},
os::raw::c_char,
os::raw::{c_char, c_int},
};
use crate::{hash::Hash, poseidon_tree::PoseidonTree};
/// Creates a new idenity and returns the object
#[no_mangle]
pub extern "C" fn generate_identity_commitment(seed: *const c_char) -> *mut c_char {
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn new_identity(seed: *const c_char) -> *mut identity::Identity {
let c_str = unsafe { CStr::from_ptr(seed) };
let seed = match c_str.to_str() {
Err(_) => "there",
@ -19,7 +30,222 @@ pub extern "C" fn generate_identity_commitment(seed: *const c_char) -> *mut c_ch
};
let id = identity::Identity::new(seed.as_bytes());
CString::new(id.commitment().to_str_radix(10))
let boxed: Box<identity::Identity> = Box::new(id);
Box::into_raw(boxed)
}
/// Generates the identity commitment based on seed for identity
#[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn generate_identity_commitment(
identity: *mut identity::Identity,
) -> *mut c_char {
let identity = &*identity;
CString::new(identity.commitment().to_str_radix(10))
.unwrap()
.into_raw()
}
/// Generates nullifier hash based on identity and external nullifier
#[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn generate_nullifier_hash(
identity: *mut identity::Identity,
external_nullifier: *const c_char,
) -> *mut c_char {
let identity = &*identity;
let c_str = unsafe { CStr::from_ptr(external_nullifier) };
let external_nullifier = match c_str.to_str() {
Err(_) => "there",
Ok(string) => string,
};
CString::new(
protocol::generate_nullifier_hash(identity, external_nullifier.as_bytes()).to_str_radix(10),
)
.unwrap()
.into_raw()
}
/// Generates nullifier hash based on identity and external nullifier
#[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn create_poseidon_tree(depth: c_int) -> *mut PoseidonTree {
const LEAF: Hash = Hash::from_bytes_be(hex!(
"0000000000000000000000000000000000000000000000000000000000000000"
));
let tree = PoseidonTree::new(depth as usize, LEAF);
let boxed: Box<PoseidonTree> = Box::new(tree);
Box::into_raw(boxed)
}
/// Generates nullifier hash based on identity and external nullifier
#[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn insert_leaf(tree: *mut PoseidonTree, identity: *mut identity::Identity) {
let identity = &*identity;
let tree = unsafe {
assert!(!tree.is_null());
&mut *tree
};
let (_, leaf) = identity.commitment().to_bytes_be();
tree.set(0, leaf.into());
}
/// Generates nullifier hash based on identity and external nullifier
#[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn get_root(tree: *mut PoseidonTree) -> *mut c_char {
let tree = unsafe {
assert!(!tree.is_null());
&mut *tree
};
let root: BigInt = tree.root().into();
CString::new(root.to_str_radix(10)).unwrap().into_raw()
}
/// Generates nullifier hash based on identity and external nullifier
#[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn get_merkle_proof(
tree: *mut PoseidonTree,
leaf_idx: c_int,
) -> *mut merkle_tree::Proof<PoseidonHash> {
let tree = unsafe {
assert!(!tree.is_null());
&mut *tree
};
let proof = tree.proof(leaf_idx as usize).expect("proof should exist");
let boxed: Box<merkle_tree::Proof<PoseidonHash>> = Box::new(proof);
Box::into_raw(boxed)
}
/// Generates nullifier hash based on identity and external nullifier
#[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn generate_proof(
identity: *mut identity::Identity,
external_nullifier: *const c_char,
signal: *const c_char,
merkle_proof: *mut merkle_tree::Proof<PoseidonHash>,
zkey_path: *const c_char,
wasm_path: *const c_char,
) -> *mut Proof<Bn<Parameters>> {
let c_str = unsafe { CStr::from_ptr(external_nullifier) };
let external_nullifier = match c_str.to_str() {
Err(_) => "there",
Ok(string) => string,
};
let c_str = unsafe { CStr::from_ptr(signal) };
let signal = match c_str.to_str() {
Err(_) => "there",
Ok(string) => string,
};
let c_str = unsafe { CStr::from_ptr(zkey_path) };
let zkey_path = match c_str.to_str() {
Err(_) => "there",
Ok(string) => string,
};
let c_str = unsafe { CStr::from_ptr(wasm_path) };
let wasm_path = match c_str.to_str() {
Err(_) => "there",
Ok(string) => string,
};
let config = SnarkFileConfig {
zkey: zkey_path.to_string(),
wasm: wasm_path.to_string(),
};
let identity = &*identity;
let merkle_proof = &*merkle_proof;
let res = protocol::generate_proof(
&config,
identity,
merkle_proof,
external_nullifier.as_bytes(),
signal.as_bytes(),
);
let boxed: Box<Proof<Bn<Parameters>>> = Box::new(res.unwrap());
Box::into_raw(boxed)
}
#[no_mangle]
#[allow(clippy::missing_safety_doc)]
pub unsafe extern "C" fn verify_proof(
root: *const c_char,
external_nullifier: *const c_char,
signal: *const c_char,
nullifier: *const c_char,
proof: *mut Proof<Bn<Parameters>>,
zkey_path: *const c_char,
wasm_path: *const c_char,
) -> c_int {
let c_str = unsafe { CStr::from_ptr(root) };
let root = match c_str.to_str() {
Err(_) => "there",
Ok(string) => string,
};
let c_str = unsafe { CStr::from_ptr(external_nullifier) };
let external_nullifier = match c_str.to_str() {
Err(_) => "there",
Ok(string) => string,
};
let c_str = unsafe { CStr::from_ptr(signal) };
let signal = match c_str.to_str() {
Err(_) => "there",
Ok(string) => string,
};
let c_str = unsafe { CStr::from_ptr(nullifier) };
let nullifier = match c_str.to_str() {
Err(_) => "there",
Ok(string) => string,
};
let c_str = unsafe { CStr::from_ptr(zkey_path) };
let zkey_path = match c_str.to_str() {
Err(_) => "there",
Ok(string) => string,
};
let c_str = unsafe { CStr::from_ptr(wasm_path) };
let wasm_path = match c_str.to_str() {
Err(_) => "there",
Ok(string) => string,
};
let config = SnarkFileConfig {
zkey: zkey_path.to_string(),
wasm: wasm_path.to_string(),
};
let proof = &*proof;
let root = BigInt::parse_bytes(root.as_bytes(), 10).unwrap();
let nullifier = BigInt::parse_bytes(nullifier.as_bytes(), 10).unwrap();
protocol::verify_proof(
&config,
&root,
&nullifier,
signal.as_bytes(),
external_nullifier.as_bytes(),
proof,
)
.unwrap() as i32
}

View File

@ -1,3 +0,0 @@
#include <stdint.h>
const char* generate_identity_commitment(const char* to);

View File

@ -8,7 +8,7 @@ mod util;
use hash::*;
use hex_literal::hex;
use identity::*;
use num_bigint::BigInt;
use num_bigint::{BigInt, Sign};
use poseidon_rs::Poseidon;
use poseidon_tree::*;
use protocol::*;
@ -38,12 +38,17 @@ fn main() {
let signal = "xxx".as_bytes();
let external_nullifier = "appId".as_bytes();
let nullifier_hash = generate_nullifier_hash(&external_nullifier, &id.nullifier);
let nullifier_hash = generate_nullifier_hash(&id, external_nullifier);
dbg!(&nullifier_hash);
let proof = generate_proof(&id, &merkle_proof, &external_nullifier, &signal).unwrap();
let config = SnarkFileConfig {
zkey: "./snarkfiles/semaphore.zkey".to_string(),
wasm: "./snarkfiles/semaphore.wasm".to_string(),
};
let proof = generate_proof(&config, &id, &merkle_proof, external_nullifier, signal).unwrap();
let success =
verify_proof(&root, &nullifier_hash, &signal, &external_nullifier, &proof).unwrap();
verify_proof(&config, &root, &nullifier_hash, signal, external_nullifier, &proof).unwrap();
dbg!(success);
}

View File

@ -19,11 +19,13 @@ use crate::{
util::{bigint_to_fr, fr_to_bigint},
};
static SNARK_FILES: &str = "./snarkfiles/";
static ZKEY_FILE: &str = "semaphore.zkey";
static WASM_FILE: &str = "semaphore.wasm";
static POSEIDON: Lazy<Poseidon> = Lazy::new(Poseidon::new);
pub struct SnarkFileConfig {
pub zkey: String,
pub wasm: String,
}
/// 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<BigInt> {
@ -50,11 +52,11 @@ pub fn hash_external_nullifier(nullifier: &[u8]) -> BigInt {
}
/// Generates the nullifier hash
pub fn generate_nullifier_hash(external_nullifier: &[u8], identity_nullifier: &BigInt) -> BigInt {
pub fn generate_nullifier_hash(identity: &Identity, external_nullifier: &[u8]) -> BigInt {
let res = POSEIDON
.hash(vec![
bigint_to_fr(&hash_external_nullifier(external_nullifier)),
bigint_to_fr(identity_nullifier),
bigint_to_fr(&identity.nullifier),
])
.unwrap();
fr_to_bigint(res)
@ -62,12 +64,13 @@ pub fn generate_nullifier_hash(external_nullifier: &[u8], identity_nullifier: &B
/// Generates a semaphore proof
pub fn generate_proof(
config: &SnarkFileConfig,
identity: &Identity,
merkle_proof: &merkle_tree::Proof<PoseidonHash>,
external_nullifier: &[u8],
signal: &[u8],
) -> Result<Proof<Bn<Parameters>>, SynthesisError> {
let mut file = File::open(format!("{}{}", SNARK_FILES, ZKEY_FILE)).unwrap();
let mut file = File::open(&config.zkey).unwrap();
let (params, matrices) = read_zkey(&mut file).unwrap();
let num_inputs = matrices.num_instance_variables;
let num_constraints = matrices.num_constraints;
@ -97,7 +100,7 @@ pub fn generate_proof(
use std::time::Instant;
let now = Instant::now();
let mut wtns = WitnessCalculator::new(format!("{}{}", SNARK_FILES, WASM_FILE)).unwrap();
let mut wtns = WitnessCalculator::new(&config.wasm).unwrap();
let full_assignment = wtns
.calculate_witness_element::<Bn254, _>(inputs, false)
@ -131,13 +134,14 @@ pub fn generate_proof(
/// Verifies a given semaphore proof
pub fn verify_proof(
config: &SnarkFileConfig,
root: &BigInt,
nullifier_hash: &BigInt,
signal: &[u8],
external_nullifier: &[u8],
proof: &Proof<Bn<Parameters>>,
) -> Result<bool, SynthesisError> {
let mut file = File::open(format!("{}{}", SNARK_FILES, ZKEY_FILE)).unwrap();
let mut file = File::open(&config.zkey).unwrap();
let (params, _) = read_zkey(&mut file).unwrap();
let pvk = prepare_verifying_key(&params.vk);