Skip to content

Commit 538353a

Browse files
authored
Frontend: simplify custom predicates interfaces (#83)
* add comments detailing logic, migrate middleware::custom::tests to frontend::custom * simplify custom predicate's frontend interfaces, making it less verbose to define Statement Template arguments The main idea is that when defining the arguments at a statement template, it can be done from 3 different inputs: i. `(&str, literal)`: this is to set a POD and a field, ie. `(POD, literal("field")`) ii. `(&str, &str)`: this is to define a origin-key wildcard pair, ie. `(src_origin, src_dest)` iii. `Value`: this is to define a literal value, ie. `0`
1 parent d3bc892 commit 538353a

File tree

5 files changed

+351
-273
lines changed

5 files changed

+351
-273
lines changed

src/backends/mock_main/mod.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,11 @@ fn fill_pad<T: Clone>(v: &mut Vec<T>, pad_value: T, len: usize) {
115115
}
116116
}
117117

118+
/// Inputs are sorted as:
119+
/// - SignedPods
120+
/// - MainPods
121+
/// - private Statements
122+
/// - public Statements
118123
impl MockMainPod {
119124
fn offset_input_signed_pods(&self) -> usize {
120125
0
@@ -136,6 +141,8 @@ impl MockMainPod {
136141
fill_pad(&mut op.1, OperationArg::None, params.max_operation_args)
137142
}
138143

144+
/// Returns the statements from the given MainPodInputs, padding to the
145+
/// respective max lengths defined at the given Params.
139146
fn layout_statements(params: &Params, inputs: &MainPodInputs) -> Vec<Statement> {
140147
let mut statements = Vec::new();
141148

@@ -259,8 +266,10 @@ impl MockMainPod {
259266
Ok(operations)
260267
}
261268

262-
// NOTE: In this implementation public statements are always copies from previous statements,
263-
// so we fill in the operations accordingly.
269+
// NOTE: In this implementation public statements are always copies from
270+
// previous statements, so we fill in the operations accordingly.
271+
/// This method assumes that the given `statements` array has been padded to
272+
/// `params.max_statements`.
264273
fn process_public_statements_operations(
265274
params: &Params,
266275
statements: &[Statement],

src/frontend/custom.rs

Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
#![allow(unused)]
2+
use std::sync::Arc;
3+
4+
use crate::middleware::{
5+
hash_str, CustomPredicate, CustomPredicateBatch, Hash, HashOrWildcard, NativePredicate,
6+
Predicate, StatementTmpl, StatementTmplArg, ToFields, Value, F,
7+
};
8+
9+
/// Argument to an statement template
10+
pub enum HashOrWildcardStr {
11+
Hash(Hash), // represents a literal key
12+
Wildcard(String),
13+
}
14+
15+
/// helper to build a literal HashOrWildcardStr::Hash from the given str
16+
pub fn literal(s: &str) -> HashOrWildcardStr {
17+
HashOrWildcardStr::Hash(hash_str(s))
18+
}
19+
20+
/// helper to build a HashOrWildcardStr::Wildcard from the given str. For the
21+
/// moment this method does not need to be public.
22+
fn wildcard(s: &str) -> HashOrWildcardStr {
23+
HashOrWildcardStr::Wildcard(s.to_string())
24+
}
25+
26+
/// Builder Argument for the StatementTmplBuilder
27+
pub enum BuilderArg {
28+
Literal(Value),
29+
/// Key: (origin, key), where origin & key can be both Hash or Wildcard
30+
Key(HashOrWildcardStr, HashOrWildcardStr),
31+
}
32+
33+
/// When defining a `BuilderArg`, it can be done from 3 different inputs:
34+
/// i. (&str, literal): this is to set a POD and a field, ie. (POD, literal("field"))
35+
/// ii. (&str, &str): this is to define a origin-key wildcard pair, ie. (src_origin, src_dest)
36+
/// iii. Value: this is to define a literal value, ie. 0
37+
///
38+
/// case i.
39+
impl From<(&str, HashOrWildcardStr)> for BuilderArg {
40+
fn from((origin, lit): (&str, HashOrWildcardStr)) -> Self {
41+
// ensure that `lit` is of HashOrWildcardStr::Hash type
42+
match lit {
43+
HashOrWildcardStr::Hash(_) => (),
44+
_ => panic!("not supported"),
45+
};
46+
Self::Key(wildcard(&origin), lit)
47+
}
48+
}
49+
/// case ii.
50+
impl From<(&str, &str)> for BuilderArg {
51+
fn from((origin, field): (&str, &str)) -> Self {
52+
Self::Key(wildcard(&origin), wildcard(&field))
53+
}
54+
}
55+
/// case iii.
56+
impl<V> From<V> for BuilderArg
57+
where
58+
V: Into<Value>,
59+
{
60+
fn from(v: V) -> Self {
61+
Self::Literal(v.into())
62+
}
63+
}
64+
65+
struct StatementTmplBuilder {
66+
predicate: Predicate,
67+
args: Vec<BuilderArg>,
68+
}
69+
70+
impl StatementTmplBuilder {
71+
fn new(p: impl Into<Predicate>) -> StatementTmplBuilder {
72+
StatementTmplBuilder {
73+
predicate: p.into(),
74+
args: Vec::new(),
75+
}
76+
}
77+
78+
fn arg(mut self, a: impl Into<BuilderArg>) -> Self {
79+
self.args.push(a.into());
80+
self
81+
}
82+
}
83+
84+
struct CustomPredicateBatchBuilder {
85+
name: String,
86+
predicates: Vec<CustomPredicate>,
87+
}
88+
89+
impl CustomPredicateBatchBuilder {
90+
fn new(name: String) -> Self {
91+
Self {
92+
name,
93+
predicates: Vec::new(),
94+
}
95+
}
96+
97+
fn predicate_and(
98+
&mut self,
99+
args: &[&str],
100+
priv_args: &[&str],
101+
sts: &[StatementTmplBuilder],
102+
) -> Predicate {
103+
self.predicate(true, args, priv_args, sts)
104+
}
105+
106+
fn predicate_or(
107+
&mut self,
108+
args: &[&str],
109+
priv_args: &[&str],
110+
sts: &[StatementTmplBuilder],
111+
) -> Predicate {
112+
self.predicate(false, args, priv_args, sts)
113+
}
114+
115+
/// creates the custom predicate from the given input, adds it to the
116+
/// self.predicates, and returns the index of the created predicate
117+
fn predicate(
118+
&mut self,
119+
conjunction: bool,
120+
args: &[&str],
121+
priv_args: &[&str],
122+
sts: &[StatementTmplBuilder],
123+
) -> Predicate {
124+
let statements = sts
125+
.iter()
126+
.map(|sb| {
127+
let args = sb
128+
.args
129+
.iter()
130+
.map(|a| match a {
131+
BuilderArg::Literal(v) => StatementTmplArg::Literal(*v),
132+
BuilderArg::Key(pod_id, key) => StatementTmplArg::Key(
133+
resolve_wildcard(args, priv_args, pod_id),
134+
resolve_wildcard(args, priv_args, key),
135+
),
136+
})
137+
.collect();
138+
StatementTmpl(sb.predicate.clone(), args)
139+
})
140+
.collect();
141+
let custom_predicate = CustomPredicate {
142+
conjunction,
143+
statements,
144+
args_len: args.len(),
145+
};
146+
self.predicates.push(custom_predicate);
147+
Predicate::BatchSelf(self.predicates.len() - 1)
148+
}
149+
150+
fn finish(self) -> Arc<CustomPredicateBatch> {
151+
Arc::new(CustomPredicateBatch {
152+
name: self.name,
153+
predicates: self.predicates,
154+
})
155+
}
156+
}
157+
158+
fn resolve_wildcard(args: &[&str], priv_args: &[&str], v: &HashOrWildcardStr) -> HashOrWildcard {
159+
match v {
160+
HashOrWildcardStr::Hash(h) => HashOrWildcard::Hash(*h),
161+
HashOrWildcardStr::Wildcard(s) => HashOrWildcard::Wildcard(
162+
args.iter()
163+
.chain(priv_args.iter())
164+
.enumerate()
165+
.find_map(|(i, name)| (&s == name).then_some(i))
166+
.unwrap(),
167+
),
168+
}
169+
}
170+
171+
#[cfg(test)]
172+
mod tests {
173+
use super::*;
174+
use crate::middleware::PodType;
175+
176+
#[test]
177+
fn test_custom_pred() {
178+
use NativePredicate as NP;
179+
use StatementTmplBuilder as STB;
180+
181+
let mut builder = CustomPredicateBatchBuilder::new("eth_friend".into());
182+
let _eth_friend = builder.predicate_and(
183+
// arguments:
184+
&["src_ori", "src_key", "dst_ori", "dst_key"],
185+
// private arguments:
186+
&["attestation_pod"],
187+
// statement templates:
188+
&[
189+
// there is an attestation pod that's a SignedPod
190+
STB::new(NP::ValueOf)
191+
.arg(("attestation_pod", literal("type")))
192+
.arg(PodType::Signed),
193+
// the attestation pod is signed by (src_or, src_key)
194+
STB::new(NP::Equal)
195+
.arg(("attestation_pod", literal("signer")))
196+
.arg(("src_ori", "src_key")),
197+
// that same attestation pod has an "attestation"
198+
STB::new(NP::Equal)
199+
.arg(("attestation_pod", literal("attestation")))
200+
.arg(("dst_ori", "dst_key")),
201+
],
202+
);
203+
204+
println!("a.0. eth_friend = {}", builder.predicates.last().unwrap());
205+
let eth_friend = builder.finish();
206+
// This batch only has 1 predicate, so we pick it already for convenience
207+
let eth_friend = Predicate::Custom(eth_friend, 0);
208+
209+
// next chunk builds:
210+
// eth_dos_distance_base(src_or, src_key, dst_or, dst_key, distance_or, distance_key) = and<
211+
// eq(src_or, src_key, dst_or, dst_key),
212+
// ValueOf(distance_or, distance_key, 0)
213+
// >
214+
let mut builder = CustomPredicateBatchBuilder::new("eth_dos_distance_base".into());
215+
let eth_dos_distance_base = builder.predicate_and(
216+
&[
217+
// arguments:
218+
"src_ori",
219+
"src_key",
220+
"dst_ori",
221+
"dst_key",
222+
"distance_ori",
223+
"distance_key",
224+
],
225+
&[ // private arguments:
226+
],
227+
&[
228+
// statement templates:
229+
STB::new(NP::Equal)
230+
.arg(("src_ori", "src_key"))
231+
.arg(("dst_ori", "dst_key")),
232+
STB::new(NP::ValueOf)
233+
.arg(("distance_ori", "distance_key"))
234+
.arg(0),
235+
],
236+
);
237+
println!(
238+
"b.0. eth_dos_distance_base = {}",
239+
builder.predicates.last().unwrap()
240+
);
241+
242+
let eth_dos_distance = Predicate::BatchSelf(3);
243+
244+
// next chunk builds:
245+
let eth_dos_distance_ind = builder.predicate_and(
246+
&[
247+
// arguments:
248+
"src_ori",
249+
"src_key",
250+
"dst_ori",
251+
"dst_key",
252+
"distance_ori",
253+
"distance_key",
254+
],
255+
&[
256+
// private arguments:
257+
"one_ori",
258+
"one_key",
259+
"shorter_distance_ori",
260+
"shorter_distance_key",
261+
"intermed_ori",
262+
"intermed_key",
263+
],
264+
&[
265+
// statement templates:
266+
STB::new(eth_dos_distance)
267+
.arg(("src_ori", "src_key"))
268+
.arg(("intermed_ori", "intermed_key"))
269+
.arg(("shorter_distance_ori", "shorter_distance_key")),
270+
// distance == shorter_distance + 1
271+
STB::new(NP::ValueOf).arg(("one_ori", "one_key")).arg(1),
272+
STB::new(NP::SumOf)
273+
.arg(("distance_ori", "distance_key"))
274+
.arg(("shorter_distance_ori", "shorter_distance_key"))
275+
.arg(("one_ori", "one_key")),
276+
// intermed is a friend of dst
277+
STB::new(eth_friend)
278+
.arg(("intermed_ori", "intermed_key"))
279+
.arg(("dst_ori", "dst_key")),
280+
],
281+
);
282+
283+
println!(
284+
"b.1. eth_dos_distance_ind = {}",
285+
builder.predicates.last().unwrap()
286+
);
287+
288+
let _eth_dos_distance = builder.predicate_or(
289+
&[
290+
"src_ori",
291+
"src_key",
292+
"dst_ori",
293+
"dst_key",
294+
"distance_ori",
295+
"distance_key",
296+
],
297+
&[],
298+
&[
299+
STB::new(eth_dos_distance_base)
300+
.arg(("src_ori", "src_key"))
301+
.arg(("dst_ori", "dst_key"))
302+
.arg(("distance_ori", "distance_key")),
303+
STB::new(eth_dos_distance_ind)
304+
.arg(("src_ori", "src_key"))
305+
.arg(("dst_ori", "dst_key"))
306+
.arg(("distance_ori", "distance_key")),
307+
],
308+
);
309+
310+
println!(
311+
"b.2. eth_dos_distance = {}",
312+
builder.predicates.last().unwrap()
313+
);
314+
}
315+
}

src/frontend/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ use crate::middleware::{
1414
PodSigner, SELF,
1515
};
1616

17+
mod custom;
1718
mod operation;
1819
mod statement;
20+
pub use custom::*;
1921
pub use operation::*;
2022
pub use statement::*;
2123

0 commit comments

Comments
 (0)