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:
parent
9e860ef262
commit
c232c8dae5
33 changed files with 1985 additions and 2800 deletions
2
.github/workflows/typos.toml
vendored
2
.github/workflows/typos.toml
vendored
|
|
@ -5,3 +5,5 @@ Ded = "Ded" # "ANDed", it thought "Ded" should be "Dead"
|
||||||
OT = "OT"
|
OT = "OT"
|
||||||
aks = "aks" # anchored keys
|
aks = "aks" # anchored keys
|
||||||
nin = "nin" # not in
|
nin = "nin" # not in
|
||||||
|
kow = "kow" # key or wildcard
|
||||||
|
KOW = "KOW" # Key Or Wildcard
|
||||||
|
|
|
||||||
|
|
@ -2,37 +2,10 @@
|
||||||
//! `backend_plonky2` feature is enabled.
|
//! `backend_plonky2` feature is enabled.
|
||||||
//! See src/middleware/basetypes.rs for more details.
|
//! See src/middleware/basetypes.rs for more details.
|
||||||
|
|
||||||
use std::{
|
use plonky2::plonk::{config::PoseidonGoldilocksConfig, proof::Proof as Plonky2Proof};
|
||||||
cmp::{Ord, Ordering},
|
|
||||||
fmt,
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::{anyhow, Error, Result};
|
use crate::middleware::F;
|
||||||
use hex::{FromHex, FromHexError};
|
|
||||||
use plonky2::{
|
|
||||||
field::{
|
|
||||||
goldilocks_field::GoldilocksField,
|
|
||||||
types::{Field, PrimeField64},
|
|
||||||
},
|
|
||||||
hash::poseidon::PoseidonHash,
|
|
||||||
plonk::{
|
|
||||||
config::{Hasher, PoseidonGoldilocksConfig},
|
|
||||||
proof::Proof as Plonky2Proof,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use schemars::JsonSchema;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
use crate::middleware::{
|
|
||||||
serialization::{
|
|
||||||
deserialize_hash_tuple, deserialize_value_tuple, serialize_hash_tuple,
|
|
||||||
serialize_value_tuple,
|
|
||||||
},
|
|
||||||
Params, ToFields,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// F is the native field we use everywhere. Currently it's Goldilocks from plonky2
|
|
||||||
pub type F = GoldilocksField;
|
|
||||||
/// C is the Plonky2 config used in POD2 to work with Plonky2 recursion.
|
/// C is the Plonky2 config used in POD2 to work with Plonky2 recursion.
|
||||||
pub type C = PoseidonGoldilocksConfig;
|
pub type C = PoseidonGoldilocksConfig;
|
||||||
/// D defines the extension degree of the field used in the Plonky2 proofs (quadratic extension).
|
/// D defines the extension degree of the field used in the Plonky2 proofs (quadratic extension).
|
||||||
|
|
@ -40,228 +13,3 @@ pub const D: usize = 2;
|
||||||
|
|
||||||
/// proof system proof
|
/// proof system proof
|
||||||
pub type Proof = Plonky2Proof<F, PoseidonGoldilocksConfig, D>;
|
pub type Proof = Plonky2Proof<F, PoseidonGoldilocksConfig, D>;
|
||||||
|
|
||||||
pub const HASH_SIZE: usize = 4;
|
|
||||||
pub const VALUE_SIZE: usize = 4;
|
|
||||||
|
|
||||||
pub const EMPTY_VALUE: Value = Value([F::ZERO, F::ZERO, F::ZERO, F::ZERO]);
|
|
||||||
pub const SELF_ID_HASH: Hash = Hash([F::ONE, F::ZERO, F::ZERO, F::ZERO]);
|
|
||||||
pub const EMPTY_HASH: Hash = Hash([F::ZERO, F::ZERO, F::ZERO, F::ZERO]);
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
|
||||||
#[schemars(rename = "MiddlewareValue")]
|
|
||||||
pub struct Value(
|
|
||||||
#[serde(
|
|
||||||
serialize_with = "serialize_value_tuple",
|
|
||||||
deserialize_with = "deserialize_value_tuple"
|
|
||||||
)]
|
|
||||||
// We know that Serde will serialize and deserialize this as a string, so we can
|
|
||||||
// use the JsonSchema to validate the format.
|
|
||||||
#[schemars(with = "String", regex(pattern = r"^[0-9a-fA-F]{64}$"))]
|
|
||||||
pub [F; VALUE_SIZE],
|
|
||||||
);
|
|
||||||
|
|
||||||
impl ToFields for Value {
|
|
||||||
fn to_fields(&self, _params: &Params) -> Vec<F> {
|
|
||||||
self.0.to_vec()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Value {
|
|
||||||
pub fn to_bytes(self) -> Vec<u8> {
|
|
||||||
self.0
|
|
||||||
.iter()
|
|
||||||
.flat_map(|e| e.to_canonical_u64().to_le_bytes())
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for Value {
|
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
|
||||||
for (lhs, rhs) in self.0.iter().zip(other.0.iter()).rev() {
|
|
||||||
let (lhs, rhs) = (lhs.to_canonical_u64(), rhs.to_canonical_u64());
|
|
||||||
match lhs.cmp(&rhs) {
|
|
||||||
Ordering::Less => return Ordering::Less,
|
|
||||||
Ordering::Greater => return Ordering::Greater,
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ordering::Equal
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for Value {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
Some(self.cmp(other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<i64> for Value {
|
|
||||||
fn from(v: i64) -> Self {
|
|
||||||
let lo = F::from_canonical_u64((v as u64) & 0xffffffff);
|
|
||||||
let hi = F::from_canonical_u64((v as u64) >> 32);
|
|
||||||
Value([lo, hi, F::ZERO, F::ZERO])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Hash> for Value {
|
|
||||||
fn from(h: Hash) -> Self {
|
|
||||||
Value(h.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryInto<i64> for Value {
|
|
||||||
type Error = Error;
|
|
||||||
fn try_into(self) -> std::result::Result<i64, Self::Error> {
|
|
||||||
let value = self.0;
|
|
||||||
if value[2..] != [F::ZERO, F::ZERO]
|
|
||||||
|| value[..2]
|
|
||||||
.iter()
|
|
||||||
.all(|x| x.to_canonical_u64() > u32::MAX as u64)
|
|
||||||
{
|
|
||||||
Err(anyhow!("Value not an element of the i64 embedding."))
|
|
||||||
} else {
|
|
||||||
Ok((value[0].to_canonical_u64() | (value[1].to_canonical_u64() << 32)) as i64)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Value {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
if self.0[2].is_zero() && self.0[3].is_zero() {
|
|
||||||
// Assume this is an integer
|
|
||||||
let (l0, l1) = (self.0[0].to_canonical_u64(), self.0[1].to_canonical_u64());
|
|
||||||
assert!(l0 < (1 << 32));
|
|
||||||
assert!(l1 < (1 << 32));
|
|
||||||
write!(f, "{}", l0 + l1 * (1 << 32))
|
|
||||||
} else {
|
|
||||||
// Assume this is a hash
|
|
||||||
Hash(self.0).fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Hash, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
|
|
||||||
pub struct Hash(
|
|
||||||
#[serde(
|
|
||||||
serialize_with = "serialize_hash_tuple",
|
|
||||||
deserialize_with = "deserialize_hash_tuple"
|
|
||||||
)]
|
|
||||||
#[schemars(with = "String", regex(pattern = r"^[0-9a-fA-F]{64}$"))]
|
|
||||||
pub [F; HASH_SIZE],
|
|
||||||
);
|
|
||||||
|
|
||||||
pub fn hash_value(input: &Value) -> Hash {
|
|
||||||
hash_fields(&input.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hash_fields(input: &[F]) -> Hash {
|
|
||||||
Hash(PoseidonHash::hash_no_pad(input).elements)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Value> for Hash {
|
|
||||||
fn from(v: Value) -> Self {
|
|
||||||
Hash(v.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Hash {
|
|
||||||
pub fn value(self) -> Value {
|
|
||||||
Value(self.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToFields for Hash {
|
|
||||||
fn to_fields(&self, _params: &Params) -> Vec<F> {
|
|
||||||
self.0.to_vec()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ord for Hash {
|
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
|
||||||
Value(self.0).cmp(&Value(other.0))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialOrd for Hash {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
Some(self.cmp(other))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Hash {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let v0 = self.0[0].to_canonical_u64();
|
|
||||||
for i in 0..HASH_SIZE {
|
|
||||||
write!(f, "{:02x}", (v0 >> (i * 8)) & 0xff)?;
|
|
||||||
}
|
|
||||||
write!(f, "…")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromHex for Hash {
|
|
||||||
type Error = FromHexError;
|
|
||||||
|
|
||||||
// TODO make it dependant on backend::Value len
|
|
||||||
fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
|
|
||||||
// In little endian
|
|
||||||
let bytes = <[u8; 32]>::from_hex(hex)?;
|
|
||||||
let mut buf: [u8; 8] = [0; 8];
|
|
||||||
let mut inner = [F::ZERO; HASH_SIZE];
|
|
||||||
for i in 0..HASH_SIZE {
|
|
||||||
buf.copy_from_slice(&bytes[8 * i..8 * (i + 1)]);
|
|
||||||
inner[i] = F::from_canonical_u64(u64::from_le_bytes(buf));
|
|
||||||
}
|
|
||||||
Ok(Self(inner))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&str> for Hash {
|
|
||||||
fn from(s: &str) -> Self {
|
|
||||||
hash_str(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hash_str(s: &str) -> Hash {
|
|
||||||
let mut input = s.as_bytes().to_vec();
|
|
||||||
input.push(1); // padding
|
|
||||||
|
|
||||||
// Merge 7 bytes into 1 field, because the field is slightly below 64 bits
|
|
||||||
let input: Vec<F> = input
|
|
||||||
.chunks(7)
|
|
||||||
.map(|bytes| {
|
|
||||||
let mut v: u64 = 0;
|
|
||||||
for b in bytes.iter().rev() {
|
|
||||||
v <<= 8;
|
|
||||||
v += *b as u64;
|
|
||||||
}
|
|
||||||
F::from_canonical_u64(v)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
hash_fields(&input)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_i64_value_roundtrip() {
|
|
||||||
let test_cases = [
|
|
||||||
0i64,
|
|
||||||
1,
|
|
||||||
-1,
|
|
||||||
i64::MAX,
|
|
||||||
i64::MIN,
|
|
||||||
42,
|
|
||||||
-42,
|
|
||||||
1 << 32,
|
|
||||||
-(1 << 32),
|
|
||||||
];
|
|
||||||
|
|
||||||
for &original in test_cases.iter() {
|
|
||||||
let value = Value::from(original);
|
|
||||||
let roundtrip: i64 = value.try_into().unwrap();
|
|
||||||
assert_eq!(original, roundtrip, "Failed roundtrip for {}", original);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ use crate::{
|
||||||
primitives::merkletree::MerkleClaimAndProofTarget,
|
primitives::merkletree::MerkleClaimAndProofTarget,
|
||||||
},
|
},
|
||||||
middleware::{
|
middleware::{
|
||||||
NativeOperation, NativePredicate, Params, Predicate, StatementArg, ToFields, Value,
|
NativeOperation, NativePredicate, Params, Predicate, RawValue, StatementArg, ToFields,
|
||||||
EMPTY_VALUE, F, HASH_SIZE, OPERATION_ARG_F_LEN, OPERATION_AUX_F_LEN, STATEMENT_ARG_F_LEN,
|
EMPTY_VALUE, F, HASH_SIZE, OPERATION_ARG_F_LEN, OPERATION_AUX_F_LEN, STATEMENT_ARG_F_LEN,
|
||||||
VALUE_SIZE,
|
VALUE_SIZE,
|
||||||
},
|
},
|
||||||
|
|
@ -294,7 +294,7 @@ pub trait CircuitBuilderPod<F: RichField + Extendable<D>, const D: usize> {
|
||||||
fn add_virtual_operation(&mut self, params: &Params) -> OperationTarget;
|
fn add_virtual_operation(&mut self, params: &Params) -> OperationTarget;
|
||||||
fn select_value(&mut self, b: BoolTarget, x: ValueTarget, y: ValueTarget) -> ValueTarget;
|
fn select_value(&mut self, b: BoolTarget, x: ValueTarget, y: ValueTarget) -> ValueTarget;
|
||||||
fn select_bool(&mut self, b: BoolTarget, x: BoolTarget, y: BoolTarget) -> BoolTarget;
|
fn select_bool(&mut self, b: BoolTarget, x: BoolTarget, y: BoolTarget) -> BoolTarget;
|
||||||
fn constant_value(&mut self, v: Value) -> ValueTarget;
|
fn constant_value(&mut self, v: RawValue) -> ValueTarget;
|
||||||
fn is_equal_slice(&mut self, xs: &[Target], ys: &[Target]) -> BoolTarget;
|
fn is_equal_slice(&mut self, xs: &[Target], ys: &[Target]) -> BoolTarget;
|
||||||
|
|
||||||
// Convenience methods for checking values.
|
// Convenience methods for checking values.
|
||||||
|
|
@ -365,7 +365,7 @@ impl CircuitBuilderPod<F, D> for CircuitBuilder<F, D> {
|
||||||
BoolTarget::new_unsafe(self.select(b, x.target, y.target))
|
BoolTarget::new_unsafe(self.select(b, x.target, y.target))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn constant_value(&mut self, v: Value) -> ValueTarget {
|
fn constant_value(&mut self, v: RawValue) -> ValueTarget {
|
||||||
ValueTarget {
|
ValueTarget {
|
||||||
elements: std::array::from_fn(|i| {
|
elements: std::array::from_fn(|i| {
|
||||||
self.constant(F::from_noncanonical_u64(v.0[i].to_noncanonical_u64()))
|
self.constant(F::from_noncanonical_u64(v.0[i].to_noncanonical_u64()))
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use plonky2::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backends::plonky2::{
|
backends::plonky2::{
|
||||||
basetypes::{Value, D, EMPTY_HASH, F, VALUE_SIZE},
|
basetypes::D,
|
||||||
circuits::{
|
circuits::{
|
||||||
common::{
|
common::{
|
||||||
CircuitBuilderPod, Flattenable, MerkleClaimTarget, OperationTarget,
|
CircuitBuilderPod, Flattenable, MerkleClaimTarget, OperationTarget,
|
||||||
|
|
@ -24,8 +24,8 @@ use crate::{
|
||||||
signedpod::SignedPod,
|
signedpod::SignedPod,
|
||||||
},
|
},
|
||||||
middleware::{
|
middleware::{
|
||||||
hash_str, AnchoredKey, NativeOperation, NativePredicate, Params, PodType, Statement,
|
AnchoredKey, NativeOperation, NativePredicate, Params, PodType, Statement, StatementArg,
|
||||||
StatementArg, ToFields, KEY_TYPE, SELF,
|
ToFields, Value, F, KEY_TYPE, SELF, VALUE_SIZE,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -304,7 +304,7 @@ impl OperationVerifyGadget {
|
||||||
let st_code_ok = st.has_native_type(builder, &self.params, NativePredicate::ValueOf);
|
let st_code_ok = st.has_native_type(builder, &self.params, NativePredicate::ValueOf);
|
||||||
|
|
||||||
let expected_arg_prefix = builder.constants(
|
let expected_arg_prefix = builder.constants(
|
||||||
&StatementArg::Key(AnchoredKey(SELF, EMPTY_HASH)).to_fields(&self.params)[..VALUE_SIZE],
|
&StatementArg::Key(AnchoredKey::from((SELF, ""))).to_fields(&self.params)[..VALUE_SIZE],
|
||||||
);
|
);
|
||||||
let arg_prefix_ok =
|
let arg_prefix_ok =
|
||||||
builder.is_equal_slice(&st.args[0].elements[..VALUE_SIZE], &expected_arg_prefix);
|
builder.is_equal_slice(&st.args[0].elements[..VALUE_SIZE], &expected_arg_prefix);
|
||||||
|
|
@ -422,10 +422,12 @@ impl MainPodVerifyGadget {
|
||||||
let type_statement = &pub_statements[0];
|
let type_statement = &pub_statements[0];
|
||||||
// TODO: Store this hash in a global static with lazy init so that we don't have to
|
// TODO: Store this hash in a global static with lazy init so that we don't have to
|
||||||
// compute it every time.
|
// compute it every time.
|
||||||
let key_type = hash_str(KEY_TYPE);
|
|
||||||
let expected_type_statement = StatementTarget::from_flattened(
|
let expected_type_statement = StatementTarget::from_flattened(
|
||||||
&builder.constants(
|
&builder.constants(
|
||||||
&Statement::ValueOf(AnchoredKey(SELF, key_type), Value::from(PodType::MockMain))
|
&Statement::ValueOf(
|
||||||
|
AnchoredKey::from((SELF, KEY_TYPE)),
|
||||||
|
Value::from(PodType::MockMain),
|
||||||
|
)
|
||||||
.to_fields(params),
|
.to_fields(params),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -541,7 +543,7 @@ mod tests {
|
||||||
mainpod::{OperationArg, OperationAux},
|
mainpod::{OperationArg, OperationAux},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
middleware::{OperationType, PodId},
|
middleware::{OperationType, PodId, RawValue},
|
||||||
};
|
};
|
||||||
|
|
||||||
fn operation_verify(
|
fn operation_verify(
|
||||||
|
|
@ -573,7 +575,7 @@ mod tests {
|
||||||
.map(|pf| pf.into())
|
.map(|pf| pf.into())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let operation_verify = OperationVerifyGadget {
|
OperationVerifyGadget {
|
||||||
params: params.clone(),
|
params: params.clone(),
|
||||||
}
|
}
|
||||||
.eval(
|
.eval(
|
||||||
|
|
@ -634,10 +636,10 @@ mod tests {
|
||||||
|
|
||||||
// NewEntry
|
// NewEntry
|
||||||
let st1: mainpod::Statement =
|
let st1: mainpod::Statement =
|
||||||
Statement::ValueOf(AnchoredKey(SELF, "hello".into()), 55.into()).into();
|
Statement::ValueOf(AnchoredKey::from((SELF, "hello")), Value::from(55)).into();
|
||||||
let st2: mainpod::Statement = Statement::ValueOf(
|
let st2: mainpod::Statement = Statement::ValueOf(
|
||||||
AnchoredKey(PodId(Value::from(75).into()), "hello".into()),
|
AnchoredKey::from((PodId(RawValue::from(75).into()), "hello")),
|
||||||
55.into(),
|
Value::from(55),
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
let prev_statements = vec![st2];
|
let prev_statements = vec![st2];
|
||||||
|
|
@ -665,13 +667,13 @@ mod tests {
|
||||||
|
|
||||||
// Eq
|
// Eq
|
||||||
let st2: mainpod::Statement = Statement::ValueOf(
|
let st2: mainpod::Statement = Statement::ValueOf(
|
||||||
AnchoredKey(PodId(Value::from(75).into()), "world".into()),
|
AnchoredKey::from((PodId(RawValue::from(75).into()), "world")),
|
||||||
55.into(),
|
Value::from(55),
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
let st: mainpod::Statement = Statement::Equal(
|
let st: mainpod::Statement = Statement::Equal(
|
||||||
AnchoredKey(SELF, "hello".into()),
|
AnchoredKey::from((SELF, "hello")),
|
||||||
AnchoredKey(PodId(Value::from(75).into()), "world".into()),
|
AnchoredKey::from((PodId(RawValue::from(75).into()), "world")),
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
let op = mainpod::Operation(
|
let op = mainpod::Operation(
|
||||||
|
|
@ -684,13 +686,13 @@ mod tests {
|
||||||
|
|
||||||
// Lt
|
// Lt
|
||||||
let st2: mainpod::Statement = Statement::ValueOf(
|
let st2: mainpod::Statement = Statement::ValueOf(
|
||||||
AnchoredKey(PodId(Value::from(88).into()), "hello".into()),
|
AnchoredKey::from((PodId(RawValue::from(88).into()), "hello")),
|
||||||
56.into(),
|
Value::from(56),
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
let st: mainpod::Statement = Statement::Lt(
|
let st: mainpod::Statement = Statement::Lt(
|
||||||
AnchoredKey(SELF, "hello".into()),
|
AnchoredKey::from((SELF, "hello")),
|
||||||
AnchoredKey(PodId(Value::from(88).into()), "hello".into()),
|
AnchoredKey::from((PodId(RawValue::from(88).into()), "hello")),
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
let op = mainpod::Operation(
|
let op = mainpod::Operation(
|
||||||
|
|
@ -711,16 +713,16 @@ mod tests {
|
||||||
.collect();
|
.collect();
|
||||||
let mt = MerkleTree::new(params.max_depth_mt_gadget, &kvs)?;
|
let mt = MerkleTree::new(params.max_depth_mt_gadget, &kvs)?;
|
||||||
|
|
||||||
let root = mt.root().into();
|
let root = Value::from(mt.root());
|
||||||
let root_ak = AnchoredKey(PodId(Value::from(88).into()), "merkle root".into());
|
let root_ak = AnchoredKey::from((PodId(RawValue::from(88).into()), "merkle root"));
|
||||||
|
|
||||||
let key = 5.into();
|
let key = 5.into();
|
||||||
let key_ak = AnchoredKey(PodId(Value::from(88).into()), "key".into());
|
let key_ak = AnchoredKey::from((PodId(RawValue::from(88).into()), "key"));
|
||||||
|
|
||||||
let no_key_pf = mt.prove_nonexistence(&key)?;
|
let no_key_pf = mt.prove_nonexistence(&key)?;
|
||||||
|
|
||||||
let root_st: mainpod::Statement = Statement::ValueOf(root_ak, root).into();
|
let root_st: mainpod::Statement = Statement::ValueOf(root_ak.clone(), root.clone()).into();
|
||||||
let key_st: mainpod::Statement = Statement::ValueOf(key_ak, key).into();
|
let key_st: mainpod::Statement = Statement::ValueOf(key_ak.clone(), key.into()).into();
|
||||||
let st: mainpod::Statement = Statement::NotContains(root_ak, key_ak).into();
|
let st: mainpod::Statement = Statement::NotContains(root_ak, key_ak).into();
|
||||||
let op = mainpod::Operation(
|
let op = mainpod::Operation(
|
||||||
OperationType::Native(NativeOperation::NotContainsFromEntries),
|
OperationType::Native(NativeOperation::NotContainsFromEntries),
|
||||||
|
|
@ -729,7 +731,11 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
let merkle_proofs = vec![mainpod::MerkleClaimAndProof::try_from_middleware(
|
let merkle_proofs = vec![mainpod::MerkleClaimAndProof::try_from_middleware(
|
||||||
¶ms, &root, &key, None, &no_key_pf,
|
¶ms,
|
||||||
|
&root.raw(),
|
||||||
|
&key,
|
||||||
|
None,
|
||||||
|
&no_key_pf,
|
||||||
)?];
|
)?];
|
||||||
let prev_statements = vec![root_st, key_st];
|
let prev_statements = vec![root_st, key_st];
|
||||||
operation_verify(st, op, prev_statements, merkle_proofs.clone())?;
|
operation_verify(st, op, prev_statements, merkle_proofs.clone())?;
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ use plonky2::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backends::plonky2::{
|
backends::plonky2::{
|
||||||
basetypes::{Value, D, EMPTY_VALUE, F},
|
basetypes::D,
|
||||||
circuits::common::{CircuitBuilderPod, StatementArgTarget, StatementTarget, ValueTarget},
|
circuits::common::{CircuitBuilderPod, StatementArgTarget, StatementTarget, ValueTarget},
|
||||||
primitives::{
|
primitives::{
|
||||||
merkletree::{MerkleProof, MerkleProofExistenceGadget, MerkleProofExistenceTarget},
|
merkletree::{MerkleProof, MerkleProofExistenceGadget, MerkleProofExistenceTarget},
|
||||||
|
|
@ -22,7 +22,8 @@ use crate::{
|
||||||
signedpod::SignedPod,
|
signedpod::SignedPod,
|
||||||
},
|
},
|
||||||
middleware::{
|
middleware::{
|
||||||
hash_str, NativePredicate, Params, PodType, Predicate, ToFields, KEY_SIGNER, KEY_TYPE, SELF,
|
hash_str, Key, NativePredicate, Params, PodType, Predicate, RawValue, ToFields, Value,
|
||||||
|
EMPTY_VALUE, F, KEY_SIGNER, KEY_TYPE, SELF,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -48,7 +49,7 @@ impl SignedPodVerifyGadget {
|
||||||
let type_mt_proof = &mt_proofs[0];
|
let type_mt_proof = &mt_proofs[0];
|
||||||
let key_type = builder.constant_value(hash_str(KEY_TYPE).into());
|
let key_type = builder.constant_value(hash_str(KEY_TYPE).into());
|
||||||
builder.connect_values(type_mt_proof.key, key_type);
|
builder.connect_values(type_mt_proof.key, key_type);
|
||||||
let value_type = builder.constant_value(Value::from(PodType::Signed));
|
let value_type = builder.constant_value(Value::from(PodType::Signed).raw());
|
||||||
builder.connect_values(type_mt_proof.value, value_type);
|
builder.connect_values(type_mt_proof.value, value_type);
|
||||||
|
|
||||||
// 3.a. Verify signature
|
// 3.a. Verify signature
|
||||||
|
|
@ -56,7 +57,7 @@ impl SignedPodVerifyGadget {
|
||||||
|
|
||||||
// 3.b. Verify signer (ie. signature.pk == merkletree.signer_leaf)
|
// 3.b. Verify signer (ie. signature.pk == merkletree.signer_leaf)
|
||||||
let signer_mt_proof = &mt_proofs[1];
|
let signer_mt_proof = &mt_proofs[1];
|
||||||
let key_signer = builder.constant_value(hash_str(KEY_SIGNER).into());
|
let key_signer = builder.constant_value(Key::from(KEY_SIGNER).raw());
|
||||||
builder.connect_values(signer_mt_proof.key, key_signer);
|
builder.connect_values(signer_mt_proof.key, key_signer);
|
||||||
builder.connect_values(signer_mt_proof.value, signature.pk);
|
builder.connect_values(signer_mt_proof.value, signature.pk);
|
||||||
|
|
||||||
|
|
@ -122,21 +123,28 @@ impl SignedPodVerifyTarget {
|
||||||
// - empty leaves (if needed)
|
// - empty leaves (if needed)
|
||||||
|
|
||||||
// add proof verification of KEY_TYPE & KEY_SIGNER leaves
|
// add proof verification of KEY_TYPE & KEY_SIGNER leaves
|
||||||
let key_type_key = Value::from(hash_str(KEY_TYPE));
|
let key_type_key = Key::from(KEY_TYPE);
|
||||||
let key_signer_key = Value::from(hash_str(KEY_SIGNER));
|
let key_signer_key = Key::from(KEY_SIGNER);
|
||||||
let key_signer_value = [key_type_key, key_signer_key]
|
let key_signer_value = [&key_type_key, &key_signer_key]
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, k)| {
|
.map(|(i, k)| {
|
||||||
let (v, proof) = pod.dict.prove(k)?;
|
let (v, proof) = pod.dict.prove(k)?;
|
||||||
self.mt_proofs[i].set_targets(pw, true, pod.dict.commitment(), proof, *k, v)?;
|
self.mt_proofs[i].set_targets(
|
||||||
|
pw,
|
||||||
|
true,
|
||||||
|
pod.dict.commitment(),
|
||||||
|
proof,
|
||||||
|
k.raw(),
|
||||||
|
v.raw(),
|
||||||
|
)?;
|
||||||
Ok(v)
|
Ok(v)
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<Value>>>()?[1];
|
.collect::<Result<Vec<&Value>>>()?[1];
|
||||||
|
|
||||||
// add the verification of the rest of leaves
|
// add the verification of the rest of leaves
|
||||||
let mut curr = 2; // since we already added key_type and key_signer
|
let mut curr = 2; // since we already added key_type and key_signer
|
||||||
for (k, v) in pod.dict.iter().sorted_by_key(|kv| kv.0) {
|
for (k, v) in pod.dict.kvs().iter().sorted_by_key(|kv| kv.0.hash()) {
|
||||||
if *k == key_type_key || *k == key_signer_key {
|
if *k == key_type_key || *k == key_signer_key {
|
||||||
// skip the key_type & key_signer leaves, since they have
|
// skip the key_type & key_signer leaves, since they have
|
||||||
// already been checked
|
// already been checked
|
||||||
|
|
@ -144,9 +152,16 @@ impl SignedPodVerifyTarget {
|
||||||
}
|
}
|
||||||
|
|
||||||
let (obtained_v, proof) = pod.dict.prove(k)?;
|
let (obtained_v, proof) = pod.dict.prove(k)?;
|
||||||
assert_eq!(obtained_v, *v); // sanity check
|
assert_eq!(obtained_v, v); // sanity check
|
||||||
|
|
||||||
self.mt_proofs[curr].set_targets(pw, true, pod.dict.commitment(), proof, *k, *v)?;
|
self.mt_proofs[curr].set_targets(
|
||||||
|
pw,
|
||||||
|
true,
|
||||||
|
pod.dict.commitment(),
|
||||||
|
proof,
|
||||||
|
k.raw(),
|
||||||
|
v.raw(),
|
||||||
|
)?;
|
||||||
curr += 1;
|
curr += 1;
|
||||||
}
|
}
|
||||||
// sanity check
|
// sanity check
|
||||||
|
|
@ -170,9 +185,9 @@ impl SignedPodVerifyTarget {
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the signer pk
|
// get the signer pk
|
||||||
let pk = PublicKey(key_signer_value);
|
let pk = PublicKey(key_signer_value.raw());
|
||||||
// the msg signed is the pod.id
|
// the msg signed is the pod.id
|
||||||
let msg = Value::from(pod.id.0);
|
let msg = RawValue::from(pod.id.0);
|
||||||
|
|
||||||
// set signature targets values
|
// set signature targets values
|
||||||
self.signature
|
self.signature
|
||||||
|
|
|
||||||
|
|
@ -11,13 +11,13 @@ use plonky2::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backends::plonky2::{
|
backends::plonky2::{
|
||||||
basetypes::{C, D, F},
|
basetypes::{C, D},
|
||||||
circuits::mainpod::{MainPodVerifyCircuit, MainPodVerifyInput},
|
circuits::mainpod::{MainPodVerifyCircuit, MainPodVerifyInput},
|
||||||
mock::mainpod::{hash_statements, MockMainPod, Statement},
|
mock::mainpod::{hash_statements, MockMainPod, Statement},
|
||||||
signedpod::SignedPod,
|
signedpod::SignedPod,
|
||||||
},
|
},
|
||||||
middleware::{
|
middleware::{
|
||||||
self, AnchoredKey, MainPodInputs, Params, Pod, PodId, PodProver, StatementArg, SELF,
|
self, AnchoredKey, MainPodInputs, Params, Pod, PodId, PodProver, StatementArg, F, SELF,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
// TODO: Move the shared components between MockMainPod and MainPod to a common place.
|
// TODO: Move the shared components between MockMainPod and MainPod to a common place.
|
||||||
|
|
@ -136,10 +136,10 @@ impl Pod for MainPod {
|
||||||
.1
|
.1
|
||||||
.iter()
|
.iter()
|
||||||
.map(|sa| match &sa {
|
.map(|sa| match &sa {
|
||||||
StatementArg::Key(AnchoredKey(pod_id, h)) if *pod_id == SELF => {
|
StatementArg::Key(AnchoredKey { pod_id, key }) if *pod_id == SELF => {
|
||||||
StatementArg::Key(AnchoredKey(self.id(), *h))
|
StatementArg::Key(AnchoredKey::new(self.id(), key.clone()))
|
||||||
}
|
}
|
||||||
_ => *sa,
|
_ => sa.clone(),
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
|
|
@ -168,61 +168,12 @@ pub mod tests {
|
||||||
backends::plonky2::{
|
backends::plonky2::{
|
||||||
mock::mainpod::MockProver, primitives::signature::SecretKey, signedpod::Signer,
|
mock::mainpod::MockProver, primitives::signature::SecretKey, signedpod::Signer,
|
||||||
},
|
},
|
||||||
examples::zu_kyc_sign_pod_builders,
|
examples::{zu_kyc_pod_builder, zu_kyc_sign_pod_builders},
|
||||||
frontend, middleware,
|
frontend, middleware,
|
||||||
middleware::Value,
|
middleware::RawValue,
|
||||||
op,
|
op,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Use the method from examples once everything works
|
|
||||||
pub fn zu_kyc_pod_builder(
|
|
||||||
params: &Params,
|
|
||||||
gov_id: &frontend::SignedPod,
|
|
||||||
pay_stub: &frontend::SignedPod,
|
|
||||||
sanction_list: &frontend::SignedPod,
|
|
||||||
) -> Result<frontend::MainPodBuilder> {
|
|
||||||
let sanction_set = match sanction_list.kvs.get("sanctionList") {
|
|
||||||
Some(frontend::Value::Set(s)) => Ok(s),
|
|
||||||
_ => Err(anyhow!("Missing sanction list!")),
|
|
||||||
}?;
|
|
||||||
let now_minus_18y: i64 = 1169909388;
|
|
||||||
let now_minus_1y: i64 = 1706367566;
|
|
||||||
|
|
||||||
let gov_id_kvs = gov_id.kvs();
|
|
||||||
let id_number_value = gov_id_kvs.get(&"idNumber".into()).unwrap();
|
|
||||||
|
|
||||||
let mut kyc = frontend::MainPodBuilder::new(params);
|
|
||||||
kyc.add_signed_pod(gov_id);
|
|
||||||
kyc.add_signed_pod(pay_stub);
|
|
||||||
kyc.add_signed_pod(sanction_list);
|
|
||||||
kyc.pub_op(op!(
|
|
||||||
set_not_contains,
|
|
||||||
(sanction_list, "sanctionList"),
|
|
||||||
(gov_id, "idNumber"),
|
|
||||||
sanction_set
|
|
||||||
.middleware_set()
|
|
||||||
.prove_nonexistence(id_number_value)?
|
|
||||||
))?;
|
|
||||||
kyc.pub_op(op!(lt, (gov_id, "dateOfBirth"), now_minus_18y))?;
|
|
||||||
kyc.pub_op(op!(
|
|
||||||
eq,
|
|
||||||
(gov_id, "socialSecurityNumber"),
|
|
||||||
(pay_stub, "socialSecurityNumber")
|
|
||||||
))?;
|
|
||||||
let start_date_st = kyc.pub_op(frontend::Operation(
|
|
||||||
frontend::OperationType::Native(frontend::NativeOperation::NewEntry),
|
|
||||||
vec![frontend::OperationArg::Entry(
|
|
||||||
"startDate".to_string(),
|
|
||||||
now_minus_1y.into(),
|
|
||||||
)],
|
|
||||||
middleware::OperationAux::None,
|
|
||||||
))?;
|
|
||||||
kyc.pub_op(op!(eq, (pay_stub, "startDate"), start_date_st))?;
|
|
||||||
kyc.pub_op(op!(eq, (pay_stub, "startDate"), now_minus_1y))?;
|
|
||||||
|
|
||||||
Ok(kyc)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_main_zu_kyc() -> Result<()> {
|
fn test_main_zu_kyc() -> Result<()> {
|
||||||
let params = middleware::Params {
|
let params = middleware::Params {
|
||||||
|
|
@ -232,15 +183,13 @@ pub mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let sanctions_values = vec!["A343434340".into()];
|
|
||||||
let sanction_set = frontend::Value::Set(frontend::containers::Set::new(sanctions_values)?);
|
|
||||||
let (gov_id_builder, pay_stub_builder, sanction_list_builder) =
|
let (gov_id_builder, pay_stub_builder, sanction_list_builder) =
|
||||||
zu_kyc_sign_pod_builders(¶ms, &sanction_set);
|
zu_kyc_sign_pod_builders(¶ms);
|
||||||
let mut signer = Signer(SecretKey(Value::from(1)));
|
let mut signer = Signer(SecretKey(RawValue::from(1)));
|
||||||
let gov_id_pod = gov_id_builder.sign(&mut signer)?;
|
let gov_id_pod = gov_id_builder.sign(&mut signer)?;
|
||||||
let mut signer = Signer(SecretKey(Value::from(2)));
|
let mut signer = Signer(SecretKey(RawValue::from(2)));
|
||||||
let pay_stub_pod = pay_stub_builder.sign(&mut signer)?;
|
let pay_stub_pod = pay_stub_builder.sign(&mut signer)?;
|
||||||
let mut signer = Signer(SecretKey(Value::from(3)));
|
let mut signer = Signer(SecretKey(RawValue::from(3)));
|
||||||
let sanction_list_pod = sanction_list_builder.sign(&mut signer)?;
|
let sanction_list_pod = sanction_list_builder.sign(&mut signer)?;
|
||||||
let kyc_builder =
|
let kyc_builder =
|
||||||
zu_kyc_pod_builder(¶ms, &gov_id_pod, &pay_stub_pod, &sanction_list_pod)?;
|
zu_kyc_pod_builder(¶ms, &gov_id_pod, &pay_stub_pod, &sanction_list_pod)?;
|
||||||
|
|
@ -267,7 +216,7 @@ pub mod tests {
|
||||||
gov_id_builder.insert("idNumber", "4242424242");
|
gov_id_builder.insert("idNumber", "4242424242");
|
||||||
gov_id_builder.insert("dateOfBirth", 1169909384);
|
gov_id_builder.insert("dateOfBirth", 1169909384);
|
||||||
gov_id_builder.insert("socialSecurityNumber", "G2121210");
|
gov_id_builder.insert("socialSecurityNumber", "G2121210");
|
||||||
let mut signer = Signer(SecretKey(Value::from(42)));
|
let mut signer = Signer(SecretKey(RawValue::from(42)));
|
||||||
let gov_id = gov_id_builder.sign(&mut signer).unwrap();
|
let gov_id = gov_id_builder.sign(&mut signer).unwrap();
|
||||||
let now_minus_18y: i64 = 1169909388;
|
let now_minus_18y: i64 = 1169909388;
|
||||||
let mut kyc_builder = frontend::MainPodBuilder::new(¶ms);
|
let mut kyc_builder = frontend::MainPodBuilder::new(¶ms);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
use std::{any::Any, fmt};
|
use std::{any::Any, fmt};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use base64::prelude::*;
|
// use base64::prelude::*;
|
||||||
use plonky2::{hash::poseidon::PoseidonHash, plonk::config::Hasher};
|
use plonky2::{hash::poseidon::PoseidonHash, plonk::config::Hasher};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
|
// use serde::{Deserialize, Serialize};
|
||||||
use crate::{
|
use crate::{
|
||||||
backends::plonky2::primitives::merkletree,
|
backends::plonky2::primitives::merkletree,
|
||||||
middleware::{
|
middleware::{
|
||||||
|
|
@ -27,7 +27,7 @@ impl PodProver for MockProver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct MockMainPod {
|
pub struct MockMainPod {
|
||||||
params: Params,
|
params: Params,
|
||||||
id: PodId,
|
id: PodId,
|
||||||
|
|
@ -199,7 +199,7 @@ impl MockMainPod {
|
||||||
// Public statements
|
// Public statements
|
||||||
assert!(inputs.public_statements.len() < params.max_public_statements);
|
assert!(inputs.public_statements.len() < params.max_public_statements);
|
||||||
let mut type_st = middleware::Statement::ValueOf(
|
let mut type_st = middleware::Statement::ValueOf(
|
||||||
AnchoredKey(SELF, hash_str(KEY_TYPE)),
|
AnchoredKey::from((SELF, KEY_TYPE)),
|
||||||
middleware::Value::from(PodType::MockMain),
|
middleware::Value::from(PodType::MockMain),
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
|
|
@ -235,9 +235,9 @@ impl MockMainPod {
|
||||||
pf,
|
pf,
|
||||||
) => Some(MerkleClaimAndProof::try_from_middleware(
|
) => Some(MerkleClaimAndProof::try_from_middleware(
|
||||||
params,
|
params,
|
||||||
root,
|
&root.raw(),
|
||||||
key,
|
&key.raw(),
|
||||||
Some(value),
|
Some(&value.raw()),
|
||||||
pf,
|
pf,
|
||||||
)),
|
)),
|
||||||
middleware::Operation::NotContainsFromEntries(
|
middleware::Operation::NotContainsFromEntries(
|
||||||
|
|
@ -245,7 +245,11 @@ impl MockMainPod {
|
||||||
middleware::Statement::ValueOf(_, key),
|
middleware::Statement::ValueOf(_, key),
|
||||||
pf,
|
pf,
|
||||||
) => Some(MerkleClaimAndProof::try_from_middleware(
|
) => Some(MerkleClaimAndProof::try_from_middleware(
|
||||||
params, root, key, None, pf,
|
params,
|
||||||
|
&root.raw(),
|
||||||
|
&key.raw(),
|
||||||
|
None,
|
||||||
|
pf,
|
||||||
)),
|
)),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
|
|
@ -417,14 +421,14 @@ impl MockMainPod {
|
||||||
fill_pad(args, OperationArg::None, params.max_operation_args)
|
fill_pad(args, OperationArg::None, params.max_operation_args)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deserialize(serialized: String) -> Result<Self> {
|
// pub fn deserialize(serialized: String) -> Result<Self> {
|
||||||
let proof = String::from_utf8(BASE64_STANDARD.decode(&serialized)?)
|
// let proof = String::from_utf8(BASE64_STANDARD.decode(&serialized)?)
|
||||||
.map_err(|e| anyhow::anyhow!("Invalid base64 encoding: {}", e))?;
|
// .map_err(|e| anyhow::anyhow!("Invalid base64 encoding: {}", e))?;
|
||||||
let pod: MockMainPod = serde_json::from_str(&proof)
|
// let pod: MockMainPod = serde_json::from_str(&proof)
|
||||||
.map_err(|e| anyhow::anyhow!("Failed to parse proof: {}", e))?;
|
// .map_err(|e| anyhow::anyhow!("Failed to parse proof: {}", e))?;
|
||||||
|
|
||||||
Ok(pod)
|
// Ok(pod)
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hash_statements(statements: &[Statement], _params: &Params) -> middleware::Hash {
|
pub fn hash_statements(statements: &[Statement], _params: &Params) -> middleware::Hash {
|
||||||
|
|
@ -449,8 +453,8 @@ impl Pod for MockMainPod {
|
||||||
let has_type_statement = self.public_statements.iter().any(|s| {
|
let has_type_statement = self.public_statements.iter().any(|s| {
|
||||||
s.0 == Predicate::Native(NativePredicate::ValueOf)
|
s.0 == Predicate::Native(NativePredicate::ValueOf)
|
||||||
&& !s.1.is_empty()
|
&& !s.1.is_empty()
|
||||||
&& if let StatementArg::Key(AnchoredKey(pod_id, key_hash)) = s.1[0] {
|
&& if let StatementArg::Key(AnchoredKey { pod_id, ref key }) = s.1[0] {
|
||||||
pod_id == SELF && key_hash == hash_str(KEY_TYPE)
|
pod_id == SELF && key.hash() == hash_str(KEY_TYPE)
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
@ -477,7 +481,7 @@ impl Pod for MockMainPod {
|
||||||
.filter(|(_, s)| s.0 == Predicate::Native(NativePredicate::ValueOf))
|
.filter(|(_, s)| s.0 == Predicate::Native(NativePredicate::ValueOf))
|
||||||
.flat_map(|(i, s)| {
|
.flat_map(|(i, s)| {
|
||||||
if let StatementArg::Key(ak) = &s.1[0] {
|
if let StatementArg::Key(ak) = &s.1[0] {
|
||||||
vec![(i, ak.1, ak.0)]
|
vec![(i, ak.pod_id, ak.key.hash())]
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
@ -536,10 +540,10 @@ impl Pod for MockMainPod {
|
||||||
.1
|
.1
|
||||||
.iter()
|
.iter()
|
||||||
.map(|sa| match &sa {
|
.map(|sa| match &sa {
|
||||||
StatementArg::Key(AnchoredKey(pod_id, h)) if *pod_id == SELF => {
|
StatementArg::Key(AnchoredKey { pod_id, key }) if *pod_id == SELF => {
|
||||||
StatementArg::Key(AnchoredKey(self.id(), *h))
|
StatementArg::Key(AnchoredKey::new(self.id(), key.clone()))
|
||||||
}
|
}
|
||||||
_ => *sa,
|
_ => sa.clone(),
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
|
|
@ -557,7 +561,8 @@ impl Pod for MockMainPod {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialized_proof(&self) -> String {
|
fn serialized_proof(&self) -> String {
|
||||||
BASE64_STANDARD.encode(serde_json::to_string(self).unwrap())
|
todo!()
|
||||||
|
// BASE64_STANDARD.encode(serde_json::to_string(self).unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -570,19 +575,14 @@ pub mod tests {
|
||||||
great_boy_pod_full_flow, tickets_pod_full_flow, zu_kyc_pod_builder,
|
great_boy_pod_full_flow, tickets_pod_full_flow, zu_kyc_pod_builder,
|
||||||
zu_kyc_sign_pod_builders,
|
zu_kyc_sign_pod_builders,
|
||||||
},
|
},
|
||||||
middleware,
|
middleware::{self},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mock_main_zu_kyc() -> Result<()> {
|
fn test_mock_main_zu_kyc() -> Result<()> {
|
||||||
let params = middleware::Params::default();
|
let params = middleware::Params::default();
|
||||||
let sanctions_values = ["A343434340"].map(|s| crate::frontend::Value::from(s));
|
|
||||||
let sanction_set = crate::frontend::Value::Set(crate::frontend::containers::Set::new(
|
|
||||||
sanctions_values.to_vec(),
|
|
||||||
)?);
|
|
||||||
|
|
||||||
let (gov_id_builder, pay_stub_builder, sanction_list_builder) =
|
let (gov_id_builder, pay_stub_builder, sanction_list_builder) =
|
||||||
zu_kyc_sign_pod_builders(¶ms, &sanction_set);
|
zu_kyc_sign_pod_builders(¶ms);
|
||||||
let mut signer = MockSigner {
|
let mut signer = MockSigner {
|
||||||
pk: "ZooGov".into(),
|
pk: "ZooGov".into(),
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,19 @@ use std::{fmt, iter};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use plonky2::field::types::Field;
|
use plonky2::field::types::Field;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
|
// use serde::{Deserialize, Serialize};
|
||||||
use crate::{
|
use crate::{
|
||||||
backends::plonky2::{
|
backends::plonky2::{
|
||||||
mock::mainpod::Statement,
|
mock::mainpod::Statement,
|
||||||
primitives::merkletree::{self},
|
primitives::merkletree::{self},
|
||||||
},
|
},
|
||||||
middleware::{self, Hash, OperationType, Params, ToFields, Value, EMPTY_HASH, EMPTY_VALUE, F},
|
middleware::{
|
||||||
|
self, Hash, OperationType, Params, RawValue, ToFields, EMPTY_HASH, EMPTY_VALUE, F,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum OperationArg {
|
pub enum OperationArg {
|
||||||
None,
|
None,
|
||||||
Index(usize),
|
Index(usize),
|
||||||
|
|
@ -34,7 +36,7 @@ impl OperationArg {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum OperationAux {
|
pub enum OperationAux {
|
||||||
None,
|
None,
|
||||||
MerkleProofIndex(usize),
|
MerkleProofIndex(usize),
|
||||||
|
|
@ -50,17 +52,17 @@ impl ToFields for OperationAux {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct MerkleClaimAndProof {
|
pub struct MerkleClaimAndProof {
|
||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub root: Hash,
|
pub root: Hash,
|
||||||
pub key: Value,
|
pub key: RawValue,
|
||||||
pub value: Value,
|
pub value: RawValue,
|
||||||
pub existence: bool,
|
pub existence: bool,
|
||||||
pub siblings: Vec<Hash>,
|
pub siblings: Vec<Hash>,
|
||||||
pub case_ii_selector: bool,
|
pub case_ii_selector: bool,
|
||||||
pub other_key: Value,
|
pub other_key: RawValue,
|
||||||
pub other_value: Value,
|
pub other_value: RawValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MerkleClaimAndProof {
|
impl MerkleClaimAndProof {
|
||||||
|
|
@ -68,7 +70,7 @@ impl MerkleClaimAndProof {
|
||||||
Self {
|
Self {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
root: EMPTY_HASH,
|
root: EMPTY_HASH,
|
||||||
key: Value::from(1),
|
key: RawValue::from(1),
|
||||||
value: EMPTY_VALUE,
|
value: EMPTY_VALUE,
|
||||||
existence: false,
|
existence: false,
|
||||||
siblings: iter::repeat(EMPTY_HASH).take(max_depth).collect(),
|
siblings: iter::repeat(EMPTY_HASH).take(max_depth).collect(),
|
||||||
|
|
@ -79,9 +81,9 @@ impl MerkleClaimAndProof {
|
||||||
}
|
}
|
||||||
pub fn try_from_middleware(
|
pub fn try_from_middleware(
|
||||||
params: &Params,
|
params: &Params,
|
||||||
root: &Value,
|
root: &RawValue,
|
||||||
key: &Value,
|
key: &RawValue,
|
||||||
value: Option<&Value>,
|
value: Option<&RawValue>,
|
||||||
mid_mp: &merkletree::MerkleProof,
|
mid_mp: &merkletree::MerkleProof,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
if mid_mp.siblings.len() > params.max_depth_mt_gadget {
|
if mid_mp.siblings.len() > params.max_depth_mt_gadget {
|
||||||
|
|
@ -152,7 +154,7 @@ impl fmt::Display for MerkleClaimAndProof {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Operation(pub OperationType, pub Vec<OperationArg>, pub OperationAux);
|
pub struct Operation(pub OperationType, pub Vec<OperationArg>, pub OperationAux);
|
||||||
|
|
||||||
impl Operation {
|
impl Operation {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
|
// use serde::{Deserialize, Serialize};
|
||||||
use crate::middleware::{
|
use crate::middleware::{
|
||||||
self, AnchoredKey, NativePredicate, Params, Predicate, StatementArg, ToFields,
|
self, NativePredicate, Params, Predicate, StatementArg, ToFields, WildcardValue,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct Statement(pub Predicate, pub Vec<StatementArg>);
|
pub struct Statement(pub Predicate, pub Vec<StatementArg>);
|
||||||
|
|
||||||
impl Statement {
|
impl Statement {
|
||||||
|
|
@ -81,15 +81,15 @@ impl TryFrom<Statement> for middleware::Statement {
|
||||||
_ => Err(anyhow!("Ill-formed statement expression {:?}", s))?,
|
_ => Err(anyhow!("Ill-formed statement expression {:?}", s))?,
|
||||||
},
|
},
|
||||||
Predicate::Custom(cpr) => {
|
Predicate::Custom(cpr) => {
|
||||||
let aks: Vec<AnchoredKey> = proper_args
|
let vs: Vec<WildcardValue> = proper_args
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|arg| match arg {
|
.filter_map(|arg| match arg {
|
||||||
SA::None => None,
|
SA::None => None,
|
||||||
SA::Key(ak) => Some(ak),
|
SA::WildcardLiteral(v) => Some(v),
|
||||||
SA::Literal(_) => unreachable!(),
|
_ => unreachable!(),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
S::Custom(cpr, aks)
|
S::Custom(cpr, vs)
|
||||||
}
|
}
|
||||||
Predicate::BatchSelf(_) => {
|
Predicate::BatchSelf(_) => {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ use crate::{
|
||||||
backends::plonky2::primitives::merkletree::MerkleTree,
|
backends::plonky2::primitives::merkletree::MerkleTree,
|
||||||
constants::MAX_DEPTH,
|
constants::MAX_DEPTH,
|
||||||
middleware::{
|
middleware::{
|
||||||
containers::Dictionary, hash_str, AnchoredKey, Hash, Params, Pod, PodId, PodSigner,
|
containers::Dictionary, hash_str, AnchoredKey, Hash, Key, Params, Pod, PodId, PodSigner,
|
||||||
PodType, Statement, Value, KEY_SIGNER, KEY_TYPE,
|
PodType, RawValue, Statement, Value, KEY_SIGNER, KEY_TYPE,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -17,26 +17,22 @@ pub struct MockSigner {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MockSigner {
|
impl MockSigner {
|
||||||
pub fn pubkey(&self) -> Value {
|
pub fn pubkey(&self) -> Hash {
|
||||||
Value(hash_str(&self.pk).0)
|
hash_str(&self.pk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PodSigner for MockSigner {
|
impl PodSigner for MockSigner {
|
||||||
fn sign(&mut self, _params: &Params, kvs: &HashMap<Hash, Value>) -> Result<Box<dyn Pod>> {
|
fn sign(&mut self, _params: &Params, kvs: &HashMap<Key, Value>) -> Result<Box<dyn Pod>> {
|
||||||
let mut kvs = kvs.clone();
|
let mut kvs = kvs.clone();
|
||||||
let pubkey = self.pubkey();
|
let pubkey = self.pubkey();
|
||||||
kvs.insert(hash_str(KEY_SIGNER), pubkey);
|
kvs.insert(Key::from(KEY_SIGNER), Value::from(pubkey));
|
||||||
kvs.insert(hash_str(KEY_TYPE), Value::from(PodType::MockSigned));
|
kvs.insert(Key::from(KEY_TYPE), Value::from(PodType::MockSigned));
|
||||||
|
|
||||||
let dict = Dictionary::new(&kvs)?;
|
let dict = Dictionary::new(kvs.clone())?;
|
||||||
let id = PodId(dict.commitment());
|
let id = PodId(dict.commitment());
|
||||||
let signature = format!("{}_signed_by_{}", id, pubkey);
|
let signature = format!("{}_signed_by_{}", id, pubkey);
|
||||||
Ok(Box::new(MockSignedPod {
|
Ok(Box::new(MockSignedPod { id, signature, kvs }))
|
||||||
dict,
|
|
||||||
id,
|
|
||||||
signature,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -44,18 +40,18 @@ impl PodSigner for MockSigner {
|
||||||
pub struct MockSignedPod {
|
pub struct MockSignedPod {
|
||||||
id: PodId,
|
id: PodId,
|
||||||
signature: String,
|
signature: String,
|
||||||
dict: Dictionary,
|
kvs: HashMap<Key, Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MockSignedPod {
|
// impl MockSignedPod {
|
||||||
pub fn deserialize(id: PodId, signature: String, dict: Dictionary) -> Self {
|
// pub fn deserialize(id: PodId, signature: String, dict: Dictionary) -> Self {
|
||||||
Self {
|
// Self {
|
||||||
id,
|
// id,
|
||||||
signature,
|
// signature,
|
||||||
dict,
|
// dict,
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
impl Pod for MockSignedPod {
|
impl Pod for MockSignedPod {
|
||||||
fn verify(&self) -> Result<()> {
|
fn verify(&self) -> Result<()> {
|
||||||
|
|
@ -63,10 +59,10 @@ impl Pod for MockSignedPod {
|
||||||
let mt = MerkleTree::new(
|
let mt = MerkleTree::new(
|
||||||
MAX_DEPTH,
|
MAX_DEPTH,
|
||||||
&self
|
&self
|
||||||
.dict
|
.kvs
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(&k, &v)| (k, v))
|
.map(|(k, v)| (k.raw(), v.raw()))
|
||||||
.collect::<HashMap<Value, Value>>(),
|
.collect::<HashMap<RawValue, RawValue>>(),
|
||||||
)?;
|
)?;
|
||||||
let id = PodId(mt.root());
|
let id = PodId(mt.root());
|
||||||
if id != self.id {
|
if id != self.id {
|
||||||
|
|
@ -78,8 +74,11 @@ impl Pod for MockSignedPod {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Verify type
|
// 2. Verify type
|
||||||
let value_at_type = self.dict.get(&hash_str(KEY_TYPE).into())?;
|
let value_at_type = self
|
||||||
if Value::from(PodType::MockSigned) != value_at_type {
|
.kvs
|
||||||
|
.get(&Key::from(KEY_TYPE))
|
||||||
|
.ok_or(anyhow!("key not found"))?;
|
||||||
|
if &Value::from(PodType::MockSigned) != value_at_type {
|
||||||
return Err(anyhow!(
|
return Err(anyhow!(
|
||||||
"type does not match, expected MockSigned ({}), found {}",
|
"type does not match, expected MockSigned ({}), found {}",
|
||||||
PodType::MockSigned,
|
PodType::MockSigned,
|
||||||
|
|
@ -88,7 +87,10 @@ impl Pod for MockSignedPod {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Verify signature
|
// 3. Verify signature
|
||||||
let pk_hash = self.dict.get(&hash_str(KEY_SIGNER).into())?;
|
let pk_hash = self
|
||||||
|
.kvs
|
||||||
|
.get(&Key::from(KEY_SIGNER))
|
||||||
|
.ok_or(anyhow!("key not found"))?;
|
||||||
let signature = format!("{}_signed_by_{}", id, pk_hash);
|
let signature = format!("{}_signed_by_{}", id, pk_hash);
|
||||||
if signature != self.signature {
|
if signature != self.signature {
|
||||||
return Err(anyhow!(
|
return Err(anyhow!(
|
||||||
|
|
@ -108,15 +110,15 @@ impl Pod for MockSignedPod {
|
||||||
fn pub_statements(&self) -> Vec<Statement> {
|
fn pub_statements(&self) -> Vec<Statement> {
|
||||||
let id = self.id();
|
let id = self.id();
|
||||||
// By convention we put the KEY_TYPE first and KEY_SIGNER second
|
// By convention we put the KEY_TYPE first and KEY_SIGNER second
|
||||||
let mut kvs: HashMap<_, _> = self.dict.iter().collect();
|
let mut kvs = self.kvs.clone();
|
||||||
let key_type = Value::from(hash_str(KEY_TYPE));
|
let key_type = Key::from(KEY_TYPE);
|
||||||
let value_type = kvs.remove(&key_type).expect("KEY_TYPE");
|
let value_type = kvs.remove(&key_type).expect("KEY_TYPE");
|
||||||
let key_signer = Value::from(hash_str(KEY_SIGNER));
|
let key_signer = Key::from(KEY_SIGNER);
|
||||||
let value_signer = kvs.remove(&key_signer).expect("KEY_SIGNER");
|
let value_signer = kvs.remove(&key_signer).expect("KEY_SIGNER");
|
||||||
[(&key_type, value_type), (&key_signer, value_signer)]
|
[(key_type, value_type), (key_signer, value_signer)]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(kvs.into_iter().sorted_by_key(|kv| kv.0))
|
.chain(kvs.into_iter().sorted_by_key(|kv| kv.0.hash()))
|
||||||
.map(|(k, v)| Statement::ValueOf(AnchoredKey(id, Hash(k.0)), *v))
|
.map(|(k, v)| Statement::ValueOf(AnchoredKey::from((id, k)), v))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -140,9 +142,8 @@ pub mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
constants::MAX_DEPTH,
|
|
||||||
frontend,
|
frontend,
|
||||||
middleware::{self, EMPTY_HASH, F},
|
middleware::{self, EMPTY_VALUE, F},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -170,27 +171,25 @@ pub mod tests {
|
||||||
assert!(bad_pod.verify().is_err());
|
assert!(bad_pod.verify().is_err());
|
||||||
|
|
||||||
let mut bad_pod = pod.clone();
|
let mut bad_pod = pod.clone();
|
||||||
let bad_kv = (hash_str(KEY_SIGNER).into(), Value(PodId(EMPTY_HASH).0 .0));
|
let bad_kv = (Key::from(KEY_SIGNER), Value::from(EMPTY_VALUE));
|
||||||
let bad_kvs_mt = &bad_pod
|
let bad_kvs = bad_pod
|
||||||
.kvs()
|
.kvs
|
||||||
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(AnchoredKey(_, k), v)| (Value(k.0), v))
|
|
||||||
.chain(iter::once(bad_kv))
|
.chain(iter::once(bad_kv))
|
||||||
.collect::<HashMap<Value, Value>>();
|
.collect::<HashMap<Key, Value>>();
|
||||||
let bad_mt = MerkleTree::new(MAX_DEPTH, bad_kvs_mt)?;
|
bad_pod.kvs = bad_kvs;
|
||||||
bad_pod.dict.mt = bad_mt;
|
|
||||||
assert!(bad_pod.verify().is_err());
|
assert!(bad_pod.verify().is_err());
|
||||||
|
|
||||||
let mut bad_pod = pod.clone();
|
let mut bad_pod = pod.clone();
|
||||||
let bad_kv = (hash_str(KEY_TYPE).into(), Value::from(0));
|
let bad_kv = (Key::from(KEY_TYPE), Value::from(0));
|
||||||
let bad_kvs_mt = &bad_pod
|
let bad_kvs = bad_pod
|
||||||
.kvs()
|
.kvs
|
||||||
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(AnchoredKey(_, k), v)| (Value(k.0), v))
|
|
||||||
.chain(iter::once(bad_kv))
|
.chain(iter::once(bad_kv))
|
||||||
.collect::<HashMap<Value, Value>>();
|
.collect::<HashMap<Key, Value>>();
|
||||||
let bad_mt = MerkleTree::new(MAX_DEPTH, bad_kvs_mt)?;
|
bad_pod.kvs = bad_kvs;
|
||||||
bad_pod.dict.mt = bad_mt;
|
|
||||||
assert!(bad_pod.verify().is_err());
|
assert!(bad_pod.verify().is_err());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ use std::{collections::HashMap, fmt, iter::IntoIterator};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use plonky2::field::types::Field;
|
use plonky2::field::types::Field;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
|
// use serde::{Deserialize, Serialize};
|
||||||
pub use super::merkletree_circuit::*;
|
pub use super::merkletree_circuit::*;
|
||||||
use crate::backends::plonky2::basetypes::{hash_fields, Hash, Value, EMPTY_HASH, F};
|
use crate::middleware::{hash_fields, Hash, RawValue, EMPTY_HASH, F};
|
||||||
|
|
||||||
/// Implements the MerkleTree specified at
|
/// Implements the MerkleTree specified at
|
||||||
/// https://0xparc.github.io/pod2/merkletree.html
|
/// https://0xparc.github.io/pod2/merkletree.html
|
||||||
|
|
@ -19,7 +19,7 @@ pub struct MerkleTree {
|
||||||
|
|
||||||
impl MerkleTree {
|
impl MerkleTree {
|
||||||
/// builds a new `MerkleTree` where the leaves contain the given key-values
|
/// builds a new `MerkleTree` where the leaves contain the given key-values
|
||||||
pub fn new(max_depth: usize, kvs: &HashMap<Value, Value>) -> Result<Self> {
|
pub fn new(max_depth: usize, kvs: &HashMap<RawValue, RawValue>) -> Result<Self> {
|
||||||
// Construct leaves.
|
// Construct leaves.
|
||||||
let mut leaves: Vec<_> = kvs
|
let mut leaves: Vec<_> = kvs
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -50,7 +50,7 @@ impl MerkleTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// returns the value at the given key
|
/// returns the value at the given key
|
||||||
pub fn get(&self, key: &Value) -> Result<Value> {
|
pub fn get(&self, key: &RawValue) -> Result<RawValue> {
|
||||||
let path = keypath(self.max_depth, *key)?;
|
let path = keypath(self.max_depth, *key)?;
|
||||||
let key_resolution = self.root.down(0, self.max_depth, path, None)?;
|
let key_resolution = self.root.down(0, self.max_depth, path, None)?;
|
||||||
match key_resolution {
|
match key_resolution {
|
||||||
|
|
@ -60,7 +60,7 @@ impl MerkleTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// returns a boolean indicating whether the key exists in the tree
|
/// returns a boolean indicating whether the key exists in the tree
|
||||||
pub fn contains(&self, key: &Value) -> Result<bool> {
|
pub fn contains(&self, key: &RawValue) -> Result<bool> {
|
||||||
let path = keypath(self.max_depth, *key)?;
|
let path = keypath(self.max_depth, *key)?;
|
||||||
match self.root.down(0, self.max_depth, path, None) {
|
match self.root.down(0, self.max_depth, path, None) {
|
||||||
Ok(Some((k, _))) => {
|
Ok(Some((k, _))) => {
|
||||||
|
|
@ -77,7 +77,7 @@ impl MerkleTree {
|
||||||
/// returns a proof of existence, which proves that the given key exists in
|
/// returns a proof of existence, which proves that the given key exists in
|
||||||
/// the tree. It returns the `value` of the leaf at the given `key`, and the
|
/// the tree. It returns the `value` of the leaf at the given `key`, and the
|
||||||
/// `MerkleProof`.
|
/// `MerkleProof`.
|
||||||
pub fn prove(&self, key: &Value) -> Result<(Value, MerkleProof)> {
|
pub fn prove(&self, key: &RawValue) -> Result<(RawValue, MerkleProof)> {
|
||||||
let path = keypath(self.max_depth, *key)?;
|
let path = keypath(self.max_depth, *key)?;
|
||||||
|
|
||||||
let mut siblings: Vec<Hash> = Vec::new();
|
let mut siblings: Vec<Hash> = Vec::new();
|
||||||
|
|
@ -102,7 +102,7 @@ impl MerkleTree {
|
||||||
/// `key` does not exist in the tree. The return value specifies
|
/// `key` does not exist in the tree. The return value specifies
|
||||||
/// the key-value pair in the leaf reached as a result of
|
/// the key-value pair in the leaf reached as a result of
|
||||||
/// resolving `key` as well as a `MerkleProof`.
|
/// resolving `key` as well as a `MerkleProof`.
|
||||||
pub fn prove_nonexistence(&self, key: &Value) -> Result<MerkleProof> {
|
pub fn prove_nonexistence(&self, key: &RawValue) -> Result<MerkleProof> {
|
||||||
let path = keypath(self.max_depth, *key)?;
|
let path = keypath(self.max_depth, *key)?;
|
||||||
|
|
||||||
let mut siblings: Vec<Hash> = Vec::new();
|
let mut siblings: Vec<Hash> = Vec::new();
|
||||||
|
|
@ -134,8 +134,8 @@ impl MerkleTree {
|
||||||
max_depth: usize,
|
max_depth: usize,
|
||||||
root: Hash,
|
root: Hash,
|
||||||
proof: &MerkleProof,
|
proof: &MerkleProof,
|
||||||
key: &Value,
|
key: &RawValue,
|
||||||
value: &Value,
|
value: &RawValue,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let h = proof.compute_root_from_leaf(max_depth, key, Some(*value))?;
|
let h = proof.compute_root_from_leaf(max_depth, key, Some(*value))?;
|
||||||
|
|
||||||
|
|
@ -152,13 +152,13 @@ impl MerkleTree {
|
||||||
max_depth: usize,
|
max_depth: usize,
|
||||||
root: Hash,
|
root: Hash,
|
||||||
proof: &MerkleProof,
|
proof: &MerkleProof,
|
||||||
key: &Value,
|
key: &RawValue,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
match proof.other_leaf {
|
match proof.other_leaf {
|
||||||
Some((k, _v)) if &k == key => Err(anyhow!("Invalid non-existence proof.")),
|
Some((k, _v)) if &k == key => Err(anyhow!("Invalid non-existence proof.")),
|
||||||
_ => {
|
_ => {
|
||||||
let k = proof.other_leaf.map(|(k, _)| k).unwrap_or(*key);
|
let k = proof.other_leaf.map(|(k, _)| k).unwrap_or(*key);
|
||||||
let v: Option<Value> = proof.other_leaf.map(|(_, v)| v);
|
let v: Option<RawValue> = proof.other_leaf.map(|(_, v)| v);
|
||||||
let h = proof.compute_root_from_leaf(max_depth, &k, v)?;
|
let h = proof.compute_root_from_leaf(max_depth, &k, v)?;
|
||||||
|
|
||||||
if h != root {
|
if h != root {
|
||||||
|
|
@ -180,14 +180,14 @@ impl MerkleTree {
|
||||||
|
|
||||||
/// Hash function for key-value pairs. Different branch pair hashes to
|
/// Hash function for key-value pairs. Different branch pair hashes to
|
||||||
/// mitigate fake proofs.
|
/// mitigate fake proofs.
|
||||||
pub fn kv_hash(key: &Value, value: Option<Value>) -> Hash {
|
pub fn kv_hash(key: &RawValue, value: Option<RawValue>) -> Hash {
|
||||||
value
|
value
|
||||||
.map(|v| hash_fields(&[key.0.to_vec(), v.0.to_vec(), vec![F::ONE]].concat()))
|
.map(|v| hash_fields(&[key.0.to_vec(), v.0.to_vec(), vec![F::ONE]].concat()))
|
||||||
.unwrap_or(EMPTY_HASH)
|
.unwrap_or(EMPTY_HASH)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoIterator for &'a MerkleTree {
|
impl<'a> IntoIterator for &'a MerkleTree {
|
||||||
type Item = (&'a Value, &'a Value);
|
type Item = (&'a RawValue, &'a RawValue);
|
||||||
type IntoIter = Iter<'a>;
|
type IntoIter = Iter<'a>;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
|
@ -208,7 +208,7 @@ impl fmt::Display for MerkleTree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct MerkleProof {
|
pub struct MerkleProof {
|
||||||
// note: currently we don't use the `_existence` field, we would use if we merge the methods
|
// note: currently we don't use the `_existence` field, we would use if we merge the methods
|
||||||
// `verify` and `verify_nonexistence` into a single one
|
// `verify` and `verify_nonexistence` into a single one
|
||||||
|
|
@ -216,7 +216,7 @@ pub struct MerkleProof {
|
||||||
pub(crate) existence: bool,
|
pub(crate) existence: bool,
|
||||||
pub(crate) siblings: Vec<Hash>,
|
pub(crate) siblings: Vec<Hash>,
|
||||||
// other_leaf is used for non-existence proofs
|
// other_leaf is used for non-existence proofs
|
||||||
pub(crate) other_leaf: Option<(Value, Value)>,
|
pub(crate) other_leaf: Option<(RawValue, RawValue)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for MerkleProof {
|
impl fmt::Display for MerkleProof {
|
||||||
|
|
@ -238,8 +238,8 @@ impl MerkleProof {
|
||||||
fn compute_root_from_leaf(
|
fn compute_root_from_leaf(
|
||||||
&self,
|
&self,
|
||||||
max_depth: usize,
|
max_depth: usize,
|
||||||
key: &Value,
|
key: &RawValue,
|
||||||
value: Option<Value>,
|
value: Option<RawValue>,
|
||||||
) -> Result<Hash> {
|
) -> Result<Hash> {
|
||||||
if self.siblings.len() >= max_depth {
|
if self.siblings.len() >= max_depth {
|
||||||
return Err(anyhow!("max depth reached"));
|
return Err(anyhow!("max depth reached"));
|
||||||
|
|
@ -335,7 +335,7 @@ impl Node {
|
||||||
max_depth: usize,
|
max_depth: usize,
|
||||||
path: Vec<bool>,
|
path: Vec<bool>,
|
||||||
mut siblings: Option<&mut Vec<Hash>>,
|
mut siblings: Option<&mut Vec<Hash>>,
|
||||||
) -> Result<Option<(Value, Value)>> {
|
) -> Result<Option<(RawValue, RawValue)>> {
|
||||||
if lvl >= max_depth {
|
if lvl >= max_depth {
|
||||||
return Err(anyhow!("max depth reached"));
|
return Err(anyhow!("max depth reached"));
|
||||||
}
|
}
|
||||||
|
|
@ -494,11 +494,11 @@ impl Intermediate {
|
||||||
struct Leaf {
|
struct Leaf {
|
||||||
hash: Option<Hash>,
|
hash: Option<Hash>,
|
||||||
path: Vec<bool>,
|
path: Vec<bool>,
|
||||||
key: Value,
|
key: RawValue,
|
||||||
value: Value,
|
value: RawValue,
|
||||||
}
|
}
|
||||||
impl Leaf {
|
impl Leaf {
|
||||||
fn new(max_depth: usize, key: Value, value: Value) -> Result<Self> {
|
fn new(max_depth: usize, key: RawValue, value: RawValue) -> Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
hash: None,
|
hash: None,
|
||||||
path: keypath(max_depth, key)?,
|
path: keypath(max_depth, key)?,
|
||||||
|
|
@ -523,7 +523,7 @@ impl Leaf {
|
||||||
// max-depth? ie, what happens when two keys share the same path for more bits
|
// max-depth? ie, what happens when two keys share the same path for more bits
|
||||||
// than the max_depth?
|
// than the max_depth?
|
||||||
/// returns the path of the given key
|
/// returns the path of the given key
|
||||||
pub(crate) fn keypath(max_depth: usize, k: Value) -> Result<Vec<bool>> {
|
pub(crate) fn keypath(max_depth: usize, k: RawValue) -> Result<Vec<bool>> {
|
||||||
let bytes = k.to_bytes();
|
let bytes = k.to_bytes();
|
||||||
if max_depth > 8 * bytes.len() {
|
if max_depth > 8 * bytes.len() {
|
||||||
// note that our current keys are of Value type, which are 4 Goldilocks
|
// note that our current keys are of Value type, which are 4 Goldilocks
|
||||||
|
|
@ -545,7 +545,7 @@ pub struct Iter<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Iterator for Iter<'a> {
|
impl<'a> Iterator for Iter<'a> {
|
||||||
type Item = (&'a Value, &'a Value);
|
type Item = (&'a RawValue, &'a RawValue);
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
let node = self.state.pop();
|
let node = self.state.pop();
|
||||||
|
|
@ -586,10 +586,10 @@ pub mod tests {
|
||||||
if i == 1 {
|
if i == 1 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
kvs.insert(Value::from(i), Value::from(1000 + i));
|
kvs.insert(RawValue::from(i), RawValue::from(1000 + i));
|
||||||
}
|
}
|
||||||
let key = Value::from(13);
|
let key = RawValue::from(13);
|
||||||
let value = Value::from(1013);
|
let value = RawValue::from(1013);
|
||||||
kvs.insert(key, value);
|
kvs.insert(key, value);
|
||||||
|
|
||||||
let tree = MerkleTree::new(32, &kvs)?;
|
let tree = MerkleTree::new(32, &kvs)?;
|
||||||
|
|
@ -598,25 +598,25 @@ pub mod tests {
|
||||||
println!("{}", tree);
|
println!("{}", tree);
|
||||||
|
|
||||||
// Inclusion checks
|
// Inclusion checks
|
||||||
let (v, proof) = tree.prove(&Value::from(13))?;
|
let (v, proof) = tree.prove(&RawValue::from(13))?;
|
||||||
assert_eq!(v, Value::from(1013));
|
assert_eq!(v, RawValue::from(1013));
|
||||||
println!("{}", proof);
|
println!("{}", proof);
|
||||||
|
|
||||||
MerkleTree::verify(32, tree.root(), &proof, &key, &value)?;
|
MerkleTree::verify(32, tree.root(), &proof, &key, &value)?;
|
||||||
|
|
||||||
// Exclusion checks
|
// Exclusion checks
|
||||||
let key = Value::from(12);
|
let key = RawValue::from(12);
|
||||||
let proof = tree.prove_nonexistence(&key)?;
|
let proof = tree.prove_nonexistence(&key)?;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
proof.other_leaf.unwrap(),
|
proof.other_leaf.unwrap(),
|
||||||
(Value::from(4), Value::from(1004))
|
(RawValue::from(4), RawValue::from(1004))
|
||||||
);
|
);
|
||||||
println!("{}", proof);
|
println!("{}", proof);
|
||||||
|
|
||||||
MerkleTree::verify_nonexistence(32, tree.root(), &proof, &key)?;
|
MerkleTree::verify_nonexistence(32, tree.root(), &proof, &key)?;
|
||||||
|
|
||||||
let key = Value::from(1);
|
let key = RawValue::from(1);
|
||||||
let proof = tree.prove_nonexistence(&Value::from(1))?;
|
let proof = tree.prove_nonexistence(&RawValue::from(1))?;
|
||||||
assert_eq!(proof.other_leaf, None);
|
assert_eq!(proof.other_leaf, None);
|
||||||
println!("{}", proof);
|
println!("{}", proof);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,10 +24,13 @@ use plonky2::{
|
||||||
plonk::circuit_builder::CircuitBuilder,
|
plonk::circuit_builder::CircuitBuilder,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::backends::plonky2::{
|
use crate::{
|
||||||
basetypes::{Hash, Value, D, EMPTY_HASH, EMPTY_VALUE, F, HASH_SIZE},
|
backends::plonky2::{
|
||||||
|
basetypes::D,
|
||||||
circuits::common::{CircuitBuilderPod, ValueTarget},
|
circuits::common::{CircuitBuilderPod, ValueTarget},
|
||||||
primitives::merkletree::MerkleProof,
|
primitives::merkletree::MerkleProof,
|
||||||
|
},
|
||||||
|
middleware::{Hash, RawValue, EMPTY_HASH, EMPTY_VALUE, F, HASH_SIZE},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// `MerkleProofGadget` allows to verify both proofs of existence and proofs
|
/// `MerkleProofGadget` allows to verify both proofs of existence and proofs
|
||||||
|
|
@ -163,8 +166,8 @@ impl MerkleClaimAndProofTarget {
|
||||||
existence: bool,
|
existence: bool,
|
||||||
root: Hash,
|
root: Hash,
|
||||||
proof: MerkleProof,
|
proof: MerkleProof,
|
||||||
key: Value,
|
key: RawValue,
|
||||||
value: Value,
|
value: RawValue,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
pw.set_bool_target(self.enabled, enabled)?;
|
pw.set_bool_target(self.enabled, enabled)?;
|
||||||
pw.set_hash_target(self.root, HashOut::from_vec(root.0.to_vec()))?;
|
pw.set_hash_target(self.root, HashOut::from_vec(root.0.to_vec()))?;
|
||||||
|
|
@ -268,8 +271,8 @@ impl MerkleProofExistenceTarget {
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
root: Hash,
|
root: Hash,
|
||||||
proof: MerkleProof,
|
proof: MerkleProof,
|
||||||
key: Value,
|
key: RawValue,
|
||||||
value: Value,
|
value: RawValue,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
assert!(proof.existence); // sanity check
|
assert!(proof.existence); // sanity check
|
||||||
|
|
||||||
|
|
@ -405,9 +408,9 @@ pub mod tests {
|
||||||
use plonky2::plonk::{circuit_builder::CircuitBuilder, circuit_data::CircuitConfig};
|
use plonky2::plonk::{circuit_builder::CircuitBuilder, circuit_data::CircuitConfig};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::backends::plonky2::{
|
use crate::{
|
||||||
basetypes::{hash_value, C},
|
backends::plonky2::{basetypes::C, primitives::merkletree::*},
|
||||||
primitives::merkletree::*,
|
middleware::{hash_value, RawValue},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -424,7 +427,7 @@ pub mod tests {
|
||||||
let mut builder = CircuitBuilder::<F, D>::new(config);
|
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||||
let mut pw = PartialWitness::<F>::new();
|
let mut pw = PartialWitness::<F>::new();
|
||||||
|
|
||||||
let key = Value::from(hash_value(&Value::from(i)));
|
let key = RawValue::from(hash_value(&RawValue::from(i)));
|
||||||
let expected_path = keypath(max_depth, key)?;
|
let expected_path = keypath(max_depth, key)?;
|
||||||
|
|
||||||
// small circuit logic to check
|
// small circuit logic to check
|
||||||
|
|
@ -455,8 +458,8 @@ pub mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_kv_hash() -> Result<()> {
|
fn test_kv_hash() -> Result<()> {
|
||||||
for i in 0..10 {
|
for i in 0..10 {
|
||||||
let key = Value::from(hash_value(&Value::from(i)));
|
let key = RawValue::from(hash_value(&RawValue::from(i)));
|
||||||
let value = Value::from(1000 + i);
|
let value = RawValue::from(1000 + i);
|
||||||
let h = kv_hash(&key, Some(value));
|
let h = kv_hash(&key, Some(value));
|
||||||
|
|
||||||
// circuit
|
// circuit
|
||||||
|
|
@ -502,20 +505,23 @@ pub mod tests {
|
||||||
|
|
||||||
// test logic to be reused both by the existence & nonexistence tests
|
// test logic to be reused both by the existence & nonexistence tests
|
||||||
fn test_merkleproof_verify_opt(max_depth: usize, existence: bool) -> Result<()> {
|
fn test_merkleproof_verify_opt(max_depth: usize, existence: bool) -> Result<()> {
|
||||||
let mut kvs: HashMap<Value, Value> = HashMap::new();
|
let mut kvs: HashMap<RawValue, RawValue> = HashMap::new();
|
||||||
for i in 0..10 {
|
for i in 0..10 {
|
||||||
kvs.insert(Value::from(hash_value(&Value::from(i))), Value::from(i));
|
kvs.insert(
|
||||||
|
RawValue::from(hash_value(&RawValue::from(i))),
|
||||||
|
RawValue::from(i),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let tree = MerkleTree::new(max_depth, &kvs)?;
|
let tree = MerkleTree::new(max_depth, &kvs)?;
|
||||||
|
|
||||||
let (key, value, proof) = if existence {
|
let (key, value, proof) = if existence {
|
||||||
let key = Value::from(hash_value(&Value::from(5)));
|
let key = RawValue::from(hash_value(&RawValue::from(5)));
|
||||||
let (value, proof) = tree.prove(&key)?;
|
let (value, proof) = tree.prove(&key)?;
|
||||||
assert_eq!(value, Value::from(5));
|
assert_eq!(value, RawValue::from(5));
|
||||||
(key, value, proof)
|
(key, value, proof)
|
||||||
} else {
|
} else {
|
||||||
let key = Value::from(hash_value(&Value::from(200)));
|
let key = RawValue::from(hash_value(&RawValue::from(200)));
|
||||||
(key, EMPTY_VALUE, tree.prove_nonexistence(&key)?)
|
(key, EMPTY_VALUE, tree.prove_nonexistence(&key)?)
|
||||||
};
|
};
|
||||||
assert_eq!(proof.existence, existence);
|
assert_eq!(proof.existence, existence);
|
||||||
|
|
@ -559,16 +565,19 @@ pub mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_merkleproof_only_existence_verify_opt(max_depth: usize) -> Result<()> {
|
fn test_merkleproof_only_existence_verify_opt(max_depth: usize) -> Result<()> {
|
||||||
let mut kvs: HashMap<Value, Value> = HashMap::new();
|
let mut kvs: HashMap<RawValue, RawValue> = HashMap::new();
|
||||||
for i in 0..10 {
|
for i in 0..10 {
|
||||||
kvs.insert(Value::from(hash_value(&Value::from(i))), Value::from(i));
|
kvs.insert(
|
||||||
|
RawValue::from(hash_value(&RawValue::from(i))),
|
||||||
|
RawValue::from(i),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let tree = MerkleTree::new(max_depth, &kvs)?;
|
let tree = MerkleTree::new(max_depth, &kvs)?;
|
||||||
|
|
||||||
let key = Value::from(hash_value(&Value::from(5)));
|
let key = RawValue::from(hash_value(&RawValue::from(5)));
|
||||||
let (value, proof) = tree.prove(&key)?;
|
let (value, proof) = tree.prove(&key)?;
|
||||||
assert_eq!(value, Value::from(5));
|
assert_eq!(value, RawValue::from(5));
|
||||||
assert_eq!(proof.existence, true);
|
assert_eq!(proof.existence, true);
|
||||||
|
|
||||||
MerkleTree::verify(max_depth, tree.root(), &proof, &key, &value)?;
|
MerkleTree::verify(max_depth, tree.root(), &proof, &key, &value)?;
|
||||||
|
|
@ -604,24 +613,28 @@ pub mod tests {
|
||||||
// 5 13
|
// 5 13
|
||||||
|
|
||||||
let mut kvs = HashMap::new();
|
let mut kvs = HashMap::new();
|
||||||
kvs.insert(Value::from(0), Value::from(1000));
|
kvs.insert(RawValue::from(0), RawValue::from(1000));
|
||||||
kvs.insert(Value::from(2), Value::from(1002));
|
kvs.insert(RawValue::from(2), RawValue::from(1002));
|
||||||
kvs.insert(Value::from(5), Value::from(1005));
|
kvs.insert(RawValue::from(5), RawValue::from(1005));
|
||||||
kvs.insert(Value::from(13), Value::from(1013));
|
kvs.insert(RawValue::from(13), RawValue::from(1013));
|
||||||
|
|
||||||
let max_depth = 5;
|
let max_depth = 5;
|
||||||
let tree = MerkleTree::new(max_depth, &kvs)?;
|
let tree = MerkleTree::new(max_depth, &kvs)?;
|
||||||
// existence
|
// existence
|
||||||
test_merkletree_edgecase_opt(max_depth, &tree, Value::from(5))?;
|
test_merkletree_edgecase_opt(max_depth, &tree, RawValue::from(5))?;
|
||||||
// non-existence case i) expected leaf does not exist
|
// non-existence case i) expected leaf does not exist
|
||||||
test_merkletree_edgecase_opt(max_depth, &tree, Value::from(1))?;
|
test_merkletree_edgecase_opt(max_depth, &tree, RawValue::from(1))?;
|
||||||
// non-existence case ii) expected leaf does exist but it has a different 'key'
|
// non-existence case ii) expected leaf does exist but it has a different 'key'
|
||||||
test_merkletree_edgecase_opt(max_depth, &tree, Value::from(21))?;
|
test_merkletree_edgecase_opt(max_depth, &tree, RawValue::from(21))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_merkletree_edgecase_opt(max_depth: usize, tree: &MerkleTree, key: Value) -> Result<()> {
|
fn test_merkletree_edgecase_opt(
|
||||||
|
max_depth: usize,
|
||||||
|
tree: &MerkleTree,
|
||||||
|
key: RawValue,
|
||||||
|
) -> Result<()> {
|
||||||
let contains = tree.contains(&key)?;
|
let contains = tree.contains(&key)?;
|
||||||
// generate merkleproof
|
// generate merkleproof
|
||||||
let (value, proof) = if contains {
|
let (value, proof) = if contains {
|
||||||
|
|
@ -666,19 +679,19 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_wrong_witness() -> Result<()> {
|
fn test_wrong_witness() -> Result<()> {
|
||||||
let mut kvs: HashMap<Value, Value> = HashMap::new();
|
let mut kvs: HashMap<RawValue, RawValue> = HashMap::new();
|
||||||
for i in 0..10 {
|
for i in 0..10 {
|
||||||
kvs.insert(Value::from(i), Value::from(i));
|
kvs.insert(RawValue::from(i), RawValue::from(i));
|
||||||
}
|
}
|
||||||
let max_depth = 16;
|
let max_depth = 16;
|
||||||
let tree = MerkleTree::new(max_depth, &kvs)?;
|
let tree = MerkleTree::new(max_depth, &kvs)?;
|
||||||
|
|
||||||
let key = Value::from(3);
|
let key = RawValue::from(3);
|
||||||
let (value, proof) = tree.prove(&key)?;
|
let (value, proof) = tree.prove(&key)?;
|
||||||
|
|
||||||
// build another tree with an extra key-value, so that it has a
|
// build another tree with an extra key-value, so that it has a
|
||||||
// different root
|
// different root
|
||||||
kvs.insert(Value::from(100), Value::from(100));
|
kvs.insert(RawValue::from(100), RawValue::from(100));
|
||||||
let tree2 = MerkleTree::new(max_depth, &kvs)?;
|
let tree2 = MerkleTree::new(max_depth, &kvs)?;
|
||||||
|
|
||||||
MerkleTree::verify(max_depth, tree.root(), &proof, &key, &value)?;
|
MerkleTree::verify(max_depth, tree.root(), &proof, &key, &value)?;
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,10 @@ use plonky2::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use super::signature_circuit::*;
|
pub use super::signature_circuit::*;
|
||||||
use crate::backends::plonky2::basetypes::{Proof, Value, C, D, F, VALUE_SIZE};
|
use crate::{
|
||||||
|
backends::plonky2::basetypes::{Proof, C, D},
|
||||||
|
middleware::{RawValue, F, VALUE_SIZE},
|
||||||
|
};
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
/// Signature prover parameters
|
/// Signature prover parameters
|
||||||
|
|
@ -45,10 +48,10 @@ pub struct ProverParams {
|
||||||
pub struct VerifierParams(pub(crate) VerifierCircuitData<F, C, D>);
|
pub struct VerifierParams(pub(crate) VerifierCircuitData<F, C, D>);
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SecretKey(pub(crate) Value);
|
pub struct SecretKey(pub(crate) RawValue);
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PublicKey(pub(crate) Value);
|
pub struct PublicKey(pub(crate) RawValue);
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Signature(pub(crate) Proof);
|
pub struct Signature(pub(crate) Proof);
|
||||||
|
|
@ -57,16 +60,16 @@ pub struct Signature(pub(crate) Proof);
|
||||||
impl SecretKey {
|
impl SecretKey {
|
||||||
pub fn new_rand() -> Self {
|
pub fn new_rand() -> Self {
|
||||||
// note: the `F::rand()` internally uses `rand::rngs::OsRng`
|
// note: the `F::rand()` internally uses `rand::rngs::OsRng`
|
||||||
Self(Value(std::array::from_fn(|_| F::rand())))
|
Self(RawValue(std::array::from_fn(|_| F::rand())))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn public_key(&self) -> PublicKey {
|
pub fn public_key(&self) -> PublicKey {
|
||||||
PublicKey(Value(PoseidonHash::hash_no_pad(&self.0 .0).elements))
|
PublicKey(RawValue(PoseidonHash::hash_no_pad(&self.0 .0).elements))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sign(&self, msg: Value) -> Result<Signature> {
|
pub fn sign(&self, msg: RawValue) -> Result<Signature> {
|
||||||
let pk = self.public_key();
|
let pk = self.public_key();
|
||||||
let s = Value(PoseidonHash::hash_no_pad(&[pk.0 .0, msg.0].concat()).elements);
|
let s = RawValue(PoseidonHash::hash_no_pad(&[pk.0 .0, msg.0].concat()).elements);
|
||||||
|
|
||||||
let mut pw = PartialWitness::<F>::new();
|
let mut pw = PartialWitness::<F>::new();
|
||||||
PP.circuit.set_targets(&mut pw, self.clone(), pk, msg, s)?;
|
PP.circuit.set_targets(&mut pw, self.clone(), pk, msg, s)?;
|
||||||
|
|
@ -108,9 +111,9 @@ impl Signature {
|
||||||
Ok((builder, circuit))
|
Ok((builder, circuit))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify(&self, pk: &PublicKey, msg: Value) -> Result<()> {
|
pub fn verify(&self, pk: &PublicKey, msg: RawValue) -> Result<()> {
|
||||||
// prepare public inputs as [pk, msg, s]
|
// prepare public inputs as [pk, msg, s]
|
||||||
let s = Value(PoseidonHash::hash_no_pad(&[pk.0 .0, msg.0].concat()).elements);
|
let s = RawValue(PoseidonHash::hash_no_pad(&[pk.0 .0, msg.0].concat()).elements);
|
||||||
let public_inputs: Vec<F> = [pk.0 .0, msg.0, s.0].concat();
|
let public_inputs: Vec<F> = [pk.0 .0, msg.0, s.0].concat();
|
||||||
|
|
||||||
// verify plonky2 proof
|
// verify plonky2 proof
|
||||||
|
|
@ -122,16 +125,16 @@ impl Signature {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dummy_public_inputs() -> Result<Vec<F>> {
|
fn dummy_public_inputs() -> Result<Vec<F>> {
|
||||||
let sk = SecretKey(Value::from(0));
|
let sk = SecretKey(RawValue::from(0));
|
||||||
let pk = sk.public_key();
|
let pk = sk.public_key();
|
||||||
let msg = Value::from(0);
|
let msg = RawValue::from(0);
|
||||||
let s = Value(PoseidonHash::hash_no_pad(&[pk.0 .0, msg.0].concat()).elements);
|
let s = RawValue(PoseidonHash::hash_no_pad(&[pk.0 .0, msg.0].concat()).elements);
|
||||||
Ok([pk.0 .0, msg.0, s.0].concat())
|
Ok([pk.0 .0, msg.0, s.0].concat())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dummy_signature() -> Result<Signature> {
|
fn dummy_signature() -> Result<Signature> {
|
||||||
let sk = SecretKey(Value::from(0));
|
let sk = SecretKey(RawValue::from(0));
|
||||||
let msg = Value::from(0);
|
let msg = RawValue::from(0);
|
||||||
sk.sign(msg)
|
sk.sign(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,8 +189,8 @@ impl SignatureInternalCircuit {
|
||||||
pw: &mut PartialWitness<F>,
|
pw: &mut PartialWitness<F>,
|
||||||
sk: SecretKey,
|
sk: SecretKey,
|
||||||
pk: PublicKey,
|
pk: PublicKey,
|
||||||
msg: Value,
|
msg: RawValue,
|
||||||
s: Value,
|
s: RawValue,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
pw.set_target_arr(&self.sk_targ, sk.0 .0.as_ref())?;
|
pw.set_target_arr(&self.sk_targ, sk.0 .0.as_ref())?;
|
||||||
pw.set_hash_target(self.pk_targ, HashOut::<F>::from_vec(pk.0 .0.to_vec()))?;
|
pw.set_hash_target(self.pk_targ, HashOut::<F>::from_vec(pk.0 .0.to_vec()))?;
|
||||||
|
|
@ -201,23 +204,23 @@ impl SignatureInternalCircuit {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::backends::plonky2::basetypes::Hash;
|
use crate::middleware::hash_str;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_signature() -> Result<()> {
|
fn test_signature() -> Result<()> {
|
||||||
let sk = SecretKey::new_rand();
|
let sk = SecretKey::new_rand();
|
||||||
let pk = sk.public_key();
|
let pk = sk.public_key();
|
||||||
|
|
||||||
let msg = Value::from(42);
|
let msg = RawValue::from(42);
|
||||||
let sig = sk.sign(msg)?;
|
let sig = sk.sign(msg)?;
|
||||||
sig.verify(&pk, msg)?;
|
sig.verify(&pk, msg)?;
|
||||||
|
|
||||||
// expect the signature verification to fail when using a different msg
|
// expect the signature verification to fail when using a different msg
|
||||||
let v = sig.verify(&pk, Value::from(24));
|
let v = sig.verify(&pk, RawValue::from(24));
|
||||||
assert!(v.is_err(), "should fail to verify");
|
assert!(v.is_err(), "should fail to verify");
|
||||||
|
|
||||||
// perform a 2nd signature over another msg and verify it
|
// perform a 2nd signature over another msg and verify it
|
||||||
let msg_2 = Value::from(Hash::from("message"));
|
let msg_2 = RawValue::from(hash_str("message"));
|
||||||
let sig2 = sk.sign(msg_2)?;
|
let sig2 = sk.sign(msg_2)?;
|
||||||
sig2.verify(&pk, msg_2)?;
|
sig2.verify(&pk, msg_2)?;
|
||||||
|
|
||||||
|
|
@ -226,9 +229,9 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_dummy_signature() -> Result<()> {
|
fn test_dummy_signature() -> Result<()> {
|
||||||
let sk = SecretKey(Value::from(0));
|
let sk = SecretKey(RawValue::from(0));
|
||||||
let pk = sk.public_key();
|
let pk = sk.public_key();
|
||||||
let msg = Value::from(0);
|
let msg = RawValue::from(0);
|
||||||
DUMMY_SIGNATURE.clone().verify(&pk, msg)?;
|
DUMMY_SIGNATURE.clone().verify(&pk, msg)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -22,12 +22,15 @@ use plonky2::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::backends::plonky2::{
|
use crate::{
|
||||||
basetypes::{Hash, Proof, Value, C, D, EMPTY_HASH, EMPTY_VALUE, F, VALUE_SIZE},
|
backends::plonky2::{
|
||||||
|
basetypes::{Proof, C, D},
|
||||||
circuits::common::{CircuitBuilderPod, ValueTarget},
|
circuits::common::{CircuitBuilderPod, ValueTarget},
|
||||||
primitives::signature::{
|
primitives::signature::{
|
||||||
PublicKey, SecretKey, Signature, DUMMY_PUBLIC_INPUTS, DUMMY_SIGNATURE,
|
PublicKey, SecretKey, Signature, DUMMY_PUBLIC_INPUTS, DUMMY_SIGNATURE,
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
middleware::{Hash, RawValue, EMPTY_HASH, EMPTY_VALUE, F, VALUE_SIZE},
|
||||||
};
|
};
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
|
@ -81,12 +84,12 @@ impl SignatureVerifyGadget {
|
||||||
let dummy_pi = DUMMY_PUBLIC_INPUTS.clone();
|
let dummy_pi = DUMMY_PUBLIC_INPUTS.clone();
|
||||||
|
|
||||||
let pk_targ_dummy =
|
let pk_targ_dummy =
|
||||||
builder.constant_value(Value(dummy_pi[..VALUE_SIZE].try_into().unwrap()));
|
builder.constant_value(RawValue(dummy_pi[..VALUE_SIZE].try_into().unwrap()));
|
||||||
let msg_targ_dummy = builder.constant_value(Value(
|
let msg_targ_dummy = builder.constant_value(RawValue(
|
||||||
dummy_pi[VALUE_SIZE..VALUE_SIZE * 2].try_into().unwrap(),
|
dummy_pi[VALUE_SIZE..VALUE_SIZE * 2].try_into().unwrap(),
|
||||||
));
|
));
|
||||||
let s_targ_dummy =
|
let s_targ_dummy =
|
||||||
builder.constant_value(Value(dummy_pi[VALUE_SIZE * 2..].try_into().unwrap()));
|
builder.constant_value(RawValue(dummy_pi[VALUE_SIZE * 2..].try_into().unwrap()));
|
||||||
|
|
||||||
// connect the {pk, msg, s} with the proof_targ.public_inputs conditionally
|
// connect the {pk, msg, s} with the proof_targ.public_inputs conditionally
|
||||||
let pk_targ_connect = builder.select_value(enabled, pk_targ, pk_targ_dummy);
|
let pk_targ_connect = builder.select_value(enabled, pk_targ, pk_targ_dummy);
|
||||||
|
|
@ -129,7 +132,7 @@ impl SignatureVerifyTarget {
|
||||||
pw: &mut PartialWitness<F>,
|
pw: &mut PartialWitness<F>,
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
pk: PublicKey,
|
pk: PublicKey,
|
||||||
msg: Value,
|
msg: RawValue,
|
||||||
signature: Signature,
|
signature: Signature,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
pw.set_bool_target(self.enabled, enabled)?;
|
pw.set_bool_target(self.enabled, enabled)?;
|
||||||
|
|
@ -137,7 +140,7 @@ impl SignatureVerifyTarget {
|
||||||
pw.set_target_arr(&self.msg.elements, &msg.0)?;
|
pw.set_target_arr(&self.msg.elements, &msg.0)?;
|
||||||
|
|
||||||
// note that this hash is checked again in-circuit at the `SignatureInternalCircuit`
|
// note that this hash is checked again in-circuit at the `SignatureInternalCircuit`
|
||||||
let s = Value(PoseidonHash::hash_no_pad(&[pk.0 .0, msg.0].concat()).elements);
|
let s = RawValue(PoseidonHash::hash_no_pad(&[pk.0 .0, msg.0].concat()).elements);
|
||||||
let public_inputs: Vec<F> = [pk.0 .0, msg.0, s.0].concat();
|
let public_inputs: Vec<F> = [pk.0 .0, msg.0, s.0].concat();
|
||||||
|
|
||||||
if enabled {
|
if enabled {
|
||||||
|
|
@ -170,14 +173,14 @@ impl SignatureVerifyTarget {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::backends::plonky2::{basetypes::Hash, primitives::signature::SecretKey};
|
use crate::{backends::plonky2::primitives::signature::SecretKey, middleware::Hash};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_signature_gadget() -> Result<()> {
|
fn test_signature_gadget() -> Result<()> {
|
||||||
// generate a valid signature
|
// generate a valid signature
|
||||||
let sk = SecretKey::new_rand();
|
let sk = SecretKey::new_rand();
|
||||||
let pk = sk.public_key();
|
let pk = sk.public_key();
|
||||||
let msg = Value::from(42);
|
let msg = RawValue::from(42);
|
||||||
let sig = sk.sign(msg)?;
|
let sig = sk.sign(msg)?;
|
||||||
sig.verify(&pk, msg)?;
|
sig.verify(&pk, msg)?;
|
||||||
|
|
||||||
|
|
@ -208,15 +211,15 @@ pub mod tests {
|
||||||
// generate a valid signature
|
// generate a valid signature
|
||||||
let sk = SecretKey::new_rand();
|
let sk = SecretKey::new_rand();
|
||||||
let pk = sk.public_key();
|
let pk = sk.public_key();
|
||||||
let msg = Value::from(42);
|
let msg = RawValue::from(42);
|
||||||
let sig = sk.sign(msg)?;
|
let sig = sk.sign(msg)?;
|
||||||
// verification should pass
|
// verification should pass
|
||||||
sig.verify(&pk, msg)?;
|
sig.verify(&pk, msg)?;
|
||||||
|
|
||||||
// replace the message, so that verifications should fail
|
// replace the message, so that verifications should fail
|
||||||
let msg = Value::from(24);
|
let msg = RawValue::from(24);
|
||||||
// expect signature native verification to fail
|
// expect signature native verification to fail
|
||||||
let v = sig.verify(&pk, Value::from(24));
|
let v = sig.verify(&pk, RawValue::from(24));
|
||||||
assert!(v.is_err(), "should fail to verify");
|
assert!(v.is_err(), "should fail to verify");
|
||||||
|
|
||||||
// circuit
|
// circuit
|
||||||
|
|
|
||||||
|
|
@ -10,22 +10,22 @@ use crate::{
|
||||||
},
|
},
|
||||||
constants::MAX_DEPTH,
|
constants::MAX_DEPTH,
|
||||||
middleware::{
|
middleware::{
|
||||||
containers::Dictionary, hash_str, AnchoredKey, Hash, Params, Pod, PodId, PodSigner,
|
containers::Dictionary, AnchoredKey, Hash, Key, Params, Pod, PodId, PodSigner, PodType,
|
||||||
PodType, Statement, Value, KEY_SIGNER, KEY_TYPE,
|
RawValue, Statement, Value, KEY_SIGNER, KEY_TYPE,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Signer(pub SecretKey);
|
pub struct Signer(pub SecretKey);
|
||||||
|
|
||||||
impl PodSigner for Signer {
|
impl PodSigner for Signer {
|
||||||
fn sign(&mut self, _params: &Params, kvs: &HashMap<Hash, Value>) -> Result<Box<dyn Pod>> {
|
fn sign(&mut self, _params: &Params, kvs: &HashMap<Key, Value>) -> Result<Box<dyn Pod>> {
|
||||||
let mut kvs = kvs.clone();
|
let mut kvs = kvs.clone();
|
||||||
let pubkey = self.0.public_key();
|
let pubkey = self.0.public_key();
|
||||||
kvs.insert(hash_str(KEY_SIGNER), pubkey.0);
|
kvs.insert(Key::from(KEY_SIGNER), Value::from(pubkey.0));
|
||||||
kvs.insert(hash_str(KEY_TYPE), Value::from(PodType::Signed));
|
kvs.insert(Key::from(KEY_TYPE), Value::from(PodType::Signed));
|
||||||
|
|
||||||
let dict = Dictionary::new(&kvs)?;
|
let dict = Dictionary::new(kvs)?;
|
||||||
let id = Value::from(dict.commitment()); // PodId as Value
|
let id = RawValue::from(dict.commitment()); // PodId as Value
|
||||||
|
|
||||||
let signature: Signature = self.0.sign(id)?;
|
let signature: Signature = self.0.sign(id)?;
|
||||||
Ok(Box::new(SignedPod {
|
Ok(Box::new(SignedPod {
|
||||||
|
|
@ -46,8 +46,8 @@ pub struct SignedPod {
|
||||||
impl Pod for SignedPod {
|
impl Pod for SignedPod {
|
||||||
fn verify(&self) -> Result<()> {
|
fn verify(&self) -> Result<()> {
|
||||||
// 1. Verify type
|
// 1. Verify type
|
||||||
let value_at_type = self.dict.get(&hash_str(KEY_TYPE).into())?;
|
let value_at_type = self.dict.get(&Key::from(KEY_TYPE))?;
|
||||||
if Value::from(PodType::Signed) != value_at_type {
|
if Value::from(PodType::Signed) != *value_at_type {
|
||||||
return Err(anyhow!(
|
return Err(anyhow!(
|
||||||
"type does not match, expected Signed ({}), found {}",
|
"type does not match, expected Signed ({}), found {}",
|
||||||
PodType::Signed,
|
PodType::Signed,
|
||||||
|
|
@ -60,9 +60,10 @@ impl Pod for SignedPod {
|
||||||
MAX_DEPTH,
|
MAX_DEPTH,
|
||||||
&self
|
&self
|
||||||
.dict
|
.dict
|
||||||
|
.kvs()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(&k, &v)| (k, v))
|
.map(|(k, v)| (k.raw(), v.raw()))
|
||||||
.collect::<HashMap<Value, Value>>(),
|
.collect::<HashMap<RawValue, RawValue>>(),
|
||||||
)?;
|
)?;
|
||||||
let id = PodId(mt.root());
|
let id = PodId(mt.root());
|
||||||
if id != self.id {
|
if id != self.id {
|
||||||
|
|
@ -74,9 +75,9 @@ impl Pod for SignedPod {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Verify signature
|
// 3. Verify signature
|
||||||
let pk_value = self.dict.get(&hash_str(KEY_SIGNER).into())?;
|
let pk_value = self.dict.get(&Key::from(KEY_SIGNER))?;
|
||||||
let pk = PublicKey(pk_value);
|
let pk = PublicKey(pk_value.raw());
|
||||||
self.signature.verify(&pk, Value::from(id.0))?;
|
self.signature.verify(&pk, RawValue::from(id.0))?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -88,15 +89,15 @@ impl Pod for SignedPod {
|
||||||
fn pub_statements(&self) -> Vec<Statement> {
|
fn pub_statements(&self) -> Vec<Statement> {
|
||||||
let id = self.id();
|
let id = self.id();
|
||||||
// By convention we put the KEY_TYPE first and KEY_SIGNER second
|
// By convention we put the KEY_TYPE first and KEY_SIGNER second
|
||||||
let mut kvs: HashMap<_, _> = self.dict.iter().collect();
|
let mut kvs: HashMap<Key, Value> = self.dict.kvs().clone();
|
||||||
let key_type = Value::from(hash_str(KEY_TYPE));
|
let key_type = Key::from(KEY_TYPE);
|
||||||
let value_type = kvs.remove(&key_type).expect("KEY_TYPE");
|
let value_type = kvs.remove(&key_type).expect("KEY_TYPE");
|
||||||
let key_signer = Value::from(hash_str(KEY_SIGNER));
|
let key_signer = Key::from(KEY_SIGNER);
|
||||||
let value_signer = kvs.remove(&key_signer).expect("KEY_SIGNER");
|
let value_signer = kvs.remove(&key_signer).expect("KEY_SIGNER");
|
||||||
[(&key_type, value_type), (&key_signer, value_signer)]
|
[(key_type, value_type), (key_signer, value_signer)]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(kvs.into_iter().sorted_by_key(|kv| kv.0))
|
.chain(kvs.into_iter().sorted_by_key(|kv| kv.0.hash()))
|
||||||
.map(|(k, v)| Statement::ValueOf(AnchoredKey(id, Hash(k.0)), *v))
|
.map(|(k, v)| Statement::ValueOf(AnchoredKey::from((id, k)), v))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,9 +124,8 @@ pub mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
constants::MAX_DEPTH,
|
|
||||||
frontend,
|
frontend,
|
||||||
middleware::{self, EMPTY_HASH, F},
|
middleware::{self, EMPTY_VALUE, F},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -147,7 +147,7 @@ pub mod tests {
|
||||||
println!("kvs: {:?}", pod.kvs());
|
println!("kvs: {:?}", pod.kvs());
|
||||||
|
|
||||||
let mut bad_pod = pod.clone();
|
let mut bad_pod = pod.clone();
|
||||||
bad_pod.signature = signer.0.sign(Value::from(42_i64))?;
|
bad_pod.signature = signer.0.sign(RawValue::from(42_i64))?;
|
||||||
assert!(bad_pod.verify().is_err());
|
assert!(bad_pod.verify().is_err());
|
||||||
|
|
||||||
let mut bad_pod = pod.clone();
|
let mut bad_pod = pod.clone();
|
||||||
|
|
@ -155,27 +155,27 @@ pub mod tests {
|
||||||
assert!(bad_pod.verify().is_err());
|
assert!(bad_pod.verify().is_err());
|
||||||
|
|
||||||
let mut bad_pod = pod.clone();
|
let mut bad_pod = pod.clone();
|
||||||
let bad_kv = (hash_str(KEY_SIGNER).into(), Value(PodId(EMPTY_HASH).0 .0));
|
let bad_kv = (Key::from(KEY_SIGNER), Value::from(EMPTY_VALUE));
|
||||||
let bad_kvs_mt = &bad_pod
|
let bad_kvs = bad_pod
|
||||||
|
.dict
|
||||||
.kvs()
|
.kvs()
|
||||||
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(AnchoredKey(_, k), v)| (Value(k.0), v))
|
|
||||||
.chain(iter::once(bad_kv))
|
.chain(iter::once(bad_kv))
|
||||||
.collect::<HashMap<Value, Value>>();
|
.collect::<HashMap<Key, Value>>();
|
||||||
let bad_mt = MerkleTree::new(MAX_DEPTH, bad_kvs_mt)?;
|
bad_pod.dict = Dictionary::new(bad_kvs).unwrap();
|
||||||
bad_pod.dict.mt = bad_mt;
|
|
||||||
assert!(bad_pod.verify().is_err());
|
assert!(bad_pod.verify().is_err());
|
||||||
|
|
||||||
let mut bad_pod = pod.clone();
|
let mut bad_pod = pod.clone();
|
||||||
let bad_kv = (hash_str(KEY_TYPE).into(), Value::from(0));
|
let bad_kv = (Key::from(KEY_TYPE), Value::from(0));
|
||||||
let bad_kvs_mt = &bad_pod
|
let bad_kvs = bad_pod
|
||||||
|
.dict
|
||||||
.kvs()
|
.kvs()
|
||||||
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(AnchoredKey(_, k), v)| (Value(k.0), v))
|
|
||||||
.chain(iter::once(bad_kv))
|
.chain(iter::once(bad_kv))
|
||||||
.collect::<HashMap<Value, Value>>();
|
.collect::<HashMap<Key, Value>>();
|
||||||
let bad_mt = MerkleTree::new(MAX_DEPTH, bad_kvs_mt)?;
|
bad_pod.dict = Dictionary::new(bad_kvs).unwrap();
|
||||||
bad_pod.dict.mt = bad_mt;
|
|
||||||
assert!(bad_pod.verify().is_err());
|
assert!(bad_pod.verify().is_err());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,21 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use NativePredicate as NP;
|
|
||||||
use StatementTmplBuilder as STB;
|
use StatementTmplBuilder as STB;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
frontend::{
|
frontend::{key, literal, CustomPredicateBatchBuilder, StatementTmplBuilder},
|
||||||
literal, CustomPredicateBatch, CustomPredicateBatchBuilder, CustomPredicateRef,
|
middleware::{
|
||||||
NativePredicate, Predicate, StatementTmplBuilder,
|
CustomPredicateBatch, CustomPredicateRef, NativePredicate as NP, Params, PodType,
|
||||||
|
Predicate, KEY_SIGNER, KEY_TYPE,
|
||||||
},
|
},
|
||||||
middleware::{self, Params, PodType, KEY_SIGNER, KEY_TYPE},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Instantiates an ETH friend batch
|
/// Instantiates an ETH friend batch
|
||||||
pub fn eth_friend_batch(params: &Params) -> Result<Arc<CustomPredicateBatch>> {
|
pub fn eth_friend_batch(params: &Params) -> Result<Arc<CustomPredicateBatch>> {
|
||||||
let mut builder = CustomPredicateBatchBuilder::new("eth_friend".into());
|
let mut builder = CustomPredicateBatchBuilder::new("eth_friend".into());
|
||||||
let _eth_friend = builder.predicate_and(
|
let _eth_friend = builder.predicate_and(
|
||||||
|
"eth_friend",
|
||||||
params,
|
params,
|
||||||
// arguments:
|
// arguments:
|
||||||
&["src_ori", "src_key", "dst_ori", "dst_key"],
|
&["src_ori", "src_key", "dst_ori", "dst_key"],
|
||||||
|
|
@ -25,18 +25,17 @@ pub fn eth_friend_batch(params: &Params) -> Result<Arc<CustomPredicateBatch>> {
|
||||||
&[
|
&[
|
||||||
// there is an attestation pod that's a SignedPod
|
// there is an attestation pod that's a SignedPod
|
||||||
STB::new(NP::ValueOf)
|
STB::new(NP::ValueOf)
|
||||||
.arg(("attestation_pod", literal(KEY_TYPE)))
|
.arg(("attestation_pod", key(KEY_TYPE)))
|
||||||
.arg(middleware::Value::from(PodType::MockSigned)), // TODO
|
.arg(literal(PodType::MockSigned)), // TODO
|
||||||
// the attestation pod is signed by (src_or, src_key)
|
// the attestation pod is signed by (src_or, src_key)
|
||||||
STB::new(NP::Equal)
|
STB::new(NP::Equal)
|
||||||
.arg(("attestation_pod", literal(KEY_SIGNER)))
|
.arg(("attestation_pod", key(KEY_SIGNER)))
|
||||||
.arg(("src_ori", "src_key")),
|
.arg(("src_ori", "src_key")),
|
||||||
// that same attestation pod has an "attestation"
|
// that same attestation pod has an "attestation"
|
||||||
STB::new(NP::Equal)
|
STB::new(NP::Equal)
|
||||||
.arg(("attestation_pod", literal("attestation")))
|
.arg(("attestation_pod", key("attestation")))
|
||||||
.arg(("dst_ori", "dst_key")),
|
.arg(("dst_ori", "dst_key")),
|
||||||
],
|
],
|
||||||
"eth_friend",
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
println!("a.0. eth_friend = {}", builder.predicates.last().unwrap());
|
println!("a.0. eth_friend = {}", builder.predicates.last().unwrap());
|
||||||
|
|
@ -53,6 +52,7 @@ pub fn eth_dos_batch(params: &Params) -> Result<Arc<CustomPredicateBatch>> {
|
||||||
// ValueOf(distance_or, distance_key, 0)
|
// ValueOf(distance_or, distance_key, 0)
|
||||||
// >
|
// >
|
||||||
let eth_dos_distance_base = builder.predicate_and(
|
let eth_dos_distance_base = builder.predicate_and(
|
||||||
|
"eth_dos_distance_base",
|
||||||
params,
|
params,
|
||||||
&[
|
&[
|
||||||
// arguments:
|
// arguments:
|
||||||
|
|
@ -72,9 +72,8 @@ pub fn eth_dos_batch(params: &Params) -> Result<Arc<CustomPredicateBatch>> {
|
||||||
.arg(("dst_ori", "dst_key")),
|
.arg(("dst_ori", "dst_key")),
|
||||||
STB::new(NP::ValueOf)
|
STB::new(NP::ValueOf)
|
||||||
.arg(("distance_ori", "distance_key"))
|
.arg(("distance_ori", "distance_key"))
|
||||||
.arg(0),
|
.arg(literal(0)),
|
||||||
],
|
],
|
||||||
"eth_dos_distance_base",
|
|
||||||
)?;
|
)?;
|
||||||
println!(
|
println!(
|
||||||
"b.0. eth_dos_distance_base = {}",
|
"b.0. eth_dos_distance_base = {}",
|
||||||
|
|
@ -84,6 +83,7 @@ pub fn eth_dos_batch(params: &Params) -> Result<Arc<CustomPredicateBatch>> {
|
||||||
let eth_dos_distance = Predicate::BatchSelf(2);
|
let eth_dos_distance = Predicate::BatchSelf(2);
|
||||||
|
|
||||||
let eth_dos_distance_ind = builder.predicate_and(
|
let eth_dos_distance_ind = builder.predicate_and(
|
||||||
|
"eth_dos_distance_ind",
|
||||||
params,
|
params,
|
||||||
&[
|
&[
|
||||||
// arguments:
|
// arguments:
|
||||||
|
|
@ -106,21 +106,27 @@ pub fn eth_dos_batch(params: &Params) -> Result<Arc<CustomPredicateBatch>> {
|
||||||
&[
|
&[
|
||||||
// statement templates:
|
// statement templates:
|
||||||
STB::new(eth_dos_distance)
|
STB::new(eth_dos_distance)
|
||||||
.arg(("src_ori", "src_key"))
|
.arg("src_ori")
|
||||||
.arg(("intermed_ori", "intermed_key"))
|
.arg("src_key")
|
||||||
.arg(("shorter_distance_ori", "shorter_distance_key")),
|
.arg("intermed_ori")
|
||||||
|
.arg("intermed_key")
|
||||||
|
.arg("shorter_distance_ori")
|
||||||
|
.arg("shorter_distance_key"),
|
||||||
// distance == shorter_distance + 1
|
// distance == shorter_distance + 1
|
||||||
STB::new(NP::ValueOf).arg(("one_ori", "one_key")).arg(1),
|
STB::new(NP::ValueOf)
|
||||||
|
.arg(("one_ori", "one_key"))
|
||||||
|
.arg(literal(1)),
|
||||||
STB::new(NP::SumOf)
|
STB::new(NP::SumOf)
|
||||||
.arg(("distance_ori", "distance_key"))
|
.arg(("distance_ori", "distance_key"))
|
||||||
.arg(("shorter_distance_ori", "shorter_distance_key"))
|
.arg(("shorter_distance_ori", "shorter_distance_key"))
|
||||||
.arg(("one_ori", "one_key")),
|
.arg(("one_ori", "one_key")),
|
||||||
// intermed is a friend of dst
|
// intermed is a friend of dst
|
||||||
STB::new(eth_friend)
|
STB::new(eth_friend)
|
||||||
.arg(("intermed_ori", "intermed_key"))
|
.arg("intermed_ori")
|
||||||
.arg(("dst_ori", "dst_key")),
|
.arg("intermed_key")
|
||||||
|
.arg("dst_ori")
|
||||||
|
.arg("dst_key"),
|
||||||
],
|
],
|
||||||
"eth_dos_distance_ind",
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
|
|
@ -129,6 +135,7 @@ pub fn eth_dos_batch(params: &Params) -> Result<Arc<CustomPredicateBatch>> {
|
||||||
);
|
);
|
||||||
|
|
||||||
let _eth_dos_distance = builder.predicate_or(
|
let _eth_dos_distance = builder.predicate_or(
|
||||||
|
"eth_dos_distance",
|
||||||
params,
|
params,
|
||||||
&[
|
&[
|
||||||
"src_ori",
|
"src_ori",
|
||||||
|
|
@ -141,15 +148,20 @@ pub fn eth_dos_batch(params: &Params) -> Result<Arc<CustomPredicateBatch>> {
|
||||||
&[],
|
&[],
|
||||||
&[
|
&[
|
||||||
STB::new(eth_dos_distance_base)
|
STB::new(eth_dos_distance_base)
|
||||||
.arg(("src_ori", "src_key"))
|
.arg("src_ori")
|
||||||
.arg(("dst_ori", "dst_key"))
|
.arg("src_key")
|
||||||
.arg(("distance_ori", "distance_key")),
|
.arg("dst_ori")
|
||||||
|
.arg("dst_key")
|
||||||
|
.arg("distance_ori")
|
||||||
|
.arg("distance_key"),
|
||||||
STB::new(eth_dos_distance_ind)
|
STB::new(eth_dos_distance_ind)
|
||||||
.arg(("src_ori", "src_key"))
|
.arg("src_ori")
|
||||||
.arg(("dst_ori", "dst_key"))
|
.arg("src_key")
|
||||||
.arg(("distance_ori", "distance_key")),
|
.arg("dst_ori")
|
||||||
|
.arg("dst_key")
|
||||||
|
.arg("distance_ori")
|
||||||
|
.arg("distance_key"),
|
||||||
],
|
],
|
||||||
"eth_dos_distance",
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
pub mod custom;
|
pub mod custom;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use custom::{eth_dos_batch, eth_friend_batch};
|
use custom::{eth_dos_batch, eth_friend_batch};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backends::plonky2::mock::signedpod::MockSigner,
|
backends::plonky2::mock::signedpod::MockSigner,
|
||||||
frontend::{
|
frontend::{MainPodBuilder, SignedPod, SignedPodBuilder},
|
||||||
containers::Dictionary, CustomPredicateRef, MainPodBuilder, SignedPod, SignedPodBuilder,
|
middleware::{
|
||||||
Statement, Value,
|
containers::Set, CustomPredicateRef, Key, Params, PodType, Statement, TypedValue, Value,
|
||||||
|
KEY_SIGNER, KEY_TYPE,
|
||||||
},
|
},
|
||||||
middleware::{Params, PodType, KEY_SIGNER, KEY_TYPE},
|
|
||||||
op,
|
op,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -19,8 +19,10 @@ use crate::{
|
||||||
|
|
||||||
pub fn zu_kyc_sign_pod_builders(
|
pub fn zu_kyc_sign_pod_builders(
|
||||||
params: &Params,
|
params: &Params,
|
||||||
sanction_set: &Value,
|
|
||||||
) -> (SignedPodBuilder, SignedPodBuilder, SignedPodBuilder) {
|
) -> (SignedPodBuilder, SignedPodBuilder, SignedPodBuilder) {
|
||||||
|
let sanctions_values: HashSet<Value> = ["A343434340"].iter().map(|s| Value::from(*s)).collect();
|
||||||
|
let sanction_set = Value::from(Set::new(sanctions_values).unwrap());
|
||||||
|
|
||||||
let mut gov_id = SignedPodBuilder::new(params);
|
let mut gov_id = SignedPodBuilder::new(params);
|
||||||
gov_id.insert("idNumber", "4242424242");
|
gov_id.insert("idNumber", "4242424242");
|
||||||
gov_id.insert("dateOfBirth", 1169909384);
|
gov_id.insert("dateOfBirth", 1169909384);
|
||||||
|
|
@ -31,7 +33,8 @@ pub fn zu_kyc_sign_pod_builders(
|
||||||
pay_stub.insert("startDate", 1706367566);
|
pay_stub.insert("startDate", 1706367566);
|
||||||
|
|
||||||
let mut sanction_list = SignedPodBuilder::new(params);
|
let mut sanction_list = SignedPodBuilder::new(params);
|
||||||
sanction_list.insert("sanctionList", sanction_set.clone());
|
|
||||||
|
sanction_list.insert("sanctionList", sanction_set);
|
||||||
|
|
||||||
(gov_id, pay_stub, sanction_list)
|
(gov_id, pay_stub, sanction_list)
|
||||||
}
|
}
|
||||||
|
|
@ -42,15 +45,15 @@ pub fn zu_kyc_pod_builder(
|
||||||
pay_stub: &SignedPod,
|
pay_stub: &SignedPod,
|
||||||
sanction_list: &SignedPod,
|
sanction_list: &SignedPod,
|
||||||
) -> Result<MainPodBuilder> {
|
) -> Result<MainPodBuilder> {
|
||||||
let sanction_set = match sanction_list.kvs.get("sanctionList") {
|
let sanction_set = match sanction_list.get("sanctionList").map(|v| v.typed()) {
|
||||||
Some(Value::Set(s)) => Ok(s),
|
Some(TypedValue::Set(s)) => Ok(s),
|
||||||
_ => Err(anyhow!("Missing sanction list!")),
|
_ => Err(anyhow!("Missing sanction list!")),
|
||||||
}?;
|
}?;
|
||||||
let now_minus_18y: i64 = 1169909388;
|
let now_minus_18y: i64 = 1169909388;
|
||||||
let now_minus_1y: i64 = 1706367566;
|
let now_minus_1y: i64 = 1706367566;
|
||||||
|
|
||||||
let gov_id_kvs = gov_id.kvs();
|
let gov_id_kvs = gov_id.kvs();
|
||||||
let id_number_value = gov_id_kvs.get(&"idNumber".into()).unwrap();
|
let id_number_value = gov_id_kvs.get(&Key::from("idNumber")).unwrap();
|
||||||
|
|
||||||
let mut kyc = MainPodBuilder::new(params);
|
let mut kyc = MainPodBuilder::new(params);
|
||||||
kyc.add_signed_pod(gov_id);
|
kyc.add_signed_pod(gov_id);
|
||||||
|
|
@ -60,9 +63,7 @@ pub fn zu_kyc_pod_builder(
|
||||||
set_not_contains,
|
set_not_contains,
|
||||||
(sanction_list, "sanctionList"),
|
(sanction_list, "sanctionList"),
|
||||||
(gov_id, "idNumber"),
|
(gov_id, "idNumber"),
|
||||||
sanction_set
|
sanction_set.prove_nonexistence(id_number_value)?
|
||||||
.middleware_set()
|
|
||||||
.prove_nonexistence(id_number_value)?
|
|
||||||
))?;
|
))?;
|
||||||
kyc.pub_op(op!(lt, (gov_id, "dateOfBirth"), now_minus_18y))?;
|
kyc.pub_op(op!(lt, (gov_id, "dateOfBirth"), now_minus_18y))?;
|
||||||
kyc.pub_op(op!(
|
kyc.pub_op(op!(
|
||||||
|
|
@ -77,7 +78,10 @@ pub fn zu_kyc_pod_builder(
|
||||||
|
|
||||||
// ETHDoS
|
// ETHDoS
|
||||||
|
|
||||||
pub fn eth_friend_signed_pod_builder(params: &Params, friend_pubkey: Value) -> SignedPodBuilder {
|
pub fn eth_friend_signed_pod_builder(
|
||||||
|
params: &Params,
|
||||||
|
friend_pubkey: TypedValue,
|
||||||
|
) -> SignedPodBuilder {
|
||||||
let mut attestation = SignedPodBuilder::new(params);
|
let mut attestation = SignedPodBuilder::new(params);
|
||||||
attestation.insert("attestation", friend_pubkey);
|
attestation.insert("attestation", friend_pubkey);
|
||||||
|
|
||||||
|
|
@ -88,7 +92,7 @@ pub fn eth_dos_pod_builder(
|
||||||
params: &Params,
|
params: &Params,
|
||||||
alice_attestation: &SignedPod,
|
alice_attestation: &SignedPod,
|
||||||
charlie_attestation: &SignedPod,
|
charlie_attestation: &SignedPod,
|
||||||
bob_pubkey: &Value,
|
bob_pubkey: &TypedValue,
|
||||||
) -> Result<MainPodBuilder> {
|
) -> Result<MainPodBuilder> {
|
||||||
// Will need ETH friend and ETH DoS custom predicate batches.
|
// Will need ETH friend and ETH DoS custom predicate batches.
|
||||||
let eth_friend = CustomPredicateRef::new(eth_friend_batch(params)?, 0);
|
let eth_friend = CustomPredicateRef::new(eth_friend_batch(params)?, 0);
|
||||||
|
|
@ -103,14 +107,14 @@ pub fn eth_dos_pod_builder(
|
||||||
alice_bob_ethdos.add_signed_pod(charlie_attestation);
|
alice_bob_ethdos.add_signed_pod(charlie_attestation);
|
||||||
|
|
||||||
// Attestation POD entries
|
// Attestation POD entries
|
||||||
let alice_pubkey = *alice_attestation
|
let alice_pubkey = alice_attestation
|
||||||
.kvs()
|
.get(KEY_SIGNER)
|
||||||
.get(&KEY_SIGNER.into())
|
.expect("Could not find Alice's public key!")
|
||||||
.ok_or(anyhow!("Could not find Alice's public key!"))?;
|
.clone();
|
||||||
let charlie_pubkey = *charlie_attestation
|
let charlie_pubkey = charlie_attestation
|
||||||
.kvs()
|
.get(KEY_SIGNER)
|
||||||
.get(&KEY_SIGNER.into())
|
.expect("Could not find Charlie's public key!")
|
||||||
.ok_or(anyhow!("Could not find Charlie's public key!"))?;
|
.clone();
|
||||||
|
|
||||||
// Include Alice and Bob's keys as public statements. We don't
|
// Include Alice and Bob's keys as public statements. We don't
|
||||||
// want to reveal the middleman.
|
// want to reveal the middleman.
|
||||||
|
|
@ -119,7 +123,7 @@ pub fn eth_dos_pod_builder(
|
||||||
let charlie_pubkey = alice_bob_ethdos.priv_op(op!(new_entry, ("Charlie", charlie_pubkey)))?;
|
let charlie_pubkey = alice_bob_ethdos.priv_op(op!(new_entry, ("Charlie", charlie_pubkey)))?;
|
||||||
|
|
||||||
// The ETHDoS distance from Alice to Alice is 0.
|
// The ETHDoS distance from Alice to Alice is 0.
|
||||||
let zero = alice_bob_ethdos.priv_literal(&0)?;
|
let zero = alice_bob_ethdos.priv_literal(0)?;
|
||||||
let alice_equals_alice = alice_bob_ethdos.priv_op(op!(
|
let alice_equals_alice = alice_bob_ethdos.priv_op(op!(
|
||||||
eq,
|
eq,
|
||||||
(alice_attestation, KEY_SIGNER),
|
(alice_attestation, KEY_SIGNER),
|
||||||
|
|
@ -134,11 +138,12 @@ pub fn eth_dos_pod_builder(
|
||||||
let ethdos_alice_alice_is_zero = alice_bob_ethdos.priv_op(op!(
|
let ethdos_alice_alice_is_zero = alice_bob_ethdos.priv_op(op!(
|
||||||
custom,
|
custom,
|
||||||
eth_dos.clone(),
|
eth_dos.clone(),
|
||||||
ethdos_alice_alice_is_zero_base
|
ethdos_alice_alice_is_zero_base,
|
||||||
|
Statement::None
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
// Alice and Charlie are ETH friends.
|
// Alice and Charlie are ETH friends.
|
||||||
let attestation_is_signed_pod = Statement::from((alice_attestation, KEY_TYPE));
|
let attestation_is_signed_pod = alice_attestation.get_statement(KEY_TYPE).unwrap();
|
||||||
let attestation_signed_by_alice =
|
let attestation_signed_by_alice =
|
||||||
alice_bob_ethdos.priv_op(op!(eq, (alice_attestation, KEY_SIGNER), alice_pubkey_copy))?;
|
alice_bob_ethdos.priv_op(op!(eq, (alice_attestation, KEY_SIGNER), alice_pubkey_copy))?;
|
||||||
let alice_attests_to_charlie = alice_bob_ethdos.priv_op(op!(
|
let alice_attests_to_charlie = alice_bob_ethdos.priv_op(op!(
|
||||||
|
|
@ -155,7 +160,7 @@ pub fn eth_dos_pod_builder(
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
// ...and so are Chuck and Bob.
|
// ...and so are Chuck and Bob.
|
||||||
let attestation_is_signed_pod = Statement::from((charlie_attestation, KEY_TYPE));
|
let attestation_is_signed_pod = charlie_attestation.get_statement(KEY_TYPE).unwrap();
|
||||||
let attestation_signed_by_charlie =
|
let attestation_signed_by_charlie =
|
||||||
alice_bob_ethdos.priv_op(op!(eq, (charlie_attestation, KEY_SIGNER), charlie_pubkey))?;
|
alice_bob_ethdos.priv_op(op!(eq, (charlie_attestation, KEY_SIGNER), charlie_pubkey))?;
|
||||||
let charlie_attests_to_bob = alice_bob_ethdos.priv_op(op!(
|
let charlie_attests_to_bob = alice_bob_ethdos.priv_op(op!(
|
||||||
|
|
@ -172,7 +177,7 @@ pub fn eth_dos_pod_builder(
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
// The ETHDoS distance from Alice to Charlie is 1.
|
// The ETHDoS distance from Alice to Charlie is 1.
|
||||||
let one = alice_bob_ethdos.priv_literal(&1)?;
|
let one = alice_bob_ethdos.priv_literal(1)?;
|
||||||
// 1 = 0 + 1
|
// 1 = 0 + 1
|
||||||
let ethdos_sum =
|
let ethdos_sum =
|
||||||
alice_bob_ethdos.priv_op(op!(sum_of, one.clone(), zero.clone(), one.clone()))?;
|
alice_bob_ethdos.priv_op(op!(sum_of, one.clone(), zero.clone(), one.clone()))?;
|
||||||
|
|
@ -187,13 +192,14 @@ pub fn eth_dos_pod_builder(
|
||||||
let ethdos_alice_charlie_is_one = alice_bob_ethdos.priv_op(op!(
|
let ethdos_alice_charlie_is_one = alice_bob_ethdos.priv_op(op!(
|
||||||
custom,
|
custom,
|
||||||
eth_dos.clone(),
|
eth_dos.clone(),
|
||||||
|
Statement::None,
|
||||||
ethdos_alice_charlie_is_one_ind
|
ethdos_alice_charlie_is_one_ind
|
||||||
))?;
|
))?;
|
||||||
|
|
||||||
// The ETHDoS distance from Alice to Bob is 2.
|
// The ETHDoS distance from Alice to Bob is 2.
|
||||||
// The constant "TWO" and the final statement are both to be
|
// The constant "TWO" and the final statement are both to be
|
||||||
// public.
|
// public.
|
||||||
let two = alice_bob_ethdos.pub_literal(&2)?;
|
let two = alice_bob_ethdos.pub_literal(2)?;
|
||||||
// 2 = 1 + 1
|
// 2 = 1 + 1
|
||||||
let ethdos_sum =
|
let ethdos_sum =
|
||||||
alice_bob_ethdos.priv_op(op!(sum_of, two.clone(), one.clone(), one.clone()))?;
|
alice_bob_ethdos.priv_op(op!(sum_of, two.clone(), one.clone(), one.clone()))?;
|
||||||
|
|
@ -205,8 +211,12 @@ pub fn eth_dos_pod_builder(
|
||||||
ethdos_sum,
|
ethdos_sum,
|
||||||
ethfriends_charlie_bob
|
ethfriends_charlie_bob
|
||||||
))?;
|
))?;
|
||||||
let _ethdos_alice_bob_is_two =
|
let _ethdos_alice_bob_is_two = alice_bob_ethdos.pub_op(op!(
|
||||||
alice_bob_ethdos.pub_op(op!(custom, eth_dos.clone(), ethdos_alice_bob_is_two_ind))?;
|
custom,
|
||||||
|
eth_dos.clone(),
|
||||||
|
Statement::None,
|
||||||
|
ethdos_alice_bob_is_two_ind
|
||||||
|
))?;
|
||||||
|
|
||||||
Ok(alice_bob_ethdos)
|
Ok(alice_bob_ethdos)
|
||||||
}
|
}
|
||||||
|
|
@ -268,18 +278,15 @@ pub fn great_boy_pod_builder(
|
||||||
PodType::MockSigned as i64
|
PodType::MockSigned as i64
|
||||||
))?;
|
))?;
|
||||||
// Each good boy POD comes from a valid issuer
|
// Each good boy POD comes from a valid issuer
|
||||||
let good_boy_proof = match good_boy_issuers {
|
let good_boy_proof = match good_boy_issuers.typed() {
|
||||||
Value::Dictionary(dict) => Ok(dict),
|
TypedValue::Set(set) => Ok(set),
|
||||||
_ => Err(anyhow!("Invalid good boy issuers!")),
|
_ => Err(anyhow!("Invalid good boy issuers!")),
|
||||||
}?
|
}?
|
||||||
.middleware_dict()
|
.prove(pod_kvs.get(&Key::from(KEY_SIGNER)).unwrap())?;
|
||||||
.prove(pod_kvs.get(&KEY_SIGNER.into()).unwrap())?
|
|
||||||
.1;
|
|
||||||
great_boy.pub_op(op!(
|
great_boy.pub_op(op!(
|
||||||
dict_contains,
|
set_contains,
|
||||||
good_boy_issuers,
|
good_boy_issuers,
|
||||||
(good_boy_pods[good_boy_idx * 2 + issuer_idx], KEY_SIGNER),
|
(good_boy_pods[good_boy_idx * 2 + issuer_idx], KEY_SIGNER),
|
||||||
0,
|
|
||||||
good_boy_proof
|
good_boy_proof
|
||||||
))?;
|
))?;
|
||||||
// Each good boy has 2 good boy pods
|
// Each good boy has 2 good boy pods
|
||||||
|
|
@ -357,11 +364,8 @@ pub fn great_boy_pod_full_flow() -> Result<MainPodBuilder> {
|
||||||
alice_friend_pods.push(friend.sign(&mut bob_signer).unwrap());
|
alice_friend_pods.push(friend.sign(&mut bob_signer).unwrap());
|
||||||
alice_friend_pods.push(friend.sign(&mut charlie_signer).unwrap());
|
alice_friend_pods.push(friend.sign(&mut charlie_signer).unwrap());
|
||||||
|
|
||||||
let good_boy_issuers = Value::Dictionary(Dictionary::new(
|
let good_boy_issuers = Value::from(Set::new(
|
||||||
good_boy_issuers
|
good_boy_issuers.into_iter().map(Value::from).collect(),
|
||||||
.into_iter()
|
|
||||||
.map(|issuer| (issuer.to_string(), 0.into()))
|
|
||||||
.collect(),
|
|
||||||
)?);
|
)?);
|
||||||
|
|
||||||
great_boy_pod_builder(
|
great_boy_pod_builder(
|
||||||
|
|
@ -397,13 +401,15 @@ pub fn tickets_pod_builder(
|
||||||
signed_pod: &SignedPod,
|
signed_pod: &SignedPod,
|
||||||
expected_event_id: i64,
|
expected_event_id: i64,
|
||||||
expect_consumed: bool,
|
expect_consumed: bool,
|
||||||
blacklisted_emails: &Dictionary,
|
blacklisted_emails: &Set,
|
||||||
) -> Result<MainPodBuilder> {
|
) -> Result<MainPodBuilder> {
|
||||||
let attendee_email_value = signed_pod.kvs.get("attendeeEmail").unwrap();
|
let attendee_email_value = signed_pod
|
||||||
let attendee_nin_blacklist_pf = blacklisted_emails
|
.kvs()
|
||||||
.middleware_dict()
|
.get(&Key::from("attendeeEmail"))
|
||||||
.prove_nonexistence(&attendee_email_value.into())?;
|
.unwrap()
|
||||||
let blacklisted_email_dict_value = Value::Dictionary(blacklisted_emails.clone());
|
.clone();
|
||||||
|
let attendee_nin_blacklist_pf = blacklisted_emails.prove_nonexistence(&attendee_email_value)?;
|
||||||
|
let blacklisted_email_set_value = Value::from(TypedValue::Set(blacklisted_emails.clone()));
|
||||||
// Create a main pod referencing this signed pod with some statements
|
// Create a main pod referencing this signed pod with some statements
|
||||||
let mut builder = MainPodBuilder::new(params);
|
let mut builder = MainPodBuilder::new(params);
|
||||||
builder.add_signed_pod(signed_pod);
|
builder.add_signed_pod(signed_pod);
|
||||||
|
|
@ -412,7 +418,7 @@ pub fn tickets_pod_builder(
|
||||||
builder.pub_op(op!(eq, (signed_pod, "isRevoked"), false))?;
|
builder.pub_op(op!(eq, (signed_pod, "isRevoked"), false))?;
|
||||||
builder.pub_op(op!(
|
builder.pub_op(op!(
|
||||||
dict_not_contains,
|
dict_not_contains,
|
||||||
blacklisted_email_dict_value,
|
blacklisted_email_set_value,
|
||||||
(signed_pod, "attendeeEmail"),
|
(signed_pod, "attendeeEmail"),
|
||||||
attendee_nin_blacklist_pf
|
attendee_nin_blacklist_pf
|
||||||
))?;
|
))?;
|
||||||
|
|
@ -423,11 +429,5 @@ pub fn tickets_pod_full_flow() -> Result<MainPodBuilder> {
|
||||||
let params = Params::default();
|
let params = Params::default();
|
||||||
let builder = tickets_sign_pod_builder(¶ms);
|
let builder = tickets_sign_pod_builder(¶ms);
|
||||||
let signed_pod = builder.sign(&mut MockSigner { pk: "test".into() }).unwrap();
|
let signed_pod = builder.sign(&mut MockSigner { pk: "test".into() }).unwrap();
|
||||||
tickets_pod_builder(
|
tickets_pod_builder(¶ms, &signed_pod, 123, true, &Set::new(HashSet::new())?)
|
||||||
¶ms,
|
|
||||||
&signed_pod,
|
|
||||||
123,
|
|
||||||
true,
|
|
||||||
&Dictionary::new(HashMap::new())?,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -3,97 +3,40 @@ use std::{collections::HashMap, fmt, hash as h, iter, iter::zip, sync::Arc};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
|
// use serde::{Deserialize, Serialize};
|
||||||
use crate::{
|
use crate::{
|
||||||
frontend::{AnchoredKey, NativePredicate, Origin, Statement, StatementArg, Value},
|
frontend::{AnchoredKey, Statement, StatementArg},
|
||||||
middleware::{self, hash_str, HashOrWildcard, Params, PodId, ToFields},
|
middleware::{
|
||||||
util::hashmap_insert_no_dupe,
|
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
|
/// Argument to a statement template
|
||||||
pub enum KeyOrWildcardStr {
|
pub enum KeyOrWildcardStr {
|
||||||
Key(String), // represents a literal key
|
Key(String), // represents a literal key
|
||||||
Wildcard(String),
|
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
|
/// 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())
|
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
|
/// Builder Argument for the StatementTmplBuilder
|
||||||
pub enum BuilderArg {
|
pub enum BuilderArg {
|
||||||
Literal(Value),
|
Literal(Value),
|
||||||
/// Key: (origin, key), where origin & key can be both Hash or Wildcard
|
/// Key: (origin, key), where origin is a Wildcard and key can be both Key or Wildcard
|
||||||
Key(KeyOrWildcardStr, KeyOrWildcardStr),
|
Key(String, KeyOrWildcardStr),
|
||||||
|
WildcardLiteral(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// When defining a `BuilderArg`, it can be done from 3 different inputs:
|
/// 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"))
|
/// 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)
|
/// 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.
|
/// case i.
|
||||||
impl From<(&str, KeyOrWildcardStr)> for BuilderArg {
|
impl From<(&str, KeyOrWildcardStr)> for BuilderArg {
|
||||||
|
|
@ -103,267 +46,24 @@ impl From<(&str, KeyOrWildcardStr)> for BuilderArg {
|
||||||
KeyOrWildcardStr::Key(_) => (),
|
KeyOrWildcardStr::Key(_) => (),
|
||||||
_ => panic!("not supported"),
|
_ => panic!("not supported"),
|
||||||
};
|
};
|
||||||
Self::Key(wildcard(origin), lit)
|
Self::Key(origin.into(), lit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// case ii.
|
/// case ii.
|
||||||
impl From<(&str, &str)> for BuilderArg {
|
impl From<(&str, &str)> for BuilderArg {
|
||||||
fn from((origin, field): (&str, &str)) -> Self {
|
fn from((origin, field): (&str, &str)) -> Self {
|
||||||
Self::Key(wildcard(origin), wildcard(field))
|
Self::Key(origin.into(), KeyOrWildcardStr::Wildcard(field.to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// case iii.
|
/// case iii.
|
||||||
impl<V> From<V> for BuilderArg
|
impl From<&str> for BuilderArg {
|
||||||
where
|
fn from(wc: &str) -> Self {
|
||||||
V: Into<Value>,
|
Self::WildcardLiteral(wc.to_string())
|
||||||
{
|
|
||||||
fn from(v: V) -> Self {
|
|
||||||
Self::Literal(v.into())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
pub fn literal(v: impl Into<Value>) -> BuilderArg {
|
||||||
#[serde(tag = "type", content = "value")]
|
BuilderArg::Literal(v.into())
|
||||||
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 struct StatementTmplBuilder {
|
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 struct CustomPredicateBatchBuilder {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub predicates: Vec<CustomPredicate>,
|
pub predicates: Vec<CustomPredicate>,
|
||||||
|
|
@ -477,37 +100,52 @@ impl CustomPredicateBatchBuilder {
|
||||||
|
|
||||||
pub fn predicate_and(
|
pub fn predicate_and(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
name: &str,
|
||||||
params: &Params,
|
params: &Params,
|
||||||
args: &[&str],
|
args: &[&str],
|
||||||
priv_args: &[&str],
|
priv_args: &[&str],
|
||||||
sts: &[StatementTmplBuilder],
|
sts: &[StatementTmplBuilder],
|
||||||
name: &str,
|
|
||||||
) -> Result<Predicate> {
|
) -> Result<Predicate> {
|
||||||
self.predicate(params, true, args, priv_args, sts, name)
|
self.predicate(name, params, true, args, priv_args, sts)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn predicate_or(
|
pub fn predicate_or(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
name: &str,
|
||||||
params: &Params,
|
params: &Params,
|
||||||
args: &[&str],
|
args: &[&str],
|
||||||
priv_args: &[&str],
|
priv_args: &[&str],
|
||||||
sts: &[StatementTmplBuilder],
|
sts: &[StatementTmplBuilder],
|
||||||
name: &str,
|
|
||||||
) -> Result<Predicate> {
|
) -> 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
|
/// creates the custom predicate from the given input, adds it to the
|
||||||
/// self.predicates, and returns the index of the created predicate
|
/// self.predicates, and returns the index of the created predicate
|
||||||
fn predicate(
|
fn predicate(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
name: &str,
|
||||||
params: &Params,
|
params: &Params,
|
||||||
conjunction: bool,
|
conjunction: bool,
|
||||||
args: &[&str],
|
args: &[&str],
|
||||||
priv_args: &[&str],
|
priv_args: &[&str],
|
||||||
sts: &[StatementTmplBuilder],
|
sts: &[StatementTmplBuilder],
|
||||||
name: &str,
|
|
||||||
) -> Result<Predicate> {
|
) -> 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
|
let statements = sts
|
||||||
.iter()
|
.iter()
|
||||||
.map(|sb| {
|
.map(|sb| {
|
||||||
|
|
@ -518,8 +156,11 @@ impl CustomPredicateBatchBuilder {
|
||||||
BuilderArg::Literal(v) => StatementTmplArg::Literal(v.clone()),
|
BuilderArg::Literal(v) => StatementTmplArg::Literal(v.clone()),
|
||||||
BuilderArg::Key(pod_id, key) => StatementTmplArg::Key(
|
BuilderArg::Key(pod_id, key) => StatementTmplArg::Key(
|
||||||
resolve_wildcard(args, priv_args, pod_id),
|
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();
|
.collect();
|
||||||
StatementTmpl {
|
StatementTmpl {
|
||||||
|
|
@ -529,7 +170,7 @@ impl CustomPredicateBatchBuilder {
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let custom_predicate =
|
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);
|
self.predicates.push(custom_predicate);
|
||||||
Ok(Predicate::BatchSelf(self.predicates.len() - 1))
|
Ok(Predicate::BatchSelf(self.predicates.len() - 1))
|
||||||
}
|
}
|
||||||
|
|
@ -542,17 +183,25 @@ 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 {
|
match v {
|
||||||
KeyOrWildcardStr::Key(k) => KeyOrWildcard::Key(k.clone()),
|
KeyOrWildcardStr::Key(k) => KeyOrWildcard::Key(Key::from(k)),
|
||||||
KeyOrWildcardStr::Wildcard(s) => KeyOrWildcard::Wildcard(
|
KeyOrWildcardStr::Wildcard(s) => {
|
||||||
|
KeyOrWildcard::Wildcard(resolve_wildcard(args, priv_args, s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_wildcard(args: &[&str], priv_args: &[&str], s: &str) -> Wildcard {
|
||||||
args.iter()
|
args.iter()
|
||||||
.chain(priv_args.iter())
|
.chain(priv_args.iter())
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.find_map(|(i, name)| (s == name).then_some(IndexedWildcard::new(s.clone(), i)))
|
.find_map(|(i, name)| (s == *name).then_some(Wildcard::new(s.to_string(), i)))
|
||||||
.unwrap(),
|
.unwrap()
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
@ -561,7 +210,7 @@ mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
examples::custom::{eth_dos_batch, eth_friend_batch},
|
examples::custom::{eth_dos_batch, eth_friend_batch},
|
||||||
middleware,
|
middleware,
|
||||||
// middleware::{CustomPredicateRef, Params, PodType},
|
middleware::{CustomPredicateRef, Params, PodType},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -569,7 +218,11 @@ mod tests {
|
||||||
use NativePredicate as NP;
|
use NativePredicate as NP;
|
||||||
use StatementTmplBuilder as STB;
|
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();
|
params.print_serialized_sizes();
|
||||||
|
|
||||||
// ETH friend custom predicate batch
|
// ETH friend custom predicate batch
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,13 +1,12 @@
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
// use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
frontend::{CustomPredicateRef, NativePredicate, Predicate, SignedPod, Statement, Value},
|
frontend::SignedPod,
|
||||||
middleware::{self, OperationAux},
|
middleware::{AnchoredKey, OperationAux, OperationType, Statement, Value},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum OperationArg {
|
pub enum OperationArg {
|
||||||
Statement(Statement),
|
Statement(Statement),
|
||||||
Literal(Value),
|
Literal(Value),
|
||||||
|
|
@ -56,7 +55,16 @@ impl From<bool> for OperationArg {
|
||||||
|
|
||||||
impl From<(&SignedPod, &str)> for OperationArg {
|
impl From<(&SignedPod, &str)> for OperationArg {
|
||||||
fn from((pod, key): (&SignedPod, &str)) -> Self {
|
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)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
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)]
|
|
||||||
pub struct Operation(pub OperationType, pub Vec<OperationArg>, pub OperationAux);
|
pub struct Operation(pub OperationType, pub Vec<OperationArg>, pub OperationAux);
|
||||||
|
|
||||||
impl fmt::Display for Operation {
|
impl fmt::Display for Operation {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
/*
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::{BTreeMap, HashMap};
|
||||||
|
|
||||||
use schemars::{JsonSchema, Schema};
|
use schemars::{JsonSchema, Schema};
|
||||||
|
|
@ -5,14 +6,14 @@ use serde::{Deserialize, Serialize, Serializer};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backends::plonky2::mock::{mainpod::MockMainPod, signedpod::MockSignedPod},
|
backends::plonky2::mock::{mainpod::MockMainPod, signedpod::MockSignedPod},
|
||||||
frontend::{containers::Dictionary, MainPod, SignedPod, Statement, Value},
|
frontend::{containers::Dictionary, MainPod, SignedPod, Statement, TypedValue},
|
||||||
middleware::PodId,
|
middleware::PodId,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, JsonSchema)]
|
#[derive(Serialize, Deserialize, JsonSchema)]
|
||||||
#[schemars(title = "SignedPod")]
|
#[schemars(title = "SignedPod")]
|
||||||
pub struct SignedPodHelper {
|
pub struct SignedPodHelper {
|
||||||
entries: HashMap<String, Value>,
|
entries: HashMap<String, TypedValue>,
|
||||||
proof: String,
|
proof: String,
|
||||||
pod_class: String,
|
pod_class: String,
|
||||||
pod_type: String,
|
pod_type: String,
|
||||||
|
|
@ -169,30 +170,34 @@ mod tests {
|
||||||
fn test_value_serialization() {
|
fn test_value_serialization() {
|
||||||
// Pairs of values and their expected serialized representations
|
// Pairs of values and their expected serialized representations
|
||||||
let values = vec![
|
let values = vec![
|
||||||
(Value::String("hello".to_string()), "\"hello\""),
|
(TypedValue::String("hello".to_string()), "\"hello\""),
|
||||||
(Value::Int(42), "{\"Int\":\"42\"}"),
|
(TypedValue::Int(42), "{\"Int\":\"42\"}"),
|
||||||
(Value::Bool(true), "true"),
|
(TypedValue::Bool(true), "true"),
|
||||||
(
|
(
|
||||||
Value::Array(
|
TypedValue::Array(
|
||||||
Array::new(vec![Value::String("foo".to_string()), Value::Bool(false)]).unwrap(),
|
Array::new(vec![
|
||||||
|
TypedValue::String("foo".to_string()),
|
||||||
|
TypedValue::Bool(false),
|
||||||
|
])
|
||||||
|
.unwrap(),
|
||||||
),
|
),
|
||||||
"[\"foo\",false]",
|
"[\"foo\",false]",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
Value::Dictionary(
|
TypedValue::Dictionary(
|
||||||
Dictionary::new(HashMap::from([
|
Dictionary::new(HashMap::from([
|
||||||
("foo".to_string(), Value::Int(123)),
|
("foo".to_string(), TypedValue::Int(123)),
|
||||||
("bar".to_string(), Value::String("baz".to_string())),
|
("bar".to_string(), TypedValue::String("baz".to_string())),
|
||||||
]))
|
]))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
),
|
),
|
||||||
"{\"Dictionary\":{\"bar\":\"baz\",\"foo\":{\"Int\":\"123\"}}}",
|
"{\"Dictionary\":{\"bar\":\"baz\",\"foo\":{\"Int\":\"123\"}}}",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
Value::Set(
|
TypedValue::Set(
|
||||||
Set::new(vec![
|
Set::new(vec![
|
||||||
Value::String("foo".to_string()),
|
TypedValue::String("foo".to_string()),
|
||||||
Value::String("bar".to_string()),
|
TypedValue::String("bar".to_string()),
|
||||||
])
|
])
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
),
|
),
|
||||||
|
|
@ -203,9 +208,9 @@ mod tests {
|
||||||
for (value, expected) in values {
|
for (value, expected) in values {
|
||||||
let serialized = serde_json::to_string(&value).unwrap();
|
let serialized = serde_json::to_string(&value).unwrap();
|
||||||
assert_eq!(serialized, expected);
|
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);
|
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);
|
assert_eq!(value, expected_deserialized);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -219,27 +224,32 @@ mod tests {
|
||||||
builder.insert("very_large_int", 1152921504606846976);
|
builder.insert("very_large_int", 1152921504606846976);
|
||||||
builder.insert(
|
builder.insert(
|
||||||
"a_dict_containing_one_key",
|
"a_dict_containing_one_key",
|
||||||
Value::Dictionary(
|
TypedValue::Dictionary(
|
||||||
Dictionary::new(HashMap::from([
|
Dictionary::new(HashMap::from([
|
||||||
("foo".to_string(), Value::Int(123)),
|
("foo".to_string(), TypedValue::Int(123)),
|
||||||
(
|
(
|
||||||
"an_array_containing_three_ints".to_string(),
|
"an_array_containing_three_ints".to_string(),
|
||||||
Value::Array(
|
TypedValue::Array(
|
||||||
Array::new(vec![Value::Int(1), Value::Int(2), Value::Int(3)]).unwrap(),
|
Array::new(vec![
|
||||||
|
TypedValue::Int(1),
|
||||||
|
TypedValue::Int(2),
|
||||||
|
TypedValue::Int(3),
|
||||||
|
])
|
||||||
|
.unwrap(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"a_set_containing_two_strings".to_string(),
|
"a_set_containing_two_strings".to_string(),
|
||||||
Value::Set(
|
TypedValue::Set(
|
||||||
Set::new(vec![
|
Set::new(vec![
|
||||||
Value::Array(
|
TypedValue::Array(
|
||||||
Array::new(vec![
|
Array::new(vec![
|
||||||
Value::String("foo".to_string()),
|
TypedValue::String("foo".to_string()),
|
||||||
Value::String("bar".to_string()),
|
TypedValue::String("bar".to_string()),
|
||||||
])
|
])
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
),
|
),
|
||||||
Value::String("baz".to_string()),
|
TypedValue::String("baz".to_string()),
|
||||||
])
|
])
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
),
|
),
|
||||||
|
|
@ -256,7 +266,6 @@ mod tests {
|
||||||
let deserialized: SignedPod = serde_json::from_str(&serialized).unwrap();
|
let deserialized: SignedPod = serde_json::from_str(&serialized).unwrap();
|
||||||
|
|
||||||
assert_eq!(pod.kvs, deserialized.kvs);
|
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.verify().is_ok(), deserialized.verify().is_ok());
|
||||||
assert_eq!(pod.id(), deserialized.id())
|
assert_eq!(pod.id(), deserialized.id())
|
||||||
}
|
}
|
||||||
|
|
@ -265,7 +274,7 @@ mod tests {
|
||||||
fn test_main_pod_serialization() -> Result<()> {
|
fn test_main_pod_serialization() -> Result<()> {
|
||||||
let params = middleware::Params::default();
|
let params = middleware::Params::default();
|
||||||
let sanctions_values = vec!["A343434340".into()];
|
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) =
|
let (gov_id_builder, pay_stub_builder, sanction_list_builder) =
|
||||||
zu_kyc_sign_pod_builders(¶ms, &sanction_set);
|
zu_kyc_sign_pod_builders(¶ms, &sanction_set);
|
||||||
|
|
@ -311,3 +320,4 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
|
||||||
|
|
@ -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(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -4,7 +4,6 @@ pub mod backends;
|
||||||
pub mod constants;
|
pub mod constants;
|
||||||
pub mod frontend;
|
pub mod frontend;
|
||||||
pub mod middleware;
|
pub mod middleware;
|
||||||
mod util;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod examples;
|
pub mod examples;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
// TODO: Update this doc
|
||||||
//! This file exposes the backend dependent basetypes as middleware types,
|
//! This file exposes the backend dependent basetypes as middleware types,
|
||||||
//! taking them from the feature-enabled backend.
|
//! taking them from the feature-enabled backend.
|
||||||
//!
|
//!
|
||||||
|
|
@ -29,12 +30,212 @@
|
||||||
//! u64/i64 to F conversion. Eventually we will do those conversions through the
|
//! u64/i64 to F conversion. Eventually we will do those conversions through the
|
||||||
//! approach described in this file, removing the imports of plonky2 in the
|
//! approach described in this file, removing the imports of plonky2 in the
|
||||||
//! middleware.
|
//! middleware.
|
||||||
|
//! TODO: Update this doc
|
||||||
|
|
||||||
/// Value, Hash and F are imported based on 'features'. For example by default
|
/// Value, Hash and F are imported based on 'features'. For example by default
|
||||||
/// we use the 'plonky2' feature, but it could be used a 'plonky3' feature, so
|
/// we use the 'plonky2' feature, but it could be used a 'plonky3' feature, so
|
||||||
/// then the Value, Hash and F types would come from the plonky3 backend.
|
/// then the Value, Hash and F types would come from the plonky3 backend.
|
||||||
#[cfg(feature = "backend_plonky2")]
|
// #[cfg(feature = "backend_plonky2")]
|
||||||
pub use crate::backends::plonky2::basetypes::{
|
// pub use crate::backends::plonky2::basetypes::{
|
||||||
hash_fields, hash_str, hash_value, Hash, Value, EMPTY_HASH, EMPTY_VALUE, F, HASH_SIZE,
|
// hash_fields, hash_str, hash_value, Hash, RawValue, EMPTY_HASH, EMPTY_VALUE, F, HASH_SIZE,
|
||||||
SELF_ID_HASH, VALUE_SIZE,
|
// SELF_ID_HASH, VALUE_SIZE,
|
||||||
|
// };
|
||||||
|
use std::{
|
||||||
|
cmp::{Ord, Ordering},
|
||||||
|
fmt,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use hex::{FromHex, FromHexError};
|
||||||
|
use plonky2::{
|
||||||
|
field::{
|
||||||
|
goldilocks_field::GoldilocksField,
|
||||||
|
types::{Field, PrimeField64},
|
||||||
|
},
|
||||||
|
hash::poseidon::PoseidonHash,
|
||||||
|
plonk::config::Hasher,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::middleware::{
|
||||||
|
// serialization::{
|
||||||
|
// deserialize_hash_tuple, deserialize_value_tuple, serialize_hash_tuple,
|
||||||
|
// serialize_value_tuple,
|
||||||
|
// },
|
||||||
|
Params,
|
||||||
|
ToFields,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// F is the native field we use everywhere. Currently it's Goldilocks from plonky2
|
||||||
|
pub type F = GoldilocksField;
|
||||||
|
|
||||||
|
pub const HASH_SIZE: usize = 4;
|
||||||
|
pub const VALUE_SIZE: usize = 4;
|
||||||
|
|
||||||
|
pub const EMPTY_VALUE: RawValue = RawValue([F::ZERO, F::ZERO, F::ZERO, F::ZERO]);
|
||||||
|
pub const SELF_ID_HASH: Hash = Hash([F::ONE, F::ZERO, F::ZERO, F::ZERO]);
|
||||||
|
pub const EMPTY_HASH: Hash = Hash([F::ZERO, F::ZERO, F::ZERO, F::ZERO]);
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)]
|
||||||
|
// #[schemars(rename = "RawValue")]
|
||||||
|
pub struct RawValue(
|
||||||
|
// #[serde(
|
||||||
|
// serialize_with = "serialize_value_tuple",
|
||||||
|
// deserialize_with = "deserialize_value_tuple"
|
||||||
|
// )]
|
||||||
|
// We know that Serde will serialize and deserialize this as a string, so we can
|
||||||
|
// use the JsonSchema to validate the format.
|
||||||
|
// #[schemars(with = "String", regex(pattern = r"^[0-9a-fA-F]{64}$"))]
|
||||||
|
pub [F; VALUE_SIZE],
|
||||||
|
);
|
||||||
|
|
||||||
|
impl ToFields for RawValue {
|
||||||
|
fn to_fields(&self, _params: &Params) -> Vec<F> {
|
||||||
|
self.0.to_vec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RawValue {
|
||||||
|
pub fn to_bytes(self) -> Vec<u8> {
|
||||||
|
self.0
|
||||||
|
.iter()
|
||||||
|
.flat_map(|e| e.to_canonical_u64().to_le_bytes())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for RawValue {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
for (lhs, rhs) in self.0.iter().zip(other.0.iter()).rev() {
|
||||||
|
let (lhs, rhs) = (lhs.to_canonical_u64(), rhs.to_canonical_u64());
|
||||||
|
match lhs.cmp(&rhs) {
|
||||||
|
Ordering::Less => return Ordering::Less,
|
||||||
|
Ordering::Greater => return Ordering::Greater,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ordering::Equal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for RawValue {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i64> for RawValue {
|
||||||
|
fn from(v: i64) -> Self {
|
||||||
|
let lo = F::from_canonical_u64((v as u64) & 0xffffffff);
|
||||||
|
let hi = F::from_canonical_u64((v as u64) >> 32);
|
||||||
|
RawValue([lo, hi, F::ZERO, F::ZERO])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Hash> for RawValue {
|
||||||
|
fn from(h: Hash) -> Self {
|
||||||
|
RawValue(h.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for RawValue {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
if self.0[2].is_zero() && self.0[3].is_zero() {
|
||||||
|
// Assume this is an integer
|
||||||
|
let (l0, l1) = (self.0[0].to_canonical_u64(), self.0[1].to_canonical_u64());
|
||||||
|
assert!(l0 < (1 << 32));
|
||||||
|
assert!(l1 < (1 << 32));
|
||||||
|
write!(f, "{}", l0 + l1 * (1 << 32))
|
||||||
|
} else {
|
||||||
|
// Assume this is a hash
|
||||||
|
Hash(self.0).fmt(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Hash, Eq, PartialEq)]
|
||||||
|
pub struct Hash(
|
||||||
|
// #[serde(
|
||||||
|
// serialize_with = "serialize_hash_tuple",
|
||||||
|
// deserialize_with = "deserialize_hash_tuple"
|
||||||
|
// )]
|
||||||
|
// #[schemars(with = "String", regex(pattern = r"^[0-9a-fA-F]{64}$"))]
|
||||||
|
pub [F; HASH_SIZE],
|
||||||
|
);
|
||||||
|
|
||||||
|
pub fn hash_value(input: &RawValue) -> Hash {
|
||||||
|
hash_fields(&input.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hash_fields(input: &[F]) -> Hash {
|
||||||
|
Hash(PoseidonHash::hash_no_pad(input).elements)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RawValue> for Hash {
|
||||||
|
fn from(v: RawValue) -> Self {
|
||||||
|
Hash(v.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToFields for Hash {
|
||||||
|
fn to_fields(&self, _params: &Params) -> Vec<F> {
|
||||||
|
self.0.to_vec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Hash {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Hash {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
RawValue(self.0).cmp(&RawValue(other.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Hash {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
let v0 = self.0[0].to_canonical_u64();
|
||||||
|
for i in 0..HASH_SIZE {
|
||||||
|
write!(f, "{:02x}", (v0 >> (i * 8)) & 0xff)?;
|
||||||
|
}
|
||||||
|
write!(f, "…")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromHex for Hash {
|
||||||
|
type Error = FromHexError;
|
||||||
|
|
||||||
|
// TODO make it dependant on backend::Value len
|
||||||
|
fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
|
||||||
|
// In little endian
|
||||||
|
let bytes = <[u8; 32]>::from_hex(hex)?;
|
||||||
|
let mut buf: [u8; 8] = [0; 8];
|
||||||
|
let mut inner = [F::ZERO; HASH_SIZE];
|
||||||
|
for i in 0..HASH_SIZE {
|
||||||
|
buf.copy_from_slice(&bytes[8 * i..8 * (i + 1)]);
|
||||||
|
inner[i] = F::from_canonical_u64(u64::from_le_bytes(buf));
|
||||||
|
}
|
||||||
|
Ok(Self(inner))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hash_str(s: &str) -> Hash {
|
||||||
|
let mut input = s.as_bytes().to_vec();
|
||||||
|
input.push(1); // padding
|
||||||
|
|
||||||
|
// Merge 7 bytes into 1 field, because the field is slightly below 64 bits
|
||||||
|
let input: Vec<F> = input
|
||||||
|
.chunks(7)
|
||||||
|
.map(|bytes| {
|
||||||
|
let mut v: u64 = 0;
|
||||||
|
for b in bytes.iter().rev() {
|
||||||
|
v <<= 8;
|
||||||
|
v += *b as u64;
|
||||||
|
}
|
||||||
|
F::from_canonical_u64(v)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
hash_fields(&input)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
/// This file implements the types defined at
|
/// This file implements the types defined at
|
||||||
/// https://0xparc.github.io/pod2/values.html#dictionary-array-set .
|
/// https://0xparc.github.io/pod2/values.html#dictionary-array-set .
|
||||||
use anyhow::Result;
|
use anyhow::{anyhow, Result};
|
||||||
|
|
||||||
#[cfg(feature = "backend_plonky2")]
|
#[cfg(feature = "backend_plonky2")]
|
||||||
use crate::backends::plonky2::primitives::merkletree::{Iter as TreeIter, MerkleProof, MerkleTree};
|
use crate::backends::plonky2::primitives::merkletree::{MerkleProof, MerkleTree};
|
||||||
use crate::{
|
use crate::{
|
||||||
constants::MAX_DEPTH,
|
constants::MAX_DEPTH,
|
||||||
middleware::basetypes::{hash_value, Hash, Value, EMPTY_VALUE},
|
middleware::{hash_value, Hash, Key, RawValue, Value, EMPTY_VALUE},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Dictionary: the user original keys and values are hashed to be used in the leaf.
|
/// Dictionary: the user original keys and values are hashed to be used in the leaf.
|
||||||
|
|
@ -16,47 +16,58 @@ use crate::{
|
||||||
/// leaf.value=hash(original_value)
|
/// leaf.value=hash(original_value)
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Dictionary {
|
pub struct Dictionary {
|
||||||
// exposed with pub(crate) so that it can be modified at tests
|
mt: MerkleTree,
|
||||||
pub(crate) mt: MerkleTree,
|
kvs: HashMap<Key, Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dictionary {
|
impl Dictionary {
|
||||||
pub fn new(kvs: &HashMap<Hash, Value>) -> Result<Self> {
|
pub fn new(kvs: HashMap<Key, Value>) -> Result<Self> {
|
||||||
let kvs: HashMap<Value, Value> = kvs.iter().map(|(&k, &v)| (Value(k.0), v)).collect();
|
let kvs_raw: HashMap<RawValue, RawValue> = kvs
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| (RawValue(k.hash().0), v.raw()))
|
||||||
|
.collect();
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
mt: MerkleTree::new(MAX_DEPTH, &kvs)?,
|
mt: MerkleTree::new(MAX_DEPTH, &kvs_raw)?,
|
||||||
|
kvs,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn commitment(&self) -> Hash {
|
pub fn commitment(&self) -> Hash {
|
||||||
self.mt.root()
|
self.mt.root()
|
||||||
}
|
}
|
||||||
pub fn get(&self, key: &Value) -> Result<Value> {
|
pub fn get(&self, key: &Key) -> Result<&Value> {
|
||||||
self.mt.get(key)
|
self.kvs
|
||||||
|
.get(key)
|
||||||
|
.ok_or_else(|| anyhow!("key \"{}\" not found", key.name()))
|
||||||
}
|
}
|
||||||
pub fn prove(&self, key: &Value) -> Result<(Value, MerkleProof)> {
|
pub fn prove(&self, key: &Key) -> Result<(&Value, MerkleProof)> {
|
||||||
self.mt.prove(key)
|
let (_, mtp) = self.mt.prove(&RawValue(key.hash().0))?;
|
||||||
|
let value = self.kvs.get(key).expect("key exists");
|
||||||
|
Ok((value, mtp))
|
||||||
}
|
}
|
||||||
pub fn prove_nonexistence(&self, key: &Value) -> Result<MerkleProof> {
|
pub fn prove_nonexistence(&self, key: &Key) -> Result<MerkleProof> {
|
||||||
self.mt.prove_nonexistence(key)
|
self.mt.prove_nonexistence(&RawValue(key.hash().0))
|
||||||
}
|
}
|
||||||
pub fn verify(root: Hash, proof: &MerkleProof, key: &Value, value: &Value) -> Result<()> {
|
pub fn verify(root: Hash, proof: &MerkleProof, key: &Key, value: &Value) -> Result<()> {
|
||||||
MerkleTree::verify(MAX_DEPTH, root, proof, key, value)
|
let key = RawValue(key.hash().0);
|
||||||
|
MerkleTree::verify(MAX_DEPTH, root, proof, &key, &value.raw())
|
||||||
}
|
}
|
||||||
pub fn verify_nonexistence(root: Hash, proof: &MerkleProof, key: &Value) -> Result<()> {
|
pub fn verify_nonexistence(root: Hash, proof: &MerkleProof, key: &Key) -> Result<()> {
|
||||||
MerkleTree::verify_nonexistence(MAX_DEPTH, root, proof, key)
|
let key = RawValue(key.hash().0);
|
||||||
|
MerkleTree::verify_nonexistence(MAX_DEPTH, root, proof, &key)
|
||||||
}
|
}
|
||||||
pub fn iter(&self) -> TreeIter {
|
// TODO: Rename to dict to be consistent maybe?
|
||||||
self.mt.iter()
|
pub fn kvs(&self) -> &HashMap<Key, Value> {
|
||||||
}
|
&self.kvs
|
||||||
}
|
|
||||||
impl<'a> IntoIterator for &'a Dictionary {
|
|
||||||
type Item = (&'a Value, &'a Value);
|
|
||||||
type IntoIter = TreeIter<'a>;
|
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
self.mt.iter()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// impl<'a> IntoIterator for &'a Dictionary {
|
||||||
|
// type Item = (&'a RawValue, &'a RawValue);
|
||||||
|
// type IntoIter = TreeIter<'a>;
|
||||||
|
//
|
||||||
|
// fn into_iter(self) -> Self::IntoIter {
|
||||||
|
// self.mt.iter()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
impl PartialEq for Dictionary {
|
impl PartialEq for Dictionary {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
|
@ -71,42 +82,48 @@ impl Eq for Dictionary {}
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Set {
|
pub struct Set {
|
||||||
mt: MerkleTree,
|
mt: MerkleTree,
|
||||||
|
set: HashSet<Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Set {
|
impl Set {
|
||||||
pub fn new(set: &[Value]) -> Result<Self> {
|
pub fn new(set: HashSet<Value>) -> Result<Self> {
|
||||||
let kvs: HashMap<Value, Value> = set
|
let kvs_raw: HashMap<RawValue, RawValue> = set
|
||||||
.iter()
|
.iter()
|
||||||
.map(|e| {
|
.map(|e| {
|
||||||
let h = hash_value(e);
|
let h = hash_value(&e.raw());
|
||||||
(Value::from(h), EMPTY_VALUE)
|
(RawValue::from(h), EMPTY_VALUE)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
mt: MerkleTree::new(MAX_DEPTH, &kvs)?,
|
mt: MerkleTree::new(MAX_DEPTH, &kvs_raw)?,
|
||||||
|
set,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn commitment(&self) -> Hash {
|
pub fn commitment(&self) -> Hash {
|
||||||
self.mt.root()
|
self.mt.root()
|
||||||
}
|
}
|
||||||
pub fn contains(&self, value: &Value) -> Result<bool> {
|
pub fn contains(&self, value: &Value) -> bool {
|
||||||
self.mt.contains(value)
|
self.set.contains(value)
|
||||||
}
|
}
|
||||||
pub fn prove(&self, value: &Value) -> Result<MerkleProof> {
|
pub fn prove(&self, value: &Value) -> Result<MerkleProof> {
|
||||||
let (_, proof) = self.mt.prove(value)?;
|
let h = hash_value(&value.raw());
|
||||||
|
let (_, proof) = self.mt.prove(&RawValue::from(h))?;
|
||||||
Ok(proof)
|
Ok(proof)
|
||||||
}
|
}
|
||||||
pub fn prove_nonexistence(&self, value: &Value) -> Result<MerkleProof> {
|
pub fn prove_nonexistence(&self, value: &Value) -> Result<MerkleProof> {
|
||||||
self.mt.prove_nonexistence(value)
|
let h = hash_value(&value.raw());
|
||||||
|
self.mt.prove_nonexistence(&RawValue::from(h))
|
||||||
}
|
}
|
||||||
pub fn verify(root: Hash, proof: &MerkleProof, value: &Value) -> Result<()> {
|
pub fn verify(root: Hash, proof: &MerkleProof, value: &Value) -> Result<()> {
|
||||||
MerkleTree::verify(MAX_DEPTH, root, proof, value, &EMPTY_VALUE)
|
let h = hash_value(&value.raw());
|
||||||
|
MerkleTree::verify(MAX_DEPTH, root, proof, &RawValue::from(h), &EMPTY_VALUE)
|
||||||
}
|
}
|
||||||
pub fn verify_nonexistence(root: Hash, proof: &MerkleProof, value: &Value) -> Result<()> {
|
pub fn verify_nonexistence(root: Hash, proof: &MerkleProof, value: &Value) -> Result<()> {
|
||||||
MerkleTree::verify_nonexistence(MAX_DEPTH, root, proof, value)
|
let h = hash_value(&value.raw());
|
||||||
|
MerkleTree::verify_nonexistence(MAX_DEPTH, root, proof, &RawValue::from(h))
|
||||||
}
|
}
|
||||||
pub fn iter(&self) -> TreeIter {
|
pub fn set(&self) -> &HashSet<Value> {
|
||||||
self.mt.iter()
|
&self.set
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -124,34 +141,46 @@ impl Eq for Set {}
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Array {
|
pub struct Array {
|
||||||
mt: MerkleTree,
|
mt: MerkleTree,
|
||||||
|
array: Vec<Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Array {
|
impl Array {
|
||||||
pub fn new(array: &[Value]) -> Result<Self> {
|
pub fn new(array: Vec<Value>) -> Result<Self> {
|
||||||
let kvs: HashMap<Value, Value> = array
|
let kvs_raw: HashMap<RawValue, RawValue> = array
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, &e)| (Value::from(i as i64), e))
|
.map(|(i, e)| (RawValue::from(i as i64), e.raw()))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
mt: MerkleTree::new(MAX_DEPTH, &kvs)?,
|
mt: MerkleTree::new(MAX_DEPTH, &kvs_raw)?,
|
||||||
|
array,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn commitment(&self) -> Hash {
|
pub fn commitment(&self) -> Hash {
|
||||||
self.mt.root()
|
self.mt.root()
|
||||||
}
|
}
|
||||||
pub fn get(&self, i: usize) -> Result<Value> {
|
pub fn get(&self, i: usize) -> Result<&Value> {
|
||||||
self.mt.get(&Value::from(i as i64))
|
self.array
|
||||||
|
.get(i)
|
||||||
|
.ok_or_else(|| anyhow!("index {} out of bounds 0..{}", i, self.array.len()))
|
||||||
}
|
}
|
||||||
pub fn prove(&self, i: usize) -> Result<(Value, MerkleProof)> {
|
pub fn prove(&self, i: usize) -> Result<(&Value, MerkleProof)> {
|
||||||
self.mt.prove(&Value::from(i as i64))
|
let (_, mtp) = self.mt.prove(&RawValue::from(i as i64))?;
|
||||||
|
let value = self.array.get(i).expect("valid index");
|
||||||
|
Ok((value, mtp))
|
||||||
}
|
}
|
||||||
pub fn verify(root: Hash, proof: &MerkleProof, i: usize, value: &Value) -> Result<()> {
|
pub fn verify(root: Hash, proof: &MerkleProof, i: usize, value: &Value) -> Result<()> {
|
||||||
MerkleTree::verify(MAX_DEPTH, root, proof, &Value::from(i as i64), value)
|
MerkleTree::verify(
|
||||||
|
MAX_DEPTH,
|
||||||
|
root,
|
||||||
|
proof,
|
||||||
|
&RawValue::from(i as i64),
|
||||||
|
&value.raw(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
pub fn iter(&self) -> TreeIter {
|
pub fn array(&self) -> &[Value] {
|
||||||
self.mt.iter()
|
&self.array
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,101 +1,85 @@
|
||||||
use std::{collections::HashMap, fmt, hash as h, iter, iter::zip, sync::Arc};
|
use std::{fmt, iter, sync::Arc};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use plonky2::field::types::Field;
|
use plonky2::field::types::Field;
|
||||||
use schemars::JsonSchema;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
|
// use schemars::JsonSchema;
|
||||||
|
|
||||||
|
// use serde::{Deserialize, Serialize};
|
||||||
use crate::{
|
use crate::{
|
||||||
backends::plonky2::basetypes::HASH_SIZE,
|
middleware::HASH_SIZE,
|
||||||
middleware::{
|
middleware::{hash_fields, Hash, Key, NativePredicate, Params, ToFields, Value, F},
|
||||||
hash_fields, AnchoredKey, Hash, NativePredicate, Params, PodId, Statement, StatementArg,
|
|
||||||
ToFields, Value, F,
|
|
||||||
},
|
|
||||||
util::hashmap_insert_no_dupe,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// BEGIN Custom 1b
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct Wildcard {
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, h::Hash, Serialize, Deserialize, JsonSchema)]
|
pub name: String,
|
||||||
pub enum HashOrWildcard {
|
pub index: usize,
|
||||||
Hash(Hash),
|
|
||||||
Wildcard(usize),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HashOrWildcard {
|
impl Wildcard {
|
||||||
/// Matches a hash or wildcard against a value, returning a pair
|
pub fn new(name: String, index: usize) -> Self {
|
||||||
/// representing a wildcard binding (if any) or an error if no
|
Self { name, index }
|
||||||
/// match is possible.
|
|
||||||
pub fn match_against(&self, v: &Value) -> Result<Option<(usize, Value)>> {
|
|
||||||
match self {
|
|
||||||
HashOrWildcard::Hash(h) if &Value::from(*h) == v => Ok(None),
|
|
||||||
HashOrWildcard::Wildcard(i) => Ok(Some((*i, *v))),
|
|
||||||
_ => Err(anyhow!(
|
|
||||||
"Failed to match hash or wildcard {} against value {}.",
|
|
||||||
self,
|
|
||||||
v
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for HashOrWildcard {
|
impl fmt::Display for Wildcard {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "*{}[{}]", self.index, self.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToFields for Wildcard {
|
||||||
|
fn to_fields(&self, _params: &Params) -> Vec<F> {
|
||||||
|
vec![F::from_canonical_u64(self.index as u64)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum KeyOrWildcard {
|
||||||
|
Key(Key),
|
||||||
|
Wildcard(Wildcard),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for KeyOrWildcard {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Self::Hash(h) => write!(f, "{}", h),
|
Self::Key(k) => write!(f, "{}", k),
|
||||||
Self::Wildcard(n) => write!(f, "*{}", n),
|
Self::Wildcard(wc) => write!(f, "{}", wc),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToFields for HashOrWildcard {
|
impl ToFields for KeyOrWildcard {
|
||||||
fn to_fields(&self, params: &Params) -> Vec<F> {
|
fn to_fields(&self, params: &Params) -> Vec<F> {
|
||||||
match self {
|
match self {
|
||||||
HashOrWildcard::Hash(h) => h.to_fields(params),
|
KeyOrWildcard::Key(k) => k.hash().to_fields(params),
|
||||||
HashOrWildcard::Wildcard(w) => (0..HASH_SIZE - 1)
|
KeyOrWildcard::Wildcard(wc) => iter::once(F::ZERO)
|
||||||
.chain(iter::once(*w))
|
.take(HASH_SIZE - 1)
|
||||||
.map(|x| F::from_canonical_u64(x as u64))
|
.chain(iter::once(F::from_canonical_u64(wc.index as u64)))
|
||||||
.collect(),
|
.collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, h::Hash, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum StatementTmplArg {
|
pub enum StatementTmplArg {
|
||||||
None,
|
None,
|
||||||
Literal(Value),
|
Literal(Value),
|
||||||
Key(HashOrWildcard, HashOrWildcard),
|
// AnchoredKey
|
||||||
}
|
Key(Wildcard, KeyOrWildcard),
|
||||||
|
// TODO: This naming is a bit confusing: a WildcardLiteral that contains a Wildcard...
|
||||||
impl StatementTmplArg {
|
// Could we merge WildcardValue and Value and allow wildcard value apart from pod_id and key?
|
||||||
/// Matches a statement template argument against a statement
|
WildcardLiteral(Wildcard),
|
||||||
/// 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(PodId(o), k))) => {
|
|
||||||
let o_corr = tmpl_o.match_against(&(*o).into())?;
|
|
||||||
let k_corr = tmpl_k.match_against(&(*k).into())?;
|
|
||||||
Ok([o_corr, k_corr].into_iter().flatten().collect())
|
|
||||||
}
|
|
||||||
_ => Err(anyhow!(
|
|
||||||
"Failed to match statement template argument {:?} against statement argument {:?}.",
|
|
||||||
self,
|
|
||||||
s_arg
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToFields for StatementTmplArg {
|
impl ToFields for StatementTmplArg {
|
||||||
fn to_fields(&self, params: &Params) -> Vec<F> {
|
fn to_fields(&self, params: &Params) -> Vec<F> {
|
||||||
// None => (0, ...)
|
// None => (0, ...)
|
||||||
// Literal(value) => (1, [value], 0, 0, 0, 0)
|
// Literal(value) => (1, [value], 0, 0, 0, 0)
|
||||||
// Key(hash_or_wildcard1, hash_or_wildcard2)
|
// Key(wildcard1, key_or_wildcard2)
|
||||||
// => (2, [hash_or_wildcard1], [hash_or_wildcard2])
|
// => (2, [wildcard1], [key_or_wildcard2])
|
||||||
|
// WildcardLiteral(wildcard) => (3, [wildcard], 0, 0, 0, 0)
|
||||||
// In all three cases, we pad to 2 * hash_size + 1 = 9 field elements
|
// In all three cases, we pad to 2 * hash_size + 1 = 9 field elements
|
||||||
let statement_tmpl_arg_size = 2 * HASH_SIZE + 1;
|
let statement_tmpl_arg_size = 2 * HASH_SIZE + 1;
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -107,15 +91,22 @@ impl ToFields for StatementTmplArg {
|
||||||
}
|
}
|
||||||
StatementTmplArg::Literal(v) => {
|
StatementTmplArg::Literal(v) => {
|
||||||
let fields: Vec<F> = iter::once(F::from_canonical_u64(1))
|
let fields: Vec<F> = iter::once(F::from_canonical_u64(1))
|
||||||
.chain(v.to_fields(params))
|
.chain(v.raw().to_fields(params))
|
||||||
.chain(iter::repeat_with(|| F::from_canonical_u64(0)).take(HASH_SIZE))
|
.chain(iter::repeat_with(|| F::from_canonical_u64(0)).take(HASH_SIZE))
|
||||||
.collect();
|
.collect();
|
||||||
fields
|
fields
|
||||||
}
|
}
|
||||||
StatementTmplArg::Key(hw1, hw2) => {
|
StatementTmplArg::Key(wc1, kw2) => {
|
||||||
let fields: Vec<F> = iter::once(F::from_canonical_u64(2))
|
let fields: Vec<F> = iter::once(F::from_canonical_u64(2))
|
||||||
.chain(hw1.to_fields(params))
|
.chain(wc1.to_fields(params))
|
||||||
.chain(hw2.to_fields(params))
|
.chain(kw2.to_fields(params))
|
||||||
|
.collect();
|
||||||
|
fields
|
||||||
|
}
|
||||||
|
StatementTmplArg::WildcardLiteral(wc) => {
|
||||||
|
let fields: Vec<F> = iter::once(F::from_canonical_u64(3))
|
||||||
|
.chain(wc.to_fields(params))
|
||||||
|
.chain(iter::repeat_with(|| F::from_canonical_u64(0)).take(HASH_SIZE))
|
||||||
.collect();
|
.collect();
|
||||||
fields
|
fields
|
||||||
}
|
}
|
||||||
|
|
@ -129,50 +120,37 @@ impl fmt::Display for StatementTmplArg {
|
||||||
Self::None => write!(f, "none"),
|
Self::None => write!(f, "none"),
|
||||||
Self::Literal(v) => write!(f, "{}", v),
|
Self::Literal(v) => write!(f, "{}", v),
|
||||||
Self::Key(pod_id, key) => write!(f, "({}, {})", pod_id, key),
|
Self::Key(pod_id, key) => write!(f, "({}, {})", pod_id, key),
|
||||||
|
Self::WildcardLiteral(v) => write!(f, "{}", v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// END
|
|
||||||
|
|
||||||
// BEGIN Custom 2
|
|
||||||
|
|
||||||
// pub enum StatementTmplArg {
|
|
||||||
// None,
|
|
||||||
// Literal(Value),
|
|
||||||
// Wildcard(usize),
|
|
||||||
// }
|
|
||||||
|
|
||||||
// END
|
|
||||||
|
|
||||||
/// Statement Template for a Custom Predicate
|
/// Statement Template for a Custom Predicate
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct StatementTmpl(pub Predicate, pub Vec<StatementTmplArg>);
|
pub struct StatementTmpl {
|
||||||
|
pub pred: Predicate,
|
||||||
|
pub args: Vec<StatementTmplArg>,
|
||||||
|
}
|
||||||
|
|
||||||
impl StatementTmpl {
|
impl StatementTmpl {
|
||||||
pub fn pred(&self) -> &Predicate {
|
pub fn pred(&self) -> &Predicate {
|
||||||
&self.0
|
&self.pred
|
||||||
}
|
}
|
||||||
pub fn args(&self) -> &[StatementTmplArg] {
|
pub fn args(&self) -> &[StatementTmplArg] {
|
||||||
&self.1
|
&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(P::BatchSelf(_), _)) {
|
|
||||||
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())
|
|
||||||
.map(|(t_arg, s_arg)| t_arg.match_against(&s_arg))
|
|
||||||
.collect::<Result<Vec<_>>>()
|
|
||||||
.map(|v| v.concat())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for StatementTmpl {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}(", self.pred)?;
|
||||||
|
for (i, arg) in self.args.iter().enumerate() {
|
||||||
|
if i != 0 {
|
||||||
|
write!(f, ", ")?;
|
||||||
|
}
|
||||||
|
write!(f, "{}", arg)?;
|
||||||
|
}
|
||||||
|
writeln!(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -185,25 +163,26 @@ impl ToFields for StatementTmpl {
|
||||||
// TODO think if this check should go into the StatementTmpl creation,
|
// TODO think if this check should go into the StatementTmpl creation,
|
||||||
// instead of at the `to_fields` method, where we should assume that the
|
// instead of at the `to_fields` method, where we should assume that the
|
||||||
// values are already valid
|
// values are already valid
|
||||||
if self.1.len() > params.max_statement_args {
|
if self.args.len() > params.max_statement_args {
|
||||||
panic!("Statement template has too many arguments");
|
panic!("Statement template has too many arguments");
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut fields: Vec<F> = self
|
let mut fields: Vec<F> = self
|
||||||
.0
|
.pred
|
||||||
.to_fields(params)
|
.to_fields(params)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(self.1.iter().flat_map(|sta| sta.to_fields(params)))
|
.chain(self.args.iter().flat_map(|sta| sta.to_fields(params)))
|
||||||
.collect();
|
.collect();
|
||||||
fields.resize_with(params.statement_tmpl_size(), || F::from_canonical_u64(0));
|
fields.resize_with(params.statement_tmpl_size(), || F::from_canonical_u64(0));
|
||||||
fields
|
fields
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
/// NOTE: fields are not public (outside of crate) to enforce the struct instantiation through
|
/// NOTE: fields are not public (outside of crate) to enforce the struct instantiation through
|
||||||
/// the `::and/or` methods, which performs checks on the values.
|
/// the `::and/or` methods, which performs checks on the values.
|
||||||
pub struct CustomPredicate {
|
pub struct CustomPredicate {
|
||||||
|
pub name: String, // Non-cryptographic metadata
|
||||||
/// true for "and", false for "or"
|
/// true for "and", false for "or"
|
||||||
pub(crate) conjunction: bool,
|
pub(crate) conjunction: bool,
|
||||||
pub(crate) statements: Vec<StatementTmpl>,
|
pub(crate) statements: Vec<StatementTmpl>,
|
||||||
|
|
@ -213,13 +192,24 @@ pub struct CustomPredicate {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CustomPredicate {
|
impl CustomPredicate {
|
||||||
pub fn and(params: &Params, statements: Vec<StatementTmpl>, args_len: usize) -> Result<Self> {
|
pub fn and(
|
||||||
Self::new(params, true, statements, args_len)
|
name: String,
|
||||||
|
params: &Params,
|
||||||
|
statements: Vec<StatementTmpl>,
|
||||||
|
args_len: usize,
|
||||||
|
) -> Result<Self> {
|
||||||
|
Self::new(name, params, true, statements, args_len)
|
||||||
}
|
}
|
||||||
pub fn or(params: &Params, statements: Vec<StatementTmpl>, args_len: usize) -> Result<Self> {
|
pub fn or(
|
||||||
Self::new(params, false, statements, args_len)
|
name: String,
|
||||||
|
params: &Params,
|
||||||
|
statements: Vec<StatementTmpl>,
|
||||||
|
args_len: usize,
|
||||||
|
) -> Result<Self> {
|
||||||
|
Self::new(name, params, false, statements, args_len)
|
||||||
}
|
}
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
name: String,
|
||||||
params: &Params,
|
params: &Params,
|
||||||
conjunction: bool,
|
conjunction: bool,
|
||||||
statements: Vec<StatementTmpl>,
|
statements: Vec<StatementTmpl>,
|
||||||
|
|
@ -230,6 +220,7 @@ impl CustomPredicate {
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
name,
|
||||||
conjunction,
|
conjunction,
|
||||||
statements,
|
statements,
|
||||||
args_len,
|
args_len,
|
||||||
|
|
@ -266,8 +257,8 @@ impl fmt::Display for CustomPredicate {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
writeln!(f, "{}<", if self.conjunction { "and" } else { "or" })?;
|
writeln!(f, "{}<", if self.conjunction { "and" } else { "or" })?;
|
||||||
for st in &self.statements {
|
for st in &self.statements {
|
||||||
write!(f, " {}", st.0)?;
|
write!(f, " {}(", st.pred)?;
|
||||||
for (i, arg) in st.1.iter().enumerate() {
|
for (i, arg) in st.args.iter().enumerate() {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
write!(f, ", ")?;
|
write!(f, ", ")?;
|
||||||
}
|
}
|
||||||
|
|
@ -287,7 +278,7 @@ impl fmt::Display for CustomPredicate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct CustomPredicateBatch {
|
pub struct CustomPredicateBatch {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub predicates: Vec<CustomPredicate>,
|
pub predicates: Vec<CustomPredicate>,
|
||||||
|
|
@ -324,67 +315,23 @@ impl CustomPredicateBatch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct CustomPredicateRef(pub Arc<CustomPredicateBatch>, pub usize);
|
pub struct CustomPredicateRef {
|
||||||
|
pub batch: Arc<CustomPredicateBatch>,
|
||||||
|
pub index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
impl CustomPredicateRef {
|
impl CustomPredicateRef {
|
||||||
|
pub fn new(batch: Arc<CustomPredicateBatch>, index: usize) -> Self {
|
||||||
|
Self { batch, index }
|
||||||
|
}
|
||||||
pub fn arg_len(&self) -> usize {
|
pub fn arg_len(&self) -> usize {
|
||||||
self.0.predicates[self.1].args_len
|
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.0.clone()).predicates[self.1];
|
|
||||||
CustomPredicate {
|
|
||||||
conjunction: cp.conjunction,
|
|
||||||
statements: cp
|
|
||||||
.statements
|
|
||||||
.iter()
|
|
||||||
.map(|StatementTmpl(p, args)| {
|
|
||||||
StatementTmpl(
|
|
||||||
match p {
|
|
||||||
Predicate::BatchSelf(i) => {
|
|
||||||
Predicate::Custom(CustomPredicateRef(self.0.clone(), *i))
|
|
||||||
}
|
|
||||||
_ => p.clone(),
|
|
||||||
},
|
|
||||||
args.to_vec(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
args_len: cp.args_len,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
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)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
#[serde(tag = "type", content = "value")]
|
// #[serde(tag = "type", content = "value")]
|
||||||
pub enum Predicate {
|
pub enum Predicate {
|
||||||
Native(NativePredicate),
|
Native(NativePredicate),
|
||||||
BatchSelf(usize),
|
BatchSelf(usize),
|
||||||
|
|
@ -414,10 +361,12 @@ impl ToFields for Predicate {
|
||||||
Self::BatchSelf(i) => iter::once(F::from_canonical_u64(2))
|
Self::BatchSelf(i) => iter::once(F::from_canonical_u64(2))
|
||||||
.chain(iter::once(F::from_canonical_usize(*i)))
|
.chain(iter::once(F::from_canonical_usize(*i)))
|
||||||
.collect(),
|
.collect(),
|
||||||
Self::Custom(CustomPredicateRef(pb, i)) => iter::once(F::from_canonical_u64(3))
|
Self::Custom(CustomPredicateRef { batch, index }) => {
|
||||||
.chain(pb.hash(params).0)
|
iter::once(F::from_canonical_u64(3))
|
||||||
.chain(iter::once(F::from_canonical_usize(*i)))
|
.chain(batch.hash(params).0)
|
||||||
.collect(),
|
.chain(iter::once(F::from_canonical_usize(*index)))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
fields.resize_with(Params::predicate_size(), || F::from_canonical_u64(0));
|
fields.resize_with(Params::predicate_size(), || F::from_canonical_u64(0));
|
||||||
fields
|
fields
|
||||||
|
|
@ -429,7 +378,13 @@ impl fmt::Display for Predicate {
|
||||||
match self {
|
match self {
|
||||||
Self::Native(p) => write!(f, "{:?}", p),
|
Self::Native(p) => write!(f, "{:?}", p),
|
||||||
Self::BatchSelf(i) => write!(f, "self.{}", i),
|
Self::BatchSelf(i) => write!(f, "self.{}", i),
|
||||||
Self::Custom(CustomPredicateRef(pb, i)) => write!(f, "{}.{}", pb.name, i),
|
Self::Custom(CustomPredicateRef { batch, index }) => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}.{}[{}]",
|
||||||
|
batch.name, index, batch.predicates[*index].name
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -441,18 +396,29 @@ mod tests {
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use plonky2::field::goldilocks_field::GoldilocksField;
|
use plonky2::field::goldilocks_field::GoldilocksField;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
use crate::middleware::{
|
use crate::middleware::{
|
||||||
AnchoredKey, CustomPredicate, CustomPredicateBatch, CustomPredicateRef, Hash,
|
AnchoredKey, CustomPredicate, CustomPredicateBatch, CustomPredicateRef, Hash,
|
||||||
HashOrWildcard, NativePredicate, Operation, Params, PodId, PodType, Predicate, Statement,
|
KeyOrWildcard, NativePredicate, Operation, Params, PodId, PodType, Predicate, Statement,
|
||||||
StatementTmpl, StatementTmplArg, SELF,
|
StatementTmpl, StatementTmplArg, WildcardValue, SELF,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn st(p: Predicate, args: Vec<StatementTmplArg>) -> StatementTmpl {
|
fn st(p: Predicate, args: Vec<StatementTmplArg>) -> StatementTmpl {
|
||||||
StatementTmpl(p, args)
|
StatementTmpl { pred: p, args }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn kow_wc(i: usize) -> KOW {
|
||||||
|
KOW::Wildcard(wc(i))
|
||||||
|
}
|
||||||
|
fn wc(i: usize) -> Wildcard {
|
||||||
|
Wildcard {
|
||||||
|
name: format!("{}", i),
|
||||||
|
index: i,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type STA = StatementTmplArg;
|
type STA = StatementTmplArg;
|
||||||
type HOW = HashOrWildcard;
|
type KOW = KeyOrWildcard;
|
||||||
type P = Predicate;
|
type P = Predicate;
|
||||||
type NP = NativePredicate;
|
type NP = NativePredicate;
|
||||||
|
|
||||||
|
|
@ -468,44 +434,42 @@ mod tests {
|
||||||
let cust_pred_batch = Arc::new(CustomPredicateBatch {
|
let cust_pred_batch = Arc::new(CustomPredicateBatch {
|
||||||
name: "is_double".to_string(),
|
name: "is_double".to_string(),
|
||||||
predicates: vec![CustomPredicate::and(
|
predicates: vec![CustomPredicate::and(
|
||||||
|
"_".into(),
|
||||||
¶ms,
|
¶ms,
|
||||||
vec![
|
vec![
|
||||||
st(
|
st(
|
||||||
P::Native(NP::ValueOf),
|
P::Native(NP::ValueOf),
|
||||||
vec![
|
vec![STA::Key(wc(4), kow_wc(5)), STA::Literal(2.into())],
|
||||||
STA::Key(HOW::Wildcard(4), HOW::Wildcard(5)),
|
|
||||||
STA::Literal(2.into()),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
st(
|
st(
|
||||||
P::Native(NP::ProductOf),
|
P::Native(NP::ProductOf),
|
||||||
vec![
|
vec![
|
||||||
STA::Key(HOW::Wildcard(0), HOW::Wildcard(1)),
|
STA::Key(wc(0), kow_wc(1)),
|
||||||
STA::Key(HOW::Wildcard(4), HOW::Wildcard(5)),
|
STA::Key(wc(4), kow_wc(5)),
|
||||||
STA::Key(HOW::Wildcard(2), HOW::Wildcard(3)),
|
STA::Key(wc(2), kow_wc(3)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
4,
|
2,
|
||||||
)?],
|
)?],
|
||||||
});
|
});
|
||||||
|
|
||||||
let custom_statement = Statement::Custom(
|
let custom_statement = Statement::Custom(
|
||||||
CustomPredicateRef(cust_pred_batch.clone(), 0),
|
CustomPredicateRef::new(cust_pred_batch.clone(), 0),
|
||||||
vec![
|
vec![
|
||||||
AnchoredKey(SELF, "Some value".into()),
|
WildcardValue::PodId(SELF),
|
||||||
AnchoredKey(SELF, "Some other value".into()),
|
WildcardValue::Key(Key::from("Some value")),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
let custom_deduction = Operation::Custom(
|
let custom_deduction = Operation::Custom(
|
||||||
CustomPredicateRef(cust_pred_batch, 0),
|
CustomPredicateRef::new(cust_pred_batch, 0),
|
||||||
vec![
|
vec![
|
||||||
Statement::ValueOf(AnchoredKey(SELF, "Some constant".into()), 2.into()),
|
Statement::ValueOf(AnchoredKey::from((SELF, "Some constant")), 2.into()),
|
||||||
Statement::ProductOf(
|
Statement::ProductOf(
|
||||||
AnchoredKey(SELF, "Some value".into()),
|
AnchoredKey::from((SELF, "Some value")),
|
||||||
AnchoredKey(SELF, "Some constant".into()),
|
AnchoredKey::from((SELF, "Some constant")),
|
||||||
AnchoredKey(SELF, "Some other value".into()),
|
AnchoredKey::from((SELF, "Some other value")),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
@ -517,30 +481,34 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ethdos_test() -> Result<()> {
|
fn ethdos_test() -> Result<()> {
|
||||||
let params = Params::default();
|
let params = Params {
|
||||||
|
max_custom_predicate_wildcards: 12,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
let eth_friend_cp = CustomPredicate::and(
|
let eth_friend_cp = CustomPredicate::and(
|
||||||
|
"eth_friend_cp".into(),
|
||||||
¶ms,
|
¶ms,
|
||||||
vec![
|
vec![
|
||||||
st(
|
st(
|
||||||
P::Native(NP::ValueOf),
|
P::Native(NP::ValueOf),
|
||||||
vec![
|
vec![
|
||||||
STA::Key(HOW::Wildcard(4), HashOrWildcard::Hash("type".into())),
|
STA::Key(wc(4), KeyOrWildcard::Key("type".into())),
|
||||||
STA::Literal(PodType::Signed.into()),
|
STA::Literal(PodType::Signed.into()),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
st(
|
st(
|
||||||
P::Native(NP::Equal),
|
P::Native(NP::Equal),
|
||||||
vec![
|
vec![
|
||||||
STA::Key(HOW::Wildcard(4), HashOrWildcard::Hash("signer".into())),
|
STA::Key(wc(4), KeyOrWildcard::Key("signer".into())),
|
||||||
STA::Key(HOW::Wildcard(0), HOW::Wildcard(1)),
|
STA::Key(wc(0), kow_wc(1)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
st(
|
st(
|
||||||
P::Native(NP::Equal),
|
P::Native(NP::Equal),
|
||||||
vec![
|
vec![
|
||||||
STA::Key(HOW::Wildcard(4), HashOrWildcard::Hash("attestation".into())),
|
STA::Key(wc(4), KeyOrWildcard::Key("attestation".into())),
|
||||||
STA::Key(HOW::Wildcard(2), HOW::Wildcard(3)),
|
STA::Key(wc(2), kow_wc(3)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -552,81 +520,89 @@ mod tests {
|
||||||
predicates: vec![eth_friend_cp],
|
predicates: vec![eth_friend_cp],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 0
|
||||||
let eth_dos_base = CustomPredicate::and(
|
let eth_dos_base = CustomPredicate::and(
|
||||||
|
"eth_dos_base".into(),
|
||||||
¶ms,
|
¶ms,
|
||||||
vec![
|
vec![
|
||||||
st(
|
st(
|
||||||
P::Native(NP::Equal),
|
P::Native(NP::Equal),
|
||||||
vec![
|
vec![STA::Key(wc(0), kow_wc(1)), STA::Key(wc(2), kow_wc(3))],
|
||||||
STA::Key(HOW::Wildcard(0), HOW::Wildcard(1)),
|
|
||||||
STA::Key(HOW::Wildcard(2), HOW::Wildcard(3)),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
st(
|
st(
|
||||||
P::Native(NP::ValueOf),
|
P::Native(NP::ValueOf),
|
||||||
vec![
|
vec![STA::Key(wc(4), kow_wc(5)), STA::Literal(0.into())],
|
||||||
STA::Key(HOW::Wildcard(4), HOW::Wildcard(5)),
|
|
||||||
STA::Literal(0.into()),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
6,
|
6,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// 1
|
||||||
let eth_dos_ind = CustomPredicate::and(
|
let eth_dos_ind = CustomPredicate::and(
|
||||||
|
"eth_dos_ind".into(),
|
||||||
¶ms,
|
¶ms,
|
||||||
vec![
|
vec![
|
||||||
st(
|
st(
|
||||||
P::BatchSelf(2),
|
P::BatchSelf(2),
|
||||||
vec![
|
vec![
|
||||||
STA::Key(HOW::Wildcard(0), HOW::Wildcard(1)),
|
STA::WildcardLiteral(wc(0)),
|
||||||
STA::Key(HOW::Wildcard(10), HOW::Wildcard(11)),
|
STA::WildcardLiteral(wc(1)),
|
||||||
STA::Key(HOW::Wildcard(8), HOW::Wildcard(9)),
|
STA::WildcardLiteral(wc(10)),
|
||||||
|
STA::WildcardLiteral(wc(11)),
|
||||||
|
STA::WildcardLiteral(wc(8)),
|
||||||
|
STA::WildcardLiteral(wc(9)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
st(
|
st(
|
||||||
P::Native(NP::ValueOf),
|
P::Native(NP::ValueOf),
|
||||||
vec![
|
vec![STA::Key(wc(6), kow_wc(7)), STA::Literal(1.into())],
|
||||||
STA::Key(HOW::Wildcard(6), HOW::Wildcard(7)),
|
|
||||||
STA::Literal(1.into()),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
st(
|
st(
|
||||||
P::Native(NP::SumOf),
|
P::Native(NP::SumOf),
|
||||||
vec![
|
vec![
|
||||||
STA::Key(HOW::Wildcard(4), HOW::Wildcard(5)),
|
STA::Key(wc(4), kow_wc(5)),
|
||||||
STA::Key(HOW::Wildcard(8), HOW::Wildcard(9)),
|
STA::Key(wc(8), kow_wc(9)),
|
||||||
STA::Key(HOW::Wildcard(6), HOW::Wildcard(7)),
|
STA::Key(wc(6), kow_wc(7)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
st(
|
st(
|
||||||
P::Custom(CustomPredicateRef(eth_friend_batch.clone(), 0)),
|
P::Custom(CustomPredicateRef::new(eth_friend_batch.clone(), 0)),
|
||||||
vec![
|
vec![
|
||||||
STA::Key(HOW::Wildcard(10), HOW::Wildcard(11)),
|
STA::WildcardLiteral(wc(10)),
|
||||||
STA::Key(HOW::Wildcard(2), HOW::Wildcard(3)),
|
STA::WildcardLiteral(wc(11)),
|
||||||
|
STA::WildcardLiteral(wc(2)),
|
||||||
|
STA::WildcardLiteral(wc(3)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
6,
|
6,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// 2
|
||||||
let eth_dos_distance_either = CustomPredicate::or(
|
let eth_dos_distance_either = CustomPredicate::or(
|
||||||
|
"eth_dos_distance_either".into(),
|
||||||
¶ms,
|
¶ms,
|
||||||
vec![
|
vec![
|
||||||
st(
|
st(
|
||||||
P::BatchSelf(0),
|
P::BatchSelf(0),
|
||||||
vec![
|
vec![
|
||||||
STA::Key(HOW::Wildcard(0), HOW::Wildcard(1)),
|
STA::WildcardLiteral(wc(0)),
|
||||||
STA::Key(HOW::Wildcard(2), HOW::Wildcard(3)),
|
STA::WildcardLiteral(wc(1)),
|
||||||
STA::Key(HOW::Wildcard(4), HOW::Wildcard(5)),
|
STA::WildcardLiteral(wc(2)),
|
||||||
|
STA::WildcardLiteral(wc(3)),
|
||||||
|
STA::WildcardLiteral(wc(4)),
|
||||||
|
STA::WildcardLiteral(wc(5)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
st(
|
st(
|
||||||
P::BatchSelf(1),
|
P::BatchSelf(1),
|
||||||
vec![
|
vec![
|
||||||
STA::Key(HOW::Wildcard(0), HOW::Wildcard(1)),
|
STA::WildcardLiteral(wc(0)),
|
||||||
STA::Key(HOW::Wildcard(2), HOW::Wildcard(3)),
|
STA::WildcardLiteral(wc(1)),
|
||||||
STA::Key(HOW::Wildcard(4), HOW::Wildcard(5)),
|
STA::WildcardLiteral(wc(2)),
|
||||||
|
STA::WildcardLiteral(wc(3)),
|
||||||
|
STA::WildcardLiteral(wc(4)),
|
||||||
|
STA::WildcardLiteral(wc(5)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -646,11 +622,14 @@ mod tests {
|
||||||
|
|
||||||
// Example statement
|
// Example statement
|
||||||
let ethdos_example = Statement::Custom(
|
let ethdos_example = Statement::Custom(
|
||||||
CustomPredicateRef(eth_dos_distance_batch.clone(), 2),
|
CustomPredicateRef::new(eth_dos_distance_batch.clone(), 2),
|
||||||
vec![
|
vec![
|
||||||
AnchoredKey(pod_id1, "Alice".into()),
|
WildcardValue::PodId(pod_id1),
|
||||||
AnchoredKey(pod_id2, "Bob".into()),
|
WildcardValue::Key(Key::from("Alice")),
|
||||||
AnchoredKey(SELF, "Seven".into()),
|
WildcardValue::PodId(pod_id2),
|
||||||
|
WildcardValue::Key(Key::from("Bob")),
|
||||||
|
WildcardValue::PodId(SELF),
|
||||||
|
WildcardValue::Key(Key::from("Seven")),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -659,17 +638,20 @@ mod tests {
|
||||||
|
|
||||||
// This could arise as the inductive step.
|
// This could arise as the inductive step.
|
||||||
let ethdos_ind_example = Statement::Custom(
|
let ethdos_ind_example = Statement::Custom(
|
||||||
CustomPredicateRef(eth_dos_distance_batch.clone(), 1),
|
CustomPredicateRef::new(eth_dos_distance_batch.clone(), 1),
|
||||||
vec![
|
vec![
|
||||||
AnchoredKey(pod_id1, "Alice".into()),
|
WildcardValue::PodId(pod_id1),
|
||||||
AnchoredKey(pod_id2, "Bob".into()),
|
WildcardValue::Key(Key::from("Alice")),
|
||||||
AnchoredKey(SELF, "Seven".into()),
|
WildcardValue::PodId(pod_id2),
|
||||||
|
WildcardValue::Key(Key::from("Bob")),
|
||||||
|
WildcardValue::PodId(SELF),
|
||||||
|
WildcardValue::Key(Key::from("Seven")),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(Operation::Custom(
|
assert!(Operation::Custom(
|
||||||
CustomPredicateRef(eth_dos_distance_batch.clone(), 2),
|
CustomPredicateRef::new(eth_dos_distance_batch.clone(), 2),
|
||||||
vec![ethdos_ind_example.clone()]
|
vec![Statement::None, ethdos_ind_example.clone()]
|
||||||
)
|
)
|
||||||
.check(¶ms, ðdos_example)?);
|
.check(¶ms, ðdos_example)?);
|
||||||
|
|
||||||
|
|
@ -678,30 +660,35 @@ mod tests {
|
||||||
// less than 7, and Charlie is ETH-friends with Bob.
|
// less than 7, and Charlie is ETH-friends with Bob.
|
||||||
let ethdos_facts = vec![
|
let ethdos_facts = vec![
|
||||||
Statement::Custom(
|
Statement::Custom(
|
||||||
CustomPredicateRef(eth_dos_distance_batch.clone(), 2),
|
CustomPredicateRef::new(eth_dos_distance_batch.clone(), 2),
|
||||||
vec![
|
vec![
|
||||||
AnchoredKey(pod_id1, "Alice".into()),
|
WildcardValue::PodId(pod_id1),
|
||||||
AnchoredKey(pod_id3, "Charlie".into()),
|
WildcardValue::Key(Key::from("Alice")),
|
||||||
AnchoredKey(pod_id4, "Six".into()),
|
WildcardValue::PodId(pod_id3),
|
||||||
|
WildcardValue::Key(Key::from("Charlie")),
|
||||||
|
WildcardValue::PodId(pod_id4),
|
||||||
|
WildcardValue::Key(Key::from("Six")),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Statement::ValueOf(AnchoredKey(SELF, "One".into()), 1.into()),
|
Statement::ValueOf(AnchoredKey::from((SELF, "One")), 1.into()),
|
||||||
Statement::SumOf(
|
Statement::SumOf(
|
||||||
AnchoredKey(SELF, "Seven".into()),
|
AnchoredKey::from((SELF, "Seven")),
|
||||||
AnchoredKey(pod_id4, "Six".into()),
|
AnchoredKey::from((pod_id4, "Six")),
|
||||||
AnchoredKey(SELF, "One".into()),
|
AnchoredKey::from((SELF, "One")),
|
||||||
),
|
),
|
||||||
Statement::Custom(
|
Statement::Custom(
|
||||||
CustomPredicateRef(eth_friend_batch.clone(), 0),
|
CustomPredicateRef::new(eth_friend_batch.clone(), 0),
|
||||||
vec![
|
vec![
|
||||||
AnchoredKey(pod_id3, "Charlie".into()),
|
WildcardValue::PodId(pod_id3),
|
||||||
AnchoredKey(pod_id2, "Bob".into()),
|
WildcardValue::Key(Key::from("Charlie")),
|
||||||
|
WildcardValue::PodId(pod_id2),
|
||||||
|
WildcardValue::Key(Key::from("Bob")),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
assert!(Operation::Custom(
|
assert!(Operation::Custom(
|
||||||
CustomPredicateRef(eth_dos_distance_batch.clone(), 1),
|
CustomPredicateRef::new(eth_dos_distance_batch.clone(), 1),
|
||||||
ethdos_facts
|
ethdos_facts
|
||||||
)
|
)
|
||||||
.check(¶ms, ðdos_ind_example)?);
|
.check(¶ms, ðdos_ind_example)?);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,15 @@
|
||||||
//! The middleware includes the type definitions and the traits used to connect the frontend and
|
//! The middleware includes the type definitions and the traits used to connect the frontend and
|
||||||
//! the backend.
|
//! the backend.
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
mod basetypes;
|
mod basetypes;
|
||||||
|
use std::{
|
||||||
|
cmp::{Ordering, PartialEq, PartialOrd},
|
||||||
|
hash,
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::anyhow;
|
||||||
|
use containers::{Array, Dictionary, Set};
|
||||||
pub mod containers;
|
pub mod containers;
|
||||||
mod custom;
|
mod custom;
|
||||||
mod operation;
|
mod operation;
|
||||||
|
|
@ -14,12 +22,214 @@ pub use basetypes::*;
|
||||||
pub use custom::*;
|
pub use custom::*;
|
||||||
use dyn_clone::DynClone;
|
use dyn_clone::DynClone;
|
||||||
pub use operation::*;
|
pub use operation::*;
|
||||||
use schemars::JsonSchema;
|
// use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
// use serde::{Deserialize, Serialize};
|
||||||
pub use statement::*;
|
pub use statement::*;
|
||||||
|
|
||||||
pub const SELF: PodId = PodId(SELF_ID_HASH);
|
pub const SELF: PodId = PodId(SELF_ID_HASH);
|
||||||
|
|
||||||
|
// TODO: Move all value-related types to to `value.rs`
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
// TODO #[schemars(transform = serialization::transform_value_schema)]
|
||||||
|
pub enum TypedValue {
|
||||||
|
// Serde cares about the order of the enum variants, with untagged variants
|
||||||
|
// appearing at the end.
|
||||||
|
// Variants without "untagged" will be serialized as "tagged" values by
|
||||||
|
// default, meaning that a Set appears in JSON as {"Set":[...]}
|
||||||
|
// and not as [...]
|
||||||
|
// Arrays, Strings and Booleans are untagged, as there is a natural JSON
|
||||||
|
// representation for them that is unambiguous to deserialize and is fully
|
||||||
|
// compatible with the semantics of the POD types.
|
||||||
|
// As JSON integers do not specify precision, and JavaScript is limited to
|
||||||
|
// 53-bit precision for integers, integers are represented as tagged
|
||||||
|
// strings, with a custom serializer and deserializer.
|
||||||
|
// TAGGED TYPES:
|
||||||
|
Set(Set),
|
||||||
|
Dictionary(Dictionary),
|
||||||
|
Int(
|
||||||
|
// TODO #[serde(serialize_with = "serialize_i64", deserialize_with = "deserialize_i64")]
|
||||||
|
// #[schemars(with = "String", regex(pattern = r"^\d+$"))]
|
||||||
|
i64,
|
||||||
|
),
|
||||||
|
// Uses the serialization for middleware::Value:
|
||||||
|
Raw(RawValue),
|
||||||
|
// UNTAGGED TYPES:
|
||||||
|
// #[serde(untagged)]
|
||||||
|
// #[schemars(skip)]
|
||||||
|
Array(Array),
|
||||||
|
// #[serde(untagged)]
|
||||||
|
// #[schemars(skip)]
|
||||||
|
String(String),
|
||||||
|
// #[serde(untagged)]
|
||||||
|
// #[schemars(skip)]
|
||||||
|
Bool(bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&str> for TypedValue {
|
||||||
|
fn from(s: &str) -> Self {
|
||||||
|
TypedValue::String(s.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for TypedValue {
|
||||||
|
fn from(s: String) -> Self {
|
||||||
|
TypedValue::String(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i64> for TypedValue {
|
||||||
|
fn from(v: i64) -> Self {
|
||||||
|
TypedValue::Int(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<bool> for TypedValue {
|
||||||
|
fn from(b: bool) -> Self {
|
||||||
|
TypedValue::Bool(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Hash> for TypedValue {
|
||||||
|
fn from(h: Hash) -> Self {
|
||||||
|
TypedValue::Raw(RawValue(h.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Set> for TypedValue {
|
||||||
|
fn from(s: Set) -> Self {
|
||||||
|
TypedValue::Set(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Dictionary> for TypedValue {
|
||||||
|
fn from(d: Dictionary) -> Self {
|
||||||
|
TypedValue::Dictionary(d)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Array> for TypedValue {
|
||||||
|
fn from(a: Array) -> Self {
|
||||||
|
TypedValue::Array(a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RawValue> for TypedValue {
|
||||||
|
fn from(v: RawValue) -> Self {
|
||||||
|
TypedValue::Raw(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PodType> for TypedValue {
|
||||||
|
fn from(t: PodType) -> Self {
|
||||||
|
TypedValue::from(t as i64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&TypedValue> for i64 {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
fn try_from(v: &TypedValue) -> std::result::Result<Self, Self::Error> {
|
||||||
|
if let TypedValue::Int(n) = v {
|
||||||
|
Ok(*n)
|
||||||
|
} else {
|
||||||
|
Err(anyhow!("Value not an int"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for TypedValue {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
TypedValue::String(s) => write!(f, "\"{}\"", s),
|
||||||
|
TypedValue::Int(v) => write!(f, "{}", v),
|
||||||
|
TypedValue::Bool(b) => write!(f, "{}", b),
|
||||||
|
TypedValue::Dictionary(d) => write!(f, "dict:{}", d.commitment()),
|
||||||
|
TypedValue::Set(s) => write!(f, "set:{}", s.commitment()),
|
||||||
|
TypedValue::Array(a) => write!(f, "arr:{}", a.commitment()),
|
||||||
|
TypedValue::Raw(v) => write!(f, "{}", v),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&TypedValue> for RawValue {
|
||||||
|
fn from(v: &TypedValue) -> Self {
|
||||||
|
match v {
|
||||||
|
TypedValue::String(s) => RawValue::from(hash_str(s)),
|
||||||
|
TypedValue::Int(v) => RawValue::from(*v),
|
||||||
|
TypedValue::Bool(b) => RawValue::from(*b as i64),
|
||||||
|
TypedValue::Dictionary(d) => RawValue::from(d.commitment()),
|
||||||
|
TypedValue::Set(s) => RawValue::from(s.commitment()),
|
||||||
|
TypedValue::Array(a) => RawValue::from(a.commitment()),
|
||||||
|
TypedValue::Raw(v) => *v,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Value {
|
||||||
|
// The `TypedValue` is under `Arc` so that cloning a `Value` is cheap.
|
||||||
|
typed: Arc<TypedValue>,
|
||||||
|
raw: RawValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for Value {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.raw == other.raw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for Value {}
|
||||||
|
|
||||||
|
impl PartialOrd for Value {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
Some(self.raw.cmp(&other.raw))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Value {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
self.raw.cmp(&other.raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl hash::Hash for Value {
|
||||||
|
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.raw.hash(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Value {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.typed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Value {
|
||||||
|
pub fn new(value: TypedValue) -> Self {
|
||||||
|
let raw_value = RawValue::from(&value);
|
||||||
|
Self {
|
||||||
|
typed: Arc::new(value),
|
||||||
|
raw: raw_value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn typed(&self) -> &TypedValue {
|
||||||
|
&self.typed
|
||||||
|
}
|
||||||
|
pub fn raw(&self) -> RawValue {
|
||||||
|
self.raw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Value can be created from any type Into<TypedValue> type: bool, string-like, i64, ...
|
||||||
|
impl<T> From<T> for Value
|
||||||
|
where
|
||||||
|
T: Into<TypedValue>,
|
||||||
|
{
|
||||||
|
fn from(t: T) -> Self {
|
||||||
|
Self::new(t.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for PodId {
|
impl fmt::Display for PodId {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
if *self == SELF {
|
if *self == SELF {
|
||||||
|
|
@ -32,30 +242,93 @@ impl fmt::Display for PodId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// AnchoredKey is a tuple containing (OriginId: PodId, key: Hash)
|
impl From<&Value> for Hash {
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
fn from(v: &Value) -> Self {
|
||||||
pub struct AnchoredKey(pub PodId, pub Hash);
|
Self(v.raw.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Key {
|
||||||
|
name: String,
|
||||||
|
hash: Hash,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Key {
|
||||||
|
pub fn new(name: String) -> Self {
|
||||||
|
let hash = hash_str(&name);
|
||||||
|
Self { name, hash }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
pub fn hash(&self) -> Hash {
|
||||||
|
self.hash
|
||||||
|
}
|
||||||
|
pub fn raw(&self) -> RawValue {
|
||||||
|
RawValue(self.hash.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Key can easily be created from a string-like type
|
||||||
|
impl<T> From<T> for Key
|
||||||
|
where
|
||||||
|
T: Into<String>,
|
||||||
|
{
|
||||||
|
fn from(t: T) -> Self {
|
||||||
|
Self::new(t.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToFields for Key {
|
||||||
|
fn to_fields(&self, params: &Params) -> Vec<F> {
|
||||||
|
self.hash.to_fields(params)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Key {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.name)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Key> for RawValue {
|
||||||
|
fn from(key: Key) -> RawValue {
|
||||||
|
RawValue(key.hash.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct AnchoredKey {
|
||||||
|
pub pod_id: PodId,
|
||||||
|
pub key: Key,
|
||||||
|
}
|
||||||
|
|
||||||
impl AnchoredKey {
|
impl AnchoredKey {
|
||||||
pub fn origin(&self) -> PodId {
|
pub fn new(pod_id: PodId, key: Key) -> Self {
|
||||||
self.0
|
Self { pod_id, key }
|
||||||
}
|
|
||||||
pub fn key(&self) -> Hash {
|
|
||||||
self.1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for AnchoredKey {
|
impl fmt::Display for AnchoredKey {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(f, "{}.{}", self.0, self.1)?;
|
write!(f, "{}.{}", self.pod_id, self.key)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An entry consists of a key-value pair.
|
impl<T> From<(PodId, T)> for AnchoredKey
|
||||||
pub type Entry = (String, Value);
|
where
|
||||||
|
T: Into<Key>,
|
||||||
|
{
|
||||||
|
fn from((pod_id, t): (PodId, T)) -> Self {
|
||||||
|
Self::new(pod_id, t.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
|
||||||
pub struct PodId(pub Hash);
|
pub struct PodId(pub Hash);
|
||||||
|
|
||||||
impl ToFields for PodId {
|
impl ToFields for PodId {
|
||||||
|
|
@ -72,6 +345,7 @@ pub enum PodType {
|
||||||
Signed = 3,
|
Signed = 3,
|
||||||
Main = 4,
|
Main = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for PodType {
|
impl fmt::Display for PodType {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -84,13 +358,7 @@ impl fmt::Display for PodType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<PodType> for Value {
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
fn from(v: PodType) -> Self {
|
|
||||||
Value::from(v as i64)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
pub struct Params {
|
pub struct Params {
|
||||||
pub max_input_signed_pods: usize,
|
pub max_input_signed_pods: usize,
|
||||||
pub max_input_main_pods: usize,
|
pub max_input_main_pods: usize,
|
||||||
|
|
@ -102,6 +370,7 @@ pub struct Params {
|
||||||
// max number of statements that can be ANDed or ORed together
|
// max number of statements that can be ANDed or ORed together
|
||||||
// in a custom predicate
|
// in a custom predicate
|
||||||
pub max_custom_predicate_arity: usize,
|
pub max_custom_predicate_arity: usize,
|
||||||
|
pub max_custom_predicate_wildcards: usize,
|
||||||
pub max_custom_batch_size: usize,
|
pub max_custom_batch_size: usize,
|
||||||
// maximum number of merkle proofs
|
// maximum number of merkle proofs
|
||||||
pub max_merkle_proofs: usize,
|
pub max_merkle_proofs: usize,
|
||||||
|
|
@ -120,6 +389,7 @@ impl Default for Params {
|
||||||
max_statement_args: 5,
|
max_statement_args: 5,
|
||||||
max_operation_args: 5,
|
max_operation_args: 5,
|
||||||
max_custom_predicate_arity: 5,
|
max_custom_predicate_arity: 5,
|
||||||
|
max_custom_predicate_wildcards: 10,
|
||||||
max_custom_batch_size: 5,
|
max_custom_batch_size: 5,
|
||||||
max_merkle_proofs: 5,
|
max_merkle_proofs: 5,
|
||||||
max_depth_mt_gadget: 32,
|
max_depth_mt_gadget: 32,
|
||||||
|
|
@ -213,7 +483,7 @@ pub trait Pod: fmt::Debug + DynClone {
|
||||||
dyn_clone::clone_trait_object!(Pod);
|
dyn_clone::clone_trait_object!(Pod);
|
||||||
|
|
||||||
pub trait PodSigner {
|
pub trait PodSigner {
|
||||||
fn sign(&mut self, params: &Params, kvs: &HashMap<Hash, Value>) -> Result<Box<dyn Pod>>;
|
fn sign(&mut self, params: &Params, kvs: &HashMap<Key, Value>) -> Result<Box<dyn Pod>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is a filler type that fulfills the Pod trait and always verifies. It's empty. This
|
/// This is a filler type that fulfills the Pod trait and always verifies. It's empty. This
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,26 @@
|
||||||
use std::{fmt, iter};
|
use std::{fmt, iter, sync::Arc};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use log::error;
|
use log::error;
|
||||||
use plonky2::field::types::Field;
|
use plonky2::field::types::Field;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
|
// use serde::{Deserialize, Serialize};
|
||||||
use crate::{
|
use crate::{
|
||||||
backends::plonky2::primitives::merkletree::{MerkleProof, MerkleTree},
|
backends::plonky2::primitives::merkletree::{MerkleProof, MerkleTree},
|
||||||
middleware::{
|
middleware::{
|
||||||
AnchoredKey, CustomPredicateRef, NativePredicate, Params, Predicate, Statement,
|
custom::KeyOrWildcard, AnchoredKey, CustomPredicateBatch, CustomPredicateRef,
|
||||||
StatementArg, ToFields, Value, F, SELF,
|
NativePredicate, Params, Predicate, Statement, StatementArg, StatementTmplArg, ToFields,
|
||||||
|
Wildcard, WildcardValue, F, SELF,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum OperationType {
|
pub enum OperationType {
|
||||||
Native(NativeOperation),
|
Native(NativeOperation),
|
||||||
Custom(CustomPredicateRef),
|
Custom(CustomPredicateRef),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum OperationAux {
|
pub enum OperationAux {
|
||||||
None,
|
None,
|
||||||
MerkleProof(MerkleProof),
|
MerkleProof(MerkleProof),
|
||||||
|
|
@ -41,17 +42,19 @@ impl ToFields for OperationType {
|
||||||
Self::Native(p) => iter::once(F::from_canonical_u64(1))
|
Self::Native(p) => iter::once(F::from_canonical_u64(1))
|
||||||
.chain(p.to_fields(params))
|
.chain(p.to_fields(params))
|
||||||
.collect(),
|
.collect(),
|
||||||
Self::Custom(CustomPredicateRef(pb, i)) => iter::once(F::from_canonical_u64(3))
|
Self::Custom(CustomPredicateRef { batch, index }) => {
|
||||||
.chain(pb.hash(params).0)
|
iter::once(F::from_canonical_u64(3))
|
||||||
.chain(iter::once(F::from_canonical_usize(*i)))
|
.chain(batch.hash(params).0)
|
||||||
.collect(),
|
.chain(iter::once(F::from_canonical_usize(*index)))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
};
|
};
|
||||||
fields.resize_with(Params::operation_type_size(), || F::from_canonical_u64(0));
|
fields.resize_with(Params::operation_type_size(), || F::from_canonical_u64(0));
|
||||||
fields
|
fields
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum NativeOperation {
|
pub enum NativeOperation {
|
||||||
None = 0,
|
None = 0,
|
||||||
NewEntry = 1,
|
NewEntry = 1,
|
||||||
|
|
@ -68,6 +71,14 @@ pub enum NativeOperation {
|
||||||
SumOf = 13,
|
SumOf = 13,
|
||||||
ProductOf = 14,
|
ProductOf = 14,
|
||||||
MaxOf = 15,
|
MaxOf = 15,
|
||||||
|
|
||||||
|
// Syntactic sugar operations. These operations are not supported by the backend. The
|
||||||
|
// frontend compiler is responsible of translating these operations into the operations above.
|
||||||
|
DictContainsFromEntries = 1001,
|
||||||
|
DictNotContainsFromEntries = 1002,
|
||||||
|
SetContainsFromEntries = 1003,
|
||||||
|
SetNotContainsFromEntries = 1004,
|
||||||
|
ArrayContainsFromEntries = 1005,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToFields for NativeOperation {
|
impl ToFields for NativeOperation {
|
||||||
|
|
@ -108,6 +119,7 @@ impl OperationType {
|
||||||
NativeOperation::SumOf => Some(Predicate::Native(NativePredicate::SumOf)),
|
NativeOperation::SumOf => Some(Predicate::Native(NativePredicate::SumOf)),
|
||||||
NativeOperation::ProductOf => Some(Predicate::Native(NativePredicate::ProductOf)),
|
NativeOperation::ProductOf => Some(Predicate::Native(NativePredicate::ProductOf)),
|
||||||
NativeOperation::MaxOf => Some(Predicate::Native(NativePredicate::MaxOf)),
|
NativeOperation::MaxOf => Some(Predicate::Native(NativePredicate::MaxOf)),
|
||||||
|
no => unreachable!("Unexpected syntactic sugar op {:?}", no),
|
||||||
},
|
},
|
||||||
OperationType::Custom(cpr) => Some(Predicate::Custom(cpr.clone())),
|
OperationType::Custom(cpr) => Some(Predicate::Custom(cpr.clone())),
|
||||||
}
|
}
|
||||||
|
|
@ -115,7 +127,7 @@ impl OperationType {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Refine this enum.
|
// TODO: Refine this enum.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Operation {
|
pub enum Operation {
|
||||||
None,
|
None,
|
||||||
NewEntry,
|
NewEntry,
|
||||||
|
|
@ -263,7 +275,10 @@ impl Operation {
|
||||||
Self::CopyStatement(s1) => Some(s1.args()),
|
Self::CopyStatement(s1) => Some(s1.args()),
|
||||||
Self::EqualFromEntries(ValueOf(ak1, v1), ValueOf(ak2, v2)) => {
|
Self::EqualFromEntries(ValueOf(ak1, v1), ValueOf(ak2, v2)) => {
|
||||||
if v1 == v2 {
|
if v1 == v2 {
|
||||||
Some(vec![StatementArg::Key(*ak1), StatementArg::Key(*ak2)])
|
Some(vec![
|
||||||
|
StatementArg::Key(ak1.clone()),
|
||||||
|
StatementArg::Key(ak2.clone()),
|
||||||
|
])
|
||||||
} else {
|
} else {
|
||||||
return Err(anyhow!("Invalid operation"));
|
return Err(anyhow!("Invalid operation"));
|
||||||
}
|
}
|
||||||
|
|
@ -273,7 +288,10 @@ impl Operation {
|
||||||
}
|
}
|
||||||
Self::NotEqualFromEntries(ValueOf(ak1, v1), ValueOf(ak2, v2)) => {
|
Self::NotEqualFromEntries(ValueOf(ak1, v1), ValueOf(ak2, v2)) => {
|
||||||
if v1 != v2 {
|
if v1 != v2 {
|
||||||
Some(vec![StatementArg::Key(*ak1), StatementArg::Key(*ak2)])
|
Some(vec![
|
||||||
|
StatementArg::Key(ak1.clone()),
|
||||||
|
StatementArg::Key(ak2.clone()),
|
||||||
|
])
|
||||||
} else {
|
} else {
|
||||||
return Err(anyhow!("Invalid operation"));
|
return Err(anyhow!("Invalid operation"));
|
||||||
}
|
}
|
||||||
|
|
@ -283,7 +301,10 @@ impl Operation {
|
||||||
}
|
}
|
||||||
Self::GtFromEntries(ValueOf(ak1, v1), ValueOf(ak2, v2)) => {
|
Self::GtFromEntries(ValueOf(ak1, v1), ValueOf(ak2, v2)) => {
|
||||||
if v1 > v2 {
|
if v1 > v2 {
|
||||||
Some(vec![StatementArg::Key(*ak1), StatementArg::Key(*ak2)])
|
Some(vec![
|
||||||
|
StatementArg::Key(ak1.clone()),
|
||||||
|
StatementArg::Key(ak2.clone()),
|
||||||
|
])
|
||||||
} else {
|
} else {
|
||||||
return Err(anyhow!("Invalid operation"));
|
return Err(anyhow!("Invalid operation"));
|
||||||
}
|
}
|
||||||
|
|
@ -293,7 +314,10 @@ impl Operation {
|
||||||
}
|
}
|
||||||
Self::LtFromEntries(ValueOf(ak1, v1), ValueOf(ak2, v2)) => {
|
Self::LtFromEntries(ValueOf(ak1, v1), ValueOf(ak2, v2)) => {
|
||||||
if v1 < v2 {
|
if v1 < v2 {
|
||||||
Some(vec![StatementArg::Key(*ak1), StatementArg::Key(*ak2)])
|
Some(vec![
|
||||||
|
StatementArg::Key(ak1.clone()),
|
||||||
|
StatementArg::Key(ak2.clone()),
|
||||||
|
])
|
||||||
} else {
|
} else {
|
||||||
return Err(anyhow!("Invalid operation"));
|
return Err(anyhow!("Invalid operation"));
|
||||||
}
|
}
|
||||||
|
|
@ -303,7 +327,10 @@ impl Operation {
|
||||||
}
|
}
|
||||||
Self::TransitiveEqualFromStatements(Equal(ak1, ak2), Equal(ak3, ak4)) => {
|
Self::TransitiveEqualFromStatements(Equal(ak1, ak2), Equal(ak3, ak4)) => {
|
||||||
if ak2 == ak3 {
|
if ak2 == ak3 {
|
||||||
Some(vec![StatementArg::Key(*ak1), StatementArg::Key(*ak4)])
|
Some(vec![
|
||||||
|
StatementArg::Key(ak1.clone()),
|
||||||
|
StatementArg::Key(ak4.clone()),
|
||||||
|
])
|
||||||
} else {
|
} else {
|
||||||
return Err(anyhow!("Invalid operation"));
|
return Err(anyhow!("Invalid operation"));
|
||||||
}
|
}
|
||||||
|
|
@ -311,48 +338,54 @@ impl Operation {
|
||||||
Self::TransitiveEqualFromStatements(_, _) => {
|
Self::TransitiveEqualFromStatements(_, _) => {
|
||||||
return Err(anyhow!("Invalid operation"));
|
return Err(anyhow!("Invalid operation"));
|
||||||
}
|
}
|
||||||
Self::GtToNotEqual(Gt(ak1, ak2)) => {
|
Self::GtToNotEqual(Gt(ak1, ak2)) => Some(vec![
|
||||||
Some(vec![StatementArg::Key(*ak1), StatementArg::Key(*ak2)])
|
StatementArg::Key(ak1.clone()),
|
||||||
}
|
StatementArg::Key(ak2.clone()),
|
||||||
|
]),
|
||||||
Self::GtToNotEqual(_) => {
|
Self::GtToNotEqual(_) => {
|
||||||
return Err(anyhow!("Invalid operation"));
|
return Err(anyhow!("Invalid operation"));
|
||||||
}
|
}
|
||||||
Self::LtToNotEqual(Gt(ak1, ak2)) => {
|
Self::LtToNotEqual(Gt(ak1, ak2)) => Some(vec![
|
||||||
Some(vec![StatementArg::Key(*ak1), StatementArg::Key(*ak2)])
|
StatementArg::Key(ak1.clone()),
|
||||||
}
|
StatementArg::Key(ak2.clone()),
|
||||||
|
]),
|
||||||
Self::LtToNotEqual(_) => {
|
Self::LtToNotEqual(_) => {
|
||||||
return Err(anyhow!("Invalid operation"));
|
return Err(anyhow!("Invalid operation"));
|
||||||
}
|
}
|
||||||
Self::ContainsFromEntries(ValueOf(ak1, v1), ValueOf(ak2, v2), ValueOf(ak3, v3), pf)
|
Self::ContainsFromEntries(ValueOf(ak1, v1), ValueOf(ak2, v2), ValueOf(ak3, v3), pf)
|
||||||
if MerkleTree::verify(pf.siblings.len(), (*v1).into(), pf, v2, v3).is_ok() =>
|
if MerkleTree::verify(pf.siblings.len(), v1.into(), pf, &v2.raw(), &v3.raw())
|
||||||
|
.is_ok() =>
|
||||||
{
|
{
|
||||||
Some(vec![
|
Some(vec![
|
||||||
StatementArg::Key(*ak1),
|
StatementArg::Key(ak1.clone()),
|
||||||
StatementArg::Key(*ak2),
|
StatementArg::Key(ak2.clone()),
|
||||||
StatementArg::Key(*ak3),
|
StatementArg::Key(ak3.clone()),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
Self::ContainsFromEntries(_, _, _, _) => {
|
Self::ContainsFromEntries(_, _, _, _) => {
|
||||||
return Err(anyhow!("Invalid operation"));
|
return Err(anyhow!("Invalid operation"));
|
||||||
}
|
}
|
||||||
Self::NotContainsFromEntries(ValueOf(ak1, v1), ValueOf(ak2, v2), pf)
|
Self::NotContainsFromEntries(ValueOf(ak1, v1), ValueOf(ak2, v2), pf)
|
||||||
if MerkleTree::verify_nonexistence(pf.siblings.len(), (*v1).into(), pf, v2)
|
if MerkleTree::verify_nonexistence(pf.siblings.len(), v1.into(), pf, &v2.raw())
|
||||||
.is_ok() =>
|
.is_ok() =>
|
||||||
{
|
{
|
||||||
Some(vec![StatementArg::Key(*ak1), StatementArg::Key(*ak2)])
|
Some(vec![
|
||||||
|
StatementArg::Key(ak1.clone()),
|
||||||
|
StatementArg::Key(ak2.clone()),
|
||||||
|
])
|
||||||
}
|
}
|
||||||
Self::NotContainsFromEntries(_, _, _) => {
|
Self::NotContainsFromEntries(_, _, _) => {
|
||||||
return Err(anyhow!("Invalid operation"));
|
return Err(anyhow!("Invalid operation"));
|
||||||
}
|
}
|
||||||
Self::SumOf(ValueOf(ak1, v1), ValueOf(ak2, v2), ValueOf(ak3, v3)) => {
|
Self::SumOf(ValueOf(ak1, v1), ValueOf(ak2, v2), ValueOf(ak3, v3)) => {
|
||||||
let v1: i64 = (*v1).try_into()?;
|
let v1: i64 = v1.typed().try_into()?;
|
||||||
let v2: i64 = (*v2).try_into()?;
|
let v2: i64 = v2.typed().try_into()?;
|
||||||
let v3: i64 = (*v3).try_into()?;
|
let v3: i64 = v3.typed().try_into()?;
|
||||||
if v1 == v2 + v3 {
|
if v1 == v2 + v3 {
|
||||||
Some(vec![
|
Some(vec![
|
||||||
StatementArg::Key(*ak1),
|
StatementArg::Key(ak1.clone()),
|
||||||
StatementArg::Key(*ak2),
|
StatementArg::Key(ak2.clone()),
|
||||||
StatementArg::Key(*ak3),
|
StatementArg::Key(ak3.clone()),
|
||||||
])
|
])
|
||||||
} else {
|
} else {
|
||||||
return Err(anyhow!("Invalid operation"));
|
return Err(anyhow!("Invalid operation"));
|
||||||
|
|
@ -362,14 +395,14 @@ impl Operation {
|
||||||
return Err(anyhow!("Invalid operation"));
|
return Err(anyhow!("Invalid operation"));
|
||||||
}
|
}
|
||||||
Self::ProductOf(ValueOf(ak1, v1), ValueOf(ak2, v2), ValueOf(ak3, v3)) => {
|
Self::ProductOf(ValueOf(ak1, v1), ValueOf(ak2, v2), ValueOf(ak3, v3)) => {
|
||||||
let v1: i64 = (*v1).try_into()?;
|
let v1: i64 = v1.typed().try_into()?;
|
||||||
let v2: i64 = (*v2).try_into()?;
|
let v2: i64 = v2.typed().try_into()?;
|
||||||
let v3: i64 = (*v3).try_into()?;
|
let v3: i64 = v3.typed().try_into()?;
|
||||||
if v1 == v2 * v3 {
|
if v1 == v2 * v3 {
|
||||||
Some(vec![
|
Some(vec![
|
||||||
StatementArg::Key(*ak1),
|
StatementArg::Key(ak1.clone()),
|
||||||
StatementArg::Key(*ak2),
|
StatementArg::Key(ak2.clone()),
|
||||||
StatementArg::Key(*ak3),
|
StatementArg::Key(ak3.clone()),
|
||||||
])
|
])
|
||||||
} else {
|
} else {
|
||||||
return Err(anyhow!("Invalid operation"));
|
return Err(anyhow!("Invalid operation"));
|
||||||
|
|
@ -379,14 +412,14 @@ impl Operation {
|
||||||
return Err(anyhow!("Invalid operation"));
|
return Err(anyhow!("Invalid operation"));
|
||||||
}
|
}
|
||||||
Self::MaxOf(ValueOf(ak1, v1), ValueOf(ak2, v2), ValueOf(ak3, v3)) => {
|
Self::MaxOf(ValueOf(ak1, v1), ValueOf(ak2, v2), ValueOf(ak3, v3)) => {
|
||||||
let v1: i64 = (*v1).try_into()?;
|
let v1: i64 = v1.typed().try_into()?;
|
||||||
let v2: i64 = (*v2).try_into()?;
|
let v2: i64 = v2.typed().try_into()?;
|
||||||
let v3: i64 = (*v3).try_into()?;
|
let v3: i64 = v3.typed().try_into()?;
|
||||||
if v1 == std::cmp::max(v2, v3) {
|
if v1 == std::cmp::max(v2, v3) {
|
||||||
Some(vec![
|
Some(vec![
|
||||||
StatementArg::Key(*ak1),
|
StatementArg::Key(ak1.clone()),
|
||||||
StatementArg::Key(*ak2),
|
StatementArg::Key(ak2.clone()),
|
||||||
StatementArg::Key(*ak3),
|
StatementArg::Key(ak3.clone()),
|
||||||
])
|
])
|
||||||
} else {
|
} else {
|
||||||
return Err(anyhow!("Invalid operation"));
|
return Err(anyhow!("Invalid operation"));
|
||||||
|
|
@ -413,11 +446,11 @@ impl Operation {
|
||||||
Ok(valid)
|
Ok(valid)
|
||||||
}
|
}
|
||||||
/// Checks the given operation against a statement.
|
/// Checks the given operation against a statement.
|
||||||
pub fn check(&self, _params: &Params, output_statement: &Statement) -> Result<bool> {
|
pub fn check(&self, params: &Params, output_statement: &Statement) -> Result<bool> {
|
||||||
use Statement::*;
|
use Statement::*;
|
||||||
match (self, output_statement) {
|
match (self, output_statement) {
|
||||||
(Self::None, None) => Ok(true),
|
(Self::None, None) => Ok(true),
|
||||||
(Self::NewEntry, ValueOf(AnchoredKey(pod_id, _), _)) => Ok(pod_id == &SELF),
|
(Self::NewEntry, ValueOf(AnchoredKey { pod_id, .. }, _)) => Ok(pod_id == &SELF),
|
||||||
(Self::CopyStatement(s1), s2) => Ok(s1 == s2),
|
(Self::CopyStatement(s1), s2) => Ok(s1 == s2),
|
||||||
(Self::EqualFromEntries(ValueOf(ak1, v1), ValueOf(ak2, v2)), Equal(ak3, ak4)) => {
|
(Self::EqualFromEntries(ValueOf(ak1, v1), ValueOf(ak2, v2)), Equal(ak3, ak4)) => {
|
||||||
Ok(v1 == v2 && ak3 == ak1 && ak4 == ak2)
|
Ok(v1 == v2 && ak3 == ak1 && ak4 == ak2)
|
||||||
|
|
@ -451,40 +484,15 @@ impl Operation {
|
||||||
Self::SumOf(ValueOf(ak1, v1), ValueOf(ak2, v2), ValueOf(ak3, v3)),
|
Self::SumOf(ValueOf(ak1, v1), ValueOf(ak2, v2), ValueOf(ak3, v3)),
|
||||||
SumOf(ak4, ak5, ak6),
|
SumOf(ak4, ak5, ak6),
|
||||||
) => {
|
) => {
|
||||||
let v1: i64 = (*v1).try_into()?;
|
let v1: i64 = v1.typed().try_into()?;
|
||||||
let v2: i64 = (*v2).try_into()?;
|
let v2: i64 = v2.typed().try_into()?;
|
||||||
let v3: i64 = (*v3).try_into()?;
|
let v3: i64 = v3.typed().try_into()?;
|
||||||
Ok((v1 == v2 + v3) && ak4 == ak1 && ak5 == ak2 && ak6 == ak3)
|
Ok((v1 == v2 + v3) && ak4 == ak1 && ak5 == ak2 && ak6 == ak3)
|
||||||
}
|
}
|
||||||
(Self::Custom(CustomPredicateRef(cpb, i), args), Custom(cpr, s_args))
|
(Self::Custom(CustomPredicateRef { batch, index }, args), Custom(cpr, s_args))
|
||||||
if cpb == &cpr.0 && i == &cpr.1 =>
|
if batch == &cpr.batch && index == &cpr.index =>
|
||||||
{
|
{
|
||||||
// Bind according to custom predicate pattern match against arg list.
|
check_custom_pred(params, batch, *index, args, s_args)
|
||||||
let bindings = cpr.match_against(args)?;
|
|
||||||
// Check arg length
|
|
||||||
let arg_len = cpr.arg_len();
|
|
||||||
if arg_len != 2 * s_args.len() {
|
|
||||||
Err(anyhow!("Custom predicate arg list {:?} must have {} arguments after destructuring.", s_args, arg_len))
|
|
||||||
} else {
|
|
||||||
let bound_args = (0..arg_len)
|
|
||||||
.map(|i| {
|
|
||||||
bindings.get(&i).cloned().ok_or(anyhow!(
|
|
||||||
"Wildcard {} of custom predicate {:?} is unbound.",
|
|
||||||
i,
|
|
||||||
cpr
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>>>()?;
|
|
||||||
let s_args = s_args
|
|
||||||
.iter()
|
|
||||||
.flat_map(|AnchoredKey(o, k)| [Value::from(o.0), (*k).into()])
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
if bound_args != s_args {
|
|
||||||
Err(anyhow!("Arguments to output statement {} do not match those implied by operation {:?}", output_statement,self))
|
|
||||||
} else {
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => Err(anyhow!(
|
_ => Err(anyhow!(
|
||||||
"Invalid deduction: {:?} ⇏ {:#}",
|
"Invalid deduction: {:?} ⇏ {:#}",
|
||||||
|
|
@ -495,6 +503,120 @@ impl Operation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check that a StatementArg follows a StatementTmplArg based on the currently mapped wildcards.
|
||||||
|
/// Update the wildcard map with newly found wildcards.
|
||||||
|
pub fn check_st_tmpl(
|
||||||
|
st_tmpl_arg: &StatementTmplArg,
|
||||||
|
st_arg: &StatementArg,
|
||||||
|
// Map from wildcards to values that we have seen so far.
|
||||||
|
wildcard_map: &mut [Option<WildcardValue>],
|
||||||
|
) -> bool {
|
||||||
|
// Check that the value `v` at wildcard `wc` exists in the map or set it.
|
||||||
|
fn check_or_set(
|
||||||
|
v: WildcardValue,
|
||||||
|
wc: &Wildcard,
|
||||||
|
wildcard_map: &mut [Option<WildcardValue>],
|
||||||
|
) -> bool {
|
||||||
|
if let Some(prev) = &wildcard_map[wc.index] {
|
||||||
|
if *prev != v {
|
||||||
|
// TODO: Return nice error
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
wildcard_map[wc.index] = Some(v);
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
match (st_tmpl_arg, st_arg) {
|
||||||
|
(StatementTmplArg::None, StatementArg::None) => true,
|
||||||
|
(StatementTmplArg::Literal(lhs), StatementArg::Literal(rhs)) if lhs == rhs => true,
|
||||||
|
(
|
||||||
|
StatementTmplArg::Key(pod_id_wc, key_or_wc),
|
||||||
|
StatementArg::Key(AnchoredKey { pod_id, key }),
|
||||||
|
) => {
|
||||||
|
let pod_id_ok = check_or_set(WildcardValue::PodId(*pod_id), pod_id_wc, wildcard_map);
|
||||||
|
let key_ok = match key_or_wc {
|
||||||
|
KeyOrWildcard::Key(tmpl_key) => tmpl_key == key,
|
||||||
|
KeyOrWildcard::Wildcard(key_wc) => {
|
||||||
|
check_or_set(WildcardValue::Key(key.clone()), key_wc, wildcard_map)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
pod_id_ok && key_ok
|
||||||
|
}
|
||||||
|
(StatementTmplArg::WildcardLiteral(wc), StatementArg::WildcardLiteral(v)) => {
|
||||||
|
check_or_set(v.clone(), wc, wildcard_map)
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_custom_pred(
|
||||||
|
params: &Params,
|
||||||
|
batch: &Arc<CustomPredicateBatch>,
|
||||||
|
index: usize,
|
||||||
|
args: &[Statement],
|
||||||
|
s_args: &[WildcardValue],
|
||||||
|
) -> Result<bool> {
|
||||||
|
let pred = &batch.predicates[index];
|
||||||
|
if pred.statements.len() != args.len() {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"Custom predicate operation needs {} statements but has {}.",
|
||||||
|
pred.statements.len(),
|
||||||
|
args.len()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if pred.args_len != s_args.len() {
|
||||||
|
return Err(anyhow!(
|
||||||
|
"Custom predicate statement needs {} args but has {}.",
|
||||||
|
pred.args_len,
|
||||||
|
s_args.len()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that all wildcard have consistent values as assigned in the statements while storing a
|
||||||
|
// map of their values. Count the number of statements that match the templates by predicate.
|
||||||
|
// NOTE: We assume the statements have the same order as defined in the custom predicate. For
|
||||||
|
// disjunctions we expect Statement::None for the unused statements.
|
||||||
|
let mut num_matches = 0;
|
||||||
|
let mut wildcard_map = vec![None; params.max_custom_predicate_wildcards];
|
||||||
|
for (st_tmpl, st) in pred.statements.iter().zip(args) {
|
||||||
|
let st_args = st.args();
|
||||||
|
for (st_tmpl_arg, st_arg) in st_tmpl.args.iter().zip(&st_args) {
|
||||||
|
if !check_st_tmpl(st_tmpl_arg, st_arg, &mut wildcard_map) {
|
||||||
|
// TODO: Better errors. Example:
|
||||||
|
// println!("{} doesn't match {}", st_arg, st_tmpl_arg);
|
||||||
|
// println!("{} doesn't match {}", st, st_tmpl);
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let st_tmpl_pred = match &st_tmpl.pred {
|
||||||
|
Predicate::BatchSelf(i) => Predicate::Custom(CustomPredicateRef {
|
||||||
|
batch: batch.clone(),
|
||||||
|
index: *i,
|
||||||
|
}),
|
||||||
|
p => p.clone(),
|
||||||
|
};
|
||||||
|
if st_tmpl_pred == st.predicate() {
|
||||||
|
num_matches += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the resolved wildcard match the statement arguments.
|
||||||
|
for (s_arg, wc_value) in s_args.iter().zip(wildcard_map.iter()) {
|
||||||
|
if !wc_value.as_ref().is_none_or(|wc_value| *wc_value == *s_arg) {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if pred.conjunction {
|
||||||
|
Ok(num_matches == pred.statements.len())
|
||||||
|
} else {
|
||||||
|
Ok(num_matches > 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ToFields for Operation {
|
impl ToFields for Operation {
|
||||||
fn to_fields(&self, _params: &Params) -> Vec<F> {
|
fn to_fields(&self, _params: &Params) -> Vec<F> {
|
||||||
todo!()
|
todo!()
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
// TODO: Reenable
|
||||||
|
/*
|
||||||
use plonky2::field::types::Field;
|
use plonky2::field::types::Field;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
|
@ -67,3 +69,4 @@ where
|
||||||
{
|
{
|
||||||
deserialize_field_tuple::<D, VALUE_SIZE>(deserializer)
|
deserialize_field_tuple::<D, VALUE_SIZE>(deserializer)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,16 @@ use std::{fmt, iter};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use plonky2::field::types::Field;
|
use plonky2::field::types::Field;
|
||||||
use schemars::JsonSchema;
|
// use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
// use serde::{Deserialize, Serialize};
|
||||||
use strum_macros::FromRepr;
|
use strum_macros::FromRepr;
|
||||||
|
|
||||||
use crate::middleware::{
|
use crate::middleware::{
|
||||||
AnchoredKey, CustomPredicateRef, Params, Predicate, ToFields, Value, F, VALUE_SIZE,
|
AnchoredKey, CustomPredicateRef, Key, Params, PodId, Predicate, RawValue, ToFields, Value, F,
|
||||||
|
VALUE_SIZE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: Maybe store KEY_SIGNER and KEY_TYPE as Key with lazy_static
|
||||||
// hash(KEY_SIGNER) = [2145458785152392366, 15113074911296146791, 15323228995597834291, 11804480340100333725]
|
// hash(KEY_SIGNER) = [2145458785152392366, 15113074911296146791, 15323228995597834291, 11804480340100333725]
|
||||||
pub const KEY_SIGNER: &str = "_signer";
|
pub const KEY_SIGNER: &str = "_signer";
|
||||||
// hash(KEY_TYPE) = [17948789436443445142, 12513915140657440811, 15878361618879468769, 938231894693848619]
|
// hash(KEY_TYPE) = [17948789436443445142, 12513915140657440811, 15878361618879468769, 938231894693848619]
|
||||||
|
|
@ -18,7 +20,7 @@ pub const STATEMENT_ARG_F_LEN: usize = 8;
|
||||||
pub const OPERATION_ARG_F_LEN: usize = 1;
|
pub const OPERATION_ARG_F_LEN: usize = 1;
|
||||||
pub const OPERATION_AUX_F_LEN: usize = 1;
|
pub const OPERATION_AUX_F_LEN: usize = 1;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, FromRepr, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Copy, Debug, FromRepr, PartialEq, Eq, Hash)]
|
||||||
pub enum NativePredicate {
|
pub enum NativePredicate {
|
||||||
None = 0,
|
None = 0,
|
||||||
ValueOf = 1,
|
ValueOf = 1,
|
||||||
|
|
@ -31,6 +33,14 @@ pub enum NativePredicate {
|
||||||
SumOf = 8,
|
SumOf = 8,
|
||||||
ProductOf = 9,
|
ProductOf = 9,
|
||||||
MaxOf = 10,
|
MaxOf = 10,
|
||||||
|
|
||||||
|
// Syntactic sugar predicates. These predicates are not supported by the backend. The
|
||||||
|
// frontend compiler is responsible of translating these predicates into the predicates above.
|
||||||
|
DictContains = 1000,
|
||||||
|
DictNotContains = 1001,
|
||||||
|
SetContains = 1002,
|
||||||
|
SetNotContains = 1003,
|
||||||
|
ArrayContains = 1004, // there is no ArrayNotContains
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToFields for NativePredicate {
|
impl ToFields for NativePredicate {
|
||||||
|
|
@ -39,8 +49,41 @@ impl ToFields for NativePredicate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum WildcardValue {
|
||||||
|
PodId(PodId),
|
||||||
|
Key(Key),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WildcardValue {
|
||||||
|
pub fn raw(&self) -> RawValue {
|
||||||
|
match self {
|
||||||
|
WildcardValue::PodId(pod_id) => RawValue::from(pod_id.0),
|
||||||
|
WildcardValue::Key(key) => key.raw(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for WildcardValue {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
WildcardValue::PodId(pod_id) => write!(f, "{}", pod_id),
|
||||||
|
WildcardValue::Key(key) => write!(f, "{}", key),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToFields for WildcardValue {
|
||||||
|
fn to_fields(&self, params: &Params) -> Vec<F> {
|
||||||
|
match self {
|
||||||
|
WildcardValue::PodId(pod_id) => pod_id.to_fields(params),
|
||||||
|
WildcardValue::Key(key) => key.to_fields(params),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Type encapsulating statements with their associated arguments.
|
/// Type encapsulating statements with their associated arguments.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum Statement {
|
pub enum Statement {
|
||||||
None,
|
None,
|
||||||
ValueOf(AnchoredKey, Value),
|
ValueOf(AnchoredKey, Value),
|
||||||
|
|
@ -57,7 +100,7 @@ pub enum Statement {
|
||||||
SumOf(AnchoredKey, AnchoredKey, AnchoredKey),
|
SumOf(AnchoredKey, AnchoredKey, AnchoredKey),
|
||||||
ProductOf(AnchoredKey, AnchoredKey, AnchoredKey),
|
ProductOf(AnchoredKey, AnchoredKey, AnchoredKey),
|
||||||
MaxOf(AnchoredKey, AnchoredKey, AnchoredKey),
|
MaxOf(AnchoredKey, AnchoredKey, AnchoredKey),
|
||||||
Custom(CustomPredicateRef, Vec<AnchoredKey>),
|
Custom(CustomPredicateRef, Vec<WildcardValue>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Statement {
|
impl Statement {
|
||||||
|
|
@ -95,7 +138,7 @@ impl Statement {
|
||||||
Self::SumOf(ak1, ak2, ak3) => vec![Key(ak1), Key(ak2), Key(ak3)],
|
Self::SumOf(ak1, ak2, ak3) => vec![Key(ak1), Key(ak2), Key(ak3)],
|
||||||
Self::ProductOf(ak1, ak2, ak3) => vec![Key(ak1), Key(ak2), Key(ak3)],
|
Self::ProductOf(ak1, ak2, ak3) => vec![Key(ak1), Key(ak2), Key(ak3)],
|
||||||
Self::MaxOf(ak1, ak2, ak3) => vec![Key(ak1), Key(ak2), Key(ak3)],
|
Self::MaxOf(ak1, ak2, ak3) => vec![Key(ak1), Key(ak2), Key(ak3)],
|
||||||
Self::Custom(_, args) => Vec::from_iter(args.into_iter().map(Key)),
|
Self::Custom(_, args) => Vec::from_iter(args.into_iter().map(WildcardLiteral)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn from_args(pred: Predicate, args: Vec<StatementArg>) -> Result<Self> {
|
pub fn from_args(pred: Predicate, args: Vec<StatementArg>) -> Result<Self> {
|
||||||
|
|
@ -103,35 +146,45 @@ impl Statement {
|
||||||
let st: Result<Self> = match pred {
|
let st: Result<Self> = match pred {
|
||||||
Native(NativePredicate::None) => Ok(Self::None),
|
Native(NativePredicate::None) => Ok(Self::None),
|
||||||
Native(NativePredicate::ValueOf) => {
|
Native(NativePredicate::ValueOf) => {
|
||||||
if let (StatementArg::Key(a0), StatementArg::Literal(v1)) = (args[0], args[1]) {
|
if let (StatementArg::Key(a0), StatementArg::Literal(v1)) =
|
||||||
|
(args[0].clone(), args[1].clone())
|
||||||
|
{
|
||||||
Ok(Self::ValueOf(a0, v1))
|
Ok(Self::ValueOf(a0, v1))
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("Incorrect statement args"))
|
Err(anyhow!("Incorrect statement args"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Native(NativePredicate::Equal) => {
|
Native(NativePredicate::Equal) => {
|
||||||
if let (StatementArg::Key(a0), StatementArg::Key(a1)) = (args[0], args[1]) {
|
if let (StatementArg::Key(a0), StatementArg::Key(a1)) =
|
||||||
|
(args[0].clone(), args[1].clone())
|
||||||
|
{
|
||||||
Ok(Self::Equal(a0, a1))
|
Ok(Self::Equal(a0, a1))
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("Incorrect statement args"))
|
Err(anyhow!("Incorrect statement args"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Native(NativePredicate::NotEqual) => {
|
Native(NativePredicate::NotEqual) => {
|
||||||
if let (StatementArg::Key(a0), StatementArg::Key(a1)) = (args[0], args[1]) {
|
if let (StatementArg::Key(a0), StatementArg::Key(a1)) =
|
||||||
|
(args[0].clone(), args[1].clone())
|
||||||
|
{
|
||||||
Ok(Self::NotEqual(a0, a1))
|
Ok(Self::NotEqual(a0, a1))
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("Incorrect statement args"))
|
Err(anyhow!("Incorrect statement args"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Native(NativePredicate::Gt) => {
|
Native(NativePredicate::Gt) => {
|
||||||
if let (StatementArg::Key(a0), StatementArg::Key(a1)) = (args[0], args[1]) {
|
if let (StatementArg::Key(a0), StatementArg::Key(a1)) =
|
||||||
|
(args[0].clone(), args[1].clone())
|
||||||
|
{
|
||||||
Ok(Self::Gt(a0, a1))
|
Ok(Self::Gt(a0, a1))
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("Incorrect statement args"))
|
Err(anyhow!("Incorrect statement args"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Native(NativePredicate::Lt) => {
|
Native(NativePredicate::Lt) => {
|
||||||
if let (StatementArg::Key(a0), StatementArg::Key(a1)) = (args[0], args[1]) {
|
if let (StatementArg::Key(a0), StatementArg::Key(a1)) =
|
||||||
|
(args[0].clone(), args[1].clone())
|
||||||
|
{
|
||||||
Ok(Self::Lt(a0, a1))
|
Ok(Self::Lt(a0, a1))
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("Incorrect statement args"))
|
Err(anyhow!("Incorrect statement args"))
|
||||||
|
|
@ -139,7 +192,7 @@ impl Statement {
|
||||||
}
|
}
|
||||||
Native(NativePredicate::Contains) => {
|
Native(NativePredicate::Contains) => {
|
||||||
if let (StatementArg::Key(a0), StatementArg::Key(a1), StatementArg::Key(a2)) =
|
if let (StatementArg::Key(a0), StatementArg::Key(a1), StatementArg::Key(a2)) =
|
||||||
(args[0], args[1], args[2])
|
(args[0].clone(), args[1].clone(), args[2].clone())
|
||||||
{
|
{
|
||||||
Ok(Self::Contains(a0, a1, a2))
|
Ok(Self::Contains(a0, a1, a2))
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -147,7 +200,9 @@ impl Statement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Native(NativePredicate::NotContains) => {
|
Native(NativePredicate::NotContains) => {
|
||||||
if let (StatementArg::Key(a0), StatementArg::Key(a1)) = (args[0], args[1]) {
|
if let (StatementArg::Key(a0), StatementArg::Key(a1)) =
|
||||||
|
(args[0].clone(), args[1].clone())
|
||||||
|
{
|
||||||
Ok(Self::NotContains(a0, a1))
|
Ok(Self::NotContains(a0, a1))
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("Incorrect statement args"))
|
Err(anyhow!("Incorrect statement args"))
|
||||||
|
|
@ -155,7 +210,7 @@ impl Statement {
|
||||||
}
|
}
|
||||||
Native(NativePredicate::SumOf) => {
|
Native(NativePredicate::SumOf) => {
|
||||||
if let (StatementArg::Key(a0), StatementArg::Key(a1), StatementArg::Key(a2)) =
|
if let (StatementArg::Key(a0), StatementArg::Key(a1), StatementArg::Key(a2)) =
|
||||||
(args[0], args[1], args[2])
|
(args[0].clone(), args[1].clone(), args[2].clone())
|
||||||
{
|
{
|
||||||
Ok(Self::SumOf(a0, a1, a2))
|
Ok(Self::SumOf(a0, a1, a2))
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -164,7 +219,7 @@ impl Statement {
|
||||||
}
|
}
|
||||||
Native(NativePredicate::ProductOf) => {
|
Native(NativePredicate::ProductOf) => {
|
||||||
if let (StatementArg::Key(a0), StatementArg::Key(a1), StatementArg::Key(a2)) =
|
if let (StatementArg::Key(a0), StatementArg::Key(a1), StatementArg::Key(a2)) =
|
||||||
(args[0], args[1], args[2])
|
(args[0].clone(), args[1].clone(), args[2].clone())
|
||||||
{
|
{
|
||||||
Ok(Self::ProductOf(a0, a1, a2))
|
Ok(Self::ProductOf(a0, a1, a2))
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -173,23 +228,24 @@ impl Statement {
|
||||||
}
|
}
|
||||||
Native(NativePredicate::MaxOf) => {
|
Native(NativePredicate::MaxOf) => {
|
||||||
if let (StatementArg::Key(a0), StatementArg::Key(a1), StatementArg::Key(a2)) =
|
if let (StatementArg::Key(a0), StatementArg::Key(a1), StatementArg::Key(a2)) =
|
||||||
(args[0], args[1], args[2])
|
(args[0].clone(), args[1].clone(), args[2].clone())
|
||||||
{
|
{
|
||||||
Ok(Self::MaxOf(a0, a1, a2))
|
Ok(Self::MaxOf(a0, a1, a2))
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow!("Incorrect statement args"))
|
Err(anyhow!("Incorrect statement args"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Native(np) => Err(anyhow!("Predicate {:?} is syntax sugar", np)),
|
||||||
BatchSelf(_) => unreachable!(),
|
BatchSelf(_) => unreachable!(),
|
||||||
Custom(cpr) => {
|
Custom(cpr) => {
|
||||||
let ak_args: Result<Vec<AnchoredKey>> = args
|
let v_args: Result<Vec<WildcardValue>> = args
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| match x {
|
.map(|x| match x {
|
||||||
StatementArg::Key(ak) => Ok(*ak),
|
StatementArg::WildcardLiteral(v) => Ok(v.clone()),
|
||||||
_ => Err(anyhow!("Incorrect statement args")),
|
_ => Err(anyhow!("Incorrect statement args")),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
Ok(Self::Custom(cpr, ak_args?))
|
Ok(Self::Custom(cpr, v_args?))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
st
|
st
|
||||||
|
|
@ -207,23 +263,24 @@ impl ToFields for Statement {
|
||||||
|
|
||||||
impl fmt::Display for Statement {
|
impl fmt::Display for Statement {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{:?} ", self.predicate())?;
|
write!(f, "{}(", self.predicate())?;
|
||||||
for (i, arg) in self.args().iter().enumerate() {
|
for (i, arg) in self.args().iter().enumerate() {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
write!(f, " ")?;
|
write!(f, ", ")?;
|
||||||
}
|
}
|
||||||
write!(f, "{}", arg)?;
|
write!(f, "{}", arg)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
write!(f, ")")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Statement argument type. Useful for statement decompositions.
|
/// Statement argument type. Useful for statement decompositions.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum StatementArg {
|
pub enum StatementArg {
|
||||||
None,
|
None,
|
||||||
Literal(Value),
|
Literal(Value),
|
||||||
Key(AnchoredKey),
|
Key(AnchoredKey),
|
||||||
|
WildcardLiteral(WildcardValue),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for StatementArg {
|
impl fmt::Display for StatementArg {
|
||||||
|
|
@ -231,7 +288,8 @@ impl fmt::Display for StatementArg {
|
||||||
match self {
|
match self {
|
||||||
StatementArg::None => write!(f, "none"),
|
StatementArg::None => write!(f, "none"),
|
||||||
StatementArg::Literal(v) => write!(f, "{}", v),
|
StatementArg::Literal(v) => write!(f, "{}", v),
|
||||||
StatementArg::Key(r) => write!(f, "{}.{}", r.0, r.1),
|
StatementArg::Key(r) => write!(f, "{}.{}", r.pod_id, r.key),
|
||||||
|
StatementArg::WildcardLiteral(v) => write!(f, "{}", v),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -242,13 +300,13 @@ impl StatementArg {
|
||||||
}
|
}
|
||||||
pub fn literal(&self) -> Result<Value> {
|
pub fn literal(&self) -> Result<Value> {
|
||||||
match self {
|
match self {
|
||||||
Self::Literal(value) => Ok(*value),
|
Self::Literal(value) => Ok(value.clone()),
|
||||||
_ => Err(anyhow!("Statement argument {:?} is not a literal.", self)),
|
_ => Err(anyhow!("Statement argument {:?} is not a literal.", self)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn key(&self) -> Result<AnchoredKey> {
|
pub fn key(&self) -> Result<AnchoredKey> {
|
||||||
match self {
|
match self {
|
||||||
Self::Key(ak) => Ok(*ak),
|
Self::Key(ak) => Ok(ak.clone()),
|
||||||
_ => Err(anyhow!("Statement argument {:?} is not a key.", self)),
|
_ => Err(anyhow!("Statement argument {:?} is not a key.", self)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -265,16 +323,23 @@ impl ToFields for StatementArg {
|
||||||
// dealing with `Literal` it would be of length 4.
|
// dealing with `Literal` it would be of length 4.
|
||||||
let f = match self {
|
let f = match self {
|
||||||
StatementArg::None => vec![F::ZERO; STATEMENT_ARG_F_LEN],
|
StatementArg::None => vec![F::ZERO; STATEMENT_ARG_F_LEN],
|
||||||
StatementArg::Literal(v) => {
|
StatementArg::Literal(v) => v
|
||||||
v.0.into_iter()
|
.raw()
|
||||||
|
.0
|
||||||
|
.into_iter()
|
||||||
.chain(iter::repeat(F::ZERO).take(STATEMENT_ARG_F_LEN - VALUE_SIZE))
|
.chain(iter::repeat(F::ZERO).take(STATEMENT_ARG_F_LEN - VALUE_SIZE))
|
||||||
.collect()
|
.collect(),
|
||||||
}
|
|
||||||
StatementArg::Key(ak) => {
|
StatementArg::Key(ak) => {
|
||||||
let mut fields = ak.0.to_fields(_params);
|
let mut fields = ak.pod_id.to_fields(_params);
|
||||||
fields.extend(ak.1.to_fields(_params));
|
fields.extend(ak.key.to_fields(_params));
|
||||||
fields
|
fields
|
||||||
}
|
}
|
||||||
|
StatementArg::WildcardLiteral(v) => v
|
||||||
|
.raw()
|
||||||
|
.0
|
||||||
|
.into_iter()
|
||||||
|
.chain(iter::repeat(F::ZERO).take(STATEMENT_ARG_F_LEN - VALUE_SIZE))
|
||||||
|
.collect(),
|
||||||
};
|
};
|
||||||
assert_eq!(f.len(), STATEMENT_ARG_F_LEN); // sanity check
|
assert_eq!(f.len(), STATEMENT_ARG_F_LEN); // sanity check
|
||||||
f
|
f
|
||||||
|
|
|
||||||
20
src/util.rs
20
src/util.rs
|
|
@ -1,20 +0,0 @@
|
||||||
use std::{collections::HashMap, fmt::Debug, hash::Hash};
|
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
|
|
||||||
pub(crate) fn hashmap_insert_no_dupe<S: Clone + Debug + Eq + Hash, T: Clone + Debug + Eq>(
|
|
||||||
hm: &mut HashMap<S, T>,
|
|
||||||
kv: (S, T),
|
|
||||||
) -> Result<()> {
|
|
||||||
let (k, v) = kv.clone();
|
|
||||||
let res = hm.insert(kv.0, kv.1);
|
|
||||||
match res {
|
|
||||||
Some(w) if w != v => Err(anyhow!(
|
|
||||||
"Key {:?} exists in table with value {:?} != {:?}.",
|
|
||||||
k,
|
|
||||||
w,
|
|
||||||
v
|
|
||||||
)),
|
|
||||||
_ => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue