Implement generic serialization/deserialization (#260)

* complete general serialization

* bump default params temporarily

* disable recursion in great_boy_pod example
This commit is contained in:
Eduard S. 2025-06-10 12:17:30 +02:00 committed by GitHub
parent c66506c048
commit 621f8be6b5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 392 additions and 253 deletions

View file

@ -46,6 +46,8 @@ use super::serialization::*;
// types would come from the plonky3 backend.
#[cfg(feature = "backend_plonky2")]
pub use crate::backends::plonky2::basetypes::*;
#[cfg(feature = "backend_plonky2")]
pub use crate::backends::plonky2::{Error as BackendError, Result as BackendResult};
use crate::middleware::{Params, ToFields, Value};
pub const HASH_SIZE: usize = 4;

View file

@ -2,6 +2,8 @@
//! the backend.
use std::sync::Arc;
use strum_macros::FromRepr;
mod basetypes;
use std::{
cmp::{Ordering, PartialEq, PartialOrd},
@ -15,6 +17,7 @@ pub mod containers;
mod custom;
mod error;
mod operation;
mod pod_deserialization;
pub mod serialization;
mod statement;
use std::{any::Any, collections::HashMap, fmt};
@ -24,6 +27,7 @@ pub use custom::*;
use dyn_clone::DynClone;
pub use error::*;
pub use operation::*;
pub use pod_deserialization::*;
use serialization::*;
pub use statement::*;
@ -565,7 +569,7 @@ impl ToFields for PodId {
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, FromRepr, Serialize, Deserialize, JsonSchema)]
pub enum PodType {
None = 0,
MockSigned = 1,
@ -636,7 +640,8 @@ impl Default for Params {
max_signed_pod_values: 8,
max_public_statements: 10,
num_public_statements_id: 16,
max_statement_args: 5,
// TODO: Reduce to 5 or less after https://github.com/0xPARC/pod2/issues/229
max_statement_args: 6,
max_operation_args: 5,
max_custom_predicate_batches: 2,
max_custom_predicate_verifications: 5,
@ -735,6 +740,8 @@ pub trait Pod: fmt::Debug + DynClone + Any {
fn params(&self) -> &Params;
fn verify(&self) -> Result<(), Box<DynError>>;
fn id(&self) -> PodId;
/// Return a uuid of the pod type and its name. The name is only used as metadata.
fn pod_type(&self) -> (usize, &'static str);
/// Statements as internally generated, where self-referencing arguments use SELF in the
/// anchored key. The serialization of these statements is used to calculate the id.
fn pub_self_statements(&self) -> Vec<Statement>;
@ -746,6 +753,9 @@ pub trait Pod: fmt::Debug + DynClone + Any {
.map(|statement| normalize_statement(&statement, self.id()))
.collect()
}
/// Return this Pods data serialized into a json value. This serialization can skip `params,
/// id, vds_root`
fn serialize_data(&self) -> serde_json::Value;
/// Extract key-values from ValueOf public statements
fn kvs(&self) -> HashMap<AnchoredKey, Value> {
self.pub_statements()
@ -767,7 +777,7 @@ pub trait Pod: fmt::Debug + DynClone + Any {
// reconstruct the proof.
// It is an important principle that this data is opaque to the front-end
// and any third-party code.
fn serialized_proof(&self) -> String;
// fn serialized_proof(&self) -> String;
}
// impl Clone for Box<dyn Pod>
@ -810,11 +820,14 @@ impl Pod for NonePod {
fn id(&self) -> PodId {
PodId(EMPTY_HASH)
}
fn pod_type(&self) -> (usize, &'static str) {
(PodType::None as usize, "None")
}
fn pub_self_statements(&self) -> Vec<Statement> {
Vec::new()
}
fn serialized_proof(&self) -> String {
"".to_string()
fn serialize_data(&self) -> serde_json::Value {
serde_json::Value::Null
}
}

View file

@ -0,0 +1,93 @@
use std::{
collections::HashMap,
sync::{LazyLock, Mutex},
};
use crate::middleware::{
BackendResult, Error, Hash, Params, Pod, PodId, PodType, RecursivePod, Result,
};
type DeserializeFn = fn(
params: Params,
id: PodId,
vds_root: Hash,
data: serde_json::Value,
) -> BackendResult<Box<dyn RecursivePod>>;
static DESERIALIZERS: LazyLock<Mutex<HashMap<usize, DeserializeFn>>> =
LazyLock::new(backend::deserializers_default);
pub fn register_pod_deserializer(pod_type: usize, deserialize_fn: DeserializeFn) {
DESERIALIZERS
.lock()
.unwrap()
.insert(pod_type, deserialize_fn);
}
pub fn deserialize_pod(
pod_type: usize,
params: Params,
id: PodId,
vds_root: Hash,
data: serde_json::Value,
) -> Result<Box<dyn RecursivePod>> {
let deserialize_fn: DeserializeFn =
*DESERIALIZERS
.lock()
.unwrap()
.get(&pod_type)
.ok_or(Error::custom(format!(
"pod deserializer for pod_type={} not registered. See https://github.com/0xPARC/pod2/wiki/PodType for pod type assignments.",
pod_type
)))?;
deserialize_fn(params, id, vds_root, data)
.map_err(|e| Error::custom(format!("deserialize error: {:?}", e)))
}
pub fn deserialize_signed_pod(
pod_type: usize,
id: PodId,
data: serde_json::Value,
) -> Result<Box<dyn Pod>> {
backend::deserialize_signed_pod(pod_type, id, data)
}
#[cfg(feature = "backend_plonky2")]
mod backend {
use super::*;
use crate::backends::plonky2::{
emptypod::EmptyPod,
mainpod::MainPod,
mock::{emptypod::MockEmptyPod, mainpod::MockMainPod, signedpod::MockSignedPod},
signedpod::SignedPod,
};
pub(super) fn deserializers_default() -> Mutex<HashMap<usize, DeserializeFn>> {
let mut map: HashMap<usize, DeserializeFn> = HashMap::new();
map.insert(PodType::Empty as usize, EmptyPod::deserialize);
map.insert(PodType::Main as usize, MainPod::deserialize);
map.insert(PodType::MockEmpty as usize, MockEmptyPod::deserialize);
map.insert(PodType::MockMain as usize, MockMainPod::deserialize);
Mutex::new(map)
}
pub(super) fn deserialize_signed_pod(
pod_type: usize,
id: PodId,
data: serde_json::Value,
) -> Result<Box<dyn Pod>> {
if pod_type == PodType::MockSigned as usize {
MockSignedPod::deserialize(id, data)
.map_err(|e| Error::custom(format!("deserialize error: {:?}", e)))
} else if pod_type == PodType::Signed as usize {
SignedPod::deserialize(id, data)
.map_err(|e| Error::custom(format!("deserialize error: {:?}", e)))
} else {
Err(Error::custom(format!(
"unexpected pod_type={} for deserialize_signed_pod",
pod_type
)))
}
}
}