Skip to content

Commit

Permalink
feat(RLN): add new_with_params (#36)
Browse files Browse the repository at this point in the history
Allows passing the wasm, zkey and verification key data as buffers, instead of using a path to a folder
  • Loading branch information
richard-ramos committed Aug 31, 2022
1 parent dc31272 commit 7acbde2
Show file tree
Hide file tree
Showing 4 changed files with 202 additions and 30 deletions.
81 changes: 64 additions & 17 deletions rln/src/circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use num_bigint::BigUint;
use once_cell::sync::OnceCell;
use serde_json::Value;
use std::fs::File;
use std::io::{Error, ErrorKind, Read, Result};
use std::io::{Cursor, Error, ErrorKind, Read, Result};
use std::path::Path;
use std::str::FromStr;
use std::sync::Mutex;
Expand Down Expand Up @@ -43,9 +43,21 @@ pub type G1Projective = ArkG1Projective;
pub type G2Affine = ArkG2Affine;
pub type G2Projective = ArkG2Projective;

#[allow(non_snake_case)]
// Loads the proving key using a bytes vector
pub fn zkey_from_raw(zkey_data: &Vec<u8>) -> Result<(ProvingKey<Curve>, ConstraintMatrices<Fr>)> {
if !zkey_data.is_empty() {
let mut c = Cursor::new(zkey_data);
let proving_key_and_matrices = read_zkey(&mut c)?;
Ok(proving_key_and_matrices)
} else {
Err(Error::new(ErrorKind::NotFound, "No proving key found!"))
}
}

// Loads the proving key
pub fn ZKEY(resources_folder: &str) -> Result<(ProvingKey<Curve>, ConstraintMatrices<Fr>)> {
pub fn zkey_from_folder(
resources_folder: &str,
) -> Result<(ProvingKey<Curve>, ConstraintMatrices<Fr>)> {
let zkey_path = format!("{resources_folder}{ZKEY_FILENAME}");
if Path::new(&zkey_path).exists() {
let mut file = File::open(&zkey_path)?;
Expand All @@ -56,9 +68,27 @@ pub fn ZKEY(resources_folder: &str) -> Result<(ProvingKey<Curve>, ConstraintMatr
}
}

#[allow(non_snake_case)]
// Loads the verification key from a bytes vector
pub fn vk_from_raw(vk_data: &Vec<u8>, zkey_data: &Vec<u8>) -> Result<VerifyingKey<Curve>> {
let verifying_key: VerifyingKey<Curve>;

if !vk_data.is_empty() {
verifying_key = vk_from_vector(vk_data);
Ok(verifying_key)
} else if !zkey_data.is_empty() {
let (proving_key, _matrices) = zkey_from_raw(zkey_data)?;
verifying_key = proving_key.vk;
Ok(verifying_key)
} else {
Err(Error::new(
ErrorKind::NotFound,
"No proving/verification key found!",
))
}
}

// Loads the verification key
pub fn VK(resources_folder: &str) -> Result<VerifyingKey<Curve>> {
pub fn vk_from_folder(resources_folder: &str) -> Result<VerifyingKey<Curve>> {
let vk_path = format!("{resources_folder}{VK_FILENAME}");
let zkey_path = format!("{resources_folder}{ZKEY_FILENAME}");

Expand All @@ -68,7 +98,7 @@ pub fn VK(resources_folder: &str) -> Result<VerifyingKey<Curve>> {
verifying_key = vk_from_json(&vk_path);
Ok(verifying_key)
} else if Path::new(&zkey_path).exists() {
let (proving_key, _matrices) = ZKEY(resources_folder)?;
let (proving_key, _matrices) = zkey_from_folder(resources_folder)?;
verifying_key = proving_key.vk;
Ok(verifying_key)
} else {
Expand All @@ -93,12 +123,9 @@ fn read_wasm(resources_folder: &str) -> Vec<u8> {
wasm_buffer
}

#[allow(non_snake_case)]
// Initializes the witness calculator
pub fn CIRCOM(resources_folder: &str) -> &'static Mutex<WitnessCalculator> {
// Initializes the witness calculator using a bytes vector
pub fn circom_from_raw(wasm_buffer: Vec<u8>) -> &'static Mutex<WitnessCalculator> {
WITNESS_CALCULATOR.get_or_init(|| {
// We read the wasm file
let wasm_buffer = read_wasm(resources_folder);
let store = Store::default();
let module = Module::from_binary(&store, &wasm_buffer).expect("wasm should be valid");
let result =
Expand All @@ -107,6 +134,13 @@ pub fn CIRCOM(resources_folder: &str) -> &'static Mutex<WitnessCalculator> {
})
}

// Initializes the witness calculator
pub fn circom_from_folder(resources_folder: &str) -> &'static Mutex<WitnessCalculator> {
// We read the wasm file
let wasm_buffer = read_wasm(resources_folder);
circom_from_raw(wasm_buffer)
}

// The following function implementations are taken/adapted from https://github.com/gakonst/ark-circom/blob/1732e15d6313fe176b0b1abb858ac9e095d0dbd7/src/zkey.rs

// Utilities to convert a json verification key in a groth16::VerificationKey
Expand Down Expand Up @@ -182,11 +216,8 @@ fn json_to_g2(json: &Value, key: &str) -> G2Affine {
G2Affine::from(G2Projective::new(x, y, z))
}

// Computes the verification key from its JSON serialization
fn vk_from_json(vk_path: &str) -> VerifyingKey<Curve> {
let json = std::fs::read_to_string(vk_path).unwrap();
let json: Value = serde_json::from_str(&json).unwrap();

// Converts JSON to a VerifyingKey
fn to_verifying_key(json: serde_json::Value) -> VerifyingKey<Curve> {
VerifyingKey {
alpha_g1: json_to_g1(&json, "vk_alpha_1"),
beta_g2: json_to_g2(&json, "vk_beta_2"),
Expand All @@ -196,8 +227,24 @@ fn vk_from_json(vk_path: &str) -> VerifyingKey<Curve> {
}
}

// Computes the verification key from its JSON serialization
fn vk_from_json(vk_path: &str) -> VerifyingKey<Curve> {
let json = std::fs::read_to_string(vk_path).unwrap();
let json: Value = serde_json::from_str(&json).unwrap();

to_verifying_key(json)
}

// Computes the verification key from a bytes vector containing its JSON serialization
fn vk_from_vector(vk: &[u8]) -> VerifyingKey<Curve> {
let json = String::from_utf8(vk.to_vec()).expect("Found invalid UTF-8");
let json: Value = serde_json::from_str(&json).unwrap();

to_verifying_key(json)
}

// Checks verification key to be correct with respect to proving key
pub fn check_vk_from_zkey(resources_folder: &str, verifying_key: VerifyingKey<Curve>) {
let (proving_key, _matrices) = ZKEY(resources_folder).unwrap();
let (proving_key, _matrices) = zkey_from_folder(resources_folder).unwrap();
assert_eq!(proving_key.vk, verifying_key);
}
91 changes: 91 additions & 0 deletions rln/src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,23 @@ pub extern "C" fn new(tree_height: usize, input_buffer: *const Buffer, ctx: *mut
true
}

#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn new_with_params(
tree_height: usize,
circom_buffer: *const Buffer,
zkey_buffer: *const Buffer,
vk_buffer: *const Buffer,
ctx: *mut *mut RLN,
) -> bool {
let circom_data = <&[u8]>::from(unsafe { &*circom_buffer });
let zkey_data = <&[u8]>::from(unsafe { &*zkey_buffer });
let vk_data = <&[u8]>::from(unsafe { &*vk_buffer });
let rln = RLN::new_with_params(tree_height, circom_data, zkey_data, vk_data);
unsafe { *ctx = Box::into_raw(Box::new(rln)) };
true
}

////////////////////////////////////////////////////////
// Merkle tree APIs
////////////////////////////////////////////////////////
Expand Down Expand Up @@ -251,6 +268,8 @@ mod test {
use crate::utils::*;
use ark_std::{rand::thread_rng, UniformRand};
use rand::Rng;
use std::fs::File;
use std::io::Read;
use std::mem::MaybeUninit;
use std::time::{Duration, Instant};

Expand Down Expand Up @@ -578,6 +597,78 @@ mod test {
);
}

#[test]
// Creating a RLN with raw data should generate same results as using a path to resources
fn test_rln_raw_ffi() {
let tree_height = TEST_TREE_HEIGHT;

// We create a RLN instance using a resource folder path
let mut rln_pointer = MaybeUninit::<*mut RLN>::uninit();
let input_buffer = &Buffer::from(TEST_RESOURCES_FOLDER.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() };

// We obtain the root from the RLN instance
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
let success = get_root(rln_pointer, output_buffer.as_mut_ptr());
assert!(success, "get root call failed");
let output_buffer = unsafe { output_buffer.assume_init() };
let result_data = <&[u8]>::from(&output_buffer).to_vec();
let (root_rln_folder, _) = bytes_le_to_fr(&result_data);

// Reading the raw data from the files required for instantiating a RLN instance using raw data
let circom_path = format!("./resources/tree_height_{TEST_TREE_HEIGHT}/rln.wasm");
let mut circom_file = File::open(&circom_path).expect("no file found");
let metadata = std::fs::metadata(&circom_path).expect("unable to read metadata");
let mut circom_buffer = vec![0; metadata.len() as usize];
circom_file
.read_exact(&mut circom_buffer)
.expect("buffer overflow");

let zkey_path = format!("./resources/tree_height_{TEST_TREE_HEIGHT}/rln_final.zkey");
let mut zkey_file = File::open(&zkey_path).expect("no file found");
let metadata = std::fs::metadata(&zkey_path).expect("unable to read metadata");
let mut zkey_buffer = vec![0; metadata.len() as usize];
zkey_file
.read_exact(&mut zkey_buffer)
.expect("buffer overflow");

let vk_path = format!("./resources/tree_height_{TEST_TREE_HEIGHT}/verification_key.json");

let mut vk_file = File::open(&vk_path).expect("no file found");
let metadata = std::fs::metadata(&vk_path).expect("unable to read metadata");
let mut vk_buffer = vec![0; metadata.len() as usize];
vk_file.read_exact(&mut vk_buffer).expect("buffer overflow");

let circom_data = &Buffer::from(&circom_buffer[..]);
let zkey_data = &Buffer::from(&zkey_buffer[..]);
let vk_data = &Buffer::from(&vk_buffer[..]);

// Creating a RLN instance passing the raw data
let mut rln_pointer_raw_bytes = MaybeUninit::<*mut RLN>::uninit();
let success = new_with_params(
tree_height,
circom_data,
zkey_data,
vk_data,
rln_pointer_raw_bytes.as_mut_ptr(),
);
assert!(success, "RLN object creation failed");
let rln_pointer2 = unsafe { &mut *rln_pointer_raw_bytes.assume_init() };

// We obtain the root from the RLN instance containing raw data
let mut output_buffer = MaybeUninit::<Buffer>::uninit();
let success = get_root(rln_pointer2, output_buffer.as_mut_ptr());
assert!(success, "get root call failed");
let output_buffer = unsafe { output_buffer.assume_init() };
let result_data = <&[u8]>::from(&output_buffer).to_vec();
let (root_rln_raw, _) = bytes_le_to_fr(&result_data);

// And compare that the same root was generated
assert_eq!(root_rln_folder, root_rln_raw);
}

#[test]
// Computes and verifies an RLN ZK proof using FFI APIs
fn test_rln_proof_ffi() {
Expand Down
17 changes: 10 additions & 7 deletions rln/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ pub mod utils;
#[cfg(test)]
mod test {

use crate::circuit::{Fr, CIRCOM, TEST_RESOURCES_FOLDER, TEST_TREE_HEIGHT, VK, ZKEY};
use crate::circuit::{
circom_from_folder, vk_from_folder, zkey_from_folder, Fr, TEST_RESOURCES_FOLDER,
TEST_TREE_HEIGHT,
};
use crate::poseidon_hash::poseidon_hash;
use crate::poseidon_tree::PoseidonTree;
use crate::protocol::*;
Expand Down Expand Up @@ -327,9 +330,9 @@ mod test {
// We test a RLN proof generation and verification
fn test_witness_from_json() {
// We generate all relevant keys
let proving_key = ZKEY(TEST_RESOURCES_FOLDER).unwrap();
let verification_key = VK(TEST_RESOURCES_FOLDER).unwrap();
let builder = CIRCOM(TEST_RESOURCES_FOLDER);
let proving_key = zkey_from_folder(TEST_RESOURCES_FOLDER).unwrap();
let verification_key = vk_from_folder(TEST_RESOURCES_FOLDER).unwrap();
let builder = circom_from_folder(TEST_RESOURCES_FOLDER);

// We compute witness from the json input example
let mut witness_json: &str = "";
Expand Down Expand Up @@ -386,9 +389,9 @@ mod test {
);

// We generate all relevant keys
let proving_key = ZKEY(TEST_RESOURCES_FOLDER).unwrap();
let verification_key = VK(TEST_RESOURCES_FOLDER).unwrap();
let builder = CIRCOM(TEST_RESOURCES_FOLDER);
let proving_key = zkey_from_folder(TEST_RESOURCES_FOLDER).unwrap();
let verification_key = vk_from_folder(TEST_RESOURCES_FOLDER).unwrap();
let builder = circom_from_folder(TEST_RESOURCES_FOLDER);

// Let's generate a zkSNARK proof
let proof = generate_proof(builder, &proving_key, &rln_witness).unwrap();
Expand Down
43 changes: 37 additions & 6 deletions rln/src/public.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ use std::io::Cursor;
use std::io::{self, Result};
use std::sync::Mutex;

use crate::circuit::{Curve, Fr, CIRCOM, TEST_RESOURCES_FOLDER, TEST_TREE_HEIGHT, VK, ZKEY};
use crate::circuit::{
circom_from_folder, circom_from_raw, vk_from_folder, vk_from_raw, zkey_from_folder,
zkey_from_raw, Curve, Fr, TEST_RESOURCES_FOLDER, TEST_TREE_HEIGHT,
};
use crate::poseidon_tree::PoseidonTree;
use crate::protocol::*;
use crate::utils::*;
Expand All @@ -25,7 +28,6 @@ pub struct RLN<'a> {
proving_key: Result<(ProvingKey<Curve>, ConstraintMatrices<Fr>)>,
verification_key: Result<VerifyingKey<Curve>>,
tree: PoseidonTree,
resources_folder: String,
}

impl RLN<'_> {
Expand All @@ -36,10 +38,40 @@ impl RLN<'_> {

let resources_folder = String::from_utf8(input).expect("Found invalid UTF-8");

let witness_calculator = CIRCOM(&resources_folder);
let witness_calculator = circom_from_folder(&resources_folder);

let proving_key = ZKEY(&resources_folder);
let verification_key = VK(&resources_folder);
let proving_key = zkey_from_folder(&resources_folder);
let verification_key = vk_from_folder(&resources_folder);

// We compute a default empty tree
let tree = PoseidonTree::default(tree_height);

RLN {
witness_calculator,
proving_key,
verification_key,
tree,
}
}

pub fn new_with_params<R: Read>(
tree_height: usize,
mut circom_data: R,
mut zkey_data: R,
mut vk_data: R,
) -> RLN<'static> {
// We read input
let mut circom_vec: Vec<u8> = Vec::new();
circom_data.read_to_end(&mut circom_vec).unwrap();
let mut zkey_vec: Vec<u8> = Vec::new();
zkey_data.read_to_end(&mut zkey_vec).unwrap();
let mut vk_vec: Vec<u8> = Vec::new();
vk_data.read_to_end(&mut vk_vec).unwrap();

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);

// We compute a default empty tree
let tree = PoseidonTree::default(tree_height);
Expand All @@ -49,7 +81,6 @@ impl RLN<'_> {
proving_key,
verification_key,
tree,
resources_folder,
}
}

Expand Down

0 comments on commit 7acbde2

Please sign in to comment.