refactor(RLN:) Remove dependencies and add new APIs (#45)

* refactor(rln): removing unused crates/dependencies

* cargo fmt

* refactor(rln): removed more dependencies; curve/fields as parameters

* refactor(rln): use poseidon-rs hash instead of semaphore-rs poseidon

* chore(rln): remove deps

* refactor(rln): use exclusively arkworks Fr

* refactor(rln): integrate poseidon-rs implementation to work with arkworks arithmetic

* fix(rln): remove previous poseidon-rs wrapper

* feat(rln): add features to select MT; remove prints if not in debug mode

* fix(rln): collect test parameters in a vector

* feat(RLN): add `new_with_params` (#36)

Allows passing the wasm, zkey and verification key data as buffers, instead of using a path to a folder

* chore(rln): simplify read wasm

* fix(rln): remove unused dependencies

* cargo fmt

* fix(rln): update dependencies, fix commit

* refactor(rln): restore ark-circom original dep

Co-authored-by: Richard Ramos <info@richardramos.me>
This commit is contained in:
G 2022-09-15 10:32:45 +02:00 committed by GitHub
parent 1131b76a66
commit 4dc600d353
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 261 additions and 73 deletions

2
.gitmodules vendored
View File

@ -1,7 +1,7 @@
[submodule "rln/vendor/rln"]
path = rln/vendor/rln
ignore = dirty
url = https://github.com/privacy-scaling-explorations/rln.git
url = https://github.com/Rate-Limiting-Nullifier/rln_circuits
[submodule "semaphore/vendor/semaphore"]
path = semaphore/vendor/semaphore
ignore = dirty

View File

@ -9,28 +9,34 @@ crate-type = ["cdylib", "rlib", "staticlib"]
[dependencies]
# ZKP Generation
ark-ec = { version = "0.3.0", default-features = false, features = ["parallel"] }
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-relations = { version = "0.3.0", default-features = false, features = [ "std" ] }
ark-serialize = { version = "0.3.0", default-features = false }
ark-circom = { git = "https://github.com/gakonst/ark-circom", features = ["circom-2"] }
wasmer = { version = "2.0" }
ark-circom = { git = "https://github.com/gakonst/ark-circom", rev = "06eb075", features = ["circom-2"] }
#ark-circom = { git = "https://github.com/vacp2p/ark-circom", branch = "no-ethers-core", features = ["circom-2"] }
wasmer = "2.3.0"
# error handling
color-eyre = "0.5"
color-eyre = "0.5.11"
thiserror = "1.0.0"
# utilities
hex-literal = "0.3"
num-bigint = { version = "0.4", default-features = false, features = ["rand"] }
once_cell = "1.8"
cfg-if = "1.0"
num-bigint = { version = "0.4.3", default-features = false, features = ["rand"] }
num-traits = "0.2.11"
once_cell = "1.14.0"
rand = "0.8"
tiny-keccak = "2.0.2"
num-traits = "0.2.15"
tiny-keccak = { version = "2.0.2", features = ["keccak"] }
# serialization
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
serde_json = "1.0.48"
serde_json = "1.0.48"
[dev-dependencies]
hex-literal = "0.3.4"
[features]
fullmerkletree = []

View File

@ -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, Result};
use std::path::Path;
use std::str::FromStr;
use std::sync::Mutex;
@ -22,18 +22,18 @@ const VK_FILENAME: &str = "verifying_key.json";
const WASM_FILENAME: &str = "rln.wasm";
// These parameters are used for tests
// Note that the circuit and keys in TEST_RESOURCES_FOLDER are compiled for Merkle trees of height 15 and 19
// Changing these parameters to other values than these two defaults will cause zkSNARK proof verification to fail
//pub const TEST_TREE_HEIGHT: usize = 15;
//pub const TEST_RESOURCES_FOLDER: &str = "./resources/tree_height_15/";
//pub const TEST_TREE_HEIGHT: usize = 19;
//pub const TEST_RESOURCES_FOLDER: &str = "./resources/tree_height_19/";
pub const TEST_TREE_HEIGHT: usize = 20;
pub const TEST_RESOURCES_FOLDER: &str = "./resources/tree_height_20/";
// Note that the circuit and keys in TEST_RESOURCES_FOLDER are compiled for Merkle trees of height 15, 19 and 20
// Changing these parameters to other values than these defaults will cause zkSNARK proof verification to fail
pub const TEST_PARAMETERS_INDEX: usize = 2;
pub const TEST_TREE_HEIGHT: usize = [15, 19, 20][TEST_PARAMETERS_INDEX];
pub const TEST_RESOURCES_FOLDER: &str = [
"./resources/tree_height_15/",
"./resources/tree_height_19/",
"./resources/tree_height_20/",
][TEST_PARAMETERS_INDEX];
// The following types define the pairing friendly elliptic curve, the underlying finite fields and groups default to this module
// Note that proofs are serialized assuming Fr to be 4x8 = 32 bytes in size. Hence, changing to a curve with different encoding will make proof verification to fail
pub type Curve = Bn254;
pub type Fr = ArkFr;
pub type Fq = ArkFq;
@ -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)?;
@ -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}");
@ -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 {
@ -81,32 +111,25 @@ pub fn VK(resources_folder: &str) -> Result<VerifyingKey<Curve>> {
static WITNESS_CALCULATOR: OnceCell<Mutex<WitnessCalculator>> = OnceCell::new();
// Loads the circuit WASM
fn read_wasm(resources_folder: &str) -> Vec<u8> {
let wasm_path = format!("{resources_folder}{WASM_FILENAME}");
let mut wasm_file = File::open(&wasm_path).expect("no file found");
let metadata = std::fs::metadata(&wasm_path).expect("unable to read metadata");
let mut wasm_buffer = vec![0; metadata.len() as usize];
wasm_file
.read_exact(&mut wasm_buffer)
.expect("buffer overflow");
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 module = Module::new(&store, wasm_buffer).unwrap();
let result =
WitnessCalculator::from_module(module).expect("Failed to create witness calculator");
Mutex::new(result)
})
}
// Initializes the witness calculator
pub fn circom_from_folder(resources_folder: &str) -> &'static Mutex<WitnessCalculator> {
// We read the wasm file
let wasm_path = format!("{resources_folder}{WASM_FILENAME}");
let wasm_buffer = std::fs::read(&wasm_path).unwrap();
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
@ -182,11 +205,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"),
@ -196,8 +216,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);
}

View File

@ -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
////////////////////////////////////////////////////////
@ -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};
@ -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() {

View File

@ -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::*;
@ -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 = "";
@ -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();

View File

@ -5,13 +5,20 @@
use crate::circuit::Fr;
use crate::merkle_tree::*;
use crate::poseidon_hash::poseidon_hash;
use cfg_if::cfg_if;
// The zerokit RLN default Merkle tree implementation.
// To switch to FullMerkleTree implementation it is enough to redefine the following two types
pub type PoseidonTree = OptimalMerkleTree<PoseidonHash>;
pub type MerkleProof = OptimalMerkleProof<PoseidonHash>;
//pub type PoseidonTree = FullMerkleTree<PoseidonHash>;
//pub type MerkleProof = FullMerkleProof<PoseidonHash>;
// The zerokit RLN default Merkle tree implementation is the OptimalMerkleTree.
// To switch to FullMerkleTree implementation, it is enough to enable the fullmerkletree feature
cfg_if! {
if #[cfg(feature = "fullmerkletree")] {
pub type PoseidonTree = FullMerkleTree<PoseidonHash>;
pub type MerkleProof = FullMerkleProof<PoseidonHash>;
} else {
pub type PoseidonTree = OptimalMerkleTree<PoseidonHash>;
pub type MerkleProof = OptimalMerkleProof<PoseidonHash>;
}
}
// The zerokit RLN default Hasher
#[derive(Clone, Copy, PartialEq, Eq)]

View File

@ -12,6 +12,7 @@ use color_eyre::Result;
use num_bigint::BigInt;
use rand::Rng;
use std::sync::Mutex;
#[cfg(debug_assertions)]
use std::time::Instant;
use thiserror::Error;
use tiny_keccak::{Hasher as _, Keccak};
@ -415,6 +416,8 @@ pub fn generate_proof(
.into_iter()
.map(|(name, values)| (name.to_string(), values));
// If in debug mode, we measure and later print time take to compute witness
#[cfg(debug_assertions)]
let now = Instant::now();
let full_assignment = witness_calculator
@ -423,6 +426,7 @@ pub fn generate_proof(
.calculate_witness_element::<Curve, _>(inputs, false)
.map_err(ProofError::WitnessError)?;
#[cfg(debug_assertions)]
println!("witness generation took: {:.2?}", now.elapsed());
// Random Values
@ -430,7 +434,10 @@ pub fn generate_proof(
let r = Fr::rand(&mut rng);
let s = Fr::rand(&mut rng);
// If in debug mode, we measure and later print time take to compute proof
#[cfg(debug_assertions)]
let now = Instant::now();
let proof = create_proof_with_reduction_and_matrices::<_, CircomReduction>(
&proving_key.0,
r,
@ -440,6 +447,8 @@ pub fn generate_proof(
proving_key.1.num_constraints,
full_assignment.as_slice(),
)?;
#[cfg(debug_assertions)]
println!("proof generation took: {:.2?}", now.elapsed());
Ok(proof)
@ -469,8 +478,14 @@ pub fn verify_proof(
// Check that the proof is valid
let pvk = prepare_verifying_key(verifying_key);
//let pr: ArkProof<Curve> = (*proof).into();
// If in debug mode, we measure and later print time take to verify proof
#[cfg(debug_assertions)]
let now = Instant::now();
let verified = ark_verify_proof(&pvk, proof, &inputs)?;
#[cfg(debug_assertions)]
println!("verify took: {:.2?}", now.elapsed());
Ok(verified)

View File

@ -4,13 +4,15 @@ use ark_circom::WitnessCalculator;
use ark_groth16::Proof as ArkProof;
use ark_groth16::{ProvingKey, VerifyingKey};
use ark_relations::r1cs::ConstraintMatrices;
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, Write};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use std::default::Default;
use std::io::Cursor;
use std::io::{self, Result};
use std::io::{self, Cursor, Read, Result, Write};
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::*;
@ -25,7 +27,6 @@ pub struct RLN<'a> {
proving_key: Result<(ProvingKey<Curve>, ConstraintMatrices<Fr>)>,
verification_key: Result<VerifyingKey<Curve>>,
tree: PoseidonTree,
resources_folder: String,
}
impl RLN<'_> {
@ -36,10 +37,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);
@ -49,7 +80,6 @@ impl RLN<'_> {
proving_key,
verification_key,
tree,
resources_folder,
}
}