From c6493bd10fd2f824bc6a6f19b45e7678395930d3 Mon Sep 17 00:00:00 2001 From: Aaryamann Challani <43716372+rymnc@users.noreply.github.com> Date: Fri, 14 Jun 2024 11:03:55 +0530 Subject: [PATCH] chore(rln): use ark serialized verification key for faster serde (#259) * chore(rln): use ark serialized verification key for faster serde * fix: unused imports * fix: rm verification_key.json * fix: s/vk_from_slice/vk_from_ark_serialized/g --- rln-cli/src/main.rs | 4 +- rln-wasm/tests/rln-wasm.rs | 4 +- rln/README.md | 2 +- rln/benches/circuit_deser_benchmark.rs | 8 +- .../tree_height_20/verification_key.arkvkey | Bin 0 -> 840 bytes .../tree_height_20/verification_key.json | 114 ---------------- rln/src/circuit.rs | 126 ++---------------- rln/src/public.rs | 4 +- rln/src/public_api_tests.rs | 21 +-- rln/tests/ffi.rs | 2 +- 10 files changed, 21 insertions(+), 264 deletions(-) create mode 100644 rln/resources/tree_height_20/verification_key.arkvkey delete mode 100644 rln/resources/tree_height_20/verification_key.json diff --git a/rln-cli/src/main.rs b/rln-cli/src/main.rs index 849e02c..c8931ff 100644 --- a/rln-cli/src/main.rs +++ b/rln-cli/src/main.rs @@ -38,9 +38,9 @@ fn main() -> Result<()> { }) => { let mut resources: Vec> = Vec::new(); #[cfg(feature = "arkzkey")] - let filenames = ["rln.wasm", "rln_final.arkzkey", "verification_key.json"]; + let filenames = ["rln.wasm", "rln_final.arkzkey", "verification_key.arkvkey"]; #[cfg(not(feature = "arkzkey"))] - let filenames = ["rln.wasm", "rln_final.zkey", "verification_key.json"]; + let filenames = ["rln.wasm", "rln_final.zkey", "verification_key.arkvkey"]; for filename in filenames { let fullpath = config.join(Path::new(filename)); let mut file = File::open(&fullpath)?; diff --git a/rln-wasm/tests/rln-wasm.rs b/rln-wasm/tests/rln-wasm.rs index a4f29a8..1c30707 100644 --- a/rln-wasm/tests/rln-wasm.rs +++ b/rln-wasm/tests/rln-wasm.rs @@ -25,7 +25,7 @@ mod tests { let circom_path = format!("../rln/resources/tree_height_{TEST_TREE_HEIGHT}/rln.wasm"); let zkey_path = format!("../rln/resources/tree_height_{TEST_TREE_HEIGHT}/rln_final.zkey"); let vk_path = - format!("../rln/resources/tree_height_{TEST_TREE_HEIGHT}/verification_key.json"); + format!("../rln/resources/tree_height_{TEST_TREE_HEIGHT}/verification_key.arkvkey"); let zkey = read_file(&zkey_path).unwrap(); let vk = read_file(&vk_path).unwrap(); @@ -129,7 +129,7 @@ mod tests { let tree_height = TEST_TREE_HEIGHT; let zkey_path = format!("../rln/resources/tree_height_{TEST_TREE_HEIGHT}/rln_final.zkey"); let vk_path = - format!("../rln/resources/tree_height_{TEST_TREE_HEIGHT}/verification_key.json"); + format!("../rln/resources/tree_height_{TEST_TREE_HEIGHT}/verification_key.arkvkey"); let zkey = read_file(&zkey_path).unwrap(); let vk = read_file(&vk_path).unwrap(); diff --git a/rln/README.md b/rln/README.md index 4271908..ae38ee4 100644 --- a/rln/README.md +++ b/rln/README.md @@ -74,7 +74,7 @@ rln = { git = "https://github.com/vacp2p/zerokit" } First, we need to create a RLN object for a chosen input Merkle tree size. -Note that we need to pass to RLN object constructor the path where the circuit (`rln.wasm`, built for the input tree size), the corresponding proving key (`rln_final.zkey`) or (`rln_final.arkzkey`) and verification key (`verification_key.json`, optional) are found. +Note that we need to pass to RLN object constructor the path where the circuit (`rln.wasm`, built for the input tree size), the corresponding proving key (`rln_final.zkey`) or (`rln_final.arkzkey`) and verification key (`verification_key.arkvkey`, optional) are found. In the following we will use [cursors](https://doc.rust-lang.org/std/io/struct.Cursor.html) as readers/writers for interfacing with RLN public APIs. diff --git a/rln/benches/circuit_deser_benchmark.rs b/rln/benches/circuit_deser_benchmark.rs index ce8a160..1311c9f 100644 --- a/rln/benches/circuit_deser_benchmark.rs +++ b/rln/benches/circuit_deser_benchmark.rs @@ -1,6 +1,5 @@ use criterion::{criterion_group, criterion_main, Criterion}; -use rln::circuit::{to_verifying_key, RESOURCES_DIR, VK_FILENAME}; -use serde_json::Value; +use rln::circuit::{vk_from_ark_serialized, RESOURCES_DIR, VK_FILENAME}; use std::path::Path; // Here we benchmark how long the deserialization of the @@ -8,12 +7,11 @@ use std::path::Path; // and skipping conversion from bytes => string => serde_json::Value pub fn vk_deserialize_benchmark(c: &mut Criterion) { let vk = RESOURCES_DIR.get_file(Path::new(VK_FILENAME)).unwrap(); - let vk = vk.contents_utf8().unwrap(); - let json: Value = serde_json::from_str(vk).unwrap(); + let vk = vk.contents(); c.bench_function("circuit::to_verifying_key", |b| { b.iter(|| { - let _ = to_verifying_key(&json); + let _ = vk_from_ark_serialized(&vk); }) }); } diff --git a/rln/resources/tree_height_20/verification_key.arkvkey b/rln/resources/tree_height_20/verification_key.arkvkey new file mode 100644 index 0000000000000000000000000000000000000000..1c24e1bc166f2ea814169b10f7aac688c1f7c3b0 GIT binary patch literal 840 zcmV-O1GoI*@@>ANne`$&WNfkfGwRy~osX`msyb}T*Byr%ti`IRF0re|$*3fi=c}%TGco+^0 z2J<_-OnIrw-ttqXfP~Y6@pp*$g~_kccK6FK17``y&d_>P27lG51?oP=x0d*lkuOcs zBQY_@s8^)1!8sSFMk5hTnx>sM4SH+D-u3#-T)ZF67#ONh2)nK=czR_NviQBUHdV&zhH7YNDY#c zla3jxefnkr%;rpc(?bkt=h5SjK+BDQajQz}ZH(KcUdR$#mm<;J+N@Rcj!Wr#->EH@}KEP-EUm-0;#$d85em}i1vUj5;cOhf3 zo-3p>!ZnzK)?D++>mBA~kT(UU2Xs0Sqq`t=*>)*8&L2XLocW3^Vr$dU3lefiMaBhFwpaeGl91kEvROB|YaKrSR} z*uGl}mBbsM>#l(qx-HY}BLgjUM+c`?`9VV=70RW;;F=4FRwTh-$vLMF(Up@a8 zSw$Uc%Z9QXnj#2(-TTM^pv!XLt9tYJS$vg(P*EX4{tm9M=P$%qSJ3O5$&_uKo&?aN|pyYMiX(78RK)X@^QguxTIHFotFf3Kh0hNiu2Kp9O8 S0fU`{OU(LJ_8**?G71Z*$(U~d literal 0 HcmV?d00001 diff --git a/rln/resources/tree_height_20/verification_key.json b/rln/resources/tree_height_20/verification_key.json deleted file mode 100644 index 162e303..0000000 --- a/rln/resources/tree_height_20/verification_key.json +++ /dev/null @@ -1,114 +0,0 @@ -{ - "protocol": "groth16", - "curve": "bn128", - "nPublic": 5, - "vk_alpha_1": [ - "20491192805390485299153009773594534940189261866228447918068658471970481763042", - "9383485363053290200918347156157836566562967994039712273449902621266178545958", - "1" - ], - "vk_beta_2": [ - [ - "6375614351688725206403948262868962793625744043794305715222011528459656738731", - "4252822878758300859123897981450591353533073413197771768651442665752259397132" - ], - [ - "10505242626370262277552901082094356697409835680220590971873171140371331206856", - "21847035105528745403288232691147584728191162732299865338377159692350059136679" - ], - [ - "1", - "0" - ] - ], - "vk_gamma_2": [ - [ - "10857046999023057135944570762232829481370756359578518086990519993285655852781", - "11559732032986387107991004021392285783925812861821192530917403151452391805634" - ], - [ - "8495653923123431417604973247489272438418190587263600148770280649306958101930", - "4082367875863433681332203403145435568316851327593401208105741076214120093531" - ], - [ - "1", - "0" - ] - ], - "vk_delta_2": [ - [ - "17077735495685170943380938230836408503627170115414840315502244846025577589191", - "14030085636943255545683322474441991939484590437387381169642530788494152024614" - ], - [ - "11568745146423307387256571230823432454624378106569286849514884592874522611163", - "1838524899938769516485895655063198583192139511330418290063560641219523306282" - ], - [ - "1", - "0" - ] - ], - "vk_alphabeta_12": [ - [ - [ - "2029413683389138792403550203267699914886160938906632433982220835551125967885", - "21072700047562757817161031222997517981543347628379360635925549008442030252106" - ], - [ - "5940354580057074848093997050200682056184807770593307860589430076672439820312", - "12156638873931618554171829126792193045421052652279363021382169897324752428276" - ], - [ - "7898200236362823042373859371574133993780991612861777490112507062703164551277", - "7074218545237549455313236346927434013100842096812539264420499035217050630853" - ] - ], - [ - [ - "7077479683546002997211712695946002074877511277312570035766170199895071832130", - "10093483419865920389913245021038182291233451549023025229112148274109565435465" - ], - [ - "4595479056700221319381530156280926371456704509942304414423590385166031118820", - "19831328484489333784475432780421641293929726139240675179672856274388269393268" - ], - [ - "11934129596455521040620786944827826205713621633706285934057045369193958244500", - "8037395052364110730298837004334506829870972346962140206007064471173334027475" - ] - ] - ], - "IC": [ - [ - "4920513730204767532050733107749276406754520419375654722016092399980613788208", - "10950491564509418434657706642388934308456795265036074733953533582377584967294", - "1" - ], - [ - "6815064660695497986531118446154820702646540722664044216580897159556261271171", - "17838140936832571103329556013529166877877534025488014783346458943575275015438", - "1" - ], - [ - "16364982450206976302246609763791333525052810246590359380676749324389440643932", - "17092624338100676284548565502349491320314889021833923882585524649862570629227", - "1" - ], - [ - "3679639231485547795420532910726924727560917141402837495597760107842698404034", - "16213191511474848247596810551723578773353083440353745908057321946068926848382", - "1" - ], - [ - "9215428431027260354679105025212521481930206886203677270216204485256690813172", - "934602510541226149881779979217731465262250233587980565969044391353665291792", - "1" - ], - [ - "8935861545794299876685457331391349387048184820319250771243971382360441890897", - "4993459033694759724715904486381952906869986989682015547152342336961693234616", - "1" - ] - ] -} \ No newline at end of file diff --git a/rln/src/circuit.rs b/rln/src/circuit.rs index 91b27e0..bcd2bb1 100644 --- a/rln/src/circuit.rs +++ b/rln/src/circuit.rs @@ -6,11 +6,9 @@ use ark_bn254::{ }; use ark_groth16::{ProvingKey, VerifyingKey}; use ark_relations::r1cs::ConstraintMatrices; +use ark_serialize::CanonicalDeserialize; use cfg_if::cfg_if; use color_eyre::{Report, Result}; -use num_bigint::BigUint; -use serde_json::Value; -use std::str::FromStr; cfg_if! { if #[cfg(not(target_arch = "wasm32"))] { @@ -35,7 +33,7 @@ cfg_if! { } const ZKEY_FILENAME: &str = "tree_height_20/rln_final.zkey"; -pub const VK_FILENAME: &str = "tree_height_20/verification_key.json"; +pub const VK_FILENAME: &str = "tree_height_20/verification_key.arkvkey"; const WASM_FILENAME: &str = "tree_height_20/rln.wasm"; pub const TEST_TREE_HEIGHT: usize = 20; @@ -101,7 +99,7 @@ pub fn vk_from_raw(vk_data: &[u8], zkey_data: &Vec) -> Result; if !vk_data.is_empty() { - verifying_key = vk_from_vector(vk_data)?; + verifying_key = vk_from_ark_serialized(vk_data)?; Ok(verifying_key) } else if !zkey_data.is_empty() { let (proving_key, _matrices) = zkey_from_raw(zkey_data)?; @@ -120,9 +118,7 @@ pub fn vk_from_folder() -> Result> { let verifying_key: VerifyingKey; if let Some(vk) = vk { - verifying_key = vk_from_json(vk.contents_utf8().ok_or(Report::msg( - "Could not read verification key from JSON file!", - ))?)?; + verifying_key = vk_from_ark_serialized(vk.contents())?; Ok(verifying_key) } else if let Some(_zkey) = zkey { let (proving_key, _matrices) = zkey_from_folder()?; @@ -161,115 +157,11 @@ pub fn circom_from_folder() -> Result<&'static Mutex> { } } -// 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 -fn fq_from_str(s: &str) -> Result { - Ok(Fq::from(BigUint::from_str(s)?)) -} - -// Extracts the element in G1 corresponding to its JSON serialization -fn json_to_g1(json: &Value, key: &str) -> Result { - let els: Vec = json - .get(key) - .ok_or(Report::msg("no json value"))? - .as_array() - .ok_or(Report::msg("value not an array"))? - .iter() - .map(|i| i.as_str().ok_or(Report::msg("element is not a string"))) - .map(|x| x.map(|v| v.to_owned())) - .collect::>>()?; - - Ok(G1Affine::from(G1Projective::new( - fq_from_str(&els[0])?, - fq_from_str(&els[1])?, - fq_from_str(&els[2])?, - ))) -} - -// Extracts the vector of G1 elements corresponding to its JSON serialization -fn json_to_g1_vec(json: &Value, key: &str) -> Result> { - let els: Vec> = json - .get(key) - .ok_or(Report::msg("no json value"))? - .as_array() - .ok_or(Report::msg("value not an array"))? - .iter() - .map(|i| { - i.as_array() - .ok_or(Report::msg("element is not an array")) - .and_then(|array| { - array - .iter() - .map(|x| x.as_str().ok_or(Report::msg("element is not a string"))) - .map(|x| x.map(|v| v.to_owned())) - .collect::>>() - }) - }) - .collect::>>>()?; - - let mut res = vec![]; - for coords in els { - res.push(G1Affine::from(G1Projective::new( - fq_from_str(&coords[0])?, - fq_from_str(&coords[1])?, - fq_from_str(&coords[2])?, - ))) - } - - Ok(res) -} - -// Extracts the element in G2 corresponding to its JSON serialization -fn json_to_g2(json: &Value, key: &str) -> Result { - let els: Vec> = json - .get(key) - .ok_or(Report::msg("no json value"))? - .as_array() - .ok_or(Report::msg("value not an array"))? - .iter() - .map(|i| { - i.as_array() - .ok_or(Report::msg("element is not an array")) - .and_then(|array| { - array - .iter() - .map(|x| x.as_str().ok_or(Report::msg("element is not a string"))) - .map(|x| x.map(|v| v.to_owned())) - .collect::>>() - }) - }) - .collect::>>>()?; - - let x = Fq2::new(fq_from_str(&els[0][0])?, fq_from_str(&els[0][1])?); - let y = Fq2::new(fq_from_str(&els[1][0])?, fq_from_str(&els[1][1])?); - let z = Fq2::new(fq_from_str(&els[2][0])?, fq_from_str(&els[2][1])?); - Ok(G2Affine::from(G2Projective::new(x, y, z))) -} - -// Converts JSON to a VerifyingKey -pub fn to_verifying_key(json: &serde_json::Value) -> Result> { - Ok(VerifyingKey { - alpha_g1: json_to_g1(json, "vk_alpha_1")?, - beta_g2: json_to_g2(json, "vk_beta_2")?, - gamma_g2: json_to_g2(json, "vk_gamma_2")?, - delta_g2: json_to_g2(json, "vk_delta_2")?, - gamma_abc_g1: json_to_g1_vec(json, "IC")?, - }) -} - -// Computes the verification key from its JSON serialization -fn vk_from_json(vk: &str) -> Result> { - let json: Value = serde_json::from_str(vk)?; - to_verifying_key(&json) -} - -// Computes the verification key from a bytes vector containing its JSON serialization -fn vk_from_vector(vk: &[u8]) -> Result> { - let json = String::from_utf8(vk.to_vec())?; - let json: Value = serde_json::from_str(&json)?; - - to_verifying_key(&json) +// Computes the verification key from a bytes vector containing pre-processed ark-serialized verification key +// uncompressed, unchecked +pub fn vk_from_ark_serialized(data: &[u8]) -> Result> { + let vk = VerifyingKey::::deserialize_uncompressed_unchecked(data)?; + Ok(vk) } // Checks verification key to be correct with respect to proving key diff --git a/rln/src/public.rs b/rln/src/public.rs index c49498f..5a64fbd 100644 --- a/rln/src/public.rs +++ b/rln/src/public.rs @@ -112,7 +112,7 @@ impl RLN<'_> { /// - `tree_height`: the height of the internal Merkle tree /// - `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`) or (`rln_final.arkzkey`) as binary file - /// - `vk_vec`: a byte vector containing to the verification key (`verification_key.json`) as binary file + /// - `vk_vec`: a byte vector containing to the verification key (`verification_key.arkvkey`) as binary file /// - `tree_config_input`: a reader for a string containing a json with the merkle tree configuration /// /// Example: @@ -124,7 +124,7 @@ impl RLN<'_> { /// let resources_folder = "./resources/tree_height_20/"; /// /// let mut resources: Vec> = Vec::new(); - /// for filename in ["rln.wasm", "rln_final.zkey", "verification_key.json"] { + /// for filename in ["rln.wasm", "rln_final.zkey", "verification_key.arkvkey"] { /// let fullpath = format!("{resources_folder}{filename}"); /// let mut file = File::open(&fullpath).expect("no file found"); /// let metadata = std::fs::metadata(&fullpath).expect("unable to read metadata"); diff --git a/rln/src/public_api_tests.rs b/rln/src/public_api_tests.rs index 6dc78ea..d263339 100644 --- a/rln/src/public_api_tests.rs +++ b/rln/src/public_api_tests.rs @@ -1,11 +1,10 @@ -use crate::circuit::{Curve, Fr, TEST_TREE_HEIGHT}; +use crate::circuit::{Fr, TEST_TREE_HEIGHT}; use crate::hashers::{hash_to_field, poseidon_hash as utils_poseidon_hash}; use crate::protocol::*; use crate::public::RLN; use crate::utils::*; use ark_groth16::Proof as ArkProof; use ark_serialize::{CanonicalDeserialize, Read}; -use num_bigint::BigInt; use std::io::Cursor; use std::str::FromStr; use utils::ZerokitMerkleTree; @@ -646,24 +645,6 @@ fn test_rln_with_witness() { let serialized_witness = serialize_witness(&rln_witness).unwrap(); - // Calculate witness outside zerokit (simulating what JS is doing) - let inputs = inputs_for_witness_calculation(&rln_witness) - .unwrap() - .into_iter() - .map(|(name, values)| (name.to_string(), values)); - let calculated_witness = rln - .witness_calculator - .lock() - .expect("witness_calculator mutex should not get poisoned") - .calculate_witness_element::(inputs, false) - .map_err(ProofError::WitnessError) - .unwrap(); - - let calculated_witness_vec: Vec = calculated_witness - .into_iter() - .map(|v| to_bigint(&v).unwrap()) - .collect(); - // Generating the proof let mut input_buffer = Cursor::new(serialized_witness); let mut output_buffer = Cursor::new(Vec::::new()); diff --git a/rln/tests/ffi.rs b/rln/tests/ffi.rs index 30b977a..1bdb520 100644 --- a/rln/tests/ffi.rs +++ b/rln/tests/ffi.rs @@ -427,7 +427,7 @@ mod test { .read_exact(&mut zkey_buffer) .expect("buffer overflow"); - let vk_path = "./resources/tree_height_20/verification_key.json"; + let vk_path = "./resources/tree_height_20/verification_key.arkvkey"; 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];