diff --git a/folding-schemes/Cargo.toml b/folding-schemes/Cargo.toml index 2fbbc3b3..4a77a6f2 100644 --- a/folding-schemes/Cargo.toml +++ b/folding-schemes/Cargo.toml @@ -41,6 +41,8 @@ ark-pallas = {version="0.4.0", features=["r1cs"]} ark-vesta = {version="0.4.0", features=["r1cs"]} ark-bn254 = {version="0.4.0", features=["r1cs"]} ark-grumpkin = {version="0.4.0", features=["r1cs"]} +ark-mnt4-298 = {version="0.4.0", features=["r1cs"]} +ark-mnt6-298 = {version="0.4.0", features=["r1cs"]} rand = "0.8.5" tracing = { version = "0.1", default-features = false, features = [ "attributes" ] } tracing-subscriber = { version = "0.2" } diff --git a/folding-schemes/src/folding/circuits/cyclefold.rs b/folding-schemes/src/folding/circuits/cyclefold.rs index dc22061a..3cb3402e 100644 --- a/folding-schemes/src/folding/circuits/cyclefold.rs +++ b/folding-schemes/src/folding/circuits/cyclefold.rs @@ -61,8 +61,10 @@ where f().and_then(|val| { let cs = cs.into(); - let u = NonNativeUintVar::new_variable(cs.clone(), || Ok(val.borrow().u), mode)?; - let x = Vec::new_variable(cs.clone(), || Ok(val.borrow().x.clone()), mode)?; + let u = + NonNativeUintVar::>::new_variable(cs.clone(), || Ok(val.borrow().u), mode)?; + let x: Vec>> = + Vec::new_variable(cs.clone(), || Ok(val.borrow().x.clone()), mode)?; let cmE = GC::new_variable(cs.clone(), || Ok(val.borrow().cmE), mode)?; let cmW = GC::new_variable(cs.clone(), || Ok(val.borrow().cmW), mode)?; diff --git a/folding-schemes/src/folding/nova/decider.rs b/folding-schemes/src/folding/nova/decider.rs new file mode 100644 index 00000000..f528f97e --- /dev/null +++ b/folding-schemes/src/folding/nova/decider.rs @@ -0,0 +1,496 @@ +/// This file implements the offchain decider. For ethereum use cases, use the +/// DeciderEth from decider_eth.rs file. +/// More details can be found at the documentation page: +/// https://privacy-scaling-explorations.github.io/sonobe-docs/design/nova-decider-offchain.html +use ark_crypto_primitives::sponge::Absorb; +use ark_ec::{AffineRepr, CurveGroup, Group}; +use ark_ff::{BigInteger, PrimeField}; +use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldGadget}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_snark::SNARK; +use ark_std::rand::{CryptoRng, RngCore}; +use ark_std::{One, Zero}; +use core::marker::PhantomData; + +use super::decider_circuits::{DeciderCircuit1, DeciderCircuit2}; +use super::{nifs::NIFS, CommittedInstance, Nova}; +use crate::commitment::CommitmentScheme; +use crate::folding::circuits::{ + cyclefold::CycleFoldCommittedInstance, + nonnative::{affine::NonNativeAffineVar, uint::NonNativeUintVar}, + CF2, +}; +use crate::frontend::FCircuit; +use crate::Error; +use crate::{Decider as DeciderTrait, FoldingScheme}; + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct Proof +where + C1: CurveGroup, + C2: CurveGroup, + CS1: CommitmentScheme, + CS2: CommitmentScheme, + S1: SNARK, + S2: SNARK, +{ + c1_snark_proof: S1::Proof, + c2_snark_proof: S2::Proof, + cs1_proofs: [CS1::Proof; 2], + cs2_proofs: [CS2::Proof; 2], + // cmT and r are values for the last fold, U_{i+1}=NIFS.V(r, U_i, u_i, cmT), and they are + // checked in-circuit + cmT: C1, + r: C1::ScalarField, + // cyclefold committed instance + cf_U_i: CycleFoldCommittedInstance, + // the CS challenges are provided by the prover, but in-circuit they are checked to match the + // in-circuit computed computed ones. + cs1_challenges: [C1::ScalarField; 2], + cs2_challenges: [C2::ScalarField; 2], +} + +#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)] +pub struct ProverParam +where + CS1_ProvingKey: Clone + CanonicalSerialize + CanonicalDeserialize, + S1_ProvingKey: Clone + CanonicalSerialize + CanonicalDeserialize, + CS2_ProvingKey: Clone + CanonicalSerialize + CanonicalDeserialize, + S2_ProvingKey: Clone + CanonicalSerialize + CanonicalDeserialize, +{ + pub c1_snark_pp: S1_ProvingKey, + pub c1_cs_pp: CS1_ProvingKey, + pub c2_snark_pp: S2_ProvingKey, + pub c2_cs_pp: CS2_ProvingKey, +} + +#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)] +pub struct VerifierParam +where + C1: CurveGroup, + CS1_VerifyingKey: Clone + CanonicalSerialize + CanonicalDeserialize, + S1_VerifyingKey: Clone + CanonicalSerialize + CanonicalDeserialize, + CS2_VerifyingKey: Clone + CanonicalSerialize + CanonicalDeserialize, + S2_VerifyingKey: Clone + CanonicalSerialize + CanonicalDeserialize, +{ + pub pp_hash: C1::ScalarField, + pub c1_snark_vp: S1_VerifyingKey, + pub c1_cs_vp: CS1_VerifyingKey, + pub c2_snark_vp: S2_VerifyingKey, + pub c2_cs_vp: CS2_VerifyingKey, +} + +/// Onchain Decider, for ethereum use cases +#[derive(Clone, Debug)] +pub struct Decider { + _c1: PhantomData, + _gc1: PhantomData, + _c2: PhantomData, + _gc2: PhantomData, + _fc: PhantomData, + _cs1: PhantomData, + _cs2: PhantomData, + _s1: PhantomData, + _s2: PhantomData, + _fs: PhantomData, +} + +impl DeciderTrait + for Decider +where + C1: CurveGroup, + C2: CurveGroup, + GC1: CurveVar> + ToConstraintFieldGadget>, + GC2: CurveVar> + ToConstraintFieldGadget>, + FC: FCircuit, + CS1: CommitmentScheme< + C1, + ProverChallenge = C1::ScalarField, + Challenge = C1::ScalarField, + Proof = crate::commitment::kzg::Proof, + >, + CS2: CommitmentScheme< + C2, + ProverChallenge = C2::ScalarField, + Challenge = C2::ScalarField, + Proof = crate::commitment::kzg::Proof, + >, + S1: SNARK, + S2: SNARK, + FS: FoldingScheme, + ::BaseField: PrimeField, + ::BaseField: PrimeField, + ::ScalarField: Absorb, + ::ScalarField: Absorb, + C1: CurveGroup, + for<'b> &'b GC1: GroupOpsBounds<'b, C1, GC1>, + for<'b> &'b GC2: GroupOpsBounds<'b, C2, GC2>, + // constrain FS into Nova, since this is a Decider specifically for Nova + Nova: From, + crate::folding::nova::ProverParams: + From<>::ProverParam>, + crate::folding::nova::VerifierParams: + From<>::VerifierParam>, +{ + type PreprocessorParam = (FS::ProverParam, FS::VerifierParam); + type ProverParam = + ProverParam; + type Proof = Proof; + type VerifierParam = VerifierParam< + C1, + CS1::VerifierParams, + S1::VerifyingKey, + CS2::VerifierParams, + S2::VerifyingKey, + >; + type PublicInput = Vec; + type CommittedInstance = CommittedInstance; + + fn preprocess( + mut rng: impl RngCore + CryptoRng, + prep_param: Self::PreprocessorParam, + fs: FS, + ) -> Result<(Self::ProverParam, Self::VerifierParam), Error> { + let circuit1 = DeciderCircuit1::::from_nova::( + fs.clone().into(), + )?; + let circuit2 = + DeciderCircuit2::::from_nova::(fs.into())?; + + // get the Groth16 specific setup for the circuits + let (c1_g16_pk, c1_g16_vk) = S1::circuit_specific_setup(circuit1, &mut rng).unwrap(); + let (c2_g16_pk, c2_g16_vk) = S2::circuit_specific_setup(circuit2, &mut rng).unwrap(); + + // get the FoldingScheme prover & verifier params from Nova + #[allow(clippy::type_complexity)] + let nova_pp: as FoldingScheme< + C1, + C2, + FC, + >>::ProverParam = prep_param.0.clone().into(); + #[allow(clippy::type_complexity)] + let nova_vp: as FoldingScheme< + C1, + C2, + FC, + >>::VerifierParam = prep_param.1.clone().into(); + + let pp_hash = nova_vp.pp_hash()?; + let pp = Self::ProverParam { + c1_snark_pp: c1_g16_pk, + c1_cs_pp: nova_pp.cs_pp, + c2_snark_pp: c2_g16_pk, + c2_cs_pp: nova_pp.cf_cs_pp, + }; + let vp = Self::VerifierParam { + pp_hash, + c1_snark_vp: c1_g16_vk, + c1_cs_vp: nova_vp.cs_vp, + c2_snark_vp: c2_g16_vk, + c2_cs_vp: nova_vp.cf_cs_vp, + }; + Ok((pp, vp)) + } + + fn prove( + mut rng: impl RngCore + CryptoRng, + pp: Self::ProverParam, + fs: FS, + ) -> Result { + let circuit1 = DeciderCircuit1::::from_nova::( + fs.clone().into(), + )?; + let circuit2 = + DeciderCircuit2::::from_nova::(fs.into())?; + + let c1_snark_proof = S1::prove(&pp.c1_snark_pp, circuit1.clone(), &mut rng) + .map_err(|e| Error::Other(e.to_string()))?; + let c2_snark_proof = S2::prove(&pp.c2_snark_pp, circuit2.clone(), &mut rng) + .map_err(|e| Error::Other(e.to_string()))?; + + let cmT = circuit1.cmT.unwrap(); + let r_Fr = circuit1.r.unwrap(); + let W_i1 = circuit1.W_i1.unwrap(); + let cf_W_i = circuit2.cf_W_i.unwrap(); + + // get the challenges that have been already computed when preparing the circuits inputs in + // the above `from_nova` calls + let challenge_W = circuit1 + .cs_c_W + .ok_or(Error::MissingValue("cs_c_W".to_string()))?; + let challenge_E = circuit1 + .cs_c_E + .ok_or(Error::MissingValue("cs_c_E".to_string()))?; + let c2_challenge_W = circuit2 + .cs_c_W + .ok_or(Error::MissingValue("c2's cs_c_W".to_string()))?; + let c2_challenge_E = circuit2 + .cs_c_E + .ok_or(Error::MissingValue("c2's cs_c_E".to_string()))?; + + // generate CommitmentScheme proofs for the main instance + let U_cmW_proof = CS1::prove_with_challenge( + &pp.c1_cs_pp, + challenge_W, + &W_i1.W, + &C1::ScalarField::zero(), + None, + )?; + let U_cmE_proof = CS1::prove_with_challenge( + &pp.c1_cs_pp, + challenge_E, + &W_i1.E, + &C1::ScalarField::zero(), + None, + )?; + // CS proofs for the CycleFold instance + let cf_cmW_proof = CS2::prove_with_challenge( + &pp.c2_cs_pp, + c2_challenge_W, + &cf_W_i.W, + &C2::ScalarField::zero(), + None, + )?; + let cf_cmE_proof = CS2::prove_with_challenge( + &pp.c2_cs_pp, + c2_challenge_E, + &cf_W_i.E, + &C2::ScalarField::zero(), + None, + )?; + + Ok(Self::Proof { + c1_snark_proof, + c2_snark_proof, + cs1_proofs: [U_cmW_proof, U_cmE_proof], + cs2_proofs: [cf_cmW_proof, cf_cmE_proof], + cmT, + r: r_Fr, + cf_U_i: circuit1.cf_U_i.unwrap(), + cs1_challenges: [challenge_W, challenge_E], + cs2_challenges: [c2_challenge_W, c2_challenge_E], + }) + } + + fn verify( + vp: Self::VerifierParam, + i: C1::ScalarField, + z_0: Vec, + z_i: Vec, + running_instance: &Self::CommittedInstance, + incoming_instance: &Self::CommittedInstance, + proof: &Self::Proof, + ) -> Result { + if i <= C1::ScalarField::one() { + return Err(Error::NotEnoughSteps); + } + + // compute U = U_{d+1}= NIFS.V(U_d, u_d, cmT) + let U = NIFS::::verify(proof.r, running_instance, incoming_instance, &proof.cmT); + + let (cmE_x, cmE_y) = NonNativeAffineVar::inputize(U.cmE)?; + let (cmW_x, cmW_y) = NonNativeAffineVar::inputize(U.cmW)?; + let (cmT_x, cmT_y) = NonNativeAffineVar::inputize(proof.cmT)?; + + let zero = (&C2::BaseField::zero(), &C2::BaseField::zero()); + let cmE_affine = proof.cf_U_i.cmE.into_affine(); + let cmW_affine = proof.cf_U_i.cmW.into_affine(); + let (cf_cmE_x, cf_cmE_y) = cmE_affine.xy().unwrap_or(zero); + let cf_cmE_z = C1::ScalarField::one(); + let (cf_cmW_x, cf_cmW_y) = cmW_affine.xy().unwrap_or(zero); + let cf_cmW_z = C1::ScalarField::one(); + + // snark proof 1 + let c1_public_input: Vec = [ + vec![vp.pp_hash, i], + z_0, + z_i, + // U_{i+1} values: + vec![U.u], + U.x.clone(), + cmE_x, + cmE_y, + cmW_x, + cmW_y, + // CS1 values: + proof.cs1_challenges.to_vec(), // c_W, c_E + vec![ + proof.cs1_proofs[0].eval, // eval_W + proof.cs1_proofs[1].eval, // eval_E + ], + // cf_U_i values + NonNativeUintVar::>::inputize(proof.cf_U_i.u), + proof + .cf_U_i + .x + .iter() + .flat_map(|&x_i| NonNativeUintVar::>::inputize(x_i)) + .collect::>(), + vec![ + *cf_cmE_x, *cf_cmE_y, cf_cmE_z, *cf_cmW_x, *cf_cmW_y, cf_cmW_z, + ], + // NIFS values: + cmT_x, + cmT_y, + vec![proof.r], + ] + .concat(); + + let c1_snark_v = S1::verify(&vp.c1_snark_vp, &c1_public_input, &proof.c1_snark_proof) + .map_err(|e| Error::Other(e.to_string()))?; + if !c1_snark_v { + return Err(Error::SNARKVerificationFail); + } + dbg!("snark1 verified"); + + let (cf2_cmE_x, cf2_cmE_y) = NonNativeAffineVar::inputize(proof.cf_U_i.cmE)?; + let (cf2_cmW_x, cf2_cmW_y) = NonNativeAffineVar::inputize(proof.cf_U_i.cmW)?; + + // snark proof 2 + // migrate pp_hash from C1::Fr to C1::Fq + let pp_hash_Fq = + C2::ScalarField::from_le_bytes_mod_order(&vp.pp_hash.into_bigint().to_bytes_le()); + let c2_public_input: Vec = [ + vec![pp_hash_Fq], + vec![proof.cf_U_i.u], + proof.cf_U_i.x.clone(), + cf2_cmE_x, + cf2_cmE_y, + cf2_cmW_x, + cf2_cmW_y, + proof.cs2_challenges.to_vec(), + vec![ + proof.cs2_proofs[0].eval, // eval_W + proof.cs2_proofs[1].eval, // eval_E + ], + ] + .concat(); + + let c2_snark_v = S2::verify(&vp.c2_snark_vp, &c2_public_input, &proof.c2_snark_proof) + .map_err(|e| Error::Other(e.to_string()))?; + if !c2_snark_v { + return Err(Error::SNARKVerificationFail); + } + dbg!("snark2 verified"); + + // check C1 commitments (main instance commitments) + CS1::verify_with_challenge( + &vp.c1_cs_vp, + proof.cs1_challenges[0], + &U.cmW, + &proof.cs1_proofs[0], + )?; + CS1::verify_with_challenge( + &vp.c1_cs_vp, + proof.cs1_challenges[1], + &U.cmE, + &proof.cs1_proofs[1], + )?; + dbg!("CS1 verified"); + + // check C2 commitments (CycleFold instance commitments) + CS2::verify_with_challenge( + &vp.c2_cs_vp, + proof.cs2_challenges[0], + &proof.cf_U_i.cmW, + &proof.cs2_proofs[0], + )?; + CS2::verify_with_challenge( + &vp.c2_cs_vp, + proof.cs2_challenges[1], + &proof.cf_U_i.cmE, + &proof.cs2_proofs[1], + )?; + dbg!("CS1 verified"); + + Ok(true) + } +} + +#[cfg(test)] +pub mod tests { + use ark_groth16::Groth16; + + // Note: do not use the MNTx_298 curves in practice, these are just for tests. Use the MNTx_753 + // curves instead. + use ark_mnt4_298::{ + constraints::G1Var as GVar, Fr, G1Projective as Projective, MNT4_298 as MNT4, + }; + use ark_mnt6_298::{ + constraints::G1Var as GVar2, G1Projective as Projective2, MNT6_298 as MNT6, + }; + use std::time::Instant; + + use super::*; + use crate::commitment::kzg::KZG; + use crate::folding::nova::PreprocessorParam; + use crate::frontend::utils::CubicFCircuit; + use crate::transcript::poseidon::poseidon_canonical_config; + + #[test] + fn test_decider() { + // use Nova as FoldingScheme + type N = Nova< + Projective, + GVar, + Projective2, + GVar2, + CubicFCircuit, + KZG<'static, MNT4>, + KZG<'static, MNT6>, + false, + >; + type D = Decider< + Projective, + GVar, + Projective2, + GVar2, + CubicFCircuit, + KZG<'static, MNT4>, + KZG<'static, MNT6>, + Groth16, + Groth16, + N, // here we define the FoldingScheme to use + >; + + let mut rng = ark_std::test_rng(); + let poseidon_config = poseidon_canonical_config::(); + + let F_circuit = CubicFCircuit::::new(()).unwrap(); + let z_0 = vec![Fr::from(3_u32)]; + + let start = Instant::now(); + let prep_param = PreprocessorParam::new(poseidon_config, F_circuit); + let nova_params = N::preprocess(&mut rng, &prep_param).unwrap(); + println!("Nova preprocess, {:?}", start.elapsed()); + + let start = Instant::now(); + let mut nova = N::init(&nova_params, F_circuit, z_0.clone()).unwrap(); + println!("Nova initialized, {:?}", start.elapsed()); + let start = Instant::now(); + nova.prove_step(&mut rng, vec![], None).unwrap(); + println!("prove_step, {:?}", start.elapsed()); + nova.prove_step(&mut rng, vec![], None).unwrap(); // do a 2nd step + + let mut rng = rand::rngs::OsRng; + + // prepare the Decider prover & verifier params + let start = Instant::now(); + let (decider_pp, decider_vp) = D::preprocess(&mut rng, nova_params, nova.clone()).unwrap(); + println!("Decider preprocess, {:?}", start.elapsed()); + + // decider proof generation + let start = Instant::now(); + let proof = D::prove(rng, decider_pp, nova.clone()).unwrap(); + println!("Decider prove, {:?}", start.elapsed()); + + // decider proof verification + let start = Instant::now(); + let verified = D::verify( + decider_vp, nova.i, nova.z_0, nova.z_i, &nova.U_i, &nova.u_i, &proof, + ) + .unwrap(); + assert!(verified); + println!("Decider verify, {:?}", start.elapsed()); + } +} diff --git a/folding-schemes/src/folding/nova/decider_circuits.rs b/folding-schemes/src/folding/nova/decider_circuits.rs index 959a1e3f..457d0571 100644 --- a/folding-schemes/src/folding/nova/decider_circuits.rs +++ b/folding-schemes/src/folding/nova/decider_circuits.rs @@ -84,9 +84,10 @@ where /// CycleFold running instance pub cf_U_i: Option>, - /// KZG challenges - pub kzg_c_W: Option, - pub kzg_c_E: Option, + /// Commitment Scheme challenges + pub cs_c_W: Option, + pub cs_c_E: Option, + /// Evaluations of the committed polynomials at the challenge pub eval_W: Option, pub eval_E: Option, } @@ -134,11 +135,11 @@ where r_Fr, &nova.W_i, &nova.U_i, &nova.w_i, &nova.u_i, &T, cmT, )?; - // compute the KZG challenges used as inputs in the circuit - let (kzg_challenge_W, kzg_challenge_E) = + // compute the commitment scheme challenges used as inputs in the circuit + let (cs_challenge_W, cs_challenge_E) = KZGChallengesGadget::::get_challenges_native(&mut transcript, U_i1.clone()); - // get KZG evals + // get evals of the committed polys at the challenges let mut W = W_i1.W.clone(); W.extend( std::iter::repeat(C1::ScalarField::zero()) @@ -150,9 +151,9 @@ where .take(W_i1.E.len().next_power_of_two() - W_i1.E.len()), ); let p_W = poly_from_vec(W.to_vec())?; - let eval_W = p_W.evaluate(&kzg_challenge_W); + let eval_W = p_W.evaluate(&cs_challenge_W); let p_E = poly_from_vec(E.to_vec())?; - let eval_E = p_E.evaluate(&kzg_challenge_E); + let eval_E = p_E.evaluate(&cs_challenge_E); Ok(Self { _c1: PhantomData, @@ -176,8 +177,8 @@ where cmT: Some(cmT), r: Some(r_Fr), cf_U_i: Some(nova.cf_U_i), - kzg_c_W: Some(kzg_challenge_W), - kzg_c_E: Some(kzg_challenge_E), + cs_c_W: Some(cs_challenge_W), + cs_c_E: Some(cs_challenge_E), eval_W: Some(eval_W), eval_E: Some(eval_E), }) @@ -232,11 +233,11 @@ where })?; // allocate the inputs for the check 6 - let kzg_c_W = FpVar::>::new_input(cs.clone(), || { - Ok(self.kzg_c_W.unwrap_or_else(CF1::::zero)) + let cs_c_W = FpVar::>::new_input(cs.clone(), || { + Ok(self.cs_c_W.unwrap_or_else(CF1::::zero)) })?; - let kzg_c_E = FpVar::>::new_input(cs.clone(), || { - Ok(self.kzg_c_E.unwrap_or_else(CF1::::zero)) + let cs_c_E = FpVar::>::new_input(cs.clone(), || { + Ok(self.cs_c_E.unwrap_or_else(CF1::::zero)) })?; let _eval_W = FpVar::>::new_input(cs.clone(), || { Ok(self.eval_W.unwrap_or_else(CF1::::zero)) @@ -276,7 +277,7 @@ where [vec![U_i1.u.clone()], U_i1.x.to_vec(), W_i1.W.to_vec()].concat(); RelaxedR1CSGadget::check_native(r1cs, W_i1.E.clone(), U_i1.u.clone(), z_U1)?; - // 1.1.a, 5.1 compute NIFS.V and KZG challenges. + // 1.1.a, 5.1 compute NIFS.V and Commitment Scheme challenges. // We need to ensure the order of challenge generation is the same as // the native counterpart, so we first compute the challenges here and // do the actual checks later. @@ -292,8 +293,8 @@ where // 5.1. let (incircuit_c_W, incircuit_c_E) = KZGChallengesGadget::::get_challenges_gadget(&mut transcript, U_i1.clone())?; - incircuit_c_W.enforce_equal(&kzg_c_W)?; - incircuit_c_E.enforce_equal(&kzg_c_E)?; + incircuit_c_W.enforce_equal(&cs_c_W)?; + incircuit_c_E.enforce_equal(&cs_c_E)?; // Check 5.2 is temporary disabled due // https://github.com/privacy-scaling-explorations/sonobe/issues/80 @@ -342,9 +343,10 @@ where /// be computed natively pub cf_U_i: Option>, pub cf_W_i: Option>, - /// KZG challenges - pub kzg_c_W: Option, - pub kzg_c_E: Option, + /// Commitment Scheme challenges + pub cs_c_W: Option, + pub cs_c_E: Option, + /// Evaluations of the committed polynomials at the challenge pub eval_W: Option, pub eval_E: Option, } @@ -366,8 +368,8 @@ where CS1: CommitmentScheme, CS2: CommitmentScheme, { - // compute the KZG challenges of the CycleFold instance commitments, used as inputs in the - // circuit + // compute the Commitment Scheme challenges of the CycleFold instance commitments, used as + // inputs in the circuit let poseidon_config = crate::transcript::poseidon::poseidon_canonical_config::(); let mut transcript = PoseidonSponge::::new(&poseidon_config); @@ -375,10 +377,10 @@ where C2::ScalarField::from_le_bytes_mod_order(&nova.pp_hash.into_bigint().to_bytes_le()); transcript.absorb(&pp_hash_Fq); - let (kzg_challenge_W, kzg_challenge_E) = + let (cs_challenge_W, cs_challenge_E) = KZGChallengesGadget::::get_challenges_native(&mut transcript, nova.cf_U_i.clone()); - // get KZG evals + // get evals of the committed polynomials at the challenge let mut W = nova.cf_W_i.W.clone(); W.extend( std::iter::repeat(C2::ScalarField::zero()) @@ -390,9 +392,9 @@ where .take(nova.cf_W_i.E.len().next_power_of_two() - nova.cf_W_i.E.len()), ); let p_W = poly_from_vec(W.to_vec())?; - let eval_W = p_W.evaluate(&kzg_challenge_W); + let eval_W = p_W.evaluate(&cs_challenge_W); let p_E = poly_from_vec(E.to_vec())?; - let eval_E = p_E.evaluate(&kzg_challenge_E); + let eval_E = p_E.evaluate(&cs_challenge_E); Ok(Self { _c1: PhantomData, @@ -407,9 +409,9 @@ where cf_U_i: Some(nova.cf_U_i), cf_W_i: Some(nova.cf_W_i), - // CycleFold instance commitments kzg challenges - kzg_c_W: Some(kzg_challenge_W), - kzg_c_E: Some(kzg_challenge_E), + // CycleFold instance commitments challenges + cs_c_W: Some(cs_challenge_W), + cs_c_E: Some(cs_challenge_E), eval_W: Some(eval_W), eval_E: Some(eval_E), }) @@ -457,11 +459,11 @@ where transcript.absorb(&pp_hash)?; // allocate the inputs for the check 7.1 - let kzg_c_W = FpVar::>::new_input(cs.clone(), || { - Ok(self.kzg_c_W.unwrap_or_else(CF1::::zero)) + let cs_c_W = FpVar::>::new_input(cs.clone(), || { + Ok(self.cs_c_W.unwrap_or_else(CF1::::zero)) })?; - let kzg_c_E = FpVar::>::new_input(cs.clone(), || { - Ok(self.kzg_c_E.unwrap_or_else(CF1::::zero)) + let cs_c_E = FpVar::>::new_input(cs.clone(), || { + Ok(self.cs_c_E.unwrap_or_else(CF1::::zero)) })?; // allocate the inputs for the check 7.2 let _eval_W = FpVar::>::new_input(cs.clone(), || { @@ -471,11 +473,11 @@ where Ok(self.eval_E.unwrap_or_else(CF1::::zero)) })?; - // 7.1. check the KZG challenges correct computation + // 7.1. check the commitment scheme challenges correct computation let (incircuit_c_W, incircuit_c_E) = KZGChallengesGadget::::get_challenges_gadget(&mut transcript, cf_U_i.clone())?; - incircuit_c_W.enforce_equal(&kzg_c_W)?; - incircuit_c_E.enforce_equal(&kzg_c_E)?; + incircuit_c_W.enforce_equal(&cs_c_W)?; + incircuit_c_E.enforce_equal(&cs_c_E)?; // Check 7.2 is temporary disabled due // https://github.com/privacy-scaling-explorations/sonobe/issues/80 diff --git a/folding-schemes/src/folding/nova/decider_eth.rs b/folding-schemes/src/folding/nova/decider_eth.rs index 12ab244b..b7bdf095 100644 --- a/folding-schemes/src/folding/nova/decider_eth.rs +++ b/folding-schemes/src/folding/nova/decider_eth.rs @@ -1,4 +1,5 @@ -/// This file implements the Nova's onchain (Ethereum's EVM) decider. +/// This file implements the Nova's onchain (Ethereum's EVM) decider. For non-ethereum use cases, +/// the Decider from decider.rs file will be more efficient. /// More details can be found at the documentation page: /// https://privacy-scaling-explorations.github.io/sonobe-docs/design/nova-decider-onchain.html use ark_bn254::Bn254; diff --git a/folding-schemes/src/folding/nova/mod.rs b/folding-schemes/src/folding/nova/mod.rs index d6436c54..7a6691d5 100644 --- a/folding-schemes/src/folding/nova/mod.rs +++ b/folding-schemes/src/folding/nova/mod.rs @@ -45,6 +45,7 @@ pub mod traits; pub mod zk; // offchain decider +pub mod decider; pub mod decider_circuits; // onchain decider pub mod decider_eth;