Refactor frontend/middleware types (#194)

* unify fe/be NativeOp and NativePred

* remove Origin in favour of PodId

* Combine string and hash in Key

* use middleware::AnchoredKey in frontend

* merge frontend/middleware types

* refactor custom predicates

* clean up a bit

* fix middleware custom tests

* clean up

* clean up 2

* add acronyms in typos list
This commit is contained in:
Eduard S. 2025-04-16 11:59:30 +02:00 committed by GitHub
parent 9e860ef262
commit c232c8dae5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 1985 additions and 2800 deletions

View file

@ -1,112 +0,0 @@
use std::collections::HashMap;
use anyhow::Result;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::{
frontend::{serialization::ordered_map, Value},
middleware::{
containers::{
Array as MiddlewareArray, Dictionary as MiddlewareDictionary, Set as MiddlewareSet,
},
hash_str, Value as MiddlewareValue,
},
};
#[derive(Clone, Debug, PartialEq, Eq, Serialize, JsonSchema)]
#[serde(transparent)]
pub struct Set(Vec<Value>, #[serde(skip)] MiddlewareSet);
impl Set {
pub fn new(values: Vec<Value>) -> Result<Self> {
let set =
MiddlewareSet::new(&values.iter().map(MiddlewareValue::from).collect::<Vec<_>>())?;
Ok(Self(values, set))
}
pub fn middleware_set(&self) -> &MiddlewareSet {
&self.1
}
pub fn values(&self) -> &Vec<Value> {
&self.0
}
}
impl<'de> Deserialize<'de> for Set {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let values: Vec<Value> = Vec::deserialize(deserializer)?;
Set::new(values).map_err(serde::de::Error::custom)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, JsonSchema)]
#[serde(transparent)]
pub struct Dictionary(
#[serde(serialize_with = "ordered_map")] HashMap<String, Value>,
#[serde(skip)] MiddlewareDictionary,
);
impl Dictionary {
pub fn new(values: HashMap<String, Value>) -> Result<Self> {
let dict = MiddlewareDictionary::new(
&values
.iter()
.map(|(k, v)| (hash_str(k), MiddlewareValue::from(v)))
.collect::<HashMap<_, _>>(),
)?;
Ok(Self(values, dict))
}
pub fn middleware_dict(&self) -> &MiddlewareDictionary {
&self.1
}
pub fn values(&self) -> &HashMap<String, Value> {
&self.0
}
}
impl<'de> Deserialize<'de> for Dictionary {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let values: HashMap<String, Value> = HashMap::deserialize(deserializer)?;
Dictionary::new(values).map_err(serde::de::Error::custom)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, JsonSchema)]
#[serde(transparent)]
pub struct Array(Vec<Value>, #[serde(skip)] MiddlewareArray);
impl Array {
pub fn new(values: Vec<Value>) -> Result<Self> {
let array =
MiddlewareArray::new(&values.iter().map(MiddlewareValue::from).collect::<Vec<_>>())?;
Ok(Self(values, array))
}
pub fn middleware_array(&self) -> &MiddlewareArray {
&self.1
}
pub fn values(&self) -> &Vec<Value> {
&self.0
}
}
impl<'de> Deserialize<'de> for Array {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let values: Vec<Value> = Vec::deserialize(deserializer)?;
Array::new(values).map_err(serde::de::Error::custom)
}
}

View file

@ -3,97 +3,40 @@ use std::{collections::HashMap, fmt, hash as h, iter, iter::zip, sync::Arc};
use anyhow::{anyhow, Result};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
// use serde::{Deserialize, Serialize};
use crate::{
frontend::{AnchoredKey, NativePredicate, Origin, Statement, StatementArg, Value},
middleware::{self, hash_str, HashOrWildcard, Params, PodId, ToFields},
util::hashmap_insert_no_dupe,
frontend::{AnchoredKey, Statement, StatementArg},
middleware::{
self, hash_str, CustomPredicate, CustomPredicateBatch, Key, KeyOrWildcard, NativePredicate,
Params, PodId, Predicate, StatementTmpl, StatementTmplArg, ToFields, Value, Wildcard,
},
};
#[derive(Clone, Debug, PartialEq, Eq, h::Hash, Serialize, Deserialize, JsonSchema)]
#[derive(Clone, Debug, PartialEq, Eq)]
/// Argument to a statement template
pub enum KeyOrWildcardStr {
Key(String), // represents a literal key
Wildcard(String),
}
#[derive(Clone, Debug, PartialEq, Eq, h::Hash, Serialize, Deserialize, JsonSchema)]
pub struct IndexedWildcard {
wildcard: String,
index: usize,
}
impl IndexedWildcard {
pub fn new(wildcard: String, index: usize) -> Self {
Self { wildcard, index }
}
}
#[derive(Clone, Debug, PartialEq, Eq, h::Hash, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "type", content = "value")]
/// Represents a key or resolved wildcard
pub enum KeyOrWildcard {
Key(String),
Wildcard(IndexedWildcard),
}
impl KeyOrWildcard {
/// Matches a key or wildcard against a value, returning a pair
/// representing a wildcard binding (if any) or an error if no
/// match is possible.
pub fn match_against(&self, v: &Value) -> Result<Option<(usize, Value)>> {
match self {
KeyOrWildcard::Key(k) if Value::from(k.as_str()) == *v => Ok(None),
KeyOrWildcard::Wildcard(i) => Ok(Some((i.index, v.clone()))),
_ => Err(anyhow!(
"Failed to match key or wildcard {} against value {}.",
self,
v
)),
}
}
}
impl From<KeyOrWildcard> for middleware::HashOrWildcard {
fn from(v: KeyOrWildcard) -> Self {
match v {
KeyOrWildcard::Key(k) => middleware::HashOrWildcard::Hash(hash_str(&k)),
KeyOrWildcard::Wildcard(n) => middleware::HashOrWildcard::Wildcard(n.index),
}
}
}
impl fmt::Display for KeyOrWildcard {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Key(k) => write!(f, "{}", k),
Self::Wildcard(n) => write!(f, "*{}", n.wildcard),
}
}
}
/// helper to build a literal KeyOrWildcardStr::Key from the given str
pub fn literal(s: &str) -> KeyOrWildcardStr {
pub fn key(s: &str) -> KeyOrWildcardStr {
KeyOrWildcardStr::Key(s.to_string())
}
/// helper to build a KeyOrWildcardStr::Wildcard from the given str. For the
/// moment this method does not need to be public.
fn wildcard(s: &str) -> KeyOrWildcardStr {
KeyOrWildcardStr::Wildcard(s.to_string())
}
/// Builder Argument for the StatementTmplBuilder
pub enum BuilderArg {
Literal(Value),
/// Key: (origin, key), where origin & key can be both Hash or Wildcard
Key(KeyOrWildcardStr, KeyOrWildcardStr),
/// Key: (origin, key), where origin is a Wildcard and key can be both Key or Wildcard
Key(String, KeyOrWildcardStr),
WildcardLiteral(String),
}
/// When defining a `BuilderArg`, 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
/// iii. &str: this is to define a WildcardValue wildcard, ie. "src_or"
///
/// case i.
impl From<(&str, KeyOrWildcardStr)> for BuilderArg {
@ -103,267 +46,24 @@ impl From<(&str, KeyOrWildcardStr)> for BuilderArg {
KeyOrWildcardStr::Key(_) => (),
_ => panic!("not supported"),
};
Self::Key(wildcard(origin), lit)
Self::Key(origin.into(), lit)
}
}
/// case ii.
impl From<(&str, &str)> for BuilderArg {
fn from((origin, field): (&str, &str)) -> Self {
Self::Key(wildcard(origin), wildcard(field))
Self::Key(origin.into(), KeyOrWildcardStr::Wildcard(field.to_string()))
}
}
/// case iii.
impl<V> From<V> for BuilderArg
where
V: Into<Value>,
{
fn from(v: V) -> Self {
Self::Literal(v.into())
impl From<&str> for BuilderArg {
fn from(wc: &str) -> Self {
Self::WildcardLiteral(wc.to_string())
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "type", content = "value")]
pub enum Predicate {
Native(NativePredicate),
BatchSelf(usize),
Custom(CustomPredicateRef),
}
impl From<NativePredicate> for Predicate {
fn from(v: NativePredicate) -> Self {
Self::Native(v)
}
}
impl From<Predicate> for middleware::Predicate {
fn from(v: Predicate) -> Self {
match v {
Predicate::Native(p) => middleware::Predicate::Native(p.into()),
Predicate::BatchSelf(i) => middleware::Predicate::BatchSelf(i),
Predicate::Custom(CustomPredicateRef {
batch: pb,
index: i,
}) => {
let cpb: middleware::CustomPredicateBatch = Arc::unwrap_or_clone(pb).into();
middleware::Predicate::Custom(middleware::CustomPredicateRef(Arc::new(cpb), i))
}
}
}
}
impl fmt::Display for Predicate {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Native(p) => write!(f, "{:?}", p),
Self::BatchSelf(i) => write!(f, "self.{}", i),
Self::Custom(CustomPredicateRef { batch, index }) => {
write!(f, "{}.{}", batch.name, index)
}
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct CustomPredicateRef {
pub batch: Arc<CustomPredicateBatch>,
pub index: usize,
}
impl From<CustomPredicateRef> for middleware::CustomPredicateRef {
fn from(v: CustomPredicateRef) -> Self {
let cpb: middleware::CustomPredicateBatch = Arc::unwrap_or_clone(v.batch).into();
middleware::CustomPredicateRef(Arc::new(cpb), v.index)
}
}
impl CustomPredicateRef {
pub fn new(batch: Arc<CustomPredicateBatch>, index: usize) -> Self {
Self { batch, index }
}
pub fn arg_len(&self) -> usize {
self.batch.predicates[self.index].args_len
}
pub fn match_against(&self, statements: &[Statement]) -> Result<HashMap<usize, Value>> {
let mut bindings = HashMap::new();
// Single out custom predicate, replacing batch-self
// references with custom predicate references.
let custom_predicate = {
let cp = &Arc::unwrap_or_clone(self.batch.clone()).predicates[self.index];
CustomPredicate {
conjunction: cp.conjunction,
statements: cp
.statements
.iter()
.map(|StatementTmpl { pred: p, args }| StatementTmpl {
pred: match p {
Predicate::BatchSelf(i) => {
Predicate::Custom(CustomPredicateRef::new(self.batch.clone(), *i))
}
_ => p.clone(),
},
args: args.to_vec(),
})
.collect(),
args_len: cp.args_len,
name: cp.name.to_string(),
}
};
match custom_predicate.conjunction {
true if custom_predicate.statements.len() == statements.len() => {
// Match op args against statement templates
let match_bindings = iter::zip(custom_predicate.statements, statements).map(
|(s_tmpl, s)| s_tmpl.match_against(s)
).collect::<Result<Vec<_>>>()
.map(|v| v.concat())?;
// Add bindings to binding table, throwing if there is an inconsistency.
match_bindings.into_iter().try_for_each(|kv| hashmap_insert_no_dupe(&mut bindings, kv))?;
Ok(bindings)
},
false if statements.len() == 1 => {
// Match op arg against each statement template
custom_predicate.statements.iter().map(
|s_tmpl| {
let mut bindings = bindings.clone();
s_tmpl.match_against(&statements[0])?.into_iter().try_for_each(|kv| hashmap_insert_no_dupe(&mut bindings, kv))?;
Ok::<_, anyhow::Error>(bindings)
}
).find(|m| m.is_ok()).unwrap_or(Err(anyhow!("Statement {} does not match disjunctive custom predicate {}.", &statements[0], custom_predicate)))
},
_ => Err(anyhow!("Custom predicate statement template list {:?} does not match op argument list {:?}.", custom_predicate.statements, statements))
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct CustomPredicateBatch {
pub name: String,
pub predicates: Vec<CustomPredicate>,
}
impl From<CustomPredicateBatch> for middleware::CustomPredicateBatch {
fn from(v: CustomPredicateBatch) -> Self {
middleware::CustomPredicateBatch {
name: v.name,
predicates: v.predicates.into_iter().map(|p| p.into()).collect(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
/// NOTE: fields are not public (outside of crate) to enforce the struct instantiation through
/// the `::and/or` methods, which performs checks on the values.
pub struct CustomPredicate {
/// true for "and", false for "or"
pub(crate) conjunction: bool,
pub(crate) statements: Vec<StatementTmpl>,
pub(crate) args_len: usize,
// TODO: Add private args length?
// TODO: Add args type information?
pub(crate) name: String,
}
impl CustomPredicate {
pub fn and(
params: &Params,
statements: Vec<StatementTmpl>,
args_len: usize,
name: &str,
) -> Result<Self> {
Self::new(params, true, statements, args_len, name)
}
pub fn or(
params: &Params,
statements: Vec<StatementTmpl>,
args_len: usize,
name: &str,
) -> Result<Self> {
Self::new(params, false, statements, args_len, name)
}
pub fn new(
params: &Params,
conjunction: bool,
statements: Vec<StatementTmpl>,
args_len: usize,
name: &str,
) -> Result<Self> {
if statements.len() > params.max_custom_predicate_arity {
return Err(anyhow!("Custom predicate depends on too many statements"));
}
Ok(Self {
conjunction,
statements,
args_len,
name: name.to_string(),
})
}
}
impl From<CustomPredicate> for middleware::CustomPredicate {
fn from(v: CustomPredicate) -> Self {
middleware::CustomPredicate {
conjunction: v.conjunction,
statements: v.statements.into_iter().map(|s| s.into()).collect(),
args_len: v.args_len,
}
}
}
impl fmt::Display for CustomPredicate {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "{}<", if self.conjunction { "and" } else { "or" })?;
for st in &self.statements {
write!(f, " {}", st.pred)?;
for (i, arg) in st.args.iter().enumerate() {
if i != 0 {
write!(f, ", ")?;
}
write!(f, "{}", arg)?;
}
writeln!(f, "),")?;
}
write!(f, ">(")?;
for i in 0..self.args_len {
if i != 0 {
write!(f, ", ")?;
}
write!(f, "*{}", i)?;
}
writeln!(f, ")")?;
Ok(())
}
}
#[derive(Clone, Debug, PartialEq, Eq, h::Hash, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "type", content = "value")]
pub enum StatementTmplArg {
None,
Literal(Value),
// #[schemars(with = "Vec<KeyOrWildcard>")]
Key(KeyOrWildcard, KeyOrWildcard),
}
impl From<StatementTmplArg> for middleware::StatementTmplArg {
fn from(v: StatementTmplArg) -> Self {
match v {
StatementTmplArg::None => middleware::StatementTmplArg::None,
StatementTmplArg::Literal(v) => middleware::StatementTmplArg::Literal((&v).into()),
StatementTmplArg::Key(pod_id, key) => {
middleware::StatementTmplArg::Key(pod_id.into(), key.into())
}
}
}
}
impl fmt::Display for StatementTmplArg {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::None => write!(f, "none"),
Self::Literal(v) => write!(f, "{}", v),
Self::Key(pod_id, key) => write!(f, "({}, {})", pod_id, key),
}
}
pub fn literal(v: impl Into<Value>) -> BuilderArg {
BuilderArg::Literal(v.into())
}
pub struct StatementTmplBuilder {
@ -385,83 +85,6 @@ impl StatementTmplBuilder {
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct StatementTmpl {
pub pred: Predicate,
pub args: Vec<StatementTmplArg>,
}
impl StatementTmpl {
pub fn pred(&self) -> &Predicate {
&self.pred
}
pub fn args(&self) -> &[StatementTmplArg] {
&self.args
}
/// Matches a statement template against a statement, returning
/// the variable bindings as an association list. Returns an error
/// if there is type or argument mismatch.
pub fn match_against(&self, s: &Statement) -> Result<Vec<(usize, Value)>> {
type P = Predicate;
if matches!(
self,
Self {
pred: P::BatchSelf(_),
args: _
}
) {
Err(anyhow!(
"Cannot check self-referencing statement templates."
))
} else if self.pred() != &s.predicate {
Err(anyhow!("Type mismatch between {:?} and {}.", self, s))
} else {
zip(self.args(), s.args.clone())
.map(|(t_arg, s_arg)| t_arg.match_against(&s_arg))
.collect::<Result<Vec<_>>>()
.map(|v| v.concat())
}
}
}
impl From<StatementTmpl> for middleware::StatementTmpl {
fn from(v: StatementTmpl) -> Self {
middleware::StatementTmpl(
v.pred.into(),
v.args.into_iter().map(|a| a.into()).collect(),
)
}
}
impl StatementTmplArg {
/// Matches a statement template argument against a statement
/// argument, returning a wildcard correspondence in the case of
/// one or more wildcard matches, nothing in the case of a
/// literal/hash match, and an error otherwise.
pub fn match_against(&self, s_arg: &StatementArg) -> Result<Vec<(usize, Value)>> {
match (self, s_arg) {
// (Self::None, StatementArg::None) => Ok(vec![]),
(Self::Literal(v), StatementArg::Literal(w)) if v == w => Ok(vec![]),
(
Self::Key(tmpl_o, tmpl_k),
StatementArg::Key(AnchoredKey {
origin: Origin { pod_id: PodId(o) },
key: k,
}),
) => {
let o_corr = tmpl_o.match_against(&(middleware::Value::from(*o)).into())?;
let k_corr = tmpl_k.match_against(&(*k.as_str()).into())?;
Ok([o_corr, k_corr].into_iter().flatten().collect())
}
_ => Err(anyhow!(
"Failed to match statement template argument {:?} against statement argument {:?}.",
self,
s_arg
)),
}
}
}
pub struct CustomPredicateBatchBuilder {
pub name: String,
pub predicates: Vec<CustomPredicate>,
@ -477,37 +100,52 @@ impl CustomPredicateBatchBuilder {
pub fn predicate_and(
&mut self,
name: &str,
params: &Params,
args: &[&str],
priv_args: &[&str],
sts: &[StatementTmplBuilder],
name: &str,
) -> Result<Predicate> {
self.predicate(params, true, args, priv_args, sts, name)
self.predicate(name, params, true, args, priv_args, sts)
}
pub fn predicate_or(
&mut self,
name: &str,
params: &Params,
args: &[&str],
priv_args: &[&str],
sts: &[StatementTmplBuilder],
name: &str,
) -> Result<Predicate> {
self.predicate(params, false, args, priv_args, sts, name)
self.predicate(name, params, false, args, priv_args, sts)
}
/// creates the custom predicate from the given input, adds it to the
/// self.predicates, and returns the index of the created predicate
fn predicate(
&mut self,
name: &str,
params: &Params,
conjunction: bool,
args: &[&str],
priv_args: &[&str],
sts: &[StatementTmplBuilder],
name: &str,
) -> Result<Predicate> {
if args.len() > params.max_statement_args {
return Err(anyhow!(
"args.len {} is over the limit {}",
args.len(),
params.max_statement_args
));
}
if (args.len() + priv_args.len()) > params.max_custom_predicate_wildcards {
return Err(anyhow!(
"wildcards.len {} is over the limit {}",
args.len() + priv_args.len(),
params.max_custom_predicate_wildcards
));
}
let statements = sts
.iter()
.map(|sb| {
@ -518,8 +156,11 @@ impl CustomPredicateBatchBuilder {
BuilderArg::Literal(v) => StatementTmplArg::Literal(v.clone()),
BuilderArg::Key(pod_id, key) => StatementTmplArg::Key(
resolve_wildcard(args, priv_args, pod_id),
resolve_wildcard(args, priv_args, key),
resolve_key_or_wildcard(args, priv_args, key),
),
BuilderArg::WildcardLiteral(v) => {
StatementTmplArg::WildcardLiteral(resolve_wildcard(args, priv_args, v))
}
})
.collect();
StatementTmpl {
@ -529,7 +170,7 @@ impl CustomPredicateBatchBuilder {
})
.collect();
let custom_predicate =
CustomPredicate::new(params, conjunction, statements, args.len(), name)?;
CustomPredicate::new(name.into(), params, conjunction, statements, args.len())?;
self.predicates.push(custom_predicate);
Ok(Predicate::BatchSelf(self.predicates.len() - 1))
}
@ -542,26 +183,34 @@ impl CustomPredicateBatchBuilder {
}
}
fn resolve_wildcard(args: &[&str], priv_args: &[&str], v: &KeyOrWildcardStr) -> KeyOrWildcard {
fn resolve_key_or_wildcard(
args: &[&str],
priv_args: &[&str],
v: &KeyOrWildcardStr,
) -> KeyOrWildcard {
match v {
KeyOrWildcardStr::Key(k) => KeyOrWildcard::Key(k.clone()),
KeyOrWildcardStr::Wildcard(s) => KeyOrWildcard::Wildcard(
args.iter()
.chain(priv_args.iter())
.enumerate()
.find_map(|(i, name)| (s == name).then_some(IndexedWildcard::new(s.clone(), i)))
.unwrap(),
),
KeyOrWildcardStr::Key(k) => KeyOrWildcard::Key(Key::from(k)),
KeyOrWildcardStr::Wildcard(s) => {
KeyOrWildcard::Wildcard(resolve_wildcard(args, priv_args, s))
}
}
}
fn resolve_wildcard(args: &[&str], priv_args: &[&str], s: &str) -> Wildcard {
args.iter()
.chain(priv_args.iter())
.enumerate()
.find_map(|(i, name)| (s == *name).then_some(Wildcard::new(s.to_string(), i)))
.unwrap()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
examples::custom::{eth_dos_batch, eth_friend_batch},
middleware,
// middleware::{CustomPredicateRef, Params, PodType},
middleware::{CustomPredicateRef, Params, PodType},
};
#[test]
@ -569,7 +218,11 @@ mod tests {
use NativePredicate as NP;
use StatementTmplBuilder as STB;
let params = Params::default();
let params = Params {
max_statement_args: 6,
max_custom_predicate_wildcards: 12,
..Default::default()
};
params.print_serialized_sizes();
// ETH friend custom predicate batch

File diff suppressed because it is too large Load diff

View file

@ -1,13 +1,12 @@
use std::fmt;
use serde::{Deserialize, Serialize};
// use serde::{Deserialize, Serialize};
use crate::{
frontend::{CustomPredicateRef, NativePredicate, Predicate, SignedPod, Statement, Value},
middleware::{self, OperationAux},
frontend::SignedPod,
middleware::{AnchoredKey, OperationAux, OperationType, Statement, Value},
};
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq)]
pub enum OperationArg {
Statement(Statement),
Literal(Value),
@ -56,7 +55,16 @@ impl From<bool> for OperationArg {
impl From<(&SignedPod, &str)> for OperationArg {
fn from((pod, key): (&SignedPod, &str)) -> Self {
Self::Statement((pod, key).into())
// TODO: TryFrom.
let value = pod
.kvs()
.get(&key.into())
.cloned()
.unwrap_or_else(|| panic!("Key {} is not present in POD: {}", key, pod));
Self::Statement(Statement::ValueOf(
AnchoredKey::from((pod.id(), key)),
value,
))
}
}
@ -72,120 +80,7 @@ impl<V: Into<Value>> From<(&str, V)> for OperationArg {
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum OperationType {
Native(NativeOperation),
Custom(CustomPredicateRef),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum NativeOperation {
None = 0,
NewEntry = 1,
CopyStatement = 2,
EqualFromEntries = 3,
NotEqualFromEntries = 4,
GtFromEntries = 5,
LtFromEntries = 6,
TransitiveEqualFromStatements = 7,
GtToNotEqual = 8,
LtToNotEqual = 9,
SumOf = 13,
ProductOf = 14,
MaxOf = 15,
DictContainsFromEntries = 16,
DictNotContainsFromEntries = 17,
SetContainsFromEntries = 18,
SetNotContainsFromEntries = 19,
ArrayContainsFromEntries = 20,
}
impl TryFrom<OperationType> for middleware::OperationType {
type Error = anyhow::Error;
fn try_from(fe_ot: OperationType) -> Result<Self, Self::Error> {
type FeOT = OperationType;
type FeNO = NativeOperation;
type MwOT = middleware::OperationType;
type MwNO = middleware::NativeOperation;
let mw_ot = match fe_ot {
FeOT::Native(FeNO::None) => MwOT::Native(MwNO::None),
FeOT::Native(FeNO::NewEntry) => MwOT::Native(MwNO::NewEntry),
FeOT::Native(FeNO::CopyStatement) => MwOT::Native(MwNO::CopyStatement),
FeOT::Native(FeNO::EqualFromEntries) => MwOT::Native(MwNO::EqualFromEntries),
FeOT::Native(FeNO::NotEqualFromEntries) => MwOT::Native(MwNO::NotEqualFromEntries),
FeOT::Native(FeNO::GtFromEntries) => MwOT::Native(MwNO::GtFromEntries),
FeOT::Native(FeNO::LtFromEntries) => MwOT::Native(MwNO::LtFromEntries),
FeOT::Native(FeNO::TransitiveEqualFromStatements) => {
MwOT::Native(MwNO::TransitiveEqualFromStatements)
}
FeOT::Native(FeNO::GtToNotEqual) => MwOT::Native(MwNO::GtToNotEqual),
FeOT::Native(FeNO::LtToNotEqual) => MwOT::Native(MwNO::LtToNotEqual),
FeOT::Native(FeNO::SumOf) => MwOT::Native(MwNO::SumOf),
FeOT::Native(FeNO::ProductOf) => MwOT::Native(MwNO::ProductOf),
FeOT::Native(FeNO::MaxOf) => MwOT::Native(MwNO::MaxOf),
FeOT::Native(FeNO::DictContainsFromEntries) => MwOT::Native(MwNO::ContainsFromEntries),
FeOT::Native(FeNO::DictNotContainsFromEntries) => {
MwOT::Native(MwNO::NotContainsFromEntries)
}
FeOT::Native(FeNO::SetContainsFromEntries) => MwOT::Native(MwNO::ContainsFromEntries),
FeOT::Native(FeNO::SetNotContainsFromEntries) => {
MwOT::Native(MwNO::NotContainsFromEntries)
}
FeOT::Native(FeNO::ArrayContainsFromEntries) => MwOT::Native(MwNO::ContainsFromEntries),
FeOT::Custom(mw_cpr) => MwOT::Custom(mw_cpr.into()),
};
Ok(mw_ot)
}
}
impl OperationType {
/// Gives the type of predicate that the operation will output, if known.
/// CopyStatement may output any predicate (it will match the statement copied),
/// so output_predicate returns None on CopyStatement.
pub fn output_predicate(&self) -> Option<Predicate> {
match self {
OperationType::Native(native_op) => match native_op {
NativeOperation::None => Some(Predicate::Native(NativePredicate::None)),
NativeOperation::NewEntry => Some(Predicate::Native(NativePredicate::ValueOf)),
NativeOperation::CopyStatement => None,
NativeOperation::EqualFromEntries => {
Some(Predicate::Native(NativePredicate::Equal))
}
NativeOperation::NotEqualFromEntries => {
Some(Predicate::Native(NativePredicate::NotEqual))
}
NativeOperation::GtFromEntries => Some(Predicate::Native(NativePredicate::Gt)),
NativeOperation::LtFromEntries => Some(Predicate::Native(NativePredicate::Lt)),
NativeOperation::TransitiveEqualFromStatements => {
Some(Predicate::Native(NativePredicate::Equal))
}
NativeOperation::GtToNotEqual => Some(Predicate::Native(NativePredicate::NotEqual)),
NativeOperation::LtToNotEqual => Some(Predicate::Native(NativePredicate::NotEqual)),
NativeOperation::SumOf => Some(Predicate::Native(NativePredicate::SumOf)),
NativeOperation::ProductOf => Some(Predicate::Native(NativePredicate::ProductOf)),
NativeOperation::MaxOf => Some(Predicate::Native(NativePredicate::MaxOf)),
NativeOperation::DictContainsFromEntries => {
Some(Predicate::Native(NativePredicate::DictContains))
}
NativeOperation::DictNotContainsFromEntries => {
Some(Predicate::Native(NativePredicate::DictNotContains))
}
NativeOperation::SetContainsFromEntries => {
Some(Predicate::Native(NativePredicate::SetContains))
}
NativeOperation::SetNotContainsFromEntries => {
Some(Predicate::Native(NativePredicate::SetNotContains))
}
NativeOperation::ArrayContainsFromEntries => {
Some(Predicate::Native(NativePredicate::ArrayContains))
}
},
OperationType::Custom(cpr) => Some(Predicate::Custom(cpr.clone())),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq)]
pub struct Operation(pub OperationType, pub Vec<OperationArg>, pub OperationAux);
impl fmt::Display for Operation {

View file

@ -1,58 +0,0 @@
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::middleware::{self, CustomPredicateRef};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub enum NativePredicate {
None = 0,
ValueOf = 1,
Equal = 2,
NotEqual = 3,
Gt = 4,
Lt = 5,
SumOf = 8,
ProductOf = 9,
MaxOf = 10,
DictContains = 11,
DictNotContains = 12,
SetContains = 13,
SetNotContains = 14,
ArrayContains = 15, // there is no ArrayNotContains
}
impl From<NativePredicate> for middleware::NativePredicate {
fn from(np: NativePredicate) -> Self {
use middleware::NativePredicate as MidNP;
use NativePredicate::*;
match np {
None => MidNP::None,
ValueOf => MidNP::ValueOf,
Equal => MidNP::Equal,
NotEqual => MidNP::NotEqual,
Gt => MidNP::Gt,
Lt => MidNP::Lt,
SumOf => MidNP::SumOf,
ProductOf => MidNP::ProductOf,
MaxOf => MidNP::MaxOf,
DictContains => MidNP::Contains,
DictNotContains => MidNP::NotContains,
SetContains => MidNP::Contains,
SetNotContains => MidNP::NotContains,
ArrayContains => MidNP::Contains,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub enum Predicate {
Native(NativePredicate),
BatchSelf(usize),
Custom(CustomPredicateRef),
}
impl From<NativePredicate> for Predicate {
fn from(v: NativePredicate) -> Self {
Self::Native(v)
}
}

View file

@ -1,3 +1,4 @@
/*
use std::collections::{BTreeMap, HashMap};
use schemars::{JsonSchema, Schema};
@ -5,14 +6,14 @@ use serde::{Deserialize, Serialize, Serializer};
use crate::{
backends::plonky2::mock::{mainpod::MockMainPod, signedpod::MockSignedPod},
frontend::{containers::Dictionary, MainPod, SignedPod, Statement, Value},
frontend::{containers::Dictionary, MainPod, SignedPod, Statement, TypedValue},
middleware::PodId,
};
#[derive(Serialize, Deserialize, JsonSchema)]
#[schemars(title = "SignedPod")]
pub struct SignedPodHelper {
entries: HashMap<String, Value>,
entries: HashMap<String, TypedValue>,
proof: String,
pod_class: String,
pod_type: String,
@ -169,30 +170,34 @@ mod tests {
fn test_value_serialization() {
// Pairs of values and their expected serialized representations
let values = vec![
(Value::String("hello".to_string()), "\"hello\""),
(Value::Int(42), "{\"Int\":\"42\"}"),
(Value::Bool(true), "true"),
(TypedValue::String("hello".to_string()), "\"hello\""),
(TypedValue::Int(42), "{\"Int\":\"42\"}"),
(TypedValue::Bool(true), "true"),
(
Value::Array(
Array::new(vec![Value::String("foo".to_string()), Value::Bool(false)]).unwrap(),
TypedValue::Array(
Array::new(vec![
TypedValue::String("foo".to_string()),
TypedValue::Bool(false),
])
.unwrap(),
),
"[\"foo\",false]",
),
(
Value::Dictionary(
TypedValue::Dictionary(
Dictionary::new(HashMap::from([
("foo".to_string(), Value::Int(123)),
("bar".to_string(), Value::String("baz".to_string())),
("foo".to_string(), TypedValue::Int(123)),
("bar".to_string(), TypedValue::String("baz".to_string())),
]))
.unwrap(),
),
"{\"Dictionary\":{\"bar\":\"baz\",\"foo\":{\"Int\":\"123\"}}}",
),
(
Value::Set(
TypedValue::Set(
Set::new(vec![
Value::String("foo".to_string()),
Value::String("bar".to_string()),
TypedValue::String("foo".to_string()),
TypedValue::String("bar".to_string()),
])
.unwrap(),
),
@ -203,9 +208,9 @@ mod tests {
for (value, expected) in values {
let serialized = serde_json::to_string(&value).unwrap();
assert_eq!(serialized, expected);
let deserialized: Value = serde_json::from_str(&serialized).unwrap();
let deserialized: TypedValue = serde_json::from_str(&serialized).unwrap();
assert_eq!(value, deserialized);
let expected_deserialized: Value = serde_json::from_str(&expected).unwrap();
let expected_deserialized: TypedValue = serde_json::from_str(&expected).unwrap();
assert_eq!(value, expected_deserialized);
}
}
@ -219,27 +224,32 @@ mod tests {
builder.insert("very_large_int", 1152921504606846976);
builder.insert(
"a_dict_containing_one_key",
Value::Dictionary(
TypedValue::Dictionary(
Dictionary::new(HashMap::from([
("foo".to_string(), Value::Int(123)),
("foo".to_string(), TypedValue::Int(123)),
(
"an_array_containing_three_ints".to_string(),
Value::Array(
Array::new(vec![Value::Int(1), Value::Int(2), Value::Int(3)]).unwrap(),
TypedValue::Array(
Array::new(vec![
TypedValue::Int(1),
TypedValue::Int(2),
TypedValue::Int(3),
])
.unwrap(),
),
),
(
"a_set_containing_two_strings".to_string(),
Value::Set(
TypedValue::Set(
Set::new(vec![
Value::Array(
TypedValue::Array(
Array::new(vec![
Value::String("foo".to_string()),
Value::String("bar".to_string()),
TypedValue::String("foo".to_string()),
TypedValue::String("bar".to_string()),
])
.unwrap(),
),
Value::String("baz".to_string()),
TypedValue::String("baz".to_string()),
])
.unwrap(),
),
@ -256,7 +266,6 @@ mod tests {
let deserialized: SignedPod = serde_json::from_str(&serialized).unwrap();
assert_eq!(pod.kvs, deserialized.kvs);
assert_eq!(pod.origin(), deserialized.origin());
assert_eq!(pod.verify().is_ok(), deserialized.verify().is_ok());
assert_eq!(pod.id(), deserialized.id())
}
@ -265,7 +274,7 @@ mod tests {
fn test_main_pod_serialization() -> Result<()> {
let params = middleware::Params::default();
let sanctions_values = vec!["A343434340".into()];
let sanction_set = Value::Set(Set::new(sanctions_values)?);
let sanction_set = TypedValue::Set(Set::new(sanctions_values)?);
let (gov_id_builder, pay_stub_builder, sanction_list_builder) =
zu_kyc_sign_pod_builders(&params, &sanction_set);
@ -311,3 +320,4 @@ mod tests {
);
}
}
*/

View file

@ -1,162 +0,0 @@
use std::fmt;
use anyhow::{anyhow, Result};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::{
frontend::{AnchoredKey, NativePredicate, Predicate, SignedPod, Value},
middleware,
};
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub enum StatementArg {
Literal(Value),
Key(AnchoredKey),
}
impl fmt::Display for StatementArg {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Literal(v) => write!(f, "{}", v),
Self::Key(r) => write!(f, "{}.{}", r.origin.pod_id, r.key),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct Statement {
pub predicate: Predicate,
pub args: Vec<StatementArg>,
}
impl Statement {
pub fn new(predicate: Predicate, args: Vec<StatementArg>) -> Self {
Self { predicate, args }
}
}
impl From<(&SignedPod, &str)> for Statement {
fn from((pod, key): (&SignedPod, &str)) -> Self {
// TODO: TryFrom.
let value = pod
.kvs
.get(key)
.cloned()
.unwrap_or_else(|| panic!("Key {} is not present in POD: {}", key, pod));
Statement {
predicate: Predicate::Native(NativePredicate::ValueOf),
args: vec![
StatementArg::Key(AnchoredKey::new(pod.origin(), key.to_string())),
StatementArg::Literal(value),
],
}
}
}
#[derive(Debug)]
pub struct ManualConversionRequired();
impl std::fmt::Display for StatementConversionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Statement conversion error: statement conversion must be implemented manually."
)
}
}
impl std::error::Error for StatementConversionError {}
#[derive(Debug)]
pub enum StatementConversionError {
MCR(ManualConversionRequired),
Error(anyhow::Error),
}
impl From<anyhow::Error> for StatementConversionError {
fn from(value: anyhow::Error) -> Self {
Self::Error(value)
}
}
impl TryFrom<Statement> for middleware::Statement {
type Error = StatementConversionError;
fn try_from(s: Statement) -> Result<Self, StatementConversionError> {
type MS = middleware::Statement;
type NP = NativePredicate;
type SA = StatementArg;
let args = (
s.args.first().cloned(),
s.args.get(1).cloned(),
s.args.get(2).cloned(),
);
Ok(match &s.predicate {
Predicate::Native(np) => match (np, args) {
(NP::None, (None, None, None)) => MS::None,
(NP::ValueOf, (Some(SA::Key(ak)), Some(StatementArg::Literal(v)), None)) => {
MS::ValueOf(ak.into(), (&v).into())
}
(NP::Equal, (Some(SA::Key(ak1)), Some(SA::Key(ak2)), None)) => {
MS::Equal(ak1.into(), ak2.into())
}
(NP::NotEqual, (Some(SA::Key(ak1)), Some(SA::Key(ak2)), None)) => {
MS::NotEqual(ak1.into(), ak2.into())
}
(NP::Gt, (Some(SA::Key(ak1)), Some(SA::Key(ak2)), None)) => {
MS::Gt(ak1.into(), ak2.into())
}
(NP::Lt, (Some(SA::Key(ak1)), Some(SA::Key(ak2)), None)) => {
MS::Lt(ak1.into(), ak2.into())
}
(NP::SumOf, (Some(SA::Key(ak1)), Some(SA::Key(ak2)), Some(SA::Key(ak3)))) => {
MS::SumOf(ak1.into(), ak2.into(), ak3.into())
}
(NP::ProductOf, (Some(SA::Key(ak1)), Some(SA::Key(ak2)), Some(SA::Key(ak3)))) => {
MS::ProductOf(ak1.into(), ak2.into(), ak3.into())
}
(NP::MaxOf, (Some(SA::Key(ak1)), Some(SA::Key(ak2)), Some(SA::Key(ak3)))) => {
MS::MaxOf(ak1.into(), ak2.into(), ak3.into())
}
(
NP::DictContains,
(Some(SA::Key(ak1)), Some(SA::Key(ak2)), Some(SA::Key(ak3))),
) => MS::Contains(ak1.into(), ak2.into(), ak3.into()),
(NP::DictNotContains, (Some(SA::Key(ak1)), Some(SA::Key(ak2)), None)) => {
MS::NotContains(ak1.into(), ak2.into())
}
(NP::SetContains, (Some(SA::Key(_)), Some(SA::Key(_)), None)) => {
return Err(StatementConversionError::MCR(ManualConversionRequired()));
}
(NP::SetNotContains, (Some(SA::Key(ak1)), Some(SA::Key(ak2)), None)) => {
MS::NotContains(ak1.into(), ak2.into())
}
_ => Err(anyhow!("Ill-formed statement: {}", s))?,
},
Predicate::Custom(cpr) => MS::Custom(
cpr.clone().into(),
s.args
.iter()
.map(|arg| match arg {
StatementArg::Key(ak) => Ok(ak.clone().into()),
_ => Err(anyhow!("Invalid statement arg: {}", arg)),
})
.collect::<Result<Vec<_>>>()?,
),
_ => Err(anyhow!("Ill-formed statement: {}", s))?,
})
}
}
impl fmt::Display for Statement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?} ", self.predicate)?;
for (i, arg) in self.args.iter().enumerate() {
if i != 0 {
write!(f, " ")?;
}
write!(f, "{}", arg)?;
}
Ok(())
}
}