Skip to content

Commit 9afc436

Browse files
authored
Serialization of Signed and Main Pods (#128)
1 parent fee70af commit 9afc436

File tree

16 files changed

+817
-189
lines changed

16 files changed

+817
-189
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ log = "0.4"
1818
env_logger = "0.11"
1919
# enabled by features:
2020
plonky2 = { git = "https://github.com/0xPolygonZero/plonky2", optional = true }
21+
serde = "1.0.219"
22+
serde_json = "1.0.140"
23+
base64 = "0.22.1"
24+
schemars = "1.0.0-alpha.17"
2125

2226
[features]
2327
default = ["backend_plonky2"]

src/backends/plonky2/basetypes.rs

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
//! `backend_plonky2` feature is enabled.
33
//! See src/middleware/basetypes.rs for more details.
44
5+
use crate::middleware::serialization::{
6+
deserialize_hash_tuple, deserialize_value_tuple, serialize_hash_tuple, serialize_value_tuple,
7+
};
8+
use crate::middleware::{Params, ToFields};
59
use anyhow::{anyhow, Error, Result};
610
use hex::{FromHex, FromHexError};
711
use plonky2::field::goldilocks_field::GoldilocksField;
@@ -10,11 +14,11 @@ use plonky2::hash::poseidon::PoseidonHash;
1014
use plonky2::plonk::config::Hasher;
1115
use plonky2::plonk::config::PoseidonGoldilocksConfig;
1216
use plonky2::plonk::proof::Proof as Plonky2Proof;
17+
use schemars::JsonSchema;
18+
use serde::{Deserialize, Serialize};
1319
use std::cmp::{Ord, Ordering};
1420
use std::fmt;
1521

16-
use crate::middleware::{Params, ToFields};
17-
1822
use crate::backends::counter;
1923

2024
/// F is the native field we use everywhere. Currently it's Goldilocks from plonky2
@@ -34,8 +38,18 @@ pub const EMPTY_VALUE: Value = Value([F::ZERO, F::ZERO, F::ZERO, F::ZERO]);
3438
pub const SELF_ID_HASH: Hash = Hash([F::ONE, F::ZERO, F::ZERO, F::ZERO]);
3539
pub const EMPTY_HASH: Hash = Hash([F::ZERO, F::ZERO, F::ZERO, F::ZERO]);
3640

37-
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)]
38-
pub struct Value(pub [F; VALUE_SIZE]);
41+
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
42+
#[schemars(rename = "MiddlewareValue")]
43+
pub struct Value(
44+
#[serde(
45+
serialize_with = "serialize_value_tuple",
46+
deserialize_with = "deserialize_value_tuple"
47+
)]
48+
// We know that Serde will serialize and deserialize this as a string, so we can
49+
// use the JsonSchema to validate the format.
50+
#[schemars(with = "String", regex(pattern = r"^[0-9a-fA-F]{64}$"))]
51+
pub [F; VALUE_SIZE],
52+
);
3953

4054
impl ToFields for Value {
4155
fn to_fields(&self, _params: &Params) -> Vec<F> {
@@ -117,8 +131,15 @@ impl fmt::Display for Value {
117131
}
118132
}
119133

120-
#[derive(Clone, Copy, Debug, Default, Hash, Eq, PartialEq)]
121-
pub struct Hash(pub [F; HASH_SIZE]);
134+
#[derive(Clone, Copy, Debug, Default, Hash, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
135+
pub struct Hash(
136+
#[serde(
137+
serialize_with = "serialize_hash_tuple",
138+
deserialize_with = "deserialize_hash_tuple"
139+
)]
140+
#[schemars(with = "String", regex(pattern = r"^[0-9a-fA-F]{64}$"))]
141+
pub [F; HASH_SIZE],
142+
);
122143

123144
pub fn hash_value(input: &Value) -> Hash {
124145
hash_fields(&input.0)

src/backends/plonky2/mock_main/mod.rs

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
use anyhow::{anyhow, Result};
2+
use base64::prelude::*;
23
use itertools::Itertools;
34
use log::error;
45
use plonky2::hash::poseidon::PoseidonHash;
56
use plonky2::plonk::config::Hasher;
7+
use serde::{Deserialize, Serialize};
68
use std::any::Any;
79
use std::fmt;
810

@@ -27,14 +29,14 @@ impl PodProver for MockProver {
2729
}
2830
}
2931

30-
#[derive(Clone, Debug)]
32+
#[derive(Clone, Debug, Serialize, Deserialize)]
3133
pub struct MockMainPod {
3234
params: Params,
3335
id: PodId,
34-
input_signed_pods: Vec<Box<dyn Pod>>,
35-
input_main_pods: Vec<Box<dyn Pod>>,
36+
// input_signed_pods: Vec<Box<dyn Pod>>,
37+
// input_main_pods: Vec<Box<dyn Pod>>,
3638
// New statements introduced by this pod
37-
input_statements: Vec<Statement>,
39+
// input_statements: Vec<Statement>,
3840
public_statements: Vec<Statement>,
3941
operations: Vec<Operation>,
4042
// All statements (inherited + new)
@@ -258,7 +260,7 @@ impl MockMainPod {
258260
.map(|mid_arg| Self::find_op_arg(statements, mid_arg))
259261
.collect::<Result<Vec<_>>>()?;
260262
Self::pad_operation_args(params, &mut args);
261-
operations.push(Operation(op.code(), args));
263+
operations.push(Operation(op.predicate(), args));
262264
}
263265
Ok(operations)
264266
}
@@ -332,9 +334,9 @@ impl MockMainPod {
332334
Ok(Self {
333335
params: params.clone(),
334336
id,
335-
input_signed_pods,
336-
input_main_pods,
337-
input_statements,
337+
// input_signed_pods,
338+
// input_main_pods,
339+
// input_statements,
338340
public_statements,
339341
statements,
340342
operations,
@@ -360,6 +362,15 @@ impl MockMainPod {
360362
fn pad_operation_args(params: &Params, args: &mut Vec<OperationArg>) {
361363
fill_pad(args, OperationArg::None, params.max_operation_args)
362364
}
365+
366+
pub fn deserialize(serialized: String) -> Result<Self> {
367+
let proof = String::from_utf8(BASE64_STANDARD.decode(&serialized)?)
368+
.map_err(|e| anyhow::anyhow!("Invalid base64 encoding: {}", e))?;
369+
let pod: MockMainPod = serde_json::from_str(&proof)
370+
.map_err(|e| anyhow::anyhow!("Failed to parse proof: {}", e))?;
371+
372+
Ok(pod)
373+
}
363374
}
364375

365376
pub fn hash_statements(statements: &[Statement], _params: &Params) -> middleware::Hash {
@@ -485,6 +496,10 @@ impl Pod for MockMainPod {
485496
fn into_any(self: Box<Self>) -> Box<dyn Any> {
486497
self
487498
}
499+
500+
fn serialized_proof(&self) -> String {
501+
BASE64_STANDARD.encode(serde_json::to_string(self).unwrap())
502+
}
488503
}
489504

490505
#[cfg(test)]

src/backends/plonky2/mock_main/operation.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
use anyhow::Result;
2-
use std::fmt;
3-
41
use super::Statement;
52
use crate::middleware::{self, OperationType};
3+
use anyhow::Result;
4+
use serde::{Deserialize, Serialize};
5+
use std::fmt;
66

7-
#[derive(Clone, Debug, PartialEq, Eq)]
7+
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
88
pub enum OperationArg {
99
None,
1010
Index(usize),
@@ -16,7 +16,7 @@ impl OperationArg {
1616
}
1717
}
1818

19-
#[derive(Clone, Debug, PartialEq, Eq)]
19+
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
2020
pub struct Operation(pub OperationType, pub Vec<OperationArg>);
2121

2222
impl Operation {

src/backends/plonky2/mock_main/statement.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
use anyhow::{anyhow, Result};
2+
use serde::{Deserialize, Serialize};
23
use std::fmt;
34

45
use crate::middleware::{
56
self, AnchoredKey, NativePredicate, Params, Predicate, StatementArg, ToFields,
67
};
78

8-
#[derive(Clone, Debug, PartialEq, Eq)]
9+
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
910
pub struct Statement(pub Predicate, pub Vec<StatementArg>);
1011

1112
impl Statement {

src/backends/plonky2/mock_signed.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,16 @@ pub struct MockSignedPod {
4444
dict: Dictionary,
4545
}
4646

47+
impl MockSignedPod {
48+
pub fn deserialize(id: PodId, signature: String, dict: Dictionary) -> Self {
49+
Self {
50+
id,
51+
signature,
52+
dict,
53+
}
54+
}
55+
}
56+
4757
impl Pod for MockSignedPod {
4858
fn verify(&self) -> bool {
4959
// 1. Verify type
@@ -100,6 +110,10 @@ impl Pod for MockSignedPod {
100110
fn into_any(self: Box<Self>) -> Box<dyn Any> {
101111
self
102112
}
113+
114+
fn serialized_proof(&self) -> String {
115+
self.signature.to_string()
116+
}
103117
}
104118

105119
#[cfg(test)]

src/examples.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ use std::collections::HashMap;
66

77
use crate::backends::plonky2::mock_signed::MockSigner;
88
use crate::frontend::{
9-
MainPodBuilder, Operation, OperationArg, SignedPod, SignedPodBuilder, Statement, Value,
9+
containers::{Dictionary, Set},
10+
MainPodBuilder, SignedPod, SignedPodBuilder, Statement, Value,
1011
};
11-
use crate::middleware::containers::Set;
12-
use crate::middleware::{containers::Dictionary, Params, PodType, KEY_SIGNER, KEY_TYPE};
13-
use crate::middleware::{hash_str, CustomPredicateRef, NativeOperation, OperationType};
12+
use crate::middleware::CustomPredicateRef;
13+
use crate::middleware::{Params, PodType, KEY_SIGNER, KEY_TYPE};
1414
use crate::op;
1515

1616
// ZuKYC
@@ -28,10 +28,11 @@ pub fn zu_kyc_sign_pod_builders(
2828
pay_stub.insert("startDate", 1706367566);
2929

3030
let mut sanction_list = SignedPodBuilder::new(params);
31-
let sanctions_values = ["A343434340"].map(|s| crate::middleware::Value::from(hash_str(s)));
31+
let sanctions_values = ["A343434340"].map(Value::from);
32+
3233
sanction_list.insert(
3334
"sanctionList",
34-
Value::Set(Set::new(&sanctions_values.to_vec()).unwrap()),
35+
Value::Set(Set::new(sanctions_values.into()).unwrap()),
3536
);
3637

3738
(gov_id, pay_stub, sanction_list)
@@ -337,7 +338,7 @@ pub fn great_boy_pod_full_flow() -> Result<MainPodBuilder> {
337338
alice_friend_pods.push(friend.sign(&mut bob_signer).unwrap());
338339
alice_friend_pods.push(friend.sign(&mut charlie_signer).unwrap());
339340

340-
let good_boy_issuers_dict = Value::Dictionary(Dictionary::new(&HashMap::new())?); // empty
341+
let good_boy_issuers_dict = Value::Dictionary(Dictionary::new(HashMap::new()).unwrap()); // empty
341342
great_boy_pod_builder(
342343
&params,
343344
[
@@ -396,6 +397,6 @@ pub fn tickets_pod_full_flow() -> Result<MainPodBuilder> {
396397
&signed_pod,
397398
123,
398399
true,
399-
&Value::Dictionary(Dictionary::new(&HashMap::new())?),
400+
&Value::Dictionary(Dictionary::new(HashMap::new()).unwrap()),
400401
)
401402
}

src/frontend/containers.rs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
use std::collections::HashMap;
2+
3+
use anyhow::Result;
4+
use schemars::JsonSchema;
5+
use serde::{Deserialize, Serialize};
6+
7+
use super::Value;
8+
use crate::frontend::serialization::ordered_map;
9+
use crate::middleware::{
10+
containers::{
11+
Array as MiddlewareArray, Dictionary as MiddlewareDictionary, Set as MiddlewareSet,
12+
},
13+
hash_str, Value as MiddlewareValue,
14+
};
15+
16+
#[derive(Clone, Debug, PartialEq, Eq, Serialize, JsonSchema)]
17+
#[serde(transparent)]
18+
pub struct Set(Vec<Value>, #[serde(skip)] MiddlewareSet);
19+
20+
impl Set {
21+
pub fn new(values: Vec<Value>) -> Result<Self> {
22+
let set = MiddlewareSet::new(&values.iter().map(|v| MiddlewareValue::from(v)).collect())?;
23+
Ok(Self(values, set))
24+
}
25+
26+
pub fn middleware_set(&self) -> &MiddlewareSet {
27+
&self.1
28+
}
29+
30+
pub fn values(&self) -> &Vec<Value> {
31+
&self.0
32+
}
33+
}
34+
35+
impl<'de> Deserialize<'de> for Set {
36+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
37+
where
38+
D: serde::Deserializer<'de>,
39+
{
40+
let values: Vec<Value> = Vec::deserialize(deserializer)?;
41+
Set::new(values).map_err(serde::de::Error::custom)
42+
}
43+
}
44+
45+
#[derive(Clone, Debug, PartialEq, Eq, Serialize, JsonSchema)]
46+
#[serde(transparent)]
47+
pub struct Dictionary(
48+
#[serde(serialize_with = "ordered_map")] HashMap<String, Value>,
49+
#[serde(skip)] MiddlewareDictionary,
50+
);
51+
52+
impl Dictionary {
53+
pub fn new(values: HashMap<String, Value>) -> Result<Self> {
54+
let dict = MiddlewareDictionary::new(
55+
&values
56+
.iter()
57+
.map(|(k, v)| (hash_str(k), MiddlewareValue::from(v)))
58+
.collect::<HashMap<_, _>>(),
59+
)?;
60+
Ok(Self(values, dict))
61+
}
62+
63+
pub fn middleware_dict(&self) -> &MiddlewareDictionary {
64+
&self.1
65+
}
66+
67+
pub fn values(&self) -> &HashMap<String, Value> {
68+
&self.0
69+
}
70+
}
71+
72+
impl<'de> Deserialize<'de> for Dictionary {
73+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
74+
where
75+
D: serde::Deserializer<'de>,
76+
{
77+
let values: HashMap<String, Value> = HashMap::deserialize(deserializer)?;
78+
Dictionary::new(values).map_err(serde::de::Error::custom)
79+
}
80+
}
81+
82+
#[derive(Clone, Debug, PartialEq, Eq, Serialize, JsonSchema)]
83+
#[serde(transparent)]
84+
pub struct Array(Vec<Value>, #[serde(skip)] MiddlewareArray);
85+
86+
impl Array {
87+
pub fn new(values: Vec<Value>) -> Result<Self> {
88+
let array =
89+
MiddlewareArray::new(&values.iter().map(|v| MiddlewareValue::from(v)).collect())?;
90+
Ok(Self(values, array))
91+
}
92+
93+
pub fn middleware_array(&self) -> &MiddlewareArray {
94+
&self.1
95+
}
96+
97+
pub fn values(&self) -> &Vec<Value> {
98+
&self.0
99+
}
100+
}
101+
102+
impl<'de> Deserialize<'de> for Array {
103+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
104+
where
105+
D: serde::Deserializer<'de>,
106+
{
107+
let values: Vec<Value> = Vec::deserialize(deserializer)?;
108+
Array::new(values).map_err(serde::de::Error::custom)
109+
}
110+
}

0 commit comments

Comments
 (0)