parent
21ab3c2d0d
commit
7d0d3ad769
20 changed files with 992 additions and 825 deletions
|
|
@ -8,9 +8,9 @@ use serde::{Deserialize, Serialize};
|
|||
use serialization::{SerializedMainPod, SerializedSignedPod};
|
||||
|
||||
use crate::middleware::{
|
||||
self, check_st_tmpl, hash_str, hash_values, AnchoredKey, Hash, Key, MainPodInputs,
|
||||
NativeOperation, NativePredicate, OperationAux, OperationType, Params, PodId, PodProver,
|
||||
PodSigner, Predicate, Statement, StatementArg, VDSet, Value, WildcardValue, KEY_TYPE, SELF,
|
||||
self, check_st_tmpl, hash_op, hash_str, max_op, prod_op, sum_op, AnchoredKey, Key,
|
||||
MainPodInputs, NativeOperation, OperationAux, OperationType, Params, PodId, PodProver,
|
||||
PodSigner, Statement, StatementArg, VDSet, Value, ValueRef, WildcardValue, KEY_TYPE, SELF,
|
||||
};
|
||||
|
||||
mod custom;
|
||||
|
|
@ -102,12 +102,12 @@ impl SignedPod {
|
|||
pub fn get(&self, key: impl Into<Key>) -> Option<&Value> {
|
||||
self.kvs.get(&key.into())
|
||||
}
|
||||
// Returns the ValueOf statement that defines key if it exists.
|
||||
// Returns the Equal statement that defines key if it exists.
|
||||
pub fn get_statement(&self, key: impl Into<Key>) -> Option<Statement> {
|
||||
let key: Key = key.into();
|
||||
self.kvs()
|
||||
.get(&key)
|
||||
.map(|value| Statement::ValueOf(AnchoredKey::from((self.id(), key)), value.clone()))
|
||||
.map(|value| Statement::equal(AnchoredKey::from((self.id(), key)), value.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -125,7 +125,7 @@ pub struct MainPodBuilder {
|
|||
// Internal state
|
||||
/// Counter for constants created from literals
|
||||
const_cnt: usize,
|
||||
/// Map from (public, Value) to Key of already created literals via ValueOf statements.
|
||||
/// Map from (public, Value) to Key of already created literals via Equal statements.
|
||||
literals: HashMap<(bool, Value), Key>,
|
||||
}
|
||||
|
||||
|
|
@ -186,38 +186,6 @@ impl MainPodBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
/// Convert [OperationArg]s to [StatementArg]s for the operations that work with entries
|
||||
fn op_args_entries(
|
||||
&mut self,
|
||||
public: bool,
|
||||
args: &mut [OperationArg],
|
||||
) -> Result<Vec<StatementArg>> {
|
||||
let mut st_args = Vec::new();
|
||||
// TODO: Rewrite without calling args() and instead using matches?
|
||||
for arg in args.iter_mut() {
|
||||
match arg {
|
||||
OperationArg::Statement(s) => {
|
||||
if s.predicate() == Predicate::Native(NativePredicate::ValueOf) {
|
||||
st_args.push(s.args()[0].clone())
|
||||
} else {
|
||||
panic!("Invalid statement argument.");
|
||||
}
|
||||
}
|
||||
// todo: better error handling
|
||||
OperationArg::Literal(v) => {
|
||||
let value_of_st = self.literal(public, v.clone())?;
|
||||
*arg = OperationArg::Statement(value_of_st.clone());
|
||||
st_args.push(value_of_st.args()[0].clone())
|
||||
}
|
||||
OperationArg::Entry(k, v) => {
|
||||
st_args.push(StatementArg::Key(AnchoredKey::from((SELF, k.as_str()))));
|
||||
st_args.push(StatementArg::Literal(v.clone()))
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(st_args)
|
||||
}
|
||||
|
||||
pub fn pub_op(&mut self, op: Operation) -> Result<Statement> {
|
||||
self.op(true, op)
|
||||
}
|
||||
|
|
@ -307,169 +275,190 @@ impl MainPodBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
fn op(&mut self, public: bool, op: Operation) -> Result<Statement> {
|
||||
fn op_statement(&mut self, op: Operation) -> Result<Statement> {
|
||||
use NativeOperation::*;
|
||||
let mut op = Self::fill_in_aux(Self::lower_op(op))?;
|
||||
let Operation(op_type, ref mut args, _) = &mut op;
|
||||
// TODO: argument type checking
|
||||
let pred = op_type.output_predicate().map(Ok).unwrap_or_else(|| {
|
||||
// We are dealing with a copy here.
|
||||
match (args).first() {
|
||||
Some(OperationArg::Statement(s)) if args.len() == 1 => Ok(s.predicate().clone()),
|
||||
_ => Err(Error::op_invalid_args("copy".to_string())),
|
||||
}
|
||||
})?;
|
||||
|
||||
let st_args: Vec<StatementArg> = match op_type {
|
||||
OperationType::Native(o) => match o {
|
||||
None => vec![],
|
||||
NewEntry | EqualFromEntries | NotEqualFromEntries | LtFromEntries
|
||||
| LtEqFromEntries => self.op_args_entries(public, args)?,
|
||||
CopyStatement => match &args[0] {
|
||||
OperationArg::Statement(s) => s.args().clone(),
|
||||
_ => {
|
||||
return Err(Error::op_invalid_args("copy".to_string()));
|
||||
}
|
||||
},
|
||||
TransitiveEqualFromStatements => {
|
||||
match (args[0].clone(), args[1].clone()) {
|
||||
(
|
||||
OperationArg::Statement(Statement::Equal(ak0, ak1)),
|
||||
OperationArg::Statement(Statement::Equal(ak2, ak3)),
|
||||
) => {
|
||||
// st_args0 == vec![ak0, ak1]
|
||||
// st_args1 == vec![ak1, ak2]
|
||||
// output statement Equals(ak0, ak2)
|
||||
if ak1 == ak2 {
|
||||
vec![StatementArg::Key(ak0), StatementArg::Key(ak3)]
|
||||
} else {
|
||||
return Err(Error::op_invalid_args(
|
||||
"transitivity equality".to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::op_invalid_args(
|
||||
"transitivity equality".to_string(),
|
||||
));
|
||||
}
|
||||
let arg_error = |s: &str| Error::op_invalid_args(s.to_string());
|
||||
let st = match op.0 {
|
||||
OperationType::Native(o) => match (o, &op.1.as_slice()) {
|
||||
(None, &[]) => Statement::None,
|
||||
(NewEntry, &[OperationArg::Entry(k, v)]) => {
|
||||
Statement::equal(AnchoredKey::from((SELF, k.as_str())), v.clone())
|
||||
}
|
||||
(EqualFromEntries, &[a1, a2]) => {
|
||||
let (r1, v1) = a1
|
||||
.value_and_ref()
|
||||
.ok_or_else(|| arg_error("equal-from-entries"))?;
|
||||
let (r2, v2) = a2
|
||||
.value_and_ref()
|
||||
.ok_or_else(|| arg_error("equal-from-entries"))?;
|
||||
if v1 == v2 {
|
||||
Statement::equal(r1, r2)
|
||||
} else {
|
||||
return Err(arg_error("equal-from-entries"));
|
||||
}
|
||||
}
|
||||
LtToNotEqual => match args[0].clone() {
|
||||
OperationArg::Statement(Statement::Lt(ak0, ak1)) => {
|
||||
vec![StatementArg::Key(ak0), StatementArg::Key(ak1)]
|
||||
(NotEqualFromEntries, &[a1, a2]) => {
|
||||
let (r1, v1) = a1
|
||||
.value_and_ref()
|
||||
.ok_or_else(|| arg_error("not-equal-from-entries"))?;
|
||||
let (r2, v2) = a2
|
||||
.value_and_ref()
|
||||
.ok_or_else(|| arg_error("not-equal-from-entries"))?;
|
||||
if v1 != v2 {
|
||||
Statement::not_equal(r1, r2)
|
||||
} else {
|
||||
return Err(arg_error("not-equal-from-entries"));
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::op_invalid_args("lt-to-neq".to_string()));
|
||||
}
|
||||
(LtFromEntries, &[a1, a2]) => {
|
||||
let (r1, v1) = a1
|
||||
.value_and_ref()
|
||||
.ok_or_else(|| arg_error("lt-from-entries"))?;
|
||||
let (r2, v2) = a2
|
||||
.value_and_ref()
|
||||
.ok_or_else(|| arg_error("lt-from-entries"))?;
|
||||
if v1 < v2 {
|
||||
Statement::lt(r1, r2)
|
||||
} else {
|
||||
return Err(arg_error("lt-from-entries"));
|
||||
}
|
||||
},
|
||||
SumOf => match (args[0].clone(), args[1].clone(), args[2].clone()) {
|
||||
(
|
||||
OperationArg::Statement(Statement::ValueOf(ak0, v0)),
|
||||
OperationArg::Statement(Statement::ValueOf(ak1, v1)),
|
||||
OperationArg::Statement(Statement::ValueOf(ak2, v2)),
|
||||
) => {
|
||||
let v0: i64 = v0.typed().try_into()?;
|
||||
let v1: i64 = v1.typed().try_into()?;
|
||||
let v2: i64 = v2.typed().try_into()?;
|
||||
if v0 == v1 + v2 {
|
||||
vec![
|
||||
StatementArg::Key(ak0),
|
||||
StatementArg::Key(ak1),
|
||||
StatementArg::Key(ak2),
|
||||
]
|
||||
} else {
|
||||
return Err(Error::op_invalid_args("sum-of".to_string()));
|
||||
}
|
||||
}
|
||||
(LtEqFromEntries, &[a1, a2]) => {
|
||||
let (r1, v1) = a1
|
||||
.value_and_ref()
|
||||
.ok_or_else(|| arg_error("lt-eq-from-entries"))?;
|
||||
let (r2, v2) = a2
|
||||
.value_and_ref()
|
||||
.ok_or_else(|| arg_error("lt-eq-from-entries"))?;
|
||||
if v1 <= v2 {
|
||||
Statement::not_equal(r1, r2)
|
||||
} else {
|
||||
return Err(arg_error("lt-eq-from-entries"));
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::op_invalid_args("sum-of".to_string()));
|
||||
}
|
||||
(CopyStatement, &[OperationArg::Statement(s)]) => s.clone(),
|
||||
(
|
||||
TransitiveEqualFromStatements,
|
||||
&[OperationArg::Statement(Statement::Equal(r1, r2)), OperationArg::Statement(Statement::Equal(r3, r4))],
|
||||
) => {
|
||||
if r2 == r3 {
|
||||
Statement::Equal(r1.clone(), r4.clone())
|
||||
} else {
|
||||
return Err(arg_error("transitive-eq"));
|
||||
}
|
||||
},
|
||||
ProductOf => match (args[0].clone(), args[1].clone(), args[2].clone()) {
|
||||
(
|
||||
OperationArg::Statement(Statement::ValueOf(ak0, v0)),
|
||||
OperationArg::Statement(Statement::ValueOf(ak1, v1)),
|
||||
OperationArg::Statement(Statement::ValueOf(ak2, v2)),
|
||||
) => {
|
||||
let v0: i64 = v0.typed().try_into()?;
|
||||
let v1: i64 = v1.typed().try_into()?;
|
||||
let v2: i64 = v2.typed().try_into()?;
|
||||
if v0 == v1 * v2 {
|
||||
vec![
|
||||
StatementArg::Key(ak0),
|
||||
StatementArg::Key(ak1),
|
||||
StatementArg::Key(ak2),
|
||||
]
|
||||
} else {
|
||||
return Err(Error::op_invalid_args("product-of".to_string()));
|
||||
}
|
||||
}
|
||||
(LtToNotEqual, &[OperationArg::Statement(Statement::Lt(r1, r2))]) => {
|
||||
Statement::NotEqual(r1.clone(), r2.clone())
|
||||
}
|
||||
(SumOf, &[a1, a2, a3]) => {
|
||||
let (r1, v1) = a1
|
||||
.value_and_ref()
|
||||
.ok_or_else(|| arg_error("sum-from-entries"))?;
|
||||
let (r2, v2) = a2
|
||||
.value_and_ref()
|
||||
.ok_or_else(|| arg_error("sum-from-entries"))?;
|
||||
let (r3, v3) = a3
|
||||
.value_and_ref()
|
||||
.ok_or_else(|| arg_error("sum-from-entries"))?;
|
||||
if middleware::Operation::check_int_fn(v1, v2, v3, sum_op)? {
|
||||
Statement::SumOf(r1, r2, r3)
|
||||
} else {
|
||||
return Err(arg_error("sum-from-entries"));
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::op_invalid_args("product-of".to_string()));
|
||||
}
|
||||
(ProductOf, &[a1, a2, a3]) => {
|
||||
let (r1, v1) = a1
|
||||
.value_and_ref()
|
||||
.ok_or_else(|| arg_error("prod-from-entries"))?;
|
||||
let (r2, v2) = a2
|
||||
.value_and_ref()
|
||||
.ok_or_else(|| arg_error("prod-from-entries"))?;
|
||||
let (r3, v3) = a3
|
||||
.value_and_ref()
|
||||
.ok_or_else(|| arg_error("prod-from-entries"))?;
|
||||
if middleware::Operation::check_int_fn(v1, v2, v3, prod_op)? {
|
||||
Statement::ProductOf(r1, r2, r3)
|
||||
} else {
|
||||
return Err(arg_error("prod-from-entries"));
|
||||
}
|
||||
},
|
||||
MaxOf => match (args[0].clone(), args[1].clone(), args[2].clone()) {
|
||||
(
|
||||
OperationArg::Statement(Statement::ValueOf(ak0, v0)),
|
||||
OperationArg::Statement(Statement::ValueOf(ak1, v1)),
|
||||
OperationArg::Statement(Statement::ValueOf(ak2, v2)),
|
||||
) => {
|
||||
let v0: i64 = v0.typed().try_into()?;
|
||||
let v1: i64 = v1.typed().try_into()?;
|
||||
let v2: i64 = v2.typed().try_into()?;
|
||||
if v0 == std::cmp::max(v1, v2) {
|
||||
vec![
|
||||
StatementArg::Key(ak0),
|
||||
StatementArg::Key(ak1),
|
||||
StatementArg::Key(ak2),
|
||||
]
|
||||
} else {
|
||||
return Err(Error::op_invalid_args("max-of".to_string()));
|
||||
}
|
||||
}
|
||||
(MaxOf, &[a1, a2, a3]) => {
|
||||
let (r1, v1) = a1
|
||||
.value_and_ref()
|
||||
.ok_or_else(|| arg_error("max-from-entries"))?;
|
||||
let (r2, v2) = a2
|
||||
.value_and_ref()
|
||||
.ok_or_else(|| arg_error("max-from-entries"))?;
|
||||
let (r3, v3) = a3
|
||||
.value_and_ref()
|
||||
.ok_or_else(|| arg_error("max-from-entries"))?;
|
||||
if middleware::Operation::check_int_fn(v1, v2, v3, max_op)? {
|
||||
Statement::MaxOf(r1, r2, r3)
|
||||
} else {
|
||||
return Err(arg_error("max-from-entries"));
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::op_invalid_args("max-of".to_string()));
|
||||
}
|
||||
(HashOf, &[a1, a2, a3]) => {
|
||||
let (r1, v1) = a1
|
||||
.value_and_ref()
|
||||
.ok_or_else(|| arg_error("hash-from-entries"))?;
|
||||
let (r2, v2) = a2
|
||||
.value_and_ref()
|
||||
.ok_or_else(|| arg_error("hash-from-entries"))?;
|
||||
let (r3, v3) = a3
|
||||
.value_and_ref()
|
||||
.ok_or_else(|| arg_error("hash-from-entries"))?;
|
||||
if v1 == &hash_op(v2.clone(), v3.clone()) {
|
||||
Statement::HashOf(r1, r2, r3)
|
||||
} else {
|
||||
return Err(arg_error("hash-from-entries"));
|
||||
}
|
||||
},
|
||||
HashOf => match (args[0].clone(), args[1].clone(), args[2].clone()) {
|
||||
(
|
||||
OperationArg::Statement(Statement::ValueOf(ak0, v0)),
|
||||
OperationArg::Statement(Statement::ValueOf(ak1, v1)),
|
||||
OperationArg::Statement(Statement::ValueOf(ak2, v2)),
|
||||
) => {
|
||||
if Hash::from(v0.raw()) == hash_values(&[v1, v2]) {
|
||||
vec![
|
||||
StatementArg::Key(ak0),
|
||||
StatementArg::Key(ak1),
|
||||
StatementArg::Key(ak2),
|
||||
]
|
||||
} else {
|
||||
return Err(Error::op_invalid_args("hash-of".to_string()));
|
||||
}
|
||||
}
|
||||
(ContainsFromEntries, &[a1, a2, a3]) => {
|
||||
let (r1, _v1) = a1
|
||||
.value_and_ref()
|
||||
.ok_or_else(|| arg_error("contains-from-entries"))?;
|
||||
let (r2, _v2) = a2
|
||||
.value_and_ref()
|
||||
.ok_or_else(|| arg_error("contains-from-entries"))?;
|
||||
let (r3, _v3) = a3
|
||||
.value_and_ref()
|
||||
.ok_or_else(|| arg_error("contains-from-entries"))?;
|
||||
// TODO: validate proof
|
||||
Statement::Contains(r1, r2, r3)
|
||||
}
|
||||
(NotContainsFromEntries, &[a1, a2]) => {
|
||||
let (r1, _v1) = a1
|
||||
.value_and_ref()
|
||||
.ok_or_else(|| arg_error("contains-from-entries"))?;
|
||||
let (r2, _v2) = a2
|
||||
.value_and_ref()
|
||||
.ok_or_else(|| arg_error("contains-from-entries"))?;
|
||||
// TODO: validate proof
|
||||
Statement::NotContains(r1, r2)
|
||||
}
|
||||
(t, _) => {
|
||||
if t.is_syntactic_sugar() {
|
||||
return Err(Error::custom(format!(
|
||||
"Unexpected syntactic sugar: {:?}",
|
||||
t
|
||||
)));
|
||||
} else {
|
||||
return Err(arg_error("malformed operation"));
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::op_invalid_args("hash-of".to_string()));
|
||||
}
|
||||
},
|
||||
ContainsFromEntries => self.op_args_entries(public, args)?,
|
||||
NotContainsFromEntries => self.op_args_entries(public, args)?,
|
||||
_ => Err(Error::custom(format!(
|
||||
"Unexpected syntactic sugar: {:?}",
|
||||
op_type
|
||||
)))?,
|
||||
}
|
||||
},
|
||||
OperationType::Custom(cpr) => {
|
||||
let pred = &cpr.batch.predicates()[cpr.index];
|
||||
if pred.statements.len() != args.len() {
|
||||
if pred.statements.len() != op.1.len() {
|
||||
return Err(Error::custom(format!(
|
||||
"Custom predicate operation needs {} statements but has {}.",
|
||||
pred.statements.len(),
|
||||
args.len()
|
||||
op.1.len()
|
||||
)));
|
||||
}
|
||||
// All args should be statements to be pattern matched against statement templates.
|
||||
let args = args.iter().map(
|
||||
let args = op.1.iter().map(
|
||||
|a| match a {
|
||||
OperationArg::Statement(s) => Ok(s.clone()),
|
||||
_ => Err(Error::custom(format!("Invalid argument {} to operation corresponding to custom predicate {:?}.", a, cpr)))
|
||||
|
|
@ -488,14 +477,20 @@ impl MainPodBuilder {
|
|||
}
|
||||
}
|
||||
let v_default = WildcardValue::PodId(SELF);
|
||||
wildcard_map
|
||||
let st_args: Vec<_> = wildcard_map
|
||||
.into_iter()
|
||||
.take(pred.args_len)
|
||||
.map(|v| StatementArg::WildcardLiteral(v.unwrap_or_else(|| v_default.clone())))
|
||||
.collect()
|
||||
.map(|v| v.unwrap_or_else(|| v_default.clone()))
|
||||
.collect();
|
||||
Statement::Custom(cpr, st_args)
|
||||
}
|
||||
};
|
||||
let st = Statement::from_args(pred, st_args).expect("valid arguments");
|
||||
Ok(st)
|
||||
}
|
||||
|
||||
fn op(&mut self, public: bool, op: Operation) -> Result<Statement> {
|
||||
let op = Self::fill_in_aux(Self::lower_op(op))?;
|
||||
let st = self.op_statement(op.clone())?;
|
||||
self.insert(public, (st, op));
|
||||
|
||||
Ok(self.statements[self.statements.len() - 1].clone())
|
||||
|
|
@ -514,7 +509,7 @@ impl MainPodBuilder {
|
|||
fn literal(&mut self, public: bool, value: Value) -> Result<Statement> {
|
||||
let public_value = (public, value);
|
||||
if let Some(key) = self.literals.get(&public_value) {
|
||||
Ok(Statement::ValueOf(
|
||||
Ok(Statement::equal(
|
||||
AnchoredKey::new(SELF, key.clone()),
|
||||
public_value.1,
|
||||
))
|
||||
|
|
@ -575,14 +570,11 @@ impl MainPodBuilder {
|
|||
let type_statement = pod
|
||||
.pub_statements()
|
||||
.into_iter()
|
||||
.find_map(|s| match s {
|
||||
Statement::ValueOf(AnchoredKey { pod_id: id, key }, value)
|
||||
if id == pod_id && key.hash() == type_key_hash =>
|
||||
.find_map(|s| match s.as_entry() {
|
||||
Some((AnchoredKey { pod_id: id, key }, _))
|
||||
if id == &pod_id && key.hash() == type_key_hash =>
|
||||
{
|
||||
Some(Statement::ValueOf(
|
||||
AnchoredKey::from((pod_id, KEY_TYPE)),
|
||||
value,
|
||||
))
|
||||
Some(s)
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
|
|
@ -648,13 +640,13 @@ impl MainPod {
|
|||
self.pod.id()
|
||||
}
|
||||
|
||||
/// Returns the value of a ValueOf statement with self id that defines key if it exists.
|
||||
/// Returns the value of a Equal statement with self id that defines key if it exists.
|
||||
pub fn get(&self, key: impl Into<Key>) -> Option<Value> {
|
||||
let key: Key = key.into();
|
||||
self.public_statements
|
||||
.iter()
|
||||
.find_map(|st| match st {
|
||||
Statement::ValueOf(ak, value)
|
||||
Statement::Equal(ValueRef::Key(ak), ValueRef::Literal(value))
|
||||
if ak.pod_id == self.id() && ak.key.hash() == key.hash() =>
|
||||
{
|
||||
Some(value)
|
||||
|
|
@ -701,11 +693,7 @@ impl MainPodCompiler {
|
|||
fn compile_op_arg(&self, op_arg: &OperationArg) -> Option<Statement> {
|
||||
match op_arg {
|
||||
OperationArg::Statement(s) => Some(s.clone()),
|
||||
OperationArg::Literal(_v) => {
|
||||
// OperationArg::Literal is a syntax sugar for the frontend. This is translated to
|
||||
// a new ValueOf statement and it's key used instead.
|
||||
unreachable!()
|
||||
}
|
||||
OperationArg::Literal(_v) => Some(Statement::None),
|
||||
OperationArg::Entry(_k, _v) => {
|
||||
// OperationArg::Entry is only used in the frontend. The (key, value) will only
|
||||
// appear in the ValueOf statement in the backend. This is because a new ValueOf
|
||||
|
|
@ -1108,7 +1096,7 @@ pub mod tests {
|
|||
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn test_incorrect_pod() {
|
||||
fn test_reject_duplicate_new_entry() {
|
||||
// try to insert the same key multiple times
|
||||
// right now this is not caught when you build the pod,
|
||||
// but it is caught on verify
|
||||
|
|
@ -1117,7 +1105,7 @@ pub mod tests {
|
|||
let params = Params::default();
|
||||
let vd_set = &*DEFAULT_VD_SET;
|
||||
let mut builder = MainPodBuilder::new(¶ms, &vd_set);
|
||||
let st = Statement::ValueOf(AnchoredKey::from((SELF, "a")), Value::from(3));
|
||||
let st = Statement::equal(AnchoredKey::from((SELF, "a")), Value::from(3));
|
||||
let op_new_entry = Operation(
|
||||
OperationType::Native(NativeOperation::NewEntry),
|
||||
vec![],
|
||||
|
|
@ -1125,24 +1113,35 @@ pub mod tests {
|
|||
);
|
||||
builder.insert(false, (st, op_new_entry.clone()));
|
||||
|
||||
let st = Statement::ValueOf(AnchoredKey::from((SELF, "a")), Value::from(28));
|
||||
let st = Statement::equal(AnchoredKey::from((SELF, "a")), Value::from(28));
|
||||
builder.insert(false, (st, op_new_entry.clone()));
|
||||
|
||||
let mut prover = MockProver {};
|
||||
let pod = builder.prove(&mut prover, ¶ms).unwrap();
|
||||
pod.pod.verify().unwrap();
|
||||
}
|
||||
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn test_reject_unsound_statement() {
|
||||
// try to insert a statement that doesn't follow from the operation
|
||||
// right now the mock prover catches this when it calls compile()
|
||||
let params = Params::default();
|
||||
let vd_set = &*DEFAULT_VD_SET;
|
||||
let mut builder = MainPodBuilder::new(¶ms, &vd_set);
|
||||
let self_a = AnchoredKey::from((SELF, "a"));
|
||||
let self_b = AnchoredKey::from((SELF, "b"));
|
||||
let value_of_a = Statement::ValueOf(self_a.clone(), Value::from(3));
|
||||
let value_of_b = Statement::ValueOf(self_b.clone(), Value::from(27));
|
||||
let value_of_a = Statement::equal(self_a.clone(), Value::from(3));
|
||||
let value_of_b = Statement::equal(self_b.clone(), Value::from(27));
|
||||
|
||||
let op_new_entry = Operation(
|
||||
OperationType::Native(NativeOperation::NewEntry),
|
||||
vec![],
|
||||
OperationAux::None,
|
||||
);
|
||||
builder.insert(false, (value_of_a.clone(), op_new_entry.clone()));
|
||||
builder.insert(false, (value_of_b.clone(), op_new_entry));
|
||||
let st = Statement::Equal(self_a, self_b);
|
||||
let st = Statement::equal(self_a, self_b);
|
||||
let op = Operation(
|
||||
OperationType::Native(NativeOperation::EqualFromEntries),
|
||||
vec![
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ use std::fmt;
|
|||
|
||||
use crate::{
|
||||
frontend::{MainPod, SignedPod},
|
||||
middleware::{AnchoredKey, OperationAux, OperationType, Statement, Value},
|
||||
middleware::{
|
||||
AnchoredKey, CustomPredicateRef, NativeOperation, OperationAux, OperationType, Statement,
|
||||
Value, ValueRef,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
|
@ -18,7 +21,15 @@ impl OperationArg {
|
|||
pub(crate) fn value(&self) -> Option<&Value> {
|
||||
match self {
|
||||
Self::Literal(v) => Some(v),
|
||||
Self::Statement(Statement::ValueOf(_, v)) => Some(v),
|
||||
Self::Statement(Statement::Equal(_, ValueRef::Literal(v))) => Some(v),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn value_and_ref(&self) -> Option<(ValueRef, &Value)> {
|
||||
match self {
|
||||
Self::Literal(v) => Some((ValueRef::Literal(v.clone()), v)),
|
||||
Self::Statement(Statement::Equal(k, ValueRef::Literal(v))) => Some((k.clone(), v)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -72,9 +83,9 @@ impl From<(&SignedPod, &str)> for OperationArg {
|
|||
.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,
|
||||
Self::Statement(Statement::Equal(
|
||||
AnchoredKey::from((pod.id(), key)).into(),
|
||||
value.into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
@ -84,10 +95,7 @@ impl From<(&MainPod, &str)> for OperationArg {
|
|||
let value = pod
|
||||
.get(key)
|
||||
.unwrap_or_else(|| panic!("Key {} is not present in POD: {}", key, pod));
|
||||
Self::Statement(Statement::ValueOf(
|
||||
AnchoredKey::from((pod.id(), key)),
|
||||
value,
|
||||
))
|
||||
Self::Statement(Statement::equal(AnchoredKey::from((pod.id(), key)), value))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -97,6 +105,12 @@ impl From<Statement> for OperationArg {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<&Statement> for OperationArg {
|
||||
fn from(value: &Statement) -> Self {
|
||||
value.clone().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Into<Value>> From<(&str, V)> for OperationArg {
|
||||
fn from((key, value): (&str, V)) -> Self {
|
||||
Self::Entry(key.to_string(), value.into())
|
||||
|
|
@ -118,3 +132,78 @@ impl fmt::Display for Operation {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! op_impl_oa {
|
||||
($fn_name: ident, $op_name: ident, 2) => {
|
||||
pub fn $fn_name(a1: impl Into<OperationArg>, a2: impl Into<OperationArg>) -> Self {
|
||||
Self(
|
||||
OperationType::Native(NativeOperation::$op_name),
|
||||
vec![a1.into(), a2.into()],
|
||||
OperationAux::None,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
($fn_name: ident, $op_name: ident, 3) => {
|
||||
pub fn $fn_name(
|
||||
a1: impl Into<OperationArg>,
|
||||
a2: impl Into<OperationArg>,
|
||||
a3: impl Into<OperationArg>,
|
||||
) -> Self {
|
||||
Self(
|
||||
OperationType::Native(NativeOperation::$op_name),
|
||||
vec![a1.into(), a2.into(), a3.into()],
|
||||
OperationAux::None,
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! op_impl_st {
|
||||
($fn_name: ident, $op_name: ident, 1) => {
|
||||
pub fn $fn_name(a1: &Statement) -> Self {
|
||||
Self(
|
||||
OperationType::Native(NativeOperation::$op_name),
|
||||
vec![a1.into()],
|
||||
OperationAux::None,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
($fn_name: ident, $op_name: ident, 2) => {
|
||||
pub fn $fn_name(a1: &Statement, a2: &Statement) -> Self {
|
||||
Self(
|
||||
OperationType::Native(NativeOperation::$op_name),
|
||||
vec![a1.into(), a2.into()],
|
||||
OperationAux::None,
|
||||
)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl Operation {
|
||||
pub fn new_entry(a1: impl Into<OperationArg>, a2: impl Into<Value>) -> Self {
|
||||
Self(
|
||||
OperationType::Native(NativeOperation::NewEntry),
|
||||
vec![a1.into(), a2.into().into()],
|
||||
OperationAux::None,
|
||||
)
|
||||
}
|
||||
op_impl_oa!(eq, EqualFromEntries, 2);
|
||||
op_impl_oa!(ne, NotEqualFromEntries, 2);
|
||||
op_impl_oa!(gt, GtFromEntries, 2);
|
||||
op_impl_oa!(lt, LtFromEntries, 2);
|
||||
op_impl_st!(transitive_eq, TransitiveEqualFromStatements, 2);
|
||||
op_impl_st!(gt_to_ne, GtToNotEqual, 1);
|
||||
op_impl_oa!(sum_of, SumOf, 3);
|
||||
op_impl_oa!(product_of, ProductOf, 3);
|
||||
op_impl_oa!(max_of, MaxOf, 3);
|
||||
pub fn custom(cpr: CustomPredicateRef, args: Vec<OperationArg>) -> Self {
|
||||
Self(OperationType::Custom(cpr), args, OperationAux::None)
|
||||
}
|
||||
op_impl_oa!(dict_contains, DictContainsFromEntries, 3);
|
||||
op_impl_oa!(dict_not_contains, DictNotContainsFromEntries, 2);
|
||||
op_impl_oa!(set_contains, SetContainsFromEntries, 3);
|
||||
op_impl_oa!(set_not_contains, SetNotContainsFromEntries, 2);
|
||||
op_impl_oa!(array_contains, ArrayContainsFromEntries, 3);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue