Serialization for Plonky2 Signed and Main PODs (#234)
* WIP * WIP * Working serialization for both Mock and Plonky2 versions of Signed and Main Pods * Restore useful comment about serialized_proof() * Use plonky2 serialization for signatures and proofs * Add schema renames for Serialized SignedPod/MainPod types * Break out utility function for generating common circuit data * Review feedback fixes
This commit is contained in:
parent
def0730462
commit
de9b206852
8 changed files with 381 additions and 115 deletions
|
|
@ -2,6 +2,7 @@ pub mod operation;
|
||||||
pub mod statement;
|
pub mod statement;
|
||||||
use std::{any::Any, sync::Arc};
|
use std::{any::Any, sync::Arc};
|
||||||
|
|
||||||
|
use base64::{prelude::BASE64_STANDARD, Engine};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
pub use operation::*;
|
pub use operation::*;
|
||||||
use plonky2::{
|
use plonky2::{
|
||||||
|
|
@ -9,10 +10,11 @@ use plonky2::{
|
||||||
iop::witness::PartialWitness,
|
iop::witness::PartialWitness,
|
||||||
plonk::{
|
plonk::{
|
||||||
circuit_builder::CircuitBuilder,
|
circuit_builder::CircuitBuilder,
|
||||||
circuit_data::CircuitConfig,
|
circuit_data::{CircuitConfig, CommonCircuitData},
|
||||||
config::Hasher,
|
config::Hasher,
|
||||||
proof::{Proof, ProofWithPublicInputs},
|
proof::{Proof, ProofWithPublicInputs},
|
||||||
},
|
},
|
||||||
|
util::serialization::{Buffer, Read},
|
||||||
};
|
};
|
||||||
pub use statement::*;
|
pub use statement::*;
|
||||||
|
|
||||||
|
|
@ -219,7 +221,7 @@ fn fill_pad<T: Clone>(v: &mut Vec<T>, pad_value: T, len: usize) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pad_statement(params: &Params, s: &mut Statement) {
|
pub fn pad_statement(params: &Params, s: &mut Statement) {
|
||||||
fill_pad(&mut s.1, StatementArg::None, params.max_statement_args)
|
fill_pad(&mut s.1, StatementArg::None, params.max_statement_args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -454,12 +456,14 @@ impl PodProver for Prover {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type MainPodProof = Proof<F, C, D>;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct MainPod {
|
pub struct MainPod {
|
||||||
params: Params,
|
params: Params,
|
||||||
id: PodId,
|
id: PodId,
|
||||||
public_statements: Vec<Statement>,
|
public_statements: Vec<Statement>,
|
||||||
proof: Proof<F, C, D>,
|
proof: MainPodProof,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a Statement into middleware::Statement and replace references to SELF by `self_id`.
|
/// Convert a Statement into middleware::Statement and replace references to SELF by `self_id`.
|
||||||
|
|
@ -481,6 +485,23 @@ pub(crate) fn normalize_statement(statement: &Statement, self_id: PodId) -> midd
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is a helper function to get the CommonCircuitData necessary to decode
|
||||||
|
// a serialized proof. At some point in the future, this data may be available
|
||||||
|
// as a constant or with static initialization, but in the meantime we can
|
||||||
|
// generate it on-demand.
|
||||||
|
fn get_common_data(params: &Params) -> Result<CommonCircuitData<F, D>, Error> {
|
||||||
|
let config = CircuitConfig::standard_recursion_config();
|
||||||
|
let mut builder = CircuitBuilder::<F, D>::new(config);
|
||||||
|
let _main_pod = MainPodVerifyCircuit {
|
||||||
|
params: params.clone(),
|
||||||
|
}
|
||||||
|
.eval(&mut builder)
|
||||||
|
.map_err(|e| Error::custom(format!("Failed to evaluate MainPodVerifyCircuit: {}", e)))?;
|
||||||
|
|
||||||
|
let data = builder.build::<C>();
|
||||||
|
Ok(data.common)
|
||||||
|
}
|
||||||
|
|
||||||
impl MainPod {
|
impl MainPod {
|
||||||
fn _verify(&self) -> Result<()> {
|
fn _verify(&self) -> Result<()> {
|
||||||
// 2. get the id out of the public statements
|
// 2. get the id out of the public statements
|
||||||
|
|
@ -505,6 +526,48 @@ impl MainPod {
|
||||||
})
|
})
|
||||||
.map_err(|e| Error::custom(format!("MainPod proof verification failure: {:?}", e)))
|
.map_err(|e| Error::custom(format!("MainPod proof verification failure: {:?}", e)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn proof(&self) -> MainPodProof {
|
||||||
|
self.proof.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn params(&self) -> &Params {
|
||||||
|
&self.params
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn new(
|
||||||
|
proof: MainPodProof,
|
||||||
|
public_statements: Vec<Statement>,
|
||||||
|
id: PodId,
|
||||||
|
params: Params,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
params,
|
||||||
|
id,
|
||||||
|
public_statements,
|
||||||
|
proof,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode_proof(proof: &str, params: &Params) -> Result<MainPodProof, Error> {
|
||||||
|
let decoded = BASE64_STANDARD.decode(proof).map_err(|e| {
|
||||||
|
Error::custom(format!(
|
||||||
|
"Failed to decode proof from base64: {}. Value: {}",
|
||||||
|
e, proof
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
let mut buf = Buffer::new(&decoded);
|
||||||
|
let common = get_common_data(params)?;
|
||||||
|
|
||||||
|
let proof = buf.read_proof(&common).map_err(|e| {
|
||||||
|
Error::custom(format!(
|
||||||
|
"Failed to read proof from buffer: {}. Value: {}",
|
||||||
|
e, proof
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(proof)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pod for MainPod {
|
impl Pod for MainPod {
|
||||||
|
|
@ -526,7 +589,10 @@ impl Pod for MainPod {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialized_proof(&self) -> String {
|
fn serialized_proof(&self) -> String {
|
||||||
todo!()
|
let mut buffer = Vec::new();
|
||||||
|
use plonky2::util::serialization::Write;
|
||||||
|
buffer.write_proof(&self.proof).unwrap();
|
||||||
|
BASE64_STANDARD.encode(buffer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -271,6 +271,10 @@ impl MockMainPod {
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn params(&self) -> &Params {
|
||||||
|
&self.params
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pod for MockMainPod {
|
impl Pod for MockMainPod {
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,10 @@ impl MockSignedPod {
|
||||||
pub(crate) fn new(id: PodId, signature: String, kvs: HashMap<Key, Value>) -> Self {
|
pub(crate) fn new(id: PodId, signature: String, kvs: HashMap<Key, Value>) -> Self {
|
||||||
Self { id, signature, kvs }
|
Self { id, signature, kvs }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn signature(&self) -> String {
|
||||||
|
self.signature.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MockSignedPod {
|
impl MockSignedPod {
|
||||||
|
|
@ -131,7 +135,7 @@ impl Pod for MockSignedPod {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialized_proof(&self) -> String {
|
fn serialized_proof(&self) -> String {
|
||||||
self.signature.to_string()
|
serde_json::to_string(&self.signature).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ use plonky2::{
|
||||||
|
|
||||||
pub mod circuit;
|
pub mod circuit;
|
||||||
pub use circuit::*;
|
pub use circuit::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backends::plonky2::{
|
backends::plonky2::{
|
||||||
|
|
@ -57,7 +58,8 @@ pub struct SecretKey(pub(crate) RawValue);
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PublicKey(pub RawValue);
|
pub struct PublicKey(pub RawValue);
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(transparent)]
|
||||||
pub struct Signature(pub(crate) Proof);
|
pub struct Signature(pub(crate) Proof);
|
||||||
|
|
||||||
/// Implements the key generation and the computation of proof-based signatures.
|
/// Implements the key generation and the computation of proof-based signatures.
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,15 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use base64::{prelude::BASE64_STANDARD, Engine};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use plonky2::util::serialization::Buffer;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
backends::plonky2::{
|
backends::plonky2::{
|
||||||
error::{Error, Result},
|
error::{Error, Result},
|
||||||
primitives::{
|
primitives::{
|
||||||
merkletree::MerkleTree,
|
merkletree::MerkleTree,
|
||||||
signature::{PublicKey, SecretKey, Signature},
|
signature::{PublicKey, SecretKey, Signature, VP},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
constants::MAX_DEPTH,
|
constants::MAX_DEPTH,
|
||||||
|
|
@ -92,6 +94,29 @@ impl SignedPod {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn decode_signature(signature: &str) -> Result<Signature, Error> {
|
||||||
|
use plonky2::util::serialization::Read;
|
||||||
|
|
||||||
|
let decoded = BASE64_STANDARD.decode(signature).map_err(|e| {
|
||||||
|
Error::custom(format!(
|
||||||
|
"Failed to decode signature from base64: {}. Value: {}",
|
||||||
|
e, signature
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
let mut buf = Buffer::new(&decoded);
|
||||||
|
|
||||||
|
let proof = buf.read_proof(&VP.0.common).map_err(|e| {
|
||||||
|
Error::custom(format!(
|
||||||
|
"Failed to read signature from buffer: {}. Value: {}",
|
||||||
|
e, signature
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let sig = Signature(proof);
|
||||||
|
|
||||||
|
Ok(sig)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pod for SignedPod {
|
impl Pod for SignedPod {
|
||||||
|
|
@ -122,7 +147,7 @@ impl Pod for SignedPod {
|
||||||
let mut buffer = Vec::new();
|
let mut buffer = Vec::new();
|
||||||
use plonky2::util::serialization::Write;
|
use plonky2::util::serialization::Write;
|
||||||
buffer.write_proof(&self.signature.0).unwrap();
|
buffer.write_proof(&self.signature.0).unwrap();
|
||||||
hex::encode(buffer)
|
BASE64_STANDARD.encode(buffer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ use std::{collections::HashMap, convert::From, fmt};
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serialization::{SerializedMainPod, SerializedSignedPod};
|
||||||
|
|
||||||
use crate::middleware::{
|
use crate::middleware::{
|
||||||
self, check_st_tmpl, hash_str, hash_values, AnchoredKey, Hash, Key, MainPodInputs,
|
self, check_st_tmpl, hash_str, hash_values, AnchoredKey, Hash, Key, MainPodInputs,
|
||||||
|
|
@ -19,15 +20,6 @@ mod serialization;
|
||||||
pub use custom::*;
|
pub use custom::*;
|
||||||
pub use error::*;
|
pub use error::*;
|
||||||
pub use operation::*;
|
pub use operation::*;
|
||||||
use serialization::*;
|
|
||||||
|
|
||||||
/// This type is just for presentation purposes.
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
|
||||||
pub enum PodClass {
|
|
||||||
#[default]
|
|
||||||
Signed,
|
|
||||||
Main,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SignedPodBuilder {
|
pub struct SignedPodBuilder {
|
||||||
|
|
@ -68,7 +60,7 @@ impl SignedPodBuilder {
|
||||||
/// SignedPod is a wrapper on top of backend::SignedPod, which additionally stores the
|
/// SignedPod is a wrapper on top of backend::SignedPod, which additionally stores the
|
||||||
/// string<-->hash relation of the keys.
|
/// string<-->hash relation of the keys.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[serde(try_from = "SignedPodHelper", into = "SignedPodHelper")]
|
#[serde(from = "SerializedSignedPod", into = "SerializedSignedPod")]
|
||||||
pub struct SignedPod {
|
pub struct SignedPod {
|
||||||
pub pod: Box<dyn middleware::Pod>,
|
pub pod: Box<dyn middleware::Pod>,
|
||||||
// We store a copy of the key values for quick access
|
// We store a copy of the key values for quick access
|
||||||
|
|
@ -617,16 +609,18 @@ impl MainPodBuilder {
|
||||||
|
|
||||||
Ok(MainPod {
|
Ok(MainPod {
|
||||||
pod,
|
pod,
|
||||||
|
params: self.params.clone(),
|
||||||
public_statements,
|
public_statements,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[serde(try_from = "MainPodHelper", into = "MainPodHelper")]
|
#[serde(try_from = "SerializedMainPod", into = "SerializedMainPod")]
|
||||||
pub struct MainPod {
|
pub struct MainPod {
|
||||||
pub pod: Box<dyn middleware::Pod>,
|
pub pod: Box<dyn middleware::Pod>,
|
||||||
pub public_statements: Vec<Statement>,
|
pub public_statements: Vec<Statement>,
|
||||||
|
pub params: Params,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for MainPod {
|
impl fmt::Display for MainPod {
|
||||||
|
|
|
||||||
|
|
@ -1,109 +1,216 @@
|
||||||
use std::collections::HashMap;
|
use std::{any::Any, collections::HashMap};
|
||||||
|
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::Error;
|
||||||
use crate::{
|
use crate::{
|
||||||
backends::plonky2::mock::{mainpod::MockMainPod, signedpod::MockSignedPod},
|
backends::plonky2::{
|
||||||
frontend::{Error, MainPod, SignedPod, Statement},
|
mainpod::{pad_statement, MainPod as Plonky2MainPod, Statement as BackendStatement},
|
||||||
middleware::{containers::Dictionary, Key, PodId, Value},
|
mock::{mainpod::MockMainPod, signedpod::MockSignedPod},
|
||||||
|
signedpod::SignedPod as Plonky2SignedPod,
|
||||||
|
},
|
||||||
|
frontend::{MainPod, SignedPod},
|
||||||
|
middleware::{
|
||||||
|
self, containers::Dictionary, serialization::ordered_map, AnchoredKey, Key, Params, PodId,
|
||||||
|
Statement, StatementArg, Value, SELF,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, JsonSchema)]
|
#[derive(Serialize, Deserialize, JsonSchema)]
|
||||||
#[serde(rename_all = "camelCase")]
|
pub enum SignedPodType {
|
||||||
#[schemars(title = "SignedPod")]
|
Signed,
|
||||||
pub struct SignedPodHelper {
|
MockSigned,
|
||||||
entries: HashMap<Key, Value>,
|
|
||||||
proof: String,
|
|
||||||
pod_class: String,
|
|
||||||
pod_type: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TryFrom<SignedPodHelper> for SignedPod {
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
fn try_from(helper: SignedPodHelper) -> Result<SignedPod, Self::Error> {
|
|
||||||
if helper.pod_class != "Signed" {
|
|
||||||
return Err(Error::custom("pod_class is not Signed"));
|
|
||||||
}
|
|
||||||
if helper.pod_type != "Mock" {
|
|
||||||
return Err(Error::custom("pod_type is not Mock"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let dict = Dictionary::new(helper.entries.clone())?.clone();
|
|
||||||
let pod = MockSignedPod::new(PodId(dict.commitment()), helper.proof, dict.kvs().clone());
|
|
||||||
|
|
||||||
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)]
|
#[derive(Serialize, Deserialize, JsonSchema)]
|
||||||
#[schemars(title = "MainPod")]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct MainPodHelper {
|
#[schemars(rename = "SignedPod")]
|
||||||
|
pub struct SerializedSignedPod {
|
||||||
|
id: PodId,
|
||||||
|
#[serde(serialize_with = "ordered_map")]
|
||||||
|
entries: HashMap<Key, Value>,
|
||||||
|
proof: String,
|
||||||
|
pod_type: SignedPodType,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema)]
|
||||||
|
pub enum MainPodType {
|
||||||
|
Main,
|
||||||
|
MockMain,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, JsonSchema)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
#[schemars(rename = "MainPod")]
|
||||||
|
pub struct SerializedMainPod {
|
||||||
|
id: PodId,
|
||||||
public_statements: Vec<Statement>,
|
public_statements: Vec<Statement>,
|
||||||
proof: String,
|
proof: String,
|
||||||
pod_class: String,
|
params: Params,
|
||||||
pod_type: String,
|
pod_type: MainPodType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<MainPodHelper> for MainPod {
|
impl From<SignedPod> for SerializedSignedPod {
|
||||||
type Error = Error; // or you can create a custom error type
|
fn from(pod: SignedPod) -> Self {
|
||||||
|
SerializedSignedPod {
|
||||||
fn try_from(helper: MainPodHelper) -> Result<Self, Self::Error> {
|
id: pod.id(),
|
||||||
if helper.pod_class != "Main" {
|
entries: pod.kvs,
|
||||||
return Err(Error::custom("pod_class is not Main"));
|
|
||||||
}
|
|
||||||
if helper.pod_type != "Mock" {
|
|
||||||
return Err(Error::custom("pod_type is not Mock"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let pod = MockMainPod::deserialize(helper.proof)
|
|
||||||
.map_err(|e| Error::custom(format!("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(),
|
proof: pod.pod.serialized_proof(),
|
||||||
pod_class: "Main".to_string(),
|
pod_type: if (&*pod.pod as &dyn Any)
|
||||||
pod_type: "Mock".to_string(),
|
.downcast_ref::<Plonky2SignedPod>()
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
SignedPodType::Signed
|
||||||
|
} else if (&*pod.pod as &dyn Any)
|
||||||
|
.downcast_ref::<MockSignedPod>()
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
SignedPodType::MockSigned
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<SerializedSignedPod> for SignedPod {
|
||||||
|
fn from(serialized: SerializedSignedPod) -> Self {
|
||||||
|
match serialized.pod_type {
|
||||||
|
SignedPodType::Signed => SignedPod {
|
||||||
|
pod: Box::new(Plonky2SignedPod {
|
||||||
|
id: serialized.id,
|
||||||
|
signature: Plonky2SignedPod::decode_signature(&serialized.proof).unwrap(),
|
||||||
|
dict: Dictionary::new(serialized.entries.clone()).unwrap(),
|
||||||
|
}),
|
||||||
|
kvs: serialized.entries,
|
||||||
|
},
|
||||||
|
SignedPodType::MockSigned => SignedPod {
|
||||||
|
pod: Box::new(MockSignedPod::new(
|
||||||
|
serialized.id,
|
||||||
|
serde_json::from_str(&serialized.proof).unwrap(),
|
||||||
|
serialized.entries.clone(),
|
||||||
|
)),
|
||||||
|
kvs: serialized.entries,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<MainPod> for SerializedMainPod {
|
||||||
|
fn from(pod: MainPod) -> Self {
|
||||||
|
SerializedMainPod {
|
||||||
|
id: pod.id(),
|
||||||
|
proof: pod.pod.serialized_proof(),
|
||||||
|
params: pod.params.clone(),
|
||||||
|
pod_type: if (&*pod.pod as &dyn Any)
|
||||||
|
.downcast_ref::<Plonky2MainPod>()
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
MainPodType::Main
|
||||||
|
} else if (&*pod.pod as &dyn Any)
|
||||||
|
.downcast_ref::<MockMainPod>()
|
||||||
|
.is_some()
|
||||||
|
{
|
||||||
|
MainPodType::MockMain
|
||||||
|
} else {
|
||||||
|
unreachable!()
|
||||||
|
},
|
||||||
|
public_statements: pod.public_statements.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<SerializedMainPod> for MainPod {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(serialized: SerializedMainPod) -> Result<Self, Self::Error> {
|
||||||
|
match serialized.pod_type {
|
||||||
|
MainPodType::Main => Ok(MainPod {
|
||||||
|
pod: Box::new(Plonky2MainPod::new(
|
||||||
|
Plonky2MainPod::decode_proof(&serialized.proof, &serialized.params).map_err(
|
||||||
|
|e| {
|
||||||
|
Error::custom(format!(
|
||||||
|
"Failed to deserialize MainPod proof: {}. Value: {}",
|
||||||
|
e, serialized.proof
|
||||||
|
))
|
||||||
|
},
|
||||||
|
)?,
|
||||||
|
middleware_statements_to_backend(
|
||||||
|
serialized.public_statements.clone(),
|
||||||
|
&serialized.params,
|
||||||
|
serialized.id,
|
||||||
|
),
|
||||||
|
serialized.id,
|
||||||
|
serialized.params.clone(),
|
||||||
|
)),
|
||||||
|
public_statements: serialized.public_statements,
|
||||||
|
params: serialized.params,
|
||||||
|
}),
|
||||||
|
MainPodType::MockMain => Ok(MainPod {
|
||||||
|
pod: Box::new(
|
||||||
|
MockMainPod::deserialize(serialized.proof.clone()).map_err(|e| {
|
||||||
|
Error::custom(format!(
|
||||||
|
"Failed to deserialize MockMainPod: {}. Value: {}",
|
||||||
|
e, serialized.proof
|
||||||
|
))
|
||||||
|
})?,
|
||||||
|
),
|
||||||
|
public_statements: serialized.public_statements,
|
||||||
|
params: serialized.params,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// To deserialize a backend MainPod, we need to convert the middleware
|
||||||
|
// statements to backend statements, and padding the list with None statements.
|
||||||
|
fn middleware_statements_to_backend(
|
||||||
|
mid_statements: Vec<Statement>,
|
||||||
|
params: &Params,
|
||||||
|
id: PodId,
|
||||||
|
) -> Vec<BackendStatement> {
|
||||||
|
let mut statements = Vec::new();
|
||||||
|
for i in 0..(params.max_public_statements) {
|
||||||
|
let mut st: BackendStatement = mid_statements
|
||||||
|
.get(i)
|
||||||
|
.unwrap_or(&middleware::Statement::None)
|
||||||
|
.clone()
|
||||||
|
.into();
|
||||||
|
|
||||||
|
st = BackendStatement(
|
||||||
|
st.0.clone(),
|
||||||
|
st.1.iter()
|
||||||
|
.map(|sa| match &sa {
|
||||||
|
StatementArg::Key(AnchoredKey { pod_id, key }) if *pod_id == id => {
|
||||||
|
StatementArg::Key(AnchoredKey::new(SELF, key.clone()))
|
||||||
|
}
|
||||||
|
_ => sa.clone(),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
pad_statement(params, &mut st);
|
||||||
|
statements.push(st);
|
||||||
|
}
|
||||||
|
|
||||||
|
statements
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::collections::HashSet;
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
// Pretty assertions give nicer diffs between expected and actual values
|
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use schemars::schema_for;
|
use schemars::schema_for;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
backends::plonky2::mock::{mainpod::MockProver, signedpod::MockSigner},
|
backends::plonky2::{
|
||||||
|
mainpod::Prover,
|
||||||
|
mock::{mainpod::MockProver, signedpod::MockSigner},
|
||||||
|
primitives::signature::SecretKey,
|
||||||
|
signedpod::Signer,
|
||||||
|
},
|
||||||
examples::{
|
examples::{
|
||||||
eth_dos_pod_builder, eth_friend_signed_pod_builder, zu_kyc_pod_builder,
|
eth_dos_pod_builder, eth_friend_signed_pod_builder, zu_kyc_pod_builder,
|
||||||
zu_kyc_sign_pod_builders,
|
zu_kyc_sign_pod_builders,
|
||||||
|
|
@ -112,7 +219,7 @@ mod tests {
|
||||||
middleware::{
|
middleware::{
|
||||||
self,
|
self,
|
||||||
containers::{Array, Set},
|
containers::{Array, Set},
|
||||||
Params, TypedValue,
|
Params, RawValue, TypedValue,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -167,8 +274,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_signed_pod() -> Result<SignedPod> {
|
fn signed_pod_builder() -> SignedPodBuilder {
|
||||||
let mut signer = MockSigner { pk: "test".into() };
|
|
||||||
let mut builder = SignedPodBuilder::new(&Params::default());
|
let mut builder = SignedPodBuilder::new(&Params::default());
|
||||||
builder.insert("name", "test");
|
builder.insert("name", "test");
|
||||||
builder.insert("age", 30);
|
builder.insert("age", 30);
|
||||||
|
|
@ -195,25 +301,46 @@ mod tests {
|
||||||
]))
|
]))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
|
builder
|
||||||
let pod = builder.sign(&mut signer).unwrap();
|
|
||||||
Ok(pod)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_signed_pod_serialization() {
|
fn test_signed_pod_serialization() {
|
||||||
let pod = build_signed_pod().unwrap();
|
let builder = signed_pod_builder();
|
||||||
|
let mut signer = Signer(SecretKey(RawValue::from(1)));
|
||||||
|
let pod = builder.sign(&mut signer).unwrap();
|
||||||
|
|
||||||
let serialized = serde_json::to_string_pretty(&pod).unwrap();
|
let serialized = serde_json::to_string_pretty(&pod).unwrap();
|
||||||
println!("serialized: {}", serialized);
|
println!("serialized: {}", serialized);
|
||||||
let deserialized: SignedPod = serde_json::from_str(&serialized).unwrap();
|
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.kvs, deserialized.kvs);
|
||||||
assert_eq!(pod.verify().is_ok(), deserialized.verify().is_ok());
|
assert_eq!(pod.verify().is_ok(), deserialized.verify().is_ok());
|
||||||
assert_eq!(pod.id(), deserialized.id())
|
assert_eq!(pod.id(), deserialized.id())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_zukyc_pod() -> Result<MainPod> {
|
#[test]
|
||||||
|
fn test_mock_signed_pod_serialization() {
|
||||||
|
let builder = signed_pod_builder();
|
||||||
|
let mut signer = MockSigner { pk: "test".into() };
|
||||||
|
let pod = builder.sign(&mut 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())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_mock_zukyc_pod() -> Result<MainPod> {
|
||||||
let params = middleware::Params::default();
|
let params = middleware::Params::default();
|
||||||
|
|
||||||
let (gov_id_builder, pay_stub_builder, sanction_list_builder) =
|
let (gov_id_builder, pay_stub_builder, sanction_list_builder) =
|
||||||
|
|
@ -238,9 +365,34 @@ mod tests {
|
||||||
Ok(kyc_pod)
|
Ok(kyc_pod)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_plonky2_zukyc_pod() -> Result<MainPod> {
|
||||||
|
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_main_pods: 1,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let (gov_id_builder, pay_stub_builder, sanction_list_builder) =
|
||||||
|
zu_kyc_sign_pod_builders(¶ms);
|
||||||
|
let mut signer = Signer(SecretKey(RawValue::from(1)));
|
||||||
|
let gov_id_pod = gov_id_builder.sign(&mut signer)?;
|
||||||
|
let mut signer = Signer(SecretKey(RawValue::from(2)));
|
||||||
|
let pay_stub_pod = pay_stub_builder.sign(&mut signer)?;
|
||||||
|
let mut signer = Signer(SecretKey(RawValue::from(3)));
|
||||||
|
let sanction_list_pod = sanction_list_builder.sign(&mut signer)?;
|
||||||
|
let kyc_builder =
|
||||||
|
zu_kyc_pod_builder(¶ms, &gov_id_pod, &pay_stub_pod, &sanction_list_pod)?;
|
||||||
|
|
||||||
|
let mut prover = Prover {};
|
||||||
|
let kyc_pod = kyc_builder.prove(&mut prover, ¶ms)?;
|
||||||
|
|
||||||
|
Ok(kyc_pod)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_main_pod_serialization() -> Result<()> {
|
fn test_mock_main_pod_serialization() -> Result<()> {
|
||||||
let kyc_pod = build_zukyc_pod()?;
|
let kyc_pod = build_mock_zukyc_pod()?;
|
||||||
let serialized = serde_json::to_string_pretty(&kyc_pod).unwrap();
|
let serialized = serde_json::to_string_pretty(&kyc_pod).unwrap();
|
||||||
println!("serialized: {}", serialized);
|
println!("serialized: {}", serialized);
|
||||||
let deserialized: MainPod = serde_json::from_str(&serialized).unwrap();
|
let deserialized: MainPod = serde_json::from_str(&serialized).unwrap();
|
||||||
|
|
@ -252,6 +404,19 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_plonky2_main_pod_serialization() -> Result<()> {
|
||||||
|
let kyc_pod = build_plonky2_zukyc_pod()?;
|
||||||
|
let serialized = serde_json::to_string_pretty(&kyc_pod).unwrap();
|
||||||
|
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()?);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn build_ethdos_pod() -> Result<MainPod> {
|
fn build_ethdos_pod() -> Result<MainPod> {
|
||||||
let params = Params {
|
let params = Params {
|
||||||
max_input_signed_pods: 3,
|
max_input_signed_pods: 3,
|
||||||
|
|
@ -295,14 +460,17 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
// This tests that we can generate JSON Schemas for the MainPod and
|
// This tests that we can generate JSON Schemas for the MainPod and
|
||||||
// SignedPod types, and that we can validate real Signed and Main Pods
|
// SignedPod types, and that we can validate Signed and Main Pods
|
||||||
// against the schemas.
|
// 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() {
|
fn test_schema() {
|
||||||
let mainpod_schema = schema_for!(MainPodHelper);
|
let mainpod_schema = schema_for!(SerializedMainPod);
|
||||||
let signedpod_schema = schema_for!(SignedPodHelper);
|
let signedpod_schema = schema_for!(SerializedSignedPod);
|
||||||
|
|
||||||
let kyc_pod = build_zukyc_pod().unwrap();
|
let kyc_pod = build_mock_zukyc_pod().unwrap();
|
||||||
let signed_pod = build_signed_pod().unwrap();
|
let signed_pod = signed_pod_builder()
|
||||||
|
.sign(&mut MockSigner { pk: "test".into() })
|
||||||
|
.unwrap();
|
||||||
let ethdos_pod = build_ethdos_pod().unwrap();
|
let ethdos_pod = build_ethdos_pod().unwrap();
|
||||||
let mainpod_schema_value = serde_json::to_value(&mainpod_schema).unwrap();
|
let mainpod_schema_value = serde_json::to_value(&mainpod_schema).unwrap();
|
||||||
let signedpod_schema_value = serde_json::to_value(&signedpod_schema).unwrap();
|
let signedpod_schema_value = serde_json::to_value(&signedpod_schema).unwrap();
|
||||||
|
|
|
||||||
|
|
@ -553,7 +553,7 @@ impl ToFields for PodId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||||
pub enum PodType {
|
pub enum PodType {
|
||||||
None = 0,
|
None = 0,
|
||||||
MockSigned = 1,
|
MockSigned = 1,
|
||||||
|
|
@ -574,7 +574,7 @@ impl fmt::Display for PodType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Params {
|
pub struct Params {
|
||||||
pub max_input_signed_pods: usize,
|
pub max_input_signed_pods: usize,
|
||||||
|
|
@ -690,6 +690,7 @@ pub trait Pod: fmt::Debug + DynClone + Any {
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Front-end Pods keep references to middleware Pods. Most of the
|
// Front-end Pods keep references to middleware Pods. Most of the
|
||||||
// middleware data can be derived directly from front-end data, but the
|
// middleware data can be derived directly from front-end data, but the
|
||||||
// "proof" data is only created at the point of proving/signing, and
|
// "proof" data is only created at the point of proving/signing, and
|
||||||
|
|
@ -698,6 +699,8 @@ pub trait Pod: fmt::Debug + DynClone + Any {
|
||||||
// the implementation details of the middleware, this method allows the
|
// the implementation details of the middleware, this method allows the
|
||||||
// middleware to provide some serialized data that can be used to
|
// middleware to provide some serialized data that can be used to
|
||||||
// reconstruct the proof.
|
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue