From dd87e3d5b7c5c4cdec7e985c4b3cdd9e7fba8c6e Mon Sep 17 00:00:00 2001 From: arnaucube Date: Wed, 25 Sep 2024 13:41:34 +0200 Subject: [PATCH 1/3] update to latest plonky2 version --- Cargo.toml | 4 ++-- src/access_set.rs | 4 ++-- src/circuit.rs | 22 ++++++++++++---------- src/recursion.rs | 30 +++++++++++++++++------------- src/signal.rs | 6 +++--- 5 files changed, 36 insertions(+), 30 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6cb5a77..ebcaeb9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,5 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -plonky2 = { git = "https://github.com/mir-protocol/plonky2", branch = "semaphore-example" } -anyhow = "1.0.56" \ No newline at end of file +plonky2 = { git = "https://github.com/mir-protocol/plonky2" } +anyhow = "1.0.56" diff --git a/src/access_set.rs b/src/access_set.rs index ed42fa9..58f4394 100644 --- a/src/access_set.rs +++ b/src/access_set.rs @@ -46,7 +46,7 @@ impl AccessSet { let mut pw = PartialWitness::new(); let targets = self.semaphore_circuit(&mut builder); - self.fill_semaphore_targets(&mut pw, private_key, topic, public_key_index, targets); + self.fill_semaphore_targets(&mut pw, private_key, topic, public_key_index, targets)?; let data = builder.build(); let proof = data.prove(pw)?; @@ -56,7 +56,7 @@ impl AccessSet { nullifier, proof: proof.proof, }, - data.to_verifier_data(), + data.verifier_data(), )) } } diff --git a/src/circuit.rs b/src/circuit.rs index f6a7258..f4f6941 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -1,9 +1,10 @@ -use plonky2::field::field_types::Field; -use plonky2::hash::hash_types::{HashOutTarget, MerkleCapTarget}; +use anyhow::Result; +use plonky2::field::types::Field; +use plonky2::hash::hash_types::HashOutTarget; use plonky2::hash::merkle_proofs::MerkleProofTarget; use plonky2::hash::poseidon::PoseidonHash; use plonky2::iop::target::Target; -use plonky2::iop::witness::{PartialWitness, Witness}; +use plonky2::iop::witness::{PartialWitness, WitnessWrite}; use plonky2::plonk::circuit_builder::CircuitBuilder; use crate::access_set::AccessSet; @@ -44,7 +45,7 @@ impl AccessSet { builder.verify_merkle_proof::( [private_key, [zero; 4]].concat(), &public_key_index_bits, - &MerkleCapTarget(vec![merkle_root]), + merkle_root, &merkle_proof, ); @@ -71,7 +72,7 @@ impl AccessSet { topic: Digest, public_key_index: usize, targets: SemaphoreTargets, - ) { + ) -> Result<()> { let SemaphoreTargets { merkle_root, topic: topic_target, @@ -80,13 +81,13 @@ impl AccessSet { public_key_index: public_key_index_target, } = targets; - pw.set_hash_target(merkle_root, self.0.cap.0[0]); - pw.set_targets(&private_key_target, &private_key); - pw.set_targets(&topic_target, &topic); + pw.set_hash_target(merkle_root, self.0.cap.0[0])?; + pw.set_target_arr(&private_key_target, &private_key)?; + pw.set_target_arr(&topic_target, &topic)?; pw.set_target( public_key_index_target, F::from_canonical_usize(public_key_index), - ); + )?; let merkle_proof = self.0.prove(public_key_index); for (ht, h) in merkle_proof_target @@ -94,7 +95,8 @@ impl AccessSet { .into_iter() .zip(merkle_proof.siblings) { - pw.set_hash_target(ht, h); + pw.set_hash_target(ht, h)?; } + Ok(()) } } diff --git a/src/recursion.rs b/src/recursion.rs index e90a478..bf0e836 100644 --- a/src/recursion.rs +++ b/src/recursion.rs @@ -1,6 +1,7 @@ -use plonky2::iop::witness::{PartialWitness, Witness}; +use anyhow::Result; +use plonky2::iop::witness::{PartialWitness, WitnessWrite}; use plonky2::plonk::circuit_builder::CircuitBuilder; -use plonky2::plonk::circuit_data::{CircuitConfig, VerifierCircuitData, VerifierCircuitTarget}; +use plonky2::plonk::circuit_data::{CircuitConfig, VerifierCircuitData}; use plonky2::plonk::proof::ProofWithPublicInputs; use crate::access_set::AccessSet; @@ -14,7 +15,7 @@ impl AccessSet { topic1: Digest, signal1: Signal, verifier_data: &VerifierCircuitData, - ) -> (Digest, Digest, PlonkyProof) { + ) -> Result<(Digest, Digest, PlonkyProof, VerifierCircuitData)> { let config = CircuitConfig::standard_recursion_zk_config(); let mut builder = CircuitBuilder::new(config); let mut pw = PartialWitness::new(); @@ -45,7 +46,7 @@ impl AccessSet { proof: signal0.proof, public_inputs: public_inputs0, }, - ); + )?; let proof_target1 = builder.add_virtual_proof_with_pis(&verifier_data.common); pw.set_proof_with_pis_target( &proof_target1, @@ -53,25 +54,28 @@ impl AccessSet { proof: signal1.proof, public_inputs: public_inputs1, }, - ); + )?; - let vd_target = VerifierCircuitTarget { - constants_sigmas_cap: builder - .add_virtual_cap(verifier_data.common.config.fri_config.cap_height), - }; + let vd_target = + builder.add_virtual_verifier_data(verifier_data.common.config.fri_config.cap_height); pw.set_cap_target( &vd_target.constants_sigmas_cap, &verifier_data.verifier_only.constants_sigmas_cap, - ); + )?; - builder.verify_proof(proof_target0, &vd_target, &verifier_data.common); - builder.verify_proof(proof_target1, &vd_target, &verifier_data.common); + builder.verify_proof::(&proof_target0, &vd_target, &verifier_data.common); + builder.verify_proof::(&proof_target1, &vd_target, &verifier_data.common); let data = builder.build(); let recursive_proof = data.prove(pw).unwrap(); data.verify(recursive_proof.clone()).unwrap(); - (signal0.nullifier, signal1.nullifier, recursive_proof.proof) + Ok(( + signal0.nullifier, + signal1.nullifier, + recursive_proof.proof, + data.verifier_data(), + )) } } diff --git a/src/signal.rs b/src/signal.rs index 846940e..e5681bc 100644 --- a/src/signal.rs +++ b/src/signal.rs @@ -16,7 +16,7 @@ pub struct Signal { #[cfg(test)] mod tests { use anyhow::Result; - use plonky2::field::field_types::Field; + use plonky2::field::types::{Field, Sample}; use plonky2::hash::merkle_tree::MerkleTree; use plonky2::hash::poseidon::PoseidonHash; use plonky2::plonk::config::Hasher; @@ -27,7 +27,7 @@ mod tests { #[test] fn test_semaphore() -> Result<()> { let n = 1 << 20; - let private_keys: Vec = (0..n).map(|_| F::rand_arr()).collect(); + let private_keys: Vec = (0..n).map(|_| F::rand_array()).collect(); let public_keys: Vec> = private_keys .iter() .map(|&sk| { @@ -39,7 +39,7 @@ mod tests { let access_set = AccessSet(MerkleTree::new(public_keys, 0)); let i = 12; - let topic = F::rand_arr(); + let topic = F::rand_array(); let (signal, vd) = access_set.make_signal(private_keys[i], topic, i)?; access_set.verify_signal(topic, signal, &vd) From dc9c32c93e5076e833ac8876afd3a75c511d6ada Mon Sep 17 00:00:00 2001 From: arnaucube Date: Fri, 27 Sep 2024 07:37:18 +0200 Subject: [PATCH 2/3] Update aggregate_signals, Add test_recursion --- src/recursion.rs | 86 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 3 deletions(-) diff --git a/src/recursion.rs b/src/recursion.rs index bf0e836..3e5d3f7 100644 --- a/src/recursion.rs +++ b/src/recursion.rs @@ -40,6 +40,7 @@ impl AccessSet { .collect(); let proof_target0 = builder.add_virtual_proof_with_pis(&verifier_data.common); + builder.register_public_inputs(&proof_target0.public_inputs); pw.set_proof_with_pis_target( &proof_target0, &ProofWithPublicInputs { @@ -47,7 +48,12 @@ impl AccessSet { public_inputs: public_inputs0, }, )?; + let vd_target = + builder.add_virtual_verifier_data(verifier_data.common.config.fri_config.cap_height); + pw.set_verifier_data_target(&vd_target, &verifier_data.verifier_only)?; + let proof_target1 = builder.add_virtual_proof_with_pis(&verifier_data.common); + builder.register_public_inputs(&proof_target1.public_inputs); pw.set_proof_with_pis_target( &proof_target1, &ProofWithPublicInputs { @@ -55,9 +61,10 @@ impl AccessSet { public_inputs: public_inputs1, }, )?; - let vd_target = builder.add_virtual_verifier_data(verifier_data.common.config.fri_config.cap_height); + pw.set_verifier_data_target(&vd_target, &verifier_data.verifier_only)?; + pw.set_cap_target( &vd_target.constants_sigmas_cap, &verifier_data.verifier_only.constants_sigmas_cap, @@ -67,9 +74,9 @@ impl AccessSet { builder.verify_proof::(&proof_target1, &vd_target, &verifier_data.common); let data = builder.build(); - let recursive_proof = data.prove(pw).unwrap(); + let recursive_proof = data.prove(pw)?; - data.verify(recursive_proof.clone()).unwrap(); + data.verify(recursive_proof.clone())?; Ok(( signal0.nullifier, @@ -79,3 +86,76 @@ impl AccessSet { )) } } + +#[cfg(test)] +mod tests { + use anyhow::Result; + use plonky2::field::types::{Field, Sample}; + use plonky2::hash::merkle_tree::MerkleTree; + use plonky2::hash::poseidon::PoseidonHash; + use plonky2::plonk::config::Hasher; + use plonky2::plonk::proof::ProofWithPublicInputs; + use std::time::Instant; + + use crate::access_set::AccessSet; + use crate::signal::{Digest, F}; + + #[test] + fn test_recursion() -> Result<()> { + let n = 1 << 20; + let private_keys: Vec = (0..n).map(|_| F::rand_array()).collect(); + let public_keys: Vec> = private_keys + .iter() + .map(|&sk| { + PoseidonHash::hash_no_pad(&[sk, [F::ZERO; 4]].concat()) + .elements + .to_vec() + }) + .collect(); + let access_set = AccessSet(MerkleTree::new(public_keys, 0)); + + // first proof + let i0 = 12; + let topic0 = F::rand_array(); + let start = Instant::now(); + let (signal0, vd0) = access_set.make_signal(private_keys[i0], topic0, i0)?; + println!("generate proof: {:?}", start.elapsed()); + access_set.verify_signal(topic0, signal0.clone(), &vd0)?; + + // second proof + let i1 = 42; + let topic1 = F::rand_array(); + let start = Instant::now(); + let (signal1, vd1) = access_set.make_signal(private_keys[i1], topic1, i1)?; + println!("generate proof: {:?}", start.elapsed()); + access_set.verify_signal(topic1, signal1.clone(), &vd1)?; + + // generate recursive proof + let start = Instant::now(); + let (nullifier0, nullifier1, recursive_proof, vd2) = + access_set.aggregate_signals(topic0, signal0, topic1, signal1, &vd0)?; + println!("aggregate_signals (recursive prove): {:?}", start.elapsed()); + + // verify recursive proof + let public_inputs: Vec = access_set + .0 + .cap + .0 + .iter() + .flat_map(|h| h.elements) + .chain(nullifier0) + .chain(topic0) + .chain(access_set.0.cap.0.iter().flat_map(|h| h.elements)) + .chain(nullifier1) + .chain(topic1) + .collect(); + + let start = Instant::now(); + vd2.verify(ProofWithPublicInputs { + proof: recursive_proof, + public_inputs, + })?; + println!("verify recursive proof: {:?}", start.elapsed()); + Ok(()) + } +} From 5411adb208043a438ca97b7f165a77600900b6ce Mon Sep 17 00:00:00 2001 From: arnaucube Date: Fri, 27 Sep 2024 07:53:11 +0200 Subject: [PATCH 3/3] Add inline comments describing the usage of methods and update the Semaphore link to the current one. --- README.md | 4 ++-- src/access_set.rs | 2 ++ src/circuit.rs | 15 +++++++++++++++ src/recursion.rs | 16 +++++++--------- src/signal.rs | 2 ++ 5 files changed, 28 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 7d67453..ca1afe4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Plonky2 implementation of the [Semaphore protocol](http://semaphore.appliedzkp.org/) +# Plonky2 implementation of the [Semaphore protocol](https://semaphore.pse.dev) Used as an example in the ZKHack Plonky2 presentation. @@ -6,4 +6,4 @@ Used as an example in the ZKHack Plonky2 presentation. ```bash rustup override set nightly # Requires nightly Rust cargo test --release -``` \ No newline at end of file +``` diff --git a/src/access_set.rs b/src/access_set.rs index 58f4394..e555b24 100644 --- a/src/access_set.rs +++ b/src/access_set.rs @@ -12,6 +12,7 @@ use crate::signal::{Digest, Signal, C, F}; pub struct AccessSet(pub MerkleTree); impl AccessSet { + // Verify the plonky2 proof of the given nullifier (in the signal structure) and topic. pub fn verify_signal( &self, topic: Digest, @@ -34,6 +35,7 @@ impl AccessSet { }) } + // Generate the plonky2 proof for the given key pair and topic. pub fn make_signal( &self, private_key: Digest, diff --git a/src/circuit.rs b/src/circuit.rs index f4f6941..8b1f313 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -24,6 +24,19 @@ impl AccessSet { } pub fn semaphore_circuit(&self, builder: &mut CircuitBuilder) -> SemaphoreTargets { + // To create the circuit 'values', we call the `add_virtual_target` and it's variants (ie. + // `add_virtual_target_arr`, or `add_virtual_hash` which internally calls it for the 4 + // field values that conform the hash. + // The method `add_virtual_target` keeps an incremental index for each new virtual target + // created, and returns a new `VirtualTarget`, used for intermediate values in witness + // generation (which when needed can be copied to specific witness location (Wire)). + // + // The `register_public_input` and it's variants, append to the CircuitBuilder's + // `public_inputs` vector the given Target (either VirtualTarget or Wire). + // + // Notice that when created, the targets are not given any value, and they will be set later + // at the method `fill_semaphore_targets`. + // Register public inputs. let merkle_root = builder.add_virtual_hash(); builder.register_public_inputs(&merkle_root.elements); @@ -65,6 +78,8 @@ impl AccessSet { } } + // Fill the semaphore targets that we defined at the method `semaphore_circuit` with the given + // values. pub fn fill_semaphore_targets( &self, pw: &mut PartialWitness, diff --git a/src/recursion.rs b/src/recursion.rs index 3e5d3f7..b0120ea 100644 --- a/src/recursion.rs +++ b/src/recursion.rs @@ -39,8 +39,13 @@ impl AccessSet { .chain(topic1) .collect(); + // `add_virtual_proof_with_pis` is an extended version of the `add_virtual_target`, but + // that takes care of adding all the values of the proof and the public inputs (pis). let proof_target0 = builder.add_virtual_proof_with_pis(&verifier_data.common); + // set the public inputs builder.register_public_inputs(&proof_target0.public_inputs); + // `set_proof_with_pis_target` is an extended version of the `set_target`, but that takes + // care of adding all the values of the proof and the public inputs. pw.set_proof_with_pis_target( &proof_target0, &ProofWithPublicInputs { @@ -48,10 +53,12 @@ impl AccessSet { public_inputs: public_inputs0, }, )?; + // add & set the verifier data let vd_target = builder.add_virtual_verifier_data(verifier_data.common.config.fri_config.cap_height); pw.set_verifier_data_target(&vd_target, &verifier_data.verifier_only)?; + // now, the same as we did with the proof0, with the proof1 related values: let proof_target1 = builder.add_virtual_proof_with_pis(&verifier_data.common); builder.register_public_inputs(&proof_target1.public_inputs); pw.set_proof_with_pis_target( @@ -95,7 +102,6 @@ mod tests { use plonky2::hash::poseidon::PoseidonHash; use plonky2::plonk::config::Hasher; use plonky2::plonk::proof::ProofWithPublicInputs; - use std::time::Instant; use crate::access_set::AccessSet; use crate::signal::{Digest, F}; @@ -117,24 +123,18 @@ mod tests { // first proof let i0 = 12; let topic0 = F::rand_array(); - let start = Instant::now(); let (signal0, vd0) = access_set.make_signal(private_keys[i0], topic0, i0)?; - println!("generate proof: {:?}", start.elapsed()); access_set.verify_signal(topic0, signal0.clone(), &vd0)?; // second proof let i1 = 42; let topic1 = F::rand_array(); - let start = Instant::now(); let (signal1, vd1) = access_set.make_signal(private_keys[i1], topic1, i1)?; - println!("generate proof: {:?}", start.elapsed()); access_set.verify_signal(topic1, signal1.clone(), &vd1)?; // generate recursive proof - let start = Instant::now(); let (nullifier0, nullifier1, recursive_proof, vd2) = access_set.aggregate_signals(topic0, signal0, topic1, signal1, &vd0)?; - println!("aggregate_signals (recursive prove): {:?}", start.elapsed()); // verify recursive proof let public_inputs: Vec = access_set @@ -150,12 +150,10 @@ mod tests { .chain(topic1) .collect(); - let start = Instant::now(); vd2.verify(ProofWithPublicInputs { proof: recursive_proof, public_inputs, })?; - println!("verify recursive proof: {:?}", start.elapsed()); Ok(()) } } diff --git a/src/signal.rs b/src/signal.rs index e5681bc..dd4c989 100644 --- a/src/signal.rs +++ b/src/signal.rs @@ -41,7 +41,9 @@ mod tests { let i = 12; let topic = F::rand_array(); + // generate the plonky2 proof for the given key let (signal, vd) = access_set.make_signal(private_keys[i], topic, i)?; + // verify the plonky2 proof (contained in `signal`) access_set.verify_signal(topic, signal, &vd) } }