From 4a94b34792ed913d2feb111f41e719819583e96d Mon Sep 17 00:00:00 2001 From: arnaucube Date: Tue, 1 Apr 2025 18:20:28 +0200 Subject: [PATCH] Add SignedPod verification circuit (SignedPodVerifyGadget) (#170) * add boolean selector to the MerkleProofGadget, to allow skipping proof verifications when all the slots are not used (eg. in the SignedPod circuit) * move existing signedpod's circuits draft to its own file * implement SignedPodVerify circuit --- src/backends/plonky2/circuits/mainpod.rs | 131 +----------- src/backends/plonky2/circuits/mod.rs | 1 + src/backends/plonky2/circuits/signedpod.rs | 202 ++++++++++++++++++ src/backends/plonky2/mock/signedpod.rs | 22 +- .../plonky2/primitives/merkletree_circuit.rs | 143 ++++++++++--- .../plonky2/primitives/signature_circuit.rs | 32 +-- src/backends/plonky2/signedpod.rs | 8 +- 7 files changed, 357 insertions(+), 182 deletions(-) create mode 100644 src/backends/plonky2/circuits/signedpod.rs diff --git a/src/backends/plonky2/circuits/mainpod.rs b/src/backends/plonky2/circuits/mainpod.rs index b9dae14..2c06a83 100644 --- a/src/backends/plonky2/circuits/mainpod.rs +++ b/src/backends/plonky2/circuits/mainpod.rs @@ -23,103 +23,16 @@ use crate::backends::plonky2::primitives::merkletree::MerkleTree; use crate::backends::plonky2::primitives::merkletree::{ MerkleProofExistenceGadget, MerkleProofExistenceTarget, }; +use crate::backends::plonky2::signedpod::SignedPod; use crate::middleware::{ hash_str, AnchoredKey, NativeOperation, NativePredicate, Params, PodType, Statement, StatementArg, ToFields, KEY_TYPE, SELF, }; -use super::common::Flattenable; - -// -// SignedPod verification -// - -struct SignedPodVerifyGadget { - params: Params, -} - -impl SignedPodVerifyGadget { - fn eval(&self, builder: &mut CircuitBuilder) -> Result { - // 2. Verify id - let id = builder.add_virtual_hash(); - let mut mt_proofs = Vec::new(); - for _ in 0..self.params.max_signed_pod_values { - let mt_proof = MerkleProofExistenceGadget { - max_depth: self.params.max_depth_mt_gadget, - } - .eval(builder)?; - builder.connect_hashes(id, mt_proof.root); - mt_proofs.push(mt_proof); - } - - // 1. Verify type - let type_mt_proof = &mt_proofs[0]; - let key_type = builder.constant_value(hash_str(KEY_TYPE).into()); - builder.connect_values(type_mt_proof.key, key_type); - let value_type = builder.constant_value(Value::from(PodType::MockSigned)); - builder.connect_values(type_mt_proof.value, value_type); - - // 3. TODO: Verify signature - - Ok(SignedPodVerifyTarget { - params: self.params.clone(), - id, - mt_proofs, - }) - } -} - -struct SignedPodVerifyTarget { - params: Params, - id: HashOutTarget, - // The KEY_TYPE entry must be the first one - // The KEY_SIGNER entry must be the second one - mt_proofs: Vec, -} - -struct SignedPodVerifyInput { - kvs: HashMap, -} - -impl SignedPodVerifyTarget { - fn kvs(&self) -> Vec<(ValueTarget, ValueTarget)> { - let mut kvs = Vec::new(); - for mt_proof in &self.mt_proofs { - kvs.push((mt_proof.key, mt_proof.value)); - } - // TODO: when the slot is unused, do we force the kv to be (EMPTY, EMPTY), and then from - // it get a ValueOf((id, EMPTY), EMPTY)? Or should we keep some boolean flags for unused - // slots and translate them to Statement::None instead? - kvs - } - - fn pub_statements(&self) -> Vec { - // TODO: Here we need to use the self.id in the ValueOf statements - todo!() - } - - fn set_targets(&self, pw: &mut PartialWitness, input: &SignedPodVerifyInput) -> Result<()> { - assert!(input.kvs.len() <= self.params.max_signed_pod_values); - let tree = MerkleTree::new(self.params.max_depth_mt_gadget, &input.kvs)?; - - // First handle the type entry, then the rest of the entries, and finally pad with - // repetitions of the type entry (which always exists) - let mut kvs = input.kvs.clone(); - let key_type = Value::from(hash_str(KEY_TYPE)); - let value_type = kvs.remove(&key_type).expect("KEY_TYPE"); - - for (i, (k, v)) in iter::once((key_type, value_type)) - .chain(kvs.into_iter().sorted_by_key(|kv| kv.0)) - .chain(iter::repeat((key_type, value_type))) - .take(self.params.max_signed_pod_values) - .enumerate() - { - let (_, proof) = tree.prove(&k)?; - self.mt_proofs[i].set_targets(pw, tree.root(), proof, k, v)?; - } - Ok(()) - } -} +use super::{ + common::Flattenable, + signedpod::{SignedPodVerifyGadget, SignedPodVerifyTarget}, +}; // // MainPod verification @@ -450,7 +363,7 @@ struct MainPodVerifyTarget { } struct MainPodVerifyInput { - signed_pods: Vec, + signed_pods: Vec, } impl MainPodVerifyTarget { @@ -489,6 +402,8 @@ impl MainPodVerifyCircuit { #[cfg(test)] mod tests { + use plonky2::plonk::{circuit_builder::CircuitBuilder, circuit_data::CircuitConfig}; + use super::*; use crate::backends::plonky2::mock::mainpod; use crate::backends::plonky2::{ @@ -496,36 +411,6 @@ mod tests { mock::mainpod::{OperationArg, OperationAux}, }; use crate::middleware::{OperationType, PodId}; - use plonky2::plonk::{circuit_builder::CircuitBuilder, circuit_data::CircuitConfig}; - - #[test] - fn test_signed_pod_verify() -> Result<()> { - let params = Params::default(); - - let config = CircuitConfig::standard_recursion_config(); - let mut builder = CircuitBuilder::::new(config); - - let signed_pod_verify = SignedPodVerifyGadget { params }.eval(&mut builder)?; - - let mut pw = PartialWitness::::new(); - let kvs = [ - ( - Value::from(hash_str(KEY_TYPE)), - Value::from(PodType::MockSigned), - ), - (Value::from(hash_str("foo")), Value::from(42)), - ] - .into(); - let input = SignedPodVerifyInput { kvs }; - signed_pod_verify.set_targets(&mut pw, &input)?; - - // generate & verify proof - let data = builder.build::(); - let proof = data.prove(pw)?; - data.verify(proof)?; - - Ok(()) - } fn operation_verify( st: mainpod::Statement, diff --git a/src/backends/plonky2/circuits/mod.rs b/src/backends/plonky2/circuits/mod.rs index 134ab24..1afbc5e 100644 --- a/src/backends/plonky2/circuits/mod.rs +++ b/src/backends/plonky2/circuits/mod.rs @@ -1,2 +1,3 @@ pub mod common; pub mod mainpod; +pub mod signedpod; diff --git a/src/backends/plonky2/circuits/signedpod.rs b/src/backends/plonky2/circuits/signedpod.rs new file mode 100644 index 0000000..49dc851 --- /dev/null +++ b/src/backends/plonky2/circuits/signedpod.rs @@ -0,0 +1,202 @@ +use anyhow::Result; +use itertools::Itertools; +use plonky2::{ + hash::hash_types::{HashOut, HashOutTarget}, + iop::witness::{PartialWitness, WitnessWrite}, + plonk::circuit_builder::CircuitBuilder, +}; + +use crate::backends::plonky2::{ + basetypes::{Value, D, EMPTY_VALUE, F}, + circuits::common::{CircuitBuilderPod, StatementTarget, ValueTarget}, + primitives::{ + merkletree::{MerkleProof, MerkleProofExistenceGadget, MerkleProofExistenceTarget}, + signature::{PublicKey, SignatureVerifyGadget, SignatureVerifyTarget}, + }, + signedpod::SignedPod, +}; +use crate::middleware::{hash_str, Params, PodType, KEY_SIGNER, KEY_TYPE}; + +pub struct SignedPodVerifyGadget { + pub params: Params, +} + +impl SignedPodVerifyGadget { + pub fn eval(&self, builder: &mut CircuitBuilder) -> Result { + // 1. Verify id + let id = builder.add_virtual_hash(); + let mut mt_proofs = Vec::new(); + for _ in 0..self.params.max_signed_pod_values { + let mt_proof = MerkleProofExistenceGadget { + max_depth: self.params.max_depth_mt_gadget, + } + .eval(builder)?; + builder.connect_hashes(id, mt_proof.root); + mt_proofs.push(mt_proof); + } + + // 2. Verify type + let type_mt_proof = &mt_proofs[0]; + let key_type = builder.constant_value(hash_str(KEY_TYPE).into()); + builder.connect_values(type_mt_proof.key, key_type); + let value_type = builder.constant_value(Value::from(PodType::Signed)); + builder.connect_values(type_mt_proof.value, value_type); + + // 3.a. Verify signature + let signature = SignatureVerifyGadget {}.eval(builder)?; + + // 3.b. Verify signer (ie. signature.pk == merkletree.signer_leaf) + let signer_mt_proof = &mt_proofs[1]; + let key_signer = builder.constant_value(hash_str(KEY_SIGNER).into()); + builder.connect_values(signer_mt_proof.key, key_signer); + builder.connect_values(signer_mt_proof.value, signature.pk); + + // 3.c. connect signed message to pod.id + builder.connect_values(ValueTarget::from_slice(&id.elements), signature.msg); + + Ok(SignedPodVerifyTarget { + params: self.params.clone(), + id, + mt_proofs, + signature, + }) + } +} + +pub struct SignedPodVerifyTarget { + params: Params, + id: HashOutTarget, + // the KEY_TYPE entry must be the first one + // the KEY_SIGNER entry must be the second one + mt_proofs: Vec, + signature: SignatureVerifyTarget, +} + +impl SignedPodVerifyTarget { + pub fn pub_statements(&self) -> Vec { + // TODO: Here we need to use the self.id in the ValueOf statements + todo!() + } + + pub fn set_targets(&self, pw: &mut PartialWitness, pod: &SignedPod) -> Result<()> { + // set the self.mt_proofs witness with the following order: + // - KEY_TYPE leaf proof + // - KEY_SIGNER leaf proof + // - rest of leaves + // - empty leaves (if needed) + + // add proof verification of KEY_TYPE & KEY_SIGNER leaves + let key_type_key = Value::from(hash_str(KEY_TYPE)); + let key_signer_key = Value::from(hash_str(KEY_SIGNER)); + let key_signer_value = [key_type_key, key_signer_key] + .iter() + .enumerate() + .map(|(i, k)| { + let (v, proof) = pod.dict.prove(&k)?; + self.mt_proofs[i].set_targets(pw, true, pod.dict.commitment(), proof, *k, v)?; + Ok(v) + }) + .collect::>>()?[1]; + + // add the verification of the rest of leaves + let mut curr = 2; // since we already added key_type and key_signer + for (k, v) in pod.dict.iter().sorted_by_key(|kv| kv.0) { + if *k == key_type_key || *k == key_signer_key { + // skip the key_type & key_signer leaves, since they have + // already been checked + continue; + } + + let (obtained_v, proof) = pod.dict.prove(&k)?; + assert_eq!(obtained_v, *v); // sanity check + + self.mt_proofs[curr].set_targets(pw, true, pod.dict.commitment(), proof, *k, *v)?; + curr += 1; + } + // sanity check + assert!(curr <= self.params.max_signed_pod_values); + + // add the proofs of empty leaves (if needed), till the max_signed_pod_values + for i in curr..self.params.max_signed_pod_values { + self.mt_proofs[i].set_targets( + pw, + false, // disable verification + pod.dict.commitment(), + // use an empty proof: + MerkleProof { + existence: true, + siblings: vec![], + other_leaf: None, + }, + EMPTY_VALUE, + EMPTY_VALUE, + )?; + } + + // get the signer pk + let pk = PublicKey(key_signer_value); + // the msg signed is the pod.id + let msg = Value::from(pod.id.0); + + // set signature targets values + self.signature + .set_targets(pw, true, pk, msg, pod.signature.clone())?; + + // set the id target value + pw.set_hash_target(self.id, HashOut::from_vec(pod.id.0 .0.to_vec()))?; + + Ok(()) + } +} + +#[cfg(test)] +pub mod tests { + use plonky2::plonk::{circuit_builder::CircuitBuilder, circuit_data::CircuitConfig}; + + use super::*; + use crate::backends::plonky2::{ + basetypes::C, + primitives::signature::SecretKey, + signedpod::{SignedPod, Signer}, + }; + use crate::middleware::F; + + #[test] + fn test_signed_pod_verify() -> Result<()> { + let mut params = Params { + max_signed_pod_values: 6, + ..Default::default() + }; + // set max_signed_pod_values to 6, and we insert 3 leaves, so that the + // circuit has enough space for the 3 leaves plus the KEY_TYPE and + // KEY_SIGNER and one empty leaf. + + // prepare a signedpod + let mut pod = crate::frontend::SignedPodBuilder::new(¶ms); + pod.insert("idNumber", "4242424242"); + pod.insert("dateOfBirth", 1169909384); + pod.insert("socialSecurityNumber", "G2121210"); + let sk = SecretKey::new(); + let mut signer = Signer(sk); + let pod = pod.sign(&mut signer).unwrap(); + let signed_pod = pod.pod.into_any().downcast::().unwrap(); + + // use the pod in the circuit + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(config); + let mut pw = PartialWitness::::new(); + + // build the circuit logic + let signed_pod_verify = SignedPodVerifyGadget { params }.eval(&mut builder)?; + + // set the signed_pod as target values for the circuit + signed_pod_verify.set_targets(&mut pw, &signed_pod)?; + + // generate & verify proof + let data = builder.build::(); + let proof = data.prove(pw)?; + data.verify(proof)?; + + Ok(()) + } +} diff --git a/src/backends/plonky2/mock/signedpod.rs b/src/backends/plonky2/mock/signedpod.rs index f3c3a6c..e2fc89e 100644 --- a/src/backends/plonky2/mock/signedpod.rs +++ b/src/backends/plonky2/mock/signedpod.rs @@ -56,17 +56,7 @@ impl MockSignedPod { impl Pod for MockSignedPod { fn verify(&self) -> Result<()> { - // 1. Verify type - let value_at_type = self.dict.get(&hash_str(KEY_TYPE).into())?; - if Value::from(PodType::MockSigned) != value_at_type { - return Err(anyhow!( - "type does not match, expected MockSigned ({}), found {}", - PodType::MockSigned, - value_at_type - )); - } - - // 2. Verify id + // 1. Verify id let mt = MerkleTree::new( MAX_DEPTH, &self @@ -84,6 +74,16 @@ impl Pod for MockSignedPod { )); } + // 2. Verify type + let value_at_type = self.dict.get(&hash_str(KEY_TYPE).into())?; + if Value::from(PodType::MockSigned) != value_at_type { + return Err(anyhow!( + "type does not match, expected MockSigned ({}), found {}", + PodType::MockSigned, + value_at_type + )); + } + // 3. Verify signature let pk_hash = self.dict.get(&hash_str(KEY_SIGNER).into())?; let signature = format!("{}_signed_by_{}", id, pk_hash); diff --git a/src/backends/plonky2/primitives/merkletree_circuit.rs b/src/backends/plonky2/primitives/merkletree_circuit.rs index 27a0d1c..3dbe9e2 100644 --- a/src/backends/plonky2/primitives/merkletree_circuit.rs +++ b/src/backends/plonky2/primitives/merkletree_circuit.rs @@ -23,7 +23,7 @@ use plonky2::{ }; use std::iter; -use crate::backends::plonky2::basetypes::{Hash, Value, D, EMPTY_HASH, EMPTY_VALUE, F}; +use crate::backends::plonky2::basetypes::{Hash, Value, D, EMPTY_HASH, EMPTY_VALUE, F, HASH_SIZE}; use crate::backends::plonky2::circuits::common::{CircuitBuilderPod, ValueTarget}; use crate::backends::plonky2::primitives::merkletree::MerkleProof; @@ -37,20 +37,23 @@ pub struct MerkleProofGadget { pub struct MerkleProofTarget { max_depth: usize, - pub root: HashOutTarget, - pub key: ValueTarget, - pub value: ValueTarget, - pub existence: BoolTarget, - pub siblings: Vec, - pub case_ii_selector: BoolTarget, // for case ii) - pub other_key: ValueTarget, - pub other_value: ValueTarget, + // `enabled` determines if the merkleproof verification is enabled + pub(crate) enabled: BoolTarget, + pub(crate) root: HashOutTarget, + pub(crate) key: ValueTarget, + pub(crate) value: ValueTarget, + pub(crate) existence: BoolTarget, + pub(crate) siblings: Vec, + pub(crate) case_ii_selector: BoolTarget, // for case ii) + pub(crate) other_key: ValueTarget, + pub(crate) other_value: ValueTarget, } impl MerkleProofGadget { /// creates the targets and defines the logic of the circuit pub fn eval(&self, builder: &mut CircuitBuilder) -> Result { - // create the targets + let enabled = builder.add_virtual_bool_target_safe(); + let root = builder.add_virtual_hash(); let key = builder.add_virtual_value(); let value = builder.add_virtual_value(); // from proof struct: @@ -104,7 +107,7 @@ impl MerkleProofGadget { // previously computed hash h. let empty_hash = builder.constant_hash(HashOut::from(EMPTY_HASH.0)); let leaf_hash = HashOutTarget::from_vec( - (0..4) + (0..HASH_SIZE) .map(|j| builder.select(case_i_selector, empty_hash.elements[j], h.elements[j])) .collect(), ); @@ -115,12 +118,24 @@ impl MerkleProofGadget { // compute the root for the given siblings and the computed leaf_hash // (this is for the three cases (existence, non-existence case i, and // non-existence case ii). - // This root will be assigned in the `set_targets` method, and it is a - // public input. - let root = compute_root_from_leaf(self.max_depth, builder, &path, &leaf_hash, &siblings)?; + let obtained_root = + compute_root_from_leaf(self.max_depth, builder, &path, &leaf_hash, &siblings)?; + + // check that obtained_root==root (from inputs), when enabled==true + let zero = builder.zero(); + let expected_root: Vec = (0..HASH_SIZE) + .map(|j| builder.select(enabled, root.elements[j], zero)) + .collect(); + let computed_root: Vec = (0..HASH_SIZE) + .map(|j| builder.select(enabled, obtained_root.elements[j], zero)) + .collect(); + for j in 0..HASH_SIZE { + builder.connect(computed_root[j], expected_root[j]); + } Ok(MerkleProofTarget { max_depth: self.max_depth, + enabled, existence, root, siblings, @@ -138,12 +153,15 @@ impl MerkleProofTarget { pub fn set_targets( &self, pw: &mut PartialWitness, + // `enabled` determines if the merkleproof verification is enabled + enabled: bool, existence: bool, root: Hash, proof: MerkleProof, key: Value, value: Value, ) -> Result<()> { + pw.set_bool_target(self.enabled, enabled)?; pw.set_hash_target(self.root, HashOut::from_vec(root.0.to_vec()))?; pw.set_target_arr(&self.key.elements, &key.0)?; pw.set_target_arr(&self.value.elements, &value.0)?; @@ -185,16 +203,19 @@ pub struct MerkleProofExistenceGadget { pub struct MerkleProofExistenceTarget { max_depth: usize, - pub root: HashOutTarget, - pub key: ValueTarget, - pub value: ValueTarget, - pub siblings: Vec, + // `enabled` determines if the merkleproof verification is enabled + pub(crate) enabled: BoolTarget, + pub(crate) root: HashOutTarget, + pub(crate) key: ValueTarget, + pub(crate) value: ValueTarget, + pub(crate) siblings: Vec, } impl MerkleProofExistenceGadget { /// creates the targets and defines the logic of the circuit pub fn eval(&self, builder: &mut CircuitBuilder) -> Result { - // create the targets + let enabled = builder.add_virtual_bool_target_safe(); + let root = builder.add_virtual_hash(); let key = builder.add_virtual_value(); let value = builder.add_virtual_value(); // siblings are padded till max_depth length @@ -207,12 +228,24 @@ impl MerkleProofExistenceGadget { let path = keypath_target(self.max_depth, builder, &key); // compute the root for the given siblings and the computed leaf_hash. - // This root will be assigned in the `set_targets` method, and it is a - // public input. - let root = compute_root_from_leaf(self.max_depth, builder, &path, &leaf_hash, &siblings)?; + let obtained_root = + compute_root_from_leaf(self.max_depth, builder, &path, &leaf_hash, &siblings)?; + + // check that obtained_root==root (from inputs), when enabled==true + let zero = builder.zero(); + let expected_root: Vec = (0..HASH_SIZE) + .map(|j| builder.select(enabled, root.elements[j], zero)) + .collect(); + let computed_root: Vec = (0..HASH_SIZE) + .map(|j| builder.select(enabled, obtained_root.elements[j], zero)) + .collect(); + for j in 0..HASH_SIZE { + builder.connect(computed_root[j], expected_root[j]); + } Ok(MerkleProofExistenceTarget { max_depth: self.max_depth, + enabled, root, siblings, key, @@ -226,11 +259,16 @@ impl MerkleProofExistenceTarget { pub fn set_targets( &self, pw: &mut PartialWitness, + // `enabled` determines if the merkleproof verification is enabled + enabled: bool, root: Hash, proof: MerkleProof, key: Value, value: Value, ) -> Result<()> { + assert!(proof.existence); // sanity check + + pw.set_bool_target(self.enabled, enabled)?; pw.set_hash_target(self.root, HashOut::from_vec(root.0.to_vec()))?; pw.set_target_arr(&self.key.elements, &key.0)?; pw.set_target_arr(&self.value.elements, &value.0)?; @@ -291,16 +329,16 @@ fn compute_root_from_leaf( // input_2 = sibling * (1-s) + h * s = select(s, h, sibling) // new_h = hash([input_1, input_2]) // TODO explore if to group multiple muls in a single gate - let input_1: Vec = (0..4) + let input_1: Vec = (0..HASH_SIZE) .map(|j| builder.select(path[i], sibling.elements[j], h.elements[j])) .collect(); - let input_2: Vec = (0..4) + let input_2: Vec = (0..HASH_SIZE) .map(|j| builder.select(path[i], h.elements[j], sibling.elements[j])) .collect(); let new_h = builder.hash_n_to_hash_no_pad::([input_1, input_2, vec![two]].concat()); - let h_targ: Vec = (0..4) + let h_targ: Vec = (0..HASH_SIZE) .map(|j| builder.select(*selector, new_h.elements[j], h.elements[j])) .collect(); h = HashOutTarget::from_vec(h_targ); @@ -487,7 +525,15 @@ pub mod tests { let mut pw = PartialWitness::::new(); let targets = MerkleProofGadget { max_depth }.eval(&mut builder)?; - targets.set_targets(&mut pw, existence, tree.root(), proof, key, value)?; + targets.set_targets( + &mut pw, + true, // verification enabled + existence, + tree.root(), + proof, + key, + value, + )?; // generate & verify proof let data = builder.build::(); @@ -526,7 +572,7 @@ pub mod tests { let mut pw = PartialWitness::::new(); let targets = MerkleProofExistenceGadget { max_depth }.eval(&mut builder)?; - targets.set_targets(&mut pw, tree.root(), proof, key, value)?; + targets.set_targets(&mut pw, true, tree.root(), proof, key, value)?; // generate & verify proof let data = builder.build::(); @@ -593,7 +639,15 @@ pub mod tests { let mut pw = PartialWitness::::new(); let targets = MerkleProofGadget { max_depth }.eval(&mut builder)?; - targets.set_targets(&mut pw, proof.existence, tree.root(), proof, key, value)?; + targets.set_targets( + &mut pw, + true, // verification enabled + proof.existence, + tree.root(), + proof, + key, + value, + )?; // generate & verify proof let data = builder.build::(); @@ -634,13 +688,44 @@ pub mod tests { let mut pw = PartialWitness::::new(); let targets = MerkleProofGadget { max_depth }.eval(&mut builder)?; - targets.set_targets(&mut pw, true, tree2.root(), proof, key, value)?; + targets.set_targets( + &mut pw, + true, // verification enabled + true, // proof of existence + tree2.root(), + proof.clone(), + key, + value, + )?; // generate proof, expecting it to fail (since we're using the wrong // root) let data = builder.build::(); assert!(data.prove(pw).is_err()); + // Now generate a new proof, using `enabled=false`, which should pass the verification + // despite containing 'wrong' witness. + let config = CircuitConfig::standard_recursion_config(); + let mut builder = CircuitBuilder::::new(config); + let mut pw = PartialWitness::::new(); + + let targets = MerkleProofGadget { max_depth }.eval(&mut builder)?; + targets.set_targets( + &mut pw, + false, // verification disabled + true, // proof of existence + tree2.root(), + proof, + key, + value, + )?; + + // generate proof, should pass despite using wrong witness, since the + // `enabled=false` + let data = builder.build::(); + let proof = data.prove(pw)?; + data.verify(proof)?; + Ok(()) } } diff --git a/src/backends/plonky2/primitives/signature_circuit.rs b/src/backends/plonky2/primitives/signature_circuit.rs index 802b4e4..aec52f9 100644 --- a/src/backends/plonky2/primitives/signature_circuit.rs +++ b/src/backends/plonky2/primitives/signature_circuit.rs @@ -34,9 +34,10 @@ pub struct SignatureVerifyGadget {} pub struct SignatureVerifyTarget { // verifier_data of the SignatureInternalCircuit verifier_data_targ: VerifierCircuitTarget, - selector: BoolTarget, - pk: ValueTarget, - msg: ValueTarget, + // `enabled` determines if the signature verification is enabled + pub(crate) enabled: BoolTarget, + pub(crate) pk: ValueTarget, + pub(crate) msg: ValueTarget, // proof of the SignatureInternalCircuit (=signature::Signature.0) proof: ProofWithPublicInputsTarget, } @@ -56,7 +57,7 @@ impl SignatureVerifyGadget { impl SignatureVerifyGadget { /// creates the targets and defines the logic of the circuit pub fn eval(&self, builder: &mut CircuitBuilder) -> Result { - let selector = builder.add_virtual_bool_target_safe(); + let enabled = builder.add_virtual_bool_target_safe(); let common_data = super::signature::VP.0.common.clone(); @@ -83,10 +84,10 @@ impl SignatureVerifyGadget { builder.constant_value(Value(dummy_pi[VALUE_SIZE * 2..].try_into().unwrap())); // connect the {pk, msg, s} with the proof_targ.public_inputs conditionally - let pk_targ_connect = builder.select_value(selector, pk_targ, pk_targ_dummy); - let msg_targ_connect = builder.select_value(selector, msg_targ, msg_targ_dummy); + let pk_targ_connect = builder.select_value(enabled, pk_targ, pk_targ_dummy); + let msg_targ_connect = builder.select_value(enabled, msg_targ, msg_targ_dummy); let s_targ_connect = builder.select_value( - selector, + enabled, ValueTarget { elements: s_targ.elements, }, @@ -108,7 +109,7 @@ impl SignatureVerifyGadget { Ok(SignatureVerifyTarget { verifier_data_targ, - selector, + enabled, pk: pk_targ, msg: msg_targ, proof: proof_targ, @@ -121,12 +122,12 @@ impl SignatureVerifyTarget { pub fn set_targets( &self, pw: &mut PartialWitness, - selector: bool, + enabled: bool, pk: PublicKey, msg: Value, signature: Signature, ) -> Result<()> { - pw.set_bool_target(self.selector, selector)?; + pw.set_bool_target(self.enabled, enabled)?; pw.set_target_arr(&self.pk.elements, &pk.0 .0)?; pw.set_target_arr(&self.msg.elements, &msg.0)?; @@ -134,7 +135,7 @@ impl SignatureVerifyTarget { let s = Value(PoseidonHash::hash_no_pad(&[pk.0 .0, msg.0].concat()).elements); let public_inputs: Vec = [pk.0 .0, msg.0, s.0].concat(); - if selector { + if enabled { pw.set_proof_with_pis_target( &self.proof, &ProofWithPublicInputs { @@ -220,20 +221,21 @@ pub mod tests { let mut builder = CircuitBuilder::::new(config); let mut pw = PartialWitness::::new(); let targets = SignatureVerifyGadget {}.eval(&mut builder)?; - targets.set_targets(&mut pw, true, pk.clone(), msg, sig.clone())?; // selector=true + targets.set_targets(&mut pw, true, pk.clone(), msg, sig.clone())?; // enabled=true // generate proof, and expect it to fail let data = builder.build::(); assert!(data.prove(pw).is_err()); // expect prove to fail - // build the circuit again, but now disable the selector that disables - // the in-circuit signature verification + // build the circuit again, but now disable the selector ('enabled') + // that disables the in-circuit signature verification (ie. + // `enabled=false`) let config = CircuitConfig::standard_recursion_zk_config(); let mut builder = CircuitBuilder::::new(config); let mut pw = PartialWitness::::new(); let targets = SignatureVerifyGadget {}.eval(&mut builder)?; - targets.set_targets(&mut pw, false, pk, msg, sig)?; // selector=false + targets.set_targets(&mut pw, false, pk, msg, sig)?; // enabled=false // generate & verify proof let data = builder.build::(); diff --git a/src/backends/plonky2/signedpod.rs b/src/backends/plonky2/signedpod.rs index fcf105e..a2f2bb5 100644 --- a/src/backends/plonky2/signedpod.rs +++ b/src/backends/plonky2/signedpod.rs @@ -11,7 +11,7 @@ use crate::middleware::{ use super::primitives::signature::{PublicKey, SecretKey, Signature}; -pub struct Signer(SecretKey); +pub struct Signer(pub SecretKey); impl PodSigner for Signer { fn sign(&mut self, _params: &Params, kvs: &HashMap) -> Result> { @@ -34,9 +34,9 @@ impl PodSigner for Signer { #[derive(Clone, Debug)] pub struct SignedPod { - id: PodId, - signature: Signature, - dict: Dictionary, + pub id: PodId, + pub signature: Signature, + pub dict: Dictionary, } impl Pod for SignedPod {