No Pod IDs (#394)

- middleware:
  - Add `Statement::Intro`
  - Add `SignedBy` native predicate and operation.  The signature is auxiliary data to the operation
  - Rename `PodSigner` to `Signer` with a new API (just for signing `RawValue`)
  - Removed `NewEntry` operation.  Use `ContainsFromEntries` instead
  - Remove `KEY_SIGNER` and `KEY_TYPE` which are no longer used
  - Merge `RecursivePod` and `Pod` traits
  - Change the `Pod::deserialize_data` method to use `Self` instead of `Box<dyn Pod>` 
  - Extend `Pod` trait with these methods:
    - `is_main`: when the pod is Main, in a (recursive) verification its vk will be checked to exist in the vd_set but not if it's intro pod
    - `is_mock`: skip some verifications in the recursive mock MainPod verification
    - `verifier_data_hash`
    - `pod_id` renamed to `statements_hash`
  - AnchoredKeys are now a pair of dictionary root and key
  - Entry statements are now defined as Contains with literal arguments
    - Operations that take Entries now use Contains statements with literal arguments
- frontend:
  - Rename `SignedPod` to `SignedDict` (which now contains the dict, public key and signature, and can still `verify(self)`ed)
  - The `SignedDict` keeps the method `get_statement` for convenience but now it returns a `Contains` statement that proves the existence of the key in the dict
  - The `MainPodBuilder` automatically inserts a `Contains` statement when an operation is added that uses an entry as argument that was not yet "opened".
  - Removed the `literal` methods from the `MainPodBuilder` that were loading literals to anchored keys: that was no longer needed after we introduced literal arguments
- backend
  - Only verify inclusion of the verifying key into the vd_set if the pod is MainPod.  A pod is not MainPod if the first statement is Intro.
  - Reject intro pods that have non-intro statements
  - Empty pod now returns an intro statement
  - Don't insert a type statement automatically in MainPod and MockMainPod.  We get rid of the type entry.
  - Implement `SignedBy` operation, which uses the muxed table to store signature verifications
- Rename `PodId` to `statements_hash` or `sts_hash` for short.  Now this is only used as a hash of the statements for the circuits public inputs.
- Refactor normalization of `self` statements:
  - Before: replace values that contain `SELF` by the given pod_id
  - After: place the verifying key hash into the Intro predicates
This commit is contained in:
Eduard S. 2025-08-27 13:19:40 +02:00 committed by GitHub
parent 122f9c3cac
commit 0e2f7b756e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
39 changed files with 2127 additions and 3064 deletions

View file

@ -6,7 +6,7 @@ use schemars::JsonSchema;
use crate::{
frontend::{AnchoredKey, Error, Result, Statement, StatementArg},
middleware::{
self, hash_str, CustomPredicate, CustomPredicateBatch, Key, NativePredicate, Params, PodId,
self, hash_str, CustomPredicate, CustomPredicateBatch, Hash, Key, NativePredicate, Params,
Predicate, StatementTmpl, StatementTmplArg, ToFields, Value, Wildcard,
},
};
@ -181,8 +181,8 @@ impl CustomPredicateBatchBuilder {
.map(|a| {
Ok::<_, Error>(match a {
BuilderArg::Literal(v) => StatementTmplArg::Literal(v.clone()),
BuilderArg::Key(pod_id_wc, key_str) => StatementTmplArg::AnchoredKey(
resolve_wildcard(args, priv_args, pod_id_wc)?,
BuilderArg::Key(root_wc, key_str) => StatementTmplArg::AnchoredKey(
resolve_wildcard(args, priv_args, root_wc)?,
Key::from(key_str),
),
BuilderArg::WildcardLiteral(v) => {
@ -223,7 +223,7 @@ fn resolve_wildcard(args: &[&str], priv_args: &[&str], s: &str) -> Result<Wildca
.enumerate()
.find_map(|(i, name)| (s == *name).then_some(Wildcard::new(s.to_string(), i)))
.ok_or(Error::custom(format!(
"Wildcard {} not amongst args {:?}",
"Wildcard \"{}\" not amongst args {:?}",
s,
[args.to_vec(), priv_args.to_vec()].concat()
)))
@ -274,15 +274,10 @@ mod tests {
let mut builder = CustomPredicateBatchBuilder::new(params.clone(), "gt_custom_pred".into());
let gt_stb = StatementTmplBuilder::new(NativePredicate::Gt)
.arg(("s1_origin", "s1_key"))
.arg(("s2_origin", "s2_key"));
.arg("s1")
.arg("s2");
builder.predicate_and(
"gt_custom_pred",
&["s1_origin", "s2_origin"],
&[],
&[gt_stb],
)?;
builder.predicate_and("gt_custom_pred", &["s1", "s2"], &[], &[gt_stb])?;
let batch = builder.finish();
let batch_clone = batch.clone();
let gt_custom_pred = CustomPredicateRef::new(batch, 0);
@ -290,11 +285,8 @@ mod tests {
let mut mp_builder = MainPodBuilder::new(&params, vd_set);
// 2 > 1
let s1 = mp_builder.priv_op(Operation::new_entry("s1_key", Value::from(2)))?;
let s2 = mp_builder.priv_op(Operation::new_entry("s2_key", Value::from(1)))?;
// Adding a gt operation will produce a desugared lt operation
let desugared_gt = mp_builder.pub_op(Operation::gt(s1, s2))?;
let desugared_gt = mp_builder.pub_op(Operation::gt(2, 1))?;
assert_eq!(
desugared_gt.predicate(),
Predicate::Native(NativePredicate::Lt)
@ -324,12 +316,12 @@ mod tests {
CustomPredicateBatchBuilder::new(params.clone(), "set_contains_custom_pred".into());
let set_contains_stb = StatementTmplBuilder::new(NativePredicate::SetContains)
.arg(("s1_origin", "s1_key"))
.arg(("s2_origin", "s2_key"));
.arg("s1")
.arg("s2");
builder.predicate_and(
"set_contains_custom_pred",
&["s1_origin", "s2_origin"],
&["s1", "s2"],
&[],
&[set_contains_stb],
)?;
@ -339,11 +331,8 @@ mod tests {
let mut mp_builder = MainPodBuilder::new(&params, vd_set);
let set_values: HashSet<Value> = [1, 2, 3].iter().map(|i| Value::from(*i)).collect();
let s1 = mp_builder.priv_op(Operation::new_entry(
"s1_key",
Value::from(Set::new(params.max_depth_mt_containers, set_values)?),
))?;
let s2 = mp_builder.priv_op(Operation::new_entry("s2_key", Value::from(1)))?;
let s1 = Set::new(params.max_depth_mt_containers, set_values)?;
let s2 = 1;
let set_contains = mp_builder.pub_op(Operation::set_contains(s1, s2))?;
assert_eq!(

View file

@ -4,13 +4,15 @@
use std::{collections::HashMap, convert::From, fmt};
use itertools::Itertools;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
pub use serialization::{SerializedMainPod, SerializedSignedPod};
pub use serialization::SerializedMainPod;
use crate::middleware::{
self, check_custom_pred, check_st_tmpl, hash_op, hash_str, max_op, prod_op, sum_op,
AnchoredKey, Key, MainPodInputs, NativeOperation, OperationAux, OperationType, Params, PodId,
PodProver, PodSigner, Statement, StatementArg, VDSet, Value, ValueRef, KEY_TYPE, SELF,
self, check_custom_pred, check_st_tmpl, containers::Dictionary, hash_op, max_op, prod_op,
sum_op, AnchoredKey, Hash, Key, MainPodInputs, MainPodProver, NativeOperation, OperationAux,
OperationType, Params, PublicKey, RawValue, Signature, Signer, Statement, StatementArg, VDSet,
Value, ValueRef,
};
mod custom;
@ -24,14 +26,14 @@ pub use operation::*;
pub use pod_request::*;
#[derive(Clone, Debug)]
pub struct SignedPodBuilder {
pub struct SignedDictBuilder {
pub params: Params,
pub kvs: HashMap<Key, Value>,
}
impl fmt::Display for SignedPodBuilder {
impl fmt::Display for SignedDictBuilder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "SignedPodBuilder:")?;
writeln!(f, "SignedDictBuilder:")?;
for (k, v) in self.kvs.iter().sorted_by_key(|kv| kv.0.hash()) {
writeln!(f, " - {}: {}", k, v)?;
}
@ -39,7 +41,7 @@ impl fmt::Display for SignedPodBuilder {
}
}
impl SignedPodBuilder {
impl SignedDictBuilder {
pub fn new(params: &Params) -> Self {
Self {
params: params.clone(),
@ -51,65 +53,66 @@ impl SignedPodBuilder {
self.kvs.insert(key.into(), value.into());
}
pub fn sign<S: PodSigner>(&self, signer: &S) -> Result<SignedPod> {
// Sign POD with committed KV store.
let pod = signer.sign(&self.params, &self.kvs)?;
pub fn sign<S: Signer>(&self, signer: &S) -> Result<SignedDict> {
// Sign committed KV store.
let dict = Dictionary::new(self.params.max_depth_mt_containers, self.kvs.clone())?;
// NOTE: This is the same way that `TypedValue::Dictionary` computes the `RawValue`
let msg_raw = RawValue::from(dict.commitment());
let signature = signer.sign(msg_raw);
Ok(SignedPod::new(pod))
Ok(SignedDict {
dict,
public_key: signer.public_key(),
signature,
})
}
}
/// SignedPod is a wrapper on top of backend::SignedPod, which additionally stores the
/// string<-->hash relation of the keys.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(try_from = "SerializedSignedPod", into = "SerializedSignedPod")]
pub struct SignedPod {
pub pod: Box<dyn middleware::Pod>,
// We store a copy of the key values for quick access
kvs: HashMap<Key, Value>,
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
// #[serde(try_from = "SerializedSignedDict", into = "SerializedSignedDict")]
pub struct SignedDict {
pub dict: Dictionary,
pub public_key: PublicKey,
pub signature: Signature,
}
impl fmt::Display for SignedPod {
impl fmt::Display for SignedDict {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "SignedPod (id:{}):", self.id())?;
writeln!(f, "SignedDict (raw:{}):", self.dict.commitment())?;
// Note: current version iterates sorting by keys of the kvs, but the merkletree defined at
// https://0xparc.github.io/pod2/merkletree.html will not need it since it will be
// deterministic based on the keys values not on the order of the keys when added into the
// tree.
for (k, v) in self.pod.kvs().iter().sorted_by_key(|kv| kv.0.key.hash()) {
for (k, v) in self.dict.kvs().iter().sorted_by_key(|kv| kv.0.hash()) {
writeln!(f, " - {} = {}", k, v)?;
}
Ok(())
}
}
impl SignedPod {
pub fn new(pod: Box<dyn middleware::Pod>) -> Self {
let kvs = pod
.kvs()
.into_iter()
.map(|(AnchoredKey { key, .. }, v)| (key, v))
.collect();
Self { pod, kvs }
}
pub fn id(&self) -> PodId {
self.pod.id()
}
impl SignedDict {
pub fn verify(&self) -> Result<()> {
self.pod.verify().map_err(Error::Backend)
self.signature
.verify(self.public_key, RawValue::from(self.dict.commitment()))
.then_some(())
.ok_or(Error::custom("Invalid signature!"))
}
pub fn kvs(&self) -> &HashMap<Key, Value> {
&self.kvs
self.dict.kvs()
}
pub fn get(&self, key: impl Into<Key>) -> Option<&Value> {
self.kvs.get(&key.into())
self.kvs().get(&key.into())
}
// Returns the Equal statement that defines key if it exists.
// Returns the Contains statement that defines key if it exists.
pub fn get_statement(&self, key: impl Into<Key>) -> Option<Statement> {
let key: Key = key.into();
self.kvs()
.get(&key)
.map(|value| Statement::equal(AnchoredKey::from((self.id(), key)), value.clone()))
self.kvs().get(&key).map(|value| {
Statement::Contains(
ValueRef::Literal(Value::from(self.dict.clone())),
ValueRef::Literal(Value::from(key.name())),
ValueRef::Literal(value.clone()),
)
})
}
}
@ -119,27 +122,20 @@ impl SignedPod {
pub struct MainPodBuilder {
pub params: Params,
pub vd_set: VDSet,
pub input_signed_pods: Vec<SignedPod>,
pub input_recursive_pods: Vec<MainPod>,
pub input_pods: Vec<MainPod>,
pub statements: Vec<Statement>,
pub operations: Vec<Operation>,
pub public_statements: Vec<Statement>,
// Internal state
/// Counter for constants created from literals
const_cnt: usize,
/// Map from (public, Value) to Key of already created literals via Equal statements.
literals: HashMap<(bool, Value), Key>,
// TODO: track contains ops with literals added explicitly as well.
dict_contains: Vec<(Value, Value)>, // (root, key)
}
impl fmt::Display for MainPodBuilder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "MainPod:")?;
writeln!(f, " input_signed_pods:")?;
for in_pod in &self.input_signed_pods {
writeln!(f, " - {}", in_pod.id())?;
}
writeln!(f, " input_main_pods:")?;
for in_pod in &self.input_recursive_pods {
for in_pod in &self.input_pods {
writeln!(f, " - {}", in_pod.id())?;
}
writeln!(f, " statements:")?;
@ -157,20 +153,15 @@ impl MainPodBuilder {
Self {
params: params.clone(),
vd_set: vd_set.clone(),
input_signed_pods: Vec::new(),
input_recursive_pods: Vec::new(),
input_pods: Vec::new(),
statements: Vec::new(),
operations: Vec::new(),
public_statements: Vec::new(),
const_cnt: 0,
literals: HashMap::new(),
dict_contains: Vec::new(),
}
}
pub fn add_signed_pod(&mut self, pod: &SignedPod) {
self.input_signed_pods.push(pod.clone());
}
pub fn add_recursive_pod(&mut self, pod: MainPod) {
self.input_recursive_pods.push(pod);
pub fn add_pod(&mut self, pod: MainPod) {
self.input_pods.push(pod);
}
pub fn insert(&mut self, public: bool, st_op: (Statement, Operation)) -> Result<()> {
// TODO: Do error handling instead of panic
@ -384,12 +375,9 @@ impl MainPodBuilder {
let st = match op.0 {
OperationType::Native(o) => {
let native_arg_error = move || Error::op_invalid_args(format!("{o:?}"));
match (o, &op.1.as_slice()) {
(None, &[]) => Statement::None,
(NewEntry, &[OperationArg::Entry(k, v)]) => {
Statement::equal(AnchoredKey::from((SELF, k.as_str())), v.clone())
}
(EqualFromEntries, &[a1, a2]) => {
match (o, &op.1.as_slice(), &op.2) {
(None, &[], _) => Statement::None,
(EqualFromEntries, &[a1, a2], _) => {
let (r1, v1) = a1.value_and_ref().ok_or_else(native_arg_error)?;
let (r2, v2) = a2.value_and_ref().ok_or_else(native_arg_error)?;
if v1 == v2 {
@ -398,7 +386,7 @@ impl MainPodBuilder {
return Err(native_arg_error());
}
}
(NotEqualFromEntries, &[a1, a2]) => {
(NotEqualFromEntries, &[a1, a2], _) => {
let (r1, v1) = a1.value_and_ref().ok_or_else(native_arg_error)?;
let (r2, v2) = a2.value_and_ref().ok_or_else(native_arg_error)?;
if v1 != v2 {
@ -407,7 +395,7 @@ impl MainPodBuilder {
return Err(native_arg_error());
}
}
(LtFromEntries, &[a1, a2]) => {
(LtFromEntries, &[a1, a2], _) => {
let (r1, v1) = a1.int_value_and_ref().ok_or_else(native_arg_error)?;
let (r2, v2) = a2.int_value_and_ref().ok_or_else(native_arg_error)?;
if v1 < v2 {
@ -416,7 +404,7 @@ impl MainPodBuilder {
return Err(native_arg_error());
}
}
(LtEqFromEntries, &[a1, a2]) => {
(LtEqFromEntries, &[a1, a2], _) => {
let (r1, v1) = a1.int_value_and_ref().ok_or_else(native_arg_error)?;
let (r2, v2) = a2.int_value_and_ref().ok_or_else(native_arg_error)?;
if v1 <= v2 {
@ -425,10 +413,11 @@ impl MainPodBuilder {
return Err(native_arg_error());
}
}
(CopyStatement, &[OperationArg::Statement(s)]) => s.clone(),
(CopyStatement, &[OperationArg::Statement(s)], _) => s.clone(),
(
TransitiveEqualFromStatements,
&[OperationArg::Statement(Statement::Equal(r1, r2)), OperationArg::Statement(Statement::Equal(r3, r4))],
_,
) => {
if r2 == r3 {
Statement::Equal(r1.clone(), r4.clone())
@ -436,10 +425,10 @@ impl MainPodBuilder {
return Err(native_arg_error());
}
}
(LtToNotEqual, &[OperationArg::Statement(Statement::Lt(r1, r2))]) => {
(LtToNotEqual, &[OperationArg::Statement(Statement::Lt(r1, r2))], _) => {
Statement::NotEqual(r1.clone(), r2.clone())
}
(SumOf, &[a1, a2, a3]) => {
(SumOf, &[a1, a2, a3], _) => {
let (r1, v1) = a1.value_and_ref().ok_or_else(native_arg_error)?;
let (r2, v2) = a2.value_and_ref().ok_or_else(native_arg_error)?;
let (r3, v3) = a3.value_and_ref().ok_or_else(native_arg_error)?;
@ -449,7 +438,7 @@ impl MainPodBuilder {
return Err(native_arg_error());
}
}
(ProductOf, &[a1, a2, a3]) => {
(ProductOf, &[a1, a2, a3], _) => {
let (r1, v1) = a1.value_and_ref().ok_or_else(native_arg_error)?;
let (r2, v2) = a2.value_and_ref().ok_or_else(native_arg_error)?;
let (r3, v3) = a3.value_and_ref().ok_or_else(native_arg_error)?;
@ -459,7 +448,7 @@ impl MainPodBuilder {
return Err(native_arg_error());
}
}
(MaxOf, &[a1, a2, a3]) => {
(MaxOf, &[a1, a2, a3], _) => {
let (r1, v1) = a1.value_and_ref().ok_or_else(native_arg_error)?;
let (r2, v2) = a2.value_and_ref().ok_or_else(native_arg_error)?;
let (r3, v3) = a3.value_and_ref().ok_or_else(native_arg_error)?;
@ -469,7 +458,7 @@ impl MainPodBuilder {
return Err(native_arg_error());
}
}
(HashOf, &[a1, a2, a3]) => {
(HashOf, &[a1, a2, a3], _) => {
let (r1, v1) = a1.value_and_ref().ok_or_else(native_arg_error)?;
let (r2, v2) = a2.value_and_ref().ok_or_else(native_arg_error)?;
let (r3, v3) = a3.value_and_ref().ok_or_else(native_arg_error)?;
@ -479,20 +468,20 @@ impl MainPodBuilder {
return Err(native_arg_error());
}
}
(ContainsFromEntries, &[a1, a2, a3]) => {
(ContainsFromEntries, &[a1, a2, a3], _) => {
let (r1, _v1) = a1.value_and_ref().ok_or_else(native_arg_error)?;
let (r2, _v2) = a2.value_and_ref().ok_or_else(native_arg_error)?;
let (r3, _v3) = a3.value_and_ref().ok_or_else(native_arg_error)?;
// TODO: validate proof
Statement::Contains(r1, r2, r3)
}
(NotContainsFromEntries, &[a1, a2]) => {
(NotContainsFromEntries, &[a1, a2], _) => {
let (r1, _v1) = a1.value_and_ref().ok_or_else(native_arg_error)?;
let (r2, _v2) = a2.value_and_ref().ok_or_else(native_arg_error)?;
// TODO: validate proof
Statement::NotContains(r1, r2)
}
(PublicKeyOf, &[a1, a2]) => {
(PublicKeyOf, &[a1, a2], _) => {
let (r1, v1) = a1.value_and_ref().ok_or_else(native_arg_error)?;
let (r2, v2) = a2.value_and_ref().ok_or_else(native_arg_error)?;
if middleware::Operation::check_public_key(v1, v2)? {
@ -501,7 +490,16 @@ impl MainPodBuilder {
return Err(native_arg_error());
}
}
(ContainerInsertFromEntries, &[a1, a2, a3, a4]) => {
(SignedBy, &[a1, a2], OperationAux::Signature(sig)) => {
let (r1, v1) = a1.value_and_ref().ok_or_else(native_arg_error)?;
let (r2, v2) = a2.value_and_ref().ok_or_else(native_arg_error)?;
if middleware::Operation::check_signed_by(v1, v2, sig)? {
Statement::SignedBy(r1, r2)
} else {
return Err(native_arg_error());
}
}
(ContainerInsertFromEntries, &[a1, a2, a3, a4], _) => {
let (r1, _v1) = a1.value_and_ref().ok_or_else(native_arg_error)?;
let (r2, _v2) = a2.value_and_ref().ok_or_else(native_arg_error)?;
let (r3, _v3) = a3.value_and_ref().ok_or_else(native_arg_error)?;
@ -509,7 +507,7 @@ impl MainPodBuilder {
// TODO: validate proof
Statement::ContainerInsert(r1, r2, r3, r4)
}
(ContainerUpdateFromEntries, &[a1, a2, a3, a4]) => {
(ContainerUpdateFromEntries, &[a1, a2, a3, a4], _) => {
let (r1, _v1) = a1.value_and_ref().ok_or_else(native_arg_error)?;
let (r2, _v2) = a2.value_and_ref().ok_or_else(native_arg_error)?;
let (r3, _v3) = a3.value_and_ref().ok_or_else(native_arg_error)?;
@ -517,14 +515,14 @@ impl MainPodBuilder {
// TODO: validate proof
Statement::ContainerUpdate(r1, r2, r3, r4)
}
(ContainerDeleteFromEntries, &[a1, a2, a3]) => {
(ContainerDeleteFromEntries, &[a1, a2, a3], _) => {
let (r1, _v1) = a1.value_and_ref().ok_or_else(native_arg_error)?;
let (r2, _v2) = a2.value_and_ref().ok_or_else(native_arg_error)?;
let (r3, _v3) = a3.value_and_ref().ok_or_else(native_arg_error)?;
// TODO: validate proof
Statement::ContainerDelete(r1, r2, r3)
}
(t, _) => {
(t, _, _) => {
if t.is_syntactic_sugar() {
return Err(Error::custom(format!(
"Unexpected syntactic sugar: {:?}",
@ -583,7 +581,28 @@ impl MainPodBuilder {
Ok(st)
}
/// For every operation that has Entry statements as arguments we add a Contains statement to
/// open the dictionary.
fn add_entries_contains(&mut self, op: &Operation) -> Result<()> {
for arg in &op.1 {
if let OperationArg::Statement(Statement::Contains(
ValueRef::Literal(dict),
ValueRef::Literal(key),
ValueRef::Literal(v),
)) = arg
{
let root_key = (dict.clone(), key.clone());
if !self.dict_contains.contains(&root_key) {
self.dict_contains.push(root_key);
self.priv_op(Operation::dict_contains(dict, key, v))?;
}
}
}
Ok(())
}
fn op(&mut self, public: bool, op: Operation) -> Result<Statement> {
self.add_entries_contains(&op)?;
let op = Self::fill_in_aux(Self::lower_op(op)?)?;
let st = self.op_statement(op.clone())?;
self.insert(public, (st, op))?;
@ -591,48 +610,13 @@ impl MainPodBuilder {
Ok(self.statements[self.statements.len() - 1].clone())
}
/// Convenience method for introducing public constants.
pub fn pub_literal(&mut self, v: impl Into<Value>) -> Result<Statement> {
self.literal(true, v.into())
}
/// Convenience method for introducing private constants.
pub fn priv_literal(&mut self, v: impl Into<Value>) -> Result<Statement> {
self.literal(false, v.into())
}
fn literal(&mut self, public: bool, value: Value) -> Result<Statement> {
let public_value = (public, value);
if let Some(key) = self.literals.get(&public_value) {
Ok(Statement::equal(
AnchoredKey::new(SELF, key.clone()),
public_value.1,
))
} else {
let key = format!("c{}", self.const_cnt);
self.literals
.insert(public_value.clone(), Key::new(key.clone()));
self.const_cnt += 1;
self.op(
public,
Operation(
OperationType::Native(NativeOperation::NewEntry),
vec![OperationArg::Entry(key.clone(), public_value.1)],
OperationAux::None,
),
)
}
}
pub fn reveal(&mut self, st: &Statement) {
self.public_statements.push(st.clone());
}
pub fn prove(&self, prover: &dyn PodProver) -> Result<MainPod> {
pub fn prove(&self, prover: &dyn MainPodProver) -> Result<MainPod> {
let compiler = MainPodCompiler::new(&self.params);
let inputs = MainPodCompilerInputs {
// signed_pods: &self.input_signed_pods,
// main_pods: &self.input_main_pods,
statements: &self.statements,
operations: &self.operations,
public_statements: &self.public_statements,
@ -641,67 +625,18 @@ impl MainPodBuilder {
let (statements, operations, public_statements) = compiler.compile(inputs, &self.params)?;
let inputs = MainPodInputs {
signed_pods: &self
.input_signed_pods
.iter()
.map(|p| p.pod.as_ref())
.collect_vec(),
recursive_pods: &self
.input_recursive_pods
.iter()
.map(|p| p.pod.as_ref())
.collect_vec(),
pods: &self.input_pods.iter().map(|p| p.pod.as_ref()).collect_vec(),
statements: &statements,
operations: &operations,
public_statements: &public_statements,
vd_set: self.vd_set.clone(),
};
let pod = prover.prove(&self.params, &self.vd_set, inputs)?;
// Gather public statements, making sure to inject the type
// information specified by the backend.
let pod_id = pod.id();
let type_key_hash = hash_str(KEY_TYPE);
let type_statement = pod
.pub_statements()
.into_iter()
.find_map(|s| match s.as_entry() {
Some((AnchoredKey { pod_id: id, key }, _))
if id == &pod_id && key.hash() == type_key_hash =>
{
Some(s)
}
_ => None,
})
.ok_or(Error::custom(format!(
// TODO use a specific Error
"Missing POD type information in POD: {:?}",
pod
)))?;
// Replace instances of `SELF` with the POD ID for consistency
// with `pub_statements` method.
let public_statements = [type_statement]
.into_iter()
.chain(self.public_statements.clone().into_iter().map(|s| {
let s_type = s.predicate();
let s_args = s
.args()
.into_iter()
.map(|arg| match arg {
StatementArg::Key(AnchoredKey { pod_id: id, key }) if id == SELF => {
StatementArg::Key(AnchoredKey::new(pod_id, key))
}
_ => arg,
})
.collect();
Statement::from_args(s_type, s_args).expect("valid arguments")
}))
.collect();
let pod = prover.prove(&self.params, inputs)?;
Ok(MainPod {
pod,
params: self.params.clone(),
public_statements,
public_statements: self.public_statements.clone(),
})
}
}
@ -709,30 +644,26 @@ impl MainPodBuilder {
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(try_from = "SerializedMainPod", into = "SerializedMainPod")]
pub struct MainPod {
pub pod: Box<dyn middleware::RecursivePod>,
pub pod: Box<dyn middleware::Pod>,
pub public_statements: Vec<Statement>,
pub params: Params,
}
impl fmt::Display for MainPod {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "MainPod: {}", self.pod.id())?;
writeln!(f, "MainPod: {}", self.pod.statements_hash())?;
writeln!(f, " valid? {}", self.pod.verify().is_ok())?;
writeln!(f, " statements:")?;
for st in &self.pod.pub_statements() {
writeln!(f, " - {}", st)?;
}
writeln!(f, " kvs:")?;
for (k, v) in &self.pod.kvs() {
writeln!(f, " - {}: {}", k, v)?;
}
Ok(())
}
}
impl MainPod {
pub fn id(&self) -> PodId {
self.pod.id()
pub fn id(&self) -> Hash {
self.pod.statements_hash()
}
/// Returns the value of a Equal statement with self id that defines key if it exists.
@ -742,7 +673,7 @@ impl MainPod {
.iter()
.find_map(|st| match st {
Statement::Equal(ValueRef::Key(ak), ValueRef::Literal(value))
if ak.pod_id == self.id() && ak.key.hash() == key.hash() =>
if ak.root == self.id() && ak.key.hash() == key.hash() =>
{
Some(value)
}
@ -753,8 +684,6 @@ impl MainPod {
}
struct MainPodCompilerInputs<'a> {
// pub signed_pods: &'a [Box<dyn middleware::SignedPod>],
// pub main_pods: &'a [Box<dyn middleware::MainPod>],
pub statements: &'a [Statement],
pub operations: &'a [Operation],
pub public_statements: &'a [Statement],
@ -832,8 +761,6 @@ impl MainPodCompiler {
Vec<Statement>, // public statements
)> {
let MainPodCompilerInputs {
// signed_pods: _,
// main_pods: _,
statements,
operations,
public_statements,
@ -853,16 +780,17 @@ pub mod tests {
use super::*;
use crate::{
backends::plonky2::{
mock::mainpod::MockProver, primitives::ec::schnorr::SecretKey, signedpod::Signer,
mock::mainpod::MockProver, primitives::ec::schnorr::SecretKey, signer::Signer,
},
dict,
examples::{
attest_eth_friend, custom::eth_dos_request, great_boy_pod_full_flow,
tickets_pod_full_flow, zu_kyc_pod_builder, zu_kyc_pod_request,
zu_kyc_sign_pod_builders, EthDosHelper, MOCK_VD_SET,
zu_kyc_sign_dict_builders, EthDosHelper, MOCK_VD_SET,
},
middleware::{
containers::{Array, Dictionary, Set},
Value,
containers::{Array, Set},
Signer as _, Value,
},
};
@ -874,44 +802,21 @@ pub mod tests {
Ok(())
}
// Check that frontend key-values agree with those embedded in a
// SignedPod.
fn check_kvs(pod: &SignedPod) -> Result<()> {
let kvs = pod.kvs.clone().into_iter().collect::<HashMap<_, _>>();
let embedded_kvs = pod
.pod
.kvs()
.into_iter()
.map(|(middleware::AnchoredKey { key, .. }, v)| (key, v))
.collect::<HashMap<_, _>>();
if kvs == embedded_kvs {
Ok(())
} else {
Err(Error::custom(format!(
"KVs {:?} do not agree with those embedded in the POD: {:?}",
kvs, embedded_kvs
)))
}
}
#[test]
fn test_front_zu_kyc() -> Result<()> {
let params = Params::default();
let vd_set = &*MOCK_VD_SET;
let (gov_id, pay_stub) = zu_kyc_sign_pod_builders(&params);
let (gov_id, pay_stub) = zu_kyc_sign_dict_builders(&params);
println!("{}", gov_id);
println!("{}", pay_stub);
let signer = Signer(SecretKey(1u32.into()));
let gov_id = gov_id.sign(&signer)?;
check_kvs(&gov_id)?;
let gov_id_signer = Signer(SecretKey(1u32.into()));
let gov_id = gov_id.sign(&gov_id_signer)?;
println!("{}", gov_id);
let signer = Signer(SecretKey(2u32.into()));
let pay_stub = pay_stub.sign(&signer)?;
check_kvs(&pay_stub)?;
let pay_stub_signer = Signer(SecretKey(2u32.into()));
let pay_stub = pay_stub.sign(&pay_stub_signer)?;
println!("{}", pay_stub);
let kyc_builder = zu_kyc_pod_builder(&params, vd_set, &gov_id, &pay_stub)?;
@ -923,14 +828,22 @@ pub mod tests {
println!("{}", kyc);
kyc.pod.verify()?;
let request = zu_kyc_pod_request(
gov_id.get("_signer").unwrap(),
pay_stub.get("_signer").unwrap(),
&Value::from(gov_id_signer.public_key()),
&Value::from(pay_stub_signer.public_key()),
)?;
// Check the bindings of the "gov" and "pay" wildcards from the PodRequest
let bindings = request.exact_match_pod(&*kyc.pod).unwrap();
assert_eq!(*bindings.get("gov").unwrap(), gov_id.id().into());
assert_eq!(*bindings.get("pay").unwrap(), pay_stub.id().into());
assert_eq!(
*bindings.get("gov").unwrap(),
gov_id.dict.commitment().into()
);
assert_eq!(
*bindings.get("pay").unwrap(),
pay_stub.dict.commitment().into()
);
check_public_statements(&kyc)
}
@ -950,7 +863,7 @@ pub mod tests {
let charlie = Signer(SecretKey(3u32.into()));
let david = Signer(SecretKey(4u32.into()));
let helper = EthDosHelper::new(&params, vd_set, true, alice.public_key())?;
let helper = EthDosHelper::new(&params, vd_set, alice.public_key())?;
let prover = MockProver {};
@ -960,8 +873,8 @@ pub mod tests {
let request = eth_dos_request()?;
assert!(request.exact_match_pod(&*dist_1.pod).is_ok());
let bindings = request.exact_match_pod(&*dist_1.pod).unwrap();
assert_eq!(*bindings.get("src").unwrap(), alice.public_key());
assert_eq!(*bindings.get("dst").unwrap(), bob.public_key());
assert_eq!(*bindings.get("src").unwrap(), alice.public_key().into());
assert_eq!(*bindings.get("dst").unwrap(), bob.public_key().into());
assert_eq!(*bindings.get("distance").unwrap(), 1.into());
let bob_attestation = attest_eth_friend(&params, &bob, charlie.public_key());
@ -971,8 +884,8 @@ pub mod tests {
dist_2.pod.verify()?;
assert!(request.exact_match_pod(&*dist_2.pod).is_ok());
let bindings = request.exact_match_pod(&*dist_2.pod).unwrap();
assert_eq!(*bindings.get("src").unwrap(), alice.public_key());
assert_eq!(*bindings.get("dst").unwrap(), charlie.public_key());
assert_eq!(*bindings.get("src").unwrap(), alice.public_key().into());
assert_eq!(*bindings.get("dst").unwrap(), charlie.public_key().into());
assert_eq!(*bindings.get("distance").unwrap(), 2.into());
let charlie_attestation = attest_eth_friend(&params, &charlie, david.public_key());
@ -982,8 +895,8 @@ pub mod tests {
dist_3.pod.verify()?;
assert!(request.exact_match_pod(&*dist_3.pod).is_ok());
let bindings = request.exact_match_pod(&*dist_3.pod).unwrap();
assert_eq!(*bindings.get("src").unwrap(), alice.public_key());
assert_eq!(*bindings.get("dst").unwrap(), david.public_key());
assert_eq!(*bindings.get("src").unwrap(), alice.public_key().into());
assert_eq!(*bindings.get("dst").unwrap(), david.public_key().into());
assert_eq!(*bindings.get("distance").unwrap(), 3.into());
Ok(())
@ -1014,25 +927,22 @@ pub mod tests {
let params = Params::default();
let vd_set = &*MOCK_VD_SET;
let mut signed_builder = SignedPodBuilder::new(&params);
let mut signed_builder = SignedDictBuilder::new(&params);
signed_builder.insert("a", 1);
signed_builder.insert("b", 1);
let signer = Signer(SecretKey(1u32.into()));
let signed_pod = signed_builder.sign(&signer).unwrap();
let signed_dict = signed_builder.sign(&signer).unwrap();
let mut builder = MainPodBuilder::new(&params, vd_set);
builder.add_signed_pod(&signed_pod);
//let op_val1 = Operation{
// OperationType::Native(NativeOperation::CopyStatement),
// signed_pod.
//}
builder
.pub_op(Operation::dict_signed_by(&signed_dict))
.unwrap();
let op_eq1 = Operation(
OperationType::Native(NativeOperation::EqualFromEntries),
vec![
OperationArg::from((&signed_pod, "a")),
OperationArg::from((&signed_pod, "b")),
OperationArg::from((&signed_dict, "a")),
OperationArg::from((&signed_dict, "b")),
],
OperationAux::None,
);
@ -1040,8 +950,8 @@ pub mod tests {
let op_eq2 = Operation(
OperationType::Native(NativeOperation::EqualFromEntries),
vec![
OperationArg::from((&signed_pod, "b")),
OperationArg::from((&signed_pod, "a")),
OperationArg::from((&signed_dict, "b")),
OperationArg::from((&signed_dict, "a")),
],
OperationAux::None,
);
@ -1065,17 +975,21 @@ pub mod tests {
fn test_false_st() {
let params = Params::default();
let vd_set = &*MOCK_VD_SET;
let mut builder = SignedPodBuilder::new(&params);
let mut builder = SignedDictBuilder::new(&params);
builder.insert("num", 2);
let signer = Signer(SecretKey(1u32.into()));
let pod = builder.sign(&signer).unwrap();
let signed_dict = builder.sign(&signer).unwrap();
println!("{}", pod);
println!("{}", signed_dict);
let mut builder = MainPodBuilder::new(&params, vd_set);
builder.add_signed_pod(&pod);
builder.pub_op(Operation::gt((&pod, "num"), 5)).unwrap();
builder
.pub_op(Operation::dict_signed_by(&signed_dict))
.unwrap();
builder
.pub_op(Operation::gt((&signed_dict, "num"), 5))
.unwrap();
let prover = MockProver {};
let false_pod = builder.prove(&prover).unwrap();
@ -1088,26 +1002,28 @@ pub mod tests {
fn test_dictionaries() -> Result<()> {
let params = Params::default();
let vd_set = &*MOCK_VD_SET;
let mut builder = SignedPodBuilder::new(&params);
let mut builder = SignedDictBuilder::new(&params);
let mut my_dict_kvs: HashMap<Key, Value> = HashMap::new();
my_dict_kvs.insert(Key::from("a"), Value::from(1));
my_dict_kvs.insert(Key::from("b"), Value::from(2));
my_dict_kvs.insert(Key::from("c"), Value::from(3));
// let my_dict_as_mt = MerkleTree::new(5, &my_dict_kvs).unwrap();
// let dict = Dictionary { mt: my_dict_as_mt };
let dict = Dictionary::new(params.max_depth_mt_containers, my_dict_kvs)?;
let dict = dict!(params.max_depth_mt_containers, {
"a" => 1,
"b" => 2,
"c" => 3,
})?;
let dict_root = Value::from(dict.clone());
builder.insert("dict", dict_root);
let signer = Signer(SecretKey(1u32.into()));
let pod = builder.sign(&signer).unwrap();
let signed_dict = builder.sign(&signer).unwrap();
let mut builder = MainPodBuilder::new(&params, vd_set);
builder.add_signed_pod(&pod);
let st0 = pod.get_statement("dict").unwrap();
let st1 = builder.op(true, Operation::new_entry("key", "a")).unwrap();
let st2 = builder.literal(false, Value::from(1)).unwrap();
builder
.pub_op(Operation::dict_signed_by(&signed_dict))
.unwrap();
let st0 = signed_dict.get_statement("dict").unwrap();
let local = dict!(32, {"key" => "a"})?;
let st1 = builder
.op(true, Operation::dict_contains(local, "key", "a"))
.unwrap();
builder.pub_op(Operation(
// OperationType
@ -1116,7 +1032,7 @@ pub mod tests {
vec![
OperationArg::Statement(st0.clone()),
OperationArg::Statement(st1),
OperationArg::Statement(st2),
OperationArg::Literal(Value::from(1)),
],
OperationAux::MerkleProof(dict.prove(&Key::from("a")).unwrap().1),
))?;
@ -1259,25 +1175,35 @@ pub mod tests {
let sk = SecretKey::new_rand();
let pk = sk.public_key();
// Signed POD contains public key as owner
let mut builder = SignedPodBuilder::new(&params);
// Signed Dict contains public key as owner
let mut builder = SignedDictBuilder::new(&params);
builder.insert("owner", Value::from(pk));
builder.insert("other_data", Value::from(123));
let signer = Signer(SecretKey(1u32.into()));
let signed_pod = builder.sign(&signer).unwrap();
let signed_dict = builder.sign(&signer).unwrap();
// Main POD proves ownership of the owner's secret key.
let mut builder = MainPodBuilder::new(&params, vd_set);
builder.add_signed_pod(&signed_pod);
let st0 = signed_pod.get_statement("owner").unwrap();
let st1 = builder
.priv_op(Operation::new_entry("known_secret", Value::from(sk)))
.unwrap();
builder.pub_op(Operation::signed_by(
Value::from(signed_dict.dict.clone()),
Value::from(signed_dict.public_key),
signed_dict.signature.clone(),
))?;
let st0 = builder.priv_op(Operation::dict_contains(
signed_dict.dict,
"owner",
Value::from(pk),
))?;
let local = dict!(32, { "known_secret" => sk.clone() })?;
let st1 = builder.priv_op(Operation::dict_contains(
local,
"known_secret",
Value::from(sk),
))?;
builder
.pub_op(Operation(
// OperationType
OperationType::Native(NativeOperation::PublicKeyOf),
// Vec<OperationArg>
vec![OperationArg::Statement(st0), OperationArg::Statement(st1)],
OperationAux::None,
))
@ -1301,22 +1227,25 @@ pub mod tests {
let pk = sk.public_key();
// Signed POD contains public key as owner
let mut builder = SignedPodBuilder::new(&params);
let mut builder = SignedDictBuilder::new(&params);
builder.insert("owner", Value::from(pk));
builder.insert("other_data", Value::from(123));
let signer = Signer(SecretKey(1u32.into()));
let signed_pod = builder.sign(&signer).unwrap();
let signed_dict = builder.sign(&signer).unwrap();
// Try to build with the wrong secret key. The pre-proving checks
// will catch this.
let mut builder = MainPodBuilder::new(&params, vd_set);
builder.add_signed_pod(&signed_pod);
let st0 = signed_pod.get_statement("owner").unwrap();
builder
.pub_op(Operation::dict_signed_by(&signed_dict))
.unwrap();
let st0 = signed_dict.get_statement("owner").unwrap();
let local = dict!(32, {"known_secret" => SecretKey(BigUint::from(123u32))})?;
let st1 = builder
.priv_op(Operation::new_entry(
"known_secret",
Value::from(SecretKey(BigUint::from(123u32))),
))
.op(
true,
Operation::dict_contains(local, "known_secret", SecretKey(BigUint::from(123u32))),
)
.unwrap();
assert!(builder
.pub_op(Operation(
@ -1341,33 +1270,30 @@ pub mod tests {
// Try to build with wrong type in 1st arg
let mut builder = MainPodBuilder::new(&params, vd_set);
let st_pk = builder.literal(false, Value::from(pk)).unwrap();
let st_int1 = builder.literal(false, Value::from(123)).unwrap();
let int2 = Value::from(123);
let sk = Value::from(sk);
assert!(builder
.pub_op(Operation(
// OperationType
OperationType::Native(NativeOperation::PublicKeyOf),
// Vec<OperationArg>
vec![
OperationArg::Statement(st_pk),
OperationArg::Statement(st_int1),
],
vec![OperationArg::Literal(int2), OperationArg::Literal(sk),],
OperationAux::None,
))
.is_err());
// Try to build with wrong type in 2nd arg
builder = MainPodBuilder::new(&params, vd_set);
let st_sk = builder.literal(false, Value::from(pk)).unwrap();
let st_int2 = builder.literal(false, Value::from(123)).unwrap();
let mut builder = MainPodBuilder::new(&params, vd_set);
let pk = Value::from(pk);
let int1 = Value::from(123);
assert!(builder
.pub_op(Operation(
// OperationType
OperationType::Native(NativeOperation::PublicKeyOf),
// Vec<OperationArg>
vec![
OperationArg::Statement(st_int2),
OperationArg::Statement(st_sk),
OperationArg::Literal(pk.clone()),
OperationArg::Literal(int1),
],
OperationAux::None,
))
@ -1376,33 +1302,6 @@ pub mod tests {
Ok(())
}
#[should_panic]
#[test]
fn test_reject_duplicate_new_entry() {
// try to insert the same key multiple times
// right now this is not caught when you build the pod,
// but it is caught on verify
env_logger::init();
let params = Params::default();
let vd_set = &*MOCK_VD_SET;
let mut builder = MainPodBuilder::new(&params, vd_set);
let st = Statement::equal(AnchoredKey::from((SELF, "a")), Value::from(3));
let op_new_entry = Operation(
OperationType::Native(NativeOperation::NewEntry),
vec![],
OperationAux::None,
);
builder.insert(false, (st, op_new_entry.clone())).unwrap();
let st = Statement::equal(AnchoredKey::from((SELF, "a")), Value::from(28));
builder.insert(false, (st, op_new_entry.clone())).unwrap();
let prover = MockProver {};
let pod = builder.prove(&prover).unwrap();
pod.pod.verify().unwrap();
}
#[should_panic]
#[test]
fn test_reject_unsound_statement() {
@ -1411,23 +1310,25 @@ pub mod tests {
let params = Params::default();
let vd_set = &*MOCK_VD_SET;
let mut builder = MainPodBuilder::new(&params, vd_set);
let self_a = AnchoredKey::from((SELF, "a"));
let self_b = AnchoredKey::from((SELF, "b"));
let value_of_a = Statement::equal(self_a.clone(), Value::from(3));
let value_of_b = Statement::equal(self_b.clone(), Value::from(27));
let local = dict!(32, {"a" => 3, "b" => 27}).unwrap();
let value_of_a = Statement::contains(local.clone(), "a", 3);
let value_of_b = Statement::contains(local.clone(), "b", 27);
let op_new_entry = Operation(
OperationType::Native(NativeOperation::NewEntry),
let op_contains = Operation(
OperationType::Native(NativeOperation::DictContainsFromEntries),
vec![],
OperationAux::None,
);
builder
.insert(false, (value_of_a.clone(), op_new_entry.clone()))
.insert(false, (value_of_a.clone(), op_contains.clone()))
.unwrap();
builder
.insert(false, (value_of_b.clone(), op_new_entry))
.insert(false, (value_of_b.clone(), op_contains))
.unwrap();
let st = Statement::equal(self_a, self_b);
let st = Statement::equal(
AnchoredKey::from((&local, "a")),
AnchoredKey::from((&local, "b")),
);
let op = Operation(
OperationType::Native(NativeOperation::EqualFromEntries),
vec![

View file

@ -1,10 +1,10 @@
use std::fmt;
use crate::{
frontend::{MainPod, SignedPod},
frontend::SignedDict,
middleware::{
AnchoredKey, CustomPredicateRef, NativeOperation, OperationAux, OperationType, Statement,
TypedValue, Value, ValueRef,
containers::Dictionary, root_key_to_ak, CustomPredicateRef, NativeOperation, OperationAux,
OperationType, Signature, Statement, TypedValue, Value, ValueRef,
},
};
@ -16,12 +16,12 @@ pub enum OperationArg {
}
impl OperationArg {
/// Extracts the value underlying literal and `ValueOf` statement
/// Extracts the value underlying literal and `Contains` statement
/// operation args.
pub(crate) fn value(&self) -> Option<&Value> {
match self {
Self::Literal(v) => Some(v),
Self::Statement(Statement::Equal(_, ValueRef::Literal(v))) => Some(v),
Self::Statement(Statement::Contains(_, _, ValueRef::Literal(v))) => Some(v),
_ => None,
}
}
@ -29,7 +29,11 @@ impl OperationArg {
pub(crate) fn value_and_ref(&self) -> Option<(ValueRef, &Value)> {
match self {
Self::Literal(v) => Some((ValueRef::Literal(v.clone()), v)),
Self::Statement(Statement::Equal(k, ValueRef::Literal(v))) => Some((k.clone(), v)),
Self::Statement(Statement::Contains(
ValueRef::Literal(root),
ValueRef::Literal(key),
ValueRef::Literal(v),
)) => root_key_to_ak(root, key).map(|ak| (ValueRef::Key(ak), v)),
_ => None,
}
}
@ -64,27 +68,21 @@ impl From<&Value> for OperationArg {
}
}
impl From<(&SignedPod, &str)> for OperationArg {
fn from((pod, key): (&SignedPod, &str)) -> Self {
// 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::Equal(
AnchoredKey::from((pod.id(), key)).into(),
impl From<(&Dictionary, &str)> for OperationArg {
fn from((dict, key): (&Dictionary, &str)) -> Self {
// TODO: Use TryFrom
let value = dict.get(&key.into()).cloned().unwrap();
Self::Statement(Statement::Contains(
dict.clone().into(),
key.into(),
value.into(),
))
}
}
impl From<(&MainPod, &str)> for OperationArg {
fn from((pod, key): (&MainPod, &str)) -> Self {
// TODO: TryFrom.
let value = pod
.get(key)
.unwrap_or_else(|| panic!("Key {} is not present in POD: {}", key, pod));
Self::Statement(Statement::equal(AnchoredKey::from((pod.id(), key)), value))
impl From<(&SignedDict, &str)> for OperationArg {
fn from((signed_dict, key): (&SignedDict, &str)) -> Self {
OperationArg::from((&signed_dict.dict, key))
}
}
@ -186,13 +184,6 @@ macro_rules! op_impl_st {
}
impl Operation {
pub fn new_entry(a1: impl Into<String>, a2: impl Into<Value>) -> Self {
Self(
OperationType::Native(NativeOperation::NewEntry),
vec![OperationArg::Entry(a1.into(), a2.into())],
OperationAux::None,
)
}
op_impl_oa!(eq, EqualFromEntries, 2);
op_impl_oa!(ne, NotEqualFromEntries, 2);
op_impl_oa!(gt_eq, GtEqFromEntries, 2);
@ -229,4 +220,22 @@ impl Operation {
op_impl_oa!(set_insert, SetInsertFromEntries, 3);
op_impl_oa!(set_delete, SetDeleteFromEntries, 3);
op_impl_oa!(array_update, ArrayUpdateFromEntries, 4);
pub fn signed_by(
msg: impl Into<OperationArg>,
pk: impl Into<OperationArg>,
sig: Signature,
) -> Self {
Self(
OperationType::Native(NativeOperation::SignedBy),
vec![msg.into(), pk.into()],
OperationAux::Signature(sig),
)
}
pub fn dict_signed_by(signed_dict: &SignedDict) -> Self {
Self::signed_by(
Value::from(signed_dict.dict.clone()),
Value::from(signed_dict.public_key),
signed_dict.signature.clone(),
)
}
}

View file

@ -128,8 +128,8 @@ impl PodRequest {
}
// Try to bind wildcard to the POD ID
let pod_id_value = Value::from(stmt_key.pod_id);
self.try_bind_wildcard(&wildcard.name, pod_id_value, current_bindings, new_bindings)
let root_value = Value::from(stmt_key.root);
self.try_bind_wildcard(&wildcard.name, root_value, current_bindings, new_bindings)
}
// Other combinations don't match
@ -176,12 +176,14 @@ impl Display for PodRequest {
mod tests {
use crate::{
backends::plonky2::{
mock::mainpod::MockProver, primitives::ec::schnorr::SecretKey, signedpod::Signer,
mock::mainpod::MockProver, primitives::ec::schnorr::SecretKey, signer::Signer,
},
examples::{
zu_kyc_pod_builder, zu_kyc_pod_request, zu_kyc_sign_dict_builders, MOCK_VD_SET,
},
examples::{zu_kyc_pod_builder, zu_kyc_pod_request, zu_kyc_sign_pod_builders, MOCK_VD_SET},
frontend::{MainPodBuilder, Operation},
lang::parse,
middleware::Params,
middleware::{Params, Value},
};
#[test]
@ -189,7 +191,7 @@ mod tests {
let params = Params::default();
let vd_set = &*MOCK_VD_SET;
let (gov_id, pay_stub) = zu_kyc_sign_pod_builders(&params);
let (gov_id, pay_stub) = zu_kyc_sign_dict_builders(&params);
let gov_id = gov_id.sign(&Signer(SecretKey(1u32.into()))).unwrap();
let pay_stub = pay_stub.sign(&Signer(SecretKey(2u32.into()))).unwrap();
let builder = zu_kyc_pod_builder(&Params::default(), vd_set, &gov_id, &pay_stub).unwrap();
@ -198,8 +200,8 @@ mod tests {
// This request matches the POD
let request = zu_kyc_pod_request(
gov_id.get("_signer").unwrap(),
pay_stub.get("_signer").unwrap(),
&Value::from(gov_id.public_key),
&Value::from(pay_stub.public_key),
)
.unwrap();
assert!(request.exact_match_pod(&*kyc.pod).is_ok());

View file

@ -1,73 +1,30 @@
use std::collections::HashMap;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use super::Error;
use crate::{
frontend::{MainPod, SignedPod},
middleware::{
deserialize_pod, deserialize_signed_pod, Key, Params, PodId, Statement, VDSet, Value,
},
frontend::MainPod,
middleware::{deserialize_pod, Hash, Params, Statement, VDSet},
};
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
#[schemars(rename = "SignedPod")]
pub struct SerializedSignedPod {
pod_type: (usize, String),
id: PodId,
entries: HashMap<Key, Value>,
data: serde_json::Value,
}
impl SerializedSignedPod {
pub fn id(&self) -> PodId {
self.id
}
}
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
#[schemars(rename = "MainPod")]
pub struct SerializedMainPod {
params: Params,
pod_type: (usize, String),
id: PodId,
id: Hash,
vd_set: VDSet,
public_statements: Vec<Statement>,
data: serde_json::Value,
}
impl SerializedMainPod {
pub fn id(&self) -> PodId {
pub fn id(&self) -> Hash {
self.id
}
}
impl From<SignedPod> for SerializedSignedPod {
fn from(pod: SignedPod) -> Self {
let (pod_type, pod_type_name_str) = pod.pod.pod_type();
let data = pod.pod.serialize_data();
SerializedSignedPod {
pod_type: (pod_type, pod_type_name_str.to_string()),
id: pod.id(),
entries: pod.kvs().clone(),
data,
}
}
}
impl TryFrom<SerializedSignedPod> for SignedPod {
type Error = Error;
fn try_from(serialized: SerializedSignedPod) -> Result<Self, Self::Error> {
let pod = deserialize_signed_pod(serialized.pod_type.0, serialized.id, serialized.data)?;
let kvs = pod.kvs().into_iter().map(|(ak, v)| (ak.key, v)).collect();
Ok(Self { pod, kvs })
}
}
impl From<MainPod> for SerializedMainPod {
fn from(pod: MainPod) -> Self {
let (pod_type, pod_type_name_str) = pod.pod.pod_type();
@ -116,17 +73,17 @@ mod tests {
mainpod::{rec_main_pod_circuit_data, Prover},
mock::mainpod::MockProver,
primitives::ec::schnorr::SecretKey,
signedpod::Signer,
signer::Signer,
},
examples::{
attest_eth_friend, zu_kyc_pod_builder, zu_kyc_sign_pod_builders, EthDosHelper,
attest_eth_friend, zu_kyc_pod_builder, zu_kyc_sign_dict_builders, EthDosHelper,
MOCK_VD_SET,
},
frontend::{Result, SignedPodBuilder},
frontend::{Result, SignedDict, SignedDictBuilder},
middleware::{
self,
containers::{Array, Dictionary, Set},
Params, TypedValue, DEFAULT_VD_LIST,
Params, Signer as _, TypedValue, DEFAULT_VD_LIST,
},
};
@ -182,9 +139,9 @@ mod tests {
}
}
fn signed_pod_builder() -> SignedPodBuilder {
fn signed_dict_builder() -> SignedDictBuilder {
let params = &Params::default();
let mut builder = SignedPodBuilder::new(params);
let mut builder = SignedDictBuilder::new(params);
builder.insert("name", "test");
builder.insert("age", 30);
builder.insert("very_large_int", 1152921504606846976);
@ -228,46 +185,29 @@ mod tests {
}
#[test]
fn test_signed_pod_serialization() {
let builder = signed_pod_builder();
fn test_signed_dict_serialization() {
let builder = signed_dict_builder();
let signer = Signer(SecretKey(1u32.into()));
let pod = builder.sign(&signer).unwrap();
let signed_dict = builder.sign(&signer).unwrap();
let serialized = serde_json::to_string_pretty(&pod).unwrap();
let serialized = serde_json::to_string_pretty(&signed_dict).unwrap();
println!("serialized: {}", serialized);
let deserialized: SignedPod = serde_json::from_str(&serialized).unwrap();
let deserialized: SignedDict = serde_json::from_str(&serialized).unwrap();
println!(
"deserialized: {}",
serde_json::to_string_pretty(&deserialized).unwrap()
);
assert_eq!(pod.kvs, deserialized.kvs);
assert_eq!(pod.verify().is_ok(), deserialized.verify().is_ok());
assert_eq!(pod.id(), deserialized.id())
}
#[test]
fn test_mock_signed_pod_serialization() {
let builder = signed_pod_builder();
let signer = Signer(SecretKey(1u32.into()));
let pod = builder.sign(&signer).unwrap();
let serialized = serde_json::to_string_pretty(&pod).unwrap();
println!("serialized: {}", serialized);
let deserialized: SignedPod = serde_json::from_str(&serialized).unwrap();
println!(
"deserialized: {}",
serde_json::to_string_pretty(&deserialized).unwrap()
);
assert_eq!(pod.kvs, deserialized.kvs);
assert_eq!(pod.verify().is_ok(), deserialized.verify().is_ok());
assert_eq!(pod.id(), deserialized.id())
assert_eq!(signed_dict.dict.kvs(), deserialized.dict.kvs());
assert_eq!(signed_dict.public_key, deserialized.public_key);
assert_eq!(signed_dict.signature, deserialized.signature);
assert_eq!(signed_dict.verify().is_ok(), deserialized.verify().is_ok());
}
fn build_mock_zukyc_pod() -> Result<MainPod> {
let params = middleware::Params::default();
let vd_set = &*MOCK_VD_SET;
let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_pod_builders(&params);
let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_dict_builders(&params);
let signer = Signer(SecretKey(1u32.into()));
let gov_id_pod = gov_id_builder.sign(&signer).unwrap();
let signer = Signer(SecretKey(2u32.into()));
@ -283,18 +223,19 @@ mod tests {
let params = middleware::Params {
// Currently the circuit uses random access that only supports vectors of length 64.
// With max_input_main_pods=3 we need random access to a vector of length 73.
max_input_recursive_pods: 1,
max_input_pods: 1,
..Default::default()
};
let mut vds = DEFAULT_VD_LIST.clone();
vds.push(rec_main_pod_circuit_data(&params).1.verifier_only.clone());
let vd_set = VDSet::new(params.max_depth_mt_vds, &vds).unwrap();
let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_pod_builders(&params);
let (gov_id_builder, pay_stub_builder) = zu_kyc_sign_dict_builders(&params);
let signer = Signer(SecretKey(1u32.into()));
let gov_id_pod = gov_id_builder.sign(&signer)?;
let signer = Signer(SecretKey(2u32.into()));
let pay_stub_pod = pay_stub_builder.sign(&signer)?;
let _signer = Signer(SecretKey(3u32.into()));
let kyc_builder = zu_kyc_pod_builder(&params, &vd_set, &gov_id_pod, &pay_stub_pod)?;
let prover = Prover {};
@ -311,7 +252,10 @@ mod tests {
let deserialized: MainPod = serde_json::from_str(&serialized).unwrap();
assert_eq!(kyc_pod.public_statements, deserialized.public_statements);
assert_eq!(kyc_pod.pod.id(), deserialized.pod.id());
assert_eq!(
kyc_pod.pod.statements_hash(),
deserialized.pod.statements_hash()
);
assert_eq!(kyc_pod.pod.verify()?, deserialized.pod.verify()?);
Ok(())
@ -324,7 +268,10 @@ mod tests {
let deserialized: MainPod = serde_json::from_str(&serialized).unwrap();
assert_eq!(kyc_pod.public_statements, deserialized.public_statements);
assert_eq!(kyc_pod.pod.id(), deserialized.pod.id());
assert_eq!(
kyc_pod.pod.statements_hash(),
deserialized.pod.statements_hash()
);
assert_eq!(kyc_pod.pod.verify()?, deserialized.pod.verify()?);
Ok(())
@ -348,7 +295,7 @@ mod tests {
let alice_attestation = attest_eth_friend(&params, &alice, bob.public_key());
let bob_attestation = attest_eth_friend(&params, &bob, charlie.public_key());
let helper = EthDosHelper::new(&params, vd_set, true, alice.public_key())?;
let helper = EthDosHelper::new(&params, vd_set, alice.public_key())?;
let prover = MockProver {};
let dist_1 = helper.dist_1(&alice_attestation)?.prove(&prover)?;
let dist_2 = helper
@ -360,28 +307,28 @@ mod tests {
#[test]
// This tests that we can generate JSON Schemas for the MainPod and
// SignedPod types, and that we can validate Signed and Main Pods
// SignedDict types, and that we can validate Signed and Main Pods
// against the schemas. Since both Mock and Plonky2 PODs have the same
// public interface, we can assume that the schema works for both.
fn test_schema() {
let mainpod_schema = schema_for!(SerializedMainPod);
let signedpod_schema = schema_for!(SerializedSignedPod);
let signeddict_schema = schema_for!(SignedDict);
let kyc_pod = build_mock_zukyc_pod().unwrap();
let signed_pod = signed_pod_builder()
let signed_dict = signed_dict_builder()
.sign(&Signer(SecretKey(1u32.into())))
.unwrap();
let ethdos_pod = build_ethdos_pod().unwrap();
let mainpod_schema_value = serde_json::to_value(&mainpod_schema).unwrap();
let signedpod_schema_value = serde_json::to_value(&signedpod_schema).unwrap();
let signed_dict_schema_value = serde_json::to_value(&signeddict_schema).unwrap();
let kyc_pod_value = serde_json::to_value(&kyc_pod).unwrap();
let mainpod_valid = jsonschema::validate(&mainpod_schema_value, &kyc_pod_value);
assert!(mainpod_valid.is_ok(), "{:#?}", mainpod_valid);
let signed_pod_value = serde_json::to_value(&signed_pod).unwrap();
let signedpod_valid = jsonschema::validate(&signedpod_schema_value, &signed_pod_value);
assert!(signedpod_valid.is_ok(), "{:#?}", signedpod_valid);
let signed_dict_value = serde_json::to_value(&signed_dict).unwrap();
let signed_dict_valid = jsonschema::validate(&signed_dict_schema_value, &signed_dict_value);
assert!(signed_dict_valid.is_ok(), "{:#?}", signed_dict_valid);
let ethdos_pod_value = serde_json::to_value(&ethdos_pod).unwrap();
let ethdos_pod_valid = jsonschema::validate(&mainpod_schema_value, &ethdos_pod_value);