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

@ -294,7 +294,7 @@ mod tests {
backends::plonky2::mock::mainpod::MockProver,
examples::custom::{eth_dos_batch, eth_friend_batch},
frontend::MainPodBuilder,
middleware::{self, containers::Set, CustomPredicateRef, Params, PodType},
middleware::{self, containers::Set, CustomPredicateRef, Params, PodType, DEFAULT_VD_SET},
op,
};
@ -328,6 +328,7 @@ mod tests {
#[test]
fn test_desugared_gt_custom_pred() -> Result<()> {
let params = Params::default();
let vd_set = &*DEFAULT_VD_SET;
let mut builder = CustomPredicateBatchBuilder::new(params.clone(), "gt_custom_pred".into());
let gt_stb = StatementTmplBuilder::new(NativePredicate::Gt)
@ -344,7 +345,7 @@ mod tests {
let batch_clone = batch.clone();
let gt_custom_pred = CustomPredicateRef::new(batch, 0);
let mut mp_builder = MainPodBuilder::new(&params);
let mut mp_builder = MainPodBuilder::new(&params, &vd_set);
// 2 > 1
let s1 = mp_builder.literal(true, Value::from(2))?;
@ -376,6 +377,7 @@ mod tests {
#[test]
fn test_desugared_set_contains_custom_pred() -> Result<()> {
let params = Params::default();
let vd_set = &*DEFAULT_VD_SET;
let mut builder =
CustomPredicateBatchBuilder::new(params.clone(), "set_contains_custom_pred".into());
@ -392,10 +394,13 @@ mod tests {
let batch = builder.finish();
let batch_clone = batch.clone();
let mut mp_builder = MainPodBuilder::new(&params);
let mut mp_builder = MainPodBuilder::new(&params, &vd_set);
let set_values: HashSet<Value> = [1, 2, 3].iter().map(|i| Value::from(*i)).collect();
let s1 = mp_builder.literal(true, Value::from(Set::new(set_values)?))?;
let s1 = mp_builder.literal(
true,
Value::from(Set::new(params.max_depth_mt_containers, set_values)?),
)?;
let s2 = mp_builder.literal(true, Value::from(1))?;
let set_contains = mp_builder.pub_op(op!(set_contains, s1, s2))?;

View file

@ -10,8 +10,7 @@ use serialization::{SerializedMainPod, SerializedSignedPod};
use crate::middleware::{
self, check_st_tmpl, hash_str, hash_values, AnchoredKey, Hash, Key, MainPodInputs,
NativeOperation, NativePredicate, OperationAux, OperationType, Params, PodId, PodProver,
PodSigner, Predicate, Statement, StatementArg, Value, WildcardValue, EMPTY_HASH, KEY_TYPE,
SELF,
PodSigner, Predicate, Statement, StatementArg, VDSet, Value, WildcardValue, KEY_TYPE, SELF,
};
mod custom;
@ -117,6 +116,7 @@ impl SignedPod {
#[derive(Debug)]
pub struct MainPodBuilder {
pub params: Params,
pub vd_set: VDSet,
pub input_signed_pods: Vec<SignedPod>,
pub input_main_pods: Vec<MainPod>,
pub statements: Vec<Statement>,
@ -151,9 +151,10 @@ impl fmt::Display for MainPodBuilder {
}
impl MainPodBuilder {
pub fn new(params: &Params) -> Self {
pub fn new(params: &Params, vd_set: &VDSet) -> Self {
Self {
params: params.clone(),
vd_set: vd_set.clone(),
input_signed_pods: Vec::new(),
input_main_pods: Vec::new(),
statements: Vec::new(),
@ -548,6 +549,7 @@ impl MainPodBuilder {
};
let (statements, operations, public_statements) = compiler.compile(inputs, params)?;
let inputs = MainPodInputs {
signed_pods: &self
.input_signed_pods
@ -562,9 +564,9 @@ impl MainPodBuilder {
statements: &statements,
operations: &operations,
public_statements: &public_statements,
vds_root: EMPTY_HASH, // TODO https://github.com/0xPARC/pod2/issues/249
vds_set: self.vd_set.clone(),
};
let pod = prover.prove(&self.params, inputs)?;
let pod = prover.prove(&self.params, &self.vd_set, inputs)?;
// Gather public statements, making sure to inject the type
// information specified by the backend.
@ -838,7 +840,7 @@ pub mod tests {
eth_dos_pod_builder, eth_friend_signed_pod_builder, great_boy_pod_full_flow,
tickets_pod_full_flow, zu_kyc_pod_builder, zu_kyc_sign_pod_builders,
},
middleware::{containers::Dictionary, Value},
middleware::{containers::Dictionary, Value, DEFAULT_VD_SET},
};
// Check that frontend public statements agree with those
@ -873,6 +875,7 @@ pub mod tests {
#[test]
fn test_front_zu_kyc() -> Result<()> {
let params = Params::default();
let vd_set = &*DEFAULT_VD_SET;
let (gov_id, pay_stub, sanction_list) = zu_kyc_sign_pod_builders(&params);
println!("{}", gov_id);
@ -899,7 +902,7 @@ pub mod tests {
check_kvs(&sanction_list)?;
println!("{}", sanction_list);
let kyc_builder = zu_kyc_pod_builder(&params, &gov_id, &pay_stub, &sanction_list)?;
let kyc_builder = zu_kyc_pod_builder(&params, &vd_set, &gov_id, &pay_stub, &sanction_list)?;
println!("{}", kyc_builder);
// prove kyc with MockProver and print it
@ -926,6 +929,7 @@ pub mod tests {
max_custom_predicate_wildcards: 12,
..Default::default()
};
let vd_set = &*DEFAULT_VD_SET;
let mut alice = MockSigner { pk: "Alice".into() };
let bob = MockSigner { pk: "Bob".into() };
@ -945,6 +949,7 @@ pub mod tests {
let mut prover = MockProver {};
let alice_bob_ethdos = eth_dos_pod_builder(
&params,
&vd_set,
true,
&alice_attestation,
&charlie_attestation,
@ -978,13 +983,15 @@ pub mod tests {
#[should_panic]
fn test_equal() {
let params = Params::default();
let vd_set = &*DEFAULT_VD_SET;
let mut signed_builder = SignedPodBuilder::new(&params);
signed_builder.insert("a", 1);
signed_builder.insert("b", 1);
let mut signer = MockSigner { pk: "key".into() };
let signed_pod = signed_builder.sign(&mut signer).unwrap();
let mut builder = MainPodBuilder::new(&params);
let mut builder = MainPodBuilder::new(&params, &vd_set);
builder.add_signed_pod(&signed_pod);
//let op_val1 = Operation{
@ -1028,6 +1035,7 @@ pub mod tests {
#[should_panic]
fn test_false_st() {
let params = Params::default();
let vd_set = &*DEFAULT_VD_SET;
let mut builder = SignedPodBuilder::new(&params);
builder.insert("num", 2);
@ -1039,7 +1047,7 @@ pub mod tests {
println!("{}", pod);
let mut builder = MainPodBuilder::new(&params);
let mut builder = MainPodBuilder::new(&params, &vd_set);
builder.add_signed_pod(&pod);
builder.pub_op(op!(gt, (&pod, "num"), 5)).unwrap();
@ -1053,6 +1061,7 @@ pub mod tests {
#[test]
fn test_dictionaries() -> Result<()> {
let params = Params::default();
let vd_set = &*DEFAULT_VD_SET;
let mut builder = SignedPodBuilder::new(&params);
let mut my_dict_kvs: HashMap<Key, Value> = HashMap::new();
@ -1061,7 +1070,7 @@ pub mod tests {
my_dict_kvs.insert(Key::from("c"), Value::from(3));
// let my_dict_as_mt = MerkleTree::new(5, &my_dict_kvs).unwrap();
// let dict = Dictionary { mt: my_dict_as_mt };
let dict = Dictionary::new(my_dict_kvs)?;
let dict = Dictionary::new(params.max_depth_mt_containers, my_dict_kvs)?;
let dict_root = Value::from(dict.clone());
builder.insert("dict", dict_root);
@ -1070,7 +1079,7 @@ pub mod tests {
};
let pod = builder.sign(&mut signer).unwrap();
let mut builder = MainPodBuilder::new(&params);
let mut builder = MainPodBuilder::new(&params, &vd_set);
builder.add_signed_pod(&pod);
let st0 = pod.get_statement("dict").unwrap();
let st1 = builder.op(true, op!(new_entry, ("key", "a"))).unwrap();
@ -1106,7 +1115,8 @@ pub mod tests {
env_logger::init();
let params = Params::default();
let mut builder = MainPodBuilder::new(&params);
let vd_set = &*DEFAULT_VD_SET;
let mut builder = MainPodBuilder::new(&params, &vd_set);
let st = Statement::ValueOf(AnchoredKey::from((SELF, "a")), Value::from(3));
let op_new_entry = Operation(
OperationType::Native(NativeOperation::NewEntry),
@ -1124,8 +1134,7 @@ pub mod tests {
// try to insert a statement that doesn't follow from the operation
// right now the mock prover catches this when it calls compile()
let params = Params::default();
let mut builder = MainPodBuilder::new(&params);
let mut builder = MainPodBuilder::new(&params, &vd_set);
let self_a = AnchoredKey::from((SELF, "a"));
let self_b = AnchoredKey::from((SELF, "b"));
let value_of_a = Statement::ValueOf(self_a.clone(), Value::from(3));

View file

@ -112,24 +112,25 @@ mod tests {
middleware::{
self,
containers::{Array, Dictionary, Set},
Params, TypedValue,
Params, TypedValue, DEFAULT_VD_SET,
},
};
#[test]
fn test_value_serialization() {
let params = &Params::default();
// Pairs of values and their expected serialized representations
let values = vec![
(TypedValue::String("hello".to_string()), "\"hello\""),
(TypedValue::Int(42), "{\"Int\":\"42\"}"),
(TypedValue::Bool(true), "true"),
(
TypedValue::Array(Array::new(vec!["foo".into(), false.into()]).unwrap()),
"[\"foo\",false]",
TypedValue::Array(Array::new(params.max_depth_mt_containers, vec!["foo".into(), false.into()]).unwrap()),
"{\"max_depth\":32,\"array\":[\"foo\",false]}",
),
(
TypedValue::Dictionary(
Dictionary::new(HashMap::from([
Dictionary::new(params.max_depth_mt_containers, HashMap::from([
// The set of valid keys is equal to the set of valid JSON keys
("foo".into(), 123.into()),
// Empty strings are valid JSON keys
@ -145,11 +146,11 @@ mod tests {
]))
.unwrap(),
),
"{\"Dictionary\":{\"\":\"baz\",\"\\u0000\":\"\",\" hi\":false,\"!@£$%^&&*()\":\"\",\"foo\":{\"Int\":\"123\"},\"🥳\":\"party time!\"}}",
"{\"Dictionary\":{\"max_depth\":32,\"kvs\":{\"\":\"baz\",\"\\u0000\":\"\",\" hi\":false,\"!@£$%^&&*()\":\"\",\"foo\":{\"Int\":\"123\"},\"🥳\":\"party time!\"}}}",
),
(
TypedValue::Set(Set::new(HashSet::from(["foo".into(), "bar".into()])).unwrap()),
"{\"Set\":[\"bar\",\"foo\"]}",
TypedValue::Set(Set::new(params.max_depth_mt_containers, HashSet::from(["foo".into(), "bar".into()])).unwrap()),
"{\"Set\":{\"max_depth\":32,\"set\":[\"bar\",\"foo\"]}}",
),
];
@ -168,30 +169,45 @@ mod tests {
}
fn signed_pod_builder() -> SignedPodBuilder {
let mut builder = SignedPodBuilder::new(&Params::default());
let params = &Params::default();
let mut builder = SignedPodBuilder::new(params);
builder.insert("name", "test");
builder.insert("age", 30);
builder.insert("very_large_int", 1152921504606846976);
builder.insert(
"a_dict_containing_one_key",
Dictionary::new(HashMap::from([
("foo".into(), 123.into()),
(
"an_array_containing_three_ints".into(),
Array::new(vec![1.into(), 2.into(), 3.into()])
Dictionary::new(
params.max_depth_mt_containers,
HashMap::from([
("foo".into(), 123.into()),
(
"an_array_containing_three_ints".into(),
Array::new(
params.max_depth_mt_containers,
vec![1.into(), 2.into(), 3.into()],
)
.unwrap()
.into(),
),
(
"a_set_containing_two_strings".into(),
Set::new(HashSet::from([
Array::new(vec!["foo".into(), "bar".into()]).unwrap().into(),
"baz".into(),
]))
.unwrap()
.into(),
),
]))
),
(
"a_set_containing_two_strings".into(),
Set::new(
params.max_depth_mt_containers,
HashSet::from([
Array::new(
params.max_depth_mt_containers,
vec!["foo".into(), "bar".into()],
)
.unwrap()
.into(),
"baz".into(),
]),
)
.unwrap()
.into(),
),
]),
)
.unwrap(),
);
builder
@ -235,6 +251,7 @@ mod tests {
fn build_mock_zukyc_pod() -> Result<MainPod> {
let params = middleware::Params::default();
let vd_set = &*DEFAULT_VD_SET;
let (gov_id_builder, pay_stub_builder, sanction_list_builder) =
zu_kyc_sign_pod_builders(&params);
@ -250,8 +267,14 @@ mod tests {
pk: "ZooOFAC".into(),
};
let sanction_list_pod = sanction_list_builder.sign(&mut signer).unwrap();
let kyc_builder =
zu_kyc_pod_builder(&params, &gov_id_pod, &pay_stub_pod, &sanction_list_pod).unwrap();
let kyc_builder = zu_kyc_pod_builder(
&params,
&vd_set,
&gov_id_pod,
&pay_stub_pod,
&sanction_list_pod,
)
.unwrap();
let mut prover = MockProver {};
let kyc_pod = kyc_builder.prove(&mut prover, &params).unwrap();
@ -265,6 +288,7 @@ mod tests {
max_input_recursive_pods: 1,
..Default::default()
};
let vd_set = &*DEFAULT_VD_SET;
let (gov_id_builder, pay_stub_builder, sanction_list_builder) =
zu_kyc_sign_pod_builders(&params);
@ -274,8 +298,13 @@ mod tests {
let pay_stub_pod = pay_stub_builder.sign(&mut signer)?;
let mut signer = Signer(SecretKey(3u32.into()));
let sanction_list_pod = sanction_list_builder.sign(&mut signer)?;
let kyc_builder =
zu_kyc_pod_builder(&params, &gov_id_pod, &pay_stub_pod, &sanction_list_pod)?;
let kyc_builder = zu_kyc_pod_builder(
&params,
&vd_set,
&gov_id_pod,
&pay_stub_pod,
&sanction_list_pod,
)?;
let mut prover = Prover {};
let kyc_pod = kyc_builder.prove(&mut prover, &params)?;
@ -324,6 +353,7 @@ mod tests {
max_custom_predicate_wildcards: 12,
..Default::default()
};
let vd_set = &*DEFAULT_VD_SET;
let mut alice = MockSigner { pk: "Alice".into() };
let bob = MockSigner { pk: "Bob".into() };
@ -341,6 +371,7 @@ mod tests {
let mut prover = MockProver {};
let alice_bob_ethdos = eth_dos_pod_builder(
&params,
&vd_set,
true,
&alice_attestation,
&charlie_attestation,