arnaucube
arnaucube/sonobePublic Self-hosted repository
sonobe/folding-schemes/src/folding/nova/decider_eth.rs
folding-schemes/src/folding/nova/decider_eth.rsRaw
  1/// This file implements the onchain (Ethereum's EVM) decider.
  2use ark_crypto_primitives::sponge::Absorb;
  3use ark_ec::{CurveGroup, Group};
  4use ark_ff::PrimeField;
  5use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar};
  6use ark_snark::SNARK;
  7use ark_std::rand::CryptoRng;
  8use ark_std::rand::RngCore;
  9use core::marker::PhantomData;
 10
 11pub use super::decider_eth_circuit::DeciderEthCircuit;
 12use crate::commitment::{pedersen::Params as PedersenParams, CommitmentProver};
 13use crate::folding::circuits::nonnative::point_to_nonnative_limbs_custom_opt;
 14use crate::folding::nova::{circuits::CF2, CommittedInstance, Nova};
 15use crate::frontend::FCircuit;
 16use crate::Error;
 17use crate::{Decider as DeciderTrait, FoldingScheme};
 18use ark_r1cs_std::fields::nonnative::params::OptimizationType;
 19
 20/// Onchain Decider, for ethereum use cases
 21#[derive(Clone, Debug)]
 22pub struct Decider<C1, GC1, C2, GC2, FC, CP1, CP2, S, FS> {
 23    _c1: PhantomData<C1>,
 24    _gc1: PhantomData<GC1>,
 25    _c2: PhantomData<C2>,
 26    _gc2: PhantomData<GC2>,
 27    _fc: PhantomData<FC>,
 28    _cp1: PhantomData<CP1>,
 29    _cp2: PhantomData<CP2>,
 30    _s: PhantomData<S>,
 31    _fs: PhantomData<FS>,
 32}
 33
 34impl<C1, GC1, C2, GC2, FC, CP1, CP2, S, FS> DeciderTrait<C1, C2, FC, FS>
 35    for Decider<C1, GC1, C2, GC2, FC, CP1, CP2, S, FS>
 36where
 37    C1: CurveGroup,
 38    C2: CurveGroup,
 39    GC1: CurveVar<C1, CF2<C1>>,
 40    GC2: CurveVar<C2, CF2<C2>>,
 41    FC: FCircuit<C1::ScalarField>,
 42    CP1: CommitmentProver<C1>,
 43    // enforce that the CP2 is Pedersen commitment, since we're at Ethereum's EVM decider
 44    CP2: CommitmentProver<C2, Params = PedersenParams<C2>>,
 45    S: SNARK<C1::ScalarField>,
 46    FS: FoldingScheme<C1, C2, FC>,
 47    <C1 as CurveGroup>::BaseField: PrimeField,
 48    <C2 as CurveGroup>::BaseField: PrimeField,
 49    <C1 as Group>::ScalarField: Absorb,
 50    <C2 as Group>::ScalarField: Absorb,
 51    C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,
 52    for<'b> &'b GC2: GroupOpsBounds<'b, C2, GC2>,
 53    // constrain FS into Nova, since this is a Decider specificly for Nova
 54    Nova<C1, GC1, C2, GC2, FC, CP1, CP2>: From<FS>,
 55{
 56    type ProverParam = S::ProvingKey;
 57    type Proof = S::Proof;
 58    type VerifierParam = S::VerifyingKey;
 59    type PublicInput = Vec<C1::ScalarField>;
 60    type CommittedInstanceWithWitness = ();
 61    type CommittedInstance = CommittedInstance<C1>;
 62
 63    fn prove(
 64        pp: &Self::ProverParam,
 65        mut rng: impl RngCore + CryptoRng,
 66        folding_scheme: FS,
 67    ) -> Result<Self::Proof, Error> {
 68        let circuit =
 69            DeciderEthCircuit::<C1, GC1, C2, GC2, CP1, CP2>::from_nova::<FC>(folding_scheme.into());
 70
 71        let proof = S::prove(pp, circuit.clone(), &mut rng).unwrap();
 72
 73        Ok(proof)
 74    }
 75
 76    fn verify(
 77        vp: &Self::VerifierParam,
 78        i: C1::ScalarField,
 79        z_0: Vec<C1::ScalarField>,
 80        z_i: Vec<C1::ScalarField>,
 81        running_instance: &Self::CommittedInstance,
 82        proof: Self::Proof,
 83    ) -> Result<bool, Error> {
 84        let (cmE_x, cmE_y) = point_to_nonnative_limbs_custom_opt::<C1>(
 85            running_instance.cmE,
 86            OptimizationType::Constraints,
 87        )?;
 88        let (cmW_x, cmW_y) = point_to_nonnative_limbs_custom_opt::<C1>(
 89            running_instance.cmW,
 90            OptimizationType::Constraints,
 91        )?;
 92        let public_input: Vec<C1::ScalarField> = vec![
 93            vec![i],
 94            z_0,
 95            z_i,
 96            vec![running_instance.u],
 97            running_instance.x.clone(),
 98            cmE_x,
 99            cmE_y,
100            cmW_x,
101            cmW_y,
102        ]
103        .concat();
104        S::verify(vp, &public_input, &proof).map_err(|e| Error::Other(e.to_string()))
105    }
106}
107
108#[cfg(test)]
109pub mod tests {
110    use super::*;
111    use ark_groth16::Groth16;
112    use ark_mnt4_298::{constraints::G1Var as GVar, Fr, G1Projective as Projective, MNT4_298};
113    use ark_mnt6_298::{constraints::G1Var as GVar2, G1Projective as Projective2};
114    use std::time::Instant;
115
116    use crate::commitment::pedersen::Pedersen;
117    use crate::folding::nova::{get_pedersen_params_len, ProverParams};
118    use crate::frontend::tests::CubicFCircuit;
119    use crate::transcript::poseidon::poseidon_test_config;
120
121    // Note: since we're testing a big circuit, this test takes a bit more of computation and time,
122    // do not run in the normal CI.
123    // To run the test use `--ignored` flag, eg. `cargo test -- --ignored`
124    #[test]
125    #[ignore]
126    fn test_decider() {
127        type NOVA = Nova<
128            Projective,
129            GVar,
130            Projective2,
131            GVar2,
132            CubicFCircuit<Fr>,
133            Pedersen<Projective>,
134            Pedersen<Projective2>,
135        >;
136        type DECIDER = Decider<
137            Projective,
138            GVar,
139            Projective2,
140            GVar2,
141            CubicFCircuit<Fr>,
142            Pedersen<Projective>,
143            Pedersen<Projective2>,
144            Groth16<MNT4_298>, // here we define the Snark to use in the decider
145            NOVA,              // here we define the FoldingScheme to use
146        >;
147
148        let mut rng = ark_std::test_rng();
149        let poseidon_config = poseidon_test_config::<Fr>();
150
151        let F_circuit = CubicFCircuit::<Fr>::new(());
152        let z_0 = vec![Fr::from(3_u32)];
153
154        let (cm_len, cf_cm_len) =
155            get_pedersen_params_len::<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>>(
156                &poseidon_config,
157                F_circuit,
158            )
159            .unwrap();
160        let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, cm_len);
161        let cf_pedersen_params = Pedersen::<Projective2>::new_params(&mut rng, cf_cm_len);
162
163        let start = Instant::now();
164        let prover_params =
165            ProverParams::<Projective, Projective2, Pedersen<Projective>, Pedersen<Projective2>> {
166                poseidon_config: poseidon_config.clone(),
167                cm_params: pedersen_params,
168                cf_cm_params: cf_pedersen_params,
169            };
170        println!("generating pedersen params, {:?}", start.elapsed());
171
172        // use Nova as FoldingScheme
173        let start = Instant::now();
174        let mut nova = NOVA::init(&prover_params, F_circuit, z_0.clone()).unwrap();
175        println!("Nova initialized, {:?}", start.elapsed());
176        let start = Instant::now();
177        nova.prove_step().unwrap();
178        println!("prove_step, {:?}", start.elapsed());
179
180        // generate Groth16 setup
181        let circuit = DeciderEthCircuit::<
182            Projective,
183            GVar,
184            Projective2,
185            GVar2,
186            Pedersen<Projective>,
187            Pedersen<Projective2>,
188        >::from_nova::<CubicFCircuit<Fr>>(nova.clone());
189        let mut rng = rand::rngs::OsRng;
190
191        let start = Instant::now();
192        let (pk, vk) =
193            Groth16::<MNT4_298>::circuit_specific_setup(circuit.clone(), &mut rng).unwrap();
194        println!("Groth16 setup, {:?}", start.elapsed());
195
196        // decider proof generation
197        let start = Instant::now();
198        let proof = DECIDER::prove(&pk, rng, nova.clone()).unwrap();
199        println!("Decider Groth16 prove, {:?}", start.elapsed());
200
201        // decider proof verification
202        let verified = DECIDER::verify(&vk, nova.i, nova.z_0, nova.z_i, &nova.U_i, proof).unwrap();
203        assert!(verified);
204    }
205}