Add verifier-datas tree (set) & in-circuit verification (#274)

* containers: add method to create new {Dict,Set,Array} with custom max_depth

* add vds_tree computation, update tree circuit interface

* add VDTree struct, add DEFAULT_VD_TREE, integrate it with MainPod,EmptyPod,frontend,etc.

* adapt frontend/serialization tests to new containers field (max_depth)

* adapt interfaces to allow using custom vd_tree in frontend & backend constructors

* rename VDTree to VDSet (and derivate namings too)

* containers 'new' always with param 'max_depth', use params.max_depth_mt_containers instead of the global constant MAX_DEPTH

* adapt after rebasing the branch to main latest changes

* apply review suggestions from @ed255

* use emptypod vd_mt_proofs (using vd_set as circuit input), merge the two existing set_targets methods of MainPodVerifyTarget

* document VDSet & vds_root
This commit is contained in:
arnaucube 2025-06-11 13:08:39 +02:00 committed by GitHub
parent 6258e52e1a
commit 273d803ebd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 486 additions and 259 deletions

View file

@ -17,7 +17,7 @@ use plonky2::{
use crate::{
backends::plonky2::{
basetypes::CircuitBuilder,
basetypes::{CircuitBuilder, VDSet},
circuits::{
common::{
CircuitBuilderPod, CustomPredicateBatchTarget, CustomPredicateEntryTarget,
@ -28,7 +28,7 @@ use crate::{
},
signedpod::{SignedPodVerifyGadget, SignedPodVerifyTarget},
},
emptypod::EmptyPod,
emptypod::{EmptyPod, STANDARD_EMPTY_POD_DATA},
error::Result,
mainpod::{self, pad_statement},
primitives::merkletree::{
@ -39,10 +39,9 @@ use crate::{
},
measure_gates_begin, measure_gates_end,
middleware::{
AnchoredKey, CustomPredicate, CustomPredicateBatch, CustomPredicateRef, Hash,
NativeOperation, NativePredicate, Params, PodType, PredicatePrefix, Statement,
StatementArg, ToFields, Value, WildcardValue, EMPTY_VALUE, F, HASH_SIZE, KEY_TYPE, SELF,
VALUE_SIZE,
AnchoredKey, CustomPredicate, CustomPredicateBatch, CustomPredicateRef, NativeOperation,
NativePredicate, Params, PodType, PredicatePrefix, Statement, StatementArg, ToFields,
Value, WildcardValue, EMPTY_VALUE, F, HASH_SIZE, KEY_TYPE, SELF, VALUE_SIZE,
},
};
@ -1220,9 +1219,31 @@ impl MainPodVerifyGadget {
}
let vds_root = builder.add_virtual_hash();
// TODO: verify that all input pod proofs use verifier data from the public input VD array
// This requires merkle proofs
// https://github.com/0xPARC/pod2/issues/250
// verify that all input pod proofs use verifier data from the public input VD array This
// requires merkle proofs
let mut vd_mt_proofs: Vec<MerkleClaimAndProofTarget> = vec![];
for verified_proof in verified_proofs {
// add target for the vd_mt_proof
let vd_mt_proof = MerkleProofGadget {
max_depth: params.max_depth_mt_vds,
}
.eval(builder);
// ensure that mt_proof is enabled
let true_targ = builder._true();
builder.connect(vd_mt_proof.enabled.target, true_targ.target);
// connect the vd_mt_proof's root to the actual vds_root, to ensure that the mt proof
// verifies against the vds_root
builder.connect_hashes(vds_root, vd_mt_proof.root);
// connect vd_mt_proof's value with the verified_proof.verifier_data_hash
builder.connect_hashes(
verified_proof.verifier_data_hash,
HashOutTarget::from_vec(vd_mt_proof.value.elements.to_vec()),
);
vd_mt_proofs.push(vd_mt_proof);
}
// Verify that VD array that input pod uses is the same we use now.
for verified_proof in verified_proofs {
@ -1247,11 +1268,11 @@ impl MainPodVerifyGadget {
// Add Merkle claim/proof targets
let mp_gadget = MerkleProofGadget {
max_depth: params.max_depth_mt_gadget,
max_depth: params.max_depth_mt_containers,
};
let merkle_proofs: Vec<_> = (0..params.max_merkle_proofs)
let merkle_proofs: Vec<_> = (0..params.max_merkle_proofs_containers)
.map(|_| mp_gadget.eval(builder))
.collect::<Result<_>>()?;
.collect();
let merkle_claims: Vec<_> = merkle_proofs
.clone()
.into_iter()
@ -1310,6 +1331,7 @@ impl MainPodVerifyGadget {
Ok(MainPodVerifyTarget {
params: params.clone(),
vds_root,
vd_mt_proofs,
id,
signed_pods,
input_pods_self_statements,
@ -1325,6 +1347,7 @@ impl MainPodVerifyGadget {
pub struct MainPodVerifyTarget {
params: Params,
vds_root: HashOutTarget,
vd_mt_proofs: Vec<MerkleClaimAndProofTarget>,
id: HashOutTarget,
signed_pods: Vec<SignedPodVerifyTarget>,
input_pods_self_statements: Vec<Vec<StatementTarget>>,
@ -1344,7 +1367,11 @@ pub struct CustomPredicateVerification {
}
pub struct MainPodVerifyInput {
pub vds_root: Hash,
pub vds_set: VDSet,
// field containing the `vd_mt_proofs` aside from the `vds_set`, because
// inide the MainPodVerifyTarget circuit, since it is the InnerCircuit for
// the RecursiveCircuit, we don't have access to the used verifier_datas.
pub vd_mt_proofs: Vec<MerkleClaimAndProof>,
pub signed_pods: Vec<SignedPod>,
pub recursive_pods_pub_self_statements: Vec<Vec<Statement>>,
pub statements: Vec<mainpod::Statement>,
@ -1378,13 +1405,58 @@ fn set_targets_input_pods_self_statements(
Ok(())
}
impl MainPodVerifyTarget {
pub fn set_targets(
pub struct MainPodVerifyCircuit {
pub params: Params,
}
// TODO: Remove this type and implement it's logic directly in `impl InnerCircuit for MainPodVerifyTarget`
impl MainPodVerifyCircuit {
pub fn eval(
&self,
pw: &mut PartialWitness<F>,
input: &MainPodVerifyInput,
) -> Result<()> {
pw.set_target_arr(&self.vds_root.elements, &input.vds_root.0)?;
builder: &mut CircuitBuilder,
verified_proofs: &[VerifiedProofTarget],
) -> Result<MainPodVerifyTarget> {
let main_pod = MainPodVerifyGadget {
params: self.params.clone(),
}
.eval(builder, verified_proofs)?;
builder.register_public_inputs(&main_pod.id.elements);
builder.register_public_inputs(&main_pod.vds_root.elements);
Ok(main_pod)
}
}
impl InnerCircuit for MainPodVerifyTarget {
type Input = MainPodVerifyInput;
type Params = Params;
fn build(
builder: &mut CircuitBuilder,
params: &Self::Params,
verified_proofs: &[VerifiedProofTarget],
) -> Result<Self> {
MainPodVerifyCircuit {
params: params.clone(),
}
.eval(builder, verified_proofs)
}
/// assigns the values to the targets
fn set_targets(&self, pw: &mut PartialWitness<F>, input: &Self::Input) -> Result<()> {
let vds_root = input.vds_set.root();
pw.set_target_arr(&self.vds_root.elements, &vds_root.0)?;
for (i, vd_mt_proof) in input.vd_mt_proofs.iter().enumerate() {
self.vd_mt_proofs[i].set_targets(pw, true, vd_mt_proof)?;
}
// the rest of vd_mt_proofs set them to the empty_pod vd_mt_proof
let vd_emptypod_mt_proof = input
.vds_set
.get_vds_proofs(&[STANDARD_EMPTY_POD_DATA.1.verifier_only.clone()])?;
let vd_emptypod_mt_proof = vd_emptypod_mt_proof[0].clone();
for i in input.vd_mt_proofs.len()..self.vd_mt_proofs.len() {
self.vd_mt_proofs[i].set_targets(pw, true, &vd_emptypod_mt_proof)?;
}
assert!(input.signed_pods.len() <= self.params.max_input_signed_pods);
for (i, signed_pod) in input.signed_pods.iter().enumerate() {
@ -1414,7 +1486,7 @@ impl MainPodVerifyTarget {
}
// Padding
if input.recursive_pods_pub_self_statements.len() != self.params.max_input_recursive_pods {
let empty_pod = EmptyPod::new_boxed(&self.params, input.vds_root);
let empty_pod = EmptyPod::new_boxed(&self.params, input.vds_set.root());
let empty_pod_statements = empty_pod.pub_statements();
for i in
input.recursive_pods_pub_self_statements.len()..self.params.max_input_recursive_pods
@ -1434,13 +1506,13 @@ impl MainPodVerifyTarget {
self.operations[i].set_targets(pw, &self.params, op)?;
}
assert!(input.merkle_proofs.len() <= self.params.max_merkle_proofs);
assert!(input.merkle_proofs.len() <= self.params.max_merkle_proofs_containers);
for (i, mp) in input.merkle_proofs.iter().enumerate() {
self.merkle_proofs[i].set_targets(pw, true, mp)?;
}
// Padding
let pad_mp = MerkleClaimAndProof::empty();
for i in input.merkle_proofs.len()..self.params.max_merkle_proofs {
for i in input.merkle_proofs.len()..self.params.max_merkle_proofs_containers {
self.merkle_proofs[i].set_targets(pw, false, &pad_mp)?;
}
@ -1487,48 +1559,6 @@ impl MainPodVerifyTarget {
}
}
pub struct MainPodVerifyCircuit {
pub params: Params,
}
// TODO: Remove this type and implement it's logic directly in `impl InnerCircuit for MainPodVerifyTarget`
impl MainPodVerifyCircuit {
pub fn eval(
&self,
builder: &mut CircuitBuilder,
verified_proofs: &[VerifiedProofTarget],
) -> Result<MainPodVerifyTarget> {
let main_pod = MainPodVerifyGadget {
params: self.params.clone(),
}
.eval(builder, verified_proofs)?;
builder.register_public_inputs(&main_pod.id.elements);
builder.register_public_inputs(&main_pod.vds_root.elements);
Ok(main_pod)
}
}
impl InnerCircuit for MainPodVerifyTarget {
type Input = MainPodVerifyInput;
type Params = Params;
fn build(
builder: &mut CircuitBuilder,
params: &Self::Params,
verified_proofs: &[VerifiedProofTarget],
) -> Result<Self> {
MainPodVerifyCircuit {
params: params.clone(),
}
.eval(builder, verified_proofs)
}
/// assigns the values to the targets
fn set_targets(&self, pw: &mut PartialWitness<F>, input: &Self::Input) -> Result<()> {
self.set_targets(pw, input)
}
}
#[cfg(test)]
mod tests {
use std::{iter, ops::Not};
@ -1567,7 +1597,7 @@ mod tests {
..Default::default()
};
let mp_gadget = MerkleProofGadget {
max_depth: params.max_depth_mt_gadget,
max_depth: params.max_depth_mt_containers,
};
let config = CircuitConfig::standard_recursion_config();
@ -1581,7 +1611,7 @@ mod tests {
let merkle_proofs_target: Vec<_> = merkle_proofs
.iter()
.map(|_| mp_gadget.eval(&mut builder))
.collect::<Result<_>>()?;
.collect();
let merkle_claims_target: Vec<_> = merkle_proofs_target
.clone()
.into_iter()
@ -2360,7 +2390,7 @@ mod tests {
]
.into_iter()
.collect();
let mt = MerkleTree::new(params.max_depth_mt_gadget, &kvs)?;
let mt = MerkleTree::new(params.max_depth_mt_containers, &kvs)?;
let root = Value::from(mt.root());
let root_ak = AnchoredKey::from((PodId(RawValue::from(88).into()), "merkle root"));
@ -2400,7 +2430,7 @@ mod tests {
]
.into_iter()
.collect();
let mt = MerkleTree::new(params.max_depth_mt_gadget, &kvs)?;
let mt = MerkleTree::new(params.max_depth_mt_containers, &kvs)?;
let root = Value::from(mt.root());
let root_ak = AnchoredKey::from((PodId(RawValue::from(88).into()), "merkle root"));