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