mirror of
https://github.com/arnaucube/sonobe.git
synced 2026-01-28 14:56:40 +01:00
Protogalaxy based IVC (#123)
* Parallelize vector and matrix operations * Implement convenient methods for `NonNativeAffineVar` * Return `L_X_evals` and intermediate `phi_star`s from ProtoGalaxy prover. These values will be used as hints to the augmented circuit * Correctly use number of variables, number of constraints, and `t` * Fix the size of `F_coeffs` and `K_coeffs` for in-circuit consistency * Improve prover's performance * Make `prepare_inputs` generic * Remove redundant parameters in verifier * Move `eval_f` to arith * `u` is unnecessary in ProtoGalaxy * Convert `RelaxedR1CS` to a trait that can be used in both Nova and ProtoGalaxy * Implement several traits for ProtoGalaxy * Move `FCircuit` impls to `utils.rs` and add `DummyCircuit` * `AugmentedFCircuit` and ProtoGalaxy-based IVC * Add explanations about IVC prover and in-circuit operations * Avoid using unstable features * Rename `PROTOGALAXY` to `PG` to make clippy happy * Fix merge conflicts in `RelaxedR1CS::sample` * Fix merge conflicts in `CycleFoldCircuit` * Swap `m` and `n` for protogalaxy * Add `#[cfg(test)]` to test-only util circuits * Prefer unit struct over empty struct * Add documents to `AugmentedFCircuit` for ProtoGalaxy * Fix the names for CycleFold cricuits in ProtoGalaxy * Fix usize conversion when targeting wasm * Restrict the visibility of fields in `AugmentedFCircuit` to `pub(super)` * Make CycleFold circuits and configs public * Add docs for `ProverParams` and `VerifierParams` * Refactor `pow_i` * Fix imports * Remove lint reasons * Fix type inference
This commit is contained in:
@@ -237,31 +237,31 @@ pub struct AugmentedFCircuit<
|
||||
> where
|
||||
for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>,
|
||||
{
|
||||
pub _gc2: PhantomData<GC2>,
|
||||
pub poseidon_config: PoseidonConfig<CF1<C1>>,
|
||||
pub pp_hash: Option<CF1<C1>>,
|
||||
pub i: Option<CF1<C1>>,
|
||||
pub i_usize: Option<usize>,
|
||||
pub z_0: Option<Vec<C1::ScalarField>>,
|
||||
pub z_i: Option<Vec<C1::ScalarField>>,
|
||||
pub external_inputs: Option<Vec<C1::ScalarField>>,
|
||||
pub u_i_cmW: Option<C1>,
|
||||
pub U_i: Option<CommittedInstance<C1>>,
|
||||
pub U_i1_cmE: Option<C1>,
|
||||
pub U_i1_cmW: Option<C1>,
|
||||
pub cmT: Option<C1>,
|
||||
pub F: FC, // F circuit
|
||||
pub x: Option<CF1<C1>>, // public input (u_{i+1}.x[0])
|
||||
pub(super) _gc2: PhantomData<GC2>,
|
||||
pub(super) poseidon_config: PoseidonConfig<CF1<C1>>,
|
||||
pub(super) pp_hash: Option<CF1<C1>>,
|
||||
pub(super) i: Option<CF1<C1>>,
|
||||
pub(super) i_usize: Option<usize>,
|
||||
pub(super) z_0: Option<Vec<C1::ScalarField>>,
|
||||
pub(super) z_i: Option<Vec<C1::ScalarField>>,
|
||||
pub(super) external_inputs: Option<Vec<C1::ScalarField>>,
|
||||
pub(super) u_i_cmW: Option<C1>,
|
||||
pub(super) U_i: Option<CommittedInstance<C1>>,
|
||||
pub(super) U_i1_cmE: Option<C1>,
|
||||
pub(super) U_i1_cmW: Option<C1>,
|
||||
pub(super) cmT: Option<C1>,
|
||||
pub(super) F: FC, // F circuit
|
||||
pub(super) x: Option<CF1<C1>>, // public input (u_{i+1}.x[0])
|
||||
|
||||
// cyclefold verifier on C1
|
||||
// Here 'cf1, cf2' are for each of the CycleFold circuits, corresponding to the fold of cmW and
|
||||
// cmE respectively
|
||||
pub cf1_u_i_cmW: Option<C2>, // input
|
||||
pub cf2_u_i_cmW: Option<C2>, // input
|
||||
pub cf_U_i: Option<CycleFoldCommittedInstance<C2>>, // input
|
||||
pub cf1_cmT: Option<C2>,
|
||||
pub cf2_cmT: Option<C2>,
|
||||
pub cf_x: Option<CF1<C1>>, // public input (u_{i+1}.x[1])
|
||||
pub(super) cf1_u_i_cmW: Option<C2>, // input
|
||||
pub(super) cf2_u_i_cmW: Option<C2>, // input
|
||||
pub(super) cf_U_i: Option<CycleFoldCommittedInstance<C2>>, // input
|
||||
pub(super) cf1_cmT: Option<C2>,
|
||||
pub(super) cf2_cmT: Option<C2>,
|
||||
pub(super) cf_x: Option<CF1<C1>>, // public input (u_{i+1}.x[1])
|
||||
}
|
||||
|
||||
impl<C1: CurveGroup, C2: CurveGroup, GC2: CurveVar<C2, CF2<C2>>, FC: FCircuit<CF1<C1>>>
|
||||
|
||||
@@ -342,7 +342,7 @@ pub mod tests {
|
||||
use crate::folding::nova::{
|
||||
PreprocessorParam, ProverParams as NovaProverParams, VerifierParams as NovaVerifierParams,
|
||||
};
|
||||
use crate::frontend::tests::CubicFCircuit;
|
||||
use crate::frontend::utils::CubicFCircuit;
|
||||
use crate::transcript::poseidon::poseidon_canonical_config;
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -577,6 +577,8 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use std::cmp::max;
|
||||
|
||||
use ark_crypto_primitives::crh::{
|
||||
sha256::{
|
||||
constraints::{Sha256Gadget, UnitVar},
|
||||
@@ -587,34 +589,61 @@ pub mod tests {
|
||||
use ark_pallas::{constraints::GVar, Fq, Fr, Projective};
|
||||
use ark_r1cs_std::bits::uint8::UInt8;
|
||||
use ark_relations::r1cs::ConstraintSystem;
|
||||
use ark_std::{One, UniformRand};
|
||||
use ark_std::{
|
||||
rand::{thread_rng, Rng},
|
||||
One, UniformRand,
|
||||
};
|
||||
use ark_vesta::{constraints::GVar as GVar2, Projective as Projective2};
|
||||
|
||||
use super::*;
|
||||
use crate::arith::{
|
||||
r1cs::{
|
||||
extract_r1cs, extract_w_x,
|
||||
tests::{get_test_r1cs, get_test_z},
|
||||
{extract_r1cs, extract_w_x},
|
||||
RelaxedR1CS,
|
||||
},
|
||||
Arith,
|
||||
};
|
||||
use crate::commitment::pedersen::Pedersen;
|
||||
use crate::folding::nova::PreprocessorParam;
|
||||
use crate::frontend::tests::{CubicFCircuit, CustomFCircuit, WrapperCircuit};
|
||||
use crate::frontend::utils::{CubicFCircuit, CustomFCircuit, WrapperCircuit};
|
||||
use crate::transcript::poseidon::poseidon_canonical_config;
|
||||
use crate::FoldingScheme;
|
||||
|
||||
fn prepare_instances<C: CurveGroup, CS: CommitmentScheme<C>, R: Rng>(
|
||||
mut rng: R,
|
||||
r1cs: &R1CS<C::ScalarField>,
|
||||
z: &[C::ScalarField],
|
||||
) -> (Witness<C>, CommittedInstance<C>)
|
||||
where
|
||||
C::ScalarField: Absorb,
|
||||
{
|
||||
let (w, x) = r1cs.split_z(z);
|
||||
|
||||
let (cs_pp, _) = CS::setup(&mut rng, max(w.len(), r1cs.A.n_rows)).unwrap();
|
||||
|
||||
let mut w = Witness::new::<false>(w, r1cs.A.n_rows, &mut rng);
|
||||
w.E = r1cs.eval_relation(z).unwrap();
|
||||
let mut u = w.commit::<CS, false>(&cs_pp, x).unwrap();
|
||||
u.u = z[0];
|
||||
|
||||
(w, u)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_relaxed_r1cs_small_gadget_handcrafted() {
|
||||
let rng = &mut thread_rng();
|
||||
|
||||
let r1cs: R1CS<Fr> = get_test_r1cs();
|
||||
let rel_r1cs = r1cs.clone().relax();
|
||||
let z = get_test_z(3);
|
||||
let mut z = get_test_z(3);
|
||||
z[0] = Fr::rand(rng);
|
||||
let (w, u) = prepare_instances::<_, Pedersen<Projective>, _>(rng, &r1cs, &z);
|
||||
|
||||
let cs = ConstraintSystem::<Fr>::new_ref();
|
||||
|
||||
let zVar = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(z)).unwrap();
|
||||
let EVar = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(rel_r1cs.E)).unwrap();
|
||||
let uVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(rel_r1cs.u)).unwrap();
|
||||
let EVar = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(w.E)).unwrap();
|
||||
let uVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(u.u)).unwrap();
|
||||
let r1csVar = R1CSVar::<Fr, Fr, FpVar<Fr>>::new_witness(cs.clone(), || Ok(r1cs)).unwrap();
|
||||
|
||||
RelaxedR1CSGadget::check_native(r1csVar, EVar, uVar, zVar).unwrap();
|
||||
@@ -624,6 +653,8 @@ pub mod tests {
|
||||
// gets as input a circuit that implements the ConstraintSynthesizer trait, and that has been
|
||||
// initialized.
|
||||
fn test_relaxed_r1cs_gadget<CS: ConstraintSynthesizer<Fr>>(circuit: CS) {
|
||||
let rng = &mut thread_rng();
|
||||
|
||||
let cs = ConstraintSystem::<Fr>::new_ref();
|
||||
|
||||
circuit.generate_constraints(cs.clone()).unwrap();
|
||||
@@ -634,18 +665,19 @@ pub mod tests {
|
||||
|
||||
let r1cs = extract_r1cs::<Fr>(&cs);
|
||||
let (w, x) = extract_w_x::<Fr>(&cs);
|
||||
let z = [vec![Fr::one()], x, w].concat();
|
||||
let mut z = [vec![Fr::one()], x, w].concat();
|
||||
r1cs.check_relation(&z).unwrap();
|
||||
|
||||
let relaxed_r1cs = r1cs.clone().relax();
|
||||
relaxed_r1cs.check_relation(&z).unwrap();
|
||||
z[0] = Fr::rand(rng);
|
||||
let (w, u) = prepare_instances::<_, Pedersen<Projective>, _>(rng, &r1cs, &z);
|
||||
r1cs.check_relaxed_relation(&w, &u).unwrap();
|
||||
|
||||
// set new CS for the circuit that checks the RelaxedR1CS of our original circuit
|
||||
let cs = ConstraintSystem::<Fr>::new_ref();
|
||||
// prepare the inputs for our circuit
|
||||
let zVar = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(z)).unwrap();
|
||||
let EVar = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(relaxed_r1cs.E)).unwrap();
|
||||
let uVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(relaxed_r1cs.u)).unwrap();
|
||||
let EVar = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(w.E)).unwrap();
|
||||
let uVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(u.u)).unwrap();
|
||||
let r1csVar = R1CSVar::<Fr, Fr, FpVar<Fr>>::new_witness(cs.clone(), || Ok(r1cs)).unwrap();
|
||||
|
||||
RelaxedR1CSGadget::check_native(r1csVar, EVar, uVar, zVar).unwrap();
|
||||
@@ -709,6 +741,8 @@ pub mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_relaxed_r1cs_nonnative_circuit() {
|
||||
let rng = &mut thread_rng();
|
||||
|
||||
let cs = ConstraintSystem::<Fq>::new_ref();
|
||||
// in practice we would use CycleFoldCircuit, but is a very big circuit (when computed
|
||||
// non-natively inside the RelaxedR1CS circuit), so in order to have a short test we use a
|
||||
@@ -725,16 +759,15 @@ pub mod tests {
|
||||
let cs = cs.into_inner().unwrap();
|
||||
let r1cs = extract_r1cs::<Fq>(&cs);
|
||||
let (w, x) = extract_w_x::<Fq>(&cs);
|
||||
let z = [vec![Fq::one()], x, w].concat();
|
||||
let z = [vec![Fq::rand(rng)], x, w].concat();
|
||||
|
||||
let relaxed_r1cs = r1cs.clone().relax();
|
||||
let (w, u) = prepare_instances::<_, Pedersen<Projective2>, _>(rng, &r1cs, &z);
|
||||
|
||||
// natively
|
||||
let cs = ConstraintSystem::<Fq>::new_ref();
|
||||
let zVar = Vec::<FpVar<Fq>>::new_witness(cs.clone(), || Ok(z.clone())).unwrap();
|
||||
let EVar =
|
||||
Vec::<FpVar<Fq>>::new_witness(cs.clone(), || Ok(relaxed_r1cs.clone().E)).unwrap();
|
||||
let uVar = FpVar::<Fq>::new_witness(cs.clone(), || Ok(relaxed_r1cs.u)).unwrap();
|
||||
let EVar = Vec::<FpVar<Fq>>::new_witness(cs.clone(), || Ok(w.E.clone())).unwrap();
|
||||
let uVar = FpVar::<Fq>::new_witness(cs.clone(), || Ok(u.u)).unwrap();
|
||||
let r1csVar =
|
||||
R1CSVar::<Fq, Fq, FpVar<Fq>>::new_witness(cs.clone(), || Ok(r1cs.clone())).unwrap();
|
||||
RelaxedR1CSGadget::check_native(r1csVar, EVar, uVar, zVar).unwrap();
|
||||
@@ -742,8 +775,8 @@ pub mod tests {
|
||||
// non-natively
|
||||
let cs = ConstraintSystem::<Fr>::new_ref();
|
||||
let zVar = Vec::new_witness(cs.clone(), || Ok(z)).unwrap();
|
||||
let EVar = Vec::new_witness(cs.clone(), || Ok(relaxed_r1cs.E)).unwrap();
|
||||
let uVar = NonNativeUintVar::<Fr>::new_witness(cs.clone(), || Ok(relaxed_r1cs.u)).unwrap();
|
||||
let EVar = Vec::new_witness(cs.clone(), || Ok(w.E)).unwrap();
|
||||
let uVar = NonNativeUintVar::<Fr>::new_witness(cs.clone(), || Ok(u.u)).unwrap();
|
||||
let r1csVar =
|
||||
R1CSVar::<Fq, Fr, NonNativeUintVar<Fr>>::new_witness(cs.clone(), || Ok(r1cs)).unwrap();
|
||||
RelaxedR1CSGadget::check_nonnative(r1csVar, EVar, uVar, zVar).unwrap();
|
||||
|
||||
@@ -14,7 +14,6 @@ use ark_std::rand::RngCore;
|
||||
use ark_std::{One, UniformRand, Zero};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::commitment::CommitmentScheme;
|
||||
use crate::folding::circuits::cyclefold::{
|
||||
fold_cyclefold_circuit, CycleFoldCircuit, CycleFoldCommittedInstance, CycleFoldConfig,
|
||||
CycleFoldWitness,
|
||||
@@ -25,6 +24,7 @@ use crate::transcript::{poseidon::poseidon_canonical_config, AbsorbNonNative, Tr
|
||||
use crate::utils::vec::is_zero_vec;
|
||||
use crate::Error;
|
||||
use crate::FoldingScheme;
|
||||
use crate::{arith::r1cs::RelaxedR1CS, commitment::CommitmentScheme};
|
||||
use crate::{
|
||||
arith::r1cs::{extract_r1cs, extract_w_x, R1CS},
|
||||
constants::NOVA_N_BITS_RO,
|
||||
@@ -40,8 +40,8 @@ pub mod traits;
|
||||
pub mod zk;
|
||||
use circuits::{AugmentedFCircuit, ChallengeGadget};
|
||||
use nifs::NIFS;
|
||||
use traits::NovaR1CS;
|
||||
|
||||
/// Configuration for Nova's CycleFold circuit
|
||||
pub struct NovaCycleFoldConfig<C: CurveGroup> {
|
||||
_c: PhantomData<C>,
|
||||
}
|
||||
@@ -56,7 +56,9 @@ impl<C: CurveGroup> CycleFoldConfig for NovaCycleFoldConfig<C> {
|
||||
type F = C::BaseField;
|
||||
}
|
||||
|
||||
type NovaCycleFoldCircuit<C, GC> = CycleFoldCircuit<NovaCycleFoldConfig<C>, GC>;
|
||||
/// CycleFold circuit for computing random linear combinations of group elements
|
||||
/// in Nova instances.
|
||||
pub type NovaCycleFoldCircuit<C, GC> = CycleFoldCircuit<NovaCycleFoldConfig<C>, GC>;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
|
||||
pub struct CommittedInstance<C: CurveGroup> {
|
||||
@@ -136,10 +138,7 @@ pub struct Witness<C: CurveGroup> {
|
||||
pub rW: C::ScalarField,
|
||||
}
|
||||
|
||||
impl<C: CurveGroup> Witness<C>
|
||||
where
|
||||
<C as Group>::ScalarField: Absorb,
|
||||
{
|
||||
impl<C: CurveGroup> Witness<C> {
|
||||
pub fn new<const H: bool>(w: Vec<C::ScalarField>, e_len: usize, mut rng: impl RngCore) -> Self {
|
||||
let (rW, rE) = if H {
|
||||
(
|
||||
@@ -227,6 +226,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Proving parameters for Nova-based IVC
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ProverParams<C1, C2, CS1, CS2, const H: bool = false>
|
||||
where
|
||||
@@ -235,8 +235,11 @@ where
|
||||
CS1: CommitmentScheme<C1, H>,
|
||||
CS2: CommitmentScheme<C2, H>,
|
||||
{
|
||||
/// Poseidon sponge configuration
|
||||
pub poseidon_config: PoseidonConfig<C1::ScalarField>,
|
||||
/// Proving parameters of the underlying commitment scheme over C1
|
||||
pub cs_pp: CS1::ProverParams,
|
||||
/// Proving parameters of the underlying commitment scheme over C2
|
||||
pub cf_cs_pp: CS2::ProverParams,
|
||||
}
|
||||
|
||||
@@ -302,6 +305,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Verification parameters for Nova-based IVC
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VerifierParams<C1, C2, CS1, CS2, const H: bool = false>
|
||||
where
|
||||
@@ -310,10 +314,15 @@ where
|
||||
CS1: CommitmentScheme<C1, H>,
|
||||
CS2: CommitmentScheme<C2, H>,
|
||||
{
|
||||
/// Poseidon sponge configuration
|
||||
pub poseidon_config: PoseidonConfig<C1::ScalarField>,
|
||||
/// R1CS of the Augmented step circuit
|
||||
pub r1cs: R1CS<C1::ScalarField>,
|
||||
/// R1CS of the CycleFold circuit
|
||||
pub cf_r1cs: R1CS<C2::ScalarField>,
|
||||
/// Verification parameters of the underlying commitment scheme over C1
|
||||
pub cs_vp: CS1::VerifierParams,
|
||||
/// Verification parameters of the underlying commitment scheme over C2
|
||||
pub cf_cs_vp: CS2::VerifierParams,
|
||||
}
|
||||
|
||||
@@ -553,8 +562,9 @@ where
|
||||
let pp_hash = vp.pp_hash()?;
|
||||
|
||||
// setup the dummy instances
|
||||
let (w_dummy, u_dummy) = r1cs.dummy_instance();
|
||||
let (cf_w_dummy, cf_u_dummy) = cf_r1cs.dummy_instance();
|
||||
let (W_dummy, U_dummy) = r1cs.dummy_running_instance();
|
||||
let (w_dummy, u_dummy) = r1cs.dummy_incoming_instance();
|
||||
let (cf_W_dummy, cf_U_dummy) = cf_r1cs.dummy_running_instance();
|
||||
|
||||
// W_dummy=W_0 is a 'dummy witness', all zeroes, but with the size corresponding to the
|
||||
// R1CS that we're working with.
|
||||
@@ -572,13 +582,13 @@ where
|
||||
i: C1::ScalarField::zero(),
|
||||
z_0: z_0.clone(),
|
||||
z_i: z_0,
|
||||
w_i: w_dummy.clone(),
|
||||
u_i: u_dummy.clone(),
|
||||
W_i: w_dummy,
|
||||
U_i: u_dummy,
|
||||
w_i: w_dummy,
|
||||
u_i: u_dummy,
|
||||
W_i: W_dummy,
|
||||
U_i: U_dummy,
|
||||
// cyclefold running instance
|
||||
cf_W_i: cf_w_dummy.clone(),
|
||||
cf_U_i: cf_u_dummy.clone(),
|
||||
cf_W_i: cf_W_dummy,
|
||||
cf_U_i: cf_U_dummy,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -805,10 +815,10 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
{
|
||||
self.cf_r1cs.check_instance_relation(&_cfW_w_i, &cfW_u_i)?;
|
||||
self.cf_r1cs.check_instance_relation(&_cfE_w_i, &cfE_u_i)?;
|
||||
self.cf_r1cs.check_tight_relation(&_cfW_w_i, &cfW_u_i)?;
|
||||
self.cf_r1cs.check_tight_relation(&_cfE_w_i, &cfE_u_i)?;
|
||||
self.cf_r1cs
|
||||
.check_relaxed_instance_relation(&self.cf_W_i, &self.cf_U_i)?;
|
||||
.check_relaxed_relation(&self.cf_W_i, &self.cf_U_i)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -840,9 +850,8 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
{
|
||||
self.r1cs.check_instance_relation(&self.w_i, &self.u_i)?;
|
||||
self.r1cs
|
||||
.check_relaxed_instance_relation(&self.W_i, &self.U_i)?;
|
||||
self.r1cs.check_tight_relation(&self.w_i, &self.u_i)?;
|
||||
self.r1cs.check_relaxed_relation(&self.W_i, &self.U_i)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -908,19 +917,13 @@ where
|
||||
return Err(Error::IVCVerificationFail);
|
||||
}
|
||||
|
||||
// check u_i.cmE==0, u_i.u==1 (=u_i is a un-relaxed instance)
|
||||
if !u_i.cmE.is_zero() || !u_i.u.is_one() {
|
||||
return Err(Error::IVCVerificationFail);
|
||||
}
|
||||
|
||||
// check R1CS satisfiability
|
||||
vp.r1cs.check_instance_relation(&w_i, &u_i)?;
|
||||
// check R1CS satisfiability, which also enforces u_i.cmE==0, u_i.u==1
|
||||
vp.r1cs.check_tight_relation(&w_i, &u_i)?;
|
||||
// check RelaxedR1CS satisfiability
|
||||
vp.r1cs.check_relaxed_instance_relation(&W_i, &U_i)?;
|
||||
vp.r1cs.check_relaxed_relation(&W_i, &U_i)?;
|
||||
|
||||
// check CycleFold RelaxedR1CS satisfiability
|
||||
vp.cf_r1cs
|
||||
.check_relaxed_instance_relation(&cf_W_i, &cf_U_i)?;
|
||||
vp.cf_r1cs.check_relaxed_relation(&cf_W_i, &cf_U_i)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1077,7 +1080,7 @@ pub mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::commitment::pedersen::Pedersen;
|
||||
use crate::frontend::tests::CubicFCircuit;
|
||||
use crate::frontend::utils::CubicFCircuit;
|
||||
use crate::transcript::poseidon::poseidon_canonical_config;
|
||||
|
||||
/// This test tests the Nova+CycleFold IVC, and by consequence it is also testing the
|
||||
|
||||
@@ -210,10 +210,12 @@ pub mod tests {
|
||||
use ark_pallas::{Fr, Projective};
|
||||
use ark_std::{ops::Mul, test_rng, UniformRand};
|
||||
|
||||
use crate::arith::r1cs::tests::{get_test_r1cs, get_test_z};
|
||||
use crate::arith::r1cs::{
|
||||
tests::{get_test_r1cs, get_test_z},
|
||||
RelaxedR1CS,
|
||||
};
|
||||
use crate::commitment::pedersen::{Params as PedersenParams, Pedersen};
|
||||
use crate::folding::nova::circuits::ChallengeGadget;
|
||||
use crate::folding::nova::traits::NovaR1CS;
|
||||
use crate::transcript::poseidon::poseidon_canonical_config;
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
@@ -316,8 +318,8 @@ pub mod tests {
|
||||
let u_i = u_dummy.clone();
|
||||
let W_i = w_dummy.clone();
|
||||
let U_i = u_dummy.clone();
|
||||
r1cs.check_relaxed_instance_relation(&w_i, &u_i).unwrap();
|
||||
r1cs.check_relaxed_instance_relation(&W_i, &U_i).unwrap();
|
||||
r1cs.check_relaxed_relation(&w_i, &u_i).unwrap();
|
||||
r1cs.check_relaxed_relation(&W_i, &U_i).unwrap();
|
||||
|
||||
let r_Fr = Fr::from(3_u32);
|
||||
|
||||
@@ -334,7 +336,7 @@ pub mod tests {
|
||||
r_Fr, &w_i, &u_i, &W_i, &U_i, &T, cmT,
|
||||
)
|
||||
.unwrap();
|
||||
r1cs.check_relaxed_instance_relation(&W_i1, &U_i1).unwrap();
|
||||
r1cs.check_relaxed_relation(&W_i1, &U_i1).unwrap();
|
||||
}
|
||||
|
||||
// fold 2 instances into one
|
||||
@@ -348,9 +350,9 @@ pub mod tests {
|
||||
assert_eq!(ci3_v, ci3);
|
||||
|
||||
// check that relations hold for the 2 inputted instances and the folded one
|
||||
r1cs.check_relaxed_instance_relation(&w1, &ci1).unwrap();
|
||||
r1cs.check_relaxed_instance_relation(&w2, &ci2).unwrap();
|
||||
r1cs.check_relaxed_instance_relation(&w3, &ci3).unwrap();
|
||||
r1cs.check_relaxed_relation(&w1, &ci1).unwrap();
|
||||
r1cs.check_relaxed_relation(&w2, &ci2).unwrap();
|
||||
r1cs.check_relaxed_relation(&w3, &ci3).unwrap();
|
||||
|
||||
// check that folded commitments from folded instance (ci) are equal to folding the
|
||||
// use folded rE, rW to commit w3
|
||||
@@ -425,7 +427,7 @@ pub mod tests {
|
||||
.commit::<Pedersen<Projective>, false>(&pedersen_params, x)
|
||||
.unwrap();
|
||||
|
||||
r1cs.check_relaxed_instance_relation(&running_instance_w, &running_committed_instance)
|
||||
r1cs.check_relaxed_relation(&running_instance_w, &running_committed_instance)
|
||||
.unwrap();
|
||||
|
||||
let num_iters = 10;
|
||||
@@ -438,11 +440,8 @@ pub mod tests {
|
||||
let incoming_committed_instance = incoming_instance_w
|
||||
.commit::<Pedersen<Projective>, false>(&pedersen_params, x)
|
||||
.unwrap();
|
||||
r1cs.check_relaxed_instance_relation(
|
||||
&incoming_instance_w,
|
||||
&incoming_committed_instance,
|
||||
)
|
||||
.unwrap();
|
||||
r1cs.check_relaxed_relation(&incoming_instance_w, &incoming_committed_instance)
|
||||
.unwrap();
|
||||
|
||||
let r = Fr::rand(&mut rng); // folding challenge would come from the RO
|
||||
|
||||
@@ -475,7 +474,7 @@ pub mod tests {
|
||||
&cmT,
|
||||
);
|
||||
|
||||
r1cs.check_relaxed_instance_relation(&folded_w, &folded_committed_instance)
|
||||
r1cs.check_relaxed_relation(&folded_w, &folded_committed_instance)
|
||||
.unwrap();
|
||||
|
||||
// set running_instance for next loop iteration
|
||||
|
||||
@@ -187,7 +187,7 @@ pub mod tests {
|
||||
use crate::{
|
||||
commitment::{kzg::KZG, pedersen::Pedersen},
|
||||
folding::nova::{Nova, PreprocessorParam},
|
||||
frontend::{tests::CubicFCircuit, FCircuit},
|
||||
frontend::{utils::CubicFCircuit, FCircuit},
|
||||
transcript::poseidon::poseidon_canonical_config,
|
||||
FoldingScheme,
|
||||
};
|
||||
|
||||
@@ -1,69 +1,90 @@
|
||||
use ark_crypto_primitives::sponge::Absorb;
|
||||
use ark_ec::{CurveGroup, Group};
|
||||
use ark_std::One;
|
||||
use ark_ec::CurveGroup;
|
||||
use ark_std::{rand::RngCore, One, UniformRand};
|
||||
|
||||
use super::{CommittedInstance, Witness};
|
||||
use crate::arith::{r1cs::R1CS, Arith};
|
||||
use crate::arith::r1cs::{RelaxedR1CS, R1CS};
|
||||
use crate::Error;
|
||||
|
||||
/// NovaR1CS extends R1CS methods with Nova specific methods
|
||||
pub trait NovaR1CS<C: CurveGroup> {
|
||||
/// returns a dummy instance (Witness and CommittedInstance) for the current R1CS structure
|
||||
fn dummy_instance(&self) -> (Witness<C>, CommittedInstance<C>);
|
||||
|
||||
/// checks the R1CS relation (un-relaxed) for the given Witness and CommittedInstance.
|
||||
fn check_instance_relation(
|
||||
&self,
|
||||
W: &Witness<C>,
|
||||
U: &CommittedInstance<C>,
|
||||
) -> Result<(), Error>;
|
||||
|
||||
/// checks the Relaxed R1CS relation (corresponding to the current R1CS) for the given Witness
|
||||
/// and CommittedInstance.
|
||||
fn check_relaxed_instance_relation(
|
||||
&self,
|
||||
W: &Witness<C>,
|
||||
U: &CommittedInstance<C>,
|
||||
) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
impl<C: CurveGroup> NovaR1CS<C> for R1CS<C::ScalarField>
|
||||
where
|
||||
<C as Group>::ScalarField: Absorb,
|
||||
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
|
||||
{
|
||||
fn dummy_instance(&self) -> (Witness<C>, CommittedInstance<C>) {
|
||||
impl<C: CurveGroup> RelaxedR1CS<C, Witness<C>, CommittedInstance<C>> for R1CS<C::ScalarField> {
|
||||
fn dummy_running_instance(&self) -> (Witness<C>, CommittedInstance<C>) {
|
||||
let w_len = self.A.n_cols - 1 - self.l;
|
||||
let w_dummy = Witness::<C>::dummy(w_len, self.A.n_rows);
|
||||
let u_dummy = CommittedInstance::<C>::dummy(self.l);
|
||||
(w_dummy, u_dummy)
|
||||
}
|
||||
|
||||
// notice that this method does not check the commitment correctness
|
||||
fn check_instance_relation(
|
||||
&self,
|
||||
W: &Witness<C>,
|
||||
U: &CommittedInstance<C>,
|
||||
) -> Result<(), Error> {
|
||||
if U.cmE != C::zero() || U.u != C::ScalarField::one() {
|
||||
return Err(Error::R1CSUnrelaxedFail);
|
||||
}
|
||||
|
||||
let Z: Vec<C::ScalarField> = [vec![U.u], U.x.to_vec(), W.W.to_vec()].concat();
|
||||
self.check_relation(&Z)
|
||||
fn dummy_incoming_instance(&self) -> (Witness<C>, CommittedInstance<C>) {
|
||||
self.dummy_running_instance()
|
||||
}
|
||||
|
||||
// notice that this method does not check the commitment correctness
|
||||
fn check_relaxed_instance_relation(
|
||||
&self,
|
||||
W: &Witness<C>,
|
||||
U: &CommittedInstance<C>,
|
||||
) -> Result<(), Error> {
|
||||
let mut rel_r1cs = self.clone().relax();
|
||||
rel_r1cs.u = U.u;
|
||||
rel_r1cs.E = W.E.clone();
|
||||
fn is_relaxed(_w: &Witness<C>, u: &CommittedInstance<C>) -> bool {
|
||||
u.cmE != C::zero() || u.u != C::ScalarField::one()
|
||||
}
|
||||
|
||||
let Z: Vec<C::ScalarField> = [vec![U.u], U.x.to_vec(), W.W.to_vec()].concat();
|
||||
rel_r1cs.check_relation(&Z)
|
||||
fn extract_z(w: &Witness<C>, u: &CommittedInstance<C>) -> Vec<C::ScalarField> {
|
||||
[&[u.u][..], &u.x, &w.W].concat()
|
||||
}
|
||||
|
||||
fn check_error_terms(
|
||||
w: &Witness<C>,
|
||||
_u: &CommittedInstance<C>,
|
||||
e: Vec<C::ScalarField>,
|
||||
) -> Result<(), Error> {
|
||||
if w.E == e {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::NotSatisfied)
|
||||
}
|
||||
}
|
||||
|
||||
fn sample<CS>(
|
||||
&self,
|
||||
params: &CS::ProverParams,
|
||||
mut rng: impl RngCore,
|
||||
) -> Result<(Witness<C>, CommittedInstance<C>), Error>
|
||||
where
|
||||
CS: crate::commitment::CommitmentScheme<C, true>,
|
||||
{
|
||||
// Implements sampling a (committed) RelaxedR1CS
|
||||
// See construction 5 in https://eprint.iacr.org/2023/573.pdf
|
||||
let u = C::ScalarField::rand(&mut rng);
|
||||
let rE = C::ScalarField::rand(&mut rng);
|
||||
let rW = C::ScalarField::rand(&mut rng);
|
||||
|
||||
let W = (0..self.A.n_cols - self.l - 1)
|
||||
.map(|_| C::ScalarField::rand(&mut rng))
|
||||
.collect();
|
||||
let x = (0..self.l)
|
||||
.map(|_| C::ScalarField::rand(&mut rng))
|
||||
.collect::<Vec<C::ScalarField>>();
|
||||
let mut z = vec![u];
|
||||
z.extend(&x);
|
||||
z.extend(&W);
|
||||
|
||||
let E = <Self as RelaxedR1CS<C, Witness<C>, CommittedInstance<C>>>::compute_E(
|
||||
&self.A, &self.B, &self.C, &z, &u,
|
||||
)?;
|
||||
|
||||
debug_assert!(
|
||||
z.len() == self.A.n_cols,
|
||||
"Length of z is {}, while A has {} columns.",
|
||||
z.len(),
|
||||
self.A.n_cols
|
||||
);
|
||||
|
||||
let witness = Witness { E, rE, W, rW };
|
||||
let mut cm_witness = witness.commit::<CS, true>(params, x)?;
|
||||
|
||||
// witness.commit() sets u to 1, we set it to the sampled u value
|
||||
cm_witness.u = u;
|
||||
|
||||
debug_assert!(
|
||||
self.check_relaxed_relation(&witness, &cm_witness).is_ok(),
|
||||
"Sampled a non satisfiable relaxed R1CS, sampled u: {}, computed E: {:?}",
|
||||
u,
|
||||
witness.E
|
||||
);
|
||||
|
||||
Ok((witness, cm_witness))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
/// paper).
|
||||
/// And the Use-case-2 would require a modified version of the Decider circuits.
|
||||
///
|
||||
use crate::folding::nova::traits::NovaR1CS;
|
||||
use ark_crypto_primitives::sponge::CryptographicSponge;
|
||||
use ark_ff::{BigInteger, PrimeField};
|
||||
use ark_std::{One, Zero};
|
||||
@@ -141,9 +140,8 @@ where
|
||||
// d. Store folding proof
|
||||
let pi = FoldingProof { cmT };
|
||||
|
||||
// 2. Sample a satisfying relaxed R1CS instance-witness pair (U_r, W_r)
|
||||
let relaxed_instance = nova.r1cs.clone().relax();
|
||||
let (U_r, W_r) = relaxed_instance.sample::<C1, CS1>(&nova.cs_pp, &mut rng)?;
|
||||
// 2. Sample a satisfying relaxed R1CS instance-witness pair (W_r, U_r)
|
||||
let (W_r, U_r) = nova.r1cs.sample::<CS1>(&nova.cs_pp, &mut rng)?;
|
||||
|
||||
// 3. Fold the instance-witness pair (U_f, W_f) with (U_r, W_r)
|
||||
// a. Compute T
|
||||
@@ -280,21 +278,10 @@ where
|
||||
);
|
||||
|
||||
// 5. Check that W^{\prime}_i is a satisfying witness
|
||||
let mut z = vec![U_i_prime.u];
|
||||
z.extend(&U_i_prime.x);
|
||||
z.extend(&proof.W_i_prime.W);
|
||||
let relaxed_r1cs = RelaxedR1CS {
|
||||
l: r1cs.l,
|
||||
A: r1cs.A.clone(),
|
||||
B: r1cs.B.clone(),
|
||||
C: r1cs.C.clone(),
|
||||
u: U_i_prime.u,
|
||||
E: proof.W_i_prime.E.clone(),
|
||||
};
|
||||
relaxed_r1cs.check_relation(&z)?;
|
||||
r1cs.check_relaxed_relation(&proof.W_i_prime, &U_i_prime)?;
|
||||
|
||||
// 6. Check that the cyclefold instance-witness pair satisfies the cyclefold relaxed r1cs
|
||||
cf_r1cs.check_relaxed_instance_relation(&proof.cf_W_i, &proof.cf_U_i)?;
|
||||
cf_r1cs.check_relaxed_relation(&proof.cf_W_i, &proof.cf_U_i)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -305,7 +292,7 @@ pub mod tests {
|
||||
use super::*;
|
||||
use crate::commitment::pedersen::Pedersen;
|
||||
use crate::folding::nova::tests::test_ivc_opt;
|
||||
use crate::frontend::tests::CubicFCircuit;
|
||||
use crate::frontend::utils::CubicFCircuit;
|
||||
use crate::transcript::poseidon::poseidon_canonical_config;
|
||||
use ark_bn254::{Fr, G1Projective as Projective};
|
||||
use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2};
|
||||
@@ -380,11 +367,9 @@ pub mod tests {
|
||||
F_circuit,
|
||||
3,
|
||||
);
|
||||
let (sampled_committed_instance, _) = nova
|
||||
let (_, sampled_committed_instance) = nova
|
||||
.r1cs
|
||||
.clone()
|
||||
.relax()
|
||||
.sample::<Projective, Pedersen<Projective, true>>(&nova.cs_pp, rng)
|
||||
.sample::<Pedersen<Projective, true>>(&nova.cs_pp, rng)
|
||||
.unwrap();
|
||||
|
||||
// proof verification fails with incorrect running instance
|
||||
@@ -419,11 +404,9 @@ pub mod tests {
|
||||
F_circuit,
|
||||
3,
|
||||
);
|
||||
let (_, sampled_committed_witness) = nova
|
||||
let (sampled_committed_witness, _) = nova
|
||||
.r1cs
|
||||
.clone()
|
||||
.relax()
|
||||
.sample::<Projective, Pedersen<Projective, true>>(&nova.cs_pp, rng)
|
||||
.sample::<Pedersen<Projective, true>>(&nova.cs_pp, rng)
|
||||
.unwrap();
|
||||
|
||||
// proof generation fails with incorrect running witness
|
||||
|
||||
Reference in New Issue
Block a user