mirror of
https://github.com/arnaucube/sonobe.git
synced 2026-01-28 14:56:40 +01:00
Add IVCProof to the existing folding schemes (Nova,HyperNova,ProtoGalaxy) (#167)
* 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](f1d82418ba/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
This commit is contained in:
@@ -491,7 +491,6 @@ where
|
||||
pub mod tests {
|
||||
use ark_pallas::{constraints::GVar, Fq, Fr, Projective};
|
||||
use ark_relations::r1cs::ConstraintSystem;
|
||||
use ark_std::One;
|
||||
use ark_vesta::{constraints::GVar as GVar2, Projective as Projective2};
|
||||
|
||||
use super::*;
|
||||
@@ -533,18 +532,9 @@ pub mod tests {
|
||||
// generate a Nova instance and do a step of it
|
||||
let mut nova = N::init(&nova_params, F_circuit, z_0.clone()).unwrap();
|
||||
nova.prove_step(&mut rng, vec![], None).unwrap();
|
||||
let ivc_v = nova.clone();
|
||||
let (running_instance, incoming_instance, cyclefold_instance) = ivc_v.instances();
|
||||
N::verify(
|
||||
nova_params.1, // verifier_params
|
||||
z_0,
|
||||
ivc_v.z_i,
|
||||
Fr::one(),
|
||||
running_instance,
|
||||
incoming_instance,
|
||||
cyclefold_instance,
|
||||
)
|
||||
.unwrap();
|
||||
// verify the IVC
|
||||
let ivc_proof = nova.ivc_proof();
|
||||
N::verify(nova_params.1, ivc_proof).unwrap();
|
||||
|
||||
// load the DeciderCircuit 1 & 2 from the Nova instance
|
||||
let decider_circuit1 =
|
||||
|
||||
@@ -75,8 +75,8 @@ impl<C1, GC1, C2, GC2, FC, CS1, CS2, S, FS> DeciderTrait<C1, C2, FC, FS>
|
||||
for Decider<C1, GC1, C2, GC2, FC, CS1, CS2, S, FS>
|
||||
where
|
||||
C1: CurveGroup,
|
||||
C2: CurveGroup,
|
||||
GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
|
||||
C2: CurveGroup,
|
||||
GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>,
|
||||
FC: FCircuit<C1::ScalarField>,
|
||||
// CS1 is a KZG commitment, where challenge is C1::Fr elem
|
||||
@@ -343,9 +343,7 @@ pub mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::commitment::pedersen::Pedersen;
|
||||
use crate::folding::nova::{
|
||||
PreprocessorParam, ProverParams as NovaProverParams, VerifierParams as NovaVerifierParams,
|
||||
};
|
||||
use crate::folding::nova::{PreprocessorParam, ProverParams as NovaProverParams};
|
||||
use crate::frontend::utils::CubicFCircuit;
|
||||
use crate::transcript::poseidon::poseidon_canonical_config;
|
||||
|
||||
@@ -490,13 +488,15 @@ pub mod tests {
|
||||
&mut nova_pp_serialized.as_slice()
|
||||
)
|
||||
.unwrap();
|
||||
let nova_vp_deserialized = NovaVerifierParams::<
|
||||
let nova_vp_deserialized = <N as FoldingScheme<
|
||||
Projective,
|
||||
Projective2,
|
||||
KZG<'static, Bn254>,
|
||||
Pedersen<Projective2>,
|
||||
>::deserialize_compressed(
|
||||
&mut nova_vp_serialized.as_slice()
|
||||
CubicFCircuit<Fr>,
|
||||
>>::vp_deserialize_with_mode(
|
||||
&mut nova_vp_serialized.as_slice(),
|
||||
ark_serialize::Compress::Yes,
|
||||
ark_serialize::Validate::Yes,
|
||||
(), // fcircuit_params
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -589,7 +589,7 @@ pub mod tests {
|
||||
use ark_relations::r1cs::ConstraintSystem;
|
||||
use ark_std::{
|
||||
rand::{thread_rng, Rng},
|
||||
One, UniformRand,
|
||||
UniformRand,
|
||||
};
|
||||
use ark_vesta::{constraints::GVar as GVar2, Projective as Projective2};
|
||||
|
||||
@@ -810,18 +810,8 @@ pub mod tests {
|
||||
// generate a Nova instance and do a step of it
|
||||
let mut nova = N::init(&nova_params, F_circuit, z_0.clone()).unwrap();
|
||||
nova.prove_step(&mut rng, vec![], None).unwrap();
|
||||
let ivc_v = nova.clone();
|
||||
let (running_instance, incoming_instance, cyclefold_instance) = ivc_v.instances();
|
||||
N::verify(
|
||||
nova_params.1, // verifier_params
|
||||
z_0,
|
||||
ivc_v.z_i,
|
||||
Fr::one(),
|
||||
running_instance,
|
||||
incoming_instance,
|
||||
cyclefold_instance,
|
||||
)
|
||||
.unwrap();
|
||||
let ivc_proof = nova.ivc_proof();
|
||||
N::verify(nova_params.1, ivc_proof).unwrap();
|
||||
|
||||
// load the DeciderEthCircuit from the Nova instance
|
||||
let decider_eth_circuit = DeciderEthCircuit::<
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -345,15 +344,6 @@ where
|
||||
CS2: CommitmentScheme<C2, H>,
|
||||
{
|
||||
fn check(&self) -> Result<(), ark_serialize::SerializationError> {
|
||||
self.poseidon_config.full_rounds.check()?;
|
||||
self.poseidon_config.partial_rounds.check()?;
|
||||
self.poseidon_config.alpha.check()?;
|
||||
self.poseidon_config.ark.check()?;
|
||||
self.poseidon_config.mds.check()?;
|
||||
self.poseidon_config.rate.check()?;
|
||||
self.poseidon_config.capacity.check()?;
|
||||
self.r1cs.check()?;
|
||||
self.cf_r1cs.check()?;
|
||||
self.cs_vp.check()?;
|
||||
self.cf_cs_vp.check()?;
|
||||
Ok(())
|
||||
@@ -371,42 +361,12 @@ where
|
||||
mut writer: W,
|
||||
compress: ark_serialize::Compress,
|
||||
) -> Result<(), ark_serialize::SerializationError> {
|
||||
self.r1cs.serialize_with_mode(&mut writer, compress)?;
|
||||
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: ark_serialize::Compress) -> usize {
|
||||
self.r1cs.serialized_size(compress)
|
||||
+ self.cf_r1cs.serialized_size(compress)
|
||||
+ self.cs_vp.serialized_size(compress)
|
||||
+ self.cf_cs_vp.serialized_size(compress)
|
||||
}
|
||||
}
|
||||
impl<C1, C2, CS1, CS2, const H: bool> CanonicalDeserialize for VerifierParams<C1, C2, CS1, CS2, H>
|
||||
where
|
||||
C1: CurveGroup,
|
||||
C2: CurveGroup,
|
||||
CS1: CommitmentScheme<C1, H>,
|
||||
CS2: CommitmentScheme<C2, H>,
|
||||
{
|
||||
fn deserialize_with_mode<R: std::io::prelude::Read>(
|
||||
mut reader: R,
|
||||
compress: ark_serialize::Compress,
|
||||
validate: ark_serialize::Validate,
|
||||
) -> Result<Self, ark_serialize::SerializationError> {
|
||||
let r1cs = R1CS::deserialize_with_mode(&mut reader, compress, validate)?;
|
||||
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 {
|
||||
poseidon_config: poseidon_canonical_config::<C1::ScalarField>(),
|
||||
r1cs,
|
||||
cf_r1cs,
|
||||
cs_vp,
|
||||
cf_cs_vp,
|
||||
})
|
||||
self.cs_vp.serialized_size(compress) + self.cf_cs_vp.serialized_size(compress)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,6 +389,29 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone, CanonicalSerialize, CanonicalDeserialize)]
|
||||
pub struct IVCProof<C1, C2>
|
||||
where
|
||||
C1: CurveGroup,
|
||||
C2: CurveGroup,
|
||||
{
|
||||
// current step of the IVC
|
||||
pub i: C1::ScalarField,
|
||||
// initial state
|
||||
pub z_0: Vec<C1::ScalarField>,
|
||||
// current state
|
||||
pub z_i: Vec<C1::ScalarField>,
|
||||
// running instance
|
||||
pub W_i: Witness<C1>,
|
||||
pub U_i: CommittedInstance<C1>,
|
||||
// incoming instance
|
||||
pub w_i: Witness<C1>,
|
||||
pub u_i: CommittedInstance<C1>,
|
||||
// CycleFold instances
|
||||
pub cf_W_i: CycleFoldWitness<C2>,
|
||||
pub cf_U_i: CycleFoldCommittedInstance<C2>,
|
||||
}
|
||||
|
||||
/// Implements Nova+CycleFold's IVC, described in [Nova](https://eprint.iacr.org/2021/370.pdf) and
|
||||
/// [CycleFold](https://eprint.iacr.org/2023/1192.pdf), following the FoldingScheme trait
|
||||
/// The `H` const generic specifies whether the homorphic commitment scheme is blinding
|
||||
@@ -500,6 +483,58 @@ where
|
||||
type IncomingInstance = (CommittedInstance<C1>, Witness<C1>);
|
||||
type MultiCommittedInstanceWithWitness = ();
|
||||
type CFInstance = (CycleFoldCommittedInstance<C2>, CycleFoldWitness<C2>);
|
||||
type IVCProof = IVCProof<C1, C2>;
|
||||
|
||||
fn pp_deserialize_with_mode<R: std::io::prelude::Read>(
|
||||
reader: R,
|
||||
compress: ark_serialize::Compress,
|
||||
validate: ark_serialize::Validate,
|
||||
_fc_params: FC::Params, // FCircuit params
|
||||
) -> Result<Self::ProverParam, Error> {
|
||||
Ok(Self::ProverParam::deserialize_with_mode(
|
||||
reader, compress, validate,
|
||||
)?)
|
||||
}
|
||||
fn vp_deserialize_with_mode<R: std::io::prelude::Read>(
|
||||
mut reader: R,
|
||||
compress: ark_serialize::Compress,
|
||||
validate: ark_serialize::Validate,
|
||||
fc_params: FC::Params,
|
||||
) -> Result<Self::VerifierParam, Error> {
|
||||
let poseidon_config = poseidon_canonical_config::<C1::ScalarField>();
|
||||
|
||||
// generate the r1cs & cf_r1cs needed for the VerifierParams. In this way we avoid needing
|
||||
// to serialize them, saving significant space in the VerifierParams serialized size.
|
||||
|
||||
// main circuit R1CS:
|
||||
let f_circuit = FC::new(fc_params)?;
|
||||
let cs = ConstraintSystem::<C1::ScalarField>::new_ref();
|
||||
let augmented_F_circuit =
|
||||
AugmentedFCircuit::<C1, C2, GC2, FC>::empty(&poseidon_config, f_circuit.clone());
|
||||
augmented_F_circuit.generate_constraints(cs.clone())?;
|
||||
cs.finalize();
|
||||
let cs = cs.into_inner().ok_or(Error::NoInnerConstraintSystem)?;
|
||||
let r1cs = extract_r1cs::<C1::ScalarField>(&cs);
|
||||
|
||||
// CycleFold circuit R1CS
|
||||
let cs2 = ConstraintSystem::<C1::BaseField>::new_ref();
|
||||
let cf_circuit = NovaCycleFoldCircuit::<C1, GC1>::empty();
|
||||
cf_circuit.generate_constraints(cs2.clone())?;
|
||||
cs2.finalize();
|
||||
let cs2 = cs2.into_inner().ok_or(Error::NoInnerConstraintSystem)?;
|
||||
let cf_r1cs = extract_r1cs::<C1::BaseField>(&cs2);
|
||||
|
||||
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(Self::VerifierParam {
|
||||
poseidon_config,
|
||||
r1cs,
|
||||
cf_r1cs,
|
||||
cs_vp,
|
||||
cf_cs_vp,
|
||||
})
|
||||
}
|
||||
|
||||
fn preprocess(
|
||||
mut rng: impl RngCore,
|
||||
@@ -874,31 +909,93 @@ where
|
||||
self.z_i.clone()
|
||||
}
|
||||
|
||||
fn instances(
|
||||
&self,
|
||||
) -> (
|
||||
Self::RunningInstance,
|
||||
Self::IncomingInstance,
|
||||
Self::CFInstance,
|
||||
) {
|
||||
(
|
||||
(self.U_i.clone(), self.W_i.clone()),
|
||||
(self.u_i.clone(), self.w_i.clone()),
|
||||
(self.cf_U_i.clone(), self.cf_W_i.clone()),
|
||||
)
|
||||
fn ivc_proof(&self) -> Self::IVCProof {
|
||||
Self::IVCProof {
|
||||
i: self.i,
|
||||
z_0: self.z_0.clone(),
|
||||
z_i: self.z_i.clone(),
|
||||
W_i: self.W_i.clone(),
|
||||
U_i: self.U_i.clone(),
|
||||
w_i: self.w_i.clone(),
|
||||
u_i: self.u_i.clone(),
|
||||
cf_W_i: self.cf_W_i.clone(),
|
||||
cf_U_i: self.cf_U_i.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Implements IVC.V of Nova+CycleFold. Notice that this method does not include the
|
||||
fn from_ivc_proof(
|
||||
ivc_proof: IVCProof<C1, C2>,
|
||||
fcircuit_params: FC::Params,
|
||||
params: (Self::ProverParam, Self::VerifierParam),
|
||||
) -> Result<Self, Error> {
|
||||
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::<C1::ScalarField>::new_ref();
|
||||
let cs2 = ConstraintSystem::<C1::BaseField>::new_ref();
|
||||
let augmented_F_circuit =
|
||||
AugmentedFCircuit::<C1, C2, GC2, FC>::empty(&pp.poseidon_config, f_circuit.clone());
|
||||
let cf_circuit = NovaCycleFoldCircuit::<C1, GC1>::empty();
|
||||
|
||||
augmented_F_circuit.generate_constraints(cs.clone())?;
|
||||
cs.finalize();
|
||||
let cs = cs.into_inner().ok_or(Error::NoInnerConstraintSystem)?;
|
||||
let r1cs = extract_r1cs::<C1::ScalarField>(&cs);
|
||||
|
||||
cf_circuit.generate_constraints(cs2.clone())?;
|
||||
cs2.finalize();
|
||||
let cs2 = cs2.into_inner().ok_or(Error::NoInnerConstraintSystem)?;
|
||||
let cf_r1cs = extract_r1cs::<C1::BaseField>(&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,
|
||||
z_0: Vec<C1::ScalarField>, // initial state
|
||||
z_i: Vec<C1::ScalarField>, // last state
|
||||
num_steps: C1::ScalarField,
|
||||
running_instance: Self::RunningInstance,
|
||||
incoming_instance: Self::IncomingInstance,
|
||||
cyclefold_instance: Self::CFInstance,
|
||||
) -> Result<(), Error> {
|
||||
fn verify(vp: Self::VerifierParam, ivc_proof: Self::IVCProof) -> Result<(), Error> {
|
||||
let Self::IVCProof {
|
||||
i: num_steps,
|
||||
z_0,
|
||||
z_i,
|
||||
W_i,
|
||||
U_i,
|
||||
w_i,
|
||||
u_i,
|
||||
cf_W_i,
|
||||
cf_U_i,
|
||||
} = ivc_proof;
|
||||
|
||||
let sponge = PoseidonSponge::<C1::ScalarField>::new(&vp.poseidon_config);
|
||||
|
||||
if num_steps == C1::ScalarField::zero() {
|
||||
@@ -908,10 +1005,6 @@ where
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let (U_i, W_i) = running_instance;
|
||||
let (u_i, w_i) = incoming_instance;
|
||||
let (cf_U_i, cf_W_i) = cyclefold_instance;
|
||||
|
||||
if u_i.x.len() != 2 || U_i.x.len() != 2 {
|
||||
return Err(Error::IVCVerificationFail);
|
||||
}
|
||||
@@ -1175,15 +1268,67 @@ pub mod tests {
|
||||
}
|
||||
assert_eq!(Fr::from(num_steps as u32), nova.i);
|
||||
|
||||
let (running_instance, incoming_instance, cyclefold_instance) = nova.instances();
|
||||
// serialize the Nova Prover & Verifier params. These params are the trusted setup of the commitment schemes used
|
||||
let mut nova_pp_serialized = vec![];
|
||||
nova_params
|
||||
.0
|
||||
.serialize_compressed(&mut nova_pp_serialized)
|
||||
.unwrap();
|
||||
let mut nova_vp_serialized = vec![];
|
||||
nova_params
|
||||
.1
|
||||
.serialize_compressed(&mut nova_vp_serialized)
|
||||
.unwrap();
|
||||
|
||||
// deserialize the Nova params
|
||||
let _nova_pp_deserialized =
|
||||
ProverParams::<Projective, Projective2, CS1, CS2, H>::deserialize_compressed(
|
||||
&mut nova_pp_serialized.as_slice(),
|
||||
)
|
||||
.unwrap();
|
||||
let nova_vp_deserialized = Nova::<
|
||||
Projective,
|
||||
GVar,
|
||||
Projective2,
|
||||
GVar2,
|
||||
CubicFCircuit<Fr>,
|
||||
CS1,
|
||||
CS2,
|
||||
H,
|
||||
>::vp_deserialize_with_mode(
|
||||
&mut nova_vp_serialized.as_slice(),
|
||||
ark_serialize::Compress::Yes,
|
||||
ark_serialize::Validate::Yes,
|
||||
(), // fcircuit_params
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let ivc_proof = nova.ivc_proof();
|
||||
|
||||
// serialize IVCProof
|
||||
let mut ivc_proof_serialized = vec![];
|
||||
assert!(ivc_proof
|
||||
.serialize_compressed(&mut ivc_proof_serialized)
|
||||
.is_ok());
|
||||
// deserialize IVCProof
|
||||
let ivc_proof_deserialized = <Nova::<
|
||||
Projective,
|
||||
GVar,
|
||||
Projective2,
|
||||
GVar2,
|
||||
CubicFCircuit<Fr>,
|
||||
CS1,
|
||||
CS2,
|
||||
H,
|
||||
> as FoldingScheme<Projective,Projective2, CubicFCircuit<Fr>>>::IVCProof::deserialize_compressed(
|
||||
ivc_proof_serialized.as_slice()
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// verify the deserialized IVCProof with the deserialized VerifierParams
|
||||
Nova::<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2, H>::verify(
|
||||
nova_params.1, // Nova's verifier params
|
||||
z_0.clone(),
|
||||
nova.z_i.clone(),
|
||||
nova.i,
|
||||
running_instance,
|
||||
incoming_instance,
|
||||
cyclefold_instance,
|
||||
nova_vp_deserialized, // Nova's verifier params
|
||||
ivc_proof_deserialized,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user