From 7ac5b60cb6f948711108d3c3fb5198d61b160ee9 Mon Sep 17 00:00:00 2001 From: s1fr0 <28568419+s1fr0@users.noreply.github.com> Date: Thu, 9 Jun 2022 00:41:50 +0200 Subject: [PATCH] fix(rln): switch to field type, add field arithmetic --- rln/src/circuit.rs | 5 ++ rln/src/lib.rs | 113 +++++++++++++++++++++++++++++++++++++------- rln/src/protocol.rs | 95 +++++++++++++++++++++---------------- rln/src/public.rs | 39 ++++++++------- rln/src/utils.rs | 68 ++++++++++++++++++++++++++ 5 files changed, 243 insertions(+), 77 deletions(-) create mode 100644 rln/src/utils.rs diff --git a/rln/src/circuit.rs b/rln/src/circuit.rs index dfcbdaf..483bf01 100644 --- a/rln/src/circuit.rs +++ b/rln/src/circuit.rs @@ -51,6 +51,7 @@ fn fq_from_str(s: &str) -> Fq { .into() } +// Extracts the element in G1 corresponding to its JSON serialization fn json_to_g1(json: &Value, key: &str) -> G1Affine { let els: Vec = json .get(key) @@ -67,6 +68,7 @@ fn json_to_g1(json: &Value, key: &str) -> G1Affine { )) } +// Extracts the vector of G1 elements corresponding to its JSON serialization fn json_to_g1_vec(json: &Value, key: &str) -> Vec { let els: Vec> = json .get(key) @@ -94,6 +96,7 @@ fn json_to_g1_vec(json: &Value, key: &str) -> Vec { .collect() } +// Extracts the element in G2 corresponding to its JSON serialization fn json_to_g2(json: &Value, key: &str) -> G2Affine { let els: Vec> = json .get(key) @@ -116,6 +119,7 @@ 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 { let json = std::fs::read_to_string(vk_path).unwrap(); let json: Value = serde_json::from_str(&json).unwrap(); @@ -129,6 +133,7 @@ fn vk_from_json(vk_path: &str) -> VerifyingKey { } } +// Checks verification key to be correct with respect to proving key pub fn check_vk_from_zkey(verifying_key: VerifyingKey) { assert_eq!(ZKEY().vk, verifying_key); } diff --git a/rln/src/lib.rs b/rln/src/lib.rs index 3edff1b..5f16517 100644 --- a/rln/src/lib.rs +++ b/rln/src/lib.rs @@ -10,8 +10,7 @@ pub mod circuit; pub mod ffi; pub mod protocol; pub mod public; - -pub type Field = Fr; +pub mod utils; #[cfg(test)] mod test { @@ -55,21 +54,36 @@ mod test { // We check correct computation of the path and indexes let expected_path_elements = vec![ - "0", - "14744269619966411208579211824598458697587494354926760081771325075741142829156", - "7423237065226347324353380772367382631490014989348495481811164164159255474657", - "11286972368698509976183087595462810875513684078608517520839298933882497716792", - "3607627140608796879659380071776844901612302623152076817094415224584923813162", - "19712377064642672829441595136074946683621277828620209496774504837737984048981", - "20775607673010627194014556968476266066927294572720319469184847051418138353016", - "3396914609616007258851405644437304192397291162432396347162513310381425243293", - "21551820661461729022865262380882070649935529853313286572328683688269863701601", - "6573136701248752079028194407151022595060682063033565181951145966236778420039", - "12413880268183407374852357075976609371175688755676981206018884971008854919922", - "14271763308400718165336499097156975241954733520325982997864342600795471836726", - "20066985985293572387227381049700832219069292839614107140851619262827735677018", - "9394776414966240069580838672673694685292165040808226440647796406499139370960", - "11331146992410411304059858900317123658895005918277453009197229807340014528524", + Field::from_str("0x0000000000000000000000000000000000000000000000000000000000000000") + .unwrap(), + Field::from_str("0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864") + .unwrap(), + Field::from_str("0x1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1") + .unwrap(), + Field::from_str("0x18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238") + .unwrap(), + Field::from_str("0x07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a") + .unwrap(), + Field::from_str("0x2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55") + .unwrap(), + Field::from_str("0x2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78") + .unwrap(), + Field::from_str("0x078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d") + .unwrap(), + Field::from_str("0x2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61") + .unwrap(), + Field::from_str("0x0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747") + .unwrap(), + Field::from_str("0x1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2") + .unwrap(), + Field::from_str("0x1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636") + .unwrap(), + Field::from_str("0x2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a") + .unwrap(), + Field::from_str("0x14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0") + .unwrap(), + Field::from_str("0x190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c") + .unwrap(), ]; let expected_identity_path_index: Vec = @@ -82,6 +96,71 @@ mod test { assert!(tree.verify(id_commitment.into(), &merkle_proof)); } + #[test] + // We test a RLN proof generation and verification + fn test_witness_from_json() { + // From rln JSON witness + // Input generated with https://github.com/oskarth/zk-kit/commit/b6a872f7160c7c14e10a0ea40acab99cbb23c9a8 + let input_json_str = r#" + { + "identity_secret": "12825549237505733615964533204745049909430608936689388901883576945030025938736", + "path_elements": [ + "18622655742232062119094611065896226799484910997537830749762961454045300666333", + "20590447254980891299813706518821659736846425329007960381537122689749540452732", + "7423237065226347324353380772367382631490014989348495481811164164159255474657", + "11286972368698509976183087595462810875513684078608517520839298933882497716792", + "3607627140608796879659380071776844901612302623152076817094415224584923813162", + "19712377064642672829441595136074946683621277828620209496774504837737984048981", + "20775607673010627194014556968476266066927294572720319469184847051418138353016", + "3396914609616007258851405644437304192397291162432396347162513310381425243293", + "21551820661461729022865262380882070649935529853313286572328683688269863701601", + "6573136701248752079028194407151022595060682063033565181951145966236778420039", + "12413880268183407374852357075976609371175688755676981206018884971008854919922", + "14271763308400718165336499097156975241954733520325982997864342600795471836726", + "20066985985293572387227381049700832219069292839614107140851619262827735677018", + "9394776414966240069580838672673694685292165040808226440647796406499139370960", + "11331146992410411304059858900317123658895005918277453009197229807340014528524" + ], + "identity_path_index": [ + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "x": "8143228284048792769012135629627737459844825626241842423967352803501040982", + "epoch": "0x0000005b612540fc986b42322f8cb91c2273afad58ed006fdba0c97b4b16b12f", + "rln_identifier": "11412926387081627876309792396682864042420635853496105400039841573530884328439" + } + "#; + + // We generate all relevant keys + let proving_key = &ZKEY(); + let verification_key = &VK(); + let builder = CIRCOM(); + + // We compute witness from the json input example + let rln_witness = rln_witness_from_json(input_json_str); + + // Let's generate a zkSNARK proof + let (proof, inputs) = generate_proof(builder, proving_key, rln_witness).unwrap(); + + // Let's verify the proof + let verified = verify_proof(verification_key, proof, inputs); + + assert!(verified.unwrap()); + } + #[test] // We test a RLN proof generation and verification fn test_end_to_end() { diff --git a/rln/src/protocol.rs b/rln/src/protocol.rs index c7ce1cd..9c501e6 100644 --- a/rln/src/protocol.rs +++ b/rln/src/protocol.rs @@ -9,7 +9,7 @@ use ark_groth16::{ VerifyingKey, }; use ark_relations::r1cs::SynthesisError; -use ark_std::{rand::thread_rng, UniformRand}; +use ark_std::{rand::thread_rng, str::FromStr, UniformRand}; use color_eyre::Result; use ethers_core::utils::keccak256; use num_bigint::{BigInt, BigUint, ToBigInt}; @@ -25,24 +25,52 @@ use serde::{Deserialize, Serialize}; use std::time::Instant; use thiserror::Error; +pub use crate::utils::{str_to_field, vec_to_field, vec_to_fr}; + /////////////////////////////////////////////////////// // RLN Witness data structure and utility functions /////////////////////////////////////////////////////// #[derive(Debug, Deserialize)] pub struct RLNWitnessInput { - identity_secret: String, - path_elements: Vec, + identity_secret: Field, + path_elements: Vec, identity_path_index: Vec, - x: String, - epoch: String, - rln_identifier: String, + x: Field, + epoch: Field, + rln_identifier: Field, } pub fn rln_witness_from_json(input_json_str: &str) -> RLNWitnessInput { - let rln_witness: RLNWitnessInput = + let input_json: serde_json::Value = serde_json::from_str(input_json_str).expect("JSON was not well-formatted"); - rln_witness + + let identity_secret = str_to_field(input_json["identity_secret"].to_string(), 10); + + let mut path_elements: Vec = vec![]; + for v in input_json["path_elements"].as_array().unwrap().iter() { + path_elements.push(str_to_field(v.to_string(), 10)); + } + + let mut identity_path_index: Vec = vec![]; + for v in input_json["identity_path_index"].as_array().unwrap().iter() { + identity_path_index.push(v.as_u64().unwrap() as u8); + } + + let x = str_to_field(input_json["x"].to_string(), 10); + + let epoch = str_to_field(input_json["epoch"].to_string(), 16); + + let rln_identifier = str_to_field(input_json["rln_identifier"].to_string(), 10); + + RLNWitnessInput { + identity_secret, + path_elements, + identity_path_index, + x, + epoch, + rln_identifier, + } } pub fn rln_witness_from_values( @@ -55,16 +83,14 @@ pub fn rln_witness_from_values( let path_elements = get_path_elements(merkle_proof); let identity_path_index = get_identity_path_index(merkle_proof); - let rln_witness = RLNWitnessInput { - identity_secret: BigInt::from(identity_secret).to_str_radix(10), + RLNWitnessInput { + identity_secret, path_elements, identity_path_index, - x: BigInt::from(x).to_str_radix(10), - epoch: format!("{:#066x}", BigInt::from(epoch)), //We format it as a padded 32 bytes hex with leading 0x for compatibility with zk-kit - rln_identifier: BigInt::from(rln_identifier).to_str_radix(10), - }; - - rln_witness + x, + epoch, + rln_identifier, + } } /////////////////////////////////////////////////////// @@ -117,12 +143,12 @@ impl From for ArkProof> { /// Helper to merkle proof into a bigint vector /// TODO: we should create a From trait for this -pub fn get_path_elements(proof: &merkle_tree::Proof) -> Vec { +pub fn get_path_elements(proof: &merkle_tree::Proof) -> Vec { proof .0 .iter() .map(|x| match x { - Branch::Left(value) | Branch::Right(value) => BigInt::from(*value).to_str_radix(10), + Branch::Left(value) | Branch::Right(value) => *value, }) .collect() } @@ -180,44 +206,29 @@ pub fn generate_proof( mut builder: CircomBuilder, proving_key: &ProvingKey, rln_witness: RLNWitnessInput, -) -> Result<(Proof, Vec), ProofError> { +) -> Result<(Proof, Vec), ProofError> { let now = Instant::now(); - builder.push_input( - "identity_secret", - BigInt::parse_bytes(rln_witness.identity_secret.as_bytes(), 10).unwrap(), - ); + builder.push_input("identity_secret", BigInt::from(rln_witness.identity_secret)); for v in rln_witness.path_elements.iter() { - builder.push_input( - "path_elements", - BigInt::parse_bytes(v.as_bytes(), 10).unwrap(), - ); + builder.push_input("path_elements", BigInt::from(*v)); } for v in rln_witness.identity_path_index.iter() { builder.push_input("identity_path_index", BigInt::from(*v)); } - builder.push_input( - "x", - BigInt::parse_bytes(rln_witness.x.as_bytes(), 10).unwrap(), - ); + builder.push_input("x", BigInt::from(rln_witness.x)); - builder.push_input( - "epoch", - BigInt::parse_bytes(rln_witness.epoch.strip_prefix("0x").unwrap().as_bytes(), 16).unwrap(), - ); + builder.push_input("epoch", BigInt::from(rln_witness.epoch)); - builder.push_input( - "rln_identifier", - BigInt::parse_bytes(rln_witness.rln_identifier.as_bytes(), 10).unwrap(), - ); + builder.push_input("rln_identifier", BigInt::from(rln_witness.rln_identifier)); let circom = builder.build().unwrap(); // Get the populated instance of the circuit with the witness - let inputs = circom.get_public_inputs().unwrap(); + let inputs = vec_to_field(circom.get_public_inputs().unwrap()); println!("witness generation took: {:.2?}", now.elapsed()); @@ -249,12 +260,12 @@ pub fn generate_proof( pub fn verify_proof( verifying_key: &VerifyingKey, proof: Proof, - inputs: Vec, + inputs: Vec, ) -> Result { // Check that the proof is valid let pvk = prepare_verifying_key(verifying_key); let pr: ArkProof = proof.into(); - let verified = ark_verify_proof(&pvk, &pr, &inputs)?; + let verified = ark_verify_proof(&pvk, &pr, &vec_to_fr(inputs))?; Ok(verified) } diff --git a/rln/src/public.rs b/rln/src/public.rs index fba115f..fadbcb0 100644 --- a/rln/src/public.rs +++ b/rln/src/public.rs @@ -17,26 +17,18 @@ use serde::Deserialize; use serde_json; use std::io::{self, Read, Write}; +use crate::circuit::{CIRCOM, ZKEY}; +use crate::protocol; + // TODO Add Engine here? i.e. not // TODO Assuming we want to use IncrementalMerkleTree, figure out type/trait conversions // TODO Adopt to new protocol structure pub struct RLN { - circom: CircomCircuit, + circom: CircomCircuit, //Was CircomCircuit params: ProvingKey, tree: PoseidonTree, } -#[derive(Debug, Deserialize)] -//#[serde(rename_all = "camelCase")] -struct WitnessInput { - identity_secret: String, - path_elements: Vec, - identity_path_index: Vec, - x: String, - epoch: String, - rln_identifier: String, -} - // TODO Expand API to have better coverage of things needed impl RLN { @@ -47,19 +39,18 @@ impl RLN { let builder = CircomBuilder::new(cfg); - // create an empty instance for setting it up - let circom = builder.setup(); - - let mut rng = thread_rng(); - let params = generate_random_parameters::(circom, &mut rng).unwrap(); + let params = ZKEY(); let circom = builder.build().unwrap(); let inputs = circom.get_public_inputs().unwrap(); println!("Public inputs {:#?} ", inputs); + // We compute a default empty tree + // Probably better to pass it as parameter + let TREE_HEIGHT = 21; let leaf = Field::from(0); - let tree = PoseidonTree::new(21, leaf); + let tree = PoseidonTree::new(TREE_HEIGHT, leaf); RLN { circom, @@ -68,6 +59,18 @@ impl RLN { } } + pub fn set_tree(&self, _input_data: R) -> io::Result<()> { + //Implement leaf and deserialization + //let leaf = Leaf::deserialize(input_data).unwrap(); + + //returns H::Hash, which is a 256 bit hash value + //let root = self.tree.root(); + // TODO Return root as LE here + //root.write_le(&mut result_data)?; + //println!("NYI: root le write buffer {:#?}", root); + Ok(()) + } + /// returns current membership root /// * `root` is a scalar field element in 32 bytes pub fn get_root(&self, _result_data: W) -> io::Result<()> { diff --git a/rln/src/utils.rs b/rln/src/utils.rs new file mode 100644 index 0000000..b649b6e --- /dev/null +++ b/rln/src/utils.rs @@ -0,0 +1,68 @@ +use ark_bn254::{Bn254, Fr, Parameters}; +use ark_ff::{Fp256, PrimeField}; +use ark_std::str::FromStr; +use ethers_core::utils::keccak256; +use num_bigint::{BigInt, BigUint, ToBigInt}; +use semaphore::{identity::Identity, Field}; + +pub fn to_fr(el: Field) -> Fr { + Fr::try_from(el).unwrap() +} + +pub fn to_field(el: Fr) -> Field { + el.try_into().unwrap() +} + +pub fn vec_to_fr(v: Vec) -> Vec { + let mut result: Vec = vec![]; + for el in v { + result.push(to_fr(el)); + } + result +} + +pub fn vec_to_field(v: Vec) -> Vec { + let mut result: Vec = vec![]; + for el in v { + result.push(to_field(el)); + } + result +} + +pub fn str_to_field(input: String, radix: i32) -> Field { + assert!((radix == 10) || (radix == 16)); + + // We remove any quote present and we trim + let input_clean = input.replace("\"", ""); + let input_clean = input_clean.trim(); + + if radix == 10 { + Field::from_str(&format!( + "{:01$x}", + BigUint::from_str(input_clean).unwrap(), + 64 + )) + .unwrap() + } else { + let input_clean = input_clean.replace("0x", ""); + Field::from_str(&format!("{:0>64}", &input_clean)).unwrap() + } +} + +// Arithmetic over Field elements (wrapped over arkworks algebra crate) + +pub fn add(a: Field, b: Field) -> Field { + to_field(to_fr(a) + to_fr(b)) +} + +pub fn mul(a: Field, b: Field) -> Field { + to_field(to_fr(a) * to_fr(b)) +} + +pub fn div(a: Field, b: Field) -> Field { + to_field(to_fr(a) / to_fr(b)) +} + +pub fn inv(a: Field) -> Field { + to_field(Fr::from(1) / to_fr(a)) +}