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 specifically 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        S::prove(pp, circuit.clone(), &mut rng).map_err(|e| Error::Other(e.to_string()))
 72    }
 73
 74    fn verify(
 75        vp: &Self::VerifierParam,
 76        i: C1::ScalarField,
 77        z_0: Vec<C1::ScalarField>,
 78        z_i: Vec<C1::ScalarField>,
 79        running_instance: &Self::CommittedInstance,
 80        proof: Self::Proof,
 81    ) -> Result<bool, Error> {
 82        let (cmE_x, cmE_y) = point_to_nonnative_limbs_custom_opt::<C1>(
 83            running_instance.cmE,
 84            OptimizationType::Constraints,
 85        )?;
 86        let (cmW_x, cmW_y) = point_to_nonnative_limbs_custom_opt::<C1>(
 87            running_instance.cmW,
 88            OptimizationType::Constraints,
 89        )?;
 90        let public_input: Vec<C1::ScalarField> = vec![
 91            vec![i],
 92            z_0,
 93            z_i,
 94            vec![running_instance.u],
 95            running_instance.x.clone(),
 96            cmE_x,
 97            cmE_y,
 98            cmW_x,
 99            cmW_y,
100        ]
101        .concat();
102        S::verify(vp, &public_input, &proof).map_err(|e| Error::Other(e.to_string()))
103    }
104}
105
106#[cfg(test)]
107pub mod tests {
108    use super::*;
109    use ark_groth16::Groth16;
110    use ark_mnt4_298::{constraints::G1Var as GVar, Fr, G1Projective as Projective, MNT4_298};
111    use ark_mnt6_298::{constraints::G1Var as GVar2, G1Projective as Projective2};
112    use std::time::Instant;
113
114    use crate::commitment::pedersen::Pedersen;
115    use crate::folding::nova::{get_pedersen_params_len, ProverParams};
116    use crate::frontend::tests::CubicFCircuit;
117    use crate::transcript::poseidon::poseidon_test_config;
118
119    // Note: since we're testing a big circuit, this test takes a bit more of computation and time,
120    // do not run in the normal CI.
121    // To run the test use `--ignored` flag, eg. `cargo test -- --ignored`
122    #[test]
123    #[ignore]
124    fn test_decider() {
125        type NOVA = Nova<
126            Projective,
127            GVar,
128            Projective2,
129            GVar2,
130            CubicFCircuit<Fr>,
131            Pedersen<Projective>,
132            Pedersen<Projective2>,
133        >;
134        type DECIDER = Decider<
135            Projective,
136            GVar,
137            Projective2,
138            GVar2,
139            CubicFCircuit<Fr>,
140            Pedersen<Projective>,
141            Pedersen<Projective2>,
142            Groth16<MNT4_298>, // here we define the Snark to use in the decider
143            NOVA,              // here we define the FoldingScheme to use
144        >;
145
146        let mut rng = ark_std::test_rng();
147        let poseidon_config = poseidon_test_config::<Fr>();
148
149        let F_circuit = CubicFCircuit::<Fr>::new(());
150        let z_0 = vec![Fr::from(3_u32)];
151
152        let (cm_len, cf_cm_len) =
153            get_pedersen_params_len::<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>>(
154                &poseidon_config,
155                F_circuit,
156            )
157            .unwrap();
158        let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, cm_len);
159        let cf_pedersen_params = Pedersen::<Projective2>::new_params(&mut rng, cf_cm_len);
160
161        let start = Instant::now();
162        let prover_params =
163            ProverParams::<Projective, Projective2, Pedersen<Projective>, Pedersen<Projective2>> {
164                poseidon_config: poseidon_config.clone(),
165                cm_params: pedersen_params,
166                cf_cm_params: cf_pedersen_params,
167            };
168        println!("generating pedersen params, {:?}", start.elapsed());
169
170        // use Nova as FoldingScheme
171        let start = Instant::now();
172        let mut nova = NOVA::init(&prover_params, F_circuit, z_0.clone()).unwrap();
173        println!("Nova initialized, {:?}", start.elapsed());
174        let start = Instant::now();
175        nova.prove_step().unwrap();
176        println!("prove_step, {:?}", start.elapsed());
177
178        // generate Groth16 setup
179        let circuit = DeciderEthCircuit::<
180            Projective,
181            GVar,
182            Projective2,
183            GVar2,
184            Pedersen<Projective>,
185            Pedersen<Projective2>,
186        >::from_nova::<CubicFCircuit<Fr>>(nova.clone());
187        let mut rng = rand::rngs::OsRng;
188
189        let start = Instant::now();
190        let (pk, vk) =
191            Groth16::<MNT4_298>::circuit_specific_setup(circuit.clone(), &mut rng).unwrap();
192        println!("Groth16 setup, {:?}", start.elapsed());
193
194        // decider proof generation
195        let start = Instant::now();
196        let proof = DECIDER::prove(&pk, rng, nova.clone()).unwrap();
197        println!("Decider Groth16 prove, {:?}", start.elapsed());
198
199        // decider proof verification
200        let verified = DECIDER::verify(&vk, nova.i, nova.z_0, nova.z_i, &nova.U_i, proof).unwrap();
201        assert!(verified);
202    }
203}