Skip to content

Commit 02d6fb3

Browse files
committed
Produce JSON schema for serialized types
1 parent ea7eb45 commit 02d6fb3

File tree

9 files changed

+121
-100
lines changed

9 files changed

+121
-100
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ plonky2 = { git = "https://github.com/0xPolygonZero/plonky2", optional = true }
1919
serde = "1.0.219"
2020
serde_json = "1.0.140"
2121
base64 = "0.22.1"
22+
schemars = "1.0.0-alpha.17"
2223

2324
[features]
2425
default = ["backend_plonky2"]

src/backends/plonky2/basetypes.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use plonky2::hash::poseidon::PoseidonHash;
1414
use plonky2::plonk::config::Hasher;
1515
use plonky2::plonk::config::PoseidonGoldilocksConfig;
1616
use plonky2::plonk::proof::Proof as Plonky2Proof;
17+
use schemars::JsonSchema;
1718
use serde::{Deserialize, Serialize};
1819
use std::cmp::{Ord, Ordering};
1920
use std::fmt;
@@ -37,12 +38,16 @@ pub const EMPTY: Value = Value([F::ZERO, F::ZERO, F::ZERO, F::ZERO]);
3738
pub const SELF_ID_HASH: Hash = Hash([F::ONE, F::ZERO, F::ZERO, F::ZERO]);
3839
pub const NULL: Hash = Hash([F::ZERO, F::ZERO, F::ZERO, F::ZERO]);
3940

40-
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, Serialize, Deserialize)]
41+
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
42+
#[schemars(rename = "MiddlewareValue")]
4143
pub struct Value(
4244
#[serde(
4345
serialize_with = "serialize_value_tuple",
4446
deserialize_with = "deserialize_value_tuple"
4547
)]
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}$"))]
4651
pub [F; VALUE_SIZE],
4752
);
4853

@@ -126,12 +131,13 @@ impl fmt::Display for Value {
126131
}
127132
}
128133

129-
#[derive(Clone, Copy, Debug, Default, Hash, Eq, PartialEq, Serialize, Deserialize)]
134+
#[derive(Clone, Copy, Debug, Default, Hash, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
130135
pub struct Hash(
131136
#[serde(
132137
serialize_with = "serialize_hash_tuple",
133138
deserialize_with = "deserialize_hash_tuple"
134139
)]
140+
#[schemars(with = "String", regex(pattern = r"^[0-9a-fA-F]{64}$"))]
135141
pub [F; HASH_SIZE],
136142
);
137143

src/frontend/containers.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::collections::HashMap;
22

3+
use schemars::JsonSchema;
34
use serde::{Deserialize, Serialize};
45

56
use super::Value;
@@ -10,7 +11,7 @@ use crate::middleware::{
1011
hash_str, Value as MiddlewareValue,
1112
};
1213

13-
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
14+
#[derive(Clone, Debug, PartialEq, Eq, Serialize, JsonSchema)]
1415
#[serde(transparent)]
1516
pub struct Set(Vec<Value>, #[serde(skip)] MiddlewareSet);
1617

@@ -40,7 +41,7 @@ impl<'de> Deserialize<'de> for Set {
4041
}
4142
}
4243

43-
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
44+
#[derive(Clone, Debug, PartialEq, Eq, Serialize, JsonSchema)]
4445
#[serde(transparent)]
4546
pub struct Dictionary(HashMap<String, Value>, #[serde(skip)] MiddlewareDictionary);
4647

@@ -75,7 +76,7 @@ impl<'de> Deserialize<'de> for Dictionary {
7576
}
7677
}
7778

78-
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
79+
#[derive(Clone, Debug, PartialEq, Eq, Serialize, JsonSchema)]
7980
#[serde(transparent)]
8081
pub struct Array(Vec<Value>, #[serde(skip)] MiddlewareArray);
8182

src/frontend/mod.rs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::middleware::{OperationType, Predicate, KEY_SIGNER, KEY_TYPE};
1010
use anyhow::{anyhow, Error, Result};
1111
use containers::{Array, Dictionary, Set};
1212
use itertools::Itertools;
13+
use schemars::JsonSchema;
1314
use serde::{Deserialize, Serialize};
1415
use std::collections::HashMap;
1516
use std::convert::From;
@@ -25,19 +26,20 @@ pub use operation::*;
2526
pub use statement::*;
2627

2728
/// This type is just for presentation purposes.
28-
#[derive(Clone, Debug, Default, h::Hash, PartialEq, Eq, Serialize, Deserialize)]
29+
#[derive(Clone, Debug, Default, h::Hash, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
2930
pub enum PodClass {
3031
#[default]
3132
Signed,
3233
Main,
3334
}
3435

3536
// An Origin, which represents a reference to an ancestor POD.
36-
#[derive(Clone, Debug, PartialEq, Eq, h::Hash, Default, Serialize, Deserialize)]
37+
#[derive(Clone, Debug, PartialEq, Eq, h::Hash, Default, Serialize, Deserialize, JsonSchema)]
3738
#[serde(into = "OriginSerdeHelper", from = "OriginSerdeHelper")]
3839
pub struct Origin(pub PodClass, pub PodId);
3940

40-
#[derive(Serialize, Deserialize)]
41+
#[derive(Serialize, Deserialize, JsonSchema)]
42+
#[schemars(rename = "Origin")]
4143
struct OriginSerdeHelper {
4244
pod_class: PodClass,
4345
pod_id: PodId,
@@ -58,7 +60,7 @@ impl From<OriginSerdeHelper> for Origin {
5860
}
5961
}
6062

61-
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
63+
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
6264
pub enum Value {
6365
// Serde cares about the order of the enum variants, with untagged variants
6466
// appearing at the end.
@@ -212,7 +214,8 @@ impl SignedPodBuilder {
212214

213215
/// SignedPod is a wrapper on top of backend::SignedPod, which additionally stores the
214216
/// string<-->hash relation of the keys.
215-
#[derive(Debug, Clone)]
217+
#[derive(Debug, Clone, Serialize, Deserialize)]
218+
#[serde(try_from = "SignedPodHelper", into = "SignedPodHelper")]
216219
pub struct SignedPod {
217220
pub pod: Box<dyn middleware::Pod>,
218221
/// Key-value pairs as represented in the frontend. These should
@@ -260,9 +263,12 @@ impl SignedPod {
260263
}
261264
}
262265

263-
#[derive(Clone, Debug, PartialEq, Eq, h::Hash, Serialize, Deserialize)]
266+
#[derive(Clone, Debug, PartialEq, Eq, h::Hash, Serialize, Deserialize, JsonSchema)]
264267
#[serde(into = "AnchoredKeySerdeHelper", from = "AnchoredKeySerdeHelper")]
265-
pub struct AnchoredKey(pub Origin, pub String);
268+
pub struct AnchoredKey(
269+
#[schemars(with = "OriginSerdeHelper")] pub Origin,
270+
pub String,
271+
);
266272

267273
impl From<AnchoredKey> for middleware::AnchoredKey {
268274
fn from(ak: AnchoredKey) -> Self {
@@ -273,8 +279,10 @@ impl From<AnchoredKey> for middleware::AnchoredKey {
273279
// Tuple structs get serialized to bare JSON arrays, which is not very
274280
// user-friendly. This helper struct allows us to serialize AnchoredKeys
275281
// into a structure with named fields.
276-
#[derive(Serialize, Deserialize)]
277-
struct AnchoredKeySerdeHelper {
282+
#[derive(Serialize, Deserialize, JsonSchema)]
283+
#[schemars(rename = "AnchoredKey")]
284+
pub struct AnchoredKeySerdeHelper {
285+
#[schemars(with = "OriginSerdeHelper")]
278286
origin: Origin,
279287
key: String,
280288
}
@@ -807,7 +815,8 @@ impl MainPodBuilder {
807815
}
808816
}
809817

810-
#[derive(Debug)]
818+
#[derive(Debug, Clone, Serialize, Deserialize)]
819+
#[serde(try_from = "MainPodHelper", into = "MainPodHelper")]
811820
pub struct MainPod {
812821
pub pod: Box<dyn middleware::Pod>,
813822
pub public_statements: Vec<Statement>,

src/frontend/serialization.rs

Lines changed: 69 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,88 @@
11
use std::collections::HashMap;
22

33
use base64::prelude::*;
4-
use serde::ser::{SerializeStruct, Serializer};
4+
use schemars::JsonSchema;
55
use serde::{Deserialize, Serialize};
66

77
use crate::backends::plonky2::mock_main::MockMainPod;
88
use crate::backends::plonky2::mock_signed::MockSignedPod;
99
use crate::frontend::containers::Dictionary;
1010
use crate::frontend::Statement;
11+
use crate::frontend::StatementSerdeHelper;
1112
use crate::middleware::{PodId, F};
1213
use crate::middleware::{HASH_SIZE, VALUE_SIZE};
1314
use plonky2::field::types::Field;
1415

1516
use super::Value;
1617
use super::{MainPod, SignedPod};
1718

18-
impl Serialize for SignedPod {
19-
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
20-
where
21-
S: Serializer,
22-
{
23-
let mut state = serializer.serialize_struct("SignedPod", 3)?;
24-
state.serialize_field(
25-
"entries",
26-
&self
27-
.kvs
28-
.iter()
29-
.map(|(k, v)| (k.clone(), v))
30-
.collect::<HashMap<String, &Value>>(),
31-
)?;
32-
33-
let signature = self.pod.serialized_proof();
34-
35-
state.serialize_field("id", &self.id())?;
36-
state.serialize_field("proof", &signature)?;
37-
state.serialize_field("pod_class", "Signed")?;
38-
state.serialize_field("pod_type", "Mock")?;
39-
state.end()
40-
}
19+
#[derive(Serialize, Deserialize, JsonSchema)]
20+
pub struct SignedPodHelper {
21+
entries: HashMap<String, Value>,
22+
proof: String,
23+
pod_class: String,
24+
pod_type: String,
4125
}
4226

43-
impl<'de> Deserialize<'de> for SignedPod {
44-
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
45-
where
46-
D: serde::Deserializer<'de>,
47-
{
48-
#[derive(Deserialize)]
49-
struct SignedPodHelper {
50-
entries: HashMap<String, Value>,
51-
proof: String,
52-
pod_class: String,
53-
pod_type: String,
54-
}
27+
impl TryFrom<SignedPodHelper> for SignedPod {
28+
type Error = anyhow::Error;
5529

56-
let helper = SignedPodHelper::deserialize(deserializer)?;
30+
fn try_from(helper: SignedPodHelper) -> Result<SignedPod, Self::Error> {
5731
if helper.pod_class != "Signed" {
58-
return Err(serde::de::Error::custom("pod_class is not Signed"));
32+
return Err(anyhow::anyhow!("pod_class is not Signed"));
5933
}
6034
if helper.pod_type != "Mock" {
61-
return Err(serde::de::Error::custom("pod_type is not Mock"));
35+
return Err(anyhow::anyhow!("pod_type is not Mock"));
6236
}
63-
let kvs = helper.entries;
64-
let dict = Dictionary::new(kvs.clone()).middleware_dict().clone();
37+
38+
let dict = Dictionary::new(helper.entries.clone())
39+
.middleware_dict()
40+
.clone();
6541
let pod = MockSignedPod::new(PodId(dict.commitment()), helper.proof, dict);
42+
6643
Ok(SignedPod {
6744
pod: Box::new(pod),
68-
kvs: kvs,
45+
kvs: helper.entries,
6946
})
7047
}
7148
}
7249

73-
impl Serialize for MainPod {
74-
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
75-
where
76-
S: Serializer,
77-
{
78-
let mut state = serializer.serialize_struct("MainPod", 2)?;
79-
state.serialize_field("public_statements", &self.public_statements)?;
80-
state.serialize_field("id", &self.pod.id())?;
81-
state.serialize_field("proof", &self.pod.serialized_proof())?;
82-
state.serialize_field("pod_class", "Main")?;
83-
state.serialize_field("pod_type", "Mock")?;
84-
state.end()
50+
impl From<SignedPod> for SignedPodHelper {
51+
fn from(pod: SignedPod) -> Self {
52+
SignedPodHelper {
53+
entries: pod.kvs,
54+
proof: pod.pod.serialized_proof(),
55+
pod_class: "Signed".to_string(),
56+
pod_type: "Mock".to_string(),
57+
}
8558
}
8659
}
8760

88-
impl<'de> Deserialize<'de> for MainPod {
89-
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
90-
where
91-
D: serde::Deserializer<'de>,
92-
{
93-
#[derive(Deserialize)]
94-
struct MainPodHelper {
95-
public_statements: Vec<Statement>,
96-
proof: String,
97-
pod_class: String,
98-
pod_type: String,
99-
}
61+
#[derive(Serialize, Deserialize, JsonSchema)]
62+
pub struct MainPodHelper {
63+
#[schemars(with = "Vec<StatementSerdeHelper>")]
64+
public_statements: Vec<Statement>,
65+
proof: String,
66+
pod_class: String,
67+
pod_type: String,
68+
}
10069

101-
let helper = MainPodHelper::deserialize(deserializer)?;
70+
impl TryFrom<MainPodHelper> for MainPod {
71+
type Error = anyhow::Error; // or you can create a custom error type
72+
73+
fn try_from(helper: MainPodHelper) -> Result<Self, Self::Error> {
10274
if helper.pod_class != "Main" {
103-
return Err(serde::de::Error::custom("pod_class is not Main"));
75+
return Err(anyhow::anyhow!("pod_class is not Main"));
10476
}
10577
if helper.pod_type != "Mock" {
106-
return Err(serde::de::Error::custom("pod_type is not Mock"));
78+
return Err(anyhow::anyhow!("pod_type is not Mock"));
10779
}
108-
let proof = String::from_utf8(BASE64_STANDARD.decode(&helper.proof).unwrap()).unwrap();
109-
let pod: MockMainPod = serde_json::from_str(&proof).unwrap();
80+
81+
let proof = String::from_utf8(BASE64_STANDARD.decode(&helper.proof)?)
82+
.map_err(|e| anyhow::anyhow!("Invalid base64 encoding: {}", e))?;
83+
84+
let pod: MockMainPod = serde_json::from_str(&proof)
85+
.map_err(|e| anyhow::anyhow!("Failed to parse proof: {}", e))?;
11086

11187
Ok(MainPod {
11288
pod: Box::new(pod),
@@ -115,6 +91,17 @@ impl<'de> Deserialize<'de> for MainPod {
11591
}
11692
}
11793

94+
impl From<MainPod> for MainPodHelper {
95+
fn from(pod: MainPod) -> Self {
96+
MainPodHelper {
97+
public_statements: pod.public_statements,
98+
proof: pod.pod.serialized_proof(),
99+
pod_class: "Main".to_string(),
100+
pod_type: "Mock".to_string(),
101+
}
102+
}
103+
}
104+
118105
pub fn serialize_i64<S>(value: &i64, serializer: S) -> Result<S::Ok, S::Error>
119106
where
120107
S: serde::Serializer,
@@ -305,4 +292,15 @@ mod tests {
305292
assert_eq!(kyc_pod.pod.id(), deserialized.pod.id());
306293
assert_eq!(kyc_pod.pod.verify(), deserialized.pod.verify());
307294
}
295+
296+
#[test]
297+
fn test_gen_schema() {
298+
let schema = schemars::schema_for!(SignedPodHelper);
299+
let schema = serde_json::to_string_pretty(&schema).unwrap();
300+
println!("schema: {}", schema);
301+
302+
let schema = schemars::schema_for!(MainPodHelper);
303+
let schema = serde_json::to_string_pretty(&schema).unwrap();
304+
println!("schema: {}", schema);
305+
}
308306
}

src/frontend/statement.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
use super::{AnchoredKey, SignedPod, Value};
1+
use super::{AnchoredKey, AnchoredKeySerdeHelper, SignedPod, Value};
22
use crate::middleware::{self, NativePredicate, Predicate};
33
use anyhow::{anyhow, Result};
4+
use schemars::JsonSchema;
45
use serde::{Deserialize, Serialize};
56
use std::fmt;
67

7-
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
8+
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
89
pub enum StatementArg {
910
Literal(Value),
11+
#[schemars(with = "AnchoredKeySerdeHelper")]
1012
Key(AnchoredKey),
1113
}
1214

@@ -19,12 +21,13 @@ impl fmt::Display for StatementArg {
1921
}
2022
}
2123

22-
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
24+
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
2325
#[serde(into = "StatementSerdeHelper", from = "StatementSerdeHelper")]
2426
pub struct Statement(pub Predicate, pub Vec<StatementArg>);
2527

26-
#[derive(Serialize, Deserialize)]
27-
struct StatementSerdeHelper {
28+
#[derive(Serialize, Deserialize, JsonSchema)]
29+
#[schemars(rename = "Statement")]
30+
pub struct StatementSerdeHelper {
2831
predicate: Predicate,
2932
args: Vec<StatementArg>,
3033
}

0 commit comments

Comments
 (0)