Browse Source

Resolve the stack overflow issue when evaluating polynomials in-circuit (#166)

* Resolve the stack overflow issue when evaluating polynomials in-circuit

* Format

* Add the missing line of comment
main
winderica 1 month ago
committed by GitHub
parent
commit
ed1488978c
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
3 changed files with 34 additions and 50 deletions
  1. +9
    -10
      folding-schemes/src/folding/hypernova/decider_eth_circuit.rs
  2. +16
    -21
      folding-schemes/src/folding/nova/decider_circuits.rs
  3. +9
    -19
      folding-schemes/src/folding/nova/decider_eth_circuit.rs

+ 9
- 10
folding-schemes/src/folding/hypernova/decider_eth_circuit.rs

@ -26,7 +26,6 @@ use super::{
nimfs::{NIMFSProof, NIMFS}, nimfs::{NIMFSProof, NIMFS},
HyperNova, Witness, CCCS, LCCCS, HyperNova, Witness, CCCS, LCCCS,
}; };
use crate::commitment::{pedersen::Params as PedersenParams, CommitmentScheme};
use crate::folding::circuits::{ use crate::folding::circuits::{
cyclefold::{CycleFoldCommittedInstance, CycleFoldWitness}, cyclefold::{CycleFoldCommittedInstance, CycleFoldWitness},
CF1, CF2, CF1, CF2,
@ -42,6 +41,10 @@ use crate::{
arith::{ccs::CCS, r1cs::R1CS}, arith::{ccs::CCS, r1cs::R1CS},
folding::traits::{CommittedInstanceVarOps, Dummy, WitnessVarOps}, folding::traits::{CommittedInstanceVarOps, Dummy, WitnessVarOps},
}; };
use crate::{
commitment::{pedersen::Params as PedersenParams, CommitmentScheme},
folding::nova::decider_eth_circuit::evaluate_gadget,
};
/// In-circuit representation of the Witness associated to the CommittedInstance. /// In-circuit representation of the Witness associated to the CommittedInstance.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -322,7 +325,7 @@ where
let kzg_challenge = FpVar::<CF1<C1>>::new_input(cs.clone(), || { let kzg_challenge = FpVar::<CF1<C1>>::new_input(cs.clone(), || {
Ok(self.kzg_challenge.unwrap_or_else(CF1::<C1>::zero)) Ok(self.kzg_challenge.unwrap_or_else(CF1::<C1>::zero))
})?; })?;
let _eval_W = FpVar::<CF1<C1>>::new_input(cs.clone(), || {
let eval_W = FpVar::<CF1<C1>>::new_input(cs.clone(), || {
Ok(self.eval_W.unwrap_or_else(CF1::<C1>::zero)) Ok(self.eval_W.unwrap_or_else(CF1::<C1>::zero))
})?; })?;
@ -423,14 +426,6 @@ where
// `rho_bits` computed along the way of computing `computed_U_i1` for the later `rho_powers` // `rho_bits` computed along the way of computing `computed_U_i1` for the later `rho_powers`
// check (6.b). // check (6.b).
// Check 7 is temporary disabled due
// https://github.com/privacy-scaling-explorations/sonobe/issues/80
log::warn!("[WARNING]: issue #80 (https://github.com/privacy-scaling-explorations/sonobe/issues/80) is not resolved yet.");
//
// 7. check eval_W==p_W(c_W)
// let incircuit_eval_W = evaluate_gadget::<CF1<C1>>(W_i1.W, incircuit_c_W)?;
// incircuit_eval_W.enforce_equal(&eval_W)?;
// 8.a verify the NIMFS.V of the final fold, and check that the obtained rho_powers from the // 8.a verify the NIMFS.V of the final fold, and check that the obtained rho_powers from the
// transcript match the one from the public input (so we avoid the onchain logic of the // transcript match the one from the public input (so we avoid the onchain logic of the
// verifier computing it). // verifier computing it).
@ -462,6 +457,10 @@ where
computed_U_i1.r_x.enforce_equal(&U_i1.r_x)?; computed_U_i1.r_x.enforce_equal(&U_i1.r_x)?;
computed_U_i1.v.enforce_equal(&U_i1.v)?; computed_U_i1.v.enforce_equal(&U_i1.v)?;
// 7. check eval_W==p_W(c_W)
let incircuit_eval_W = evaluate_gadget::<CF1<C1>>(W_i1.w, incircuit_challenge)?;
incircuit_eval_W.enforce_equal(&eval_W)?;
// 8.b check that the in-circuit computed r is equal to the inputted r. // 8.b check that the in-circuit computed r is equal to the inputted r.
let rho = Boolean::le_bits_to_fp_var(&rho_bits)?; let rho = Boolean::le_bits_to_fp_var(&rho_bits)?;

+ 16
- 21
folding-schemes/src/folding/nova/decider_circuits.rs

@ -25,7 +25,9 @@ use core::marker::PhantomData;
use super::{ use super::{
circuits::{ChallengeGadget, CommittedInstanceVar}, circuits::{ChallengeGadget, CommittedInstanceVar},
decider_eth_circuit::{KZGChallengesGadget, R1CSVar, RelaxedR1CSGadget, WitnessVar},
decider_eth_circuit::{
evaluate_gadget, KZGChallengesGadget, R1CSVar, RelaxedR1CSGadget, WitnessVar,
},
nifs::NIFS, nifs::NIFS,
traits::NIFSTrait, traits::NIFSTrait,
CommittedInstance, Nova, Witness, CommittedInstance, Nova, Witness,
@ -239,10 +241,10 @@ where
let cs_c_E = FpVar::<CF1<C1>>::new_input(cs.clone(), || { let cs_c_E = FpVar::<CF1<C1>>::new_input(cs.clone(), || {
Ok(self.cs_c_E.unwrap_or_else(CF1::<C1>::zero)) Ok(self.cs_c_E.unwrap_or_else(CF1::<C1>::zero))
})?; })?;
let _eval_W = FpVar::<CF1<C1>>::new_input(cs.clone(), || {
let eval_W = FpVar::<CF1<C1>>::new_input(cs.clone(), || {
Ok(self.eval_W.unwrap_or_else(CF1::<C1>::zero)) Ok(self.eval_W.unwrap_or_else(CF1::<C1>::zero))
})?; })?;
let _eval_E = FpVar::<CF1<C1>>::new_input(cs.clone(), || {
let eval_E = FpVar::<CF1<C1>>::new_input(cs.clone(), || {
Ok(self.eval_E.unwrap_or_else(CF1::<C1>::zero)) Ok(self.eval_E.unwrap_or_else(CF1::<C1>::zero))
})?; })?;
@ -296,15 +298,11 @@ where
incircuit_c_W.enforce_equal(&cs_c_W)?; incircuit_c_W.enforce_equal(&cs_c_W)?;
incircuit_c_E.enforce_equal(&cs_c_E)?; incircuit_c_E.enforce_equal(&cs_c_E)?;
// Check 5.2 is temporary disabled due
// https://github.com/privacy-scaling-explorations/sonobe/issues/80
log::warn!("[WARNING]: issue #80 (https://github.com/privacy-scaling-explorations/sonobe/issues/80) is not resolved yet.");
//
// 5.2. check eval_W==p_W(c_W) and eval_E==p_E(c_E) // 5.2. check eval_W==p_W(c_W) and eval_E==p_E(c_E)
// let incircuit_eval_W = evaluate_gadget::<CF1<C1>>(W_i1.W, incircuit_c_W)?;
// let incircuit_eval_E = evaluate_gadget::<CF1<C1>>(W_i1.E, incircuit_c_E)?;
// incircuit_eval_W.enforce_equal(&eval_W)?;
// incircuit_eval_E.enforce_equal(&eval_E)?;
let incircuit_eval_W = evaluate_gadget::<CF1<C1>>(W_i1.W, incircuit_c_W)?;
let incircuit_eval_E = evaluate_gadget::<CF1<C1>>(W_i1.E, incircuit_c_E)?;
incircuit_eval_W.enforce_equal(&eval_W)?;
incircuit_eval_E.enforce_equal(&eval_E)?;
// 1.1.b check that the NIFS.V challenge matches the one from the public input (so we avoid // 1.1.b check that the NIFS.V challenge matches the one from the public input (so we avoid
// the verifier computing it) // the verifier computing it)
@ -451,7 +449,7 @@ where
// 6. check RelaxedR1CS of cf_U_i // 6. check RelaxedR1CS of cf_U_i
let cf_z_U = [vec![cf_U_i.u.clone()], cf_U_i.x.to_vec(), cf_W_i.W.to_vec()].concat(); let cf_z_U = [vec![cf_U_i.u.clone()], cf_U_i.x.to_vec(), cf_W_i.W.to_vec()].concat();
RelaxedR1CSGadget::check_native(cf_r1cs, cf_W_i.E, cf_U_i.u.clone(), cf_z_U)?;
RelaxedR1CSGadget::check_native(cf_r1cs, cf_W_i.E.clone(), cf_U_i.u.clone(), cf_z_U)?;
// `transcript` is for challenge generation. // `transcript` is for challenge generation.
let mut transcript = let mut transcript =
@ -466,10 +464,10 @@ where
Ok(self.cs_c_E.unwrap_or_else(CF1::<C2>::zero)) Ok(self.cs_c_E.unwrap_or_else(CF1::<C2>::zero))
})?; })?;
// allocate the inputs for the check 7.2 // allocate the inputs for the check 7.2
let _eval_W = FpVar::<CF1<C2>>::new_input(cs.clone(), || {
let eval_W = FpVar::<CF1<C2>>::new_input(cs.clone(), || {
Ok(self.eval_W.unwrap_or_else(CF1::<C2>::zero)) Ok(self.eval_W.unwrap_or_else(CF1::<C2>::zero))
})?; })?;
let _eval_E = FpVar::<CF1<C2>>::new_input(cs.clone(), || {
let eval_E = FpVar::<CF1<C2>>::new_input(cs.clone(), || {
Ok(self.eval_E.unwrap_or_else(CF1::<C2>::zero)) Ok(self.eval_E.unwrap_or_else(CF1::<C2>::zero))
})?; })?;
@ -479,14 +477,11 @@ where
incircuit_c_W.enforce_equal(&cs_c_W)?; incircuit_c_W.enforce_equal(&cs_c_W)?;
incircuit_c_E.enforce_equal(&cs_c_E)?; incircuit_c_E.enforce_equal(&cs_c_E)?;
// Check 7.2 is temporary disabled due
// https://github.com/privacy-scaling-explorations/sonobe/issues/80
log::warn!("[WARNING]: issue #80 (https://github.com/privacy-scaling-explorations/sonobe/issues/80) is not resolved yet.");
// 7.2. check eval_W==p_W(c_W) and eval_E==p_E(c_E) // 7.2. check eval_W==p_W(c_W) and eval_E==p_E(c_E)
// let incircuit_eval_W = evaluate_gadget::<CF1<C1>>(W_i1.W, incircuit_c_W)?;
// let incircuit_eval_E = evaluate_gadget::<CF1<C1>>(W_i1.E, incircuit_c_E)?;
// incircuit_eval_W.enforce_equal(&eval_W)?;
// incircuit_eval_E.enforce_equal(&eval_E)?;
let incircuit_eval_W = evaluate_gadget::<CF1<C2>>(cf_W_i.W, incircuit_c_W)?;
let incircuit_eval_E = evaluate_gadget::<CF1<C2>>(cf_W_i.E, incircuit_c_E)?;
incircuit_eval_W.enforce_equal(&eval_W)?;
incircuit_eval_E.enforce_equal(&eval_E)?;
Ok(()) Ok(())
} }

+ 9
- 19
folding-schemes/src/folding/nova/decider_eth_circuit.rs

@ -383,10 +383,10 @@ where
Ok(self.kzg_c_E.unwrap_or_else(CF1::<C1>::zero)) Ok(self.kzg_c_E.unwrap_or_else(CF1::<C1>::zero))
})?; })?;
// allocate the inputs for the check 5.2 // allocate the inputs for the check 5.2
let _eval_W = FpVar::<CF1<C1>>::new_input(cs.clone(), || {
let eval_W = FpVar::<CF1<C1>>::new_input(cs.clone(), || {
Ok(self.eval_W.unwrap_or_else(CF1::<C1>::zero)) Ok(self.eval_W.unwrap_or_else(CF1::<C1>::zero))
})?; })?;
let _eval_E = FpVar::<CF1<C1>>::new_input(cs.clone(), || {
let eval_E = FpVar::<CF1<C1>>::new_input(cs.clone(), || {
Ok(self.eval_E.unwrap_or_else(CF1::<C1>::zero)) Ok(self.eval_E.unwrap_or_else(CF1::<C1>::zero))
})?; })?;
@ -498,15 +498,11 @@ where
incircuit_c_W.enforce_equal(&kzg_c_W)?; incircuit_c_W.enforce_equal(&kzg_c_W)?;
incircuit_c_E.enforce_equal(&kzg_c_E)?; incircuit_c_E.enforce_equal(&kzg_c_E)?;
// Check 5.2 is temporary disabled due
// https://github.com/privacy-scaling-explorations/sonobe/issues/80
log::warn!("[WARNING]: issue #80 (https://github.com/privacy-scaling-explorations/sonobe/issues/80) is not resolved yet.");
//
// 5.2. check eval_W==p_W(c_W) and eval_E==p_E(c_E) // 5.2. check eval_W==p_W(c_W) and eval_E==p_E(c_E)
// let incircuit_eval_W = evaluate_gadget::<CF1<C1>>(W_i1.W, incircuit_c_W)?;
// let incircuit_eval_E = evaluate_gadget::<CF1<C1>>(W_i1.E, incircuit_c_E)?;
// incircuit_eval_W.enforce_equal(&eval_W)?;
// incircuit_eval_E.enforce_equal(&eval_E)?;
let incircuit_eval_W = evaluate_gadget::<CF1<C1>>(W_i1.W, incircuit_c_W)?;
let incircuit_eval_E = evaluate_gadget::<CF1<C1>>(W_i1.E, incircuit_c_E)?;
incircuit_eval_W.enforce_equal(&eval_W)?;
incircuit_eval_E.enforce_equal(&eval_E)?;
// 1.1.b check that the NIFS.V challenge matches the one from the public input (so we avoid // 1.1.b check that the NIFS.V challenge matches the one from the public input (so we avoid
// the verifier computing it) // the verifier computing it)
@ -523,13 +519,11 @@ where
/// Interpolates the polynomial from the given vector, and then returns it's evaluation at the /// Interpolates the polynomial from the given vector, and then returns it's evaluation at the
/// given point. /// given point.
#[allow(unused)] // unused while check 7 is disabled #[allow(unused)] // unused while check 7 is disabled
fn evaluate_gadget<F: PrimeField>(
v: Vec<FpVar<F>>,
pub fn evaluate_gadget<F: PrimeField>(
mut v: Vec<FpVar<F>>,
point: FpVar<F>, point: FpVar<F>,
) -> Result<FpVar<F>, SynthesisError> { ) -> Result<FpVar<F>, SynthesisError> {
if !v.len().is_power_of_two() {
return Err(SynthesisError::Unsatisfiable);
}
v.resize(v.len().next_power_of_two(), FpVar::zero());
let n = v.len() as u64; let n = v.len() as u64;
let gen = F::get_root_of_unity(n).unwrap(); let gen = F::get_root_of_unity(n).unwrap();
let domain = Radix2DomainVar::new(gen, log2(v.len()) as u64, FpVar::one()).unwrap(); let domain = Radix2DomainVar::new(gen, log2(v.len()) as u64, FpVar::one()).unwrap();
@ -884,10 +878,6 @@ pub mod tests {
assert_eq!(challenge_E_Var.value().unwrap(), challenge_E); assert_eq!(challenge_E_Var.value().unwrap(), challenge_E);
} }
// The test test_polynomial_interpolation is temporary disabled due
// https://github.com/privacy-scaling-explorations/sonobe/issues/80
// for n<=11 it will work, but for n>11 it will fail with stack overflow.
#[ignore]
#[test] #[test]
fn test_polynomial_interpolation() { fn test_polynomial_interpolation() {
let mut rng = ark_std::test_rng(); let mut rng = ark_std::test_rng();

Loading…
Cancel
Save