chore(frontend): make Merkle proofs optional (#198)
* Make frontend Merkle proofs optional * Code review * Clippy
This commit is contained in:
parent
281f57f0a0
commit
17e6c2a092
7 changed files with 98 additions and 48 deletions
|
|
@ -548,7 +548,7 @@ pub mod tests {
|
||||||
let key = RawValue::from(hash_value(&RawValue::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, RawValue::from(5));
|
assert_eq!(value, RawValue::from(5));
|
||||||
assert_eq!(proof.existence, true);
|
assert!(proof.existence);
|
||||||
|
|
||||||
MerkleTree::verify(max_depth, tree.root(), &proof, &key, &value)?;
|
MerkleTree::verify(max_depth, tree.root(), &proof, &key, &value)?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,14 @@ pub mod custom;
|
||||||
|
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use 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::{MainPodBuilder, SignedPod, SignedPodBuilder},
|
frontend::{MainPodBuilder, SignedPod, SignedPodBuilder},
|
||||||
middleware::{
|
middleware::{
|
||||||
containers::Set, CustomPredicateRef, Key, Params, PodType, Statement, TypedValue, Value,
|
containers::Set, CustomPredicateRef, Params, PodType, Statement, TypedValue, Value,
|
||||||
KEY_SIGNER, KEY_TYPE,
|
KEY_SIGNER, KEY_TYPE,
|
||||||
},
|
},
|
||||||
op,
|
op,
|
||||||
|
|
@ -45,16 +45,9 @@ 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.get("sanctionList").map(|v| v.typed()) {
|
|
||||||
Some(TypedValue::Set(s)) => Ok(s),
|
|
||||||
_ => 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 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);
|
||||||
kyc.add_signed_pod(pay_stub);
|
kyc.add_signed_pod(pay_stub);
|
||||||
|
|
@ -62,8 +55,7 @@ pub fn zu_kyc_pod_builder(
|
||||||
kyc.pub_op(op!(
|
kyc.pub_op(op!(
|
||||||
set_not_contains,
|
set_not_contains,
|
||||||
(sanction_list, "sanctionList"),
|
(sanction_list, "sanctionList"),
|
||||||
(gov_id, "idNumber"),
|
(gov_id, "idNumber")
|
||||||
sanction_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!(
|
||||||
|
|
@ -269,8 +261,6 @@ pub fn great_boy_pod_builder(
|
||||||
PodType::MockSigned as i64
|
PodType::MockSigned as i64
|
||||||
))?;
|
))?;
|
||||||
for issuer_idx in 0..2 {
|
for issuer_idx in 0..2 {
|
||||||
let pod_kvs = good_boy_pods[good_boy_idx * 2 + issuer_idx].kvs();
|
|
||||||
|
|
||||||
// Type check
|
// Type check
|
||||||
great_boy.pub_op(op!(
|
great_boy.pub_op(op!(
|
||||||
eq,
|
eq,
|
||||||
|
|
@ -278,16 +268,10 @@ 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.typed() {
|
|
||||||
TypedValue::Set(set) => Ok(set),
|
|
||||||
_ => Err(anyhow!("Invalid good boy issuers!")),
|
|
||||||
}?
|
|
||||||
.prove(pod_kvs.get(&Key::from(KEY_SIGNER)).unwrap())?;
|
|
||||||
great_boy.pub_op(op!(
|
great_boy.pub_op(op!(
|
||||||
set_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)
|
||||||
good_boy_proof
|
|
||||||
))?;
|
))?;
|
||||||
// Each good boy has 2 good boy pods
|
// Each good boy has 2 good boy pods
|
||||||
great_boy.pub_op(op!(
|
great_boy.pub_op(op!(
|
||||||
|
|
@ -403,12 +387,6 @@ pub fn tickets_pod_builder(
|
||||||
expect_consumed: bool,
|
expect_consumed: bool,
|
||||||
blacklisted_emails: &Set,
|
blacklisted_emails: &Set,
|
||||||
) -> Result<MainPodBuilder> {
|
) -> Result<MainPodBuilder> {
|
||||||
let attendee_email_value = signed_pod
|
|
||||||
.kvs()
|
|
||||||
.get(&Key::from("attendeeEmail"))
|
|
||||||
.unwrap()
|
|
||||||
.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()));
|
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);
|
||||||
|
|
@ -419,8 +397,7 @@ pub fn tickets_pod_builder(
|
||||||
builder.pub_op(op!(
|
builder.pub_op(op!(
|
||||||
dict_not_contains,
|
dict_not_contains,
|
||||||
blacklisted_email_set_value,
|
blacklisted_email_set_value,
|
||||||
(signed_pod, "attendeeEmail"),
|
(signed_pod, "attendeeEmail")
|
||||||
attendee_nin_blacklist_pf
|
|
||||||
))?;
|
))?;
|
||||||
Ok(builder)
|
Ok(builder)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -233,7 +233,7 @@ mod tests {
|
||||||
|
|
||||||
let eth_dos_batch = eth_dos_batch(¶ms)?;
|
let eth_dos_batch = eth_dos_batch(¶ms)?;
|
||||||
let eth_dos_batch_mw: middleware::CustomPredicateBatch =
|
let eth_dos_batch_mw: middleware::CustomPredicateBatch =
|
||||||
Arc::unwrap_or_clone(eth_dos_batch).into();
|
Arc::unwrap_or_clone(eth_dos_batch);
|
||||||
let fields = eth_dos_batch_mw.to_fields(¶ms);
|
let fields = eth_dos_batch_mw.to_fields(¶ms);
|
||||||
println!("Batch b, serialized: {:?}", fields);
|
println!("Batch b, serialized: {:?}", fields);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -262,9 +262,39 @@ impl MainPodBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fills in auxiliary data if necessary/possible.
|
||||||
|
fn fill_in_aux(op: Operation) -> Result<Operation> {
|
||||||
|
use NativeOperation::{ContainsFromEntries, NotContainsFromEntries};
|
||||||
|
use OperationAux as OpAux;
|
||||||
|
use OperationType::Native;
|
||||||
|
|
||||||
|
let op_type = &op.0;
|
||||||
|
|
||||||
|
match (op_type, &op.2) {
|
||||||
|
(Native(ContainsFromEntries), OpAux::None)
|
||||||
|
| (Native(NotContainsFromEntries), OpAux::None) => {
|
||||||
|
let container =
|
||||||
|
op.1.get(0)
|
||||||
|
.and_then(|arg| arg.value())
|
||||||
|
.ok_or(anyhow!("Invalid container argument for op {}.", op))?;
|
||||||
|
let key =
|
||||||
|
op.1.get(1)
|
||||||
|
.and_then(|arg| arg.value())
|
||||||
|
.ok_or(anyhow!("Invalid key argument for op {}.", op))?;
|
||||||
|
let proof = if op_type == &Native(ContainsFromEntries) {
|
||||||
|
container.prove_existence(key)?.1
|
||||||
|
} else {
|
||||||
|
container.prove_nonexistence(key)?
|
||||||
|
};
|
||||||
|
Ok(Operation(op_type.clone(), op.1, OpAux::MerkleProof(proof)))
|
||||||
|
}
|
||||||
|
_ => Ok(op),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn op(&mut self, public: bool, op: Operation) -> Result<Statement, anyhow::Error> {
|
fn op(&mut self, public: bool, op: Operation) -> Result<Statement, anyhow::Error> {
|
||||||
use NativeOperation::*;
|
use NativeOperation::*;
|
||||||
let mut op = Self::lower_op(op);
|
let mut op = Self::fill_in_aux(Self::lower_op(op))?;
|
||||||
let Operation(op_type, ref mut args, _) = &mut op;
|
let Operation(op_type, ref mut args, _) = &mut op;
|
||||||
// TODO: argument type checking
|
// TODO: argument type checking
|
||||||
let pred = op_type.output_predicate().map(Ok).unwrap_or_else(|| {
|
let pred = op_type.output_predicate().map(Ok).unwrap_or_else(|| {
|
||||||
|
|
@ -736,21 +766,21 @@ pub mod build_utils {
|
||||||
(custom, $op:expr, $($arg:expr),+) => { $crate::frontend::Operation(
|
(custom, $op:expr, $($arg:expr),+) => { $crate::frontend::Operation(
|
||||||
$crate::middleware::OperationType::Custom($op),
|
$crate::middleware::OperationType::Custom($op),
|
||||||
$crate::op_args!($($arg),*), $crate::middleware::OperationAux::None) };
|
$crate::op_args!($($arg),*), $crate::middleware::OperationAux::None) };
|
||||||
(dict_contains, $dict:expr, $key:expr, $value:expr, $aux:expr) => { $crate::frontend::Operation(
|
(dict_contains, $dict:expr, $key:expr, $value:expr) => { $crate::frontend::Operation(
|
||||||
$crate::middleware::OperationType::Native($crate::middleware::NativeOperation::DictContainsFromEntries),
|
$crate::middleware::OperationType::Native($crate::middleware::NativeOperation::DictContainsFromEntries),
|
||||||
$crate::op_args!($dict, $key, $value), $crate::middleware::OperationAux::MerkleProof($aux)) };
|
$crate::op_args!($dict, $key, $value), $crate::middleware::OperationAux::None) };
|
||||||
(dict_not_contains, $dict:expr, $key:expr, $aux:expr) => { $crate::frontend::Operation(
|
(dict_not_contains, $dict:expr, $key:expr) => { $crate::frontend::Operation(
|
||||||
$crate::middleware::OperationType::Native($crate::middleware::NativeOperation::DictNotContainsFromEntries),
|
$crate::middleware::OperationType::Native($crate::middleware::NativeOperation::DictNotContainsFromEntries),
|
||||||
$crate::op_args!($dict, $key), $crate::middleware::OperationAux::MerkleProof($aux)) };
|
$crate::op_args!($dict, $key), $crate::middleware::OperationAux::None) };
|
||||||
(set_contains, $set:expr, $value:expr, $aux:expr) => { $crate::frontend::Operation(
|
(set_contains, $set:expr, $value:expr) => { $crate::frontend::Operation(
|
||||||
$crate::middleware::OperationType::Native($crate::middleware::NativeOperation::SetContainsFromEntries),
|
$crate::middleware::OperationType::Native($crate::middleware::NativeOperation::SetContainsFromEntries),
|
||||||
$crate::op_args!($set, $value), $crate::middleware::OperationAux::MerkleProof($aux)) };
|
$crate::op_args!($set, $value), $crate::middleware::OperationAux::None) };
|
||||||
(set_not_contains, $set:expr, $value:expr, $aux:expr) => { $crate::frontend::Operation(
|
(set_not_contains, $set:expr, $value:expr) => { $crate::frontend::Operation(
|
||||||
$crate::middleware::OperationType::Native($crate::middleware::NativeOperation::SetNotContainsFromEntries),
|
$crate::middleware::OperationType::Native($crate::middleware::NativeOperation::SetNotContainsFromEntries),
|
||||||
$crate::op_args!($set, $value), $crate::middleware::OperationAux::MerkleProof($aux)) };
|
$crate::op_args!($set, $value), $crate::middleware::OperationAux::None) };
|
||||||
(array_contains, $array:expr, $index:expr, $value:expr, $aux:expr) => { $crate::frontend::Operation(
|
(array_contains, $array:expr, $index:expr, $value:expr) => { $crate::frontend::Operation(
|
||||||
$crate::middleware::OperationType::Native($crate::middleware::NativeOperation::ArrayContainsFromEntries),
|
$crate::middleware::OperationType::Native($crate::middleware::NativeOperation::ArrayContainsFromEntries),
|
||||||
$crate::op_args!($array, $index, $value), $crate::middleware::OperationAux::MerkleProof($aux)) };
|
$crate::op_args!($array, $index, $value), $crate::middleware::OperationAux::None) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -781,12 +811,7 @@ pub mod tests {
|
||||||
// Check that frontend key-values agree with those embedded in a
|
// Check that frontend key-values agree with those embedded in a
|
||||||
// SignedPod.
|
// SignedPod.
|
||||||
fn check_kvs(pod: &SignedPod) -> Result<()> {
|
fn check_kvs(pod: &SignedPod) -> Result<()> {
|
||||||
let kvs = pod
|
let kvs = pod.kvs.clone().into_iter().collect::<HashMap<_, _>>();
|
||||||
.kvs
|
|
||||||
.clone()
|
|
||||||
.into_iter()
|
|
||||||
.map(|(k, v)| (k, v))
|
|
||||||
.collect::<HashMap<_, _>>();
|
|
||||||
let embedded_kvs = pod
|
let embedded_kvs = pod
|
||||||
.pod
|
.pod
|
||||||
.kvs()
|
.kvs()
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,18 @@ pub enum OperationArg {
|
||||||
Entry(String, Value),
|
Entry(String, Value),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl OperationArg {
|
||||||
|
/// Extracts the value underlying literal and `ValueOf` statement
|
||||||
|
/// operation args.
|
||||||
|
pub(crate) fn value(&self) -> Option<&Value> {
|
||||||
|
match self {
|
||||||
|
Self::Literal(v) => Some(v),
|
||||||
|
Self::Statement(Statement::ValueOf(_, v)) => Some(v),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for OperationArg {
|
impl fmt::Display for OperationArg {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ pub use operation::*;
|
||||||
// use serde::{Deserialize, Serialize};
|
// use serde::{Deserialize, Serialize};
|
||||||
pub use statement::*;
|
pub use statement::*;
|
||||||
|
|
||||||
|
use crate::backends::plonky2::primitives::merkletree::MerkleProof;
|
||||||
|
|
||||||
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`
|
// TODO: Move all value-related types to to `value.rs`
|
||||||
|
|
@ -136,6 +138,16 @@ impl TryFrom<&TypedValue> for i64 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<TypedValue> for Key {
|
||||||
|
type Error = anyhow::Error;
|
||||||
|
fn try_from(tv: TypedValue) -> Result<Self> {
|
||||||
|
match tv {
|
||||||
|
TypedValue::String(s) => Ok(Key::new(s)),
|
||||||
|
_ => Err(anyhow!("Value {} cannot be converted to a key.", tv)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Display for TypedValue {
|
impl fmt::Display for TypedValue {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
|
@ -218,6 +230,30 @@ impl Value {
|
||||||
pub fn raw(&self) -> RawValue {
|
pub fn raw(&self) -> RawValue {
|
||||||
self.raw
|
self.raw
|
||||||
}
|
}
|
||||||
|
/// Determines Merkle existence proof for `key` in `self` (if applicable).
|
||||||
|
pub(crate) fn prove_existence<'a>(
|
||||||
|
&'a self,
|
||||||
|
key: &'a Value,
|
||||||
|
) -> Result<(&'a Value, MerkleProof)> {
|
||||||
|
match &self.typed() {
|
||||||
|
TypedValue::Array(a) => match key.typed() {
|
||||||
|
TypedValue::Int(i) if i >= &0 => a.prove((*i) as usize),
|
||||||
|
_ => Err(anyhow!("Invalid key {} for container {}.", key, self))?,
|
||||||
|
},
|
||||||
|
TypedValue::Dictionary(d) => d.prove(&key.typed().clone().try_into()?),
|
||||||
|
TypedValue::Set(s) => Ok((key, s.prove(key)?)),
|
||||||
|
_ => Err(anyhow!("Invalid container value {}", self.typed())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Determines Merkle non-existence proof for `key` in `self` (if applicable).
|
||||||
|
pub(crate) fn prove_nonexistence<'a>(&'a self, key: &'a Value) -> Result<MerkleProof> {
|
||||||
|
match &self.typed() {
|
||||||
|
TypedValue::Array(_) => Err(anyhow!("Arrays do not support `NotContains` operation.")),
|
||||||
|
TypedValue::Dictionary(d) => d.prove_nonexistence(&key.typed().clone().try_into()?),
|
||||||
|
TypedValue::Set(s) => s.prove_nonexistence(key),
|
||||||
|
_ => Err(anyhow!("Invalid container value {}", self.typed())),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Value can be created from any type Into<TypedValue> type: bool, string-like, i64, ...
|
// A Value can be created from any type Into<TypedValue> type: bool, string-like, i64, ...
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ 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,
|
||||||
middleware::{
|
middleware::{
|
||||||
custom::KeyOrWildcard, AnchoredKey, CustomPredicateBatch, CustomPredicateRef,
|
custom::KeyOrWildcard, AnchoredKey, CustomPredicateBatch, CustomPredicateRef,
|
||||||
NativePredicate, Params, Predicate, Statement, StatementArg, StatementTmplArg, ToFields,
|
NativePredicate, Params, Predicate, Statement, StatementArg, StatementTmplArg, ToFields,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue