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:
arnaucube 2025-04-01 18:20:28 +02:00 committed by GitHub
parent 0637f52573
commit 4a94b34792
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 357 additions and 182 deletions

View file

@ -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,