/// This file implements the onchain (Ethereum's EVM) decider. use ark_crypto_primitives::sponge::Absorb; use ark_ec::{CurveGroup, Group}; use ark_ff::PrimeField; use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar}; use ark_snark::SNARK; use ark_std::rand::CryptoRng; use ark_std::rand::RngCore; use core::marker::PhantomData; pub use super::decider_eth_circuit::DeciderEthCircuit; use crate::commitment::{pedersen::Params as PedersenParams, CommitmentProver}; use crate::folding::circuits::nonnative::point_to_nonnative_limbs_custom_opt; use crate::folding::nova::{circuits::CF2, CommittedInstance, Nova}; use crate::frontend::FCircuit; use crate::Error; use crate::{Decider as DeciderTrait, FoldingScheme}; use ark_r1cs_std::fields::nonnative::params::OptimizationType; /// Onchain Decider, for ethereum use cases #[derive(Clone, Debug)] pub struct Decider { _c1: PhantomData, _gc1: PhantomData, _c2: PhantomData, _gc2: PhantomData, _fc: PhantomData, _cp1: PhantomData, _cp2: PhantomData, _s: PhantomData, _fs: PhantomData, } impl DeciderTrait for Decider where C1: CurveGroup, C2: CurveGroup, GC1: CurveVar>, GC2: CurveVar>, FC: FCircuit, CP1: CommitmentProver, // enforce that the CP2 is Pedersen commitment, since we're at Ethereum's EVM decider CP2: CommitmentProver>, S: SNARK, FS: FoldingScheme, ::BaseField: PrimeField, ::BaseField: PrimeField, ::ScalarField: Absorb, ::ScalarField: Absorb, C1: CurveGroup, for<'b> &'b GC2: GroupOpsBounds<'b, C2, GC2>, // constrain FS into Nova, since this is a Decider specifically for Nova Nova: From, { type ProverParam = S::ProvingKey; type Proof = S::Proof; type VerifierParam = S::VerifyingKey; type PublicInput = Vec; type CommittedInstanceWithWitness = (); type CommittedInstance = CommittedInstance; fn prove( pp: &Self::ProverParam, mut rng: impl RngCore + CryptoRng, folding_scheme: FS, ) -> Result { let circuit = DeciderEthCircuit::::from_nova::(folding_scheme.into()); S::prove(pp, circuit.clone(), &mut rng).map_err(|e| Error::Other(e.to_string())) } fn verify( vp: &Self::VerifierParam, i: C1::ScalarField, z_0: Vec, z_i: Vec, running_instance: &Self::CommittedInstance, proof: Self::Proof, ) -> Result { let (cmE_x, cmE_y) = point_to_nonnative_limbs_custom_opt::( running_instance.cmE, OptimizationType::Constraints, )?; let (cmW_x, cmW_y) = point_to_nonnative_limbs_custom_opt::( running_instance.cmW, OptimizationType::Constraints, )?; let public_input: Vec = vec![ vec![i], z_0, z_i, vec![running_instance.u], running_instance.x.clone(), cmE_x, cmE_y, cmW_x, cmW_y, ] .concat(); S::verify(vp, &public_input, &proof).map_err(|e| Error::Other(e.to_string())) } } #[cfg(test)] pub mod tests { use super::*; use ark_groth16::Groth16; use ark_mnt4_298::{constraints::G1Var as GVar, Fr, G1Projective as Projective, MNT4_298}; use ark_mnt6_298::{constraints::G1Var as GVar2, G1Projective as Projective2}; use std::time::Instant; use crate::commitment::pedersen::Pedersen; use crate::folding::nova::{get_pedersen_params_len, ProverParams}; use crate::frontend::tests::CubicFCircuit; use crate::transcript::poseidon::poseidon_test_config; // Note: since we're testing a big circuit, this test takes a bit more of computation and time, // do not run in the normal CI. // To run the test use `--ignored` flag, eg. `cargo test -- --ignored` #[test] #[ignore] fn test_decider() { type NOVA = Nova< Projective, GVar, Projective2, GVar2, CubicFCircuit, Pedersen, Pedersen, >; type DECIDER = Decider< Projective, GVar, Projective2, GVar2, CubicFCircuit, Pedersen, Pedersen, Groth16, // here we define the Snark to use in the decider NOVA, // here we define the FoldingScheme to use >; let mut rng = ark_std::test_rng(); let poseidon_config = poseidon_test_config::(); let F_circuit = CubicFCircuit::::new(()); let z_0 = vec![Fr::from(3_u32)]; let (cm_len, cf_cm_len) = get_pedersen_params_len::>( &poseidon_config, F_circuit, ) .unwrap(); let pedersen_params = Pedersen::::new_params(&mut rng, cm_len); let cf_pedersen_params = Pedersen::::new_params(&mut rng, cf_cm_len); let start = Instant::now(); let prover_params = ProverParams::, Pedersen> { poseidon_config: poseidon_config.clone(), cm_params: pedersen_params, cf_cm_params: cf_pedersen_params, }; println!("generating pedersen params, {:?}", start.elapsed()); // use Nova as FoldingScheme let start = Instant::now(); let mut nova = NOVA::init(&prover_params, F_circuit, z_0.clone()).unwrap(); println!("Nova initialized, {:?}", start.elapsed()); let start = Instant::now(); nova.prove_step().unwrap(); println!("prove_step, {:?}", start.elapsed()); // generate Groth16 setup let circuit = DeciderEthCircuit::< Projective, GVar, Projective2, GVar2, Pedersen, Pedersen, >::from_nova::>(nova.clone()); let mut rng = rand::rngs::OsRng; let start = Instant::now(); let (pk, vk) = Groth16::::circuit_specific_setup(circuit.clone(), &mut rng).unwrap(); println!("Groth16 setup, {:?}", start.elapsed()); // decider proof generation let start = Instant::now(); let proof = DECIDER::prove(&pk, rng, nova.clone()).unwrap(); println!("Decider Groth16 prove, {:?}", start.elapsed()); // decider proof verification let verified = DECIDER::verify(&vk, nova.i, nova.z_0, nova.z_i, &nova.U_i, proof).unwrap(); assert!(verified); } }