From f10a5adb41474b81e774c7c7999f5a0740514734 Mon Sep 17 00:00:00 2001 From: Rob Knight Date: Wed, 30 Jul 2025 02:46:14 +0100 Subject: [PATCH] Check a single POD against a POD Request (#359) --- src/backends/plonky2/mainpod/mod.rs | 13 +- src/backends/plonky2/mock/mainpod.rs | 22 ++- src/examples/custom.rs | 19 +- src/examples/mod.rs | 71 +++++--- src/frontend/error.rs | 16 ++ src/frontend/mod.rs | 39 +++- src/frontend/pod_request.rs | 255 +++++++++++++++++++++++++++ src/frontend/serialization.rs | 27 +-- src/lang/mod.rs | 31 ++-- src/lang/pretty_print.rs | 20 ++- src/lang/processor.rs | 8 +- 11 files changed, 419 insertions(+), 102 deletions(-) create mode 100644 src/frontend/pod_request.rs diff --git a/src/backends/plonky2/mainpod/mod.rs b/src/backends/plonky2/mainpod/mod.rs index 6b11796..115336c 100644 --- a/src/backends/plonky2/mainpod/mod.rs +++ b/src/backends/plonky2/mainpod/mod.rs @@ -768,21 +768,12 @@ pub mod tests { vds.push(rec_main_pod_circuit_data(¶ms).1.verifier_only.clone()); let vd_set = VDSet::new(params.max_depth_mt_vds, &vds).unwrap(); - let (gov_id_builder, pay_stub_builder, sanction_list_builder) = - zu_kyc_sign_pod_builders(¶ms); + let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_pod_builders(¶ms); let signer = Signer(SecretKey(BigUint::one())); let gov_id_pod = gov_id_builder.sign(&signer)?; let signer = Signer(SecretKey(2u64.into())); let pay_stub_pod = pay_stub_builder.sign(&signer)?; - let signer = Signer(SecretKey(3u64.into())); - let sanction_list_pod = sanction_list_builder.sign(&signer)?; - let kyc_builder = zu_kyc_pod_builder( - ¶ms, - &vd_set, - &gov_id_pod, - &pay_stub_pod, - &sanction_list_pod, - )?; + let kyc_builder = zu_kyc_pod_builder(¶ms, &vd_set, &gov_id_pod, &pay_stub_pod)?; let prover = Prover {}; let kyc_pod = kyc_builder.prove(&prover, ¶ms)?; diff --git a/src/backends/plonky2/mock/mainpod.rs b/src/backends/plonky2/mock/mainpod.rs index f385030..d58c81f 100644 --- a/src/backends/plonky2/mock/mainpod.rs +++ b/src/backends/plonky2/mock/mainpod.rs @@ -427,7 +427,7 @@ pub mod tests { use crate::{ backends::plonky2::{primitives::ec::schnorr::SecretKey, signedpod::Signer}, examples::{ - great_boy_pod_full_flow, tickets_pod_full_flow, zu_kyc_pod_builder, + great_boy_pod_full_flow, tickets_pod_full_flow, zu_kyc_pod_builder, zu_kyc_pod_request, zu_kyc_sign_pod_builders, MOCK_VD_SET, }, frontend, middleware, @@ -437,21 +437,12 @@ pub mod tests { fn test_mock_main_zu_kyc() -> frontend::Result<()> { let params = middleware::Params::default(); let vd_set = &*MOCK_VD_SET; - let (gov_id_builder, pay_stub_builder, sanction_list_builder) = - zu_kyc_sign_pod_builders(¶ms); + let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_pod_builders(¶ms); let signer = Signer(SecretKey(1u32.into())); let gov_id_pod = gov_id_builder.sign(&signer)?; let signer = Signer(SecretKey(2u32.into())); let pay_stub_pod = pay_stub_builder.sign(&signer)?; - let signer = Signer(SecretKey(3u32.into())); - let sanction_list_pod = sanction_list_builder.sign(&signer)?; - let kyc_builder = zu_kyc_pod_builder( - ¶ms, - vd_set, - &gov_id_pod, - &pay_stub_pod, - &sanction_list_pod, - )?; + let kyc_builder = zu_kyc_pod_builder(¶ms, vd_set, &gov_id_pod, &pay_stub_pod)?; let prover = MockProver {}; let kyc_pod = kyc_builder.prove(&prover, ¶ms)?; @@ -462,6 +453,13 @@ pub mod tests { println!("{:#}", pod); pod.verify()?; + + let request = zu_kyc_pod_request( + gov_id_pod.get("_signer").unwrap(), + pay_stub_pod.get("_signer").unwrap(), + )?; + assert!(request.exact_match_pod(&*pod).is_ok()); + Ok(()) } diff --git a/src/examples/custom.rs b/src/examples/custom.rs index 7bafb66..062b296 100644 --- a/src/examples/custom.rs +++ b/src/examples/custom.rs @@ -1,7 +1,9 @@ use std::sync::Arc; +use hex::ToHex; + use crate::{ - frontend::Result, + frontend::{PodRequest, Result}, lang::parse, middleware::{CustomPredicateBatch, Params, PodType, Value, KEY_SIGNER, KEY_TYPE}, }; @@ -53,6 +55,21 @@ pub fn eth_dos_batch(params: &Params) -> Result> { Ok(batch) } +pub fn eth_dos_request() -> Result { + let batch = eth_dos_batch(&Params::default())?; + let batch_id = batch.id().encode_hex::(); + let input = format!( + r#" + use _, _, _, eth_dos from 0x{batch_id} + REQUEST( + eth_dos(?src, ?dst, ?distance) + ) + "#, + ); + let parsed = parse(&input, &Params::default(), &[batch])?; + Ok(parsed.request) +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/examples/mod.rs b/src/examples/mod.rs index 04d73a7..ea71f81 100644 --- a/src/examples/mod.rs +++ b/src/examples/mod.rs @@ -9,7 +9,8 @@ pub static MOCK_VD_SET: LazyLock = LazyLock::new(|| VDSet::new(6, &[]).un use crate::{ backends::plonky2::{primitives::ec::schnorr::SecretKey, signedpod::Signer}, - frontend::{MainPod, MainPodBuilder, Result, SignedPod, SignedPodBuilder}, + frontend::{MainPod, MainPodBuilder, PodRequest, Result, SignedPod, SignedPodBuilder}, + lang::parse, middleware::{ containers::Set, hash_values, CustomPredicateRef, Params, PodSigner, PodType, Predicate, Statement, StatementArg, TypedValue, VDSet, Value, KEY_SIGNER, KEY_TYPE, @@ -19,13 +20,7 @@ use crate::{ // ZuKYC -pub fn zu_kyc_sign_pod_builders( - params: &Params, -) -> (SignedPodBuilder, SignedPodBuilder, SignedPodBuilder) { - let sanctions_values: HashSet = ["A343434340"].iter().map(|s| Value::from(*s)).collect(); - let sanction_set = - Value::from(Set::new(params.max_depth_mt_containers, sanctions_values).unwrap()); - +pub fn zu_kyc_sign_pod_builders(params: &Params) -> (SignedPodBuilder, SignedPodBuilder) { let mut gov_id = SignedPodBuilder::new(params); gov_id.insert("idNumber", "4242424242"); gov_id.insert("dateOfBirth", 1169909384); @@ -35,32 +30,32 @@ pub fn zu_kyc_sign_pod_builders( pay_stub.insert("socialSecurityNumber", "G2121210"); pay_stub.insert("startDate", 1706367566); - let mut sanction_list = SignedPodBuilder::new(params); - - sanction_list.insert("sanctionList", sanction_set); - - (gov_id, pay_stub, sanction_list) + (gov_id, pay_stub) } +pub const ZU_KYC_NOW_MINUS_18Y: i64 = 1169909388; +pub const ZU_KYC_NOW_MINUS_1Y: i64 = 1706367566; +pub const ZU_KYC_SANCTION_LIST: &[&str] = &["A343434340"]; + pub fn zu_kyc_pod_builder( params: &Params, vd_set: &VDSet, gov_id: &SignedPod, pay_stub: &SignedPod, - sanction_list: &SignedPod, ) -> Result { - let now_minus_18y: i64 = 1169909388; - let now_minus_1y: i64 = 1706367566; + let now_minus_18y = ZU_KYC_NOW_MINUS_18Y; + let now_minus_1y = ZU_KYC_NOW_MINUS_1Y; + let sanctions_values: HashSet = ZU_KYC_SANCTION_LIST + .iter() + .map(|s| Value::from(*s)) + .collect(); + let sanction_set = + Value::from(Set::new(params.max_depth_mt_containers, sanctions_values).unwrap()); let mut kyc = MainPodBuilder::new(params, vd_set); kyc.add_signed_pod(gov_id); kyc.add_signed_pod(pay_stub); - kyc.add_signed_pod(sanction_list); - kyc.pub_op(op!( - set_not_contains, - (sanction_list, "sanctionList"), - (gov_id, "idNumber") - ))?; + kyc.pub_op(op!(set_not_contains, sanction_set, (gov_id, "idNumber")))?; kyc.pub_op(op!(lt, (gov_id, "dateOfBirth"), now_minus_18y))?; kyc.pub_op(op!( eq, @@ -68,10 +63,42 @@ pub fn zu_kyc_pod_builder( (pay_stub, "socialSecurityNumber") ))?; kyc.pub_op(op!(eq, (pay_stub, "startDate"), now_minus_1y))?; + kyc.pub_op(op!(eq, (gov_id, "_signer"), gov_id.get("_signer").unwrap()))?; + kyc.pub_op(op!( + eq, + (pay_stub, "_signer"), + pay_stub.get("_signer").unwrap() + ))?; Ok(kyc) } +pub fn zu_kyc_pod_request(gov_signer: &Value, pay_signer: &Value) -> Result { + let params = Params::default(); + let sanctions_values: HashSet = ZU_KYC_SANCTION_LIST + .iter() + .map(|s| Value::from(*s)) + .collect(); + let sanction_set = + Value::from(Set::new(params.max_depth_mt_containers, sanctions_values).unwrap()); + let input = format!( + r#" + REQUEST( + SetNotContains({sanction_set}, ?gov["idNumber"]) + Lt(?gov["dateOfBirth"], {ZU_KYC_NOW_MINUS_18Y}) + Equal(?pay["startDate"], {ZU_KYC_NOW_MINUS_1Y}) + Equal(?gov["socialSecurityNumber"], ?pay["socialSecurityNumber"]) + Equal(?gov["_signer"], {gov_signer}) + Equal(?pay["_signer"], {pay_signer}) + // TODO: Ownership check and watermarking + // Depends partly on https://github.com/0xPARC/pod2/issues/351 + ) + "#, + ); + let parsed = parse(&input, &Params::default(), &[])?; + Ok(parsed.request) +} + // ETHDoS pub fn attest_eth_friend(params: &Params, src: &impl PodSigner, dst: Value) -> SignedPod { diff --git a/src/frontend/error.rs b/src/frontend/error.rs index 45491fe..6bdae2e 100644 --- a/src/frontend/error.rs +++ b/src/frontend/error.rs @@ -31,6 +31,10 @@ pub enum InnerError { ), #[error("invalid arguments to {0} operation")] OpInvalidArgs(String), + #[error("Podlang parse error: {0}")] + PodlangParse(String), + #[error("POD Request validation error: {0}")] + PodRequestValidation(String), // Other #[error("{0}")] Custom(String), @@ -55,6 +59,12 @@ impl From for Error { } } +impl From for Error { + fn from(value: crate::lang::LangError) -> Self { + Error::podlang_parse(value) + } +} + impl Debug for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self, f) @@ -88,4 +98,10 @@ impl Error { pub(crate) fn max_length(obj: String, found: usize, expect: usize) -> Self { new!(MaxLength(obj, found, expect)) } + pub(crate) fn podlang_parse(e: crate::lang::LangError) -> Self { + new!(PodlangParse(e.to_string())) + } + pub(crate) fn pod_request_validation(e: String) -> Self { + new!(PodRequestValidation(e)) + } } diff --git a/src/frontend/mod.rs b/src/frontend/mod.rs index 3993630..315a69e 100644 --- a/src/frontend/mod.rs +++ b/src/frontend/mod.rs @@ -16,10 +16,12 @@ use crate::middleware::{ mod custom; mod error; mod operation; +mod pod_request; mod serialization; pub use custom::*; pub use error::*; pub use operation::*; +pub use pod_request::*; #[derive(Clone, Debug)] pub struct SignedPodBuilder { @@ -857,7 +859,8 @@ pub mod tests { mock::mainpod::MockProver, primitives::ec::schnorr::SecretKey, signedpod::Signer, }, examples::{ - attest_eth_friend, great_boy_pod_full_flow, tickets_pod_full_flow, zu_kyc_pod_builder, + attest_eth_friend, custom::eth_dos_request, great_boy_pod_full_flow, + tickets_pod_full_flow, zu_kyc_pod_builder, zu_kyc_pod_request, zu_kyc_sign_pod_builders, EthDosHelper, MOCK_VD_SET, }, middleware::{containers::Dictionary, Value}, @@ -896,7 +899,7 @@ pub mod tests { fn test_front_zu_kyc() -> Result<()> { let params = Params::default(); let vd_set = &*MOCK_VD_SET; - let (gov_id, pay_stub, sanction_list) = zu_kyc_sign_pod_builders(¶ms); + let (gov_id, pay_stub) = zu_kyc_sign_pod_builders(¶ms); println!("{}", gov_id); println!("{}", pay_stub); @@ -911,12 +914,7 @@ pub mod tests { check_kvs(&pay_stub)?; println!("{}", pay_stub); - let signer = Signer(SecretKey(3u32.into())); - let sanction_list = sanction_list.sign(&signer)?; - check_kvs(&sanction_list)?; - println!("{}", sanction_list); - - let kyc_builder = zu_kyc_pod_builder(¶ms, vd_set, &gov_id, &pay_stub, &sanction_list)?; + let kyc_builder = zu_kyc_pod_builder(¶ms, vd_set, &gov_id, &pay_stub)?; println!("{}", kyc_builder); // prove kyc with MockProver and print it @@ -925,6 +923,15 @@ pub mod tests { println!("{}", kyc); + let request = zu_kyc_pod_request( + gov_id.get("_signer").unwrap(), + pay_stub.get("_signer").unwrap(), + )?; + // Check the bindings of the "gov" and "pay" wildcards from the PodRequest + let bindings = request.exact_match_pod(&*kyc.pod).unwrap(); + assert_eq!(*bindings.get("gov").unwrap(), gov_id.id().into()); + assert_eq!(*bindings.get("pay").unwrap(), pay_stub.id().into()); + check_public_statements(&kyc) } @@ -950,18 +957,34 @@ pub mod tests { let alice_attestation = attest_eth_friend(¶ms, &alice, bob.public_key()); let dist_1 = helper.dist_1(&alice_attestation)?.prove(&prover, ¶ms)?; dist_1.pod.verify()?; + let request = eth_dos_request()?; + assert!(request.exact_match_pod(&*dist_1.pod).is_ok()); + let bindings = request.exact_match_pod(&*dist_1.pod).unwrap(); + assert_eq!(*bindings.get("src").unwrap(), alice.public_key()); + assert_eq!(*bindings.get("dst").unwrap(), bob.public_key()); + assert_eq!(*bindings.get("distance").unwrap(), 1.into()); let bob_attestation = attest_eth_friend(¶ms, &bob, charlie.public_key()); let dist_2 = helper .dist_n_plus_1(&dist_1, &bob_attestation)? .prove(&prover, ¶ms)?; dist_2.pod.verify()?; + assert!(request.exact_match_pod(&*dist_2.pod).is_ok()); + let bindings = request.exact_match_pod(&*dist_2.pod).unwrap(); + assert_eq!(*bindings.get("src").unwrap(), alice.public_key()); + assert_eq!(*bindings.get("dst").unwrap(), charlie.public_key()); + assert_eq!(*bindings.get("distance").unwrap(), 2.into()); let charlie_attestation = attest_eth_friend(¶ms, &charlie, david.public_key()); let dist_3 = helper .dist_n_plus_1(&dist_2, &charlie_attestation)? .prove(&prover, ¶ms)?; dist_3.pod.verify()?; + assert!(request.exact_match_pod(&*dist_3.pod).is_ok()); + let bindings = request.exact_match_pod(&*dist_3.pod).unwrap(); + assert_eq!(*bindings.get("src").unwrap(), alice.public_key()); + assert_eq!(*bindings.get("dst").unwrap(), david.public_key()); + assert_eq!(*bindings.get("distance").unwrap(), 3.into()); Ok(()) } diff --git a/src/frontend/pod_request.rs b/src/frontend/pod_request.rs new file mode 100644 index 0000000..0f39b23 --- /dev/null +++ b/src/frontend/pod_request.rs @@ -0,0 +1,255 @@ +use std::{collections::HashMap, fmt::Display}; + +use crate::{ + frontend::{Error, Result}, + lang::PrettyPrint, + middleware::{Pod, Statement, StatementArg, StatementTmpl, StatementTmplArg, Value}, +}; + +/// Represents a request for a POD, in terms of a set of statement templates. +/// The response should be a POD (or PODs) containing a set of statements which +/// satisfy the templates, with consistent wildcard bindings across all templates. +#[derive(Debug, Clone, PartialEq)] +pub struct PodRequest { + pub request_templates: Vec, +} + +impl PodRequest { + pub fn new(request_templates: Vec) -> Self { + Self { request_templates } + } + + /// Checks if the request is fully satisfied by a single supplied POD. + /// This checks for exact matches to the statement templates; that is to say + /// that it performs a "syntactic" match, not a "semantic" match; no + /// processing of the semantics of the statements is performed. + pub fn exact_match_pod(&self, pod: &dyn Pod) -> Result> { + let pod_statements = pod.pub_statements(); + let mut bindings: HashMap = HashMap::new(); + + if self.dfs_match_all(&pod_statements, &mut bindings, 0) { + Ok(bindings) + } else { + Err(Error::pod_request_validation("No match found".to_string())) + } + } + + /// Performs a depth-first search through the statement templates, trying to + /// match each template to a statement in the POD. + /// Returns true if all templates are matched, false otherwise. + /// The bindings map is used to store the bindings of the wildcards to the + /// values in the POD. + /// The template_idx is used to track the current template being matched. + fn dfs_match_all( + &self, + pod_statements: &[Statement], + bindings: &mut HashMap, + template_idx: usize, + ) -> bool { + // Base case: all templates matched + if template_idx >= self.request_templates.len() { + return true; + } + + let template = &self.request_templates[template_idx]; + + // Try to match this template with each statement in the POD + for stmt in pod_statements { + if let Some(new_bindings) = self.try_match_template(template, stmt, bindings) { + let original_bindings = bindings.clone(); + bindings.extend(new_bindings); + + if self.dfs_match_all(pod_statements, bindings, template_idx + 1) { + return true; + } + + *bindings = original_bindings; + } + } + + false + } + + fn try_match_template( + &self, + template: &StatementTmpl, + statement: &Statement, + current_bindings: &HashMap, + ) -> Option> { + if template.pred != statement.predicate() { + return None; + } + + let template_args = template.args(); + let statement_args = statement.args(); + + if template_args.len() != statement_args.len() { + return None; + } + + let mut new_bindings = HashMap::new(); + + for (tmpl_arg, stmt_arg) in template_args.iter().zip(statement_args.iter()) { + if !self.try_match_arg(tmpl_arg, stmt_arg, current_bindings, &mut new_bindings) { + return None; + } + } + + Some(new_bindings) + } + + fn try_match_arg( + &self, + template_arg: &StatementTmplArg, + statement_arg: &StatementArg, + current_bindings: &HashMap, + new_bindings: &mut HashMap, + ) -> bool { + match (template_arg, statement_arg) { + // Literal must match exactly + (StatementTmplArg::Literal(tmpl_val), StatementArg::Literal(stmt_val)) => { + tmpl_val == stmt_val + } + + // Wildcard can bind to any literal value + (StatementTmplArg::Wildcard(wildcard), StatementArg::Literal(stmt_val)) => self + .try_bind_wildcard( + &wildcard.name, + stmt_val.clone(), + current_bindings, + new_bindings, + ), + + // AnchoredKey wildcard must match statement's anchored key + (StatementTmplArg::AnchoredKey(wildcard, tmpl_key), StatementArg::Key(stmt_key)) => { + // Check if keys match + if tmpl_key != &stmt_key.key { + return false; + } + + // Try to bind wildcard to the POD ID + let pod_id_value = Value::from(stmt_key.pod_id); + self.try_bind_wildcard(&wildcard.name, pod_id_value, current_bindings, new_bindings) + } + + // Other combinations don't match + _ => false, + } + } + + fn try_bind_wildcard( + &self, + wildcard_name: &str, + value: Value, + current_bindings: &HashMap, + new_bindings: &mut HashMap, + ) -> bool { + // Check if wildcard is already bound + if let Some(existing_value) = current_bindings.get(wildcard_name) { + // Must match existing binding + return existing_value == &value; + } + + // Check if we're trying to bind it in new_bindings + if let Some(existing_value) = new_bindings.get(wildcard_name) { + // Must match existing binding in this attempt + return existing_value == &value; + } + + // Bind the wildcard + new_bindings.insert(wildcard_name.to_string(), value); + true + } + + pub fn templates(&self) -> &[StatementTmpl] { + &self.request_templates + } +} + +impl Display for PodRequest { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.fmt_podlang(f) + } +} + +#[cfg(test)] +mod tests { + use crate::{ + backends::plonky2::{ + mock::mainpod::MockProver, primitives::ec::schnorr::SecretKey, signedpod::Signer, + }, + examples::{zu_kyc_pod_builder, zu_kyc_pod_request, zu_kyc_sign_pod_builders, MOCK_VD_SET}, + frontend::{MainPodBuilder, Operation}, + lang::parse, + middleware::Params, + }; + + #[test] + fn test_pod_request_exact_match_pod() { + let params = Params::default(); + let vd_set = &*MOCK_VD_SET; + + let (gov_id, pay_stub) = zu_kyc_sign_pod_builders(¶ms); + let gov_id = gov_id.sign(&Signer(SecretKey(1u32.into()))).unwrap(); + let pay_stub = pay_stub.sign(&Signer(SecretKey(2u32.into()))).unwrap(); + let builder = zu_kyc_pod_builder(&Params::default(), vd_set, &gov_id, &pay_stub).unwrap(); + let prover = MockProver {}; + let kyc = builder.prove(&prover, ¶ms).unwrap(); + + // This request matches the POD + let request = zu_kyc_pod_request( + gov_id.get("_signer").unwrap(), + pay_stub.get("_signer").unwrap(), + ) + .unwrap(); + assert!(request.exact_match_pod(&*kyc.pod).is_ok()); + + // This request does not match the POD, because the POD does not contain a NotEqual statement. + let non_matching_request = parse( + r#" + REQUEST( + NotEqual(4, 5) + ) + "#, + ¶ms, + &[], + ) + .unwrap() + .request; + assert!(non_matching_request.exact_match_pod(&*kyc.pod).is_err()); + } + + #[test] + fn test_ambiguous_pod() { + let params = Params::default(); + let vd_set = &*MOCK_VD_SET; + + let mut builder = MainPodBuilder::new(¶ms, vd_set); + let _sum_of_stmt_1 = builder.pub_op(Operation::sum_of(11, 1, 10)); + let _sum_of_stmt_2 = builder.pub_op(Operation::sum_of(10, 9, 1)); + let _eq_stmt = builder.pub_op(Operation::eq(10, 10)); + + let prover = MockProver {}; + + let pod = builder.prove(&prover, ¶ms).unwrap(); + + println!("{pod}"); + + let request = parse( + r#" + REQUEST( + SumOf(?a, ?b, ?c) + Equal(?a, 10) + ) + "#, + ¶ms, + &[], + ) + .unwrap(); + + let bindings = request.request.exact_match_pod(&*pod.pod).unwrap(); + assert_eq!(*bindings.get("a").unwrap(), 10.into()); + assert_eq!(*bindings.get("b").unwrap(), 9.into()); + assert_eq!(*bindings.get("c").unwrap(), 1.into()); + } +} diff --git a/src/frontend/serialization.rs b/src/frontend/serialization.rs index aa4ac86..8e1d0c3 100644 --- a/src/frontend/serialization.rs +++ b/src/frontend/serialization.rs @@ -267,22 +267,12 @@ mod tests { let params = middleware::Params::default(); let vd_set = &*MOCK_VD_SET; - let (gov_id_builder, pay_stub_builder, sanction_list_builder) = - zu_kyc_sign_pod_builders(¶ms); + let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_pod_builders(¶ms); let signer = Signer(SecretKey(1u32.into())); let gov_id_pod = gov_id_builder.sign(&signer).unwrap(); let signer = Signer(SecretKey(2u32.into())); let pay_stub_pod = pay_stub_builder.sign(&signer).unwrap(); - let signer = Signer(SecretKey(3u32.into())); - let sanction_list_pod = sanction_list_builder.sign(&signer).unwrap(); - let kyc_builder = zu_kyc_pod_builder( - ¶ms, - vd_set, - &gov_id_pod, - &pay_stub_pod, - &sanction_list_pod, - ) - .unwrap(); + let kyc_builder = zu_kyc_pod_builder(¶ms, vd_set, &gov_id_pod, &pay_stub_pod).unwrap(); let prover = MockProver {}; let kyc_pod = kyc_builder.prove(&prover, ¶ms).unwrap(); @@ -300,21 +290,12 @@ mod tests { vds.push(rec_main_pod_circuit_data(¶ms).1.verifier_only.clone()); let vd_set = VDSet::new(params.max_depth_mt_vds, &vds).unwrap(); - let (gov_id_builder, pay_stub_builder, sanction_list_builder) = - zu_kyc_sign_pod_builders(¶ms); + let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_pod_builders(¶ms); let signer = Signer(SecretKey(1u32.into())); let gov_id_pod = gov_id_builder.sign(&signer)?; let signer = Signer(SecretKey(2u32.into())); let pay_stub_pod = pay_stub_builder.sign(&signer)?; - let signer = Signer(SecretKey(3u32.into())); - let sanction_list_pod = sanction_list_builder.sign(&signer)?; - let kyc_builder = zu_kyc_pod_builder( - ¶ms, - &vd_set, - &gov_id_pod, - &pay_stub_pod, - &sanction_list_pod, - )?; + let kyc_builder = zu_kyc_pod_builder(¶ms, &vd_set, &gov_id_pod, &pay_stub_pod)?; let prover = Prover {}; let kyc_pod = kyc_builder.prove(&prover, ¶ms)?; diff --git a/src/lang/mod.rs b/src/lang/mod.rs index 1a02966..05abce4 100644 --- a/src/lang/mod.rs +++ b/src/lang/mod.rs @@ -70,7 +70,7 @@ mod tests { let params = Params::default(); let processed = parse(input, ¶ms, &[])?; let batch_result = processed.custom_batch; - let request_result = processed.request_templates; + let request_result = processed.request.templates(); assert_eq!(request_result.len(), 0); assert_eq!(batch_result.predicates.len(), 1); @@ -115,13 +115,11 @@ mod tests { let params = Params::default(); let processed = parse(input, ¶ms, &[])?; let batch_result = processed.custom_batch; - let request_templates = processed.request_templates; + let request_templates = processed.request.templates(); assert_eq!(batch_result.predicates.len(), 0); assert!(!request_templates.is_empty()); - let request_templates = request_templates; - // Expected structure let expected_templates = vec![ StatementTmpl { @@ -157,7 +155,7 @@ mod tests { let params = Params::default(); let processed = parse(input, ¶ms, &[])?; let batch_result = processed.custom_batch; - let request_result = processed.request_templates; + let request_result = processed.request.templates(); assert_eq!(request_result.len(), 0); assert_eq!(batch_result.predicates.len(), 1); @@ -214,13 +212,12 @@ mod tests { let params = Params::default(); let processed = parse(input, ¶ms, &[])?; let batch_result = processed.custom_batch; - let request_templates = processed.request_templates; + let request_templates = processed.request.templates(); assert_eq!(batch_result.predicates.len(), 1); assert!(!request_templates.is_empty()); let batch = batch_result; - let request_templates = request_templates; // Expected Batch structure let expected_pred_statements = vec![StatementTmpl { @@ -278,13 +275,11 @@ mod tests { let params = Params::default(); let processed = parse(input, ¶ms, &[])?; let batch_result = processed.custom_batch; - let request_templates = processed.request_templates; + let request_templates = processed.request.templates(); assert_eq!(batch_result.predicates.len(), 1); // some_pred is defined assert!(!request_templates.is_empty()); - let request_templates = request_templates; - // Expected Wildcard Indices in Request Scope: // ?Var1 -> 0 // ?AnotherPod -> 1 @@ -330,13 +325,11 @@ mod tests { let params = Params::default(); let processed = parse(input, ¶ms, &[])?; let batch_result = processed.custom_batch; - let request_templates = processed.request_templates; + let request_templates = processed.request.templates(); assert_eq!(batch_result.predicates.len(), 0); assert!(!request_templates.is_empty()); - let request_templates = request_templates; - let expected_templates = vec![ StatementTmpl { pred: Predicate::Native(NativePredicate::LtEq), @@ -389,7 +382,7 @@ mod tests { // Parse the input string let processed = super::parse(input, &Params::default(), &[])?; - let parsed_templates = processed.request_templates; + let parsed_templates = processed.request.templates(); // Define Expected Templates (Copied from prover/mod.rs) let now_minus_18y_val = Value::from(1169909388_i64); @@ -529,7 +522,7 @@ mod tests { let processed = super::parse(input, ¶ms, &[])?; assert!( - processed.request_templates.is_empty(), + processed.request.templates().is_empty(), "Expected no request templates" ); assert_eq!( @@ -719,7 +712,7 @@ mod tests { // 3. Parse the input let processed = parse(&input, ¶ms, &available_batches)?; - let request_templates = processed.request_templates; + let request_templates = processed.request.templates(); assert!( processed.custom_batch.predicates.is_empty(), @@ -771,7 +764,7 @@ mod tests { // 3. Parse the input let processed = parse(&input, ¶ms, &available_batches)?; - let request_templates = processed.request_templates; + let request_templates = processed.request.templates(); assert_eq!(request_templates.len(), 2, "Expected two request templates"); @@ -830,7 +823,7 @@ mod tests { let processed = parse(&input, ¶ms, &available_batches)?; assert!( - processed.request_templates.is_empty(), + processed.request.templates().is_empty(), "No request should be defined" ); assert_eq!( @@ -903,7 +896,7 @@ mod tests { let params = Params::default(); let processed = parse(&input, ¶ms, &[])?; - let request_templates = processed.request_templates; + let request_templates = processed.request.templates(); let expected_templates = vec![ StatementTmpl { diff --git a/src/lang/pretty_print.rs b/src/lang/pretty_print.rs index cc58b79..6aeadd6 100644 --- a/src/lang/pretty_print.rs +++ b/src/lang/pretty_print.rs @@ -2,8 +2,11 @@ use std::fmt::Write; -use crate::middleware::{ - CustomPredicate, CustomPredicateBatch, Predicate, StatementTmpl, StatementTmplArg, Value, +use crate::{ + frontend::PodRequest, + middleware::{ + CustomPredicate, CustomPredicateBatch, Predicate, StatementTmpl, StatementTmplArg, Value, + }, }; /// Trait for converting AST nodes to Podlang source code @@ -122,6 +125,19 @@ impl PrettyPrint for Value { } } +impl PrettyPrint for PodRequest { + fn fmt_podlang_with_indent(&self, w: &mut dyn Write, _indent: usize) -> std::fmt::Result { + write!(w, "REQUEST(")?; + for (i, template) in self.request_templates.iter().enumerate() { + if i > 0 { + write!(w, ", ")?; + } + template.fmt_podlang_with_indent(w, 4)?; + } + write!(w, ")") + } +} + fn fmt_predicate_definition( w: &mut dyn Write, predicate: &CustomPredicate, diff --git a/src/lang/processor.rs b/src/lang/processor.rs index aa64f3e..bf11580 100644 --- a/src/lang/processor.rs +++ b/src/lang/processor.rs @@ -12,8 +12,8 @@ use crate::{ deserialize_bytes, primitives::ec::{curve::Point, schnorr::SecretKey}, }, - frontend::{BuilderArg, CustomPredicateBatchBuilder, StatementTmplBuilder}, - lang::Rule, + frontend::{BuilderArg, CustomPredicateBatchBuilder, PodRequest, StatementTmplBuilder}, + lang::parser::Rule, middleware::{ self, CustomPredicateBatch, CustomPredicateRef, Key, NativePredicate, Params, Predicate, StatementTmpl, StatementTmplArg, Value, Wildcard, F, VALUE_SIZE, @@ -57,7 +57,7 @@ pub fn native_predicate_from_string(s: &str) -> Option { #[derive(Debug, Clone, PartialEq)] pub struct PodlangOutput { pub custom_batch: Arc, - pub request_templates: Vec, + pub request: PodRequest, } struct ProcessingContext<'a> { @@ -295,7 +295,7 @@ fn second_pass( Ok(PodlangOutput { custom_batch, - request_templates, + request: PodRequest::new(request_templates), }) }