Wildcards without the ? prefix (#422)
This commit is contained in:
parent
7e04eb51ff
commit
5de08da32c
15 changed files with 287 additions and 262 deletions
|
|
@ -3,24 +3,24 @@
|
|||
|
||||
```
|
||||
eth_dos_distance(src_or, src_key, dst_or, dst_key, distance_or, distance_key) = OR(
|
||||
eth_dos_distance_ind_0(?src_or, ?src_key, ?dst_or, ?dst_key, ?distance_or, ?distance_key),
|
||||
eth_dos_distance_base(?src_or, ?src_key, ?dst_or, ?dst_key, ?distance_or, ?distance_key)
|
||||
eth_dos_distance_ind_0(src_or, src_key, dst_or, dst_key, distance_or, distance_key),
|
||||
eth_dos_distance_base(src_or, src_key, dst_or, dst_key, distance_or, distance_key)
|
||||
)
|
||||
|
||||
eth_dos_distance_base(src_or, src_key, dst_or, dst_key, distance_or, distance_key) = AND(
|
||||
Equal(?src_or[?src_key], ?dst_or[?dst_key]),
|
||||
ValueOf(?distance_or[?distance_key], 0)
|
||||
Equal(src_or[src_key], dst_or[dst_key]),
|
||||
ValueOf(distance_or[distance_key], 0)
|
||||
)
|
||||
|
||||
eth_dos_distance_ind_0(src_or, src_key, dst_or, dst_key, distance_or, distance_key, private: intermed_or, intermed_key, shorter_distance_or, shorter_distance_key, one_or, one_key) = AND(
|
||||
eth_dos_distance(?src_or, ?src_key, ?intermed_or, ?intermed_key, ?shorter_distance_or, ?shorter_distance_key)
|
||||
eth_dos_distance(src_or, src_key, intermed_or, intermed_key, shorter_distance_or, shorter_distance_key)
|
||||
|
||||
// distance == shorter_distance + 1
|
||||
ValueOf(?one_or[?one_key], 1)
|
||||
SumOf(?distance_or[?distance_key], ?shorter_distance_or[?shorter_distance_key], ?one_or[?one_key])
|
||||
ValueOf(one_or[one_key], 1)
|
||||
SumOf(distance_or[distance_key], shorter_distance_or[shorter_distance_key], one_or[one_key])
|
||||
|
||||
// intermed is a friend of dst
|
||||
eth_friend(?intermed_or, ?intermed_key, ?dst_or, ?dst_key)
|
||||
eth_friend(intermed_or, intermed_key, dst_or, dst_key)
|
||||
)
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -11,17 +11,17 @@ The syntax of a custom operation is best explained with an example.
|
|||
Original example with anchored keys, origins, and keys.
|
||||
| Args | Condition | Output |
|
||||
|------------|-----------------------------------------|----|
|
||||
| signed_dict: Dict, <br> signer: PublicKey, <br> good_boy_issuers: AnchoredKey::MerkleRoot, <br> receiver: AnchoredKey | SignedBy(?signed_dict, ?signer), <br> Contains(?good_boy_issuers, ?signer), <br> Equals(?signed_dict["friend"], ?receiver) | GoodBoy(?receiver, ?good_boy_issuers) |
|
||||
| signed_dict: Dict, <br> signer: PublicKey, <br> good_boy_issuers: AnchoredKey::MerkleRoot, <br> receiver: AnchoredKey | SignedBy(signed_dict, signer), <br> Contains(good_boy_issuers, signer), <br> Equals(signed_dict["friend"], receiver) | GoodBoy(receiver, good_boy_issuers) |
|
||||
|
||||
Compiled example with only origins and keys.
|
||||
| Args | Condition | Output |
|
||||
|------------|-----------------------------------------|----|
|
||||
| signed_dict: Dict, <br> signer: PublicKey, <br> good_boy_issuers_origin: Origin, <br> good_boy_issuers_key: Key::MerkleRoot, <br> receiver_origin: Origin, <br> receiver_key: Key | SignedBy(?signed_dict, ?signer), <br> Contains(?good_boy_issuers_origin[?good_boy_issuers_key], ?signer), <br> Equals(?signed_dict["friend"], ?receiver_origin[?receiver_key]) | GoodBoy(?receiver_origin[?receiver_key]), ?good_boy_issuers_origin[?good_boy_issuers_key]) |
|
||||
| signed_dict: Dict, <br> signer: PublicKey, <br> good_boy_issuers_origin: Origin, <br> good_boy_issuers_key: Key::MerkleRoot, <br> receiver_origin: Origin, <br> receiver_key: Key | SignedBy(signed_dict, signer), <br> Contains(good_boy_issuers_origin[good_boy_issuers_key], signer), <br> Equals(signed_dict["friend"], receiver_origin[receiver_key]) | GoodBoy(receiver_origin[receiver_key]), good_boy_issuers_origin[good_boy_issuers_key]) |
|
||||
|
||||
A custom operation accepts as input a number of statements (the `Condition`);
|
||||
each statement has a number of arguments, which may be constants or anchored keys; and an [anchored key](./anchoredkeys.md) in turn can optionally be decomposed as a pair of a Dict and a Key.
|
||||
|
||||
In the "original example" above, the anchored keys `good_boy_issuers` and `receiver` are not broken down, but `?signed_dict["friend"]` is. The purpose of breaking it down, in this case, is to use an entry of a dictionary that has been signed.
|
||||
In the "original example" above, the anchored keys `good_boy_issuers` and `receiver` are not broken down, but `signed_dict["friend"]` is. The purpose of breaking it down, in this case, is to use an entry of a dictionary that has been signed.
|
||||
|
||||
In the "compiled example", all the anchored keys have been broken down into dictionaries and keys.
|
||||
|
||||
|
|
|
|||
|
|
@ -13,21 +13,21 @@ rules, the inductive case and the base case.
|
|||
```
|
||||
// src, dst: PubKey, attetation_pod: Pod
|
||||
eth_dos_friend(src, dst, private: attestation_pod) = AND(
|
||||
ValueOf(?attestation_pod[KEY_TYPE], SIGNATURE)
|
||||
Equal(?attestation_pod[KEY_SIGNER], src)
|
||||
Equal(?attestation_pod["attestation"], dst)
|
||||
ValueOf(attestation_pod[KEY_TYPE], SIGNATURE)
|
||||
Equal(attestation_pod[KEY_SIGNER], src)
|
||||
Equal(attestation_pod["attestation"], dst)
|
||||
)
|
||||
|
||||
// src, intermed, dst: PubKey, distance, shorter_distance: Int
|
||||
eth_dos_distance(src, dst, distance, private: shorter_distance, intermed) = OR(
|
||||
AND(
|
||||
eth_dos_distance(?src, ?intermed, ?shorter)
|
||||
SumOf(?distance, ?shorter_distance, 1)
|
||||
eth_friend(?intermed, ?dst)
|
||||
eth_dos_distance(src, intermed, shorter)
|
||||
SumOf(distance, shorter_distance, 1)
|
||||
eth_friend(intermed, dst)
|
||||
)
|
||||
AND(
|
||||
Equal(?src, ?dst)
|
||||
Equal(?distance, 0)
|
||||
Equal(src, dst)
|
||||
Equal(distance, 0)
|
||||
)
|
||||
)
|
||||
```
|
||||
|
|
@ -52,29 +52,29 @@ A ZuKYC Pod exposes a single custom statement with one custom deduction rule.
|
|||
```
|
||||
// receiver: PubKey, gov_id, paystub, sk_pok: Pod, nullifier, sk: Raw
|
||||
loan_check(receiver, private: gov_id, paystub, nullifier, sk, sk_pok) = AND(
|
||||
Equal(?gov_id["pk"], receiver)
|
||||
Equal(gov_id["pk"], receiver)
|
||||
// Not in the sanction list
|
||||
SetNotContains(SANCTION_LIST, receiver)
|
||||
// Valid government-issued ID
|
||||
Equal(?gov_id[KEY_SIGNER], ZOO_GOV)
|
||||
Equal(?gov_id[KEY_TYPE], SIGNATURE)
|
||||
Equal(gov_id[KEY_SIGNER], ZOO_GOV)
|
||||
Equal(gov_id[KEY_TYPE], SIGNATURE)
|
||||
// At least 18 years old
|
||||
Lt(?gov_id["date_of_birth"], NOW_MINUS_18Y) # date_of_birdth is more than 18y old
|
||||
Equal(?paystub[KEY_SIGNER], ZOO_DEEL)
|
||||
Equal(?paystub[KEY_TYPE], SIGNATURE)
|
||||
Equal(?paystub[ssn], ?gov_id["ssn"])
|
||||
Lt(gov_id["date_of_birth"], NOW_MINUS_18Y) # date_of_birdth is more than 18y old
|
||||
Equal(paystub[KEY_SIGNER], ZOO_DEEL)
|
||||
Equal(paystub[KEY_TYPE], SIGNATURE)
|
||||
Equal(paystub[ssn], gov_id["ssn"])
|
||||
// At least one year of consistent employment with your current employer
|
||||
Lt(?paystub["start_date"], NOW_MINUS_1Y) # start_date is more than 1y old
|
||||
Gt(?paystub["issue_date"], NOW_MINUS_7D) # issue_date is less than 7d old
|
||||
Lt(paystub["start_date"], NOW_MINUS_1Y) # start_date is more than 1y old
|
||||
Gt(paystub["issue_date"], NOW_MINUS_7D) # issue_date is less than 7d old
|
||||
// Annual salary is at least $20,000
|
||||
Gt(?paystub["annual_salary"], 20000)
|
||||
Gt(paystub["annual_salary"], 20000)
|
||||
// Private key knowledge
|
||||
Equal(?sk_pok[KEY_SIGNER], receiver)
|
||||
Equal(?sk_pok[KEY_TYPE], SIGNATURE)
|
||||
Equal(?sk_pok["auth"], "ZUKYC_V1_AUTH")
|
||||
HashOf(, 0, ?sk)
|
||||
Equal(sk_pok[KEY_SIGNER], receiver)
|
||||
Equal(sk_pok[KEY_TYPE], SIGNATURE)
|
||||
Equal(sk_pok["auth"], "ZUKYC_V1_AUTH")
|
||||
HashOf(, 0, sk)
|
||||
// Nullifier
|
||||
HashOf(nullifier, "ZUKYC_V1_NULLIFIER", ?sk)
|
||||
HashOf(nullifier, "ZUKYC_V1_NULLIFIER", sk)
|
||||
)
|
||||
```
|
||||
|
||||
|
|
@ -95,19 +95,19 @@ A ZuKYC Pod exposes a single custom statement with one custom deduction rule.
|
|||
```
|
||||
// receiver: String, gov_pk, paystub_pk: PubKey, gov_id, paystub: Pod
|
||||
loan_check(receiver, gov_pk, paystub_pk, private: gov_id, paystub) = AND(
|
||||
Equal(?gov_id["id_number"], ?receiver)
|
||||
Equal(gov_id["id_number"], receiver)
|
||||
// Not in the sanction list
|
||||
SetNotContains(SANCTION_LIST, ?gov_id["id_number"])
|
||||
SetNotContains(SANCTION_LIST, gov_id["id_number"])
|
||||
// Valid government-issued ID
|
||||
ValueOf(?gov_id[KEY_SIGNER], ?gov_pk)
|
||||
Equal(?gov_id[KEY_TYPE], SIGNATURE)
|
||||
ValueOf(gov_id[KEY_SIGNER], gov_pk)
|
||||
Equal(gov_id[KEY_TYPE], SIGNATURE)
|
||||
// At least 18 years old
|
||||
Lt(?gov_id["date_of_birth"], NOW_MINUS_18Y) # date_of_birdth is more than 18y old
|
||||
ValueOf(?paystub[KEY_SIGNER], ?paystub_pk)
|
||||
Equal(?paystub[KEY_TYPE], SIGNATURE)
|
||||
Equal(?paystub["ssn"], ?gov_id["ssn"])
|
||||
Lt(gov_id["date_of_birth"], NOW_MINUS_18Y) # date_of_birdth is more than 18y old
|
||||
ValueOf(paystub[KEY_SIGNER], paystub_pk)
|
||||
Equal(paystub[KEY_TYPE], SIGNATURE)
|
||||
Equal(paystub["ssn"], gov_id["ssn"])
|
||||
// At least one year of consistent employment with your current employer
|
||||
Lt(?paystub["start_date"], NOW_MINUS_1Y) # start_date is more than 1y old
|
||||
Lt(paystub["start_date"], NOW_MINUS_1Y) # start_date is more than 1y old
|
||||
)
|
||||
```
|
||||
|
||||
|
|
@ -118,11 +118,11 @@ A Good Boy Pod exposes one custom statement with one custom deduction rule.
|
|||
```
|
||||
// user: PubKey, good_boy_issuers: Set, pod: Pod, age: Int
|
||||
is_good_boy(user, good_boy_issuers, private: pod, age) = AND(
|
||||
Equal(?pod[KEY_TYPE], SIGNATURE)
|
||||
SetContains(?good_boy_issuers, ?pod[KEY_SIGNER])
|
||||
Equal(pod[KEY_TYPE], SIGNATURE)
|
||||
SetContains(good_boy_issuers, pod[KEY_SIGNER])
|
||||
// A good boy issuer says this user is a good boy
|
||||
Equal(?pod["user"], ?user)
|
||||
Equal(?pod["age"], ?age)
|
||||
Equal(pod["user"], user)
|
||||
Equal(pod["age"], age)
|
||||
)
|
||||
```
|
||||
|
||||
|
|
@ -131,12 +131,12 @@ A Friend Pod exposes one custom statement with one custom deduction rule.
|
|||
```
|
||||
// good_boy, friend: PubKey, good_boy_issuers: Set, friend_pod: Pod
|
||||
is_friend(good_boy, friend, good_boy_issuers, friend_pod) = AND(
|
||||
Equal(?pod[KEY_TYPE], SIGNATURE)
|
||||
Equal(pod[KEY_TYPE], SIGNATURE)
|
||||
// The issuer is a good boy
|
||||
is_good_boy(?good_boy, ?good_boy_issuers)
|
||||
is_good_boy(good_boy, good_boy_issuers)
|
||||
// A good boy says this is their friend
|
||||
Equal(?pod[KEY_SIGNER], ?good_boy)
|
||||
Equal(?pod["friend"], ?friend)
|
||||
Equal(pod[KEY_SIGNER], good_boy)
|
||||
Equal(pod["friend"], friend)
|
||||
)
|
||||
```
|
||||
|
||||
|
|
@ -147,10 +147,10 @@ with one custom deduction rule.
|
|||
great_boy: PubKey, good_boy_issuers: Set, friend_pod_0, friend_pod_1: Pod
|
||||
is_great_boy(great_boy, good_boy_issuers, private: friend_pod_0, friend_pod_1) = AND
|
||||
// Two good boys consider this user their friend
|
||||
is_friend(?friend_pod_0[KEY_SIGNER], ?great_boy)
|
||||
is_friend(?friend_pod_1[KEY_SIGNER], ?great_boy)
|
||||
is_friend(friend_pod_0[KEY_SIGNER], great_boy)
|
||||
is_friend(friend_pod_1[KEY_SIGNER], great_boy)
|
||||
// good boy 0 != good boy 1
|
||||
NotEqual(?friend_pod_0[KEY_SIGNER], ?friend_pod_1[KEY_SIGNER])
|
||||
NotEqual(friend_pod_0[KEY_SIGNER], friend_pod_1[KEY_SIGNER])
|
||||
```
|
||||
|
||||
## Attested GreatBoy
|
||||
|
|
@ -161,13 +161,13 @@ An Attested Great Boy Pod is like a Great Boy Pod, but the names of the signers
|
|||
// great_boy: PubKey, friend0, friend1: String, good_boy_issuers: Set, friend_pod_0, friend_pod_1: Pod
|
||||
is_great_boy(great_boy, friend0, friend1, good_boy_issuers, private: friend_pod_0, friend_pod_1) = AND
|
||||
// Two good boys consider this user their friend
|
||||
is_friend(?friend_pod_0[KEY_SIGNER], ?great_boy)
|
||||
is_friend(?friend_pod_1[KEY_SIGNER], ?great_boy)
|
||||
is_friend(friend_pod_0[KEY_SIGNER], great_boy)
|
||||
is_friend(friend_pod_1[KEY_SIGNER], great_boy)
|
||||
// good boy 0 != good boy 1
|
||||
NotEqual(?friend_pod_0[KEY_SIGNER], ?friend_pod_1[KEY_SIGNER])
|
||||
NotEqual(friend_pod_0[KEY_SIGNER], friend_pod_1[KEY_SIGNER])
|
||||
// publicize signer names
|
||||
ValueOf(?friend_pod_0["name"], ?friend0)
|
||||
ValueOf(?friend_pod_1["name"], ?friend1)
|
||||
ValueOf(friend_pod_0["name"], friend0)
|
||||
ValueOf(friend_pod_1["name"], friend1)
|
||||
```
|
||||
|
||||
To produce a Great Boy Pod, you need two Friend Pods, `friend_pod0` and `friend_pod1`, each of which reveals its `signer`.
|
||||
|
|
@ -197,9 +197,9 @@ A post is popular if it has at least two comments from different signers.
|
|||
```
|
||||
// post, comment1, comment2: Pod
|
||||
statement is_popular(post, private: comment1, comment2) = AND(
|
||||
IsEqual(?comment1["referenced_post"], ?post)
|
||||
IsEqual(?comment2["referenced_post"], ?post)
|
||||
NotEqual(?comment1[KEY_SIGNER], ?comment2[KEY_SIGNER])
|
||||
IsEqual(comment1["referenced_post"], post)
|
||||
IsEqual(comment2["referenced_post"], post)
|
||||
NotEqual(comment1[KEY_SIGNER], comment2[KEY_SIGNER])
|
||||
)
|
||||
```
|
||||
|
||||
|
|
@ -209,7 +209,7 @@ Suppose I want to prove that two different people are over 18, and a third perso
|
|||
```
|
||||
// age: Int
|
||||
over_18(age) = AND(
|
||||
GtEq(?age, 18)
|
||||
GtEq(age, 18)
|
||||
)
|
||||
```
|
||||
|
||||
|
|
@ -229,10 +229,10 @@ over_18(?1) = AND(
|
|||
|
||||
Maybe I have two input pods `gov_id1` and `gov_id2`, and I want to prove that these pods refer to two different people, both of whom are over 18; and a third pods `gov_id3` refers to someone under 18. So in my public output statements, I want to have:
|
||||
```
|
||||
NotEqual(?gov_id1["name"], ?gov_id2["name"])
|
||||
over_18(?gov_id1["age"])
|
||||
over_18(?gov_id2["age"])
|
||||
under_18(?gov_id3["age"]).
|
||||
NotEqual(gov_id1["name"], gov_id2["name"])
|
||||
over_18(gov_id1["age"])
|
||||
over_18(gov_id2["age"])
|
||||
under_18(gov_id3["age"]).
|
||||
```
|
||||
|
||||
I would prove this with the following sequence of deductions:
|
||||
|
|
|
|||
|
|
@ -118,10 +118,10 @@ The wildcard system handles this very naturally, since the dict of the anchored
|
|||
```
|
||||
eth_friend(src_or, src_key, dst_or, dst_key) = and<
|
||||
// the attestation dict is signed by (src_or, src_key)
|
||||
SignedBy(?attestation_dict, ?src_or[?src_key])
|
||||
SignedBy(attestation_dict, src_or[src_key])
|
||||
|
||||
// that same attestation pod has an "attestation"
|
||||
Equal(?attestation_dict["attestation"], ?dst_or[?dst_key])
|
||||
Equal(attestation_dict["attestation"], dst_or[dst_key])
|
||||
>
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -72,17 +72,17 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
let input = format!(
|
||||
r#"
|
||||
points(player, level, points, private: points_dict) = AND(
|
||||
SignedBy(?points_dict, PublicKey({game_pk}))
|
||||
Contains(?points_dict, "player", ?player)
|
||||
Contains(?points_dict, "level", ?level)
|
||||
Contains(?points_dict, "points", ?points)
|
||||
SignedBy(points_dict, PublicKey({game_pk}))
|
||||
Contains(points_dict, "player", player)
|
||||
Contains(points_dict, "level", level)
|
||||
Contains(points_dict, "points", points)
|
||||
)
|
||||
|
||||
over_9000(player, private: points_lvl_1, points_lvl_2, points_total) = AND(
|
||||
points(?player, 1, ?points_lvl_1)
|
||||
points(?player, 2, ?points_lvl_2)
|
||||
SumOf(?points_total, ?points_lvl_1, ?points_lvl_2)
|
||||
Gt(?points_total, 9000)
|
||||
points(player, 1, points_lvl_1)
|
||||
points(player, 2, points_lvl_2)
|
||||
SumOf(points_total, points_lvl_1, points_lvl_2)
|
||||
Gt(points_total, 9000)
|
||||
)
|
||||
"#,
|
||||
game_pk = game_pk,
|
||||
|
|
|
|||
|
|
@ -12,24 +12,24 @@ use crate::{
|
|||
pub fn eth_dos_batch(params: &Params) -> Result<Arc<CustomPredicateBatch>> {
|
||||
let input = r#"
|
||||
eth_friend(src, dst, private: attestation) = AND(
|
||||
SignedBy(?attestation, ?src)
|
||||
Contains(?attestation, "attestation", ?dst)
|
||||
SignedBy(attestation, src)
|
||||
Contains(attestation, "attestation", dst)
|
||||
)
|
||||
|
||||
eth_dos_base(src, dst, distance) = AND(
|
||||
Equal(?src, ?dst)
|
||||
Equal(?distance, 0)
|
||||
Equal(src, dst)
|
||||
Equal(distance, 0)
|
||||
)
|
||||
|
||||
eth_dos_ind(src, dst, distance, private: shorter_distance, intermed) = AND(
|
||||
eth_dos(?src, ?intermed, ?shorter_distance)
|
||||
SumOf(?distance, ?shorter_distance, 1)
|
||||
eth_friend(?intermed, ?dst)
|
||||
eth_dos(src, intermed, shorter_distance)
|
||||
SumOf(distance, shorter_distance, 1)
|
||||
eth_friend(intermed, dst)
|
||||
)
|
||||
|
||||
eth_dos(src, dst, distance) = OR(
|
||||
eth_dos_base(?src, ?dst, ?distance)
|
||||
eth_dos_ind(?src, ?dst, ?distance)
|
||||
eth_dos_base(src, dst, distance)
|
||||
eth_dos_ind(src, dst, distance)
|
||||
)
|
||||
"#;
|
||||
let batch = parse(input, params, &[]).expect("lang parse").custom_batch;
|
||||
|
|
@ -47,7 +47,7 @@ pub fn eth_dos_request() -> Result<PodRequest> {
|
|||
r#"
|
||||
use _, _, _, eth_dos from 0x{batch_id}
|
||||
REQUEST(
|
||||
eth_dos(?src, ?dst, ?distance)
|
||||
eth_dos(src, dst, distance)
|
||||
)
|
||||
"#,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -82,12 +82,12 @@ pub fn zu_kyc_pod_request(gov_signer: &Value, pay_signer: &Value) -> Result<PodR
|
|||
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)
|
||||
SignedBy(?gov, {gov_signer})
|
||||
SignedBy(?pay, {pay_signer})
|
||||
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)
|
||||
SignedBy(gov, {gov_signer})
|
||||
SignedBy(pay, {pay_signer})
|
||||
// TODO: Ownership check and watermarking
|
||||
// Depends partly on https://github.com/0xPARC/pod2/issues/351
|
||||
)
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ pub enum BuilderArg {
|
|||
}
|
||||
|
||||
/// When defining a `BuilderArg`, it can be done from 3 different inputs:
|
||||
/// i. (&str, &str): this is to define a origin-key pair, ie. ?attestation_pod["attestation"])
|
||||
/// ii. &str: this is to define a Value wildcard, ie. ?distance
|
||||
/// i. (&str, &str): this is to define a origin-key pair, ie. attestation_pod["attestation"])
|
||||
/// ii. &str: this is to define a Value wildcard, ie. distance
|
||||
///
|
||||
/// case i.
|
||||
impl From<(&str, &str)> for BuilderArg {
|
||||
|
|
|
|||
|
|
@ -240,8 +240,8 @@ mod tests {
|
|||
let request = parse(
|
||||
r#"
|
||||
REQUEST(
|
||||
SumOf(?a, ?b, ?c)
|
||||
Equal(?a, 10)
|
||||
SumOf(a, b, c)
|
||||
Equal(a, 10)
|
||||
)
|
||||
"#,
|
||||
¶ms,
|
||||
|
|
|
|||
|
|
@ -11,15 +11,14 @@ WHITESPACE = _{ (" " | "\t" | NEWLINE)+ }
|
|||
// COMMENT matches a line comment (//...\n) or block comment (/*...*/).
|
||||
COMMENT = _{ ("//" ~ (!NEWLINE ~ ANY)* | "/*" ~ (!"*/" ~ ANY)* ~ "*/" ) }
|
||||
|
||||
// Define rules for identifiers (predicate names, variable names without '?')
|
||||
reserved_identifier = { "private" | "true" | "false" }
|
||||
|
||||
// Define rules for identifiers (predicate names, wildcard names)
|
||||
// Must start with alpha or _, followed by alpha, numeric, or _
|
||||
identifier = @{ !("private") ~ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* }
|
||||
identifier = @{ !reserved_identifier ~ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* }
|
||||
|
||||
private_kw = { "private:" }
|
||||
|
||||
// Define wildcard names (start with '?')
|
||||
wildcard = @{ "?" ~ identifier }
|
||||
|
||||
arg_section = {
|
||||
public_arg_list ~ ("," ~ private_kw ~ private_arg_list)?
|
||||
}
|
||||
|
|
@ -49,15 +48,15 @@ custom_predicate_def = {
|
|||
|
||||
statement_list = { statement+ }
|
||||
|
||||
statement_arg = { anchored_key | wildcard | literal_value }
|
||||
statement_arg = { literal_value | anchored_key | identifier }
|
||||
statement_arg_list = { statement_arg ~ ("," ~ statement_arg)* }
|
||||
|
||||
statement = { identifier ~ "(" ~ statement_arg_list? ~ ")" }
|
||||
|
||||
// Anchored Key: ?Var["key_literal"] or ?Var.key_identifier
|
||||
// Anchored Key: Var["key_literal"] or Var.key_identifier
|
||||
anchored_key = {
|
||||
(wildcard ~ "[" ~ literal_string ~ "]")
|
||||
| (wildcard ~ "." ~ identifier)
|
||||
(identifier ~ "[" ~ literal_string ~ "]")
|
||||
| (identifier ~ "." ~ identifier)
|
||||
}
|
||||
|
||||
// Literal Values (ordered to avoid ambiguity, e.g., string before int)
|
||||
|
|
@ -110,10 +109,10 @@ dict_pair = { literal_string ~ ":" ~ literal_value }
|
|||
|
||||
// --- Rules for testing full input matching ---
|
||||
test_identifier = { SOI ~ identifier ~ EOI }
|
||||
test_wildcard = { SOI ~ wildcard ~ EOI }
|
||||
test_literal_int = { SOI ~ literal_int ~ EOI }
|
||||
test_hash_hex = { SOI ~ hash_hex ~ EOI }
|
||||
test_literal_raw = { SOI ~ literal_raw ~ EOI }
|
||||
test_literal_value = { SOI ~ literal_value ~ EOI }
|
||||
test_statement = { SOI ~ statement ~ EOI }
|
||||
test_statement_arg = { SOI ~ statement_arg ~ EOI }
|
||||
test_custom_predicate_def = { SOI ~ custom_predicate_def ~ EOI }
|
||||
|
|
|
|||
144
src/lang/mod.rs
144
src/lang/mod.rs
|
|
@ -62,7 +62,7 @@ mod tests {
|
|||
fn test_e2e_simple_predicate() -> Result<(), LangError> {
|
||||
let input = r#"
|
||||
is_equal(PodA, PodB) = AND(
|
||||
Equal(?PodA["the_key"], ?PodB["the_key"])
|
||||
Equal(PodA["the_key"], PodB["the_key"])
|
||||
)
|
||||
"#;
|
||||
|
||||
|
|
@ -80,8 +80,8 @@ mod tests {
|
|||
let expected_statements = vec![StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Equal),
|
||||
args: vec![
|
||||
sta_ak(("PodA", 0), "the_key"), // ?PodA["the_key"] -> Wildcard(0), Key("the_key")
|
||||
sta_ak(("PodB", 1), "the_key"), // ?PodB["the_key"] -> Wildcard(1), Key("the_key")
|
||||
sta_ak(("PodA", 0), "the_key"), // PodA["the_key"] -> Wildcard(0), Key("the_key")
|
||||
sta_ak(("PodB", 1), "the_key"), // PodB["the_key"] -> Wildcard(1), Key("the_key")
|
||||
],
|
||||
}];
|
||||
let expected_predicate = CustomPredicate::and(
|
||||
|
|
@ -106,8 +106,8 @@ mod tests {
|
|||
fn test_e2e_simple_request() -> Result<(), LangError> {
|
||||
let input = r#"
|
||||
REQUEST(
|
||||
Equal(?ConstPod["my_val"], Raw(0x0000000000000000000000000000000000000000000000000000000000000001))
|
||||
Lt(?GovPod["dob"], ?ConstPod["my_val"])
|
||||
Equal(ConstPod["my_val"], Raw(0x0000000000000000000000000000000000000000000000000000000000000001))
|
||||
Lt(GovPod["dob"], ConstPod["my_val"])
|
||||
)
|
||||
"#;
|
||||
|
||||
|
|
@ -124,15 +124,15 @@ mod tests {
|
|||
StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Equal),
|
||||
args: vec![
|
||||
sta_ak(("ConstPod", 0), "my_val"), // ?ConstPod["my_val"] -> Wildcard(0), Key("my_val")
|
||||
sta_ak(("ConstPod", 0), "my_val"), // ConstPod["my_val"] -> Wildcard(0), Key("my_val")
|
||||
sta_lit(RawValue::from(1)),
|
||||
],
|
||||
},
|
||||
StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Lt),
|
||||
args: vec![
|
||||
sta_ak(("GovPod", 1), "dob"), // ?GovPod["dob"] -> Wildcard(1), Key("dob")
|
||||
sta_ak(("ConstPod", 0), "my_val"), // ?ConstPod["my_val"] -> Wildcard(0), Key("my_val")
|
||||
sta_ak(("GovPod", 1), "dob"), // GovPod["dob"] -> Wildcard(1), Key("dob")
|
||||
sta_ak(("ConstPod", 0), "my_val"), // ConstPod["my_val"] -> Wildcard(0), Key("my_val")
|
||||
],
|
||||
},
|
||||
];
|
||||
|
|
@ -146,8 +146,8 @@ mod tests {
|
|||
fn test_e2e_predicate_with_private_var() -> Result<(), LangError> {
|
||||
let input = r#"
|
||||
uses_private(A, private: Temp) = AND(
|
||||
Equal(?A["input_key"], ?Temp["const_key"])
|
||||
Equal(?Temp["const_key"], "some_value")
|
||||
Equal(A["input_key"], Temp["const_key"])
|
||||
Equal(Temp["const_key"], "some_value")
|
||||
)
|
||||
"#;
|
||||
|
||||
|
|
@ -166,14 +166,14 @@ mod tests {
|
|||
StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Equal),
|
||||
args: vec![
|
||||
sta_ak(("A", 0), "input_key"), // ?A["input_key"] -> Wildcard(0), Key("input_key")
|
||||
sta_ak(("Temp", 1), "const_key"), // ?Temp["const_key"] -> Wildcard(1), Key("const_key")
|
||||
sta_ak(("A", 0), "input_key"), // A["input_key"] -> Wildcard(0), Key("input_key")
|
||||
sta_ak(("Temp", 1), "const_key"), // Temp["const_key"] -> Wildcard(1), Key("const_key")
|
||||
],
|
||||
},
|
||||
StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Equal),
|
||||
args: vec![
|
||||
sta_ak(("Temp", 1), "const_key"), // ?Temp["const_key"] -> Wildcard(1), Key("const_key")
|
||||
sta_ak(("Temp", 1), "const_key"), // Temp["const_key"] -> Wildcard(1), Key("const_key")
|
||||
sta_lit("some_value"), // Literal("some_value")
|
||||
],
|
||||
},
|
||||
|
|
@ -200,11 +200,11 @@ mod tests {
|
|||
fn test_e2e_request_with_custom_call() -> Result<(), LangError> {
|
||||
let input = r#"
|
||||
my_pred(X, Y) = AND(
|
||||
Equal(?X["val"], ?Y["val"])
|
||||
Equal(X["val"], Y["val"])
|
||||
)
|
||||
|
||||
REQUEST(
|
||||
my_pred(?Pod1, ?Pod2)
|
||||
my_pred(Pod1, Pod2)
|
||||
)
|
||||
"#;
|
||||
|
||||
|
|
@ -222,8 +222,8 @@ mod tests {
|
|||
let expected_pred_statements = vec![StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Equal),
|
||||
args: vec![
|
||||
sta_ak(("X", 0), "val"), // ?X["val"] -> Wildcard(0), Key("val")
|
||||
sta_ak(("Y", 1), "val"), // ?Y["val"] -> Wildcard(1), Key("val")
|
||||
sta_ak(("X", 0), "val"), // X["val"] -> Wildcard(0), Key("val")
|
||||
sta_ak(("Y", 1), "val"), // Y["val"] -> Wildcard(1), Key("val")
|
||||
],
|
||||
}];
|
||||
let expected_predicate = CustomPredicate::and(
|
||||
|
|
@ -259,15 +259,15 @@ mod tests {
|
|||
#[test]
|
||||
fn test_e2e_request_with_various_args() -> Result<(), LangError> {
|
||||
let input = r#"
|
||||
some_pred(A, B, C) = AND( Equal(?A["foo"], ?B["bar"]) )
|
||||
some_pred(A, B, C) = AND( Equal(A["foo"], B["bar"]) )
|
||||
|
||||
REQUEST(
|
||||
some_pred(
|
||||
?Var1, // Wildcard
|
||||
Var1, // Wildcard
|
||||
12345, // Int Literal
|
||||
"hello_string" // String Literal (Removed invalid AK args)
|
||||
)
|
||||
Equal(?AnotherPod["another_key"], ?Var1["some_field"])
|
||||
Equal(AnotherPod["another_key"], Var1["some_field"])
|
||||
)
|
||||
"#;
|
||||
|
||||
|
|
@ -280,15 +280,15 @@ mod tests {
|
|||
assert!(!request_templates.is_empty());
|
||||
|
||||
// Expected Wildcard Indices in Request Scope:
|
||||
// ?Var1 -> 0
|
||||
// ?AnotherPod -> 1
|
||||
// Var1 -> 0
|
||||
// AnotherPod -> 1
|
||||
|
||||
// Expected structure
|
||||
let expected_templates = vec![
|
||||
StatementTmpl {
|
||||
pred: Predicate::Custom(CustomPredicateRef::new(batch_result, 0)), // Refers to some_pred
|
||||
args: vec![
|
||||
StatementTmplArg::Wildcard(wc("Var1", 0)), // ?Var1
|
||||
StatementTmplArg::Wildcard(wc("Var1", 0)), // Var1
|
||||
StatementTmplArg::Literal(Value::from(12345i64)), // 12345
|
||||
StatementTmplArg::Literal(Value::from("hello_string")), // "hello_string"
|
||||
],
|
||||
|
|
@ -296,9 +296,9 @@ mod tests {
|
|||
StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Equal),
|
||||
args: vec![
|
||||
// ?AnotherPod["another_key"] -> Wildcard(1), Key("another_key")
|
||||
// AnotherPod["another_key"] -> Wildcard(1), Key("another_key")
|
||||
sta_ak(("AnotherPod", 1), "another_key"),
|
||||
// ?Var1["some_field"] -> Wildcard(0), Key("some_field")
|
||||
// Var1["some_field"] -> Wildcard(0), Key("some_field")
|
||||
sta_ak(("Var1", 0), "some_field"),
|
||||
],
|
||||
},
|
||||
|
|
@ -313,11 +313,11 @@ mod tests {
|
|||
fn test_e2e_syntactic_sugar_predicates() -> Result<(), LangError> {
|
||||
let input = r#"
|
||||
REQUEST(
|
||||
GtEq(?A["foo"], ?B["bar"])
|
||||
Gt(?C["baz"], ?D["qux"])
|
||||
DictContains(?A["foo"], ?B["bar"], ?C["baz"])
|
||||
DictNotContains(?A["foo"], ?B["bar"])
|
||||
ArrayContains(?A["foo"], ?B["bar"], ?C["baz"])
|
||||
GtEq(A["foo"], B["bar"])
|
||||
Gt(C["baz"], D["qux"])
|
||||
DictContains(A["foo"], B["bar"], C["baz"])
|
||||
DictNotContains(A["foo"], B["bar"])
|
||||
ArrayContains(A["foo"], B["bar"], C["baz"])
|
||||
)
|
||||
"#;
|
||||
|
||||
|
|
@ -370,12 +370,12 @@ mod tests {
|
|||
let input = r#"
|
||||
REQUEST(
|
||||
// Order matters for comparison with the hardcoded templates
|
||||
SetNotContains(?sanctions["sanctionList"], ?gov["idNumber"])
|
||||
Lt(?gov["dateOfBirth"], ?SELF_HOLDER_18Y["const_18y"])
|
||||
Equal(?pay["startDate"], ?SELF_HOLDER_1Y["const_1y"])
|
||||
Equal(?gov["socialSecurityNumber"], ?pay["socialSecurityNumber"])
|
||||
Equal(?SELF_HOLDER_18Y["const_18y"], 1169909388)
|
||||
Equal(?SELF_HOLDER_1Y["const_1y"], 1706367566)
|
||||
SetNotContains(sanctions["sanctionList"], gov["idNumber"])
|
||||
Lt(gov["dateOfBirth"], SELF_HOLDER_18Y["const_18y"])
|
||||
Equal(pay["startDate"], SELF_HOLDER_1Y["const_1y"])
|
||||
Equal(gov["socialSecurityNumber"], pay["socialSecurityNumber"])
|
||||
Equal(SELF_HOLDER_18Y["const_18y"], 1169909388)
|
||||
Equal(SELF_HOLDER_1Y["const_1y"], 1706367566)
|
||||
)
|
||||
"#;
|
||||
|
||||
|
|
@ -406,7 +406,7 @@ mod tests {
|
|||
|
||||
// Define the request templates using wildcards for constants
|
||||
let expected_templates = vec![
|
||||
// 1. NotContains(?sanctions["sanctionList"], ?gov["idNumber"])
|
||||
// 1. NotContains(sanctions["sanctionList"], gov["idNumber"])
|
||||
StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::NotContains),
|
||||
args: vec![
|
||||
|
|
@ -417,7 +417,7 @@ mod tests {
|
|||
sta_ak((wc_gov.name.as_str(), wc_gov.index), id_num_key),
|
||||
],
|
||||
},
|
||||
// 2. Lt(?gov["dateOfBirth"], ?SELF_HOLDER_18Y["const_18y"])
|
||||
// 2. Lt(gov["dateOfBirth"], SELF_HOLDER_18Y["const_18y"])
|
||||
StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Lt),
|
||||
args: vec![
|
||||
|
|
@ -428,7 +428,7 @@ mod tests {
|
|||
),
|
||||
],
|
||||
},
|
||||
// 3. Equal(?pay["startDate"], ?SELF_HOLDER_1Y["const_1y"])
|
||||
// 3. Equal(pay["startDate"], SELF_HOLDER_1Y["const_1y"])
|
||||
StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Equal),
|
||||
args: vec![
|
||||
|
|
@ -436,7 +436,7 @@ mod tests {
|
|||
sta_ak((wc_self_1y.name.as_str(), wc_self_1y.index), const_1y_key),
|
||||
],
|
||||
},
|
||||
// 4. Equal(?gov["socialSecurityNumber"], ?pay["socialSecurityNumber"])
|
||||
// 4. Equal(gov["socialSecurityNumber"], pay["socialSecurityNumber"])
|
||||
StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Equal),
|
||||
args: vec![
|
||||
|
|
@ -444,7 +444,7 @@ mod tests {
|
|||
sta_ak((wc_pay.name.as_str(), wc_pay.index), ssn_key),
|
||||
],
|
||||
},
|
||||
// 5. Equal(?SELF_HOLDER_18Y["const_18y"], 1169909388)
|
||||
// 5. Equal(SELF_HOLDER_18Y["const_18y"], 1169909388)
|
||||
StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Equal),
|
||||
args: vec![
|
||||
|
|
@ -455,7 +455,7 @@ mod tests {
|
|||
sta_lit(now_minus_18y_val.clone()),
|
||||
],
|
||||
},
|
||||
// 6. Equal(?SELF_HOLDER_1Y["const_1y"], 1706367566)
|
||||
// 6. Equal(SELF_HOLDER_1Y["const_1y"], 1706367566)
|
||||
StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Equal),
|
||||
args: vec![
|
||||
|
|
@ -494,24 +494,24 @@ mod tests {
|
|||
|
||||
let input = r#"
|
||||
eth_friend(src, dst, private: attestation_dict) = AND(
|
||||
SignedBy(?attestation_dict, ?src)
|
||||
Equal(?attestation_dict["attestation"], ?dst)
|
||||
SignedBy(attestation_dict, src)
|
||||
Equal(attestation_dict["attestation"], dst)
|
||||
)
|
||||
|
||||
eth_dos_distance_base(src, dst, distance) = AND(
|
||||
Equal(?src, ?dst)
|
||||
Equal(?distance, 0)
|
||||
Equal(src, dst)
|
||||
Equal(distance, 0)
|
||||
)
|
||||
|
||||
eth_dos_distance_ind(src, dst, distance, private: shorter_distance, intermed) = AND(
|
||||
eth_dos_distance(?src, ?dst, ?distance)
|
||||
SumOf(?distance, ?shorter_distance, 1)
|
||||
eth_friend(?intermed, ?dst)
|
||||
eth_dos_distance(src, dst, distance)
|
||||
SumOf(distance, shorter_distance, 1)
|
||||
eth_friend(intermed, dst)
|
||||
)
|
||||
|
||||
eth_dos_distance(src, dst, distance) = OR(
|
||||
eth_dos_distance_base(?src, ?dst, ?distance)
|
||||
eth_dos_distance_ind(?src, ?dst, ?distance)
|
||||
eth_dos_distance_base(src, dst, distance)
|
||||
eth_dos_distance_ind(src, dst, distance)
|
||||
)
|
||||
"#;
|
||||
|
||||
|
|
@ -668,8 +668,8 @@ mod tests {
|
|||
let imported_pred_stmts = vec![StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Equal),
|
||||
args: vec![
|
||||
sta_ak(("A", 0), "foo"), // ?A["foo"]
|
||||
sta_ak(("B", 1), "bar"), // ?B["bar"]
|
||||
sta_ak(("A", 0), "foo"), // A["foo"]
|
||||
sta_ak(("B", 1), "bar"), // B["bar"]
|
||||
],
|
||||
}];
|
||||
let imported_predicate = CustomPredicate::and(
|
||||
|
|
@ -690,7 +690,7 @@ mod tests {
|
|||
use imported_pred from 0x{}
|
||||
|
||||
REQUEST(
|
||||
imported_pred(?Pod1, ?Pod2)
|
||||
imported_pred(Pod1, Pod2)
|
||||
)
|
||||
"#,
|
||||
batch_id_str
|
||||
|
|
@ -741,8 +741,8 @@ mod tests {
|
|||
use pred_one, _, pred_three from 0x{}
|
||||
|
||||
REQUEST(
|
||||
pred_one(?Pod1)
|
||||
pred_three(?Pod2)
|
||||
pred_one(Pod1)
|
||||
pred_three(Pod2)
|
||||
)
|
||||
"#,
|
||||
batch_id_str
|
||||
|
|
@ -799,7 +799,7 @@ mod tests {
|
|||
use imported_eq from 0x{}
|
||||
|
||||
wrapper_pred(X, Y) = AND(
|
||||
imported_eq(?X, ?Y)
|
||||
imported_eq(X, Y)
|
||||
)
|
||||
"#,
|
||||
batch_id_str
|
||||
|
|
@ -848,12 +848,12 @@ mod tests {
|
|||
let input = format!(
|
||||
r#"
|
||||
REQUEST(
|
||||
Equal(?A["pk"], {})
|
||||
Equal(?B["raw"], {})
|
||||
Equal(?C["string"], {})
|
||||
Equal(?D["int"], {})
|
||||
Equal(?E["bool"], {})
|
||||
Equal(?F["sk"], {})
|
||||
Equal(A["pk"], {})
|
||||
Equal(B["raw"], {})
|
||||
Equal(C["string"], {})
|
||||
Equal(D["int"], {})
|
||||
Equal(E["bool"], {})
|
||||
Equal(F["sk"], {})
|
||||
)
|
||||
"#,
|
||||
Value::from(pk).to_podlang_string(),
|
||||
|
|
@ -865,13 +865,13 @@ mod tests {
|
|||
);
|
||||
/*
|
||||
REQUEST(
|
||||
Equal(?A["pk"], PublicKey(3t9fNuU194n7mSJPRdeaJRMqw6ZQCUddzvECWNe1k2b1rdBezXpJxF))
|
||||
Equal(?C["raw"], Raw(0x0000000000000000000000000000000000000000000000000000000000000001))
|
||||
Equal(?D["string"], "hello")
|
||||
Equal(?E["int"], 123)
|
||||
Equal(?F["bool"], true)
|
||||
Equal(?G["sk"], SecretKey(random_secret_key_base_64))
|
||||
Equal(?H["self"], SELF)
|
||||
Equal(A["pk"], PublicKey(3t9fNuU194n7mSJPRdeaJRMqw6ZQCUddzvECWNe1k2b1rdBezXpJxF))
|
||||
Equal(C["raw"], Raw(0x0000000000000000000000000000000000000000000000000000000000000001))
|
||||
Equal(D["string"], "hello")
|
||||
Equal(E["int"], 123)
|
||||
Equal(F["bool"], true)
|
||||
Equal(G["sk"], SecretKey(random_secret_key_base_64))
|
||||
Equal(H["self"], SELF)
|
||||
)
|
||||
*/
|
||||
|
||||
|
|
@ -947,8 +947,8 @@ mod tests {
|
|||
|
||||
let input = r#"
|
||||
identity_verified(username, private: identity_dict) = AND(
|
||||
Equal(?identity_dict["username"], ?username)
|
||||
Equal(?identity_dict["user_public_key"], ?user_public_key)
|
||||
Equal(identity_dict["username"], username)
|
||||
Equal(identity_dict["user_public_key"], user_public_key)
|
||||
)
|
||||
"#
|
||||
.to_string();
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@ pub fn parse_podlang(input: &str) -> Result<Pairs<'_, Rule>, ParseError> {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn assert_parses(rule: Rule, input: &str) {
|
||||
fn assert_parses(rule: Rule, input: &str) -> Pairs<'_, Rule> {
|
||||
match PodlangParser::parse(rule, input) {
|
||||
Ok(_) => (), // Successfully parsed
|
||||
Ok(pairs) => pairs, // Successfully parsed
|
||||
Err(e) => panic!("Failed to parse input:\n{}\nError: {}", input, e),
|
||||
}
|
||||
}
|
||||
|
|
@ -79,26 +79,52 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_parse_wildcard() {
|
||||
assert_parses(Rule::wildcard, "?Var");
|
||||
assert_parses(Rule::wildcard, "?_Internal");
|
||||
assert_parses(Rule::wildcard, "?X1");
|
||||
assert_fails(Rule::test_wildcard, "NotAVar"); // Use test rule
|
||||
assert_fails(Rule::test_wildcard, "?"); // Use test rule
|
||||
assert_fails(Rule::test_wildcard, "?invalid-char"); // Use test rule
|
||||
assert_parses(Rule::identifier, "Var");
|
||||
assert_parses(Rule::identifier, "_Internal");
|
||||
assert_parses(Rule::identifier, "X1");
|
||||
assert_fails(Rule::test_identifier, ""); // Use test rule
|
||||
assert_fails(Rule::test_identifier, "invalid-char"); // Use test rule
|
||||
assert_fails(Rule::test_identifier, "?noMoreQuestionMarks"); // Use test rule
|
||||
assert_fails(Rule::test_identifier, "123noStartingDigits"); // Use test rule
|
||||
assert_fails(Rule::test_identifier, "true"); // Use test rule
|
||||
assert_fails(Rule::test_identifier, "false"); // Use test rule
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_anchored_key() {
|
||||
assert_parses(Rule::anchored_key, "?PodVar[\"literal key\"]");
|
||||
assert_parses(Rule::anchored_key, "?PodVar.literal_key");
|
||||
assert_fails(Rule::anchored_key, "PodVar[\"key\"]"); // Needs wildcard for pod
|
||||
assert_fails(Rule::anchored_key, "PodVar.key"); // Needs wildcard for pod
|
||||
assert_fails(Rule::anchored_key, "?PodVar[invalid_key]"); // Key must be literal string
|
||||
assert_fails(Rule::anchored_key, "?PodVar.123"); // Key must be valid identifier
|
||||
assert_fails(Rule::anchored_key, "?PodVar[]"); // Key cannot be empty
|
||||
assert_fails(Rule::anchored_key, "?PodVar."); // Key cannot be empty
|
||||
assert_fails(Rule::anchored_key, "?PodVar[?key]"); // Key cannot be wildcard
|
||||
assert_fails(Rule::anchored_key, "?PodVar.?key"); // Key cannot be wildcard
|
||||
assert_parses(Rule::anchored_key, "PodVar[\"literal_key\"]");
|
||||
assert_parses(Rule::anchored_key, "PodVar.literal_key");
|
||||
assert_fails(Rule::anchored_key, "PodVar[invalid_key]"); // Key must be literal string
|
||||
assert_fails(Rule::anchored_key, "PodVar.123"); // Key must be valid identifier
|
||||
assert_fails(Rule::anchored_key, "PodVar[]"); // Key cannot be empty
|
||||
assert_fails(Rule::anchored_key, "PodVar."); // Key cannot be empty
|
||||
assert_fails(Rule::anchored_key, "?PodVar[\"key\"]"); // No more question marks on wildcards
|
||||
assert_fails(Rule::anchored_key, "?PodVar.key"); // No more question marks on wildcards
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_arg_ambiguity() {
|
||||
fn assert_inner(rule: &Rule, input: &str) {
|
||||
assert_eq!(
|
||||
assert_parses(Rule::test_statement_arg, input)
|
||||
.next()
|
||||
.unwrap()
|
||||
.into_inner()
|
||||
.next()
|
||||
.unwrap()
|
||||
.into_inner()
|
||||
.next()
|
||||
.unwrap()
|
||||
.as_rule(),
|
||||
*rule
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure different types of args parse in the right priority order.
|
||||
assert_inner(&Rule::identifier, "someVar");
|
||||
assert_inner(&Rule::anchored_key, "someVar[\"key\"]");
|
||||
assert_inner(&Rule::literal_value, "true");
|
||||
assert_inner(&Rule::literal_value, "PublicKey(abc)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -179,10 +205,10 @@ mod tests {
|
|||
// Trimmed leading/trailing whitespace
|
||||
r#"REQUEST(
|
||||
// Check equality
|
||||
Equal(?gov["socialSecurityNumber"], ?pay["socialSecurityNumber"])
|
||||
Equal(gov["socialSecurityNumber"], pay["socialSecurityNumber"])
|
||||
// Check age > 18
|
||||
ValueOf(?const_holder["const_18y"], 1169909388)
|
||||
Lt(?gov["dateOfBirth"], ?const_holder["const_18y"])
|
||||
ValueOf(const_holder["const_18y"], 1169909388)
|
||||
Lt(gov["dateOfBirth"], const_holder["const_18y"])
|
||||
)"#,
|
||||
);
|
||||
}
|
||||
|
|
@ -193,14 +219,14 @@ mod tests {
|
|||
Rule::test_custom_predicate_def,
|
||||
// Trimmed leading/trailing whitespace
|
||||
r#"my_pred(A, B) = AND(
|
||||
Equal(?A["foo"], ?B["bar"])
|
||||
Equal(A["foo"], B["bar"])
|
||||
)"#,
|
||||
);
|
||||
assert_parses(
|
||||
Rule::test_custom_predicate_def,
|
||||
// Trimmed leading/trailing whitespace
|
||||
r#"pred_with_private(X, private: TempKey) = OR(
|
||||
Equal(?X["key"], 1234)
|
||||
Equal(X["key"], 1234)
|
||||
)"#,
|
||||
);
|
||||
assert_fails(
|
||||
|
|
@ -216,15 +242,15 @@ mod tests {
|
|||
r#"// File defining one predicate and one request
|
||||
is_valid_user(UserPod, private: ConstVal) = AND(
|
||||
// User age must be > 18 (using a constant value)
|
||||
ValueOf(?ConstVal["min_age"], 18)
|
||||
Gt(?UserPod["age"], ?ConstVal["min_age"])
|
||||
ValueOf(ConstVal["min_age"], 18)
|
||||
Gt(UserPod["age"], ConstVal["min_age"])
|
||||
// User must not be banned
|
||||
NotContains(?_BANNED_USERS.list, ?UserPod.userId)
|
||||
NotContains(_BANNED_USERS.list, UserPod.userId)
|
||||
)
|
||||
|
||||
REQUEST(
|
||||
is_valid_user(?SomeUser)
|
||||
Equal(?SomeUser["country"], ?Other["country"])
|
||||
is_valid_user(SomeUser)
|
||||
Equal(SomeUser["country"], Other["country"])
|
||||
)"#,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ mod tests {
|
|||
fn test_simple_predicate_pretty_print() {
|
||||
let params = Params::default();
|
||||
|
||||
// Create a simple predicate: is_equal(PodA, PodB) = AND(Equal(?PodA["key"], ?PodB["key"]))
|
||||
// Create a simple predicate: is_equal(PodA, PodB) = AND(Equal(PodA["key"], PodB["key"]))
|
||||
let statements = vec![StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Equal),
|
||||
args: vec![
|
||||
|
|
@ -254,7 +254,7 @@ mod tests {
|
|||
let pretty_printed = predicate.to_podlang_string();
|
||||
|
||||
let expected = r#"is_equal(PodA, PodB) = AND(
|
||||
Equal(?PodA["key"], ?PodB["key"])
|
||||
Equal(PodA["key"], PodB["key"])
|
||||
)"#;
|
||||
assert_eq!(pretty_printed, expected);
|
||||
}
|
||||
|
|
@ -263,7 +263,7 @@ mod tests {
|
|||
fn test_predicate_with_private_args() {
|
||||
let params = Params::default();
|
||||
|
||||
// Create: uses_private(A, private: Temp) = AND(Equal(?A["input"], ?Temp["const"]))
|
||||
// Create: uses_private(A, private: Temp) = AND(Equal(A["input"], Temp["const"]))
|
||||
let statements = vec![StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Equal),
|
||||
args: vec![
|
||||
|
|
@ -290,7 +290,7 @@ mod tests {
|
|||
let pretty_printed = predicate.to_podlang_string();
|
||||
|
||||
let expected = r#"uses_private(A, private: Temp) = AND(
|
||||
Equal(?A["input"], ?Temp["const"])
|
||||
Equal(A["input"], Temp["const"])
|
||||
)"#;
|
||||
assert_eq!(pretty_printed, expected);
|
||||
}
|
||||
|
|
@ -299,7 +299,7 @@ mod tests {
|
|||
fn test_statement_with_literal_args() {
|
||||
let params = Params::default();
|
||||
|
||||
// Create: check_value(Pod) = AND(Equal(?Pod["field"], 42))
|
||||
// Create: check_value(Pod) = AND(Equal(Pod["field"], 42))
|
||||
let statements = vec![StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Equal),
|
||||
args: vec![
|
||||
|
|
@ -323,7 +323,7 @@ mod tests {
|
|||
let pretty_printed = predicate.to_podlang_string();
|
||||
|
||||
let expected = r#"check_value(Pod) = AND(
|
||||
Equal(?Pod["field"], 42)
|
||||
Equal(Pod["field"], 42)
|
||||
)"#;
|
||||
assert_eq!(pretty_printed, expected);
|
||||
}
|
||||
|
|
@ -332,7 +332,7 @@ mod tests {
|
|||
fn test_or_predicate() {
|
||||
let params = Params::default();
|
||||
|
||||
// Create: either_or(A, B) = OR(Equal(?A["x"], 1), Equal(?B["y"], 2))
|
||||
// Create: either_or(A, B) = OR(Equal(A["x"], 1), Equal(B["y"], 2))
|
||||
let statements = vec![
|
||||
StatementTmpl {
|
||||
pred: Predicate::Native(NativePredicate::Equal),
|
||||
|
|
@ -368,8 +368,8 @@ mod tests {
|
|||
let pretty_printed = predicate.to_podlang_string();
|
||||
|
||||
let expected = r#"either_or(A, B) = OR(
|
||||
Equal(?A["x"], 1)
|
||||
Equal(?B["y"], 2)
|
||||
Equal(A["x"], 1)
|
||||
Equal(B["y"], 2)
|
||||
)"#;
|
||||
assert_eq!(pretty_printed, expected);
|
||||
}
|
||||
|
|
@ -402,7 +402,7 @@ mod tests {
|
|||
fn test_round_trip_simple_predicate() {
|
||||
let input = r#"
|
||||
simple_equal(PodA, PodB) = AND(
|
||||
Equal(?PodA["key"], ?PodB["key"])
|
||||
Equal(PodA["key"], PodB["key"])
|
||||
)
|
||||
"#;
|
||||
assert_round_trip(input);
|
||||
|
|
@ -412,8 +412,8 @@ mod tests {
|
|||
fn test_round_trip_predicate_with_private_args() {
|
||||
let input = r#"
|
||||
uses_private(A, private: Temp) = AND(
|
||||
Equal(?A["input_key"], ?Temp["const_key"])
|
||||
Equal(?Temp["const_key"], "some_value")
|
||||
Equal(A["input_key"], Temp["const_key"])
|
||||
Equal(Temp["const_key"], "some_value")
|
||||
)
|
||||
"#;
|
||||
assert_round_trip(input);
|
||||
|
|
@ -423,8 +423,8 @@ mod tests {
|
|||
fn test_round_trip_or_predicate() {
|
||||
let input = r#"
|
||||
either_condition(X, Y) = OR(
|
||||
Equal(?X["status"], "active")
|
||||
Equal(?Y["type"], 1)
|
||||
Equal(X["status"], "active")
|
||||
Equal(Y["type"], 1)
|
||||
)
|
||||
"#;
|
||||
assert_round_trip(input);
|
||||
|
|
@ -434,12 +434,12 @@ mod tests {
|
|||
fn test_round_trip_multiple_predicates() {
|
||||
let input = r#"
|
||||
pred_one(A) = AND(
|
||||
Equal(?A["field"], 42)
|
||||
Equal(A["field"], 42)
|
||||
)
|
||||
|
||||
pred_two(B, C) = AND(
|
||||
Equal(?B["value"], ?C["value"])
|
||||
NotEqual(?B["id"], ?C["id"])
|
||||
Equal(B["value"], C["value"])
|
||||
NotEqual(B["id"], C["id"])
|
||||
)
|
||||
"#;
|
||||
assert_round_trip(input);
|
||||
|
|
@ -449,10 +449,10 @@ mod tests {
|
|||
fn test_round_trip_various_literals() {
|
||||
let input = r#"
|
||||
literal_test(Pod) = AND(
|
||||
Equal(?Pod["int_field"], 123)
|
||||
Equal(?Pod["string_field"], "hello world")
|
||||
Equal(?Pod["bool_field"], true)
|
||||
NotEqual(?Pod["other_bool"], false)
|
||||
Equal(Pod["int_field"], 123)
|
||||
Equal(Pod["string_field"], "hello world")
|
||||
Equal(Pod["bool_field"], true)
|
||||
NotEqual(Pod["other_bool"], false)
|
||||
)
|
||||
"#;
|
||||
assert_round_trip(input);
|
||||
|
|
@ -462,11 +462,11 @@ mod tests {
|
|||
fn test_round_trip_complex_predicate() {
|
||||
let input = r#"
|
||||
complex_predicate(User, Document, private: Verifier, Timestamp) = AND(
|
||||
Equal(?User["active"], true)
|
||||
Equal(?Document["owner"], ?User["id"])
|
||||
Equal(?Verifier["type"], 1)
|
||||
Lt(?Timestamp["created"], ?Timestamp["expires"])
|
||||
NotContains(?Document["blocked_users"], ?User["id"])
|
||||
Equal(User["active"], true)
|
||||
Equal(Document["owner"], User["id"])
|
||||
Equal(Verifier["type"], 1)
|
||||
Lt(Timestamp["created"], Timestamp["expires"])
|
||||
NotContains(Document["blocked_users"], User["id"])
|
||||
)
|
||||
"#;
|
||||
assert_round_trip(input);
|
||||
|
|
@ -476,10 +476,10 @@ mod tests {
|
|||
fn test_round_trip_with_sum_and_hash_operations() {
|
||||
let input = r#"
|
||||
math_operations(A, B, C) = AND(
|
||||
SumOf(?A["value"], ?B["value"], ?C["total"])
|
||||
ProductOf(?A["factor"], ?B["factor"], ?C["product"])
|
||||
MaxOf(?A["score"], ?B["score"], ?C["max_score"])
|
||||
HashOf(?A["data"], ?B["salt"], ?C["hash"])
|
||||
SumOf(A["value"], B["value"], C["total"])
|
||||
ProductOf(A["factor"], B["factor"], C["product"])
|
||||
MaxOf(A["score"], B["score"], C["max_score"])
|
||||
HashOf(A["data"], B["salt"], C["hash"])
|
||||
)
|
||||
"#;
|
||||
assert_round_trip(input);
|
||||
|
|
@ -489,13 +489,13 @@ mod tests {
|
|||
fn test_round_trip_nested_custom_calls() {
|
||||
let input = r#"
|
||||
base_check(Pod) = AND(
|
||||
Equal(?Pod["status"], "valid")
|
||||
Equal(Pod["status"], "valid")
|
||||
)
|
||||
|
||||
derived_check(PodA, PodB) = AND(
|
||||
base_check(?PodA)
|
||||
base_check(?PodB)
|
||||
NotEqual(?PodA["id"], ?PodB["id"])
|
||||
base_check(PodA)
|
||||
base_check(PodB)
|
||||
NotEqual(PodA["id"], PodB["id"])
|
||||
)
|
||||
"#;
|
||||
assert_round_trip(input);
|
||||
|
|
@ -505,8 +505,8 @@ mod tests {
|
|||
fn test_round_trip_container_operations() {
|
||||
let input = r#"
|
||||
container_checks(List, Item, Dict, Key, Value) = AND(
|
||||
Contains(?List, ?Item, ?Value)
|
||||
NotContains(?Dict, ?Key)
|
||||
Contains(List, Item, Value)
|
||||
NotContains(Dict, Key)
|
||||
)
|
||||
"#;
|
||||
assert_round_trip(input);
|
||||
|
|
@ -518,7 +518,7 @@ mod tests {
|
|||
let input = format!(
|
||||
r#"
|
||||
secret_key_test(Pod) = AND(
|
||||
Equal(?Pod["sk"], {})
|
||||
Equal(Pod["sk"], {})
|
||||
)
|
||||
"#,
|
||||
Value::from(sk.clone()).to_podlang_string()
|
||||
|
|
@ -530,13 +530,13 @@ mod tests {
|
|||
fn test_pretty_print_demonstration() {
|
||||
let input = r#"
|
||||
base_check(Pod) = AND(
|
||||
Equal(?Pod["status"], "valid")
|
||||
Equal(Pod["status"], "valid")
|
||||
)
|
||||
|
||||
derived_check(PodA, PodB) = AND(
|
||||
base_check(?PodA)
|
||||
base_check(?PodB)
|
||||
NotEqual(?PodA["id"], ?PodB["id"])
|
||||
base_check(PodA)
|
||||
base_check(PodB)
|
||||
NotEqual(PodA["id"], PodB["id"])
|
||||
)
|
||||
"#;
|
||||
|
||||
|
|
@ -603,7 +603,7 @@ mod tests {
|
|||
let input = format!(
|
||||
r#"
|
||||
test_pred(Pod) = AND(
|
||||
Equal(?Pod["field"], "{}")
|
||||
Equal(Pod["field"], "{}")
|
||||
)
|
||||
"#,
|
||||
// Manually escape for the input - this simulates what would be in actual Podlang source
|
||||
|
|
|
|||
|
|
@ -319,8 +319,8 @@ fn pest_pair_to_builder_arg(
|
|||
let value = process_literal_value(params, arg_content_pair)?;
|
||||
Ok(BuilderArg::Literal(value))
|
||||
}
|
||||
Rule::wildcard => {
|
||||
let wc_str = arg_content_pair.as_str().strip_prefix("?").unwrap();
|
||||
Rule::identifier => {
|
||||
let wc_str = arg_content_pair.as_str();
|
||||
if let StatementContext::CustomPredicate {
|
||||
argument_names,
|
||||
pred_name,
|
||||
|
|
@ -339,7 +339,7 @@ fn pest_pair_to_builder_arg(
|
|||
Rule::anchored_key => {
|
||||
let mut inner_ak_pairs = arg_content_pair.clone().into_inner();
|
||||
let root_pair = inner_ak_pairs.next().unwrap();
|
||||
let root_wc_str = root_pair.as_str().strip_prefix("?").unwrap();
|
||||
let root_wc_str = root_pair.as_str();
|
||||
|
||||
if let StatementContext::CustomPredicate {
|
||||
argument_names,
|
||||
|
|
@ -1072,7 +1072,7 @@ mod processor_tests {
|
|||
|
||||
#[test]
|
||||
fn test_fp_only_request() -> Result<(), ProcessorError> {
|
||||
let input = "REQUEST( Equal(?A[\"k\"],?B.k) )"; // Escaped quotes
|
||||
let input = "REQUEST( Equal(A[\"k\"],B.k) )"; // Escaped quotes
|
||||
let pairs = get_document_content_pairs(input)?;
|
||||
let params = Params::default();
|
||||
let mut ctx = ProcessingContext::new(¶ms);
|
||||
|
|
@ -1089,7 +1089,7 @@ mod processor_tests {
|
|||
|
||||
#[test]
|
||||
fn test_fp_simple_predicate() -> Result<(), ProcessorError> {
|
||||
let input = "my_pred(A, B) = AND( Equal(?A[\"k\"],?B.k) )"; // Escaped quotes
|
||||
let input = "my_pred(A, B) = AND( Equal(A[\"k\"],B.k) )"; // Escaped quotes
|
||||
let pairs = get_document_content_pairs(input)?;
|
||||
let params = Params::default();
|
||||
let mut ctx = ProcessingContext::new(¶ms);
|
||||
|
|
@ -1111,8 +1111,8 @@ mod processor_tests {
|
|||
#[test]
|
||||
fn test_fp_multiple_predicates() -> Result<(), ProcessorError> {
|
||||
let input = r#"
|
||||
pred1(X) = AND( Equal(?X["k"],?X.k) )
|
||||
pred2(Y, Z) = OR( Equal(?Y["v"], 123) )
|
||||
pred1(X) = AND( Equal(X["k"],X.k) )
|
||||
pred2(Y, Z) = OR( Equal(Y["v"], 123) )
|
||||
"#;
|
||||
let pairs = get_document_content_pairs(input)?;
|
||||
let params = Params::default();
|
||||
|
|
@ -1203,7 +1203,7 @@ mod processor_tests {
|
|||
fn test_fp_mixed_content() -> Result<(), ProcessorError> {
|
||||
let input = r#"
|
||||
pred_one(X) = AND(None())
|
||||
REQUEST( pred_one(?A) )
|
||||
REQUEST( pred_one(A) )
|
||||
pred_two(Y, Z) = OR(None())
|
||||
"#;
|
||||
let pairs = get_document_content_pairs(input)?;
|
||||
|
|
@ -1231,7 +1231,7 @@ mod processor_tests {
|
|||
.as_ref()
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.contains("pred_one(?A)"));
|
||||
.contains("pred_one(A)"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1241,7 +1241,7 @@ mod processor_tests {
|
|||
// Undefined predicates will be flagged as an error on the second pass
|
||||
let input = r#"
|
||||
REQUEST(
|
||||
pred_one(?A)
|
||||
pred_one(A)
|
||||
)
|
||||
"#;
|
||||
let pairs = get_document_content_pairs(input)?;
|
||||
|
|
@ -1260,7 +1260,7 @@ mod processor_tests {
|
|||
// Native predicate names are case-sensitive
|
||||
let input = r#"
|
||||
REQUEST(
|
||||
EQUAL(?A["b"], ?C.d)
|
||||
EQUAL(A["b"], C.d)
|
||||
)
|
||||
"#;
|
||||
let pairs = get_document_content_pairs(input)?;
|
||||
|
|
|
|||
|
|
@ -24,9 +24,9 @@ impl Wildcard {
|
|||
impl fmt::Display for Wildcard {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if f.alternate() {
|
||||
write!(f, "?{}:{}", self.index, self.name)
|
||||
write!(f, "{}:{}", self.index, self.name)
|
||||
} else {
|
||||
write!(f, "?{}", self.name)
|
||||
write!(f, "{}", self.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue