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
This commit is contained in:
parent
0637f52573
commit
4a94b34792
7 changed files with 357 additions and 182 deletions
|
|
@ -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<F, D>) -> Result<SignedPodVerifyTarget> {
|
||||
// 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<MerkleProofExistenceTarget>,
|
||||
}
|
||||
|
||||
struct SignedPodVerifyInput {
|
||||
kvs: HashMap<Value, Value>,
|
||||
}
|
||||
|
||||
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<StatementTarget> {
|
||||
// TODO: Here we need to use the self.id in the ValueOf statements
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_targets(&self, pw: &mut PartialWitness<F>, 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<SignedPodVerifyInput>,
|
||||
signed_pods: Vec<SignedPod>,
|
||||
}
|
||||
|
||||
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::<F, D>::new(config);
|
||||
|
||||
let signed_pod_verify = SignedPodVerifyGadget { params }.eval(&mut builder)?;
|
||||
|
||||
let mut pw = PartialWitness::<F>::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::<C>();
|
||||
let proof = data.prove(pw)?;
|
||||
data.verify(proof)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn operation_verify(
|
||||
st: mainpod::Statement,
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
pub mod common;
|
||||
pub mod mainpod;
|
||||
pub mod signedpod;
|
||||
|
|
|
|||
202
src/backends/plonky2/circuits/signedpod.rs
Normal file
202
src/backends/plonky2/circuits/signedpod.rs
Normal file
|
|
@ -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<F, D>) -> Result<SignedPodVerifyTarget> {
|
||||
// 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<MerkleProofExistenceTarget>,
|
||||
signature: SignatureVerifyTarget,
|
||||
}
|
||||
|
||||
impl SignedPodVerifyTarget {
|
||||
pub fn pub_statements(&self) -> Vec<StatementTarget> {
|
||||
// TODO: Here we need to use the self.id in the ValueOf statements
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn set_targets(&self, pw: &mut PartialWitness<F>, 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::<Result<Vec<Value>>>()?[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::<SignedPod>().unwrap();
|
||||
|
||||
// use the pod in the circuit
|
||||
let config = CircuitConfig::standard_recursion_config();
|
||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||
let mut pw = PartialWitness::<F>::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::<C>();
|
||||
let proof = data.prove(pw)?;
|
||||
data.verify(proof)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<HashOutTarget>,
|
||||
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<HashOutTarget>,
|
||||
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<F, D>) -> Result<MerkleProofTarget> {
|
||||
// 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<Target> = (0..HASH_SIZE)
|
||||
.map(|j| builder.select(enabled, root.elements[j], zero))
|
||||
.collect();
|
||||
let computed_root: Vec<Target> = (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<F>,
|
||||
// `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<HashOutTarget>,
|
||||
// `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<HashOutTarget>,
|
||||
}
|
||||
|
||||
impl MerkleProofExistenceGadget {
|
||||
/// creates the targets and defines the logic of the circuit
|
||||
pub fn eval(&self, builder: &mut CircuitBuilder<F, D>) -> Result<MerkleProofExistenceTarget> {
|
||||
// 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<Target> = (0..HASH_SIZE)
|
||||
.map(|j| builder.select(enabled, root.elements[j], zero))
|
||||
.collect();
|
||||
let computed_root: Vec<Target> = (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<F>,
|
||||
// `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<Target> = (0..4)
|
||||
let input_1: Vec<Target> = (0..HASH_SIZE)
|
||||
.map(|j| builder.select(path[i], sibling.elements[j], h.elements[j]))
|
||||
.collect();
|
||||
let input_2: Vec<Target> = (0..4)
|
||||
let input_2: Vec<Target> = (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::<PoseidonHash>([input_1, input_2, vec![two]].concat());
|
||||
|
||||
let h_targ: Vec<Target> = (0..4)
|
||||
let h_targ: Vec<Target> = (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::<F>::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::<C>();
|
||||
|
|
@ -526,7 +572,7 @@ pub mod tests {
|
|||
let mut pw = PartialWitness::<F>::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::<C>();
|
||||
|
|
@ -593,7 +639,15 @@ pub mod tests {
|
|||
let mut pw = PartialWitness::<F>::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::<C>();
|
||||
|
|
@ -634,13 +688,44 @@ pub mod tests {
|
|||
let mut pw = PartialWitness::<F>::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::<C>();
|
||||
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::<F, D>::new(config);
|
||||
let mut pw = PartialWitness::<F>::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::<C>();
|
||||
let proof = data.prove(pw)?;
|
||||
data.verify(proof)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<D>,
|
||||
}
|
||||
|
|
@ -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<F, D>) -> Result<SignatureVerifyTarget> {
|
||||
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<F>,
|
||||
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<F> = [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::<F, D>::new(config);
|
||||
let mut pw = PartialWitness::<F>::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::<C>();
|
||||
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::<F, D>::new(config);
|
||||
let mut pw = PartialWitness::<F>::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::<C>();
|
||||
|
|
|
|||
|
|
@ -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<Hash, Value>) -> Result<Box<dyn Pod>> {
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue