diff --git a/rln/src/circuit.rs b/rln/src/circuit.rs index 4dadf39..dfcbdaf 100644 --- a/rln/src/circuit.rs +++ b/rln/src/circuit.rs @@ -1,20 +1,16 @@ -use ark_bn254::{Bn254, Fq, Fq2, Fr, G1Affine, G2Affine}; -use ark_bn254::{G1Projective, G2Projective}; +use ark_bn254::{Bn254, Fq, Fq2, Fr, G1Affine, G1Projective, G2Affine, G2Projective}; use ark_circom::{read_zkey, CircomBuilder, CircomConfig, WitnessCalculator}; use ark_ff::BigInteger256; -/// Adapted from semaphore-rs use ark_groth16::{ProvingKey, VerifyingKey}; use ark_relations::r1cs::ConstraintMatrices; use core::include_bytes; use num_bigint::BigUint; -use once_cell::sync::Lazy; use serde_json::Value; use std::convert::TryFrom; use std::fs::File; use std::io::{Cursor, Write}; use std::path::Path; use std::str::FromStr; -use tempfile::NamedTempFile; const ZKEY_PATH: &str = "./resources/rln_final.zkey"; const VK_PATH: &str = "./resources/verifying_key.json"; @@ -23,19 +19,19 @@ const WASM_PATH: &str = "./resources/rln.wasm"; pub fn ZKEY() -> ProvingKey /*, ConstraintMatrices)*/ { let mut file = File::open(ZKEY_PATH).unwrap(); - let (provingKey, _matrices) = read_zkey(&mut file).unwrap(); - provingKey + let (proving_key, _matrices) = read_zkey(&mut file).unwrap(); + proving_key } pub fn VK() -> VerifyingKey { - let verifyingKey: VerifyingKey; + let verifying_key: VerifyingKey; if Path::new(VK_PATH).exists() { - let verifyingKey = vk_from_json(VK_PATH); - verifyingKey + verifying_key = vk_from_json(VK_PATH); + verifying_key } else if Path::new(ZKEY_PATH).exists() { - verifyingKey = ZKEY().vk; - verifyingKey + verifying_key = ZKEY().vk; + verifying_key } else { panic!("No proving/verification key present!"); } @@ -44,11 +40,8 @@ pub fn VK() -> VerifyingKey { pub fn CIRCOM() -> CircomBuilder { // Load the WASM and R1CS for witness and proof generation let cfg = CircomConfig::::new(WASM_PATH, R1CS_PATH).unwrap(); // should be )?; but need to address "the trait `From` is not implemented for `protocol::ProofError`" - - // We build the circuit - let builder = CircomBuilder::new(cfg); - - builder + // We build and return the circuit + CircomBuilder::new(cfg) } // Utilities to convert a json verification key in a groth16::VerificationKey @@ -127,30 +120,15 @@ 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(); - let vk = VerifyingKey { + 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"), - }; - - return vk; + } } -pub fn check_vk_from_zkey(verifyingKey: VerifyingKey) { - assert_eq!(ZKEY().vk, verifyingKey); +pub fn check_vk_from_zkey(verifying_key: VerifyingKey) { + assert_eq!(ZKEY().vk, verifying_key); } - -// Not sure this is still useful... -const WASM: &[u8] = include_bytes!("../resources/rln.wasm"); -pub static WITNESS_CALCULATOR: Lazy = Lazy::new(|| { - // HACK: ark-circom requires a file, so we make one! - let mut tmpfile = NamedTempFile::new().expect("Failed to create temp file"); - let written = tmpfile.write(WASM).expect("Failed to write to temp file"); - assert_eq!(written, WASM.len()); - let path = tmpfile.into_temp_path(); - let result = WitnessCalculator::new(&path).expect("Failed to create witness calculator"); - path.close().expect("Could not remove tempfile"); - result -}); diff --git a/rln/src/lib.rs b/rln/src/lib.rs index 3b71b39..3edff1b 100644 --- a/rln/src/lib.rs +++ b/rln/src/lib.rs @@ -1,20 +1,17 @@ #![allow(dead_code)] #![allow(unused_imports)] -pub mod ffi; -pub mod public; - +use crate::circuit::{CIRCOM, VK, ZKEY}; use ark_bn254::{Fr, Parameters}; use ark_ec::bn::Bn; +use ark_std::str::FromStr; pub mod circuit; +pub mod ffi; pub mod protocol; +pub mod public; pub type Field = Fr; -pub type Groth16Proof = ark_groth16::Proof>; -pub type EthereumGroth16Proof = ark_circom::ethereum::Proof; - -use crate::circuit::{CIRCOM, VK, ZKEY}; #[cfg(test)] mod test { @@ -28,68 +25,68 @@ mod test { }; #[test] + // We test Merkle Tree generation, proofs and verification fn test_merkle_proof() { - let leaf = Field::from(0); + let tree_height = 16; + let leaf_index = 3; // generate identity - let id = Identity::from_seed(b"hello"); + // We follow zk-kit approach for identity generation + let id = Identity::from_seed(b"test-merkle-proof"); + let identity_secret = poseidon_hash(&vec![id.trapdoor, id.nullifier]); + let id_commitment = poseidon_hash(&vec![identity_secret]); // generate merkle tree - let mut tree = PoseidonTree::new(21, leaf); - tree.set(0, id.commitment()); + let default_leaf = Field::from(0); + let mut tree = PoseidonTree::new(tree_height, default_leaf); + tree.set(leaf_index, id_commitment.into()); - let merkle_proof = tree.proof(0).expect("proof should exist"); - let root: Field = tree.root().into(); - - println!("Root: {:#}", root); - println!("Merkle proof: {:#?}", merkle_proof); - } - - #[test] - fn test_semaphore() { - let leaf = Field::from(0); - - // generate identity - let id = Identity::from_seed(b"hello"); - - // generate merkle tree - let mut tree = PoseidonTree::new(21, leaf); - tree.set(0, id.commitment()); - - let merkle_proof = tree.proof(0).expect("proof should exist"); - let root = tree.root().into(); - - // change signal_hash and external_nullifier here - let signal_hash = hash_to_field(b"xxx"); - let external_nullifier_hash = hash_to_field(b"appId"); - - let nullifier_hash = - semaphore::protocol::generate_nullifier_hash(&id, external_nullifier_hash); - - let proof = semaphore::protocol::generate_proof( - &id, - &merkle_proof, - external_nullifier_hash, - signal_hash, - ) - .unwrap(); - - let success = semaphore::protocol::verify_proof( + // We check correct computation of the root + let root = tree.root(); + assert_eq!( root, - nullifier_hash, - signal_hash, - external_nullifier_hash, - &proof, - ) - .unwrap(); + Field::from_str("0x27401a4559ce263630907ce3b77c570649e28ede22d2a7f5296839627a16e870") + .unwrap() + ); - assert!(success); + let merkle_proof = tree.proof(leaf_index).expect("proof should exist"); + let path_elements = get_path_elements(&merkle_proof); + let identity_path_index = get_identity_path_index(&merkle_proof); + + // 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", + ]; + + let expected_identity_path_index: Vec = + vec![1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + + assert_eq!(path_elements, expected_path_elements); + assert_eq!(identity_path_index, expected_identity_path_index); + + // We check correct verification of the proof + assert!(tree.verify(id_commitment.into(), &merkle_proof)); } #[test] + // We test a RLN proof generation and verification fn test_end_to_end() { - let TREE_HEIGHT = 16; - let leafIndex = 3; + let tree_height = 16; + let leaf_index = 3; // Generate identity // We follow zk-kit approach for identity generation @@ -98,11 +95,11 @@ mod test { let id_commitment = poseidon_hash(&vec![identity_secret]); //// generate merkle tree - let leaf = Field::from(0); - let mut tree = PoseidonTree::new(TREE_HEIGHT, leaf); - tree.set(leafIndex, id_commitment.into()); + let default_leaf = Field::from(0); + let mut tree = PoseidonTree::new(tree_height, default_leaf); + tree.set(leaf_index, id_commitment.into()); - let merkle_proof = tree.proof(leafIndex).expect("proof should exist"); + let merkle_proof = tree.proof(leaf_index).expect("proof should exist"); let signal = b"hey hey"; let x = hash_to_field(signal); @@ -111,39 +108,20 @@ mod test { let epoch = hash_to_field(b"test-epoch"); let rln_identifier = hash_to_field(b"test-rln-identifier"); - let rlnWitness: RLNWitnessInput = - initRLNWitnessFromValues(identity_secret, &merkle_proof, x, epoch, rln_identifier); - - println!("rlnWitness: {:#?}", rlnWitness); + let rln_witness: RLNWitnessInput = + rln_witness_from_values(identity_secret, &merkle_proof, x, epoch, rln_identifier); // We generate all relevant keys - let provingKey = &ZKEY(); - let verificationKey = &VK(); + let proving_key = &ZKEY(); + let verification_key = &VK(); let builder = CIRCOM(); // Let's generate a zkSNARK proof - let (proof, inputs) = generate_proof(builder, provingKey, rlnWitness).unwrap(); + let (proof, inputs) = generate_proof(builder, proving_key, rln_witness).unwrap(); // Let's verify the proof - let success = verify_proof(verificationKey, proof, inputs).unwrap(); + let success = verify_proof(verification_key, proof, inputs).unwrap(); assert!(success); } - - //to_str_radix(10); - - // - //// change signal_hash and external_nullifier_hash here - //let signal_hash = hash_to_field(b"xxx"); - //let external_nullifier_hash = hash_to_field(b"appId"); - // - //let nullifier_hash = generate_nullifier_hash(&id, external_nullifier_hash); - // - // - //// We generate all relevant keys - //let provingKey = &ZKEY(); - //let verificationKey = &VK(); - //let mut builder = CIRCOM(); - - //println!("Proof: {:#?}", proof); } diff --git a/rln/src/main.rs b/rln/src/main.rs index 2063c88..b23a0be 100644 --- a/rln/src/main.rs +++ b/rln/src/main.rs @@ -1,19 +1,13 @@ - - use color_eyre::Result; - - - // Tracing use ark_relations::r1cs::{ConstraintLayer, ConstraintTrace, TracingMode}; use tracing_subscriber::layer::SubscriberExt; // JSON - use rln::circuit::{CIRCOM, VK, ZKEY}; -use rln::protocol::{generate_proof, initRLNWitnessFromJSON, verify_proof}; +use rln::protocol::{generate_proof, rln_witness_from_json, verify_proof}; // RLN fn groth16_proof_example() -> Result<()> { @@ -72,18 +66,18 @@ fn groth16_proof_example() -> Result<()> { "#; // We generate all relevant keys - let provingKey = &ZKEY(); - let verificationKey = &VK(); + let proving_key = &ZKEY(); + let verification_key = &VK(); let builder = CIRCOM(); // We compute witness from the json input example - let rlnWitness = initRLNWitnessFromJSON(input_json_str); + let rln_witness = rln_witness_from_json(input_json_str); // Let's generate a zkSNARK proof - let (proof, inputs) = generate_proof(builder, provingKey, rlnWitness).unwrap(); + let (proof, inputs) = generate_proof(builder, proving_key, rln_witness).unwrap(); // Let's verify the proof - let verified = verify_proof(verificationKey, proof, inputs); + let verified = verify_proof(verification_key, proof, inputs); assert!(verified.unwrap()); diff --git a/rln/src/protocol.rs b/rln/src/protocol.rs index c3d48a4..c7ce1cd 100644 --- a/rln/src/protocol.rs +++ b/rln/src/protocol.rs @@ -1,6 +1,6 @@ -/// Adapted from semaphore-rs -use crate::circuit::{VK, WITNESS_CALCULATOR, ZKEY}; +use crate::circuit::{VK, ZKEY}; use ark_bn254::{Bn254, Fr, Parameters}; +use ark_circom::{read_zkey, CircomBuilder, CircomConfig, CircomReduction}; use ark_ec::bn::Bn; use ark_ff::{Fp256, PrimeField}; use ark_groth16::{ @@ -25,7 +25,9 @@ use serde::{Deserialize, Serialize}; use std::time::Instant; use thiserror::Error; -use ark_circom::{read_zkey, CircomBuilder, CircomConfig, CircomReduction}; +/////////////////////////////////////////////////////// +// RLN Witness data structure and utility functions +/////////////////////////////////////////////////////// #[derive(Debug, Deserialize)] pub struct RLNWitnessInput { @@ -37,36 +39,37 @@ pub struct RLNWitnessInput { rln_identifier: String, } -pub fn initRLNWitnessFromJSON(input_json_str: &str) -> RLNWitnessInput { - let rlnWitness: RLNWitnessInput = - serde_json::from_str(&input_json_str).expect("JSON was not well-formatted"); - return rlnWitness; +pub fn rln_witness_from_json(input_json_str: &str) -> RLNWitnessInput { + let rln_witness: RLNWitnessInput = + serde_json::from_str(input_json_str).expect("JSON was not well-formatted"); + rln_witness } -pub fn initRLNWitnessFromValues( +pub fn rln_witness_from_values( identity_secret: Field, merkle_proof: &merkle_tree::Proof, x: Field, epoch: Field, rln_identifier: Field, ) -> RLNWitnessInput { - //println!("Merkle proof: {:#?}", merkle_proof); - let path_elements = getPathElements(merkle_proof); - let identity_path_index = getIdentityPathIndex(merkle_proof); + let path_elements = get_path_elements(merkle_proof); + let identity_path_index = get_identity_path_index(merkle_proof); - let rlnWitness = RLNWitnessInput { + let rln_witness = RLNWitnessInput { identity_secret: BigInt::from(identity_secret).to_str_radix(10), - path_elements: path_elements, - identity_path_index: identity_path_index, + 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), }; - return rlnWitness; + rln_witness } -// TODO Fields need to be updated to RLN based ones +/////////////////////////////////////////////////////// +// Proof data structure and utility functions +/////////////////////////////////////////////////////// // Matches the private G1Tup type in ark-circom. pub type G1 = (U256, U256); @@ -108,9 +111,13 @@ impl From for ArkProof> { } } +/////////////////////////////////////////////////////// +// Merkle tree utility functions +/////////////////////////////////////////////////////// + /// Helper to merkle proof into a bigint vector /// TODO: we should create a From trait for this -pub fn getPathElements(proof: &merkle_tree::Proof) -> Vec { +pub fn get_path_elements(proof: &merkle_tree::Proof) -> Vec { proof .0 .iter() @@ -120,7 +127,7 @@ pub fn getPathElements(proof: &merkle_tree::Proof) -> Vec .collect() } -pub fn getIdentityPathIndex(proof: &merkle_tree::Proof) -> Vec { +pub fn get_identity_path_index(proof: &merkle_tree::Proof) -> Vec { proof .0 .iter() @@ -131,6 +138,10 @@ pub fn getIdentityPathIndex(proof: &merkle_tree::Proof) -> Vec .collect() } +/////////////////////////////////////////////////////// +// Signal/nullifier utility functions +/////////////////////////////////////////////////////// + /// Internal helper to hash the signal to make sure it's in the field fn hash_signal(signal: &[u8]) -> Field { let hash = keccak256(signal); @@ -146,6 +157,10 @@ pub fn generate_nullifier_hash(identity: &Identity, external_nullifier: Field) - poseidon_hash(&[external_nullifier, identity.nullifier]) } +/////////////////////////////////////////////////////// +// zkSNARK utility functions +/////////////////////////////////////////////////////// + #[derive(Error, Debug)] pub enum ProofError { #[error("Error reading circuit key: {0}")] @@ -232,12 +247,12 @@ pub fn generate_proof( /// Returns a [`ProofError`] if verifying fails. Verification failure does not /// necessarily mean the proof is incorrect. pub fn verify_proof( - verifyingKey: &VerifyingKey, + verifying_key: &VerifyingKey, proof: Proof, inputs: Vec, ) -> Result { // Check that the proof is valid - let pvk = prepare_verifying_key(verifyingKey); + let pvk = prepare_verifying_key(verifying_key); let pr: ArkProof = proof.into(); let verified = ark_verify_proof(&pvk, &pr, &inputs)?; diff --git a/rln/src/public.rs b/rln/src/public.rs index cb33cec..fba115f 100644 --- a/rln/src/public.rs +++ b/rln/src/public.rs @@ -1,32 +1,21 @@ /// This is the main public API for RLN. It is used by the FFI, and should be /// used by tests etc as well /// -use semaphore::{ - hash_to_field, identity::Identity, poseidon_tree::PoseidonTree, protocol::*, Field, -}; - -use ark_circom::{CircomBuilder, CircomCircuit, CircomConfig}; -use ark_std::rand::thread_rng; - use ark_bn254::Bn254; +use ark_circom::{CircomBuilder, CircomCircuit, CircomConfig}; use ark_groth16::{ create_random_proof as prove, generate_random_parameters, prepare_verifying_key, verify_proof, Proof, ProvingKey, }; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; -// , SerializationError}; - -use std::io::{self, Read, Write}; - +use ark_std::rand::thread_rng; use num_bigint::BigInt; - -// JSON +use semaphore::{ + hash_to_field, identity::Identity, poseidon_tree::PoseidonTree, protocol::*, Field, +}; use serde::Deserialize; use serde_json; - -// For RLN Rust version -//use bellman::pairing::ff::{Field, PrimeField, PrimeFieldRepr, ScalarEngine}; -//use sapling_crypto::bellman::pairing::bn256::Bn256; +use std::io::{self, Read, Write}; // TODO Add Engine here? i.e. not // TODO Assuming we want to use IncrementalMerkleTree, figure out type/trait conversions @@ -34,8 +23,6 @@ use serde_json; pub struct RLN { circom: CircomCircuit, params: ProvingKey, - // RLN Rust version - //tree: IncrementalMerkleTree, tree: PoseidonTree, } @@ -81,131 +68,6 @@ impl RLN { } } - // XXX This is a tempory hack to get end to end proving/verification working - // Not supposed to be part of public API - pub fn new_json_spike() -> RLN { - let cfg = - CircomConfig::::new("./resources/rln.wasm", "./resources/rln.r1cs").unwrap(); - - // TODO Refactor - // 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" - } -"#; - - let witness_input: WitnessInput = - serde_json::from_str(input_json_str).expect("JSON was not well-formatted"); - - println!("Witness input JSON: {:?}", witness_input); - - let mut builder = CircomBuilder::new(cfg); - - builder.push_input( - "identity_secret", - BigInt::parse_bytes(witness_input.identity_secret.as_bytes(), 10).unwrap(), - ); - - for v in witness_input.path_elements.iter() { - builder.push_input( - "path_elements", - BigInt::parse_bytes(v.as_bytes(), 10).unwrap(), - ); - } - - for v in witness_input.identity_path_index.iter() { - builder.push_input("identity_path_index", BigInt::from(*v)); - } - - builder.push_input( - "x", - BigInt::parse_bytes(witness_input.x.as_bytes(), 10).unwrap(), - ); - - builder.push_input( - "epoch", - BigInt::parse_bytes( - witness_input.epoch.strip_prefix("0x").unwrap().as_bytes(), - 16, - ) - .unwrap(), - ); - - builder.push_input( - "rln_identifier", - BigInt::parse_bytes(witness_input.rln_identifier.as_bytes(), 10).unwrap(), - ); - - println!("Builder input:\n {:#?}", builder.inputs); - - // 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 circom = builder.build().unwrap(); - - let inputs = circom.get_public_inputs().unwrap(); - - println!("Public inputs {:#?} ", inputs); - - // Sapling based tree - // // TODO Add as parameter(s) - // let merkle_depth: usize = 3; - // let poseidon_params = PoseidonParams::::new(8, 55, 3, None, None, None); - // let hasher = PoseidonHasher::new(poseidon_params.clone()); - // let tree = IncrementalMerkleTree::empty(hasher, merkle_depth); - - let leaf = Field::from(0); - let tree = PoseidonTree::new(21, leaf); - - RLN { - circom, - params, - tree, - } - } - /// returns current membership root /// * `root` is a scalar field element in 32 bytes pub fn get_root(&self, _result_data: W) -> io::Result<()> { @@ -260,26 +122,3 @@ impl Default for RLN { Self::new() } } - -// NOTE: Expensive test, ignoring by default -#[ignore] -#[test] -fn rln_proof() { - let rln = RLN::new(); - let rln_spike = RLN::new_json_spike(); - //let inputs = mul.circom.get_public_inputs().unwrap(); - - let mut output_data: Vec = Vec::new(); - let _ = rln_spike.prove(&mut output_data); - - let proof_data = &output_data[..]; - - // XXX Pass as arg? - //let pvk = prepare_verifying_key(&mul.params.vk); - - // XXX: Something is wrong here I think, because it doesn't verify with the - // full proof fields like yShare - just witness? Might be a bug - let verified = rln.verify(proof_data).unwrap(); - - assert!(verified); -}