chore(middleware): add container op checks and tests (#347)

* Add container op checks

* Formatting & comments

* Formatting
This commit is contained in:
Ahmad Afuni 2025-07-19 10:25:42 +10:00 committed by GitHub
parent 673a461246
commit 1c9b735da6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -5,7 +5,7 @@ use plonky2::field::types::Field;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
backends::plonky2::primitives::merkletree::MerkleProof, backends::plonky2::primitives::merkletree::{MerkleProof, MerkleTree},
middleware::{ middleware::{
hash_values, AnchoredKey, CustomPredicate, CustomPredicateRef, Error, NativePredicate, hash_values, AnchoredKey, CustomPredicate, CustomPredicateRef, Error, NativePredicate,
Params, Predicate, Result, Statement, StatementArg, StatementTmplArg, ToFields, Value, Params, Predicate, Result, Statement, StatementArg, StatementTmplArg, ToFields, Value,
@ -325,14 +325,31 @@ impl Operation {
(Self::NotEqualFromEntries(s1, s2), NotEqual(v3, v4)) => val(v3, s1)? != val(v4, s2)?, (Self::NotEqualFromEntries(s1, s2), NotEqual(v3, v4)) => val(v3, s1)? != val(v4, s2)?,
(Self::LtEqFromEntries(s1, s2), LtEq(v3, v4)) => val(v3, s1)? <= val(v4, s2)?, (Self::LtEqFromEntries(s1, s2), LtEq(v3, v4)) => val(v3, s1)? <= val(v4, s2)?,
(Self::LtFromEntries(s1, s2), Lt(v3, v4)) => val(v3, s1)? < val(v4, s2)?, (Self::LtFromEntries(s1, s2), Lt(v3, v4)) => val(v3, s1)? < val(v4, s2)?,
(Self::ContainsFromEntries(_, _, _, _), Contains(_, _, _)) => (
/* TODO */ Self::ContainsFromEntries(root_s, key_s, val_s, pf),
{ Contains(root_v, key_v, val_v),
) => {
let root = val(root_v, root_s)?;
let key = val(key_v, key_s)?;
let value = val(val_v, val_s)?;
MerkleTree::verify(
params.max_depth_mt_containers,
root.raw().into(),
pf,
&key.raw(),
&value.raw(),
)?;
true true
} }
(Self::NotContainsFromEntries(_, _, _), NotContains(_, _)) => (Self::NotContainsFromEntries(root_s, key_s, pf), NotContains(root_v, key_v)) => {
/* TODO */ let root = val(root_v, root_s)?;
{ let key = val(key_v, key_s)?;
MerkleTree::verify_nonexistence(
params.max_depth_mt_containers,
root.raw().into(),
pf,
&key.raw(),
)?;
true true
} }
( (
@ -542,3 +559,80 @@ pub(crate) fn value_from_op(input_st: &Statement, output_ref: &ValueRef) -> Opti
_ => None, _ => None,
} }
} }
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use crate::{
backends::plonky2::primitives::merkletree::MerkleTree,
middleware::{
hash_value, AnchoredKey, Error, Key, Operation, Params, PodId, Result, Statement,
},
};
#[test]
fn check_container_ops() -> Result<()> {
let params = Params::default();
let pod_id = PodId::default();
let root_ak = AnchoredKey::new(pod_id, Key::new("root".into()));
let key_ak = AnchoredKey::new(pod_id, Key::new("key".into()));
let val_ak = AnchoredKey::new(pod_id, Key::new("value".into()));
// Form Merkle tree
let kvs = (0..10)
.map(|i| (hash_value(&i.into()).into(), i.into()))
.collect::<HashMap<_, _>>();
let mt = MerkleTree::new(params.max_depth_mt_containers, &kvs)?;
let root_s = Statement::Equal(root_ak.clone().into(), mt.root().into());
// Check existence proofs
kvs.iter().try_for_each(|(k, v)| {
// Form op args
let key_s = Statement::Equal(key_ak.clone().into(), (*k).into());
let value_s = Statement::Equal(val_ak.clone().into(), (*v).into());
let (_, pf) = mt.prove(k)?;
// Form op
let op = Operation::ContainsFromEntries(root_s.clone(), key_s, value_s, pf);
// Form output statement
let st = Statement::Contains(
root_ak.clone().into(),
key_ak.clone().into(),
val_ak.clone().into(),
);
// Check op against output statement
op.check(&params, &st).and_then(|ind| {
if ind {
Ok(())
} else {
Err(Error::custom(format!(
"ContainedFromEntries check failed for pair ({},{})",
k, v
)))
}
})
})?;
// Check non-existence proofs similarly
(50..60).try_for_each(|k| {
let key_s = Statement::Equal(key_ak.clone().into(), k.into());
let pf = mt.prove_nonexistence(&k.into())?;
let op = Operation::NotContainsFromEntries(root_s.clone(), key_s, pf);
let st = Statement::NotContains(root_ak.clone().into(), key_ak.clone().into());
op.check(&params, &st).and_then(|ind| {
if ind {
Ok(())
} else {
Err(Error::custom(format!(
"NotContainedFromEntries check failed for key {}",
k
)))
}
})
})
}
}