use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb}; use ark_ec::{CurveGroup, Group}; use ark_ff::PrimeField; use ark_r1cs_std::{ groups::{CurveVar, GroupOpsBounds}, ToConstraintFieldGadget, }; use ark_relations::r1cs::ConstraintSynthesizer; use ark_relations::r1cs::ConstraintSystem; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError, Write}; use std::marker::PhantomData; use super::{ circuits::AugmentedFCircuit, CommittedInstance, Nova, NovaCycleFoldCircuit, ProverParams, Witness, }; use crate::{ arith::r1cs::extract_r1cs, commitment::CommitmentScheme, folding::circuits::{CF1, CF2}, frontend::FCircuit, }; impl CanonicalSerialize for Nova where C1: CurveGroup, C2: CurveGroup, 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>, GC1: CurveVar::ScalarField>, GC1: ToConstraintFieldGadget<::ScalarField>, GC2: CurveVar::BaseField>, { 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) } 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) } } // Note that we can't derive or implement `CanonicalDeserialize` directly. // This is because `CurveVar` notably does not implement the `Sync` trait. impl Nova where C1: CurveGroup, C2: CurveGroup, FC: FCircuit, Params = ()>, 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>, GC1: CurveVar::ScalarField>, GC1: ToConstraintFieldGadget<::ScalarField>, GC2: CurveVar>, GC2: ToConstraintFieldGadget<::BaseField>, { pub fn deserialize_nova( mut reader: R, compress: ark_serialize::Compress, validate: ark_serialize::Validate, prover_params: ProverParams, poseidon_config: PoseidonConfig, ) -> Result { 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 = CommittedInstance::::deserialize_with_mode(&mut reader, compress, validate)?; let W_i = Witness::::deserialize_with_mode(&mut reader, compress, validate)?; let U_i = CommittedInstance::::deserialize_with_mode(&mut reader, compress, validate)?; let cf_W_i = Witness::::deserialize_with_mode(&mut reader, compress, validate)?; let cf_U_i = CommittedInstance::::deserialize_with_mode(&mut reader, compress, validate)?; let f_circuit = FC::new(()).unwrap(); let cs = ConstraintSystem::::new_ref(); let cs2 = ConstraintSystem::::new_ref(); let augmented_F_circuit = AugmentedFCircuit::::empty(&poseidon_config, f_circuit.clone()); let cf_circuit = NovaCycleFoldCircuit::::empty(); augmented_F_circuit .generate_constraints(cs.clone()) .map_err(|_| SerializationError::InvalidData)?; cs.finalize(); let cs = cs.into_inner().ok_or(SerializationError::InvalidData)?; let r1cs = extract_r1cs::(&cs); cf_circuit .generate_constraints(cs2.clone()) .map_err(|_| SerializationError::InvalidData)?; cs2.finalize(); let cs2 = cs2.into_inner().ok_or(SerializationError::InvalidData)?; let cf_r1cs = extract_r1cs::(&cs2); Ok(Nova { _gc1: PhantomData, _c2: PhantomData, _gc2: PhantomData, r1cs, cf_r1cs, poseidon_config, cs_pp: prover_params.cs_pp, cf_cs_pp: prover_params.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, }) } } #[cfg(test)] pub mod tests { use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective}; use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2}; use ark_serialize::{CanonicalSerialize, Compress, Validate}; use std::{fs, io::Write}; use crate::{ commitment::{kzg::KZG, pedersen::Pedersen}, folding::nova::{Nova, PreprocessorParam}, frontend::{tests::CubicFCircuit, FCircuit}, transcript::poseidon::poseidon_canonical_config, FoldingScheme, }; #[test] fn test_serde_nova() { let mut rng = ark_std::test_rng(); let poseidon_config = poseidon_canonical_config::(); let F_circuit = CubicFCircuit::::new(()).unwrap(); // Initialize nova and make multiple `prove_step()` type N = Nova< Projective, GVar, Projective2, GVar2, CubicFCircuit, KZG<'static, Bn254>, Pedersen, false, >; let prep_param = PreprocessorParam::new(poseidon_config.clone(), F_circuit); let nova_params = N::preprocess(&mut rng, &prep_param).unwrap(); let z_0 = vec![Fr::from(3_u32)]; let mut nova = N::init(&nova_params, F_circuit, z_0.clone()).unwrap(); let num_steps: usize = 3; for _ in 0..num_steps { nova.prove_step(&mut rng, vec![], None).unwrap(); } let mut writer = vec![]; assert!(nova .serialize_with_mode(&mut writer, ark_serialize::Compress::No) .is_ok()); let mut file = fs::OpenOptions::new() .create(true) .write(true) .open("./nova.serde") .unwrap(); file.write_all(&writer).unwrap(); let bytes = fs::read("./nova.serde").unwrap(); let mut deserialized_nova = Nova::< Projective, GVar, Projective2, GVar2, CubicFCircuit, KZG, Pedersen, false, >::deserialize_nova( bytes.as_slice(), Compress::No, Validate::No, nova_params.0, // Nova's prover params poseidon_config, ) .unwrap(); assert_eq!(nova.i, deserialized_nova.i); let num_steps: usize = 3; for _ in 0..num_steps { deserialized_nova .prove_step(&mut rng, vec![], None) .unwrap(); nova.prove_step(&mut rng, vec![], None).unwrap(); } assert_eq!(deserialized_nova.w_i, nova.w_i); } }