diff --git a/plonk/Cargo.toml b/plonk/Cargo.toml index 365e2a585..55bd423ee 100644 --- a/plonk/Cargo.toml +++ b/plonk/Cargo.toml @@ -49,6 +49,7 @@ ark-ed-on-bn254 = "0.4.0" criterion = { version = "0.5", features = ["async", "async_tokio"] } hex = "^0.4.3" lazy_static = "1.4" +serde_json = "1.0" tokio = "1.33" # Benchmarks diff --git a/plonk/src/proof_system/proof_linking.rs b/plonk/src/proof_system/proof_linking.rs index 3fb7aaa65..1328723b3 100644 --- a/plonk/src/proof_system/proof_linking.rs +++ b/plonk/src/proof_system/proof_linking.rs @@ -8,6 +8,7 @@ use ark_ec::{ }; use ark_ff::{Field, One, Zero}; use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use jf_primitives::{ pcs::{ prelude::{Commitment, UnivariateKzgPCS, UnivariateKzgProof}, @@ -19,6 +20,7 @@ use mpc_relation::{ gadgets::ecc::SWToTEConParam, proof_linking::{GroupLayout, PROOF_LINK_WIRE_IDX}, }; +use serde::{ser::SerializeSeq, Deserialize, Serialize}; use crate::{errors::PlonkError, transcript::PlonkTranscript}; @@ -28,7 +30,7 @@ use super::{ }; /// A proof that two circuits are linked on a given domain -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize)] pub struct LinkingProof { /// The commitment to the linking quotient polynomial pub quotient_commitment: Commitment, @@ -36,6 +38,34 @@ pub struct LinkingProof { pub opening_proof: UnivariateKzgProof, } +impl Serialize for LinkingProof { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut bytes = Vec::new(); + self.serialize_compressed(&mut bytes).map_err(serde::ser::Error::custom)?; + + // Serialize explicitly as a sequence to avoid issues with certain serde + // formats, e.g. flexbuffers + let mut seq = serializer.serialize_seq(Some(bytes.len()))?; + for byte in bytes.iter() { + seq.serialize_element(byte)?; + } + seq.end() + } +} + +impl<'de, E: Pairing> Deserialize<'de> for LinkingProof { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let bytes = >::deserialize(deserializer)?; + Self::deserialize_compressed(bytes.as_slice()).map_err(serde::de::Error::custom) + } +} + // ---------- // | Prover | // ---------- @@ -407,8 +437,10 @@ pub mod test_helpers { #[cfg(test)] mod test { use ark_bn254::{Bn254, Fr as FrBn254}; + use ark_ec::pairing::Pairing; use ark_std::UniformRand; use itertools::Itertools; + use jf_primitives::pcs::prelude::UnivariateKzgProof; use mpc_relation::{ proof_linking::{GroupLayout, LinkableCircuit}, PlonkCircuit, @@ -424,9 +456,12 @@ mod test { transcript::SolidityTranscript, }; - use super::test_helpers::{ - gen_commit_keys, gen_proving_keys, gen_test_circuit1, gen_test_circuit2, CircuitSelector, - GROUP_NAME, + use super::{ + test_helpers::{ + gen_commit_keys, gen_proving_keys, gen_test_circuit1, gen_test_circuit2, + CircuitSelector, GROUP_NAME, + }, + LinkingProof, }; // ----------- @@ -493,6 +528,22 @@ mod test { // | Test Cases | // -------------- + /// Tests serialization and deserialization of a linking proof + #[test] + fn test_serde() { + let mut rng = thread_rng(); + let commitment = ::G1Affine::rand(&mut rng).into(); + let opening = UnivariateKzgProof { proof: ::G1Affine::rand(&mut rng) }; + + let proof = + LinkingProof:: { quotient_commitment: commitment, opening_proof: opening }; + + let bytes = serde_json::to_vec(&proof).unwrap(); + let recovered = serde_json::from_slice(&bytes).unwrap(); + + assert_eq!(proof, recovered); + } + /// Tests a linking proof between two circuits that correctly use the same /// values in the linking domain #[test] diff --git a/plonk/src/proof_system/structs.rs b/plonk/src/proof_system/structs.rs index 13153b1ff..11b62fb68 100644 --- a/plonk/src/proof_system/structs.rs +++ b/plonk/src/proof_system/structs.rs @@ -45,6 +45,7 @@ use mpc_relation::{ }, PlonkCircuit, }; +use serde::{ser::SerializeSeq, Deserialize, Serialize}; use tagged_base64::tagged; /// Universal StructuredReferenceString @@ -87,7 +88,7 @@ pub struct Proof { /// /// The linking hint contains information about the prover's witness needed to /// link the proof to another proof of a different circuit -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)] pub struct LinkingHint { /// The wire polynomial that encodes the proof-linking gates for the circuit pub linking_wire_poly: DensePolynomial, @@ -95,6 +96,34 @@ pub struct LinkingHint { pub linking_wire_comm: Commitment, } +impl Serialize for LinkingHint { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut bytes = Vec::new(); + self.serialize_compressed(&mut bytes).map_err(serde::ser::Error::custom)?; + + // Serialize explicitly as a sequence to avoid issues with certain serde + // formats, e.g. flexbuffers + let mut seq = serializer.serialize_seq(Some(bytes.len()))?; + for byte in bytes.iter() { + seq.serialize_element(byte)?; + } + seq.end() + } +} + +impl<'de, E: Pairing> Deserialize<'de> for LinkingHint { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let bytes = >::deserialize(deserializer)?; + Self::deserialize_compressed(bytes.as_slice()).map_err(serde::de::Error::custom) + } +} + impl TryFrom> for Proof where E: Pairing>, @@ -931,6 +960,29 @@ mod test { use super::*; use ark_bn254::{g1::Config, Bn254, Fq}; use ark_ec::AffineRepr; + use ark_poly::DenseUVPolynomial; + use ark_std::UniformRand; + use itertools::Itertools; + use rand::thread_rng; + + /// Test `LinkingHint` serde + #[test] + fn test_link_hint_serde() { + const N_COEFFS: usize = 10; + let mut rng = thread_rng(); + + let coeffs = + (0..N_COEFFS).map(|_| ::ScalarField::rand(&mut rng)).collect_vec(); + let poly = DensePolynomial::from_coefficients_vec(coeffs); + let comm = Commitment(::G1Affine::rand(&mut rng)); + + let hint = LinkingHint:: { linking_wire_poly: poly, linking_wire_comm: comm }; + + let bytes = serde_json::to_vec(&hint).unwrap(); + let recovered = serde_json::from_slice(&bytes).unwrap(); + + assert_eq!(hint, recovered); + } #[test] fn test_group_to_field() {