From 99fdf04b9e71e98d416f4b49d4a510aa68afdcc2 Mon Sep 17 00:00:00 2001 From: arnaucube Date: Tue, 1 Oct 2024 13:43:32 +0200 Subject: [PATCH] Implement `from_ivc_proof` for the FoldingSchemes trait (and Nova, HyperNova, ProtoGalaxy), so that the FoldingScheme IVC's instance can be constructed from the given parameters and the last IVCProof, which allows to sent the IVCProof between different parties, so that they can continue iterating the IVC from the received IVCProof. Also the serializers allow for the IVCProof to be sent to a verifier that can deserialize it and verify it. This allows to remove the logic from the file [folding/nova/serialize.rs](https://github.com/privacy-scaling-explorations/sonobe/blob/f1d82418ba047cf90805f2d0505370246df24d68/folding-schemes/src/folding/nova/serialize.rs) and [folding/hypernova/serialize.rs](https://github.com/privacy-scaling-explorations/sonobe/blob/f1d82418ba047cf90805f2d0505370246df24d68/folding-schemes/src/folding/hypernova/serialize.rs) (removing the whole files), which is now covered by the `IVCProof` generated serializers (generated by macro instead of handwritten), and the test that the file contained is now abstracted and applied to all the 3 existing folding schemes (Nova, HyperNova, ProtoGalaxy) at the folding/mod.rs file. --- .gitignore | 1 + folding-schemes/src/folding/hypernova/cccs.rs | 2 +- .../src/folding/hypernova/decider_eth.rs | 2 +- .../folding/hypernova/decider_eth_circuit.rs | 4 +- folding-schemes/src/folding/hypernova/mod.rs | 272 ++++++++++-- .../src/folding/hypernova/serialize.rs | 420 ------------------ folding-schemes/src/folding/mod.rs | 136 ++++++ folding-schemes/src/folding/nova/mod.rs | 60 ++- folding-schemes/src/folding/nova/serialize.rs | 268 ----------- .../src/folding/protogalaxy/mod.rs | 47 +- folding-schemes/src/lib.rs | 16 +- 11 files changed, 480 insertions(+), 748 deletions(-) delete mode 100644 folding-schemes/src/folding/hypernova/serialize.rs delete mode 100644 folding-schemes/src/folding/nova/serialize.rs diff --git a/.gitignore b/.gitignore index d38766b0..089ab2a1 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ solidity-verifiers/generated examples/*.sol examples/*.calldata examples/*.inputs +*.serialized diff --git a/folding-schemes/src/folding/hypernova/cccs.rs b/folding-schemes/src/folding/hypernova/cccs.rs index 5378a639..9e42bd9d 100644 --- a/folding-schemes/src/folding/hypernova/cccs.rs +++ b/folding-schemes/src/folding/hypernova/cccs.rs @@ -18,7 +18,7 @@ use crate::utils::virtual_polynomial::{build_eq_x_r_vec, VirtualPolynomial}; use crate::Error; /// Committed CCS instance -#[derive(Debug, Clone, CanonicalSerialize, CanonicalDeserialize)] +#[derive(Debug, Clone, PartialEq, Eq, 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 f897efff..2843610c 100644 --- a/folding-schemes/src/folding/hypernova/decider_eth.rs +++ b/folding-schemes/src/folding/hypernova/decider_eth.rs @@ -377,7 +377,7 @@ pub mod tests { KZG<'static, Bn254>, Pedersen, false, - >::deserialize_prover_params( + >::deserialize_with_mode( hypernova_pp_serialized.as_slice(), Compress::Yes, Validate::No, diff --git a/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs b/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs index 1485f8b2..256b0cb0 100644 --- a/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs +++ b/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs @@ -582,9 +582,7 @@ pub mod tests { // generate a Nova instance and do a step of it let mut hypernova = HN::init(&hn_params, F_circuit, z_0.clone()).unwrap(); - hypernova - .prove_step(&mut rng, vec![], Some((vec![], vec![]))) - .unwrap(); + hypernova.prove_step(&mut rng, vec![], None).unwrap(); let ivc_proof = hypernova.ivc_proof(); HN::verify(hn_params.1, ivc_proof).unwrap(); diff --git a/folding-schemes/src/folding/hypernova/mod.rs b/folding-schemes/src/folding/hypernova/mod.rs index 748c8ee9..76698a85 100644 --- a/folding-schemes/src/folding/hypernova/mod.rs +++ b/folding-schemes/src/folding/hypernova/mod.rs @@ -6,7 +6,9 @@ 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_serialize::{ + CanonicalDeserialize, CanonicalSerialize, Compress, SerializationError, Validate, +}; use ark_std::{fmt::Debug, marker::PhantomData, rand::RngCore, One, Zero}; pub mod cccs; @@ -15,7 +17,6 @@ pub mod decider_eth; pub mod decider_eth_circuit; pub mod lcccs; pub mod nimfs; -pub mod serialize; pub mod utils; use cccs::CCCS; @@ -117,6 +118,108 @@ where pub ccs: Option>, } +impl< + C1: CurveGroup, + C2: CurveGroup, + CS1: CommitmentScheme, + CS2: CommitmentScheme, + const H: bool, + > CanonicalSerialize for ProverParams +{ + 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_with_mode( + 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_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, + }) + } +} + /// Verification parameters for HyperNova-based IVC #[derive(Debug, Clone)] pub struct VerifierParams< @@ -157,7 +260,7 @@ where } } -#[derive(Debug, Clone, CanonicalSerialize, CanonicalDeserialize)] +#[derive(PartialEq, Eq, Debug, Clone, CanonicalSerialize, CanonicalDeserialize)] pub struct IVCProof where C1: CurveGroup, @@ -584,36 +687,42 @@ where // `sponge` is for digest computation. let sponge = PoseidonSponge::::new(&self.poseidon_config); - let other_instances = other_instances.ok_or(Error::MissingOtherInstances)?; - - #[allow(clippy::type_complexity)] - let (lcccs, cccs): ( - Vec<(LCCCS, Witness)>, - Vec<(CCCS, Witness)>, - ) = other_instances; - - // recall, mu & nu is the number of all the LCCCS & CCCS respectively, including the - // running and incoming instances that are not part of the 'other_instances', hence the +1 - // in the couple of following checks. - if lcccs.len() + 1 != MU { - return Err(Error::NotSameLength( - "other_instances.lcccs.len()".to_string(), - lcccs.len(), - "hypernova.mu".to_string(), - MU, - )); - } - if cccs.len() + 1 != NU { - return Err(Error::NotSameLength( - "other_instances.cccs.len()".to_string(), - cccs.len(), - "hypernova.nu".to_string(), - NU, - )); - } + let (Us, Ws, us, ws) = if MU > 1 || NU > 1 { + let other_instances = other_instances.ok_or(Error::MissingOtherInstances(MU, NU))?; + + #[allow(clippy::type_complexity)] + let (lcccs, cccs): ( + Vec<(LCCCS, Witness)>, + Vec<(CCCS, Witness)>, + ) = other_instances; + + // recall, mu & nu is the number of all the LCCCS & CCCS respectively, including the + // running and incoming instances that are not part of the 'other_instances', hence the +1 + // in the couple of following checks. + if lcccs.len() + 1 != MU { + return Err(Error::NotSameLength( + "other_instances.lcccs.len()".to_string(), + lcccs.len(), + "hypernova.mu".to_string(), + MU, + )); + } + if cccs.len() + 1 != NU { + return Err(Error::NotSameLength( + "other_instances.cccs.len()".to_string(), + cccs.len(), + "hypernova.nu".to_string(), + NU, + )); + } - let (Us, Ws): (Vec>, Vec>) = lcccs.into_iter().unzip(); - let (us, ws): (Vec>, Vec>) = cccs.into_iter().unzip(); + let (Us, Ws): (Vec>, Vec>) = + lcccs.into_iter().unzip(); + let (us, ws): (Vec>, Vec>) = cccs.into_iter().unzip(); + (Some(Us), Some(Ws), Some(us), Some(ws)) + } else { + (None, None, None, None) + }; let augmented_f_circuit: AugmentedFCircuit; @@ -691,9 +800,9 @@ where z_i: Some(self.z_i.clone()), external_inputs: Some(external_inputs.clone()), U_i: Some(self.U_i.clone()), - Us: Some(Us.clone()), + Us: Us.clone(), u_i_C: Some(self.u_i.C), - us: Some(us.clone()), + us: us.clone(), U_i1_C: Some(U_i1.C), F: self.F.clone(), x: Some(u_i1_x), @@ -709,14 +818,31 @@ where let mut transcript_p: PoseidonSponge = PoseidonSponge::::new(&self.poseidon_config); transcript_p.absorb(&self.pp_hash); + + let (all_Us, all_us, all_Ws, all_ws) = if MU > 1 || NU > 1 { + ( + [vec![self.U_i.clone()], Us.clone().unwrap()].concat(), + [vec![self.u_i.clone()], us.clone().unwrap()].concat(), + [vec![self.W_i.clone()], Ws.unwrap()].concat(), + [vec![self.w_i.clone()], ws.unwrap()].concat(), + ) + } else { + ( + vec![self.U_i.clone()], + vec![self.u_i.clone()], + vec![self.W_i.clone()], + vec![self.w_i.clone()], + ) + }; + let (rho, nimfs_proof); (nimfs_proof, U_i1, W_i1, rho) = NIMFS::>::prove( &mut transcript_p, &self.ccs, - &[vec![self.U_i.clone()], Us.clone()].concat(), - &[vec![self.u_i.clone()], us.clone()].concat(), - &[vec![self.W_i.clone()], Ws].concat(), - &[vec![self.w_i.clone()], ws].concat(), + &all_Us, + &all_us, + &all_Ws, + &all_ws, )?; // sanity check: check the folded instance relation @@ -743,12 +869,12 @@ where // where each p_i is in fact p_i.to_constraint_field() let cf_u_i_x = [ vec![rho_Fq], - get_cm_coordinates(&self.U_i.C), - Us.iter() + all_Us + .iter() .flat_map(|Us_i| get_cm_coordinates(&Us_i.C)) .collect(), - get_cm_coordinates(&self.u_i.C), - us.iter() + all_us + .iter() .flat_map(|us_i| get_cm_coordinates(&us_i.C)) .collect(), get_cm_coordinates(&U_i1.C), @@ -760,10 +886,8 @@ where r_bits: Some(rho_bits.clone()), points: Some( [ - vec![self.U_i.clone().C], - Us.iter().map(|Us_i| Us_i.C).collect(), - vec![self.u_i.clone().C], - us.iter().map(|us_i| us_i.C).collect(), + all_Us.iter().map(|Us_i| Us_i.C).collect::>(), + all_us.iter().map(|us_i| us_i.C).collect::>(), ] .concat(), ), @@ -804,9 +928,9 @@ where z_i: Some(self.z_i.clone()), external_inputs: Some(external_inputs), U_i: Some(self.U_i.clone()), - Us: Some(Us.clone()), + Us: Us.clone(), u_i_C: Some(self.u_i.C), - us: Some(us.clone()), + us: us.clone(), U_i1_C: Some(U_i1.C), F: self.F.clone(), x: Some(u_i1_x), @@ -895,6 +1019,58 @@ where } } + fn from_ivc_proof( + ivc_proof: Self::IVCProof, + fcircuit_params: FC::Params, + params: (Self::ProverParam, Self::VerifierParam), + ) -> Result { + let IVCProof { + i, + z_0, + z_i, + W_i, + U_i, + w_i, + u_i, + cf_W_i, + cf_U_i, + } = ivc_proof; + let (pp, vp) = params; + + let f_circuit = FC::new(fcircuit_params).unwrap(); + let augmented_f_circuit = AugmentedFCircuit::::empty( + &pp.poseidon_config, + f_circuit.clone(), + None, + )?; + let cf_circuit = HyperNovaCycleFoldCircuit::::empty(); + + let ccs = augmented_f_circuit.ccs.clone(); + let cf_r1cs = get_r1cs_from_cs::(cf_circuit)?; + + Ok(Self { + _gc1: PhantomData, + _c2: PhantomData, + _gc2: PhantomData, + ccs, + cf_r1cs, + poseidon_config: pp.poseidon_config, + cs_pp: pp.cs_pp, + cf_cs_pp: pp.cf_cs_pp, + F: f_circuit, + pp_hash: vp.pp_hash()?, + i, + z_0, + z_i, + w_i, + u_i, + W_i, + U_i, + cf_W_i, + cf_U_i, + }) + } + /// Implements IVC.V of Hyp.clone()erNova+CycleFold. Notice that this method does not include the /// commitments verification, which is done in the Decider. fn verify(vp: Self::VerifierParam, ivc_proof: Self::IVCProof) -> Result<(), Error> { diff --git a/folding-schemes/src/folding/hypernova/serialize.rs b/folding-schemes/src/folding/hypernova/serialize.rs deleted file mode 100644 index a7aa6d0c..00000000 --- a/folding-schemes/src/folding/hypernova/serialize.rs +++ /dev/null @@ -1,420 +0,0 @@ -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); - } -} diff --git a/folding-schemes/src/folding/mod.rs b/folding-schemes/src/folding/mod.rs index 0f3a66fd..91855b53 100644 --- a/folding-schemes/src/folding/mod.rs +++ b/folding-schemes/src/folding/mod.rs @@ -3,3 +3,139 @@ pub mod hypernova; pub mod nova; pub mod protogalaxy; pub mod traits; + +#[cfg(test)] +pub mod tests { + use ark_ec::CurveGroup; + use ark_ff::PrimeField; + use ark_pallas::{constraints::GVar as GVar1, Fr, Projective as G1}; + use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; + use ark_vesta::{constraints::GVar as GVar2, Projective as G2}; + use std::io::Write; + + use crate::commitment::pedersen::Pedersen; + use crate::folding::{ + hypernova::HyperNova, + nova::{Nova, PreprocessorParam as NovaPreprocessorParam}, + protogalaxy::ProtoGalaxy, + }; + use crate::frontend::utils::CubicFCircuit; + use crate::frontend::FCircuit; + use crate::transcript::poseidon::poseidon_canonical_config; + use crate::Error; + use crate::FoldingScheme; + + #[test] + fn test_serialize_ivc() { + let poseidon_config = poseidon_canonical_config::(); + let f_circuit = CubicFCircuit::::new(()).unwrap(); + + // test Nova + type N = Nova, Pedersen, Pedersen, false>; + let prep_param = NovaPreprocessorParam::new(poseidon_config.clone(), f_circuit); + test_serialize_ivc_opt::, N>( + "nova".to_string(), + prep_param.clone(), + ) + .unwrap(); + + // test HyperNova + type HN = HyperNova< + G1, + GVar1, + G2, + GVar2, + CubicFCircuit, + Pedersen, + Pedersen, + 1, // mu + 1, // nu + false, + >; + test_serialize_ivc_opt::, HN>( + "hypernova".to_string(), + prep_param, + ) + .unwrap(); + + // test ProtoGalaxy + type P = ProtoGalaxy, Pedersen, Pedersen>; + let prep_param = (poseidon_config, f_circuit); + test_serialize_ivc_opt::, P>( + "protogalaxy".to_string(), + prep_param, + ) + .unwrap(); + } + + fn test_serialize_ivc_opt< + C1: CurveGroup, + C2: CurveGroup, + FC: FCircuit, + FS: FoldingScheme, + >( + name: String, + prep_param: FS::PreprocessorParam, + ) -> Result<(), Error> + where + C1: CurveGroup, + C2::BaseField: PrimeField, + FC: FCircuit, + { + let mut rng = ark_std::test_rng(); + let F_circuit = FC::new(())?; + + let fs_params = FS::preprocess(&mut rng, &prep_param)?; + + let z_0 = vec![C1::ScalarField::from(3_u32)]; + let mut fs = FS::init(&fs_params, F_circuit, z_0.clone()).unwrap(); + + // perform multiple IVC steps (internally folding) + let num_steps: usize = 3; + for _ in 0..num_steps { + fs.prove_step(&mut rng, vec![], None).unwrap(); + } + + // verify the IVCProof + let ivc_proof: FS::IVCProof = fs.ivc_proof(); + FS::verify(fs_params.1.clone(), ivc_proof.clone()).unwrap(); + + // serialize the IVCProof and store it in a file + let mut writer = vec![]; + assert!(ivc_proof.serialize_compressed(&mut writer).is_ok()); + + let mut file = std::fs::OpenOptions::new() + .create(true) + .write(true) + .open(format!("./ivc_proof-{}.serialized", name)) + .unwrap(); + file.write_all(&writer).unwrap(); + + // read the IVCProof from the file deserializing it + let bytes = std::fs::read(format!("./ivc_proof-{}.serialized", name)).unwrap(); + let deserialized_ivc_proof = + FS::IVCProof::deserialize_compressed(bytes.as_slice()).unwrap(); + // verify deserialized IVCProof + FS::verify(fs_params.1.clone(), deserialized_ivc_proof.clone()).unwrap(); + + // build the FS from the given IVCProof, FC::Params, ProverParams and VerifierParams + let mut new_fs = FS::from_ivc_proof(deserialized_ivc_proof, (), fs_params.clone()).unwrap(); + + // perform several IVC steps on both the original FS instance and the recovered from the + // serialization new FS instance + let num_steps: usize = 3; + for _ in 0..num_steps { + new_fs.prove_step(&mut rng, vec![], None).unwrap(); + fs.prove_step(&mut rng, vec![], None).unwrap(); + } + + // check that the IVCProofs from both FS instances are equal + assert_eq!(new_fs.ivc_proof(), fs.ivc_proof()); + + // verify the last IVCProof from the recovered from serialization FS + let ivc_proof: FS::IVCProof = new_fs.ivc_proof(); + FS::verify(fs_params.1.clone(), ivc_proof.clone()).unwrap(); + + Ok(()) + } +} diff --git a/folding-schemes/src/folding/nova/mod.rs b/folding-schemes/src/folding/nova/mod.rs index 049a56ab..2b2ac284 100644 --- a/folding-schemes/src/folding/nova/mod.rs +++ b/folding-schemes/src/folding/nova/mod.rs @@ -38,7 +38,6 @@ use crate::{arith::Arith, commitment::CommitmentScheme}; pub mod circuits; pub mod nifs; pub mod ova; -pub mod serialize; pub mod traits; pub mod zk; @@ -429,7 +428,7 @@ where } } -#[derive(Debug, Clone, CanonicalSerialize, CanonicalDeserialize)] +#[derive(PartialEq, Eq, Debug, Clone, CanonicalSerialize, CanonicalDeserialize)] pub struct IVCProof where C1: CurveGroup, @@ -926,6 +925,63 @@ where } } + fn from_ivc_proof( + ivc_proof: IVCProof, + fcircuit_params: FC::Params, + params: (Self::ProverParam, Self::VerifierParam), + ) -> Result { + let IVCProof { + i, + z_0, + z_i, + W_i, + U_i, + w_i, + u_i, + cf_W_i, + cf_U_i, + } = ivc_proof; + let (pp, vp) = params; + + let f_circuit = FC::new(fcircuit_params).unwrap(); + let cs = ConstraintSystem::::new_ref(); + let cs2 = ConstraintSystem::::new_ref(); + let augmented_F_circuit = + AugmentedFCircuit::::empty(&pp.poseidon_config, f_circuit.clone()); + let cf_circuit = NovaCycleFoldCircuit::::empty(); + + augmented_F_circuit.generate_constraints(cs.clone())?; + cs.finalize(); + let cs = cs.into_inner().ok_or(Error::NoInnerConstraintSystem)?; + let r1cs = extract_r1cs::(&cs); + + cf_circuit.generate_constraints(cs2.clone())?; + cs2.finalize(); + let cs2 = cs2.into_inner().ok_or(Error::NoInnerConstraintSystem)?; + let cf_r1cs = extract_r1cs::(&cs2); + Ok(Self { + _gc1: PhantomData, + _c2: PhantomData, + _gc2: PhantomData, + r1cs, + cf_r1cs, + poseidon_config: pp.poseidon_config, + cs_pp: pp.cs_pp, + cf_cs_pp: pp.cf_cs_pp, + F: f_circuit, + pp_hash: vp.pp_hash()?, + i, + z_0, + z_i, + w_i, + u_i, + W_i, + U_i, + cf_W_i, + cf_U_i, + }) + } + /// Implements IVC.V of Nov.clone()a+CycleFold. Notice that this method does not include the /// commitments verification, which is done in the Decider. fn verify(vp: Self::VerifierParam, ivc_proof: Self::IVCProof) -> Result<(), Error> { diff --git a/folding-schemes/src/folding/nova/serialize.rs b/folding-schemes/src/folding/nova/serialize.rs deleted file mode 100644 index e5e53827..00000000 --- a/folding-schemes/src/folding/nova/serialize.rs +++ /dev/null @@ -1,268 +0,0 @@ -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::{utils::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); - } -} diff --git a/folding-schemes/src/folding/protogalaxy/mod.rs b/folding-schemes/src/folding/protogalaxy/mod.rs index d967423e..d12c9041 100644 --- a/folding-schemes/src/folding/protogalaxy/mod.rs +++ b/folding-schemes/src/folding/protogalaxy/mod.rs @@ -208,7 +208,7 @@ impl CommittedInstanceVarOps for CommittedIn } } -#[derive(Clone, Debug, CanonicalSerialize, CanonicalDeserialize)] +#[derive(Clone, Debug, PartialEq, Eq, CanonicalSerialize, CanonicalDeserialize)] pub struct Witness { w: Vec, r_w: F, @@ -358,7 +358,7 @@ where } } -#[derive(Debug, Clone, CanonicalSerialize, CanonicalDeserialize)] +#[derive(PartialEq, Eq, Debug, Clone, CanonicalSerialize, CanonicalDeserialize)] pub struct IVCProof where C1: CurveGroup, @@ -945,6 +945,49 @@ where } } + fn from_ivc_proof( + ivc_proof: Self::IVCProof, + fcircuit_params: FC::Params, + params: (Self::ProverParam, Self::VerifierParam), + ) -> Result { + let IVCProof { + i, + z_0, + z_i, + W_i, + U_i, + w_i, + u_i, + cf_W_i, + cf_U_i, + } = ivc_proof; + let (pp, vp) = params; + + let f_circuit = FC::new(fcircuit_params).unwrap(); + + Ok(Self { + _gc1: PhantomData, + _c2: PhantomData, + _gc2: PhantomData, + r1cs: vp.r1cs.clone(), + cf_r1cs: vp.cf_r1cs.clone(), + poseidon_config: pp.poseidon_config, + cs_params: pp.cs_params, + cf_cs_params: pp.cf_cs_params, + F: f_circuit, + pp_hash: vp.pp_hash()?, + i, + z_0, + z_i, + w_i, + u_i, + W_i, + U_i, + cf_W_i, + cf_U_i, + }) + } + /// Implements IVC.V of Pro.clone()toGalaxy+CycleFold fn verify(vp: Self::VerifierParam, ivc_proof: Self::IVCProof) -> Result<(), Error> { let Self::IVCProof { diff --git a/folding-schemes/src/lib.rs b/folding-schemes/src/lib.rs index d1d7e9d3..6909cd8b 100644 --- a/folding-schemes/src/lib.rs +++ b/folding-schemes/src/lib.rs @@ -108,8 +108,8 @@ pub enum Error { JSONSerdeError(String), #[error("Multi instances folding not supported in this scheme")] NoMultiInstances, - #[error("Missing 'other' instances, since this is a multi-instances folding scheme")] - MissingOtherInstances, + #[error("Missing 'other' instances, since this is a multi-instances folding scheme. Expected number of instances, mu:{0}, nu:{1}")] + MissingOtherInstances(usize, usize), } /// FoldingScheme defines trait that is implemented by the diverse folding schemes. It is defined @@ -131,7 +131,7 @@ where type IncomingInstance: Debug; // contains the CommittedInstance + Witness type MultiCommittedInstanceWithWitness: Debug; // type used for the extra instances in the multi-instance folding setting type CFInstance: Debug; // CycleFold CommittedInstance & Witness - type IVCProof: Debug + CanonicalSerialize + CanonicalDeserialize; + type IVCProof: PartialEq + Eq + Clone + Debug + CanonicalSerialize + CanonicalDeserialize; fn preprocess( rng: impl RngCore, @@ -167,6 +167,16 @@ where /// returns the last IVC state proof, which can be verified in the `verify` method fn ivc_proof(&self) -> Self::IVCProof; + /// constructs the FoldingScheme instance from the given IVCProof, ProverParams, VerifierParams + /// and PoseidonConfig. + /// This method is useful for when the IVCProof is sent between different parties, so that they + /// can continue iterating the IVC from the received IVCProof. + fn from_ivc_proof( + ivc_proof: Self::IVCProof, + fcircuit_params: FC::Params, + params: (Self::ProverParam, Self::VerifierParam), + ) -> Result; + fn verify(vp: Self::VerifierParam, ivc_proof: Self::IVCProof) -> Result<(), Error>; }