The idea & motivation is that the [onchain decider](https://privacy-scaling-explorations.github.io/sonobe-docs/design/nova-decider-onchain.html) could still be used for non-onchain verification but the proving time is big (eg. a little bit less than 3 minutes on my laptop) since the circuit is big due the EVM constraints. Whereas with this new [offchain decider](https://privacy-scaling-explorations.github.io/sonobe-docs/design/nova-decider-offchain.html) we can generate the proofs much faster for the cases where it is not required to verify the proofs in the EVM. The code is mostly abstracted from any specifics of the current usage of Groth16 & KZG10, with the idea that eventually in the future we can have Spartan plugged in and use non-pairing-curves such as pallas&vesta. For the current version it relies on KZG10 commitments. The logic implemented in the code of this commit can be found at the updated docs section 'offchain decider': https://privacy-scaling-explorations.github.io/sonobe-docs/design/nova-decider-offchain.htmlmain
@ -0,0 +1,492 @@ |
|||||
|
/// This file implements the offchain decider. For ethereum use cases, use the
|
||||
|
/// DeciderEth from decider_eth.rs file.
|
||||
|
/// More details can be found at the documentation page:
|
||||
|
/// https://privacy-scaling-explorations.github.io/sonobe-docs/design/nova-decider-offchain.html
|
||||
|
use ark_crypto_primitives::sponge::Absorb;
|
||||
|
use ark_ec::{AffineRepr, CurveGroup, Group};
|
||||
|
use ark_ff::{BigInteger, PrimeField};
|
||||
|
use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldGadget};
|
||||
|
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
|
||||
|
use ark_snark::SNARK;
|
||||
|
use ark_std::rand::{CryptoRng, RngCore};
|
||||
|
use ark_std::{One, Zero};
|
||||
|
use core::marker::PhantomData;
|
||||
|
|
||||
|
use super::decider_circuits::{DeciderCircuit1, DeciderCircuit2};
|
||||
|
use super::{nifs::NIFS, CommittedInstance, Nova};
|
||||
|
use crate::commitment::CommitmentScheme;
|
||||
|
use crate::folding::circuits::{
|
||||
|
cyclefold::CycleFoldCommittedInstance,
|
||||
|
nonnative::{affine::NonNativeAffineVar, uint::NonNativeUintVar},
|
||||
|
CF2,
|
||||
|
};
|
||||
|
use crate::frontend::FCircuit;
|
||||
|
use crate::Error;
|
||||
|
use crate::{Decider as DeciderTrait, FoldingScheme};
|
||||
|
|
||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
|
pub struct Proof<C1, C2, CS1, CS2, S1, S2>
|
||||
|
where
|
||||
|
C1: CurveGroup,
|
||||
|
C2: CurveGroup,
|
||||
|
CS1: CommitmentScheme<C1>,
|
||||
|
CS2: CommitmentScheme<C2>,
|
||||
|
S1: SNARK<C1::ScalarField>,
|
||||
|
S2: SNARK<C2::ScalarField>,
|
||||
|
{
|
||||
|
c1_snark_proof: S1::Proof,
|
||||
|
c2_snark_proof: S2::Proof,
|
||||
|
cs1_proofs: [CS1::Proof; 2],
|
||||
|
cs2_proofs: [CS2::Proof; 2],
|
||||
|
// cmT and r are values for the last fold, U_{i+1}=NIFS.V(r, U_i, u_i, cmT), and they are
|
||||
|
// checked in-circuit
|
||||
|
cmT: C1,
|
||||
|
r: C1::ScalarField,
|
||||
|
// cyclefold committed instance
|
||||
|
cf_U_i: CycleFoldCommittedInstance<C2>,
|
||||
|
// the CS challenges are provided by the prover, but in-circuit they are checked to match the
|
||||
|
// in-circuit computed computed ones.
|
||||
|
cs1_challenges: [C1::ScalarField; 2],
|
||||
|
cs2_challenges: [C2::ScalarField; 2],
|
||||
|
}
|
||||
|
|
||||
|
#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
|
||||
|
pub struct ProverParam<CS1_ProvingKey, S1_ProvingKey, CS2_ProvingKey, S2_ProvingKey>
|
||||
|
where
|
||||
|
CS1_ProvingKey: Clone + CanonicalSerialize + CanonicalDeserialize,
|
||||
|
S1_ProvingKey: Clone + CanonicalSerialize + CanonicalDeserialize,
|
||||
|
CS2_ProvingKey: Clone + CanonicalSerialize + CanonicalDeserialize,
|
||||
|
S2_ProvingKey: Clone + CanonicalSerialize + CanonicalDeserialize,
|
||||
|
{
|
||||
|
pub c1_snark_pp: S1_ProvingKey,
|
||||
|
pub c1_cs_pp: CS1_ProvingKey,
|
||||
|
pub c2_snark_pp: S2_ProvingKey,
|
||||
|
pub c2_cs_pp: CS2_ProvingKey,
|
||||
|
}
|
||||
|
|
||||
|
#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
|
||||
|
pub struct VerifierParam<C1, CS1_VerifyingKey, S1_VerifyingKey, CS2_VerifyingKey, S2_VerifyingKey>
|
||||
|
where
|
||||
|
C1: CurveGroup,
|
||||
|
CS1_VerifyingKey: Clone + CanonicalSerialize + CanonicalDeserialize,
|
||||
|
S1_VerifyingKey: Clone + CanonicalSerialize + CanonicalDeserialize,
|
||||
|
CS2_VerifyingKey: Clone + CanonicalSerialize + CanonicalDeserialize,
|
||||
|
S2_VerifyingKey: Clone + CanonicalSerialize + CanonicalDeserialize,
|
||||
|
{
|
||||
|
pub pp_hash: C1::ScalarField,
|
||||
|
pub c1_snark_vp: S1_VerifyingKey,
|
||||
|
pub c1_cs_vp: CS1_VerifyingKey,
|
||||
|
pub c2_snark_vp: S2_VerifyingKey,
|
||||
|
pub c2_cs_vp: CS2_VerifyingKey,
|
||||
|
}
|
||||
|
|
||||
|
/// Onchain Decider, for ethereum use cases
|
||||
|
#[derive(Clone, Debug)]
|
||||
|
pub struct Decider<C1, GC1, C2, GC2, FC, CS1, CS2, S1, S2, FS> {
|
||||
|
_c1: PhantomData<C1>,
|
||||
|
_gc1: PhantomData<GC1>,
|
||||
|
_c2: PhantomData<C2>,
|
||||
|
_gc2: PhantomData<GC2>,
|
||||
|
_fc: PhantomData<FC>,
|
||||
|
_cs1: PhantomData<CS1>,
|
||||
|
_cs2: PhantomData<CS2>,
|
||||
|
_s1: PhantomData<S1>,
|
||||
|
_s2: PhantomData<S2>,
|
||||
|
_fs: PhantomData<FS>,
|
||||
|
}
|
||||
|
|
||||
|
impl<C1, GC1, C2, GC2, FC, CS1, CS2, S1, S2, FS> DeciderTrait<C1, C2, FC, FS>
|
||||
|
for Decider<C1, GC1, C2, GC2, FC, CS1, CS2, S1, S2, FS>
|
||||
|
where
|
||||
|
C1: CurveGroup,
|
||||
|
C2: CurveGroup,
|
||||
|
GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
|
||||
|
GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>,
|
||||
|
FC: FCircuit<C1::ScalarField>,
|
||||
|
CS1: CommitmentScheme<
|
||||
|
C1,
|
||||
|
ProverChallenge = C1::ScalarField,
|
||||
|
Challenge = C1::ScalarField,
|
||||
|
Proof = crate::commitment::kzg::Proof<C1>,
|
||||
|
>,
|
||||
|
CS2: CommitmentScheme<
|
||||
|
C2,
|
||||
|
ProverChallenge = C2::ScalarField,
|
||||
|
Challenge = C2::ScalarField,
|
||||
|
Proof = crate::commitment::kzg::Proof<C2>,
|
||||
|
>,
|
||||
|
S1: SNARK<C1::ScalarField>,
|
||||
|
S2: SNARK<C2::ScalarField>,
|
||||
|
FS: FoldingScheme<C1, C2, FC>,
|
||||
|
<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<'b> &'b GC1: GroupOpsBounds<'b, C1, GC1>,
|
||||
|
for<'b> &'b GC2: GroupOpsBounds<'b, C2, GC2>,
|
||||
|
// constrain FS into Nova, since this is a Decider specifically for Nova
|
||||
|
Nova<C1, GC1, C2, GC2, FC, CS1, CS2, false>: From<FS>,
|
||||
|
crate::folding::nova::ProverParams<C1, C2, CS1, CS2, false>: |
||||
|
From<<FS as FoldingScheme<C1, C2, FC>>::ProverParam>,
|
||||
|
crate::folding::nova::VerifierParams<C1, C2, CS1, CS2, false>: |
||||
|
From<<FS as FoldingScheme<C1, C2, FC>>::VerifierParam>,
|
||||
|
{
|
||||
|
type PreprocessorParam = (FS::ProverParam, FS::VerifierParam);
|
||||
|
type ProverParam =
|
||||
|
ProverParam<CS1::ProverParams, S1::ProvingKey, CS2::ProverParams, S2::ProvingKey>;
|
||||
|
type Proof = Proof<C1, C2, CS1, CS2, S1, S2>;
|
||||
|
type VerifierParam = VerifierParam<
|
||||
|
C1,
|
||||
|
CS1::VerifierParams,
|
||||
|
S1::VerifyingKey,
|
||||
|
CS2::VerifierParams,
|
||||
|
S2::VerifyingKey,
|
||||
|
>;
|
||||
|
type PublicInput = Vec<C1::ScalarField>;
|
||||
|
type CommittedInstance = CommittedInstance<C1>;
|
||||
|
|
||||
|
fn preprocess(
|
||||
|
mut rng: impl RngCore + CryptoRng,
|
||||
|
prep_param: Self::PreprocessorParam,
|
||||
|
fs: FS,
|
||||
|
) -> Result<(Self::ProverParam, Self::VerifierParam), Error> {
|
||||
|
let circuit1 = DeciderCircuit1::<C1, C2, GC2>::from_nova::<GC1, CS1, CS2, false, FC>(
|
||||
|
fs.clone().into(),
|
||||
|
)?;
|
||||
|
let circuit2 =
|
||||
|
DeciderCircuit2::<C1, GC1, C2>::from_nova::<GC2, CS1, CS2, false, FC>(fs.into())?;
|
||||
|
|
||||
|
// get the Groth16 specific setup for the circuits
|
||||
|
let (c1_g16_pk, c1_g16_vk) = S1::circuit_specific_setup(circuit1, &mut rng).unwrap();
|
||||
|
let (c2_g16_pk, c2_g16_vk) = S2::circuit_specific_setup(circuit2, &mut rng).unwrap();
|
||||
|
|
||||
|
// get the FoldingScheme prover & verifier params from Nova
|
||||
|
#[allow(clippy::type_complexity)]
|
||||
|
let nova_pp: <Nova<C1, GC1, C2, GC2, FC, CS1, CS2, false> as FoldingScheme<
|
||||
|
C1,
|
||||
|
C2,
|
||||
|
FC,
|
||||
|
>>::ProverParam = prep_param.0.clone().into();
|
||||
|
#[allow(clippy::type_complexity)]
|
||||
|
let nova_vp: <Nova<C1, GC1, C2, GC2, FC, CS1, CS2, false> as FoldingScheme<
|
||||
|
C1,
|
||||
|
C2,
|
||||
|
FC,
|
||||
|
>>::VerifierParam = prep_param.1.clone().into();
|
||||
|
|
||||
|
let pp_hash = nova_vp.pp_hash()?;
|
||||
|
let pp = Self::ProverParam {
|
||||
|
c1_snark_pp: c1_g16_pk,
|
||||
|
c1_cs_pp: nova_pp.cs_pp,
|
||||
|
c2_snark_pp: c2_g16_pk,
|
||||
|
c2_cs_pp: nova_pp.cf_cs_pp,
|
||||
|
};
|
||||
|
let vp = Self::VerifierParam {
|
||||
|
pp_hash,
|
||||
|
c1_snark_vp: c1_g16_vk,
|
||||
|
c1_cs_vp: nova_vp.cs_vp,
|
||||
|
c2_snark_vp: c2_g16_vk,
|
||||
|
c2_cs_vp: nova_vp.cf_cs_vp,
|
||||
|
};
|
||||
|
Ok((pp, vp))
|
||||
|
}
|
||||
|
|
||||
|
fn prove(
|
||||
|
mut rng: impl RngCore + CryptoRng,
|
||||
|
pp: Self::ProverParam,
|
||||
|
fs: FS,
|
||||
|
) -> Result<Self::Proof, Error> {
|
||||
|
let circuit1 = DeciderCircuit1::<C1, C2, GC2>::from_nova::<GC1, CS1, CS2, false, FC>(
|
||||
|
fs.clone().into(),
|
||||
|
)?;
|
||||
|
let circuit2 =
|
||||
|
DeciderCircuit2::<C1, GC1, C2>::from_nova::<GC2, CS1, CS2, false, FC>(fs.into())?;
|
||||
|
|
||||
|
let c1_snark_proof = S1::prove(&pp.c1_snark_pp, circuit1.clone(), &mut rng)
|
||||
|
.map_err(|e| Error::Other(e.to_string()))?;
|
||||
|
let c2_snark_proof = S2::prove(&pp.c2_snark_pp, circuit2.clone(), &mut rng)
|
||||
|
.map_err(|e| Error::Other(e.to_string()))?;
|
||||
|
|
||||
|
let cmT = circuit1.cmT.unwrap();
|
||||
|
let r_Fr = circuit1.r.unwrap();
|
||||
|
let W_i1 = circuit1.W_i1.unwrap();
|
||||
|
let cf_W_i = circuit2.cf_W_i.unwrap();
|
||||
|
|
||||
|
// get the challenges that have been already computed when preparing the circuits inputs in
|
||||
|
// the above `from_nova` calls
|
||||
|
let challenge_W = circuit1
|
||||
|
.cs_c_W
|
||||
|
.ok_or(Error::MissingValue("cs_c_W".to_string()))?;
|
||||
|
let challenge_E = circuit1
|
||||
|
.cs_c_E
|
||||
|
.ok_or(Error::MissingValue("cs_c_E".to_string()))?;
|
||||
|
let c2_challenge_W = circuit2
|
||||
|
.cs_c_W
|
||||
|
.ok_or(Error::MissingValue("c2's cs_c_W".to_string()))?;
|
||||
|
let c2_challenge_E = circuit2
|
||||
|
.cs_c_E
|
||||
|
.ok_or(Error::MissingValue("c2's cs_c_E".to_string()))?;
|
||||
|
|
||||
|
// generate CommitmentScheme proofs for the main instance
|
||||
|
let U_cmW_proof = CS1::prove_with_challenge(
|
||||
|
&pp.c1_cs_pp,
|
||||
|
challenge_W,
|
||||
|
&W_i1.W,
|
||||
|
&C1::ScalarField::zero(),
|
||||
|
None,
|
||||
|
)?;
|
||||
|
let U_cmE_proof = CS1::prove_with_challenge(
|
||||
|
&pp.c1_cs_pp,
|
||||
|
challenge_E,
|
||||
|
&W_i1.E,
|
||||
|
&C1::ScalarField::zero(),
|
||||
|
None,
|
||||
|
)?;
|
||||
|
// CS proofs for the CycleFold instance
|
||||
|
let cf_cmW_proof = CS2::prove_with_challenge(
|
||||
|
&pp.c2_cs_pp,
|
||||
|
c2_challenge_W,
|
||||
|
&cf_W_i.W,
|
||||
|
&C2::ScalarField::zero(),
|
||||
|
None,
|
||||
|
)?;
|
||||
|
let cf_cmE_proof = CS2::prove_with_challenge(
|
||||
|
&pp.c2_cs_pp,
|
||||
|
c2_challenge_E,
|
||||
|
&cf_W_i.E,
|
||||
|
&C2::ScalarField::zero(),
|
||||
|
None,
|
||||
|
)?;
|
||||
|
|
||||
|
Ok(Self::Proof {
|
||||
|
c1_snark_proof,
|
||||
|
c2_snark_proof,
|
||||
|
cs1_proofs: [U_cmW_proof, U_cmE_proof],
|
||||
|
cs2_proofs: [cf_cmW_proof, cf_cmE_proof],
|
||||
|
cmT,
|
||||
|
r: r_Fr,
|
||||
|
cf_U_i: circuit1.cf_U_i.unwrap(),
|
||||
|
cs1_challenges: [challenge_W, challenge_E],
|
||||
|
cs2_challenges: [c2_challenge_W, c2_challenge_E],
|
||||
|
})
|
||||
|
}
|
||||
|
|
||||
|
fn verify(
|
||||
|
vp: Self::VerifierParam,
|
||||
|
i: C1::ScalarField,
|
||||
|
z_0: Vec<C1::ScalarField>,
|
||||
|
z_i: Vec<C1::ScalarField>,
|
||||
|
running_instance: &Self::CommittedInstance,
|
||||
|
incoming_instance: &Self::CommittedInstance,
|
||||
|
proof: &Self::Proof,
|
||||
|
) -> Result<bool, Error> {
|
||||
|
if i <= C1::ScalarField::one() {
|
||||
|
return Err(Error::NotEnoughSteps);
|
||||
|
}
|
||||
|
|
||||
|
// compute U = U_{d+1}= NIFS.V(U_d, u_d, cmT)
|
||||
|
let U = NIFS::<C1, CS1>::verify(proof.r, running_instance, incoming_instance, &proof.cmT);
|
||||
|
|
||||
|
let (cmE_x, cmE_y) = NonNativeAffineVar::inputize(U.cmE)?;
|
||||
|
let (cmW_x, cmW_y) = NonNativeAffineVar::inputize(U.cmW)?;
|
||||
|
let (cmT_x, cmT_y) = NonNativeAffineVar::inputize(proof.cmT)?;
|
||||
|
|
||||
|
let zero = (&C2::BaseField::zero(), &C2::BaseField::zero());
|
||||
|
let cmE_affine = proof.cf_U_i.cmE.into_affine();
|
||||
|
let cmW_affine = proof.cf_U_i.cmW.into_affine();
|
||||
|
let (cf_cmE_x, cf_cmE_y) = cmE_affine.xy().unwrap_or(zero);
|
||||
|
let cf_cmE_z = C1::ScalarField::one();
|
||||
|
let (cf_cmW_x, cf_cmW_y) = cmW_affine.xy().unwrap_or(zero);
|
||||
|
let cf_cmW_z = C1::ScalarField::one();
|
||||
|
|
||||
|
// snark proof 1
|
||||
|
let c1_public_input: Vec<C1::ScalarField> = [
|
||||
|
vec![vp.pp_hash, i],
|
||||
|
z_0,
|
||||
|
z_i,
|
||||
|
// U_{i+1} values:
|
||||
|
vec![U.u],
|
||||
|
U.x.clone(),
|
||||
|
cmE_x,
|
||||
|
cmE_y,
|
||||
|
cmW_x,
|
||||
|
cmW_y,
|
||||
|
// CS1 values:
|
||||
|
proof.cs1_challenges.to_vec(), // c_W, c_E
|
||||
|
vec![
|
||||
|
proof.cs1_proofs[0].eval, // eval_W
|
||||
|
proof.cs1_proofs[1].eval, // eval_E
|
||||
|
],
|
||||
|
// cf_U_i values
|
||||
|
NonNativeUintVar::<CF2<C2>>::inputize(proof.cf_U_i.u),
|
||||
|
proof
|
||||
|
.cf_U_i
|
||||
|
.x
|
||||
|
.iter()
|
||||
|
.flat_map(|&x_i| NonNativeUintVar::<CF2<C2>>::inputize(x_i))
|
||||
|
.collect::<Vec<C1::ScalarField>>(),
|
||||
|
vec![
|
||||
|
*cf_cmE_x, *cf_cmE_y, cf_cmE_z, *cf_cmW_x, *cf_cmW_y, cf_cmW_z,
|
||||
|
],
|
||||
|
// NIFS values:
|
||||
|
cmT_x,
|
||||
|
cmT_y,
|
||||
|
vec![proof.r],
|
||||
|
]
|
||||
|
.concat();
|
||||
|
|
||||
|
let c1_snark_v = S1::verify(&vp.c1_snark_vp, &c1_public_input, &proof.c1_snark_proof)
|
||||
|
.map_err(|e| Error::Other(e.to_string()))?;
|
||||
|
if !c1_snark_v {
|
||||
|
return Err(Error::SNARKVerificationFail);
|
||||
|
}
|
||||
|
|
||||
|
let (cf2_cmE_x, cf2_cmE_y) = NonNativeAffineVar::inputize(proof.cf_U_i.cmE)?;
|
||||
|
let (cf2_cmW_x, cf2_cmW_y) = NonNativeAffineVar::inputize(proof.cf_U_i.cmW)?;
|
||||
|
|
||||
|
// snark proof 2
|
||||
|
// migrate pp_hash from C1::Fr to C1::Fq
|
||||
|
let pp_hash_Fq =
|
||||
|
C2::ScalarField::from_le_bytes_mod_order(&vp.pp_hash.into_bigint().to_bytes_le());
|
||||
|
let c2_public_input: Vec<C2::ScalarField> = [
|
||||
|
vec![pp_hash_Fq],
|
||||
|
vec![proof.cf_U_i.u],
|
||||
|
proof.cf_U_i.x.clone(),
|
||||
|
cf2_cmE_x,
|
||||
|
cf2_cmE_y,
|
||||
|
cf2_cmW_x,
|
||||
|
cf2_cmW_y,
|
||||
|
proof.cs2_challenges.to_vec(),
|
||||
|
vec![
|
||||
|
proof.cs2_proofs[0].eval, // eval_W
|
||||
|
proof.cs2_proofs[1].eval, // eval_E
|
||||
|
],
|
||||
|
]
|
||||
|
.concat();
|
||||
|
|
||||
|
let c2_snark_v = S2::verify(&vp.c2_snark_vp, &c2_public_input, &proof.c2_snark_proof)
|
||||
|
.map_err(|e| Error::Other(e.to_string()))?;
|
||||
|
if !c2_snark_v {
|
||||
|
return Err(Error::SNARKVerificationFail);
|
||||
|
}
|
||||
|
|
||||
|
// check C1 commitments (main instance commitments)
|
||||
|
CS1::verify_with_challenge(
|
||||
|
&vp.c1_cs_vp,
|
||||
|
proof.cs1_challenges[0],
|
||||
|
&U.cmW,
|
||||
|
&proof.cs1_proofs[0],
|
||||
|
)?;
|
||||
|
CS1::verify_with_challenge(
|
||||
|
&vp.c1_cs_vp,
|
||||
|
proof.cs1_challenges[1],
|
||||
|
&U.cmE,
|
||||
|
&proof.cs1_proofs[1],
|
||||
|
)?;
|
||||
|
|
||||
|
// check C2 commitments (CycleFold instance commitments)
|
||||
|
CS2::verify_with_challenge(
|
||||
|
&vp.c2_cs_vp,
|
||||
|
proof.cs2_challenges[0],
|
||||
|
&proof.cf_U_i.cmW,
|
||||
|
&proof.cs2_proofs[0],
|
||||
|
)?;
|
||||
|
CS2::verify_with_challenge(
|
||||
|
&vp.c2_cs_vp,
|
||||
|
proof.cs2_challenges[1],
|
||||
|
&proof.cf_U_i.cmE,
|
||||
|
&proof.cs2_proofs[1],
|
||||
|
)?;
|
||||
|
|
||||
|
Ok(true)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[cfg(test)]
|
||||
|
pub mod tests {
|
||||
|
use ark_groth16::Groth16;
|
||||
|
|
||||
|
// Note: do not use the MNTx_298 curves in practice, these are just for tests. Use the MNTx_753
|
||||
|
// curves instead.
|
||||
|
use ark_mnt4_298::{
|
||||
|
constraints::G1Var as GVar, Fr, G1Projective as Projective, MNT4_298 as MNT4,
|
||||
|
};
|
||||
|
use ark_mnt6_298::{
|
||||
|
constraints::G1Var as GVar2, G1Projective as Projective2, MNT6_298 as MNT6,
|
||||
|
};
|
||||
|
use std::time::Instant;
|
||||
|
|
||||
|
use super::*;
|
||||
|
use crate::commitment::kzg::KZG;
|
||||
|
use crate::folding::nova::PreprocessorParam;
|
||||
|
use crate::frontend::utils::CubicFCircuit;
|
||||
|
use crate::transcript::poseidon::poseidon_canonical_config;
|
||||
|
|
||||
|
#[test]
|
||||
|
fn test_decider() {
|
||||
|
// use Nova as FoldingScheme
|
||||
|
type N = Nova<
|
||||
|
Projective,
|
||||
|
GVar,
|
||||
|
Projective2,
|
||||
|
GVar2,
|
||||
|
CubicFCircuit<Fr>,
|
||||
|
KZG<'static, MNT4>,
|
||||
|
KZG<'static, MNT6>,
|
||||
|
false,
|
||||
|
>;
|
||||
|
type D = Decider<
|
||||
|
Projective,
|
||||
|
GVar,
|
||||
|
Projective2,
|
||||
|
GVar2,
|
||||
|
CubicFCircuit<Fr>,
|
||||
|
KZG<'static, MNT4>,
|
||||
|
KZG<'static, MNT6>,
|
||||
|
Groth16<MNT4>,
|
||||
|
Groth16<MNT6>,
|
||||
|
N, // here we define the FoldingScheme to use
|
||||
|
>;
|
||||
|
|
||||
|
let mut rng = ark_std::test_rng();
|
||||
|
let poseidon_config = poseidon_canonical_config::<Fr>();
|
||||
|
|
||||
|
let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap();
|
||||
|
let z_0 = vec![Fr::from(3_u32)];
|
||||
|
|
||||
|
let start = Instant::now();
|
||||
|
let prep_param = PreprocessorParam::new(poseidon_config, F_circuit);
|
||||
|
let nova_params = N::preprocess(&mut rng, &prep_param).unwrap();
|
||||
|
println!("Nova preprocess, {:?}", start.elapsed());
|
||||
|
|
||||
|
let start = Instant::now();
|
||||
|
let mut nova = N::init(&nova_params, F_circuit, z_0.clone()).unwrap();
|
||||
|
println!("Nova initialized, {:?}", start.elapsed());
|
||||
|
let start = Instant::now();
|
||||
|
nova.prove_step(&mut rng, vec![], None).unwrap();
|
||||
|
println!("prove_step, {:?}", start.elapsed());
|
||||
|
nova.prove_step(&mut rng, vec![], None).unwrap(); // do a 2nd step
|
||||
|
|
||||
|
let mut rng = rand::rngs::OsRng;
|
||||
|
|
||||
|
// prepare the Decider prover & verifier params
|
||||
|
let start = Instant::now();
|
||||
|
let (decider_pp, decider_vp) = D::preprocess(&mut rng, nova_params, nova.clone()).unwrap();
|
||||
|
println!("Decider preprocess, {:?}", start.elapsed());
|
||||
|
|
||||
|
// decider proof generation
|
||||
|
let start = Instant::now();
|
||||
|
let proof = D::prove(rng, decider_pp, nova.clone()).unwrap();
|
||||
|
println!("Decider prove, {:?}", start.elapsed());
|
||||
|
|
||||
|
// decider proof verification
|
||||
|
let start = Instant::now();
|
||||
|
let verified = D::verify(
|
||||
|
decider_vp, nova.i, nova.z_0, nova.z_i, &nova.U_i, &nova.u_i, &proof,
|
||||
|
)
|
||||
|
.unwrap();
|
||||
|
assert!(verified);
|
||||
|
println!("Decider verify, {:?}", start.elapsed());
|
||||
|
}
|
||||
|
}
|