Skip to content

Commit 273d803

Browse files
authored
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
1 parent 6258e52 commit 273d803

File tree

17 files changed

+486
-259
lines changed

17 files changed

+486
-259
lines changed

src/backends/plonky2/basetypes.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,91 @@ pub type VerifierCircuitData = circuit_data::VerifierCircuitData<F, C, D>;
3636
pub type CircuitBuilder = circuit_builder::CircuitBuilder<F, D>;
3737
pub type Proof = proof::Proof<F, C, D>;
3838
pub type ProofWithPublicInputs = proof::ProofWithPublicInputs<F, C, D>;
39+
40+
use std::{collections::HashMap, sync::LazyLock};
41+
42+
use itertools::Itertools;
43+
use plonky2::hash::hash_types::HashOut;
44+
45+
use crate::{
46+
backends::plonky2::{
47+
emptypod::STANDARD_EMPTY_POD_DATA, primitives::merkletree::MerkleClaimAndProof,
48+
DEFAULT_PARAMS, STANDARD_REC_MAIN_POD_CIRCUIT_DATA,
49+
},
50+
middleware::{containers::Array, Hash, RawValue, Result, Value},
51+
};
52+
53+
pub static DEFAULT_VD_SET: LazyLock<VDSet> = LazyLock::new(|| {
54+
let params = &*DEFAULT_PARAMS;
55+
56+
let vds = vec![
57+
STANDARD_REC_MAIN_POD_CIRCUIT_DATA.verifier_only.clone(),
58+
STANDARD_EMPTY_POD_DATA.1.verifier_only.clone(),
59+
];
60+
VDSet::new(params.max_depth_mt_vds, &vds).unwrap()
61+
});
62+
63+
/// VDSet is the set of the allowed verifier_data hashes. When proving a
64+
/// MainPod, the circuit will enforce that all the used verifier_datas for
65+
/// verifying the recursive proofs of previous PODs appears in the VDSet.
66+
/// The VDSet struct that allows to get the specific merkle proofs for the given
67+
/// verifier_data.
68+
#[derive(Clone, Debug)]
69+
pub struct VDSet {
70+
root: Hash,
71+
// (verifier_data, merkleproof)
72+
proofs_map: HashMap<HashOut<F>, MerkleClaimAndProof>,
73+
}
74+
impl VDSet {
75+
/// builds the verifier_datas tree, and returns the root and the proofs
76+
pub fn new(tree_depth: usize, vds: &[VerifierOnlyCircuitData]) -> Result<Self> {
77+
// first of all, sort the vds, so that each set of verifier_datas gets
78+
// the same root
79+
let vds: Vec<&VerifierOnlyCircuitData> = vds
80+
.iter()
81+
.sorted_by_key(|vd| RawValue(vd.circuit_digest.elements))
82+
.collect::<Vec<_>>();
83+
84+
let array = Array::new(
85+
tree_depth,
86+
vds.iter()
87+
.map(|vd| Value::from(RawValue(vd.circuit_digest.elements)))
88+
.collect(),
89+
)?;
90+
91+
let root = array.commitment();
92+
let mut proofs_map = HashMap::<HashOut<F>, MerkleClaimAndProof>::new();
93+
94+
for (i, vd) in vds.iter().enumerate() {
95+
let (value, proof) = array.prove(i)?;
96+
let p = MerkleClaimAndProof {
97+
root,
98+
key: RawValue::from(i as i64),
99+
value: value.raw(),
100+
proof,
101+
};
102+
proofs_map.insert(vd.circuit_digest, p);
103+
}
104+
Ok(Self { root, proofs_map })
105+
}
106+
pub fn root(&self) -> Hash {
107+
self.root
108+
}
109+
/// returns the vector of merkle proofs corresponding to the given verifier_datas
110+
pub fn get_vds_proofs(
111+
&self,
112+
vds: &[VerifierOnlyCircuitData],
113+
) -> Result<Vec<MerkleClaimAndProof>> {
114+
let mut proofs: Vec<MerkleClaimAndProof> = vec![];
115+
for vd in vds {
116+
let p =
117+
self.proofs_map
118+
.get(&vd.circuit_digest)
119+
.ok_or(crate::middleware::Error::custom(
120+
"verifier_data not found in VDSet".to_string(),
121+
))?;
122+
proofs.push(p.clone());
123+
}
124+
Ok(proofs)
125+
}
126+
}

src/backends/plonky2/circuits/common.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ use crate::{
4040
pub const CODE_SIZE: usize = HASH_SIZE + 2;
4141
const NUM_BITS: usize = 32;
4242

43-
#[derive(Copy, Clone)]
43+
#[derive(Copy, Clone, Debug)]
4444
pub struct ValueTarget {
4545
pub elements: [Target; VALUE_SIZE],
4646
}

src/backends/plonky2/circuits/mainpod.rs

Lines changed: 98 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use plonky2::{
1717

1818
use crate::{
1919
backends::plonky2::{
20-
basetypes::CircuitBuilder,
20+
basetypes::{CircuitBuilder, VDSet},
2121
circuits::{
2222
common::{
2323
CircuitBuilderPod, CustomPredicateBatchTarget, CustomPredicateEntryTarget,
@@ -28,7 +28,7 @@ use crate::{
2828
},
2929
signedpod::{SignedPodVerifyGadget, SignedPodVerifyTarget},
3030
},
31-
emptypod::EmptyPod,
31+
emptypod::{EmptyPod, STANDARD_EMPTY_POD_DATA},
3232
error::Result,
3333
mainpod::{self, pad_statement},
3434
primitives::merkletree::{
@@ -39,10 +39,9 @@ use crate::{
3939
},
4040
measure_gates_begin, measure_gates_end,
4141
middleware::{
42-
AnchoredKey, CustomPredicate, CustomPredicateBatch, CustomPredicateRef, Hash,
43-
NativeOperation, NativePredicate, Params, PodType, PredicatePrefix, Statement,
44-
StatementArg, ToFields, Value, WildcardValue, EMPTY_VALUE, F, HASH_SIZE, KEY_TYPE, SELF,
45-
VALUE_SIZE,
42+
AnchoredKey, CustomPredicate, CustomPredicateBatch, CustomPredicateRef, NativeOperation,
43+
NativePredicate, Params, PodType, PredicatePrefix, Statement, StatementArg, ToFields,
44+
Value, WildcardValue, EMPTY_VALUE, F, HASH_SIZE, KEY_TYPE, SELF, VALUE_SIZE,
4645
},
4746
};
4847

@@ -1220,9 +1219,31 @@ impl MainPodVerifyGadget {
12201219
}
12211220

12221221
let vds_root = builder.add_virtual_hash();
1223-
// TODO: verify that all input pod proofs use verifier data from the public input VD array
1224-
// This requires merkle proofs
1225-
// https://github.com/0xPARC/pod2/issues/250
1222+
1223+
// verify that all input pod proofs use verifier data from the public input VD array This
1224+
// requires merkle proofs
1225+
let mut vd_mt_proofs: Vec<MerkleClaimAndProofTarget> = vec![];
1226+
for verified_proof in verified_proofs {
1227+
// add target for the vd_mt_proof
1228+
let vd_mt_proof = MerkleProofGadget {
1229+
max_depth: params.max_depth_mt_vds,
1230+
}
1231+
.eval(builder);
1232+
1233+
// ensure that mt_proof is enabled
1234+
let true_targ = builder._true();
1235+
builder.connect(vd_mt_proof.enabled.target, true_targ.target);
1236+
// connect the vd_mt_proof's root to the actual vds_root, to ensure that the mt proof
1237+
// verifies against the vds_root
1238+
builder.connect_hashes(vds_root, vd_mt_proof.root);
1239+
// connect vd_mt_proof's value with the verified_proof.verifier_data_hash
1240+
builder.connect_hashes(
1241+
verified_proof.verifier_data_hash,
1242+
HashOutTarget::from_vec(vd_mt_proof.value.elements.to_vec()),
1243+
);
1244+
1245+
vd_mt_proofs.push(vd_mt_proof);
1246+
}
12261247

12271248
// Verify that VD array that input pod uses is the same we use now.
12281249
for verified_proof in verified_proofs {
@@ -1247,11 +1268,11 @@ impl MainPodVerifyGadget {
12471268

12481269
// Add Merkle claim/proof targets
12491270
let mp_gadget = MerkleProofGadget {
1250-
max_depth: params.max_depth_mt_gadget,
1271+
max_depth: params.max_depth_mt_containers,
12511272
};
1252-
let merkle_proofs: Vec<_> = (0..params.max_merkle_proofs)
1273+
let merkle_proofs: Vec<_> = (0..params.max_merkle_proofs_containers)
12531274
.map(|_| mp_gadget.eval(builder))
1254-
.collect::<Result<_>>()?;
1275+
.collect();
12551276
let merkle_claims: Vec<_> = merkle_proofs
12561277
.clone()
12571278
.into_iter()
@@ -1310,6 +1331,7 @@ impl MainPodVerifyGadget {
13101331
Ok(MainPodVerifyTarget {
13111332
params: params.clone(),
13121333
vds_root,
1334+
vd_mt_proofs,
13131335
id,
13141336
signed_pods,
13151337
input_pods_self_statements,
@@ -1325,6 +1347,7 @@ impl MainPodVerifyGadget {
13251347
pub struct MainPodVerifyTarget {
13261348
params: Params,
13271349
vds_root: HashOutTarget,
1350+
vd_mt_proofs: Vec<MerkleClaimAndProofTarget>,
13281351
id: HashOutTarget,
13291352
signed_pods: Vec<SignedPodVerifyTarget>,
13301353
input_pods_self_statements: Vec<Vec<StatementTarget>>,
@@ -1344,7 +1367,11 @@ pub struct CustomPredicateVerification {
13441367
}
13451368

13461369
pub struct MainPodVerifyInput {
1347-
pub vds_root: Hash,
1370+
pub vds_set: VDSet,
1371+
// field containing the `vd_mt_proofs` aside from the `vds_set`, because
1372+
// inide the MainPodVerifyTarget circuit, since it is the InnerCircuit for
1373+
// the RecursiveCircuit, we don't have access to the used verifier_datas.
1374+
pub vd_mt_proofs: Vec<MerkleClaimAndProof>,
13481375
pub signed_pods: Vec<SignedPod>,
13491376
pub recursive_pods_pub_self_statements: Vec<Vec<Statement>>,
13501377
pub statements: Vec<mainpod::Statement>,
@@ -1378,13 +1405,58 @@ fn set_targets_input_pods_self_statements(
13781405
Ok(())
13791406
}
13801407

1381-
impl MainPodVerifyTarget {
1382-
pub fn set_targets(
1408+
pub struct MainPodVerifyCircuit {
1409+
pub params: Params,
1410+
}
1411+
1412+
// TODO: Remove this type and implement it's logic directly in `impl InnerCircuit for MainPodVerifyTarget`
1413+
impl MainPodVerifyCircuit {
1414+
pub fn eval(
13831415
&self,
1384-
pw: &mut PartialWitness<F>,
1385-
input: &MainPodVerifyInput,
1386-
) -> Result<()> {
1387-
pw.set_target_arr(&self.vds_root.elements, &input.vds_root.0)?;
1416+
builder: &mut CircuitBuilder,
1417+
verified_proofs: &[VerifiedProofTarget],
1418+
) -> Result<MainPodVerifyTarget> {
1419+
let main_pod = MainPodVerifyGadget {
1420+
params: self.params.clone(),
1421+
}
1422+
.eval(builder, verified_proofs)?;
1423+
builder.register_public_inputs(&main_pod.id.elements);
1424+
builder.register_public_inputs(&main_pod.vds_root.elements);
1425+
Ok(main_pod)
1426+
}
1427+
}
1428+
1429+
impl InnerCircuit for MainPodVerifyTarget {
1430+
type Input = MainPodVerifyInput;
1431+
type Params = Params;
1432+
1433+
fn build(
1434+
builder: &mut CircuitBuilder,
1435+
params: &Self::Params,
1436+
verified_proofs: &[VerifiedProofTarget],
1437+
) -> Result<Self> {
1438+
MainPodVerifyCircuit {
1439+
params: params.clone(),
1440+
}
1441+
.eval(builder, verified_proofs)
1442+
}
1443+
1444+
/// assigns the values to the targets
1445+
fn set_targets(&self, pw: &mut PartialWitness<F>, input: &Self::Input) -> Result<()> {
1446+
let vds_root = input.vds_set.root();
1447+
pw.set_target_arr(&self.vds_root.elements, &vds_root.0)?;
1448+
1449+
for (i, vd_mt_proof) in input.vd_mt_proofs.iter().enumerate() {
1450+
self.vd_mt_proofs[i].set_targets(pw, true, vd_mt_proof)?;
1451+
}
1452+
// the rest of vd_mt_proofs set them to the empty_pod vd_mt_proof
1453+
let vd_emptypod_mt_proof = input
1454+
.vds_set
1455+
.get_vds_proofs(&[STANDARD_EMPTY_POD_DATA.1.verifier_only.clone()])?;
1456+
let vd_emptypod_mt_proof = vd_emptypod_mt_proof[0].clone();
1457+
for i in input.vd_mt_proofs.len()..self.vd_mt_proofs.len() {
1458+
self.vd_mt_proofs[i].set_targets(pw, true, &vd_emptypod_mt_proof)?;
1459+
}
13881460

13891461
assert!(input.signed_pods.len() <= self.params.max_input_signed_pods);
13901462
for (i, signed_pod) in input.signed_pods.iter().enumerate() {
@@ -1414,7 +1486,7 @@ impl MainPodVerifyTarget {
14141486
}
14151487
// Padding
14161488
if input.recursive_pods_pub_self_statements.len() != self.params.max_input_recursive_pods {
1417-
let empty_pod = EmptyPod::new_boxed(&self.params, input.vds_root);
1489+
let empty_pod = EmptyPod::new_boxed(&self.params, input.vds_set.root());
14181490
let empty_pod_statements = empty_pod.pub_statements();
14191491
for i in
14201492
input.recursive_pods_pub_self_statements.len()..self.params.max_input_recursive_pods
@@ -1434,13 +1506,13 @@ impl MainPodVerifyTarget {
14341506
self.operations[i].set_targets(pw, &self.params, op)?;
14351507
}
14361508

1437-
assert!(input.merkle_proofs.len() <= self.params.max_merkle_proofs);
1509+
assert!(input.merkle_proofs.len() <= self.params.max_merkle_proofs_containers);
14381510
for (i, mp) in input.merkle_proofs.iter().enumerate() {
14391511
self.merkle_proofs[i].set_targets(pw, true, mp)?;
14401512
}
14411513
// Padding
14421514
let pad_mp = MerkleClaimAndProof::empty();
1443-
for i in input.merkle_proofs.len()..self.params.max_merkle_proofs {
1515+
for i in input.merkle_proofs.len()..self.params.max_merkle_proofs_containers {
14441516
self.merkle_proofs[i].set_targets(pw, false, &pad_mp)?;
14451517
}
14461518

@@ -1487,48 +1559,6 @@ impl MainPodVerifyTarget {
14871559
}
14881560
}
14891561

1490-
pub struct MainPodVerifyCircuit {
1491-
pub params: Params,
1492-
}
1493-
1494-
// TODO: Remove this type and implement it's logic directly in `impl InnerCircuit for MainPodVerifyTarget`
1495-
impl MainPodVerifyCircuit {
1496-
pub fn eval(
1497-
&self,
1498-
builder: &mut CircuitBuilder,
1499-
verified_proofs: &[VerifiedProofTarget],
1500-
) -> Result<MainPodVerifyTarget> {
1501-
let main_pod = MainPodVerifyGadget {
1502-
params: self.params.clone(),
1503-
}
1504-
.eval(builder, verified_proofs)?;
1505-
builder.register_public_inputs(&main_pod.id.elements);
1506-
builder.register_public_inputs(&main_pod.vds_root.elements);
1507-
Ok(main_pod)
1508-
}
1509-
}
1510-
1511-
impl InnerCircuit for MainPodVerifyTarget {
1512-
type Input = MainPodVerifyInput;
1513-
type Params = Params;
1514-
1515-
fn build(
1516-
builder: &mut CircuitBuilder,
1517-
params: &Self::Params,
1518-
verified_proofs: &[VerifiedProofTarget],
1519-
) -> Result<Self> {
1520-
MainPodVerifyCircuit {
1521-
params: params.clone(),
1522-
}
1523-
.eval(builder, verified_proofs)
1524-
}
1525-
1526-
/// assigns the values to the targets
1527-
fn set_targets(&self, pw: &mut PartialWitness<F>, input: &Self::Input) -> Result<()> {
1528-
self.set_targets(pw, input)
1529-
}
1530-
}
1531-
15321562
#[cfg(test)]
15331563
mod tests {
15341564
use std::{iter, ops::Not};
@@ -1567,7 +1597,7 @@ mod tests {
15671597
..Default::default()
15681598
};
15691599
let mp_gadget = MerkleProofGadget {
1570-
max_depth: params.max_depth_mt_gadget,
1600+
max_depth: params.max_depth_mt_containers,
15711601
};
15721602

15731603
let config = CircuitConfig::standard_recursion_config();
@@ -1581,7 +1611,7 @@ mod tests {
15811611
let merkle_proofs_target: Vec<_> = merkle_proofs
15821612
.iter()
15831613
.map(|_| mp_gadget.eval(&mut builder))
1584-
.collect::<Result<_>>()?;
1614+
.collect();
15851615
let merkle_claims_target: Vec<_> = merkle_proofs_target
15861616
.clone()
15871617
.into_iter()
@@ -2360,7 +2390,7 @@ mod tests {
23602390
]
23612391
.into_iter()
23622392
.collect();
2363-
let mt = MerkleTree::new(params.max_depth_mt_gadget, &kvs)?;
2393+
let mt = MerkleTree::new(params.max_depth_mt_containers, &kvs)?;
23642394

23652395
let root = Value::from(mt.root());
23662396
let root_ak = AnchoredKey::from((PodId(RawValue::from(88).into()), "merkle root"));
@@ -2400,7 +2430,7 @@ mod tests {
24002430
]
24012431
.into_iter()
24022432
.collect();
2403-
let mt = MerkleTree::new(params.max_depth_mt_gadget, &kvs)?;
2433+
let mt = MerkleTree::new(params.max_depth_mt_containers, &kvs)?;
24042434

24052435
let root = Value::from(mt.root());
24062436
let root_ak = AnchoredKey::from((PodId(RawValue::from(88).into()), "merkle root"));

src/backends/plonky2/circuits/signedpod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ impl SignedPodVerifyGadget {
4141
let mut mt_proofs = Vec::new();
4242
for _ in 0..self.params.max_signed_pod_values {
4343
let mt_proof = MerkleProofExistenceGadget {
44-
max_depth: self.params.max_depth_mt_gadget,
44+
max_depth: self.params.max_depth_mt_containers,
4545
}
4646
.eval(builder)?;
4747
builder.connect_hashes(id, mt_proof.root);

0 commit comments

Comments
 (0)