* Add IVCProof to the existing folding schemes (Nova,HyperNova,ProtoGalaxy) * 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](mainf1d82418ba/folding-schemes/src/folding/nova/serialize.rs)
and [folding/hypernova/serialize.rs](f1d82418ba/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. * update Nova VerifierParams serializers to avoid serializing the R1CS to save big part of the old serialized size * rm .instances() since it's not needed * add nova params serialization to nova's ivc test to ensure that IVC verification works with deserialized data * Add unified FS::ProverParam & VerifierParam serialization & deserialization (for all Nova, HyperNova and ProtoGalaxy), without serializing the R1CS/CCS and thus saving substantial serialized bytes space. * rm CanonicalDeserialize warnings msgs for VerifierParams
@ -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<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);
|
|
||||
}
|
|
||||
}
|
|
@ -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<C1, GC1, C2, GC2, FC, CS1, CS2, const H: bool> CanonicalSerialize
|
|
||||
for Nova<C1, GC1, C2, GC2, FC, CS1, CS2, H>
|
|
||||
where
|
|
||||
C1: CurveGroup,
|
|
||||
C2: CurveGroup,
|
|
||||
FC: FCircuit<C1::ScalarField>,
|
|
||||
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>,
|
|
||||
GC1: CurveVar<C1, <C2 as Group>::ScalarField>,
|
|
||||
GC1: ToConstraintFieldGadget<<C2 as Group>::ScalarField>,
|
|
||||
GC2: CurveVar<C2, <C2 as CurveGroup>::BaseField>,
|
|
||||
{
|
|
||||
fn serialize_with_mode<W: 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)
|
|
||||
}
|
|
||||
|
|
||||
fn serialize_compressed<W: 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: 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)
|
|
||||
}
|
|
||||
}
|
|
||||
|
|
||||
// Note that we can't derive or implement `CanonicalDeserialize` directly.
|
|
||||
// This is because `CurveVar` notably does not implement the `Sync` trait.
|
|
||||
impl<C1, GC1, C2, GC2, FC, CS1, CS2, const H: bool> Nova<C1, GC1, C2, GC2, FC, CS1, CS2, H>
|
|
||||
where
|
|
||||
C1: CurveGroup,
|
|
||||
C2: CurveGroup,
|
|
||||
FC: FCircuit<CF1<C1>, 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>,
|
|
||||
GC1: CurveVar<C1, <C2 as Group>::ScalarField>,
|
|
||||
GC1: ToConstraintFieldGadget<<C2 as Group>::ScalarField>,
|
|
||||
GC2: CurveVar<C2, CF2<C2>>,
|
|
||||
GC2: ToConstraintFieldGadget<<C2 as CurveGroup>::BaseField>,
|
|
||||
{
|
|
||||
pub fn deserialize_nova<R: std::io::prelude::Read>(
|
|
||||
mut reader: R,
|
|
||||
compress: ark_serialize::Compress,
|
|
||||
validate: ark_serialize::Validate,
|
|
||||
prover_params: ProverParams<C1, C2, CS1, CS2, H>,
|
|
||||
poseidon_config: PoseidonConfig<C1::ScalarField>,
|
|
||||
) -> Result<Self, ark_serialize::SerializationError> {
|
|
||||
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>::deserialize_with_mode(&mut reader, compress, validate)?;
|
|
||||
let u_i = CommittedInstance::<C1>::deserialize_with_mode(&mut reader, compress, validate)?;
|
|
||||
let W_i = Witness::<C1>::deserialize_with_mode(&mut reader, compress, validate)?;
|
|
||||
let U_i = CommittedInstance::<C1>::deserialize_with_mode(&mut reader, compress, validate)?;
|
|
||||
let cf_W_i = Witness::<C2>::deserialize_with_mode(&mut reader, compress, validate)?;
|
|
||||
let cf_U_i =
|
|
||||
CommittedInstance::<C2>::deserialize_with_mode(&mut reader, compress, validate)?;
|
|
||||
|
|
||||
let f_circuit = FC::new(()).unwrap();
|
|
||||
let cs = ConstraintSystem::<C1::ScalarField>::new_ref();
|
|
||||
let cs2 = ConstraintSystem::<C1::BaseField>::new_ref();
|
|
||||
let augmented_F_circuit =
|
|
||||
AugmentedFCircuit::<C1, C2, GC2, FC>::empty(&poseidon_config, f_circuit.clone());
|
|
||||
let cf_circuit = NovaCycleFoldCircuit::<C1, GC1>::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::<C1::ScalarField>(&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::<C1::BaseField>(&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::<Fr>();
|
|
||||
let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap();
|
|
||||
|
|
||||
// Initialize nova and make multiple `prove_step()`
|
|
||||
type N = Nova<
|
|
||||
Projective,
|
|
||||
GVar,
|
|
||||
Projective2,
|
|
||||
GVar2,
|
|
||||
CubicFCircuit<Fr>,
|
|
||||
KZG<'static, Bn254>,
|
|
||||
Pedersen<Projective2>,
|
|
||||
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<Fr>,
|
|
||||
KZG<Bn254>,
|
|
||||
Pedersen<Projective2>,
|
|
||||
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);
|
|
||||
}
|
|
||||
}
|
|