diff --git a/folding-schemes/src/folding/circuits/decider/mod.rs b/folding-schemes/src/folding/circuits/decider/mod.rs index 1142f809..55558793 100644 --- a/folding-schemes/src/folding/circuits/decider/mod.rs +++ b/folding-schemes/src/folding/circuits/decider/mod.rs @@ -88,11 +88,11 @@ impl EvalGadget { } } -/// This is a temporary workaround for step 8 (running NIFS.V in circuit) in a -/// folding scheme-agnostic way, as different folding schemes have different -/// interfaces of folding verification now. +/// This is a temporary workaround for step 6 (running NIFS.V for group elements +/// in circuit) in an NIFS-agnostic way, because different folding schemes have +/// different interfaces of folding verification now. /// -/// In the future, we may introduce a better solution that use a trait for all +/// In the future, we may introduce a better solution that uses a trait for all /// folding schemes that specifies their native and in-circuit behaviors. pub trait DeciderEnabledNIFS< C: CurveGroup, @@ -107,8 +107,12 @@ pub trait DeciderEnabledNIFS< type RandomnessDummyCfg; type Randomness: Dummy; + /// Fold the field elements in `U` and `u` inside the circuit. + /// + /// `U_vec` is `U` expressed as a vector of `FpVar`s, which can be reused + /// before or after calling this function to save constraints. #[allow(clippy::too_many_arguments)] - fn fold_gadget( + fn fold_field_elements_gadget( arith: &A, transcript: &mut PoseidonSpongeVar>, pp_hash: FpVar>, @@ -118,6 +122,15 @@ pub trait DeciderEnabledNIFS< proof: Self::Proof, randomness: Self::Randomness, ) -> Result; + + /// Fold the group elements (i.e., commitments) in `U` and `u` outside the + /// circuit. + fn fold_group_elements_native( + U_commitments: &[C], + u_commitments: &[C], + proof: Option, + randomness: Self::Randomness, + ) -> Result, Error>; } #[cfg(test)] diff --git a/folding-schemes/src/folding/circuits/decider/off_chain.rs b/folding-schemes/src/folding/circuits/decider/off_chain.rs index 6aa512fc..3bb0905f 100644 --- a/folding-schemes/src/folding/circuits/decider/off_chain.rs +++ b/folding-schemes/src/folding/circuits/decider/off_chain.rs @@ -203,7 +203,7 @@ where u_i.get_public_inputs().enforce_equal(&[u_i_x, cf_u_i_x])?; // 6.1. partially enforce `NIFS.V(U_i, u_i) = U_{i+1}`. - D::fold_gadget( + D::fold_field_elements_gadget( &self.arith, &mut transcript, pp_hash, diff --git a/folding-schemes/src/folding/circuits/decider/on_chain.rs b/folding-schemes/src/folding/circuits/decider/on_chain.rs index 9f006111..f091b11c 100644 --- a/folding-schemes/src/folding/circuits/decider/on_chain.rs +++ b/folding-schemes/src/folding/circuits/decider/on_chain.rs @@ -293,7 +293,7 @@ where } // 6.1. partially enforce `NIFS.V(U_i, u_i) = U_{i+1}`. - D::fold_gadget( + D::fold_field_elements_gadget( &self.arith, &mut transcript, pp_hash, diff --git a/folding-schemes/src/folding/hypernova/decider_eth.rs b/folding-schemes/src/folding/hypernova/decider_eth.rs index a7aa1356..4e2d21f7 100644 --- a/folding-schemes/src/folding/hypernova/decider_eth.rs +++ b/folding-schemes/src/folding/hypernova/decider_eth.rs @@ -10,10 +10,12 @@ use ark_std::{One, Zero}; use core::marker::PhantomData; pub use super::decider_eth_circuit::DeciderEthCircuit; +use super::decider_eth_circuit::DeciderHyperNovaGadget; use super::HyperNova; use crate::commitment::{ kzg::Proof as KZGProof, pedersen::Params as PedersenParams, CommitmentScheme, }; +use crate::folding::circuits::decider::DeciderEnabledNIFS; use crate::folding::circuits::CF2; use crate::folding::nova::decider_eth::VerifierParam; use crate::folding::traits::{Inputize, WitnessOps}; @@ -187,9 +189,12 @@ where } = vp; // 6.2. Fold the commitments - let U_C = running_commitments[0]; - let u_C = incoming_commitments[0]; - let C = U_C + u_C.mul(proof.rho); + let C = DeciderHyperNovaGadget::fold_group_elements_native( + running_commitments, + incoming_commitments, + None, + proof.rho, + )?[0]; // Note: the NIMFS proof is checked inside the DeciderEthCircuit, which ensures that the // 'proof.U_i1' is correctly computed diff --git a/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs b/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs index 203c7605..71fa91b2 100644 --- a/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs +++ b/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs @@ -194,7 +194,7 @@ where type Randomness = CF1; type RandomnessDummyCfg = (); - fn fold_gadget( + fn fold_field_elements_gadget( arith: &CCS>, transcript: &mut PoseidonSpongeVar>, pp_hash: FpVar>, @@ -220,6 +220,18 @@ where Boolean::le_bits_to_fp_var(&rho_bits)?.enforce_equal(&rho)?; Ok(computed_U_i1) } + + fn fold_group_elements_native( + U_commitments: &[C], + u_commitments: &[C], + _: Option, + r: Self::Randomness, + ) -> Result, Error> { + let U_C = U_commitments[0]; + let u_C = u_commitments[0]; + let C = U_C + u_C.mul(r); + Ok(vec![C]) + } } #[cfg(test)] diff --git a/folding-schemes/src/folding/nova/decider.rs b/folding-schemes/src/folding/nova/decider.rs index 24ea6869..21586c46 100644 --- a/folding-schemes/src/folding/nova/decider.rs +++ b/folding-schemes/src/folding/nova/decider.rs @@ -13,8 +13,10 @@ use ark_std::{One, Zero}; use core::marker::PhantomData; use super::decider_circuits::{DeciderCircuit1, DeciderCircuit2}; +use super::decider_eth_circuit::DeciderNovaGadget; use super::Nova; use crate::commitment::CommitmentScheme; +use crate::folding::circuits::decider::DeciderEnabledNIFS; use crate::folding::circuits::{ cyclefold::{CycleFoldCommittedInstance, CycleFoldCommittedInstanceVar}, CF2, @@ -255,15 +257,12 @@ where } // 6.2. Fold the commitments - let U_cmW = running_commitments[0]; - let U_cmE = running_commitments[1]; - let u_cmW = incoming_commitments[0]; - let u_cmE = incoming_commitments[1]; - if !u_cmE.is_zero() { - return Err(Error::NotIncomingCommittedInstance); - } - let cmW = U_cmW + u_cmW.mul(proof.r); - let cmE = U_cmE + proof.cmT.mul(proof.r); + let U_final_commitments = DeciderNovaGadget::fold_group_elements_native( + running_commitments, + incoming_commitments, + Some(proof.cmT), + proof.r, + )?; let cf_U = proof.cf_U_final.clone(); // snark proof 1 @@ -271,8 +270,10 @@ where &[vp.pp_hash, i][..], &z_0, &z_i, - &cmW.inputize(), - &cmE.inputize(), + &U_final_commitments + .iter() + .flat_map(|c| c.inputize()) + .collect::>(), &Inputize::, CycleFoldCommittedInstanceVar>::inputize(&cf_U), &proof.cs1_challenges, &proof.cs1_proofs.iter().map(|p| p.eval).collect::>(), @@ -306,7 +307,7 @@ where } // 7.3. check C1 commitments (main instance commitments) - for ((cm, &c), pi) in [cmW, cmE] + for ((cm, &c), pi) in U_final_commitments .iter() .zip(&proof.cs1_challenges) .zip(&proof.cs1_proofs) diff --git a/folding-schemes/src/folding/nova/decider_eth.rs b/folding-schemes/src/folding/nova/decider_eth.rs index 309cea2b..74b2c852 100644 --- a/folding-schemes/src/folding/nova/decider_eth.rs +++ b/folding-schemes/src/folding/nova/decider_eth.rs @@ -15,12 +15,14 @@ use ark_std::{One, Zero}; use core::marker::PhantomData; pub use super::decider_eth_circuit::DeciderEthCircuit; +use super::decider_eth_circuit::DeciderNovaGadget; use super::{CommittedInstance, Nova}; use crate::commitment::{ kzg::{Proof as KZGProof, KZG}, pedersen::Params as PedersenParams, CommitmentScheme, }; +use crate::folding::circuits::decider::DeciderEnabledNIFS; use crate::folding::circuits::CF2; use crate::folding::traits::{Inputize, WitnessOps}; use crate::frontend::FCircuit; @@ -203,22 +205,21 @@ where } = vp; // 6.2. Fold the commitments - let U_cmW = running_commitments[0]; - let U_cmE = running_commitments[1]; - let u_cmW = incoming_commitments[0]; - let u_cmE = incoming_commitments[1]; - if !u_cmE.is_zero() { - return Err(Error::NotIncomingCommittedInstance); - } - let cmW = U_cmW + u_cmW.mul(proof.r); - let cmE = U_cmE + proof.cmT.mul(proof.r); + let U_final_commitments = DeciderNovaGadget::fold_group_elements_native( + running_commitments, + incoming_commitments, + Some(proof.cmT), + proof.r, + )?; let public_input = [ &[pp_hash, i][..], &z_0, &z_i, - &cmW.inputize(), - &cmE.inputize(), + &U_final_commitments + .iter() + .flat_map(|c| c.inputize()) + .collect::>(), &proof.kzg_challenges, &proof.kzg_proofs.iter().map(|p| p.eval).collect::>(), &proof.cmT.inputize(), @@ -233,7 +234,7 @@ where } // 7.3. Verify the KZG proofs - for ((cm, &c), pi) in [cmW, cmE] + for ((cm, &c), pi) in U_final_commitments .iter() .zip(&proof.kzg_challenges) .zip(&proof.kzg_proofs) diff --git a/folding-schemes/src/folding/nova/decider_eth_circuit.rs b/folding-schemes/src/folding/nova/decider_eth_circuit.rs index 31fd3444..890f2e8c 100644 --- a/folding-schemes/src/folding/nova/decider_eth_circuit.rs +++ b/folding-schemes/src/folding/nova/decider_eth_circuit.rs @@ -189,7 +189,7 @@ where type RandomnessDummyCfg = (); type Randomness = CF1; - fn fold_gadget( + fn fold_field_elements_gadget( _arith: &R1CS>, transcript: &mut PoseidonSpongeVar>, pp_hash: FpVar>, @@ -213,6 +213,25 @@ where NIFSGadget::::fold_committed_instance(r, U, u) } + + fn fold_group_elements_native( + U_commitments: &[C], + u_commitments: &[C], + cmT: Option, + r: Self::Randomness, + ) -> Result, Error> { + let cmT = cmT.ok_or(Error::Empty)?; + let U_cmW = U_commitments[0]; + let U_cmE = U_commitments[1]; + let u_cmW = u_commitments[0]; + let u_cmE = u_commitments[1]; + if !u_cmE.is_zero() { + return Err(Error::NotIncomingCommittedInstance); + } + let cmW = U_cmW + u_cmW.mul(r); + let cmE = U_cmE + cmT.mul(r); + Ok(vec![cmW, cmE]) + } } #[cfg(test)] diff --git a/folding-schemes/src/folding/protogalaxy/decider_eth_circuit.rs b/folding-schemes/src/folding/protogalaxy/decider_eth_circuit.rs index 25cfb66b..57d69658 100644 --- a/folding-schemes/src/folding/protogalaxy/decider_eth_circuit.rs +++ b/folding-schemes/src/folding/protogalaxy/decider_eth_circuit.rs @@ -165,7 +165,7 @@ impl type Randomness = Vec>; type RandomnessDummyCfg = usize; - fn fold_gadget( + fn fold_field_elements_gadget( _arith: &R1CS>, transcript: &mut PoseidonSpongeVar>, _pp_hash: FpVar>, @@ -178,14 +178,25 @@ impl let cs = transcript.cs(); let F_coeffs = Vec::new_witness(cs.clone(), || Ok(&proof.F_coeffs[..]))?; let K_coeffs = Vec::new_witness(cs.clone(), || Ok(&proof.K_coeffs[..]))?; - let challenge = Vec::new_input(cs.clone(), || Ok(randomness))?; + let randomness = Vec::new_input(cs.clone(), || Ok(randomness))?; let (U_next, L_X_evals) = FoldingGadget::fold_committed_instance(transcript, &U, &[u], F_coeffs, K_coeffs)?; - L_X_evals.enforce_equal(&challenge)?; + L_X_evals.enforce_equal(&randomness)?; Ok(U_next) } + + fn fold_group_elements_native( + U_commitments: &[C], + u_commitments: &[C], + _: Option, + L_X_evals: Self::Randomness, + ) -> Result, Error> { + let U_phi = U_commitments[0]; + let u_phi = u_commitments[0]; + Ok(vec![U_phi * L_X_evals[0] + u_phi * L_X_evals[1]]) + } } #[cfg(test)]