Serialization of Signed and Main Pods (#128)

This commit is contained in:
Rob Knight 2025-03-21 14:42:16 +01:00 committed by GitHub
parent fee70af12b
commit 9afc43675d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 817 additions and 189 deletions

110
src/frontend/containers.rs Normal file
View file

@ -0,0 +1,110 @@
use std::collections::HashMap;
use anyhow::Result;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use super::Value;
use crate::frontend::serialization::ordered_map;
use crate::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(|v| MiddlewareValue::from(v)).collect())?;
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(|v| MiddlewareValue::from(v)).collect())?;
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)
}
}

View file

@ -1,31 +1,34 @@
//! The frontend includes the user-level abstractions and user-friendly types to define and work
//! with Pods.
use crate::frontend::serialization::*;
use crate::middleware::{
self, hash_str, Hash, MainPodInputs, NativeOperation, NativePredicate, Params, PodId,
PodProver, PodSigner, SELF,
};
use crate::middleware::{OperationType, Predicate, KEY_SIGNER, KEY_TYPE};
use anyhow::{anyhow, Error, Result};
use containers::{Array, Dictionary, Set};
use env_logger;
use itertools::Itertools;
use log::error;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::convert::From;
use std::{fmt, hash as h};
use crate::middleware::{
self,
containers::{Array, Dictionary, Set},
hash_str, Hash, MainPodInputs, NativeOperation, NativePredicate, Params, PodId, PodProver,
PodSigner, SELF,
};
use crate::middleware::{OperationType, Predicate, KEY_SIGNER, KEY_TYPE};
pub mod containers;
mod custom;
mod operation;
mod serialization;
mod statement;
pub use custom::*;
pub use operation::*;
pub use statement::*;
/// This type is just for presentation purposes.
#[derive(Clone, Debug, Default, h::Hash, PartialEq, Eq)]
#[derive(Clone, Debug, Default, h::Hash, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub enum PodClass {
#[default]
Signed,
@ -33,18 +36,52 @@ pub enum PodClass {
}
// An Origin, which represents a reference to an ancestor POD.
#[derive(Clone, Debug, PartialEq, Eq, h::Hash, Default)]
pub struct Origin(pub PodClass, pub PodId);
#[derive(Clone, Debug, PartialEq, Eq, h::Hash, Default, Serialize, Deserialize, JsonSchema)]
pub struct Origin {
pub pod_class: PodClass,
pub pod_id: PodId,
}
#[derive(Clone, Debug, PartialEq, Eq)]
impl Origin {
pub fn new(pod_class: PodClass, pod_id: PodId) -> Self {
Self { pod_class, pod_id }
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[schemars(transform = serialization::transform_value_schema)]
pub enum Value {
String(String),
Int(i64),
Bool(bool),
Dictionary(Dictionary),
// 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),
Array(Array),
Dictionary(Dictionary),
Int(
#[serde(serialize_with = "serialize_i64", deserialize_with = "deserialize_i64")]
#[schemars(with = "String", regex(pattern = r"^\d+$"))]
i64,
),
// Uses the serialization for middleware::Value:
Raw(middleware::Value),
// UNTAGGED TYPES:
#[serde(untagged)]
#[schemars(skip)]
Array(Array),
#[serde(untagged)]
#[schemars(skip)]
String(String),
#[serde(untagged)]
#[schemars(skip)]
Bool(bool),
}
impl From<&str> for Value {
@ -71,9 +108,9 @@ impl From<&Value> for middleware::Value {
Value::String(s) => hash_str(s).value(),
Value::Int(v) => middleware::Value::from(*v),
Value::Bool(b) => middleware::Value::from(*b as i64),
Value::Dictionary(d) => d.commitment().value(),
Value::Set(s) => s.commitment().value(),
Value::Array(a) => a.commitment().value(),
Value::Dictionary(d) => d.middleware_dict().commitment().value(),
Value::Set(s) => s.middleware_set().commitment().value(),
Value::Array(a) => a.middleware_array().commitment().value(),
Value::Raw(v) => *v,
}
}
@ -102,9 +139,9 @@ impl fmt::Display for Value {
Value::String(s) => write!(f, "\"{}\"", s),
Value::Int(v) => write!(f, "{}", v),
Value::Bool(b) => write!(f, "{}", b),
Value::Dictionary(d) => write!(f, "dict:{}", d.commitment()),
Value::Set(s) => write!(f, "set:{}", s.commitment()),
Value::Array(a) => write!(f, "arr:{}", a.commitment()),
Value::Dictionary(d) => write!(f, "dict:{}", d.middleware_dict().commitment()),
Value::Set(s) => write!(f, "set:{}", s.middleware_set().commitment()),
Value::Array(a) => write!(f, "arr:{}", a.middleware_array().commitment()),
Value::Raw(v) => write!(f, "{}", v),
}
}
@ -174,12 +211,14 @@ impl SignedPodBuilder {
/// SignedPod is a wrapper on top of backend::SignedPod, which additionally stores the
/// string<-->hash relation of the keys.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(try_from = "SignedPodHelper", into = "SignedPodHelper")]
pub struct SignedPod {
pub pod: Box<dyn middleware::Pod>,
/// Key-value pairs as represented in the frontend. These should
/// correspond to the entries of `pod.kvs()` after hashing and
/// replacing each key with its corresponding anchored key.
#[serde(serialize_with = "ordered_map")]
pub kvs: HashMap<String, Value>,
}
@ -208,7 +247,7 @@ impl SignedPod {
self.pod.id()
}
pub fn origin(&self) -> Origin {
Origin(PodClass::Signed, self.id())
Origin::new(PodClass::Signed, self.id())
}
pub fn verify(&self) -> bool {
self.pod.verify()
@ -222,12 +261,21 @@ impl SignedPod {
}
}
#[derive(Clone, Debug, PartialEq, Eq, h::Hash)]
pub struct AnchoredKey(pub Origin, pub String);
#[derive(Clone, Debug, PartialEq, Eq, h::Hash, Serialize, Deserialize, JsonSchema)]
pub struct AnchoredKey {
pub origin: Origin,
pub key: String,
}
impl AnchoredKey {
pub fn new(origin: Origin, key: String) -> Self {
Self { origin, key }
}
}
impl From<AnchoredKey> for middleware::AnchoredKey {
fn from(ak: AnchoredKey) -> Self {
middleware::AnchoredKey(ak.0 .1, hash_str(&ak.1))
middleware::AnchoredKey(ak.origin.pod_id, hash_str(&ak.key))
}
}
@ -295,11 +343,12 @@ impl MainPodBuilder {
// Add key-hash and POD ID-class correspondences to tables.
pod.public_statements
.iter()
.flat_map(|s| &s.1)
.flat_map(|s| &s.args)
.flat_map(|arg| match arg {
StatementArg::Key(AnchoredKey(Origin(pod_class, pod_id), key)) => {
Some((*pod_id, pod_class.clone(), hash_str(key), key.clone()))
}
StatementArg::Key(AnchoredKey {
origin: Origin { pod_class, pod_id },
key,
}) => Some((*pod_id, pod_class.clone(), hash_str(key), key.clone())),
_ => None,
})
.for_each(|(pod_id, pod_class, hash, key)| {
@ -324,8 +373,8 @@ impl MainPodBuilder {
for arg in args.iter_mut() {
match arg {
OperationArg::Statement(s) => {
if s.0 == Predicate::Native(NativePredicate::ValueOf) {
st_args.push(s.1[0].clone())
if s.predicate == Predicate::Native(NativePredicate::ValueOf) {
st_args.push(s.args[0].clone())
} else {
panic!("Invalid statement argument.");
}
@ -334,11 +383,11 @@ impl MainPodBuilder {
OperationArg::Literal(v) => {
let value_of_st = self.literal(public, v)?;
*arg = OperationArg::Statement(value_of_st.clone());
st_args.push(value_of_st.1[0].clone())
st_args.push(value_of_st.args[0].clone())
}
OperationArg::Entry(k, v) => {
st_args.push(StatementArg::Key(AnchoredKey(
Origin(PodClass::Main, SELF),
st_args.push(StatementArg::Key(AnchoredKey::new(
Origin::new(PodClass::Main, SELF),
k.clone(),
)));
st_args.push(StatementArg::Literal(v.clone()))
@ -366,7 +415,7 @@ impl MainPodBuilder {
.unwrap_or_else(|| {
// We are dealing with a copy here.
match (&args).get(0) {
Some(OperationArg::Statement(s)) if args.len() == 1 => Ok(s.0.clone()),
Some(OperationArg::Statement(s)) if args.len() == 1 => Ok(s.predicate.clone()),
_ => Err(anyhow!("Invalid arguments to copy operation: {:?}", args)),
}
})?;
@ -376,7 +425,7 @@ impl MainPodBuilder {
None => vec![],
NewEntry => self.op_args_entries(public, args)?,
CopyStatement => match &args[0] {
OperationArg::Statement(s) => s.1.clone(),
OperationArg::Statement(s) => s.args.clone(),
_ => {
return Err(anyhow!("Invalid arguments to copy operation: {}", op));
}
@ -388,14 +437,14 @@ impl MainPodBuilder {
TransitiveEqualFromStatements => {
match (args[0].clone(), args[1].clone()) {
(
OperationArg::Statement(Statement(
Predicate::Native(NativePredicate::Equal),
st0_args,
)),
OperationArg::Statement(Statement(
Predicate::Native(NativePredicate::Equal),
st1_args,
)),
OperationArg::Statement(Statement {
predicate: Predicate::Native(NativePredicate::Equal),
args: st0_args,
}),
OperationArg::Statement(Statement {
predicate: Predicate::Native(NativePredicate::Equal),
args: st1_args,
}),
) => {
// st_args0 == vec![ak0, ak1]
// st_args1 == vec![ak1, ak2]
@ -416,10 +465,10 @@ impl MainPodBuilder {
}
}
GtToNotEqual => match args[0].clone() {
OperationArg::Statement(Statement(
Predicate::Native(NativePredicate::Gt),
st_args,
)) => {
OperationArg::Statement(Statement {
predicate: Predicate::Native(NativePredicate::Gt),
args: st_args,
}) => {
vec![st_args[0].clone()]
}
_ => {
@ -427,10 +476,10 @@ impl MainPodBuilder {
}
},
LtToNotEqual => match args[0].clone() {
OperationArg::Statement(Statement(
Predicate::Native(NativePredicate::Lt),
st_args,
)) => {
OperationArg::Statement(Statement {
predicate: Predicate::Native(NativePredicate::Lt),
args: st_args,
}) => {
vec![st_args[0].clone()]
}
_ => {
@ -441,18 +490,18 @@ impl MainPodBuilder {
NotContainsFromEntries => self.op_args_entries(public, args)?,
SumOf => match (args[0].clone(), args[1].clone(), args[2].clone()) {
(
OperationArg::Statement(Statement(
Predicate::Native(NativePredicate::ValueOf),
st0_args,
)),
OperationArg::Statement(Statement(
Predicate::Native(NativePredicate::ValueOf),
st1_args,
)),
OperationArg::Statement(Statement(
Predicate::Native(NativePredicate::ValueOf),
st2_args,
)),
OperationArg::Statement(Statement {
predicate: Predicate::Native(NativePredicate::ValueOf),
args: st0_args,
}),
OperationArg::Statement(Statement {
predicate: Predicate::Native(NativePredicate::ValueOf),
args: st1_args,
}),
OperationArg::Statement(Statement {
predicate: Predicate::Native(NativePredicate::ValueOf),
args: st2_args,
}),
) => {
let st_args: Vec<StatementArg> = match (
st0_args[1].clone(),
@ -489,18 +538,18 @@ impl MainPodBuilder {
},
ProductOf => match (args[0].clone(), args[1].clone(), args[2].clone()) {
(
OperationArg::Statement(Statement(
Predicate::Native(NativePredicate::ValueOf),
st0_args,
)),
OperationArg::Statement(Statement(
Predicate::Native(NativePredicate::ValueOf),
st1_args,
)),
OperationArg::Statement(Statement(
Predicate::Native(NativePredicate::ValueOf),
st2_args,
)),
OperationArg::Statement(Statement {
predicate: Predicate::Native(NativePredicate::ValueOf),
args: st0_args,
}),
OperationArg::Statement(Statement {
predicate: Predicate::Native(NativePredicate::ValueOf),
args: st1_args,
}),
OperationArg::Statement(Statement {
predicate: Predicate::Native(NativePredicate::ValueOf),
args: st2_args,
}),
) => {
let st_args: Vec<StatementArg> = match (
st0_args[1].clone(),
@ -539,18 +588,18 @@ impl MainPodBuilder {
},
MaxOf => match (args[0].clone(), args[1].clone(), args[2].clone()) {
(
OperationArg::Statement(Statement(
Predicate::Native(NativePredicate::ValueOf),
st0_args,
)),
OperationArg::Statement(Statement(
Predicate::Native(NativePredicate::ValueOf),
st1_args,
)),
OperationArg::Statement(Statement(
Predicate::Native(NativePredicate::ValueOf),
st2_args,
)),
OperationArg::Statement(Statement {
predicate: Predicate::Native(NativePredicate::ValueOf),
args: st0_args,
}),
OperationArg::Statement(Statement {
predicate: Predicate::Native(NativePredicate::ValueOf),
args: st1_args,
}),
OperationArg::Statement(Statement {
predicate: Predicate::Native(NativePredicate::ValueOf),
args: st2_args,
}),
) => {
let st_args: Vec<StatementArg> = match (
st0_args[1].clone(),
@ -610,8 +659,8 @@ impl MainPodBuilder {
output_arg_values
.chunks(2)
.map(|chunk| {
Ok(StatementArg::Key(AnchoredKey(
Origin(
Ok(StatementArg::Key(AnchoredKey::new(
Origin::new(
self.pod_class_table
.get(&PodId(chunk[0].into()))
.cloned()
@ -627,15 +676,15 @@ impl MainPodBuilder {
.collect::<Result<Vec<_>>>()?
}
};
let st = Statement(pred, st_args);
let st = Statement::new(pred, st_args);
self.operations.push(op);
if public {
self.public_statements.push(st.clone());
}
// Add key-hash pairs in statement to table.
st.1.iter().for_each(|arg| {
if let StatementArg::Key(AnchoredKey(_, key)) = arg {
st.args.iter().for_each(|arg| {
if let StatementArg::Key(AnchoredKey { origin: _, key }) = arg {
self.key_table.insert(hash_str(key), key.clone());
}
});
@ -702,16 +751,16 @@ impl MainPodBuilder {
crate::middleware::Statement::ValueOf(
crate::middleware::AnchoredKey(id, key),
value,
) if id == pod_id && key == type_key_hash => Some(Statement(
Predicate::Native(NativePredicate::ValueOf),
vec![
StatementArg::Key(AnchoredKey(
Origin(PodClass::Main, pod_id),
) if id == pod_id && key == type_key_hash => Some(Statement {
predicate: Predicate::Native(NativePredicate::ValueOf),
args: vec![
StatementArg::Key(AnchoredKey::new(
Origin::new(PodClass::Main, pod_id),
KEY_TYPE.to_string(),
)),
StatementArg::Literal(value.into()),
],
)),
}),
_ => None,
})
.ok_or(anyhow!("Missing POD type information in POD: {:?}", pod))?;
@ -720,18 +769,25 @@ impl MainPodBuilder {
let public_statements = [type_statement]
.into_iter()
.chain(self.public_statements.clone().into_iter().map(|s| {
let s_type = s.0;
let s_type = s.predicate;
let s_args = s
.1
.args
.into_iter()
.map(|arg| match arg {
StatementArg::Key(AnchoredKey(Origin(class, id), key)) if id == SELF => {
StatementArg::Key(AnchoredKey(Origin(class, pod_id), key))
StatementArg::Key(AnchoredKey {
origin:
Origin {
pod_class: class,
pod_id: id,
},
key,
}) if id == SELF => {
StatementArg::Key(AnchoredKey::new(Origin::new(class, pod_id), key))
}
_ => arg,
})
.collect();
Statement(s_type, s_args)
Statement::new(s_type, s_args)
}))
.collect();
@ -742,7 +798,8 @@ impl MainPodBuilder {
}
}
#[derive(Debug)]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(try_from = "MainPodHelper", into = "MainPodHelper")]
pub struct MainPod {
pub pod: Box<dyn middleware::Pod>,
pub public_statements: Vec<Statement>,
@ -769,7 +826,7 @@ impl MainPod {
self.pod.id()
}
pub fn origin(&self) -> Origin {
Origin(PodClass::Main, self.id())
Origin::new(PodClass::Main, self.id())
}
}
@ -1166,20 +1223,26 @@ pub mod tests {
let params = Params::default();
let mut builder = MainPodBuilder::new(&params);
builder.insert((
Statement(
Statement::new(
Predicate::Native(NativePredicate::ValueOf),
vec![
StatementArg::Key(AnchoredKey(Origin(PodClass::Main, SELF), "a".into())),
StatementArg::Key(AnchoredKey::new(
Origin::new(PodClass::Main, SELF),
"a".into(),
)),
StatementArg::Literal(Value::Int(3)),
],
),
Operation(OperationType::Native(NativeOperation::NewEntry), vec![]),
));
builder.insert((
Statement(
Statement::new(
Predicate::Native(NativePredicate::ValueOf),
vec![
StatementArg::Key(AnchoredKey(Origin(PodClass::Main, SELF), "a".into())),
StatementArg::Key(AnchoredKey::new(
Origin::new(PodClass::Main, SELF),
"a".into(),
)),
StatementArg::Literal(Value::Int(28)),
],
),
@ -1194,19 +1257,25 @@ pub mod tests {
// right now the mock prover catches this when it calls compile()
let params = Params::default();
let mut builder = MainPodBuilder::new(&params);
let self_a = AnchoredKey(Origin(PodClass::Main, SELF), "a".into());
let self_b = AnchoredKey(Origin(PodClass::Main, SELF), "b".into());
let value_of_a = Statement(
let self_a = AnchoredKey::new(Origin::new(PodClass::Main, SELF), "a".into());
let self_b = AnchoredKey::new(Origin::new(PodClass::Main, SELF), "b".into());
let value_of_a = Statement::new(
Predicate::Native(NativePredicate::ValueOf),
vec![
StatementArg::Key(AnchoredKey(Origin(PodClass::Main, SELF), "a".into())),
StatementArg::Key(AnchoredKey::new(
Origin::new(PodClass::Main, SELF),
"a".into(),
)),
StatementArg::Literal(Value::Int(3)),
],
);
let value_of_b = Statement(
let value_of_b = Statement::new(
Predicate::Native(NativePredicate::ValueOf),
vec![
StatementArg::Key(AnchoredKey(Origin(PodClass::Main, SELF), "b".into())),
StatementArg::Key(AnchoredKey::new(
Origin::new(PodClass::Main, SELF),
"b".into(),
)),
StatementArg::Literal(Value::Int(27)),
],
);
@ -1220,7 +1289,7 @@ pub mod tests {
Operation(OperationType::Native(NativeOperation::NewEntry), vec![]),
));
builder.insert((
Statement(
Statement::new(
Predicate::Native(NativePredicate::Equal),
vec![StatementArg::Key(self_a), StatementArg::Key(self_b)],
),

View file

@ -0,0 +1,294 @@
use std::collections::{BTreeMap, HashMap};
use schemars::{JsonSchema, Schema};
use serde::{Deserialize, Serialize, Serializer};
use crate::backends::plonky2::mock_main::MockMainPod;
use crate::backends::plonky2::mock_signed::MockSignedPod;
use crate::frontend::containers::Dictionary;
use crate::frontend::Statement;
use crate::middleware::PodId;
use super::{MainPod, SignedPod, Value};
#[derive(Serialize, Deserialize, JsonSchema)]
pub struct SignedPodHelper {
entries: HashMap<String, Value>,
proof: String,
pod_class: String,
pod_type: String,
}
impl TryFrom<SignedPodHelper> for SignedPod {
type Error = anyhow::Error;
fn try_from(helper: SignedPodHelper) -> Result<SignedPod, Self::Error> {
if helper.pod_class != "Signed" {
return Err(anyhow::anyhow!("pod_class is not Signed"));
}
if helper.pod_type != "Mock" {
return Err(anyhow::anyhow!("pod_type is not Mock"));
}
let dict = Dictionary::new(helper.entries.clone())?
.middleware_dict()
.clone();
let pod = MockSignedPod::deserialize(PodId(dict.commitment()), helper.proof, dict);
Ok(SignedPod {
pod: Box::new(pod),
kvs: helper.entries,
})
}
}
impl From<SignedPod> for SignedPodHelper {
fn from(pod: SignedPod) -> Self {
SignedPodHelper {
entries: pod.kvs,
proof: pod.pod.serialized_proof(),
pod_class: "Signed".to_string(),
pod_type: "Mock".to_string(),
}
}
}
#[derive(Serialize, Deserialize, JsonSchema)]
pub struct MainPodHelper {
public_statements: Vec<Statement>,
proof: String,
pod_class: String,
pod_type: String,
}
impl TryFrom<MainPodHelper> for MainPod {
type Error = anyhow::Error; // or you can create a custom error type
fn try_from(helper: MainPodHelper) -> Result<Self, Self::Error> {
if helper.pod_class != "Main" {
return Err(anyhow::anyhow!("pod_class is not Main"));
}
if helper.pod_type != "Mock" {
return Err(anyhow::anyhow!("pod_type is not Mock"));
}
let pod = MockMainPod::deserialize(helper.proof)
.map_err(|e| anyhow::anyhow!("Failed to deserialize proof: {}", e))?;
Ok(MainPod {
pod: Box::new(pod),
public_statements: helper.public_statements,
})
}
}
impl From<MainPod> for MainPodHelper {
fn from(pod: MainPod) -> Self {
MainPodHelper {
public_statements: pod.public_statements,
proof: pod.pod.serialized_proof(),
pod_class: "Main".to_string(),
pod_type: "Mock".to_string(),
}
}
}
pub fn serialize_i64<S>(value: &i64, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&value.to_string())
}
pub fn deserialize_i64<'de, D>(deserializer: D) -> Result<i64, D::Error>
where
D: serde::Deserializer<'de>,
{
String::deserialize(deserializer)?
.parse()
.map_err(serde::de::Error::custom)
}
// HashMap is not ordered, but we want our dictionaries to be ordered
// by key for serialization, so we turn HashMaps into BTreeMaps.
pub fn ordered_map<S, K: Ord + Serialize, V: Serialize>(
value: &HashMap<K, V>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let ordered: BTreeMap<_, _> = value.iter().collect();
ordered.serialize(serializer)
}
pub fn transform_value_schema(schema: &mut Schema) {
let obj = schema.as_object_mut().unwrap();
// Get the oneOf array which contains our variant schemas
if let Some(one_of_container) = obj.get_mut("oneOf") {
if let Some(variants) = one_of_container.as_array_mut() {
// Add String variant (untagged)
variants.push(serde_json::json!({
"type": "string"
}));
// Add Boolean variant (untagged)
variants.push(serde_json::json!({
"type": "boolean"
}));
// Add Array variant (untagged)
variants.push(serde_json::json!({
"type": "array",
"items": {
"$ref": "#/definitions/Value"
}
}));
}
}
}
#[cfg(test)]
mod tests {
use crate::{
backends::plonky2::{mock_main::MockProver, mock_signed::MockSigner},
examples::{zu_kyc_pod_builder, zu_kyc_sign_pod_builders},
frontend::{
containers::{Array, Dictionary, Set},
SignedPodBuilder,
},
middleware::{self, Params},
};
use super::*;
#[test]
fn test_value_serialization() {
// Pairs of values and their expected serialized representations
let values = vec![
(Value::String("hello".to_string()), "\"hello\""),
(Value::Int(42), "{\"Int\":\"42\"}"),
(Value::Bool(true), "true"),
(
Value::Array(
Array::new(vec![Value::String("foo".to_string()), Value::Bool(false)]).unwrap(),
),
"[\"foo\",false]",
),
(
Value::Dictionary(
Dictionary::new(HashMap::from([
("foo".to_string(), Value::Int(123)),
("bar".to_string(), Value::String("baz".to_string())),
]))
.unwrap(),
),
"{\"Dictionary\":{\"bar\":\"baz\",\"foo\":{\"Int\":\"123\"}}}",
),
(
Value::Set(
Set::new(vec![
Value::String("foo".to_string()),
Value::String("bar".to_string()),
])
.unwrap(),
),
"{\"Set\":[\"foo\",\"bar\"]}",
),
];
for (value, expected) in values {
let serialized = serde_json::to_string(&value).unwrap();
assert_eq!(serialized, expected);
let deserialized: Value = serde_json::from_str(&serialized).unwrap();
assert_eq!(value, deserialized);
let expected_deserialized: Value = serde_json::from_str(&expected).unwrap();
assert_eq!(value, expected_deserialized);
}
}
#[test]
fn test_signed_pod_serialization() {
let mut signer = MockSigner { pk: "test".into() };
let mut builder = SignedPodBuilder::new(&Params::default());
builder.insert("name", "test");
builder.insert("age", 30);
builder.insert("very_large_int", 1152921504606846976);
builder.insert(
"a_dict_containing_one_key",
Value::Dictionary(
Dictionary::new(HashMap::from([
("foo".to_string(), Value::Int(123)),
(
"an_array_containing_three_ints".to_string(),
Value::Array(
Array::new(vec![Value::Int(1), Value::Int(2), Value::Int(3)]).unwrap(),
),
),
(
"a_set_containing_two_strings".to_string(),
Value::Set(
Set::new(vec![
Value::Array(
Array::new(vec![
Value::String("foo".to_string()),
Value::String("bar".to_string()),
])
.unwrap(),
),
Value::String("baz".to_string()),
])
.unwrap(),
),
),
]))
.unwrap(),
),
);
let pod = builder.sign(&mut signer).unwrap();
let serialized = serde_json::to_string(&pod).unwrap();
println!("serialized: {}", serialized);
let deserialized: SignedPod = serde_json::from_str(&serialized).unwrap();
assert_eq!(pod.kvs, deserialized.kvs);
assert_eq!(pod.origin(), deserialized.origin());
assert_eq!(pod.verify(), deserialized.verify());
assert_eq!(pod.id(), deserialized.id())
}
#[test]
fn test_main_pod_serialization() {
let params = middleware::Params::default();
let (gov_id_builder, pay_stub_builder, sanction_list_builder) =
zu_kyc_sign_pod_builders(&params);
let mut signer = MockSigner {
pk: "ZooGov".into(),
};
let gov_id_pod = gov_id_builder.sign(&mut signer).unwrap();
let mut signer = MockSigner {
pk: "ZooDeel".into(),
};
let pay_stub_pod = pay_stub_builder.sign(&mut signer).unwrap();
let mut signer = MockSigner {
pk: "ZooOFAC".into(),
};
let sanction_list_pod = sanction_list_builder.sign(&mut signer).unwrap();
let kyc_builder =
zu_kyc_pod_builder(&params, &gov_id_pod, &pay_stub_pod, &sanction_list_pod).unwrap();
let mut prover = MockProver {};
let kyc_pod = kyc_builder.prove(&mut prover, &params).unwrap();
let serialized = serde_json::to_string(&kyc_pod).unwrap();
println!("serialized: {}", serialized);
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.verify(), deserialized.pod.verify());
}
}

View file

@ -1,10 +1,11 @@
use anyhow::{anyhow, Result};
use std::fmt;
use super::{AnchoredKey, SignedPod, Value};
use crate::middleware::{self, NativePredicate, Predicate};
use anyhow::{anyhow, Result};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub enum StatementArg {
Literal(Value),
Key(AnchoredKey),
@ -14,13 +15,22 @@ 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.0 .1, r.1),
Self::Key(r) => write!(f, "{}.{}", r.origin.pod_id, r.key),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Statement(pub Predicate, pub Vec<StatementArg>);
#[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 {
@ -30,13 +40,13 @@ impl From<(&SignedPod, &str)> for Statement {
.get(key)
.cloned()
.unwrap_or_else(|| panic!("Key {} is not present in POD: {}", key, pod));
Statement(
Predicate::Native(NativePredicate::ValueOf),
vec![
StatementArg::Key(AnchoredKey(pod.origin(), key.to_string())),
Statement {
predicate: Predicate::Native(NativePredicate::ValueOf),
args: vec![
StatementArg::Key(AnchoredKey::new(pod.origin(), key.to_string())),
StatementArg::Literal(value),
],
)
}
}
}
@ -47,11 +57,11 @@ impl TryFrom<Statement> for middleware::Statement {
type NP = NativePredicate;
type SA = StatementArg;
let args = (
s.1.first().cloned(),
s.1.get(1).cloned(),
s.1.get(2).cloned(),
s.args.first().cloned(),
s.args.get(1).cloned(),
s.args.get(2).cloned(),
);
Ok(match &s.0 {
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)) => {
@ -88,7 +98,8 @@ impl TryFrom<Statement> for middleware::Statement {
},
Predicate::Custom(cpr) => MS::Custom(
cpr.clone(),
s.1.iter()
s.args
.iter()
.map(|arg| match arg {
StatementArg::Key(ak) => Ok(ak.clone().into()),
_ => Err(anyhow!("Invalid statement arg: {}", arg)),
@ -102,8 +113,8 @@ impl TryFrom<Statement> for middleware::Statement {
impl fmt::Display for Statement {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?} ", self.0)?;
for (i, arg) in self.1.iter().enumerate() {
write!(f, "{:?} ", self.predicate)?;
for (i, arg) in self.args.iter().enumerate() {
if i != 0 {
write!(f, " ")?;
}