feat/hypernova serialization (#159) * feat: implement serialization of hypernova prover and verifier params, proof and public inputs * chore: remove leftover comments * chore: remove timers * chore: improve typing and import naming
@@ -1,6 +1,8 @@
use ark_crypto_primitives::sponge::Absorb; use ark_ec::CurveGroup; use ark_ff::PrimeField; +use ark_serialize::CanonicalDeserialize; +use ark_serialize::CanonicalSerialize; use ark_std::One; use ark_std::Zero; use std::sync::Arc;@@ -17,7 +19,7 @@ use crate::utils::virtual_polynomial::{build_eq_x_r_vec, VirtualPolynomial};
use crate::Error; /// Committed CCS instance -#[derive(Debug, Clone)] +#[derive(Debug, Clone, CanonicalSerialize, CanonicalDeserialize)] pub struct CCCS<C: CurveGroup> { // Commitment to witness pub C: C,
@@ -3,6 +3,7 @@ use ark_crypto_primitives::sponge::Absorb;
use ark_ec::{CurveGroup, Group}; use ark_ff::PrimeField; use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldGadget}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_snark::SNARK; use ark_std::rand::{CryptoRng, RngCore}; use ark_std::{One, Zero};@@ -14,11 +15,12 @@ use crate::commitment::{
kzg::Proof as KZGProof, pedersen::Params as PedersenParams, CommitmentScheme, }; use crate::folding::circuits::{nonnative::affine::NonNativeAffineVar, CF2}; +use crate::folding::nova::decider_eth::VerifierParam; use crate::frontend::FCircuit; use crate::Error; use crate::{Decider as DeciderTrait, FoldingScheme}; -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)] pub struct Proof<C1, CS1, S> where C1: CurveGroup,@@ -85,8 +87,7 @@ {
type PreprocessorParam = (FS::ProverParam, FS::VerifierParam); type ProverParam = (S::ProvingKey, CS1::ProverParams); type Proof = Proof<C1, CS1, S>; - /// VerifierParam = (pp_hash, snark::vk, commitment_scheme::vk) - type VerifierParam = (C1::ScalarField, S::VerifyingKey, CS1::VerifierParams); + type VerifierParam = VerifierParam<C1, CS1::VerifierParams, S::VerifyingKey>; type PublicInput = Vec<C1::ScalarField>; type CommittedInstance = ();@@ -120,7 +121,12 @@ >>::VerifierParam = prep_param.1.into();
let pp_hash = hypernova_vp.pp_hash()?; let pp = (g16_pk, hypernova_pp.cs_pp); - let vp = (pp_hash, g16_vk, hypernova_vp.cs_vp); + + let vp = VerifierParam { + pp_hash, + snark_vp: g16_vk, + cs_vp: hypernova_vp.cs_vp, + }; Ok((pp, vp)) }@@ -183,7 +189,7 @@ return Err(Error::NotEnoughSteps);
} let (pp_hash, snark_vk, cs_vk): (C1::ScalarField, S::VerifyingKey, CS1::VerifierParams) = - vp; + (vp.pp_hash, vp.snark_vp, vp.cs_vp); // Note: the NIMFS proof is checked inside the DeciderEthCircuit, which ensures that the // 'proof.U_i1' is correctly computed@@ -223,11 +229,15 @@ pub mod tests {
use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective}; use ark_groth16::Groth16; use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2}; - use std::time::Instant; + use ark_serialize::{Compress, Validate}; use super::*; use crate::commitment::{kzg::KZG, pedersen::Pedersen}; - use crate::folding::hypernova::PreprocessorParam; + use crate::folding::hypernova::cccs::CCCS; + use crate::folding::hypernova::{ + PreprocessorParam, ProverParams, VerifierParams as HyperNovaVerifierParams, + }; + use crate::folding::nova::decider_eth::VerifierParam; use crate::frontend::utils::CubicFCircuit; use crate::transcript::poseidon::poseidon_canonical_config;@@ -262,7 +272,7 @@ MU,
NU, >; - let mut rng = ark_std::test_rng(); + let mut rng = rand::rngs::OsRng; let poseidon_config = poseidon_canonical_config::<Fr>(); let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap();@@ -271,31 +281,22 @@
let prep_param = PreprocessorParam::new(poseidon_config, F_circuit); let hypernova_params = HN::preprocess(&mut rng, &prep_param).unwrap(); - let start = Instant::now(); let mut hypernova = HN::init(&hypernova_params, F_circuit, z_0.clone()).unwrap(); - println!("Nova initialized, {:?}", start.elapsed()); - let start = Instant::now(); hypernova .prove_step(&mut rng, vec![], Some((vec![], vec![]))) .unwrap(); - println!("prove_step, {:?}", start.elapsed()); hypernova .prove_step(&mut rng, vec![], Some((vec![], vec![]))) .unwrap(); // do a 2nd step - - let mut rng = rand::rngs::OsRng; // prepare the Decider prover & verifier params let (decider_pp, decider_vp) = D::preprocess(&mut rng, hypernova_params, hypernova.clone()).unwrap(); // decider proof generation - let start = Instant::now(); let proof = D::prove(rng, decider_pp, hypernova.clone()).unwrap(); - println!("Decider prove, {:?}", start.elapsed()); // decider proof verification - let start = Instant::now(); let verified = D::verify( decider_vp, hypernova.i,@@ -307,6 +308,189 @@ &proof,
) .unwrap(); assert!(verified); - println!("Decider verify, {:?}", start.elapsed()); + } + + #[test] + fn test_decider_serialization() { + const MU: usize = 1; + const NU: usize = 1; + // use HyperNova as FoldingScheme + type HN = HyperNova< + Projective, + GVar, + Projective2, + GVar2, + CubicFCircuit<Fr>, + KZG<'static, Bn254>, + Pedersen<Projective2>, + MU, + NU, + false, + >; + type D = Decider< + Projective, + GVar, + Projective2, + GVar2, + CubicFCircuit<Fr>, + KZG<'static, Bn254>, + Pedersen<Projective2>, + Groth16<Bn254>, // here we define the Snark to use in the decider + HN, // here we define the FoldingScheme to use + MU, + NU, + >; + + let mut rng = ark_std::test_rng(); + let poseidon_config = poseidon_canonical_config::<Fr>(); + + let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap(); + let z_0 = vec![Fr::from(3_u32)]; + + let prep_param = PreprocessorParam::new(poseidon_config.clone(), F_circuit); + let hypernova_params = HN::preprocess(&mut rng, &prep_param).unwrap(); + + let hypernova = HN::init(&hypernova_params, F_circuit, z_0.clone()).unwrap(); + + let mut rng = rand::rngs::OsRng; + + // prepare the Decider prover & verifier params + let (decider_pp, decider_vp) = + D::preprocess(&mut rng, hypernova_params.clone(), hypernova.clone()).unwrap(); + + let mut hypernova_pp_serialized = vec![]; + hypernova_params + .0 + .clone() + .serialize_compressed(&mut hypernova_pp_serialized) + .unwrap(); + let mut hypernova_vp_serialized = vec![]; + hypernova_params + .1 + .clone() + .serialize_compressed(&mut hypernova_vp_serialized) + .unwrap(); + + let hypernova_pp_deserialized = ProverParams::< + Projective, + Projective2, + KZG<'static, Bn254>, + Pedersen<Projective2>, + false, + >::deserialize_prover_params( + hypernova_pp_serialized.as_slice(), + Compress::Yes, + Validate::No, + &hypernova_params.0.ccs, + &poseidon_config, + ) + .unwrap(); + + let hypernova_vp_deserialized = HyperNovaVerifierParams::< + Projective, + Projective2, + KZG<'static, Bn254>, + Pedersen<Projective2>, + false, + >::deserialize_verifier_params( + hypernova_vp_serialized.as_slice(), + Compress::Yes, + Validate::No, + &hypernova_params.0.ccs.unwrap(), + &poseidon_config, + ) + .unwrap(); + + let hypernova_params = (hypernova_pp_deserialized, hypernova_vp_deserialized); + let mut hypernova = HN::init(&hypernova_params, F_circuit, z_0.clone()).unwrap(); + + hypernova + .prove_step(&mut rng, vec![], Some((vec![], vec![]))) + .unwrap(); + hypernova + .prove_step(&mut rng, vec![], Some((vec![], vec![]))) + .unwrap(); + + // decider proof generation + let proof = D::prove(rng, decider_pp, hypernova.clone()).unwrap(); + + let verified = D::verify( + decider_vp.clone(), + hypernova.i.clone(), + hypernova.z_0.clone(), + hypernova.z_i.clone(), + &(), + &(), + &proof, + ) + .unwrap(); + assert!(verified); + + // The rest of this test will serialize the data and deserialize it back, and use it to + // verify the proof: + + // serialize the verifier_params, proof and public inputs + let mut decider_vp_serialized = vec![]; + decider_vp + .serialize_compressed(&mut decider_vp_serialized) + .unwrap(); + let mut proof_serialized = vec![]; + proof.serialize_compressed(&mut proof_serialized).unwrap(); + // serialize the public inputs in a single packet + let mut public_inputs_serialized = vec![]; + hypernova + .i + .serialize_compressed(&mut public_inputs_serialized) + .unwrap(); + hypernova + .z_0 + .serialize_compressed(&mut public_inputs_serialized) + .unwrap(); + hypernova + .z_i + .serialize_compressed(&mut public_inputs_serialized) + .unwrap(); + hypernova + .U_i + .serialize_compressed(&mut public_inputs_serialized) + .unwrap(); + hypernova + .u_i + .serialize_compressed(&mut public_inputs_serialized) + .unwrap(); + + // deserialize back the verifier_params, proof and public inputs + let decider_vp_deserialized = + VerifierParam::< + Projective, + <KZG<'static, Bn254> as CommitmentScheme<Projective>>::VerifierParams, + <Groth16<Bn254> as SNARK<Fr>>::VerifyingKey, + >::deserialize_compressed(&mut decider_vp_serialized.as_slice()) + .unwrap(); + + let proof_deserialized = + Proof::<Projective, KZG<'static, Bn254>, Groth16<Bn254>>::deserialize_compressed( + &mut proof_serialized.as_slice(), + ) + .unwrap(); + + let mut reader = public_inputs_serialized.as_slice(); + let i_deserialized = Fr::deserialize_compressed(&mut reader).unwrap(); + let z_0_deserialized = Vec::<Fr>::deserialize_compressed(&mut reader).unwrap(); + let z_i_deserialized = Vec::<Fr>::deserialize_compressed(&mut reader).unwrap(); + let _U_i = LCCCS::<Projective>::deserialize_compressed(&mut reader).unwrap(); + let _u_i = CCCS::<Projective>::deserialize_compressed(&mut reader).unwrap(); + + let verified = D::verify( + decider_vp_deserialized, + i_deserialized.clone(), + z_0_deserialized.clone(), + z_i_deserialized.clone(), + &(), + &(), + &proof_deserialized, + ) + .unwrap(); + assert!(verified); } }
@@ -3,6 +3,8 @@ use ark_ec::{CurveGroup, Group};
use ark_ff::PrimeField; use ark_poly::DenseMultilinearExtension; use ark_poly::MultilinearExtension; +use ark_serialize::CanonicalDeserialize; +use ark_serialize::CanonicalSerialize; use ark_std::rand::Rng; use ark_std::Zero;@@ -15,7 +17,7 @@ use crate::utils::vec::mat_vec_mul;
use crate::Error; /// Linearized Committed CCS instance -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)] pub struct LCCCS<C: CurveGroup> { // Commitment to witness pub C: C,
@@ -6,6 +6,7 @@ };
use ark_ec::{CurveGroup, Group}; use ark_ff::{BigInteger, PrimeField}; use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldGadget}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::{fmt::Debug, marker::PhantomData, rand::RngCore, One, Zero}; pub mod cccs;@@ -14,6 +15,7 @@ pub mod decider_eth;
pub mod decider_eth_circuit; pub mod lcccs; pub mod nimfs; +pub mod serialize; pub mod utils; use cccs::CCCS;@@ -62,7 +64,7 @@ pub type HyperNovaCycleFoldCircuit<C, GC, const MU: usize, const NU: usize> =
CycleFoldCircuit<HyperNovaCycleFoldConfig<C, MU, NU>, GC>; /// Witness for the LCCCS & CCCS, containing the w vector, and the r_w used as randomness in the Pedersen commitment. -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)] pub struct Witness<F: PrimeField> { pub w: Vec<F>, pub r_w: F,@@ -939,13 +941,25 @@ test_ivc_opt::<KZG<Bn254>, Pedersen<Projective2>, false>(poseidon_config, F_circuit);
} // test_ivc allowing to choose the CommitmentSchemes - fn test_ivc_opt< + pub fn test_ivc_opt< CS1: CommitmentScheme<Projective, H>, CS2: CommitmentScheme<Projective2, H>, const H: bool, >( poseidon_config: PoseidonConfig<Fr>, F_circuit: CubicFCircuit<Fr>, + ) -> ( + HyperNova<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2, 2, 3, H>, + ( + ProverParams<Projective, Projective2, CS1, CS2, H>, + VerifierParams<Projective, Projective2, CS1, CS2, H>, + ), + (LCCCS<Projective>, Witness<Fr>), + (CCCS<Projective>, Witness<Fr>), + ( + CycleFoldCommittedInstance<Projective2>, + CycleFoldWitness<Projective2>, + ), ) { let mut rng = ark_std::test_rng();@@ -1001,14 +1015,22 @@ assert_eq!(Fr::from(num_steps as u32), hypernova.i);
let (running_instance, incoming_instance, cyclefold_instance) = hypernova.instances(); HN::verify( - hypernova_params.1, // verifier_params + hypernova_params.1.clone(), // verifier_params z_0, - hypernova.z_i, - hypernova.i, + hypernova.z_i.clone(), + hypernova.i.clone(), + running_instance.clone(), + incoming_instance.clone(), + cyclefold_instance.clone(), + ) + .unwrap(); + + ( + hypernova, + hypernova_params, running_instance, incoming_instance, cyclefold_instance, ) - .unwrap(); } }
@@ -0,0 +1,420 @@
+use crate::arith::ccs::CCS; +use crate::arith::r1cs::R1CS; +use crate::folding::hypernova::ProverParams; +use crate::folding::hypernova::VerifierParams; +use ark_crypto_primitives::sponge::poseidon::PoseidonConfig; +use ark_crypto_primitives::sponge::Absorb; +use ark_ec::{CurveGroup, Group}; +use ark_ff::PrimeField; +use ark_r1cs_std::groups::{CurveVar, GroupOpsBounds}; +use ark_r1cs_std::ToConstraintFieldGadget; +use ark_serialize::CanonicalDeserialize; +use ark_serialize::{CanonicalSerialize, Compress, SerializationError, Validate}; +use ark_std::marker::PhantomData; + +use crate::folding::hypernova::cccs::CCCS; +use crate::folding::hypernova::lcccs::LCCCS; +use crate::folding::hypernova::Witness; +use crate::folding::nova::{ + CommittedInstance as CycleFoldCommittedInstance, Witness as CycleFoldWitness, +}; +use crate::FoldingScheme; +use crate::{ + commitment::CommitmentScheme, + folding::{circuits::CF2, nova::PreprocessorParam}, + frontend::FCircuit, +}; + +use super::HyperNova; + +impl<C1, GC1, C2, GC2, FC, CS1, CS2, const MU: usize, const NU: usize, const H: bool> + CanonicalSerialize for HyperNova<C1, GC1, C2, GC2, FC, CS1, CS2, MU, NU, H> +where + C1: CurveGroup, + GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>, + C2: CurveGroup, + GC2: CurveVar<C2, CF2<C2>>, + FC: FCircuit<C1::ScalarField>, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, +{ + fn serialize_compressed<W: std::io::prelude::Write>( + &self, + writer: W, + ) -> Result<(), ark_serialize::SerializationError> { + self.serialize_with_mode(writer, ark_serialize::Compress::Yes) + } + + fn compressed_size(&self) -> usize { + self.serialized_size(ark_serialize::Compress::Yes) + } + + fn serialize_uncompressed<W: std::io::prelude::Write>( + &self, + writer: W, + ) -> Result<(), ark_serialize::SerializationError> { + self.serialize_with_mode(writer, ark_serialize::Compress::No) + } + + fn uncompressed_size(&self) -> usize { + self.serialized_size(ark_serialize::Compress::No) + } + + fn serialize_with_mode<W: std::io::prelude::Write>( + &self, + mut writer: W, + compress: ark_serialize::Compress, + ) -> Result<(), ark_serialize::SerializationError> { + self.pp_hash.serialize_with_mode(&mut writer, compress)?; + self.i.serialize_with_mode(&mut writer, compress)?; + self.z_0.serialize_with_mode(&mut writer, compress)?; + self.z_i.serialize_with_mode(&mut writer, compress)?; + self.W_i.serialize_with_mode(&mut writer, compress)?; + self.U_i.serialize_with_mode(&mut writer, compress)?; + self.w_i.serialize_with_mode(&mut writer, compress)?; + self.u_i.serialize_with_mode(&mut writer, compress)?; + self.cf_W_i.serialize_with_mode(&mut writer, compress)?; + self.cf_U_i.serialize_with_mode(&mut writer, compress) + } + + fn serialized_size(&self, compress: ark_serialize::Compress) -> usize { + self.pp_hash.serialized_size(compress) + + self.i.serialized_size(compress) + + self.z_0.serialized_size(compress) + + self.z_i.serialized_size(compress) + + self.W_i.serialized_size(compress) + + self.U_i.serialized_size(compress) + + self.w_i.serialized_size(compress) + + self.u_i.serialized_size(compress) + + self.cf_W_i.serialized_size(compress) + + self.cf_U_i.serialized_size(compress) + } +} + +impl<C1, GC1, C2, GC2, FC, CS1, CS2, const MU: usize, const NU: usize, const H: bool> + HyperNova<C1, GC1, C2, GC2, FC, CS1, CS2, MU, NU, H> +where + C1: CurveGroup, + GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>, + C2: CurveGroup, + GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>, + FC: FCircuit<C1::ScalarField, Params = ()>, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, + <C1 as CurveGroup>::BaseField: PrimeField, + <C2 as CurveGroup>::BaseField: PrimeField, + <C1 as Group>::ScalarField: Absorb, + <C2 as Group>::ScalarField: Absorb, + C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>, + for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>, + for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>, +{ + #[allow(clippy::too_many_arguments)] + pub fn deserialize_hypernova<R: std::io::prelude::Read>( + mut reader: R, + compress: Compress, + validate: Validate, + poseidon_config: PoseidonConfig<C1::ScalarField>, + cs_pp: CS1::ProverParams, + cs_vp: CS1::VerifierParams, + cf_cs_pp: CS2::ProverParams, + cf_cs_vp: CS2::VerifierParams, + ) -> Result<Self, SerializationError> { + let f_circuit = FC::new(()).unwrap(); + let prep_param = PreprocessorParam { + poseidon_config: poseidon_config.clone(), + F: f_circuit.clone(), + cs_pp: Some(cs_pp.clone()), + cs_vp: Some(cs_vp.clone()), + cf_cs_pp: Some(cf_cs_pp.clone()), + cf_cs_vp: Some(cf_cs_vp.clone()), + }; + // `test_rng` won't be used in `preprocess`, since parameters have already been initialized + let (prover_params, verifier_params) = Self::preprocess(ark_std::test_rng(), &prep_param) + .or(Err(SerializationError::InvalidData))?; + let pp_hash = C1::ScalarField::deserialize_with_mode(&mut reader, compress, validate)?; + let i = C1::ScalarField::deserialize_with_mode(&mut reader, compress, validate)?; + let z_0 = Vec::<C1::ScalarField>::deserialize_with_mode(&mut reader, compress, validate)?; + let z_i = Vec::<C1::ScalarField>::deserialize_with_mode(&mut reader, compress, validate)?; + let W_i = + Witness::<C1::ScalarField>::deserialize_with_mode(&mut reader, compress, validate)?; + let U_i = LCCCS::<C1>::deserialize_with_mode(&mut reader, compress, validate)?; + let w_i = + Witness::<C1::ScalarField>::deserialize_with_mode(&mut reader, compress, validate)?; + let u_i = CCCS::<C1>::deserialize_with_mode(&mut reader, compress, validate)?; + let cf_W_i = + CycleFoldWitness::<C2>::deserialize_with_mode(&mut reader, compress, validate)?; + let cf_U_i = CycleFoldCommittedInstance::<C2>::deserialize_with_mode( + &mut reader, + compress, + validate, + )?; + let ccs = prover_params.ccs.ok_or(SerializationError::InvalidData)?; + + Ok(HyperNova { + _gc1: PhantomData, + _c2: PhantomData, + _gc2: PhantomData, + ccs, + cf_r1cs: verifier_params.cf_r1cs, + poseidon_config, + cs_pp, + cf_cs_pp, + F: f_circuit, + pp_hash, + i, + z_0, + z_i, + W_i, + U_i, + w_i, + u_i, + cf_W_i, + cf_U_i, + }) + } +} + +impl< + C1: CurveGroup, + C2: CurveGroup, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, + const H: bool, + > CanonicalSerialize for ProverParams<C1, C2, CS1, CS2, H> +{ + fn serialize_compressed<W: std::io::prelude::Write>( + &self, + writer: W, + ) -> Result<(), SerializationError> { + self.serialize_with_mode(writer, Compress::Yes) + } + + fn compressed_size(&self) -> usize { + self.serialized_size(Compress::Yes) + } + + fn serialize_uncompressed<W: std::io::prelude::Write>( + &self, + writer: W, + ) -> Result<(), SerializationError> { + self.serialize_with_mode(writer, Compress::No) + } + + fn uncompressed_size(&self) -> usize { + self.serialized_size(Compress::No) + } + + fn serialize_with_mode<W: std::io::prelude::Write>( + &self, + mut writer: W, + compress: Compress, + ) -> Result<(), SerializationError> { + self.cs_pp.serialize_with_mode(&mut writer, compress)?; + self.cf_cs_pp.serialize_with_mode(&mut writer, compress) + } + + fn serialized_size(&self, compress: Compress) -> usize { + self.cs_pp.serialized_size(compress) + self.cf_cs_pp.serialized_size(compress) + } +} + +impl< + C1: CurveGroup, + C2: CurveGroup, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, + const H: bool, + > ProverParams<C1, C2, CS1, CS2, H> +{ + pub fn deserialize_prover_params<R: std::io::prelude::Read>( + mut reader: R, + compress: Compress, + validate: Validate, + ccs: &Option<CCS<C1::ScalarField>>, + poseidon_config: &PoseidonConfig<C1::ScalarField>, + ) -> Result<Self, SerializationError> { + let cs_pp = CS1::ProverParams::deserialize_with_mode(&mut reader, compress, validate)?; + let cf_cs_pp = CS2::ProverParams::deserialize_with_mode(&mut reader, compress, validate)?; + + Ok(ProverParams { + cs_pp, + cf_cs_pp, + ccs: ccs.clone(), + poseidon_config: poseidon_config.clone(), + }) + } +} + +impl< + C1: CurveGroup, + C2: CurveGroup, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, + const H: bool, + > CanonicalSerialize for VerifierParams<C1, C2, CS1, CS2, H> +{ + fn serialize_compressed<W: std::io::prelude::Write>( + &self, + writer: W, + ) -> Result<(), SerializationError> { + self.serialize_with_mode(writer, Compress::Yes) + } + + fn compressed_size(&self) -> usize { + self.serialized_size(Compress::Yes) + } + + fn serialize_uncompressed<W: std::io::prelude::Write>( + &self, + writer: W, + ) -> Result<(), SerializationError> { + self.serialize_with_mode(writer, Compress::No) + } + + fn uncompressed_size(&self) -> usize { + self.serialized_size(Compress::No) + } + + fn serialize_with_mode<W: std::io::prelude::Write>( + &self, + mut writer: W, + compress: Compress, + ) -> Result<(), SerializationError> { + self.cf_r1cs.serialize_with_mode(&mut writer, compress)?; + self.cs_vp.serialize_with_mode(&mut writer, compress)?; + self.cf_cs_vp.serialize_with_mode(&mut writer, compress) + } + + fn serialized_size(&self, compress: Compress) -> usize { + self.cf_r1cs.serialized_size(compress) + + self.cs_vp.serialized_size(compress) + + self.cf_cs_vp.serialized_size(compress) + } +} + +impl< + C1: CurveGroup, + C2: CurveGroup, + CS1: CommitmentScheme<C1, H>, + CS2: CommitmentScheme<C2, H>, + const H: bool, + > VerifierParams<C1, C2, CS1, CS2, H> +{ + pub fn deserialize_verifier_params<R: std::io::Read>( + mut reader: R, + compress: Compress, + validate: Validate, + ccs: &CCS<C1::ScalarField>, + poseidon_config: &PoseidonConfig<C1::ScalarField>, + ) -> Result<Self, SerializationError> { + let cf_r1cs = R1CS::deserialize_with_mode(&mut reader, compress, validate)?; + let cs_vp = CS1::VerifierParams::deserialize_with_mode(&mut reader, compress, validate)?; + let cf_cs_vp = CS2::VerifierParams::deserialize_with_mode(&mut reader, compress, validate)?; + Ok(VerifierParams { + ccs: ccs.clone(), + poseidon_config: poseidon_config.clone(), + cf_r1cs, + cs_vp, + cf_cs_vp, + }) + } +} + +#[cfg(test)] +pub mod tests { + use crate::FoldingScheme; + use crate::MultiFolding; + use ark_serialize::{Compress, Validate, Write}; + use std::fs; + + use crate::{ + commitment::{kzg::KZG, pedersen::Pedersen}, + folding::hypernova::{tests::test_ivc_opt, HyperNova}, + frontend::{utils::CubicFCircuit, FCircuit}, + transcript::poseidon::poseidon_canonical_config, + }; + use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective}; + use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2}; + use ark_serialize::CanonicalSerialize; + + #[test] + fn test_serde_hypernova() { + let poseidon_config = poseidon_canonical_config::<Fr>(); + let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap(); + let (mut hn, (_, verifier_params), _, _, _) = test_ivc_opt::< + KZG<Bn254>, + Pedersen<Projective2>, + false, + >(poseidon_config.clone(), F_circuit); + + let mut writer = vec![]; + assert!(hn.serialize_compressed(&mut writer).is_ok()); + let mut writer = vec![]; + assert!(hn.serialize_uncompressed(&mut writer).is_ok()); + + let mut file = fs::OpenOptions::new() + .create(true) + .write(true) + .open("./hypernova.serde") + .unwrap(); + + file.write_all(&writer).unwrap(); + + let bytes = fs::read("./hypernova.serde").unwrap(); + + let mut hn_deserialized = HyperNova::< + Projective, + GVar, + Projective2, + GVar2, + CubicFCircuit<Fr>, + KZG<Bn254>, + Pedersen<Projective2>, + 2, + 3, + false, + >::deserialize_hypernova( + bytes.as_slice(), + Compress::No, + Validate::No, + poseidon_config, + hn.cs_pp.clone(), + verifier_params.cs_vp, + hn.cf_cs_pp.clone(), + verifier_params.cf_cs_vp, + ) + .unwrap(); + + assert_eq!(hn.i, hn_deserialized.i); + + let mut rng = ark_std::test_rng(); + for _ in 0..3 { + // prepare some new instances to fold in the multifolding step + let mut lcccs = vec![]; + for j in 0..1 { + let instance_state = vec![Fr::from(j as u32 + 85_u32)]; + let (U, W) = hn + .new_running_instance(&mut rng, instance_state, vec![]) + .unwrap(); + lcccs.push((U, W)); + } + let mut cccs = vec![]; + for j in 0..2 { + let instance_state = vec![Fr::from(j as u32 + 15_u32)]; + let (u, w) = hn + .new_incoming_instance(&mut rng, instance_state, vec![]) + .unwrap(); + cccs.push((u, w)); + } + + hn.prove_step(&mut rng, vec![], Some((lcccs.clone(), cccs.clone()))) + .unwrap(); + hn_deserialized + .prove_step(&mut rng, vec![], Some((lcccs, cccs))) + .unwrap(); + } + + assert_eq!(hn.z_i, hn_deserialized.z_i); + } +}