diff --git a/folding-schemes/src/folding/hypernova/cccs.rs b/folding-schemes/src/folding/hypernova/cccs.rs index de5c281..5b95df2 100644 --- a/folding-schemes/src/folding/hypernova/cccs.rs +++ b/folding-schemes/src/folding/hypernova/cccs.rs @@ -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 { // Commitment to witness pub C: C, diff --git a/folding-schemes/src/folding/hypernova/decider_eth.rs b/folding-schemes/src/folding/hypernova/decider_eth.rs index 3e659fe..e45876e 100644 --- a/folding-schemes/src/folding/hypernova/decider_eth.rs +++ b/folding-schemes/src/folding/hypernova/decider_eth.rs @@ -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 where C1: CurveGroup, @@ -85,8 +87,7 @@ where type PreprocessorParam = (FS::ProverParam, FS::VerifierParam); type ProverParam = (S::ProvingKey, CS1::ProverParams); type Proof = Proof; - /// VerifierParam = (pp_hash, snark::vk, commitment_scheme::vk) - type VerifierParam = (C1::ScalarField, S::VerifyingKey, CS1::VerifierParams); + type VerifierParam = VerifierParam; type PublicInput = Vec; type CommittedInstance = (); @@ -120,7 +121,12 @@ where 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 @@ where } 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 @@ pub mod tests { NU, >; - let mut rng = ark_std::test_rng(); + let mut rng = rand::rngs::OsRng; let poseidon_config = poseidon_canonical_config::(); let F_circuit = CubicFCircuit::::new(()).unwrap(); @@ -271,31 +281,22 @@ pub mod tests { 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 @@ pub mod tests { ) .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, + KZG<'static, Bn254>, + Pedersen, + MU, + NU, + false, + >; + type D = Decider< + Projective, + GVar, + Projective2, + GVar2, + CubicFCircuit, + KZG<'static, Bn254>, + Pedersen, + Groth16, // 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::(); + + let F_circuit = CubicFCircuit::::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, + 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, + 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, + as CommitmentScheme>::VerifierParams, + as SNARK>::VerifyingKey, + >::deserialize_compressed(&mut decider_vp_serialized.as_slice()) + .unwrap(); + + let proof_deserialized = + Proof::, Groth16>::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::::deserialize_compressed(&mut reader).unwrap(); + let z_i_deserialized = Vec::::deserialize_compressed(&mut reader).unwrap(); + let _U_i = LCCCS::::deserialize_compressed(&mut reader).unwrap(); + let _u_i = CCCS::::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); } } diff --git a/folding-schemes/src/folding/hypernova/lcccs.rs b/folding-schemes/src/folding/hypernova/lcccs.rs index 8cca4bc..6aa1652 100644 --- a/folding-schemes/src/folding/hypernova/lcccs.rs +++ b/folding-schemes/src/folding/hypernova/lcccs.rs @@ -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 { // Commitment to witness pub C: C, diff --git a/folding-schemes/src/folding/hypernova/mod.rs b/folding-schemes/src/folding/hypernova/mod.rs index f3c90ea..2917ea4 100644 --- a/folding-schemes/src/folding/hypernova/mod.rs +++ b/folding-schemes/src/folding/hypernova/mod.rs @@ -6,6 +6,7 @@ use ark_crypto_primitives::sponge::{ 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 = CycleFoldCircuit, 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 { pub w: Vec, pub r_w: F, @@ -939,13 +941,25 @@ mod tests { } // test_ivc allowing to choose the CommitmentSchemes - fn test_ivc_opt< + pub fn test_ivc_opt< CS1: CommitmentScheme, CS2: CommitmentScheme, const H: bool, >( poseidon_config: PoseidonConfig, F_circuit: CubicFCircuit, + ) -> ( + HyperNova, CS1, CS2, 2, 3, H>, + ( + ProverParams, + VerifierParams, + ), + (LCCCS, Witness), + (CCCS, Witness), + ( + CycleFoldCommittedInstance, + CycleFoldWitness, + ), ) { let mut rng = ark_std::test_rng(); @@ -1001,14 +1015,22 @@ mod tests { 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(); } } diff --git a/folding-schemes/src/folding/hypernova/serialize.rs b/folding-schemes/src/folding/hypernova/serialize.rs new file mode 100644 index 0000000..a7aa6d0 --- /dev/null +++ b/folding-schemes/src/folding/hypernova/serialize.rs @@ -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 + CanonicalSerialize for HyperNova +where + C1: CurveGroup, + GC1: CurveVar> + ToConstraintFieldGadget>, + C2: CurveGroup, + GC2: CurveVar>, + FC: FCircuit, + CS1: CommitmentScheme, + CS2: CommitmentScheme, +{ + fn serialize_compressed( + &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( + &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( + &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 + HyperNova +where + C1: CurveGroup, + GC1: CurveVar> + ToConstraintFieldGadget>, + C2: CurveGroup, + GC2: CurveVar> + ToConstraintFieldGadget>, + FC: FCircuit, + CS1: CommitmentScheme, + CS2: CommitmentScheme, + ::BaseField: PrimeField, + ::BaseField: PrimeField, + ::ScalarField: Absorb, + ::ScalarField: Absorb, + C1: CurveGroup, + 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( + mut reader: R, + compress: Compress, + validate: Validate, + poseidon_config: PoseidonConfig, + cs_pp: CS1::ProverParams, + cs_vp: CS1::VerifierParams, + cf_cs_pp: CS2::ProverParams, + cf_cs_vp: CS2::VerifierParams, + ) -> Result { + 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::::deserialize_with_mode(&mut reader, compress, validate)?; + let z_i = Vec::::deserialize_with_mode(&mut reader, compress, validate)?; + let W_i = + Witness::::deserialize_with_mode(&mut reader, compress, validate)?; + let U_i = LCCCS::::deserialize_with_mode(&mut reader, compress, validate)?; + let w_i = + Witness::::deserialize_with_mode(&mut reader, compress, validate)?; + let u_i = CCCS::::deserialize_with_mode(&mut reader, compress, validate)?; + let cf_W_i = + CycleFoldWitness::::deserialize_with_mode(&mut reader, compress, validate)?; + let cf_U_i = CycleFoldCommittedInstance::::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, + CS2: CommitmentScheme, + const H: bool, + > CanonicalSerialize for ProverParams +{ + fn serialize_compressed( + &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( + &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( + &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, + CS2: CommitmentScheme, + const H: bool, + > ProverParams +{ + pub fn deserialize_prover_params( + mut reader: R, + compress: Compress, + validate: Validate, + ccs: &Option>, + poseidon_config: &PoseidonConfig, + ) -> Result { + 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, + CS2: CommitmentScheme, + const H: bool, + > CanonicalSerialize for VerifierParams +{ + fn serialize_compressed( + &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( + &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( + &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, + CS2: CommitmentScheme, + const H: bool, + > VerifierParams +{ + pub fn deserialize_verifier_params( + mut reader: R, + compress: Compress, + validate: Validate, + ccs: &CCS, + poseidon_config: &PoseidonConfig, + ) -> Result { + 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::(); + let F_circuit = CubicFCircuit::::new(()).unwrap(); + let (mut hn, (_, verifier_params), _, _, _) = test_ivc_opt::< + KZG, + Pedersen, + 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, + KZG, + Pedersen, + 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); + } +}