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::fields::nonnative::params::OptimizationType;
  6use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldGadget};
  7use ark_snark::SNARK;
  8use ark_std::rand::{CryptoRng, RngCore};
  9use ark_std::Zero;
 10use core::marker::PhantomData;
 11
 12pub use super::decider_eth_circuit::{DeciderEthCircuit, KZGChallengesGadget};
 13use super::{circuits::CF2, nifs::NIFS, CommittedInstance, Nova};
 14use crate::commitment::{
 15    kzg::Proof as KZGProof, pedersen::Params as PedersenParams, CommitmentScheme,
 16};
 17use crate::folding::circuits::nonnative::point_to_nonnative_limbs_custom_opt;
 18use crate::frontend::FCircuit;
 19use crate::Error;
 20use crate::{Decider as DeciderTrait, FoldingScheme};
 21
 22#[derive(Debug, Clone, Eq, PartialEq)]
 23pub struct Proof<C1, CS1, S>
 24where
 25    C1: CurveGroup,
 26    CS1: CommitmentScheme<C1, ProverChallenge = C1::ScalarField, Challenge = C1::ScalarField>,
 27    S: SNARK<C1::ScalarField>,
 28{
 29    snark_proof: S::Proof,
 30    kzg_proofs: [CS1::Proof; 2],
 31    // cmT and r are values for the last fold, U_{i+1}=NIFS.V(r, U_i, u_i, cmT), and they are
 32    // checked in-circuit
 33    cmT: C1,
 34    r: C1::ScalarField,
 35    // the KZG challenges are provided by the prover, but in-circuit they are checked to match
 36    // the in-circuit computed computed ones.
 37    kzg_challenges: [C1::ScalarField; 2],
 38}
 39
 40/// Onchain Decider, for ethereum use cases
 41#[derive(Clone, Debug)]
 42pub struct Decider<C1, GC1, C2, GC2, FC, CS1, CS2, S, FS> {
 43    _c1: PhantomData<C1>,
 44    _gc1: PhantomData<GC1>,
 45    _c2: PhantomData<C2>,
 46    _gc2: PhantomData<GC2>,
 47    _fc: PhantomData<FC>,
 48    _cs1: PhantomData<CS1>,
 49    _cs2: PhantomData<CS2>,
 50    _s: PhantomData<S>,
 51    _fs: PhantomData<FS>,
 52}
 53
 54impl<C1, GC1, C2, GC2, FC, CS1, CS2, S, FS> DeciderTrait<C1, C2, FC, FS>
 55    for Decider<C1, GC1, C2, GC2, FC, CS1, CS2, S, FS>
 56where
 57    C1: CurveGroup,
 58    C2: CurveGroup,
 59    GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
 60    GC2: CurveVar<C2, CF2<C2>>,
 61    FC: FCircuit<C1::ScalarField>,
 62    CS1: CommitmentScheme<
 63        C1,
 64        ProverChallenge = C1::ScalarField,
 65        Challenge = C1::ScalarField,
 66        Proof = KZGProof<C1>,
 67    >, // KZG commitment, where challenge is C1::Fr elem
 68    // enforce that the CS2 is Pedersen commitment scheme, since we're at Ethereum's EVM decider
 69    CS2: CommitmentScheme<C2, ProverParams = PedersenParams<C2>>,
 70    S: SNARK<C1::ScalarField>,
 71    FS: FoldingScheme<C1, C2, FC>,
 72    <C1 as CurveGroup>::BaseField: PrimeField,
 73    <C2 as CurveGroup>::BaseField: PrimeField,
 74    <C1 as Group>::ScalarField: Absorb,
 75    <C2 as Group>::ScalarField: Absorb,
 76    C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,
 77    for<'b> &'b GC2: GroupOpsBounds<'b, C2, GC2>,
 78    // constrain FS into Nova, since this is a Decider specifically for Nova
 79    Nova<C1, GC1, C2, GC2, FC, CS1, CS2>: From<FS>,
 80{
 81    type ProverParam = (S::ProvingKey, CS1::ProverParams);
 82    type Proof = Proof<C1, CS1, S>;
 83    type VerifierParam = (S::VerifyingKey, CS1::VerifierParams);
 84    type PublicInput = Vec<C1::ScalarField>;
 85    type CommittedInstanceWithWitness = ();
 86    type CommittedInstance = CommittedInstance<C1>;
 87
 88    fn prove(
 89        pp: Self::ProverParam,
 90        mut rng: impl RngCore + CryptoRng,
 91        folding_scheme: FS,
 92    ) -> Result<Self::Proof, Error> {
 93        let (snark_pk, cs_pk): (S::ProvingKey, CS1::ProverParams) = pp;
 94
 95        let circuit = DeciderEthCircuit::<C1, GC1, C2, GC2, CS1, CS2>::from_nova::<FC>(
 96            folding_scheme.into(),
 97        )?;
 98
 99        let snark_proof = S::prove(&snark_pk, circuit.clone(), &mut rng)
100            .map_err(|e| Error::Other(e.to_string()))?;
101
102        let cmT = circuit.cmT.unwrap();
103        let r_Fr = circuit.r.unwrap();
104        let W_i1 = circuit.W_i1.unwrap();
105
106        // get the challenges that have been already computed when preparing the circuit inputs in
107        // the above `from_nova` call
108        let challenge_W = circuit
109            .kzg_c_W
110            .ok_or(Error::MissingValue("kzg_c_W".to_string()))?;
111        let challenge_E = circuit
112            .kzg_c_E
113            .ok_or(Error::MissingValue("kzg_c_E".to_string()))?;
114
115        // generate KZG proofs
116        let U_cmW_proof = CS1::prove_with_challenge(
117            &cs_pk,
118            challenge_W,
119            &W_i1.W,
120            &C1::ScalarField::zero(),
121            None,
122        )?;
123        let U_cmE_proof = CS1::prove_with_challenge(
124            &cs_pk,
125            challenge_E,
126            &W_i1.E,
127            &C1::ScalarField::zero(),
128            None,
129        )?;
130
131        Ok(Self::Proof {
132            snark_proof,
133            kzg_proofs: [U_cmW_proof, U_cmE_proof],
134            cmT,
135            r: r_Fr,
136            kzg_challenges: [challenge_W, challenge_E],
137        })
138    }
139
140    fn verify(
141        vp: Self::VerifierParam,
142        i: C1::ScalarField,
143        z_0: Vec<C1::ScalarField>,
144        z_i: Vec<C1::ScalarField>,
145        running_instance: &Self::CommittedInstance,
146        incoming_instance: &Self::CommittedInstance,
147        proof: Self::Proof,
148    ) -> Result<bool, Error> {
149        let (snark_vk, cs_vk): (S::VerifyingKey, CS1::VerifierParams) = vp;
150
151        // compute U = U_{d+1}= NIFS.V(U_d, u_d, cmT)
152        let U = NIFS::<C1, CS1>::verify(proof.r, running_instance, incoming_instance, &proof.cmT);
153
154        let (cmE_x, cmE_y) =
155            point_to_nonnative_limbs_custom_opt::<C1>(U.cmE, OptimizationType::Constraints)?;
156        let (cmW_x, cmW_y) =
157            point_to_nonnative_limbs_custom_opt::<C1>(U.cmW, OptimizationType::Constraints)?;
158        let (cmT_x, cmT_y) =
159            point_to_nonnative_limbs_custom_opt::<C1>(proof.cmT, OptimizationType::Constraints)?;
160
161        let public_input: Vec<C1::ScalarField> = vec![
162            vec![i],
163            z_0,
164            z_i,
165            vec![U.u],
166            U.x.clone(),
167            cmE_x,
168            cmE_y,
169            cmW_x,
170            cmW_y,
171            proof.kzg_challenges.to_vec(),
172            vec![
173                proof.kzg_proofs[0].eval, // eval_W
174                proof.kzg_proofs[1].eval, // eval_E
175            ],
176            cmT_x,
177            cmT_y,
178            vec![proof.r],
179        ]
180        .concat();
181
182        let snark_v = S::verify(&snark_vk, &public_input, &proof.snark_proof)
183            .map_err(|e| Error::Other(e.to_string()))?;
184        if !snark_v {
185            return Err(Error::SNARKVerificationFail);
186        }
187
188        // we're at the Ethereum EVM case, so the CS1 is KZG commitments
189        CS1::verify_with_challenge(
190            &cs_vk,
191            proof.kzg_challenges[0],
192            &U.cmW,
193            &proof.kzg_proofs[0],
194        )?;
195        CS1::verify_with_challenge(
196            &cs_vk,
197            proof.kzg_challenges[1],
198            &U.cmE,
199            &proof.kzg_proofs[1],
200        )?;
201
202        Ok(true)
203    }
204}
205
206#[cfg(test)]
207pub mod tests {
208    use super::*;
209    use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective};
210    use ark_groth16::Groth16;
211    use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2};
212    use ark_poly_commit::kzg10::VerifierKey as KZGVerifierKey;
213    use std::time::Instant;
214
215    use crate::commitment::kzg::{ProverKey as KZGProverKey, KZG};
216    use crate::commitment::pedersen::Pedersen;
217    use crate::folding::nova::{get_cs_params_len, ProverParams};
218    use crate::frontend::tests::CubicFCircuit;
219    use crate::transcript::poseidon::poseidon_test_config;
220
221    #[test]
222    fn test_decider() {
223        // use Nova as FoldingScheme
224        type NOVA = Nova<
225            Projective,
226            GVar,
227            Projective2,
228            GVar2,
229            CubicFCircuit<Fr>,
230            KZG<'static, Bn254>,
231            Pedersen<Projective2>,
232        >;
233        type DECIDER = Decider<
234            Projective,
235            GVar,
236            Projective2,
237            GVar2,
238            CubicFCircuit<Fr>,
239            KZG<'static, Bn254>,
240            Pedersen<Projective2>,
241            Groth16<Bn254>, // here we define the Snark to use in the decider
242            NOVA,           // here we define the FoldingScheme to use
243        >;
244
245        let mut rng = ark_std::test_rng();
246        let poseidon_config = poseidon_test_config::<Fr>();
247
248        let F_circuit = CubicFCircuit::<Fr>::new(());
249        let z_0 = vec![Fr::from(3_u32)];
250
251        let (cs_len, cf_cs_len) =
252            get_cs_params_len::<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>>(
253                &poseidon_config,
254                F_circuit,
255            )
256            .unwrap();
257        let start = Instant::now();
258        let (kzg_pk, kzg_vk): (KZGProverKey<Projective>, KZGVerifierKey<Bn254>) =
259            KZG::<Bn254>::setup(&mut rng, cs_len).unwrap();
260        let (cf_pedersen_params, _) = Pedersen::<Projective2>::setup(&mut rng, cf_cs_len).unwrap();
261        println!("generated KZG params, {:?}", start.elapsed());
262
263        let prover_params =
264            ProverParams::<Projective, Projective2, KZG<Bn254>, Pedersen<Projective2>> {
265                poseidon_config: poseidon_config.clone(),
266                cs_params: kzg_pk.clone(),
267                cf_cs_params: cf_pedersen_params,
268            };
269
270        let start = Instant::now();
271        let mut nova = NOVA::init(&prover_params, F_circuit, z_0.clone()).unwrap();
272        println!("Nova initialized, {:?}", start.elapsed());
273        let start = Instant::now();
274        nova.prove_step().unwrap();
275        println!("prove_step, {:?}", start.elapsed());
276        nova.prove_step().unwrap(); // do a 2nd step
277
278        // generate Groth16 setup
279        let circuit = DeciderEthCircuit::<
280            Projective,
281            GVar,
282            Projective2,
283            GVar2,
284            KZG<Bn254>,
285            Pedersen<Projective2>,
286        >::from_nova::<CubicFCircuit<Fr>>(nova.clone())
287        .unwrap();
288        let mut rng = rand::rngs::OsRng;
289
290        let start = Instant::now();
291        let (g16_pk, g16_vk) =
292            Groth16::<Bn254>::circuit_specific_setup(circuit.clone(), &mut rng).unwrap();
293        println!("Groth16 setup, {:?}", start.elapsed());
294
295        // decider proof generation
296        let start = Instant::now();
297        let decider_pp = (g16_pk, kzg_pk);
298        let proof = DECIDER::prove(decider_pp, rng, nova.clone()).unwrap();
299        println!("Decider prove, {:?}", start.elapsed());
300
301        // decider proof verification
302        let start = Instant::now();
303        let decider_vp = (g16_vk, kzg_vk);
304        let verified = DECIDER::verify(
305            decider_vp, nova.i, nova.z_0, nova.z_i, &nova.U_i, &nova.u_i, proof,
306        )
307        .unwrap();
308        assert!(verified);
309        println!("Decider verify, {:?}", start.elapsed());
310    }
311}