mirror of
https://github.com/arnaucube/sonobe.git
synced 2026-01-07 14:31:31 +01:00
Generalized CycleFold (#120)
* Support randomness of arbitrary length
* Rename `N_BITS_RO` to `NOVA_N_BITS_RO`
* Compute `r_nonnat` inside `NIFSFullGadget::fold_committed_instance`
* Format
* Use `CycleFold{CommittedInstance, Witness}` in the context of cyclefold
* Format
* Fix the creation of dummy witness
* Make clippy happy
* Improve docs
This commit is contained in:
@@ -2,4 +2,4 @@
|
||||
// From [Srinath Setty](https://microsoft.com/en-us/research/people/srinath/): In Nova, soundness
|
||||
// error ≤ 2/|S|, where S is the subset of the field F from which the challenges are drawn. In this
|
||||
// case, we keep the size of S close to 2^128.
|
||||
pub const N_BITS_RO: usize = 128;
|
||||
pub const NOVA_N_BITS_RO: usize = 128;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/// Contains [CycleFold](https://eprint.iacr.org/2023/1192.pdf) related circuits and functions that
|
||||
/// are shared across the different folding schemes
|
||||
use ark_crypto_primitives::sponge::{Absorb, CryptographicSponge};
|
||||
use ark_ec::{CurveGroup, Group};
|
||||
use ark_ff::{BigInteger, PrimeField};
|
||||
use ark_ec::{AffineRepr, CurveGroup, Group};
|
||||
use ark_ff::{BigInteger, Field, PrimeField};
|
||||
use ark_r1cs_std::{
|
||||
alloc::{AllocVar, AllocationMode},
|
||||
boolean::Boolean,
|
||||
@@ -20,25 +20,22 @@ use ark_std::rand::RngCore;
|
||||
use ark_std::Zero;
|
||||
use core::{borrow::Borrow, marker::PhantomData};
|
||||
|
||||
use super::{nonnative::uint::NonNativeUintVar, CF2};
|
||||
use super::{nonnative::uint::NonNativeUintVar, CF1, CF2};
|
||||
use crate::arith::r1cs::{extract_w_x, R1CS};
|
||||
use crate::commitment::CommitmentScheme;
|
||||
use crate::constants::N_BITS_RO;
|
||||
use crate::folding::nova::{nifs::NIFS, CommittedInstance, Witness};
|
||||
use crate::frontend::FCircuit;
|
||||
use crate::transcript::{AbsorbNonNativeGadget, Transcript, TranscriptVar};
|
||||
use crate::constants::NOVA_N_BITS_RO;
|
||||
use crate::folding::nova::nifs::NIFS;
|
||||
use crate::transcript::{AbsorbNonNative, AbsorbNonNativeGadget, Transcript, TranscriptVar};
|
||||
use crate::Error;
|
||||
|
||||
/// Public inputs length for the CycleFoldCircuit:
|
||||
/// For Nova this is: |[r, p1.x,y, p2.x,y, p3.x,y]|
|
||||
/// In general, |[r * (n_points-1), (p_i.x,y)*n_points, p_folded.x,y]|, thus, io len is:
|
||||
/// (n_points-1) + 2*n_points + 2
|
||||
pub fn cf_io_len(n_points: usize) -> usize {
|
||||
(n_points - 1) + 2 * n_points + 2
|
||||
}
|
||||
/// Re-export the Nova committed instance as `CycleFoldCommittedInstance` and
|
||||
/// witness as `CycleFoldWitness`, for clarity and consistency
|
||||
pub use crate::folding::nova::{
|
||||
CommittedInstance as CycleFoldCommittedInstance, Witness as CycleFoldWitness,
|
||||
};
|
||||
|
||||
/// CycleFoldCommittedInstanceVar is the CycleFold CommittedInstance representation in the Nova
|
||||
/// circuit.
|
||||
/// CycleFoldCommittedInstanceVar is the CycleFold CommittedInstance represented
|
||||
/// in folding verifier circuit
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CycleFoldCommittedInstanceVar<C: CurveGroup, GC: CurveVar<C, CF2<C>>>
|
||||
where
|
||||
@@ -49,14 +46,14 @@ where
|
||||
pub cmW: GC,
|
||||
pub x: Vec<NonNativeUintVar<CF2<C>>>,
|
||||
}
|
||||
impl<C, GC> AllocVar<CommittedInstance<C>, CF2<C>> for CycleFoldCommittedInstanceVar<C, GC>
|
||||
impl<C, GC> AllocVar<CycleFoldCommittedInstance<C>, CF2<C>> for CycleFoldCommittedInstanceVar<C, GC>
|
||||
where
|
||||
C: CurveGroup,
|
||||
GC: CurveVar<C, CF2<C>>,
|
||||
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
|
||||
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
||||
{
|
||||
fn new_variable<T: Borrow<CommittedInstance<C>>>(
|
||||
fn new_variable<T: Borrow<CycleFoldCommittedInstance<C>>>(
|
||||
cs: impl Into<Namespace<CF2<C>>>,
|
||||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
||||
mode: AllocationMode,
|
||||
@@ -74,6 +71,29 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: CurveGroup> AbsorbNonNative<C::BaseField> for CycleFoldCommittedInstance<C>
|
||||
where
|
||||
C::BaseField: PrimeField + Absorb,
|
||||
{
|
||||
// Compatible with the in-circuit `CycleFoldCommittedInstanceVar::to_native_sponge_field_elements`
|
||||
fn to_native_sponge_field_elements(&self, dest: &mut Vec<C::BaseField>) {
|
||||
[self.u].to_native_sponge_field_elements(dest);
|
||||
self.x.to_native_sponge_field_elements(dest);
|
||||
let (cmE_x, cmE_y) = match self.cmE.into_affine().xy() {
|
||||
Some((&x, &y)) => (x, y),
|
||||
None => (C::BaseField::zero(), C::BaseField::zero()),
|
||||
};
|
||||
let (cmW_x, cmW_y) = match self.cmW.into_affine().xy() {
|
||||
Some((&x, &y)) => (x, y),
|
||||
None => (C::BaseField::zero(), C::BaseField::zero()),
|
||||
};
|
||||
cmE_x.to_sponge_field_elements(dest);
|
||||
cmE_y.to_sponge_field_elements(dest);
|
||||
cmW_x.to_sponge_field_elements(dest);
|
||||
cmW_y.to_sponge_field_elements(dest);
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, GC> AbsorbNonNativeGadget<C::BaseField> for CycleFoldCommittedInstanceVar<C, GC>
|
||||
where
|
||||
C: CurveGroup,
|
||||
@@ -107,6 +127,25 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: CurveGroup> CycleFoldCommittedInstance<C>
|
||||
where
|
||||
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField + Absorb,
|
||||
{
|
||||
/// hash_cyclefold implements the committed instance hash compatible with the
|
||||
/// in-circuit implementation `CycleFoldCommittedInstanceVar::hash`.
|
||||
/// Returns `H(U_i)`, where `U_i` is a `CycleFoldCommittedInstance`.
|
||||
pub fn hash_cyclefold<T: Transcript<C::BaseField>>(
|
||||
&self,
|
||||
sponge: &T,
|
||||
pp_hash: C::BaseField, // public params hash
|
||||
) -> C::BaseField {
|
||||
let mut sponge = sponge.clone();
|
||||
sponge.absorb(&pp_hash);
|
||||
sponge.absorb_nonnative(self);
|
||||
sponge.squeeze_field_elements(1)[0]
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, GC> CycleFoldCommittedInstanceVar<C, GC>
|
||||
where
|
||||
C: CurveGroup,
|
||||
@@ -114,11 +153,13 @@ where
|
||||
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField + Absorb,
|
||||
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
||||
{
|
||||
/// hash implements the committed instance hash compatible with the native implementation from
|
||||
/// CommittedInstance.hash_cyclefold. Returns `H(U_i)`, where `U` is the `CommittedInstance`
|
||||
/// for CycleFold. Additionally it returns the vector of the field elements from the self
|
||||
/// parameters, so they can be reused in other gadgets avoiding recalculating (reconstraining)
|
||||
/// them.
|
||||
/// hash implements the committed instance hash compatible with the native
|
||||
/// implementation `CycleFoldCommittedInstance::hash_cyclefold`.
|
||||
/// Returns `H(U_i)`, where `U` is a `CycleFoldCommittedInstanceVar`.
|
||||
///
|
||||
/// Additionally it returns the vector of the field elements from the self
|
||||
/// parameters, so they can be reused in other gadgets without recalculating
|
||||
/// (reconstraining) them.
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn hash<S: CryptographicSponge, T: TranscriptVar<CF2<C>, S>>(
|
||||
self,
|
||||
@@ -147,13 +188,14 @@ where
|
||||
pub cmW: GC,
|
||||
}
|
||||
|
||||
impl<C, GC> AllocVar<CommittedInstance<C>, CF2<C>> for CommittedInstanceInCycleFoldVar<C, GC>
|
||||
impl<C, GC> AllocVar<CycleFoldCommittedInstance<C>, CF2<C>>
|
||||
for CommittedInstanceInCycleFoldVar<C, GC>
|
||||
where
|
||||
C: CurveGroup,
|
||||
GC: CurveVar<C, CF2<C>>,
|
||||
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
||||
{
|
||||
fn new_variable<T: Borrow<CommittedInstance<C>>>(
|
||||
fn new_variable<T: Borrow<CycleFoldCommittedInstance<C>>>(
|
||||
cs: impl Into<Namespace<CF2<C>>>,
|
||||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
||||
mode: AllocationMode,
|
||||
@@ -189,25 +231,29 @@ where
|
||||
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
||||
{
|
||||
pub fn fold_committed_instance(
|
||||
// assumes that r_bits is equal to r_nonnat just that in a different format
|
||||
r_bits: Vec<Boolean<CF2<C>>>,
|
||||
r_nonnat: NonNativeUintVar<CF2<C>>,
|
||||
cmT: GC,
|
||||
ci1: CycleFoldCommittedInstanceVar<C, GC>,
|
||||
// ci2 is assumed to be always with cmE=0, u=1 (checks done previous to this method)
|
||||
ci2: CycleFoldCommittedInstanceVar<C, GC>,
|
||||
) -> Result<CycleFoldCommittedInstanceVar<C, GC>, SynthesisError> {
|
||||
// r_nonnat is equal to r_bits just that in a different format
|
||||
let r_nonnat = {
|
||||
let mut bits = r_bits.clone();
|
||||
bits.resize(CF1::<C>::MODULUS_BIT_SIZE as usize, Boolean::FALSE);
|
||||
NonNativeUintVar::from(&bits)
|
||||
};
|
||||
Ok(CycleFoldCommittedInstanceVar {
|
||||
cmE: cmT.scalar_mul_le(r_bits.iter())? + ci1.cmE,
|
||||
cmW: ci1.cmW + ci2.cmW.scalar_mul_le(r_bits.iter())?,
|
||||
u: ci1.u.add_no_align(&r_nonnat).modulo::<C::ScalarField>()?,
|
||||
u: ci1.u.add_no_align(&r_nonnat).modulo::<CF1<C>>()?,
|
||||
x: ci1
|
||||
.x
|
||||
.iter()
|
||||
.zip(ci2.x)
|
||||
.map(|(a, b)| {
|
||||
a.add_no_align(&r_nonnat.mul_no_align(&b)?)
|
||||
.modulo::<C::ScalarField>()
|
||||
.modulo::<CF1<C>>()
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
})
|
||||
@@ -216,14 +262,13 @@ where
|
||||
pub fn verify(
|
||||
// assumes that r_bits is equal to r_nonnat just that in a different format
|
||||
r_bits: Vec<Boolean<CF2<C>>>,
|
||||
r_nonnat: NonNativeUintVar<CF2<C>>,
|
||||
cmT: GC,
|
||||
ci1: CycleFoldCommittedInstanceVar<C, GC>,
|
||||
// ci2 is assumed to be always with cmE=0, u=1 (checks done previous to this method)
|
||||
ci2: CycleFoldCommittedInstanceVar<C, GC>,
|
||||
ci3: CycleFoldCommittedInstanceVar<C, GC>,
|
||||
) -> Result<(), SynthesisError> {
|
||||
let ci = Self::fold_committed_instance(r_bits, r_nonnat, cmT, ci1, ci2)?;
|
||||
let ci = Self::fold_committed_instance(r_bits, cmT, ci1, ci2)?;
|
||||
|
||||
ci.cmE.enforce_equal(&ci3.cmE)?;
|
||||
ci.u.enforce_equal_unaligned(&ci3.u)?;
|
||||
@@ -253,15 +298,15 @@ where
|
||||
pub fn get_challenge_native<T: Transcript<C::BaseField>>(
|
||||
transcript: &mut T,
|
||||
pp_hash: C::BaseField, // public params hash
|
||||
U_i: CommittedInstance<C>,
|
||||
u_i: CommittedInstance<C>,
|
||||
U_i: CycleFoldCommittedInstance<C>,
|
||||
u_i: CycleFoldCommittedInstance<C>,
|
||||
cmT: C,
|
||||
) -> Vec<bool> {
|
||||
transcript.absorb(&pp_hash);
|
||||
transcript.absorb_nonnative(&U_i);
|
||||
transcript.absorb_nonnative(&u_i);
|
||||
transcript.absorb_point(&cmT);
|
||||
transcript.squeeze_bits(N_BITS_RO)
|
||||
transcript.squeeze_bits(NOVA_N_BITS_RO)
|
||||
}
|
||||
|
||||
// compatible with the native get_challenge_native
|
||||
@@ -276,63 +321,101 @@ where
|
||||
transcript.absorb(&U_i_vec)?;
|
||||
transcript.absorb_nonnative(&u_i)?;
|
||||
transcript.absorb_point(&cmT)?;
|
||||
transcript.squeeze_bits(N_BITS_RO)
|
||||
transcript.squeeze_bits(NOVA_N_BITS_RO)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CycleFoldConfig {
|
||||
/// `N_INPUT_POINTS` specifies the number of input points that are folded in
|
||||
/// [`CycleFoldCircuit`] via random linear combinations.
|
||||
const N_INPUT_POINTS: usize;
|
||||
/// `RANDOMNESS_BIT_LENGTH` is the (maximum) bit length of randomness `r`.
|
||||
const RANDOMNESS_BIT_LENGTH: usize;
|
||||
/// `FIELD_CAPACITY` is the maximum number of bits that can be stored in a
|
||||
/// field element.
|
||||
///
|
||||
/// E.g., given a randomness `r` with `RANDOMNESS_BIT_LENGTH` bits, we need
|
||||
/// `RANDOMNESS_BIT_LENGTH / FIELD_CAPACITY` field elements to represent `r`
|
||||
/// compactly in-circuit.
|
||||
const FIELD_CAPACITY: usize = CF2::<Self::C>::MODULUS_BIT_SIZE as usize - 1;
|
||||
|
||||
/// Public inputs length for the CycleFoldCircuit.
|
||||
/// * For Nova this is: `|[r, p1.x,y, p2.x,y, p3.x,y]|`
|
||||
/// * In general, `|[r * (n_points-1), (p_i.x,y)*n_points, p_folded.x,y]|`.
|
||||
///
|
||||
/// Thus, `IO_LEN` is:
|
||||
/// `RANDOMNESS_BIT_LENGTH / FIELD_CAPACITY * (N_INPUT_POINTS - 1) + 2 * N_INPUT_POINTS + 2`
|
||||
const IO_LEN: usize = {
|
||||
Self::RANDOMNESS_BIT_LENGTH.div_ceil(Self::FIELD_CAPACITY) * (Self::N_INPUT_POINTS - 1)
|
||||
+ 2 * Self::N_INPUT_POINTS
|
||||
+ 2
|
||||
};
|
||||
|
||||
type F: Field;
|
||||
type C: CurveGroup<BaseField = Self::F>;
|
||||
}
|
||||
|
||||
/// CycleFoldCircuit contains the constraints that check the correct fold of the committed
|
||||
/// instances from Curve1. Namely, it checks the random linear combinations of the elliptic curve
|
||||
/// (Curve1) points of u_i, U_i leading to U_{i+1}
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CycleFoldCircuit<C: CurveGroup, GC: CurveVar<C, CF2<C>>> {
|
||||
pub struct CycleFoldCircuit<CFG: CycleFoldConfig, GC: CurveVar<CFG::C, CFG::F>> {
|
||||
pub _gc: PhantomData<GC>,
|
||||
/// number of points being folded
|
||||
pub n_points: usize,
|
||||
/// r_bits is a vector containing the r_bits, one for each point except for the first one. They
|
||||
/// are used for the scalar multiplication of the points. The r_bits are the bit
|
||||
/// representation of each power of r (in Fr, while the CycleFoldCircuit is in Fq).
|
||||
pub r_bits: Option<Vec<Vec<bool>>>,
|
||||
/// points to be folded in the CycleFoldCircuit
|
||||
pub points: Option<Vec<C>>,
|
||||
pub x: Option<Vec<CF2<C>>>, // public inputs (cf_u_{i+1}.x)
|
||||
pub points: Option<Vec<CFG::C>>,
|
||||
/// public inputs (cf_u_{i+1}.x)
|
||||
pub x: Option<Vec<CFG::F>>,
|
||||
}
|
||||
impl<C: CurveGroup, GC: CurveVar<C, CF2<C>>> CycleFoldCircuit<C, GC> {
|
||||
|
||||
impl<CFG: CycleFoldConfig, GC: CurveVar<CFG::C, CFG::F>> CycleFoldCircuit<CFG, GC> {
|
||||
/// n_points indicates the number of points being folded in the CycleFoldCircuit
|
||||
pub fn empty(n_points: usize) -> Self {
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
_gc: PhantomData,
|
||||
n_points,
|
||||
r_bits: None,
|
||||
points: None,
|
||||
x: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<C, GC> ConstraintSynthesizer<CF2<C>> for CycleFoldCircuit<C, GC>
|
||||
|
||||
impl<CFG: CycleFoldConfig, GC: CurveVar<CFG::C, CFG::F>> ConstraintSynthesizer<CFG::F>
|
||||
for CycleFoldCircuit<CFG, GC>
|
||||
where
|
||||
C: CurveGroup,
|
||||
GC: CurveVar<C, CF2<C>> + ToConstraintFieldGadget<CF2<C>>,
|
||||
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
|
||||
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
||||
GC: ToConstraintFieldGadget<CFG::F>,
|
||||
CFG::F: PrimeField,
|
||||
for<'a> &'a GC: GroupOpsBounds<'a, CFG::C, GC>,
|
||||
{
|
||||
fn generate_constraints(self, cs: ConstraintSystemRef<CF2<C>>) -> Result<(), SynthesisError> {
|
||||
let r_bits: Vec<Vec<Boolean<CF2<C>>>> = self
|
||||
fn generate_constraints(self, cs: ConstraintSystemRef<CFG::F>) -> Result<(), SynthesisError> {
|
||||
let r_bits: Vec<Vec<Boolean<CFG::F>>> = self
|
||||
.r_bits
|
||||
// n_points-1, bcs is one for each point except for the first one
|
||||
.unwrap_or(vec![vec![false; N_BITS_RO]; self.n_points - 1])
|
||||
.unwrap_or(vec![
|
||||
vec![false; CFG::RANDOMNESS_BIT_LENGTH];
|
||||
CFG::N_INPUT_POINTS - 1
|
||||
])
|
||||
.iter()
|
||||
.map(|r_bits_i| {
|
||||
Vec::<Boolean<CF2<C>>>::new_witness(cs.clone(), || Ok(r_bits_i.clone()))
|
||||
Vec::<Boolean<CFG::F>>::new_witness(cs.clone(), || Ok(r_bits_i.clone()))
|
||||
})
|
||||
.collect::<Result<Vec<Vec<Boolean<CF2<C>>>>, SynthesisError>>()?;
|
||||
.collect::<Result<_, _>>()?;
|
||||
let points = Vec::<GC>::new_witness(cs.clone(), || {
|
||||
Ok(self.points.unwrap_or(vec![C::zero(); self.n_points]))
|
||||
Ok(self
|
||||
.points
|
||||
.unwrap_or(vec![CFG::C::zero(); CFG::N_INPUT_POINTS]))
|
||||
})?;
|
||||
|
||||
#[cfg(test)]
|
||||
{
|
||||
assert_eq!(self.n_points, points.len());
|
||||
assert_eq!(self.n_points - 1, r_bits.len());
|
||||
assert_eq!(CFG::N_INPUT_POINTS, points.len());
|
||||
assert_eq!(CFG::N_INPUT_POINTS - 1, r_bits.len());
|
||||
for r_bits_i in &r_bits {
|
||||
assert_eq!(r_bits_i.len(), CFG::RANDOMNESS_BIT_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
// Fold the original points of the instances natively in CycleFold.
|
||||
@@ -343,36 +426,37 @@ where
|
||||
let mut p_folded: GC = points[0].clone();
|
||||
// iter over n_points-1 because the first point is not multiplied by r^i (it is multiplied
|
||||
// by r^0=1)
|
||||
for i in 0..self.n_points - 1 {
|
||||
for i in 0..CFG::N_INPUT_POINTS - 1 {
|
||||
p_folded += points[i + 1].scalar_mul_le(r_bits[i].iter())?;
|
||||
}
|
||||
|
||||
let x = Vec::<FpVar<CF2<C>>>::new_input(cs.clone(), || {
|
||||
Ok(self
|
||||
.x
|
||||
.unwrap_or(vec![CF2::<C>::zero(); cf_io_len(self.n_points)]))
|
||||
let x = Vec::<FpVar<CFG::F>>::new_input(cs.clone(), || {
|
||||
Ok(self.x.unwrap_or(vec![CFG::F::zero(); CFG::IO_LEN]))
|
||||
})?;
|
||||
#[cfg(test)]
|
||||
assert_eq!(x.len(), cf_io_len(self.n_points)); // non-constrained sanity check
|
||||
assert_eq!(x.len(), CFG::IO_LEN); // non-constrained sanity check
|
||||
|
||||
// Check that the points coordinates are placed as the public input x:
|
||||
// In Nova, this is: x == [r, p1, p2, p3] (wheere p3 is the p_folded).
|
||||
// In multifolding schemes such as HyperNova, this is:
|
||||
// computed_x = [r_0, r_1, r_2, ..., r_n, p_0, p_1, p_2, ..., p_n, p_folded],
|
||||
// where each p_i is in fact p_i.to_constraint_field()
|
||||
let computed_x: Vec<FpVar<CF2<C>>> = [
|
||||
r_bits
|
||||
.iter()
|
||||
.map(|r_bits_i| Boolean::le_bits_to_fp_var(r_bits_i))
|
||||
.collect::<Result<Vec<FpVar<CF2<C>>>, SynthesisError>>()?,
|
||||
points
|
||||
.iter()
|
||||
.map(|p_i| Ok(p_i.to_constraint_field()?[..2].to_vec()))
|
||||
.collect::<Result<Vec<Vec<FpVar<CF2<C>>>>, SynthesisError>>()?
|
||||
.concat(),
|
||||
p_folded.to_constraint_field()?[..2].to_vec(),
|
||||
]
|
||||
.concat();
|
||||
let computed_x: Vec<FpVar<CFG::F>> = r_bits
|
||||
.iter()
|
||||
.map(|r_bits_i| {
|
||||
r_bits_i
|
||||
.chunks(CFG::FIELD_CAPACITY)
|
||||
.map(Boolean::le_bits_to_fp_var)
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
})
|
||||
.chain(
|
||||
points
|
||||
.iter()
|
||||
.chain(&[p_folded])
|
||||
.map(|p_i| Ok(p_i.to_constraint_field()?[..2].to_vec())),
|
||||
)
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
.concat();
|
||||
computed_x.enforce_equal(&x)?;
|
||||
|
||||
Ok(())
|
||||
@@ -383,35 +467,33 @@ where
|
||||
/// scheme struct because it is used both by Nova & HyperNova's CycleFold.
|
||||
#[allow(clippy::type_complexity)]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn fold_cyclefold_circuit<C1, GC1, C2, GC2, FC, CS1, CS2, const H: bool>(
|
||||
_n_points: usize,
|
||||
pub fn fold_cyclefold_circuit<CFG, C1, GC1, C2, GC2, CS2, const H: bool>(
|
||||
transcript: &mut impl Transcript<C1::ScalarField>,
|
||||
cf_r1cs: R1CS<C2::ScalarField>,
|
||||
cf_cs_params: CS2::ProverParams,
|
||||
pp_hash: C1::ScalarField, // public params hash
|
||||
cf_W_i: Witness<C2>, // witness of the running instance
|
||||
cf_U_i: CommittedInstance<C2>, // running instance
|
||||
pp_hash: C1::ScalarField, // public params hash
|
||||
cf_W_i: CycleFoldWitness<C2>, // witness of the running instance
|
||||
cf_U_i: CycleFoldCommittedInstance<C2>, // running instance
|
||||
cf_u_i_x: Vec<C2::ScalarField>,
|
||||
cf_circuit: CycleFoldCircuit<C1, GC1>,
|
||||
cf_circuit: CycleFoldCircuit<CFG, GC1>,
|
||||
mut rng: impl RngCore,
|
||||
) -> Result<
|
||||
(
|
||||
Witness<C2>,
|
||||
CommittedInstance<C2>, // u_i
|
||||
Witness<C2>, // W_i1
|
||||
CommittedInstance<C2>, // U_i1
|
||||
C2, // cmT
|
||||
C2::ScalarField, // r_Fq
|
||||
CycleFoldWitness<C2>,
|
||||
CycleFoldCommittedInstance<C2>, // u_i
|
||||
CycleFoldWitness<C2>, // W_i1
|
||||
CycleFoldCommittedInstance<C2>, // U_i1
|
||||
C2, // cmT
|
||||
C2::ScalarField, // r_Fq
|
||||
),
|
||||
Error,
|
||||
>
|
||||
where
|
||||
CFG: CycleFoldConfig<C = C1, F = CF2<C1>>,
|
||||
C1: CurveGroup,
|
||||
GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
|
||||
C2: CurveGroup,
|
||||
GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>,
|
||||
FC: FCircuit<C1::ScalarField>,
|
||||
CS1: CommitmentScheme<C1, H>,
|
||||
CS2: CommitmentScheme<C2, H>,
|
||||
<C1 as CurveGroup>::BaseField: PrimeField,
|
||||
<C2 as CurveGroup>::BaseField: PrimeField,
|
||||
@@ -431,11 +513,12 @@ where
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
assert_eq!(cf_x_i.len(), cf_io_len(_n_points));
|
||||
assert_eq!(cf_x_i.len(), CFG::IO_LEN);
|
||||
|
||||
// fold cyclefold instances
|
||||
let cf_w_i = Witness::<C2>::new::<H>(cf_w_i.clone(), cf_r1cs.A.n_rows, &mut rng);
|
||||
let cf_u_i: CommittedInstance<C2> = cf_w_i.commit::<CS2, H>(&cf_cs_params, cf_x_i.clone())?;
|
||||
let cf_w_i = CycleFoldWitness::<C2>::new::<H>(cf_w_i.clone(), cf_r1cs.A.n_rows, &mut rng);
|
||||
let cf_u_i: CycleFoldCommittedInstance<C2> =
|
||||
cf_w_i.commit::<CS2, H>(&cf_cs_params, cf_x_i.clone())?;
|
||||
|
||||
// compute T* and cmT* for CycleFoldCircuit
|
||||
let (cf_T, cf_cmT) = NIFS::<C2, CS2, H>::compute_cyclefold_cmT(
|
||||
@@ -478,11 +561,22 @@ pub mod tests {
|
||||
use crate::transcript::poseidon::poseidon_canonical_config;
|
||||
use crate::utils::get_cm_coordinates;
|
||||
|
||||
struct TestCycleFoldConfig<C: CurveGroup> {
|
||||
_c: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<C: CurveGroup> CycleFoldConfig for TestCycleFoldConfig<C> {
|
||||
const RANDOMNESS_BIT_LENGTH: usize = NOVA_N_BITS_RO;
|
||||
const N_INPUT_POINTS: usize = 2;
|
||||
type C = C;
|
||||
type F = C::BaseField;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_committed_instance_cyclefold_var() {
|
||||
let mut rng = ark_std::test_rng();
|
||||
|
||||
let ci = CommittedInstance::<Projective> {
|
||||
let ci = CycleFoldCommittedInstance::<Projective> {
|
||||
cmE: Projective::rand(&mut rng),
|
||||
u: Fr::rand(&mut rng),
|
||||
cmW: Projective::rand(&mut rng),
|
||||
@@ -516,9 +610,8 @@ pub mod tests {
|
||||
get_cm_coordinates(&ci3.cmW),
|
||||
]
|
||||
.concat();
|
||||
let cfW_circuit = CycleFoldCircuit::<Projective, GVar> {
|
||||
let cfW_circuit = CycleFoldCircuit::<TestCycleFoldConfig<Projective>, GVar> {
|
||||
_gc: PhantomData,
|
||||
n_points: 2,
|
||||
r_bits: Some(vec![r_bits.clone()]),
|
||||
points: Some(vec![ci1.clone().cmW, ci2.clone().cmW]),
|
||||
x: Some(cfW_u_i_x.clone()),
|
||||
@@ -535,9 +628,8 @@ pub mod tests {
|
||||
get_cm_coordinates(&ci3.cmE),
|
||||
]
|
||||
.concat();
|
||||
let cfE_circuit = CycleFoldCircuit::<Projective, GVar> {
|
||||
let cfE_circuit = CycleFoldCircuit::<TestCycleFoldConfig<Projective>, GVar> {
|
||||
_gc: PhantomData,
|
||||
n_points: 2,
|
||||
r_bits: Some(vec![r_bits.clone()]),
|
||||
points: Some(vec![ci1.clone().cmE, cmT]),
|
||||
x: Some(cfE_u_i_x.clone()),
|
||||
@@ -548,11 +640,10 @@ pub mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_nifs_full_gadget() {
|
||||
let (_, _, _, _, ci1, _, ci2, _, ci3, _, cmT, r_bits, r_Fr) = prepare_simple_fold_inputs();
|
||||
let (_, _, _, _, ci1, _, ci2, _, ci3, _, cmT, r_bits, _) = prepare_simple_fold_inputs();
|
||||
|
||||
let cs = ConstraintSystem::<Fq>::new_ref();
|
||||
|
||||
let r_nonnatVar = NonNativeUintVar::<Fq>::new_witness(cs.clone(), || Ok(r_Fr)).unwrap();
|
||||
let r_bitsVar = Vec::<Boolean<Fq>>::new_witness(cs.clone(), || Ok(r_bits)).unwrap();
|
||||
|
||||
let ci1Var =
|
||||
@@ -572,15 +663,8 @@ pub mod tests {
|
||||
.unwrap();
|
||||
let cmTVar = GVar::new_witness(cs.clone(), || Ok(cmT)).unwrap();
|
||||
|
||||
NIFSFullGadget::<Projective, GVar>::verify(
|
||||
r_bitsVar,
|
||||
r_nonnatVar,
|
||||
cmTVar,
|
||||
ci1Var,
|
||||
ci2Var,
|
||||
ci3Var,
|
||||
)
|
||||
.unwrap();
|
||||
NIFSFullGadget::<Projective, GVar>::verify(r_bitsVar, cmTVar, ci1Var, ci2Var, ci3Var)
|
||||
.unwrap();
|
||||
assert!(cs.is_satisfied().unwrap());
|
||||
}
|
||||
|
||||
@@ -590,20 +674,20 @@ pub mod tests {
|
||||
let poseidon_config = poseidon_canonical_config::<Fq>();
|
||||
let mut transcript = PoseidonSponge::<Fq>::new(&poseidon_config);
|
||||
|
||||
let u_i = CommittedInstance::<Projective> {
|
||||
let u_i = CycleFoldCommittedInstance::<Projective> {
|
||||
cmE: Projective::zero(), // zero on purpose, so we test also the zero point case
|
||||
u: Fr::zero(),
|
||||
cmW: Projective::rand(&mut rng),
|
||||
x: std::iter::repeat_with(|| Fr::rand(&mut rng))
|
||||
.take(7) // 7 = cf_io_len
|
||||
.take(TestCycleFoldConfig::<Projective>::IO_LEN)
|
||||
.collect(),
|
||||
};
|
||||
let U_i = CommittedInstance::<Projective> {
|
||||
let U_i = CycleFoldCommittedInstance::<Projective> {
|
||||
cmE: Projective::rand(&mut rng),
|
||||
u: Fr::rand(&mut rng),
|
||||
cmW: Projective::rand(&mut rng),
|
||||
x: std::iter::repeat_with(|| Fr::rand(&mut rng))
|
||||
.take(7) // 7 = cf_io_len
|
||||
.take(TestCycleFoldConfig::<Projective>::IO_LEN)
|
||||
.collect(),
|
||||
};
|
||||
let cmT = Projective::rand(&mut rng);
|
||||
@@ -657,12 +741,12 @@ pub mod tests {
|
||||
let poseidon_config = poseidon_canonical_config::<Fq>();
|
||||
let sponge = PoseidonSponge::<Fq>::new(&poseidon_config);
|
||||
|
||||
let U_i = CommittedInstance::<Projective> {
|
||||
let U_i = CycleFoldCommittedInstance::<Projective> {
|
||||
cmE: Projective::rand(&mut rng),
|
||||
u: Fr::rand(&mut rng),
|
||||
cmW: Projective::rand(&mut rng),
|
||||
x: std::iter::repeat_with(|| Fr::rand(&mut rng))
|
||||
.take(7) // 7 = cf_io_len in Nova
|
||||
.take(TestCycleFoldConfig::<Projective>::IO_LEN)
|
||||
.collect(),
|
||||
};
|
||||
let pp_hash = Fq::from(42u32); // only for test
|
||||
|
||||
@@ -26,7 +26,7 @@ pub struct CCCS<C: CurveGroup> {
|
||||
}
|
||||
|
||||
impl<F: PrimeField> CCS<F> {
|
||||
pub fn to_cccs<R: Rng, C: CurveGroup, CS: CommitmentScheme<C, H>, const H: bool>(
|
||||
pub fn to_cccs<R: Rng, C, CS: CommitmentScheme<C, H>, const H: bool>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
cs_params: &CS::ProverParams,
|
||||
|
||||
@@ -26,20 +26,21 @@ use super::{
|
||||
cccs::CCCS,
|
||||
lcccs::LCCCS,
|
||||
nimfs::{NIMFSProof, NIMFS},
|
||||
Witness,
|
||||
HyperNovaCycleFoldConfig, Witness,
|
||||
};
|
||||
use crate::constants::N_BITS_RO;
|
||||
use crate::constants::NOVA_N_BITS_RO;
|
||||
use crate::folding::{
|
||||
circuits::cyclefold::{
|
||||
cf_io_len, CycleFoldChallengeGadget, CycleFoldCommittedInstanceVar, NIFSFullGadget,
|
||||
},
|
||||
circuits::{
|
||||
cyclefold::{
|
||||
CycleFoldChallengeGadget, CycleFoldCommittedInstance, CycleFoldCommittedInstanceVar,
|
||||
CycleFoldConfig, NIFSFullGadget,
|
||||
},
|
||||
nonnative::{affine::NonNativeAffineVar, uint::NonNativeUintVar},
|
||||
sum_check::{IOPProofVar, SumCheckVerifierGadget, VPAuxInfoVar},
|
||||
utils::EqEvalGadget,
|
||||
CF1, CF2,
|
||||
},
|
||||
nova::{get_r1cs_from_cs, CommittedInstance},
|
||||
nova::get_r1cs_from_cs,
|
||||
};
|
||||
use crate::frontend::FCircuit;
|
||||
use crate::utils::virtual_polynomial::VPAuxInfo;
|
||||
@@ -313,7 +314,7 @@ where
|
||||
let rho_scalar_raw = C::ScalarField::from_le_bytes_mod_order(b"rho");
|
||||
let rho_scalar: FpVar<CF1<C>> = FpVar::<CF1<C>>::new_constant(cs.clone(), rho_scalar_raw)?;
|
||||
transcript.absorb(&rho_scalar)?;
|
||||
let rho_bits: Vec<Boolean<CF1<C>>> = transcript.get_challenge_nbits(N_BITS_RO)?;
|
||||
let rho_bits: Vec<Boolean<CF1<C>>> = transcript.get_challenge_nbits(NOVA_N_BITS_RO)?;
|
||||
let rho = Boolean::le_bits_to_fp_var(&rho_bits)?;
|
||||
|
||||
// Self::fold will return the folded instance, together with the rho's powers vector so
|
||||
@@ -342,7 +343,7 @@ where
|
||||
let mut v_folded: Vec<FpVar<CF1<C>>> = vec![FpVar::zero(); sigmas[0].len()];
|
||||
|
||||
let mut rho_vec: Vec<Vec<Boolean<CF1<C>>>> =
|
||||
vec![vec![Boolean::FALSE; N_BITS_RO]; lcccs.len() + cccs.len() - 1];
|
||||
vec![vec![Boolean::FALSE; NOVA_N_BITS_RO]; lcccs.len() + cccs.len() - 1];
|
||||
let mut rho_i = FpVar::one();
|
||||
for i in 0..(lcccs.len() + cccs.len()) {
|
||||
let u: FpVar<CF1<C>>;
|
||||
@@ -381,12 +382,12 @@ where
|
||||
|
||||
// compute the next power of rho
|
||||
rho_i *= rho.clone();
|
||||
// crop the size of rho_i to N_BITS_RO
|
||||
// crop the size of rho_i to NOVA_N_BITS_RO
|
||||
let rho_i_bits = rho_i.to_bits_le()?;
|
||||
rho_i = Boolean::le_bits_to_fp_var(&rho_i_bits[..N_BITS_RO])?;
|
||||
rho_i = Boolean::le_bits_to_fp_var(&rho_i_bits[..NOVA_N_BITS_RO])?;
|
||||
if i < lcccs.len() + cccs.len() - 1 {
|
||||
// store the cropped rho_i into the rho_vec
|
||||
rho_vec[i] = rho_i_bits[..N_BITS_RO].to_vec();
|
||||
rho_vec[i] = rho_i_bits[..NOVA_N_BITS_RO].to_vec();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,12 +457,32 @@ fn compute_c_gadget<F: PrimeField>(
|
||||
Ok(c)
|
||||
}
|
||||
|
||||
/// `AugmentedFCircuit` enhances the original step function `F`, so that it can
|
||||
/// be used in recursive arguments such as IVC.
|
||||
///
|
||||
/// The method for converting `F` to `AugmentedFCircuit` (`F'`) is defined in
|
||||
/// [Nova](https://eprint.iacr.org/2021/370.pdf), where `AugmentedFCircuit` not
|
||||
/// only invokes `F`, but also adds additional constraints for verifying the
|
||||
/// correct folding of primary instances (i.e., the instances over `C1`).
|
||||
/// In the paper, the primary instances are Nova's `CommittedInstance`, but we
|
||||
/// extend this method to support using HyperNova's `LCCCS` and `CCCS` instances
|
||||
/// as primary instances.
|
||||
///
|
||||
/// Furthermore, to reduce circuit size over `C2`, we implement the constraints
|
||||
/// defined in [CycleFold](https://eprint.iacr.org/2023/1192.pdf). These extra
|
||||
/// constraints verify the correct folding of CycleFold instances.
|
||||
///
|
||||
/// For multi-instance folding, one needs to specify the const generics below:
|
||||
/// * `MU` - the number of LCCCS instances to be folded
|
||||
/// * `NU` - the number of CCCS instances to be folded
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AugmentedFCircuit<
|
||||
C1: CurveGroup,
|
||||
C2: CurveGroup,
|
||||
GC2: CurveVar<C2, CF2<C2>>,
|
||||
FC: FCircuit<CF1<C1>>,
|
||||
const MU: usize,
|
||||
const NU: usize,
|
||||
> where
|
||||
for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>,
|
||||
{
|
||||
@@ -470,8 +491,6 @@ pub struct AugmentedFCircuit<
|
||||
pub poseidon_config: PoseidonConfig<CF1<C1>>,
|
||||
pub ccs: CCS<C1::ScalarField>, // CCS of the AugmentedFCircuit
|
||||
pub pp_hash: Option<CF1<C1>>,
|
||||
pub mu: usize, // max number of LCCCS instances to be folded
|
||||
pub nu: usize, // max number of CCCS instances to be folded
|
||||
pub i: Option<CF1<C1>>,
|
||||
pub i_usize: Option<usize>,
|
||||
pub z_0: Option<Vec<C1::ScalarField>>,
|
||||
@@ -487,13 +506,13 @@ pub struct AugmentedFCircuit<
|
||||
pub nimfs_proof: Option<NIMFSProof<C1>>,
|
||||
|
||||
// cyclefold verifier on C1
|
||||
pub cf_u_i_cmW: Option<C2>, // input, cf_u_i.cmW
|
||||
pub cf_U_i: Option<CommittedInstance<C2>>, // input, RelaxedR1CS CycleFold instance
|
||||
pub cf_x: Option<CF1<C1>>, // public input (cf_u_{i+1}.x[1])
|
||||
pub cf_u_i_cmW: Option<C2>, // input, cf_u_i.cmW
|
||||
pub cf_U_i: Option<CycleFoldCommittedInstance<C2>>, // input, RelaxedR1CS CycleFold instance
|
||||
pub cf_x: Option<CF1<C1>>, // public input (cf_u_{i+1}.x[1])
|
||||
pub cf_cmT: Option<C2>,
|
||||
}
|
||||
|
||||
impl<C1, C2, GC2, FC> AugmentedFCircuit<C1, C2, GC2, FC>
|
||||
impl<C1, C2, GC2, FC, const MU: usize, const NU: usize> AugmentedFCircuit<C1, C2, GC2, FC, MU, NU>
|
||||
where
|
||||
C1: CurveGroup,
|
||||
C2: CurveGroup,
|
||||
@@ -510,10 +529,8 @@ where
|
||||
poseidon_config: &PoseidonConfig<CF1<C1>>,
|
||||
F_circuit: FC,
|
||||
ccs: CCS<C1::ScalarField>,
|
||||
mu: usize,
|
||||
nu: usize,
|
||||
) -> Result<Self, Error> {
|
||||
if mu < 1 || nu < 1 {
|
||||
if MU < 1 || NU < 1 {
|
||||
return Err(Error::CantBeZero("mu,nu".to_string()));
|
||||
}
|
||||
Ok(Self {
|
||||
@@ -522,8 +539,6 @@ where
|
||||
poseidon_config: poseidon_config.clone(),
|
||||
ccs,
|
||||
pp_hash: None,
|
||||
mu,
|
||||
nu,
|
||||
i: None,
|
||||
i_usize: None,
|
||||
z_0: None,
|
||||
@@ -548,8 +563,6 @@ where
|
||||
poseidon_config: &PoseidonConfig<CF1<C1>>,
|
||||
F: FC, // FCircuit
|
||||
ccs: Option<CCS<C1::ScalarField>>,
|
||||
mu: usize,
|
||||
nu: usize,
|
||||
) -> Result<Self, Error> {
|
||||
let initial_ccs = CCS {
|
||||
// m, n, s, s_prime and M will be overwritten by the `upper_bound_ccs' method
|
||||
@@ -565,7 +578,7 @@ where
|
||||
c: vec![C1::ScalarField::one(), C1::ScalarField::one().neg()],
|
||||
M: vec![],
|
||||
};
|
||||
let mut augmented_f_circuit = Self::default(poseidon_config, F, initial_ccs, mu, nu)?;
|
||||
let mut augmented_f_circuit = Self::default(poseidon_config, F, initial_ccs)?;
|
||||
if ccs.is_some() {
|
||||
augmented_f_circuit.ccs = ccs.unwrap();
|
||||
} else {
|
||||
@@ -590,10 +603,10 @@ where
|
||||
|
||||
let n_iters = 2;
|
||||
for _ in 0..n_iters {
|
||||
let Us = vec![U_i.clone(); self.mu - 1];
|
||||
let Ws = vec![W_i.clone(); self.mu - 1];
|
||||
let us = vec![u_i.clone(); self.nu - 1];
|
||||
let ws = vec![w_i.clone(); self.nu - 1];
|
||||
let Us = vec![U_i.clone(); MU - 1];
|
||||
let Ws = vec![W_i.clone(); MU - 1];
|
||||
let us = vec![u_i.clone(); NU - 1];
|
||||
let ws = vec![w_i.clone(); NU - 1];
|
||||
|
||||
let all_Us = [vec![U_i.clone()], Us.clone()].concat();
|
||||
let all_us = [vec![u_i.clone()], us.clone()].concat();
|
||||
@@ -618,8 +631,6 @@ where
|
||||
poseidon_config: self.poseidon_config.clone(),
|
||||
ccs: ccs.clone(),
|
||||
pp_hash: Some(C1::ScalarField::zero()),
|
||||
mu: self.mu,
|
||||
nu: self.nu,
|
||||
i: Some(C1::ScalarField::zero()),
|
||||
i_usize: Some(0),
|
||||
z_0: Some(z_0.clone()),
|
||||
@@ -677,7 +688,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<C1, C2, GC2, FC> ConstraintSynthesizer<CF1<C1>> for AugmentedFCircuit<C1, C2, GC2, FC>
|
||||
impl<C1, C2, GC2, FC, const MU: usize, const NU: usize> ConstraintSynthesizer<CF1<C1>>
|
||||
for AugmentedFCircuit<C1, C2, GC2, FC, MU, NU>
|
||||
where
|
||||
C1: CurveGroup,
|
||||
C2: CurveGroup,
|
||||
@@ -719,20 +731,21 @@ where
|
||||
let U_i =
|
||||
LCCCSVar::<C1>::new_witness(cs.clone(), || Ok(self.U_i.unwrap_or(U_dummy.clone())))?;
|
||||
let Us = Vec::<LCCCSVar<C1>>::new_witness(cs.clone(), || {
|
||||
Ok(self.Us.unwrap_or(vec![U_dummy.clone(); self.mu - 1]))
|
||||
Ok(self.Us.unwrap_or(vec![U_dummy.clone(); MU - 1]))
|
||||
})?;
|
||||
let us = Vec::<CCCSVar<C1>>::new_witness(cs.clone(), || {
|
||||
Ok(self.us.unwrap_or(vec![u_dummy.clone(); self.mu - 1]))
|
||||
Ok(self.us.unwrap_or(vec![u_dummy.clone(); MU - 1]))
|
||||
})?;
|
||||
let U_i1_C = NonNativeAffineVar::new_witness(cs.clone(), || {
|
||||
Ok(self.U_i1_C.unwrap_or_else(C1::zero))
|
||||
})?;
|
||||
let nimfs_proof_dummy = NIMFSProof::<C1>::dummy(&self.ccs, self.mu, self.nu);
|
||||
let nimfs_proof_dummy = NIMFSProof::<C1>::dummy(&self.ccs, MU, NU);
|
||||
let nimfs_proof = ProofVar::<C1>::new_witness(cs.clone(), || {
|
||||
Ok(self.nimfs_proof.unwrap_or(nimfs_proof_dummy))
|
||||
})?;
|
||||
|
||||
let cf_u_dummy = CommittedInstance::dummy(cf_io_len(self.mu + self.nu));
|
||||
let cf_u_dummy =
|
||||
CycleFoldCommittedInstance::dummy(HyperNovaCycleFoldConfig::<C1, MU, NU>::IO_LEN);
|
||||
let cf_U_i = CycleFoldCommittedInstanceVar::<C2, GC2>::new_witness(cs.clone(), || {
|
||||
Ok(self.cf_U_i.unwrap_or(cf_u_dummy.clone()))
|
||||
})?;
|
||||
@@ -861,20 +874,9 @@ where
|
||||
cf_u_i.clone(),
|
||||
cf_cmT.clone(),
|
||||
)?;
|
||||
// Convert cf_r_bits to a `NonNativeFieldVar`
|
||||
let cf_r_nonnat = {
|
||||
let mut bits = cf_r_bits.clone();
|
||||
bits.resize(C1::BaseField::MODULUS_BIT_SIZE as usize, Boolean::FALSE);
|
||||
NonNativeUintVar::from(&bits)
|
||||
};
|
||||
// Fold cf1_u_i & cf_U_i into cf1_U_{i+1}
|
||||
let cf_U_i1 = NIFSFullGadget::<C2, GC2>::fold_committed_instance(
|
||||
cf_r_bits,
|
||||
cf_r_nonnat,
|
||||
cf_cmT,
|
||||
cf_U_i,
|
||||
cf_u_i,
|
||||
)?;
|
||||
let cf_U_i1 =
|
||||
NIFSFullGadget::<C2, GC2>::fold_committed_instance(cf_r_bits, cf_cmT, cf_U_i, cf_u_i)?;
|
||||
|
||||
// Back to Primary Part
|
||||
// P.4.b compute and check the second output of F'
|
||||
@@ -909,9 +911,12 @@ mod tests {
|
||||
},
|
||||
commitment::{pedersen::Pedersen, CommitmentScheme},
|
||||
folding::{
|
||||
circuits::cyclefold::{fold_cyclefold_circuit, CycleFoldCircuit},
|
||||
hypernova::utils::{compute_c, compute_sigmas_thetas},
|
||||
nova::{traits::NovaR1CS, Witness as NovaWitness},
|
||||
circuits::cyclefold::{fold_cyclefold_circuit, CycleFoldWitness},
|
||||
hypernova::{
|
||||
utils::{compute_c, compute_sigmas_thetas},
|
||||
HyperNovaCycleFoldCircuit,
|
||||
},
|
||||
nova::traits::NovaR1CS,
|
||||
},
|
||||
frontend::tests::CubicFCircuit,
|
||||
transcript::poseidon::poseidon_canonical_config,
|
||||
@@ -1179,25 +1184,25 @@ mod tests {
|
||||
let poseidon_config = poseidon_canonical_config::<Fr>();
|
||||
let sponge = PoseidonSponge::<Fr>::new(&poseidon_config);
|
||||
|
||||
let mu = 3;
|
||||
let nu = 3;
|
||||
const MU: usize = 3;
|
||||
const NU: usize = 3;
|
||||
|
||||
let start = Instant::now();
|
||||
let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap();
|
||||
let mut augmented_f_circuit = AugmentedFCircuit::<
|
||||
Projective,
|
||||
Projective2,
|
||||
GVar2,
|
||||
CubicFCircuit<Fr>,
|
||||
>::empty(&poseidon_config, F_circuit, None, mu, nu)
|
||||
.unwrap();
|
||||
let mut augmented_f_circuit =
|
||||
AugmentedFCircuit::<Projective, Projective2, GVar2, CubicFCircuit<Fr>, MU, NU>::empty(
|
||||
&poseidon_config,
|
||||
F_circuit,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
let ccs = augmented_f_circuit.ccs.clone();
|
||||
println!("AugmentedFCircuit & CCS generation: {:?}", start.elapsed());
|
||||
println!("CCS m x n: {} x {}", ccs.m, ccs.n);
|
||||
|
||||
// CycleFold circuit
|
||||
let cs2 = ConstraintSystem::<Fq>::new_ref();
|
||||
let cf_circuit = CycleFoldCircuit::<Projective, GVar>::empty(mu + nu);
|
||||
let cf_circuit = HyperNovaCycleFoldCircuit::<Projective, GVar, MU, NU>::empty();
|
||||
cf_circuit.generate_constraints(cs2.clone()).unwrap();
|
||||
cs2.finalize();
|
||||
let cs2 = cs2
|
||||
@@ -1224,8 +1229,10 @@ mod tests {
|
||||
let U_dummy = LCCCS::<Projective>::dummy(ccs.l, ccs.t, ccs.s);
|
||||
let w_dummy = W_dummy.clone();
|
||||
let u_dummy = CCCS::<Projective>::dummy(ccs.l);
|
||||
let (cf_W_dummy, cf_U_dummy): (NovaWitness<Projective2>, CommittedInstance<Projective2>) =
|
||||
cf_r1cs.dummy_instance();
|
||||
let (cf_W_dummy, cf_U_dummy): (
|
||||
CycleFoldWitness<Projective2>,
|
||||
CycleFoldCommittedInstance<Projective2>,
|
||||
) = cf_r1cs.dummy_instance();
|
||||
|
||||
// set the initial dummy instances
|
||||
let mut W_i = W_dummy.clone();
|
||||
@@ -1245,10 +1252,10 @@ mod tests {
|
||||
let start = Instant::now();
|
||||
|
||||
// for this test, let Us & us be just an array of copies of the U_i & u_i respectively
|
||||
let Us = vec![U_i.clone(); mu - 1];
|
||||
let Ws = vec![W_i.clone(); mu - 1];
|
||||
let us = vec![u_i.clone(); nu - 1];
|
||||
let ws = vec![w_i.clone(); nu - 1];
|
||||
let Us = vec![U_i.clone(); MU - 1];
|
||||
let Ws = vec![W_i.clone(); MU - 1];
|
||||
let us = vec![u_i.clone(); NU - 1];
|
||||
let ws = vec![w_i.clone(); NU - 1];
|
||||
let all_Us = [vec![U_i.clone()], Us.clone()].concat();
|
||||
let all_us = [vec![u_i.clone()], us.clone()].concat();
|
||||
let all_Ws = [vec![W_i.clone()], Ws].concat();
|
||||
@@ -1268,35 +1275,39 @@ mod tests {
|
||||
// input in the AugmentedFCircuit
|
||||
let cf_u_i1_x = cf_U_i.hash_cyclefold(&sponge, pp_hash);
|
||||
|
||||
augmented_f_circuit =
|
||||
AugmentedFCircuit::<Projective, Projective2, GVar2, CubicFCircuit<Fr>> {
|
||||
_c2: PhantomData,
|
||||
_gc2: PhantomData,
|
||||
poseidon_config: poseidon_config.clone(),
|
||||
ccs: ccs.clone(),
|
||||
pp_hash: Some(pp_hash),
|
||||
mu,
|
||||
nu,
|
||||
i: Some(Fr::zero()),
|
||||
i_usize: Some(0),
|
||||
z_0: Some(z_0.clone()),
|
||||
z_i: Some(z_i.clone()),
|
||||
external_inputs: Some(vec![]),
|
||||
U_i: Some(U_i.clone()),
|
||||
Us: Some(Us.clone()),
|
||||
u_i_C: Some(u_i.C),
|
||||
us: Some(us.clone()),
|
||||
U_i1_C: Some(U_i1.C),
|
||||
F: F_circuit,
|
||||
x: Some(u_i1_x),
|
||||
nimfs_proof: None,
|
||||
augmented_f_circuit = AugmentedFCircuit::<
|
||||
Projective,
|
||||
Projective2,
|
||||
GVar2,
|
||||
CubicFCircuit<Fr>,
|
||||
MU,
|
||||
NU,
|
||||
> {
|
||||
_c2: PhantomData,
|
||||
_gc2: PhantomData,
|
||||
poseidon_config: poseidon_config.clone(),
|
||||
ccs: ccs.clone(),
|
||||
pp_hash: Some(pp_hash),
|
||||
i: Some(Fr::zero()),
|
||||
i_usize: Some(0),
|
||||
z_0: Some(z_0.clone()),
|
||||
z_i: Some(z_i.clone()),
|
||||
external_inputs: Some(vec![]),
|
||||
U_i: Some(U_i.clone()),
|
||||
Us: Some(Us.clone()),
|
||||
u_i_C: Some(u_i.C),
|
||||
us: Some(us.clone()),
|
||||
U_i1_C: Some(U_i1.C),
|
||||
F: F_circuit,
|
||||
x: Some(u_i1_x),
|
||||
nimfs_proof: None,
|
||||
|
||||
// cyclefold values
|
||||
cf_u_i_cmW: None,
|
||||
cf_U_i: None,
|
||||
cf_x: Some(cf_u_i1_x),
|
||||
cf_cmT: None,
|
||||
};
|
||||
// cyclefold values
|
||||
cf_u_i_cmW: None,
|
||||
cf_U_i: None,
|
||||
cf_x: Some(cf_u_i1_x),
|
||||
cf_cmT: None,
|
||||
};
|
||||
} else {
|
||||
let mut transcript_p: PoseidonSponge<Fr> =
|
||||
PoseidonSponge::<Fr>::new(&poseidon_config.clone());
|
||||
@@ -1328,7 +1339,7 @@ mod tests {
|
||||
.collect();
|
||||
let rho_powers_bits: Vec<Vec<bool>> = rho_powers
|
||||
.iter()
|
||||
.map(|rho_i| rho_i.into_bigint().to_bits_le()[..N_BITS_RO].to_vec())
|
||||
.map(|rho_i| rho_i.into_bigint().to_bits_le()[..NOVA_N_BITS_RO].to_vec())
|
||||
.collect();
|
||||
|
||||
// CycleFold part:
|
||||
@@ -1348,9 +1359,8 @@ mod tests {
|
||||
]
|
||||
.concat();
|
||||
|
||||
let cf_circuit = CycleFoldCircuit::<Projective, GVar> {
|
||||
let cf_circuit = HyperNovaCycleFoldCircuit::<Projective, GVar, MU, NU> {
|
||||
_gc: PhantomData,
|
||||
n_points: mu + nu,
|
||||
r_bits: Some(rho_powers_bits.clone()),
|
||||
points: Some(
|
||||
[
|
||||
@@ -1367,24 +1377,22 @@ mod tests {
|
||||
// ensure that the CycleFoldCircuit is well defined
|
||||
assert_eq!(
|
||||
cf_circuit.r_bits.clone().unwrap().len(),
|
||||
cf_circuit.n_points - 1
|
||||
HyperNovaCycleFoldConfig::<Projective, MU, NU>::N_INPUT_POINTS - 1
|
||||
);
|
||||
assert_eq!(
|
||||
cf_circuit.points.clone().unwrap().len(),
|
||||
cf_circuit.n_points
|
||||
HyperNovaCycleFoldConfig::<Projective, MU, NU>::N_INPUT_POINTS
|
||||
);
|
||||
|
||||
let (_cf_w_i, cf_u_i, cf_W_i1, cf_U_i1, cf_cmT, _) = fold_cyclefold_circuit::<
|
||||
HyperNovaCycleFoldConfig<Projective, MU, NU>,
|
||||
Projective,
|
||||
GVar,
|
||||
Projective2,
|
||||
GVar2,
|
||||
CubicFCircuit<Fr>,
|
||||
Pedersen<Projective>,
|
||||
Pedersen<Projective2>,
|
||||
false,
|
||||
>(
|
||||
mu + nu,
|
||||
&mut transcript_p,
|
||||
cf_r1cs.clone(),
|
||||
cf_pedersen_params.clone(),
|
||||
@@ -1401,35 +1409,39 @@ mod tests {
|
||||
// AugmentedFCircuit
|
||||
let cf_u_i1_x = cf_U_i1.hash_cyclefold(&sponge, pp_hash);
|
||||
|
||||
augmented_f_circuit =
|
||||
AugmentedFCircuit::<Projective, Projective2, GVar2, CubicFCircuit<Fr>> {
|
||||
_c2: PhantomData,
|
||||
_gc2: PhantomData,
|
||||
poseidon_config: poseidon_config.clone(),
|
||||
ccs: ccs.clone(),
|
||||
pp_hash: Some(pp_hash),
|
||||
mu,
|
||||
nu,
|
||||
i: Some(iFr),
|
||||
i_usize: Some(i),
|
||||
z_0: Some(z_0.clone()),
|
||||
z_i: Some(z_i.clone()),
|
||||
external_inputs: Some(vec![]),
|
||||
U_i: Some(U_i.clone()),
|
||||
Us: Some(Us.clone()),
|
||||
u_i_C: Some(u_i.C),
|
||||
us: Some(us.clone()),
|
||||
U_i1_C: Some(U_i1.C),
|
||||
F: F_circuit,
|
||||
x: Some(u_i1_x),
|
||||
nimfs_proof: Some(nimfs_proof),
|
||||
augmented_f_circuit = AugmentedFCircuit::<
|
||||
Projective,
|
||||
Projective2,
|
||||
GVar2,
|
||||
CubicFCircuit<Fr>,
|
||||
MU,
|
||||
NU,
|
||||
> {
|
||||
_c2: PhantomData,
|
||||
_gc2: PhantomData,
|
||||
poseidon_config: poseidon_config.clone(),
|
||||
ccs: ccs.clone(),
|
||||
pp_hash: Some(pp_hash),
|
||||
i: Some(iFr),
|
||||
i_usize: Some(i),
|
||||
z_0: Some(z_0.clone()),
|
||||
z_i: Some(z_i.clone()),
|
||||
external_inputs: Some(vec![]),
|
||||
U_i: Some(U_i.clone()),
|
||||
Us: Some(Us.clone()),
|
||||
u_i_C: Some(u_i.C),
|
||||
us: Some(us.clone()),
|
||||
U_i1_C: Some(U_i1.C),
|
||||
F: F_circuit,
|
||||
x: Some(u_i1_x),
|
||||
nimfs_proof: Some(nimfs_proof),
|
||||
|
||||
// cyclefold values
|
||||
cf_u_i_cmW: Some(cf_u_i.cmW),
|
||||
cf_U_i: Some(cf_U_i),
|
||||
cf_x: Some(cf_u_i1_x),
|
||||
cf_cmT: Some(cf_cmT),
|
||||
};
|
||||
// cyclefold values
|
||||
cf_u_i_cmW: Some(cf_u_i.cmW),
|
||||
cf_U_i: Some(cf_U_i),
|
||||
cf_x: Some(cf_u_i1_x),
|
||||
cf_cmT: Some(cf_cmT),
|
||||
};
|
||||
|
||||
// assign the next round instances
|
||||
cf_W_i = cf_W_i1;
|
||||
|
||||
@@ -30,7 +30,7 @@ pub struct LCCCS<C: CurveGroup> {
|
||||
}
|
||||
|
||||
impl<F: PrimeField> CCS<F> {
|
||||
pub fn to_lcccs<R: Rng, C: CurveGroup, CS: CommitmentScheme<C, H>, const H: bool>(
|
||||
pub fn to_lcccs<R: Rng, C, CS: CommitmentScheme<C, H>, const H: bool>(
|
||||
&self,
|
||||
rng: &mut R,
|
||||
cs_params: &CS::ProverParams,
|
||||
|
||||
@@ -6,31 +6,29 @@ use ark_crypto_primitives::sponge::{
|
||||
use ark_ec::{CurveGroup, Group};
|
||||
use ark_ff::{BigInteger, PrimeField};
|
||||
use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldGadget};
|
||||
use ark_std::rand::RngCore;
|
||||
use ark_std::{One, Zero};
|
||||
use core::marker::PhantomData;
|
||||
use std::fmt::Debug;
|
||||
use ark_std::{fmt::Debug, marker::PhantomData, rand::RngCore, One, Zero};
|
||||
|
||||
pub mod cccs;
|
||||
pub mod circuits;
|
||||
use circuits::AugmentedFCircuit;
|
||||
pub mod lcccs;
|
||||
pub mod nimfs;
|
||||
pub mod utils;
|
||||
|
||||
use cccs::CCCS;
|
||||
use circuits::AugmentedFCircuit;
|
||||
use lcccs::LCCCS;
|
||||
use nimfs::NIMFS;
|
||||
|
||||
use crate::commitment::CommitmentScheme;
|
||||
use crate::constants::N_BITS_RO;
|
||||
use crate::constants::NOVA_N_BITS_RO;
|
||||
use crate::folding::circuits::{
|
||||
cyclefold::{fold_cyclefold_circuit, CycleFoldCircuit},
|
||||
cyclefold::{
|
||||
fold_cyclefold_circuit, CycleFoldCircuit, CycleFoldCommittedInstance, CycleFoldConfig,
|
||||
CycleFoldWitness,
|
||||
},
|
||||
CF2,
|
||||
};
|
||||
use crate::folding::nova::{
|
||||
get_r1cs_from_cs, traits::NovaR1CS, CommittedInstance, PreprocessorParam,
|
||||
Witness as NovaWitness,
|
||||
};
|
||||
use crate::folding::nova::{get_r1cs_from_cs, traits::NovaR1CS, PreprocessorParam};
|
||||
use crate::frontend::FCircuit;
|
||||
use crate::utils::{get_cm_coordinates, pp_hash};
|
||||
use crate::Error;
|
||||
@@ -42,6 +40,22 @@ use crate::{
|
||||
FoldingScheme, MultiFolding,
|
||||
};
|
||||
|
||||
struct HyperNovaCycleFoldConfig<C: CurveGroup, const MU: usize, const NU: usize> {
|
||||
_c: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<C: CurveGroup, const MU: usize, const NU: usize> CycleFoldConfig
|
||||
for HyperNovaCycleFoldConfig<C, MU, NU>
|
||||
{
|
||||
const RANDOMNESS_BIT_LENGTH: usize = NOVA_N_BITS_RO;
|
||||
const N_INPUT_POINTS: usize = MU + NU;
|
||||
type C = C;
|
||||
type F = C::BaseField;
|
||||
}
|
||||
|
||||
type HyperNovaCycleFoldCircuit<C, GC, const MU: usize, const NU: usize> =
|
||||
CycleFoldCircuit<HyperNovaCycleFoldConfig<C, MU, NU>, GC>;
|
||||
|
||||
/// Witness for the LCCCS & CCCS, containing the w vector, and the r_w used as randomness in the Pedersen commitment.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Witness<F: PrimeField> {
|
||||
@@ -73,8 +87,6 @@ where
|
||||
pub cf_cs_params: CS2::ProverParams,
|
||||
// if ccs is set, it will be used, if not, it will be computed at runtime
|
||||
pub ccs: Option<CCS<C1::ScalarField>>,
|
||||
pub mu: usize,
|
||||
pub nu: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -114,9 +126,23 @@ where
|
||||
/// Implements HyperNova+CycleFold's IVC, described in
|
||||
/// [HyperNova](https://eprint.iacr.org/2023/573.pdf) and
|
||||
/// [CycleFold](https://eprint.iacr.org/2023/1192.pdf), following the FoldingScheme trait
|
||||
///
|
||||
/// For multi-instance folding, one needs to specify the const generics below:
|
||||
/// * `MU` - the number of LCCCS instances to be folded
|
||||
/// * `NU` - the number of CCCS instances to be folded
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct HyperNova<C1, GC1, C2, GC2, FC, CS1, CS2, const H: bool>
|
||||
where
|
||||
pub struct HyperNova<
|
||||
C1,
|
||||
GC1,
|
||||
C2,
|
||||
GC2,
|
||||
FC,
|
||||
CS1,
|
||||
CS2,
|
||||
const MU: usize,
|
||||
const NU: usize,
|
||||
const H: bool,
|
||||
> where
|
||||
C1: CurveGroup,
|
||||
GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
|
||||
C2: CurveGroup,
|
||||
@@ -142,8 +168,6 @@ where
|
||||
pub F: FC,
|
||||
/// public params hash
|
||||
pub pp_hash: C1::ScalarField,
|
||||
pub mu: usize, // number of LCCCS instances to be folded
|
||||
pub nu: usize, // number of CCCS instances to be folded
|
||||
pub i: C1::ScalarField,
|
||||
/// initial state
|
||||
pub z_0: Vec<C1::ScalarField>,
|
||||
@@ -156,12 +180,12 @@ where
|
||||
pub u_i: CCCS<C1>,
|
||||
|
||||
/// CycleFold running instance
|
||||
pub cf_W_i: NovaWitness<C2>,
|
||||
pub cf_U_i: CommittedInstance<C2>,
|
||||
pub cf_W_i: CycleFoldWitness<C2>,
|
||||
pub cf_U_i: CycleFoldCommittedInstance<C2>,
|
||||
}
|
||||
|
||||
impl<C1, GC1, C2, GC2, FC, CS1, CS2, const H: bool> MultiFolding<C1, C2, FC>
|
||||
for HyperNova<C1, GC1, C2, GC2, FC, CS1, CS2, H>
|
||||
impl<C1, GC1, C2, GC2, FC, CS1, CS2, const MU: usize, const NU: usize, const H: bool>
|
||||
MultiFolding<C1, C2, FC> for HyperNova<C1, GC1, C2, GC2, FC, CS1, CS2, MU, NU, H>
|
||||
where
|
||||
C1: CurveGroup,
|
||||
GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
|
||||
@@ -227,7 +251,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<C1, GC1, C2, GC2, FC, CS1, CS2, const H: bool> HyperNova<C1, GC1, C2, GC2, FC, CS1, CS2, H>
|
||||
impl<C1, GC1, C2, GC2, FC, CS1, CS2, const MU: usize, const NU: usize, const H: bool>
|
||||
HyperNova<C1, GC1, C2, GC2, FC, CS1, CS2, MU, NU, H>
|
||||
where
|
||||
C1: CurveGroup,
|
||||
GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
|
||||
@@ -254,7 +279,8 @@ where
|
||||
// prepare the initial dummy instances
|
||||
let U_i = LCCCS::<C1>::dummy(self.ccs.l, self.ccs.t, self.ccs.s);
|
||||
let mut u_i = CCCS::<C1>::dummy(self.ccs.l);
|
||||
let (_, cf_U_i): (NovaWitness<C2>, CommittedInstance<C2>) = self.cf_r1cs.dummy_instance();
|
||||
let (_, cf_U_i): (CycleFoldWitness<C2>, CycleFoldCommittedInstance<C2>) =
|
||||
self.cf_r1cs.dummy_instance();
|
||||
|
||||
let sponge = PoseidonSponge::<C1::ScalarField>::new(&self.poseidon_config);
|
||||
|
||||
@@ -268,7 +294,7 @@ where
|
||||
),
|
||||
cf_U_i.hash_cyclefold(&sponge, self.pp_hash),
|
||||
];
|
||||
let us = vec![u_i.clone(); self.nu - 1];
|
||||
let us = vec![u_i.clone(); NU - 1];
|
||||
|
||||
let z_i1 = self
|
||||
.F
|
||||
@@ -285,14 +311,12 @@ where
|
||||
);
|
||||
|
||||
let cf_u_i1_x = cf_U_i.hash_cyclefold(&sponge, self.pp_hash);
|
||||
let augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> {
|
||||
let augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC, MU, NU> {
|
||||
_c2: PhantomData,
|
||||
_gc2: PhantomData,
|
||||
poseidon_config: self.poseidon_config.clone(),
|
||||
ccs: self.ccs.clone(),
|
||||
pp_hash: Some(self.pp_hash),
|
||||
mu: self.mu,
|
||||
nu: self.nu,
|
||||
i: Some(C1::ScalarField::zero()),
|
||||
i_usize: Some(0),
|
||||
z_0: Some(self.z_0.clone()),
|
||||
@@ -334,8 +358,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<C1, GC1, C2, GC2, FC, CS1, CS2, const H: bool> FoldingScheme<C1, C2, FC>
|
||||
for HyperNova<C1, GC1, C2, GC2, FC, CS1, CS2, H>
|
||||
impl<C1, GC1, C2, GC2, FC, CS1, CS2, const MU: usize, const NU: usize, const H: bool>
|
||||
FoldingScheme<C1, C2, FC> for HyperNova<C1, GC1, C2, GC2, FC, CS1, CS2, MU, NU, H>
|
||||
where
|
||||
C1: CurveGroup,
|
||||
GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
|
||||
@@ -352,37 +376,32 @@ where
|
||||
for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>,
|
||||
for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>,
|
||||
{
|
||||
/// Reuse Nova's PreprocessorParam, together with two usize values, which are mu & nu
|
||||
/// respectively, which indicate the amount of LCCCS & CCCS instances to be folded at each
|
||||
/// folding step.
|
||||
type PreprocessorParam = (PreprocessorParam<C1, C2, FC, CS1, CS2, H>, usize, usize);
|
||||
/// Reuse Nova's PreprocessorParam.
|
||||
type PreprocessorParam = PreprocessorParam<C1, C2, FC, CS1, CS2, H>;
|
||||
type ProverParam = ProverParams<C1, C2, CS1, CS2, H>;
|
||||
type VerifierParam = VerifierParams<C1, C2, CS1, CS2, H>;
|
||||
type RunningInstance = (LCCCS<C1>, Witness<C1::ScalarField>);
|
||||
type IncomingInstance = (CCCS<C1>, Witness<C1::ScalarField>);
|
||||
type MultiCommittedInstanceWithWitness =
|
||||
(Vec<Self::RunningInstance>, Vec<Self::IncomingInstance>);
|
||||
type CFInstance = (CommittedInstance<C2>, NovaWitness<C2>);
|
||||
type CFInstance = (CycleFoldCommittedInstance<C2>, CycleFoldWitness<C2>);
|
||||
|
||||
fn preprocess(
|
||||
mut rng: impl RngCore,
|
||||
prep_param: &Self::PreprocessorParam,
|
||||
) -> Result<(Self::ProverParam, Self::VerifierParam), Error> {
|
||||
let (prep_param, mu, nu) = prep_param;
|
||||
if *mu < 1 || *nu < 1 {
|
||||
if MU < 1 || NU < 1 {
|
||||
return Err(Error::CantBeZero("mu,nu".to_string()));
|
||||
}
|
||||
|
||||
let augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC>::empty(
|
||||
let augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC, MU, NU>::empty(
|
||||
&prep_param.poseidon_config,
|
||||
prep_param.F.clone(),
|
||||
None,
|
||||
*mu,
|
||||
*nu,
|
||||
)?;
|
||||
let ccs = augmented_f_circuit.ccs.clone();
|
||||
|
||||
let cf_circuit = CycleFoldCircuit::<C1, GC1>::empty(mu + nu);
|
||||
let cf_circuit = HyperNovaCycleFoldCircuit::<C1, GC1, MU, NU>::empty();
|
||||
let cf_r1cs = get_r1cs_from_cs::<C2::ScalarField>(cf_circuit)?;
|
||||
|
||||
// if cs params exist, use them, if not, generate new ones
|
||||
@@ -409,8 +428,6 @@ where
|
||||
cs_params: cs_pp.clone(),
|
||||
cf_cs_params: cf_cs_pp.clone(),
|
||||
ccs: Some(ccs.clone()),
|
||||
mu: *mu,
|
||||
nu: *nu,
|
||||
};
|
||||
let vp = VerifierParams::<C1, C2, CS1, CS2, H> {
|
||||
poseidon_config: prep_param.poseidon_config.clone(),
|
||||
@@ -429,7 +446,7 @@ where
|
||||
z_0: Vec<C1::ScalarField>,
|
||||
) -> Result<Self, Error> {
|
||||
let (pp, vp) = params;
|
||||
if pp.mu < 1 || pp.nu < 1 {
|
||||
if MU < 1 || NU < 1 {
|
||||
return Err(Error::CantBeZero("mu,nu".to_string()));
|
||||
}
|
||||
|
||||
@@ -438,16 +455,14 @@ where
|
||||
|
||||
// prepare the HyperNova's AugmentedFCircuit and CycleFold's circuits and obtain its CCS
|
||||
// and R1CS respectively
|
||||
let augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC>::empty(
|
||||
let augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC, MU, NU>::empty(
|
||||
&pp.poseidon_config,
|
||||
F.clone(),
|
||||
pp.ccs.clone(),
|
||||
pp.mu,
|
||||
pp.nu,
|
||||
)?;
|
||||
let ccs = augmented_f_circuit.ccs.clone();
|
||||
|
||||
let cf_circuit = CycleFoldCircuit::<C1, GC1>::empty(pp.mu + pp.nu);
|
||||
let cf_circuit = HyperNovaCycleFoldCircuit::<C1, GC1, MU, NU>::empty();
|
||||
let cf_r1cs = get_r1cs_from_cs::<C2::ScalarField>(cf_circuit)?;
|
||||
|
||||
// compute the public params hash
|
||||
@@ -458,7 +473,7 @@ where
|
||||
let U_dummy = LCCCS::<C1>::dummy(ccs.l, ccs.t, ccs.s);
|
||||
let w_dummy = W_dummy.clone();
|
||||
let mut u_dummy = CCCS::<C1>::dummy(ccs.l);
|
||||
let (cf_W_dummy, cf_U_dummy): (NovaWitness<C2>, CommittedInstance<C2>) =
|
||||
let (cf_W_dummy, cf_U_dummy): (CycleFoldWitness<C2>, CycleFoldCommittedInstance<C2>) =
|
||||
cf_r1cs.dummy_instance();
|
||||
u_dummy.x = vec![
|
||||
U_dummy.hash(
|
||||
@@ -484,8 +499,6 @@ where
|
||||
cf_cs_params: pp.cf_cs_params.clone(),
|
||||
F,
|
||||
pp_hash,
|
||||
mu: pp.mu,
|
||||
nu: pp.nu,
|
||||
i: C1::ScalarField::zero(),
|
||||
z_0: z_0.clone(),
|
||||
z_i: z_0,
|
||||
@@ -536,27 +549,27 @@ where
|
||||
// recall, mu & nu is the number of all the LCCCS & CCCS respectively, including the
|
||||
// running and incoming instances that are not part of the 'other_instances', hence the +1
|
||||
// in the couple of following checks.
|
||||
if lcccs.len() + 1 != self.mu {
|
||||
if lcccs.len() + 1 != MU {
|
||||
return Err(Error::NotSameLength(
|
||||
"other_instances.lcccs.len()".to_string(),
|
||||
lcccs.len(),
|
||||
"hypernova.mu".to_string(),
|
||||
self.mu,
|
||||
MU,
|
||||
));
|
||||
}
|
||||
if cccs.len() + 1 != self.nu {
|
||||
if cccs.len() + 1 != NU {
|
||||
return Err(Error::NotSameLength(
|
||||
"other_instances.cccs.len()".to_string(),
|
||||
cccs.len(),
|
||||
"hypernova.nu".to_string(),
|
||||
self.nu,
|
||||
NU,
|
||||
));
|
||||
}
|
||||
|
||||
let (Us, Ws): (Vec<LCCCS<C1>>, Vec<Witness<C1::ScalarField>>) = lcccs.into_iter().unzip();
|
||||
let (us, ws): (Vec<CCCS<C1>>, Vec<Witness<C1::ScalarField>>) = cccs.into_iter().unzip();
|
||||
|
||||
let augmented_f_circuit: AugmentedFCircuit<C1, C2, GC2, FC>;
|
||||
let augmented_f_circuit: AugmentedFCircuit<C1, C2, GC2, FC, MU, NU>;
|
||||
|
||||
if self.z_i.len() != self.F.state_len() {
|
||||
return Err(Error::NotSameLength(
|
||||
@@ -608,14 +621,12 @@ where
|
||||
// input in the AugmentedFCircuit
|
||||
cf_u_i1_x = self.cf_U_i.hash_cyclefold(&sponge, self.pp_hash);
|
||||
|
||||
augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> {
|
||||
augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC, MU, NU> {
|
||||
_c2: PhantomData,
|
||||
_gc2: PhantomData,
|
||||
poseidon_config: self.poseidon_config.clone(),
|
||||
ccs: self.ccs.clone(),
|
||||
pp_hash: Some(self.pp_hash),
|
||||
mu: self.mu,
|
||||
nu: self.nu,
|
||||
i: Some(C1::ScalarField::zero()),
|
||||
i_usize: Some(0),
|
||||
z_0: Some(self.z_0.clone()),
|
||||
@@ -674,7 +685,7 @@ where
|
||||
.collect();
|
||||
let rho_powers_bits: Vec<Vec<bool>> = rho_powers
|
||||
.iter()
|
||||
.map(|rho_i| rho_i.into_bigint().to_bits_le()[..N_BITS_RO].to_vec())
|
||||
.map(|rho_i| rho_i.into_bigint().to_bits_le()[..NOVA_N_BITS_RO].to_vec())
|
||||
.collect();
|
||||
|
||||
// CycleFold part:
|
||||
@@ -698,9 +709,8 @@ where
|
||||
]
|
||||
.concat();
|
||||
|
||||
let cf_circuit = CycleFoldCircuit::<C1, GC1> {
|
||||
let cf_circuit = HyperNovaCycleFoldCircuit::<C1, GC1, MU, NU> {
|
||||
_gc: PhantomData,
|
||||
n_points: self.mu + self.nu,
|
||||
r_bits: Some(rho_powers_bits.clone()),
|
||||
points: Some(
|
||||
[
|
||||
@@ -714,30 +724,34 @@ where
|
||||
x: Some(cf_u_i_x.clone()),
|
||||
};
|
||||
|
||||
let (_cf_w_i, cf_u_i, cf_W_i1, cf_U_i1, cf_cmT, _) =
|
||||
fold_cyclefold_circuit::<C1, GC1, C2, GC2, FC, CS1, CS2, H>(
|
||||
self.mu + self.nu,
|
||||
&mut transcript_p,
|
||||
self.cf_r1cs.clone(),
|
||||
self.cf_cs_params.clone(),
|
||||
self.pp_hash,
|
||||
self.cf_W_i.clone(), // CycleFold running instance witness
|
||||
self.cf_U_i.clone(), // CycleFold running instance
|
||||
cf_u_i_x,
|
||||
cf_circuit,
|
||||
&mut rng,
|
||||
)?;
|
||||
let (_cf_w_i, cf_u_i, cf_W_i1, cf_U_i1, cf_cmT, _) = fold_cyclefold_circuit::<
|
||||
HyperNovaCycleFoldConfig<C1, MU, NU>,
|
||||
C1,
|
||||
GC1,
|
||||
C2,
|
||||
GC2,
|
||||
CS2,
|
||||
H,
|
||||
>(
|
||||
&mut transcript_p,
|
||||
self.cf_r1cs.clone(),
|
||||
self.cf_cs_params.clone(),
|
||||
self.pp_hash,
|
||||
self.cf_W_i.clone(), // CycleFold running instance witness
|
||||
self.cf_U_i.clone(), // CycleFold running instance
|
||||
cf_u_i_x,
|
||||
cf_circuit,
|
||||
&mut rng,
|
||||
)?;
|
||||
|
||||
cf_u_i1_x = cf_U_i1.hash_cyclefold(&sponge, self.pp_hash);
|
||||
|
||||
augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> {
|
||||
augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC, MU, NU> {
|
||||
_c2: PhantomData,
|
||||
_gc2: PhantomData,
|
||||
poseidon_config: self.poseidon_config.clone(),
|
||||
ccs: self.ccs.clone(),
|
||||
pp_hash: Some(self.pp_hash),
|
||||
mu: self.mu,
|
||||
nu: self.nu,
|
||||
i: Some(self.i),
|
||||
i_usize: Some(i_usize),
|
||||
z_0: Some(self.z_0.clone()),
|
||||
@@ -920,37 +934,21 @@ mod tests {
|
||||
) {
|
||||
let mut rng = ark_std::test_rng();
|
||||
|
||||
let (mu, nu) = (2, 3);
|
||||
const MU: usize = 2;
|
||||
const NU: usize = 3;
|
||||
|
||||
type HN<CS1, CS2, const H: bool> =
|
||||
HyperNova<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2, MU, NU, H>;
|
||||
|
||||
let prep_param =
|
||||
PreprocessorParam::<Projective, Projective2, CubicFCircuit<Fr>, CS1, CS2, H>::new(
|
||||
poseidon_config.clone(),
|
||||
F_circuit,
|
||||
);
|
||||
let hypernova_params = HyperNova::<
|
||||
Projective,
|
||||
GVar,
|
||||
Projective2,
|
||||
GVar2,
|
||||
CubicFCircuit<Fr>,
|
||||
CS1,
|
||||
CS2,
|
||||
H,
|
||||
>::preprocess(&mut rng, &(prep_param, mu, nu))
|
||||
.unwrap();
|
||||
let hypernova_params = HN::preprocess(&mut rng, &prep_param).unwrap();
|
||||
|
||||
let z_0 = vec![Fr::from(3_u32)];
|
||||
let mut hypernova = HyperNova::<
|
||||
Projective,
|
||||
GVar,
|
||||
Projective2,
|
||||
GVar2,
|
||||
CubicFCircuit<Fr>,
|
||||
CS1,
|
||||
CS2,
|
||||
H,
|
||||
>::init(&hypernova_params, F_circuit, z_0.clone())
|
||||
.unwrap();
|
||||
let mut hypernova = HN::init(&hypernova_params, F_circuit, z_0.clone()).unwrap();
|
||||
|
||||
let (w_i_blinding, W_i_blinding) = if H {
|
||||
(Fr::rand(&mut rng), Fr::rand(&mut rng))
|
||||
@@ -964,7 +962,7 @@ mod tests {
|
||||
for _ in 0..num_steps {
|
||||
// prepare some new instances to fold in the multifolding step
|
||||
let mut lcccs = vec![];
|
||||
for j in 0..mu - 1 {
|
||||
for j in 0..MU - 1 {
|
||||
let instance_state = vec![Fr::from(j as u32 + 85_u32)];
|
||||
let (U, W) = hypernova
|
||||
.new_running_instance(&mut rng, instance_state, vec![])
|
||||
@@ -972,7 +970,7 @@ mod tests {
|
||||
lcccs.push((U, W));
|
||||
}
|
||||
let mut cccs = vec![];
|
||||
for j in 0..nu - 1 {
|
||||
for j in 0..NU - 1 {
|
||||
let instance_state = vec![Fr::from(j as u32 + 15_u32)];
|
||||
let (u, w) = hypernova
|
||||
.new_incoming_instance(&mut rng, instance_state, vec![])
|
||||
@@ -988,7 +986,7 @@ mod tests {
|
||||
assert_eq!(Fr::from(num_steps as u32), hypernova.i);
|
||||
|
||||
let (running_instance, incoming_instance, cyclefold_instance) = hypernova.instances();
|
||||
HyperNova::<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2, H>::verify(
|
||||
HN::verify(
|
||||
hypernova_params.1, // verifier_params
|
||||
z_0,
|
||||
hypernova.z_i,
|
||||
|
||||
@@ -12,7 +12,7 @@ use super::{
|
||||
Witness,
|
||||
};
|
||||
use crate::arith::ccs::CCS;
|
||||
use crate::constants::N_BITS_RO;
|
||||
use crate::constants::NOVA_N_BITS_RO;
|
||||
use crate::transcript::Transcript;
|
||||
use crate::utils::sum_check::structs::{IOPProof as SumCheckProof, IOPProverMessage};
|
||||
use crate::utils::sum_check::{IOPSumCheck, SumCheck};
|
||||
@@ -123,10 +123,12 @@ where
|
||||
|
||||
// compute the next power of rho
|
||||
rho_i *= rho;
|
||||
// crop the size of rho_i to N_BITS_RO
|
||||
// crop the size of rho_i to NOVA_N_BITS_RO
|
||||
let rho_i_bits = rho_i.into_bigint().to_bits_le();
|
||||
rho_i = C::ScalarField::from_bigint(BigInteger::from_bits_le(&rho_i_bits[..N_BITS_RO]))
|
||||
.unwrap();
|
||||
rho_i = C::ScalarField::from_bigint(BigInteger::from_bits_le(
|
||||
&rho_i_bits[..NOVA_N_BITS_RO],
|
||||
))
|
||||
.unwrap();
|
||||
if i < lcccs.len() + cccs.len() - 1 {
|
||||
// store the cropped rho_i into the rho_powers vector
|
||||
rho_powers[i] = rho_i;
|
||||
@@ -181,10 +183,12 @@ where
|
||||
|
||||
// compute the next power of rho
|
||||
rho_i *= rho;
|
||||
// crop the size of rho_i to N_BITS_RO
|
||||
// crop the size of rho_i to NOVA_N_BITS_RO
|
||||
let rho_i_bits = rho_i.into_bigint().to_bits_le();
|
||||
rho_i = C::ScalarField::from_bigint(BigInteger::from_bits_le(&rho_i_bits[..N_BITS_RO]))
|
||||
.unwrap();
|
||||
rho_i = C::ScalarField::from_bigint(BigInteger::from_bits_le(
|
||||
&rho_i_bits[..NOVA_N_BITS_RO],
|
||||
))
|
||||
.unwrap();
|
||||
}
|
||||
Witness {
|
||||
w: w_folded,
|
||||
@@ -272,7 +276,7 @@ where
|
||||
// Step 6: Get the folding challenge
|
||||
let rho_scalar = C::ScalarField::from_le_bytes_mod_order(b"rho");
|
||||
transcript.absorb(&rho_scalar);
|
||||
let rho_bits: Vec<bool> = transcript.get_challenge_nbits(N_BITS_RO);
|
||||
let rho_bits: Vec<bool> = transcript.get_challenge_nbits(NOVA_N_BITS_RO);
|
||||
let rho: C::ScalarField =
|
||||
C::ScalarField::from_bigint(BigInteger::from_bits_le(&rho_bits)).unwrap();
|
||||
|
||||
@@ -391,7 +395,7 @@ where
|
||||
// Step 6: Get the folding challenge
|
||||
let rho_scalar = C::ScalarField::from_le_bytes_mod_order(b"rho");
|
||||
transcript.absorb(&rho_scalar);
|
||||
let rho_bits: Vec<bool> = transcript.get_challenge_nbits(N_BITS_RO);
|
||||
let rho_bits: Vec<bool> = transcript.get_challenge_nbits(NOVA_N_BITS_RO);
|
||||
let rho: C::ScalarField =
|
||||
C::ScalarField::from_bigint(BigInteger::from_bits_le(&rho_bits)).unwrap();
|
||||
|
||||
|
||||
@@ -20,11 +20,12 @@ use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, Namespace,
|
||||
use ark_std::{fmt::Debug, One, Zero};
|
||||
use core::{borrow::Borrow, marker::PhantomData};
|
||||
|
||||
use super::{CommittedInstance, NOVA_CF_N_POINTS};
|
||||
use crate::constants::N_BITS_RO;
|
||||
use super::{CommittedInstance, NovaCycleFoldConfig};
|
||||
use crate::constants::NOVA_N_BITS_RO;
|
||||
use crate::folding::circuits::{
|
||||
cyclefold::{
|
||||
cf_io_len, CycleFoldChallengeGadget, CycleFoldCommittedInstanceVar, NIFSFullGadget,
|
||||
CycleFoldChallengeGadget, CycleFoldCommittedInstance, CycleFoldCommittedInstanceVar,
|
||||
CycleFoldConfig, NIFSFullGadget,
|
||||
},
|
||||
nonnative::{affine::NonNativeAffineVar, uint::NonNativeUintVar},
|
||||
CF1, CF2,
|
||||
@@ -196,7 +197,7 @@ where
|
||||
transcript.absorb(&U_i);
|
||||
transcript.absorb(&u_i);
|
||||
transcript.absorb_nonnative(&cmT);
|
||||
transcript.squeeze_bits(N_BITS_RO)
|
||||
transcript.squeeze_bits(NOVA_N_BITS_RO)
|
||||
}
|
||||
|
||||
// compatible with the native get_challenge_native
|
||||
@@ -211,13 +212,22 @@ where
|
||||
transcript.absorb(&U_i_vec)?;
|
||||
transcript.absorb(&u_i)?;
|
||||
transcript.absorb_nonnative(&cmT)?;
|
||||
transcript.squeeze_bits(N_BITS_RO)
|
||||
transcript.squeeze_bits(NOVA_N_BITS_RO)
|
||||
}
|
||||
}
|
||||
|
||||
/// AugmentedFCircuit implements the F' circuit (augmented F) defined in
|
||||
/// [Nova](https://eprint.iacr.org/2021/370.pdf) together with the extra constraints defined in
|
||||
/// [CycleFold](https://eprint.iacr.org/2023/1192.pdf).
|
||||
/// `AugmentedFCircuit` enhances the original step function `F`, so that it can
|
||||
/// be used in recursive arguments such as IVC.
|
||||
///
|
||||
/// The method for converting `F` to `AugmentedFCircuit` (`F'`) is defined in
|
||||
/// [Nova](https://eprint.iacr.org/2021/370.pdf), where `AugmentedFCircuit` not
|
||||
/// only invokes `F`, but also adds additional constraints for verifying the
|
||||
/// correct folding of primary instances (i.e., Nova's `CommittedInstance`s over
|
||||
/// `C1`).
|
||||
///
|
||||
/// Furthermore, to reduce circuit size over `C2`, we implement the constraints
|
||||
/// defined in [CycleFold](https://eprint.iacr.org/2023/1192.pdf). These extra
|
||||
/// constraints verify the correct folding of CycleFold instances.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AugmentedFCircuit<
|
||||
C1: CurveGroup,
|
||||
@@ -246,9 +256,9 @@ pub struct AugmentedFCircuit<
|
||||
// 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<CommittedInstance<C2>>, // input
|
||||
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])
|
||||
@@ -337,7 +347,7 @@ where
|
||||
let cmT =
|
||||
NonNativeAffineVar::new_witness(cs.clone(), || Ok(self.cmT.unwrap_or_else(C1::zero)))?;
|
||||
|
||||
let cf_u_dummy = CommittedInstance::dummy(cf_io_len(NOVA_CF_N_POINTS));
|
||||
let cf_u_dummy = CycleFoldCommittedInstance::dummy(NovaCycleFoldConfig::<C1>::IO_LEN);
|
||||
let cf_U_i = CycleFoldCommittedInstanceVar::<C2, GC2>::new_witness(cs.clone(), || {
|
||||
Ok(self.cf_U_i.unwrap_or(cf_u_dummy.clone()))
|
||||
})?;
|
||||
@@ -481,19 +491,9 @@ where
|
||||
cf1_u_i.clone(),
|
||||
cf1_cmT.clone(),
|
||||
)?;
|
||||
// Convert cf1_r_bits to a `NonNativeFieldVar`
|
||||
let cf1_r_nonnat = {
|
||||
let mut bits = cf1_r_bits.clone();
|
||||
bits.resize(C1::BaseField::MODULUS_BIT_SIZE as usize, Boolean::FALSE);
|
||||
NonNativeUintVar::from(&bits)
|
||||
};
|
||||
// Fold cf1_u_i & cf_U_i into cf1_U_{i+1}
|
||||
let cf1_U_i1 = NIFSFullGadget::<C2, GC2>::fold_committed_instance(
|
||||
cf1_r_bits,
|
||||
cf1_r_nonnat,
|
||||
cf1_cmT,
|
||||
cf_U_i,
|
||||
cf1_u_i,
|
||||
cf1_r_bits, cf1_cmT, cf_U_i, cf1_u_i,
|
||||
)?;
|
||||
|
||||
// same for cf2_r:
|
||||
@@ -504,16 +504,8 @@ where
|
||||
cf2_u_i.clone(),
|
||||
cf2_cmT.clone(),
|
||||
)?;
|
||||
let cf2_r_nonnat = {
|
||||
let mut bits = cf2_r_bits.clone();
|
||||
bits.resize(C1::BaseField::MODULUS_BIT_SIZE as usize, Boolean::FALSE);
|
||||
NonNativeUintVar::from(&bits)
|
||||
};
|
||||
let cf_U_i1 = NIFSFullGadget::<C2, GC2>::fold_committed_instance(
|
||||
cf2_r_bits,
|
||||
cf2_r_nonnat,
|
||||
cf2_cmT,
|
||||
cf1_U_i1, // the output from NIFS.V(cf1_r, cf_U, cfE_u)
|
||||
cf2_r_bits, cf2_cmT, cf1_U_i1, // the output from NIFS.V(cf1_r, cf_U, cfE_u)
|
||||
cf2_u_i,
|
||||
)?;
|
||||
|
||||
@@ -566,7 +558,7 @@ pub mod tests {
|
||||
assert_eq!(ciVar.x.value().unwrap(), ci.x);
|
||||
// the values cmE and cmW are checked in the CycleFold's circuit
|
||||
// CommittedInstanceInCycleFoldVar in
|
||||
// nova::cyclefold::tests::test_committed_instance_cyclefold_var
|
||||
// cyclefold::tests::test_committed_instance_cyclefold_var
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -22,14 +22,18 @@ use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, Namespace,
|
||||
use ark_std::{log2, Zero};
|
||||
use core::{borrow::Borrow, marker::PhantomData};
|
||||
|
||||
use super::{circuits::ChallengeGadget, nifs::NIFS};
|
||||
use super::{
|
||||
circuits::{ChallengeGadget, CommittedInstanceVar},
|
||||
nifs::NIFS,
|
||||
CommittedInstance, Nova, Witness,
|
||||
};
|
||||
use crate::arith::r1cs::R1CS;
|
||||
use crate::commitment::{pedersen::Params as PedersenParams, CommitmentScheme};
|
||||
use crate::folding::circuits::{
|
||||
cyclefold::{CycleFoldCommittedInstance, CycleFoldWitness},
|
||||
nonnative::{affine::NonNativeAffineVar, uint::NonNativeUintVar},
|
||||
CF1, CF2,
|
||||
};
|
||||
use crate::folding::nova::{circuits::CommittedInstanceVar, CommittedInstance, Nova, Witness};
|
||||
use crate::frontend::FCircuit;
|
||||
use crate::transcript::{Transcript, TranscriptVar};
|
||||
use crate::utils::{
|
||||
@@ -156,7 +160,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// In-circuit representation of the Witness associated to the CommittedInstance, but with
|
||||
/// In-circuit representation of the Witness associated to the CycleFoldCommittedInstance, but with
|
||||
/// non-native representation, since it is used to represent the CycleFold witness.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CycleFoldWitnessVar<C: CurveGroup> {
|
||||
@@ -166,12 +170,12 @@ pub struct CycleFoldWitnessVar<C: CurveGroup> {
|
||||
pub rW: NonNativeUintVar<CF2<C>>,
|
||||
}
|
||||
|
||||
impl<C> AllocVar<Witness<C>, CF2<C>> for CycleFoldWitnessVar<C>
|
||||
impl<C> AllocVar<CycleFoldWitness<C>, CF2<C>> for CycleFoldWitnessVar<C>
|
||||
where
|
||||
C: CurveGroup,
|
||||
<C as ark_ec::CurveGroup>::BaseField: PrimeField,
|
||||
{
|
||||
fn new_variable<T: Borrow<Witness<C>>>(
|
||||
fn new_variable<T: Borrow<CycleFoldWitness<C>>>(
|
||||
cs: impl Into<Namespace<CF2<C>>>,
|
||||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
||||
mode: AllocationMode,
|
||||
@@ -237,8 +241,8 @@ where
|
||||
pub cmT: Option<C1>,
|
||||
pub r: Option<C1::ScalarField>,
|
||||
/// CycleFold running instance
|
||||
pub cf_U_i: Option<CommittedInstance<C2>>,
|
||||
pub cf_W_i: Option<Witness<C2>>,
|
||||
pub cf_U_i: Option<CycleFoldCommittedInstance<C2>>,
|
||||
pub cf_W_i: Option<CycleFoldWitness<C2>>,
|
||||
|
||||
/// KZG challenges
|
||||
pub kzg_c_W: Option<C1::ScalarField>,
|
||||
@@ -447,14 +451,19 @@ where
|
||||
{
|
||||
// imports here instead of at the top of the file, so we avoid having multiple
|
||||
// `#[cfg(not(test))]`
|
||||
use super::NOVA_CF_N_POINTS;
|
||||
use crate::commitment::pedersen::PedersenGadget;
|
||||
use crate::folding::circuits::cyclefold::{cf_io_len, CycleFoldCommittedInstanceVar};
|
||||
use crate::folding::{
|
||||
circuits::cyclefold::{CycleFoldCommittedInstanceVar, CycleFoldConfig},
|
||||
nova::NovaCycleFoldConfig,
|
||||
};
|
||||
use ark_r1cs_std::ToBitsGadget;
|
||||
|
||||
let cf_u_dummy_native = CommittedInstance::<C2>::dummy(cf_io_len(NOVA_CF_N_POINTS));
|
||||
let w_dummy_native =
|
||||
Witness::<C2>::dummy(self.cf_r1cs.A.n_cols - 1 - self.cf_r1cs.l, self.cf_E_len);
|
||||
let cf_u_dummy_native =
|
||||
CycleFoldCommittedInstance::<C2>::dummy(NovaCycleFoldConfig::<C1>::IO_LEN);
|
||||
let w_dummy_native = CycleFoldWitness::<C2>::dummy(
|
||||
self.cf_r1cs.A.n_cols - 1 - self.cf_r1cs.l,
|
||||
self.cf_E_len,
|
||||
);
|
||||
let cf_U_i = CycleFoldCommittedInstanceVar::<C2, GC2>::new_witness(cs.clone(), || {
|
||||
Ok(self.cf_U_i.unwrap_or_else(|| cf_u_dummy_native.clone()))
|
||||
})?;
|
||||
|
||||
@@ -4,7 +4,7 @@ use ark_crypto_primitives::sponge::{
|
||||
poseidon::{PoseidonConfig, PoseidonSponge},
|
||||
Absorb, CryptographicSponge,
|
||||
};
|
||||
use ark_ec::{AffineRepr, CurveGroup, Group};
|
||||
use ark_ec::{CurveGroup, Group};
|
||||
use ark_ff::{BigInteger, PrimeField};
|
||||
use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldGadget};
|
||||
use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem};
|
||||
@@ -15,7 +15,10 @@ use ark_std::{One, UniformRand, Zero};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use crate::commitment::CommitmentScheme;
|
||||
use crate::folding::circuits::cyclefold::{fold_cyclefold_circuit, CycleFoldCircuit};
|
||||
use crate::folding::circuits::cyclefold::{
|
||||
fold_cyclefold_circuit, CycleFoldCircuit, CycleFoldCommittedInstance, CycleFoldConfig,
|
||||
CycleFoldWitness,
|
||||
};
|
||||
use crate::folding::circuits::CF2;
|
||||
use crate::frontend::FCircuit;
|
||||
use crate::transcript::{AbsorbNonNative, Transcript};
|
||||
@@ -24,6 +27,7 @@ use crate::Error;
|
||||
use crate::FoldingScheme;
|
||||
use crate::{
|
||||
arith::r1cs::{extract_r1cs, extract_w_x, R1CS},
|
||||
constants::NOVA_N_BITS_RO,
|
||||
utils::{get_cm_coordinates, pp_hash},
|
||||
};
|
||||
|
||||
@@ -33,13 +37,23 @@ pub mod decider_eth_circuit;
|
||||
pub mod nifs;
|
||||
pub mod serialize;
|
||||
pub mod traits;
|
||||
|
||||
use circuits::{AugmentedFCircuit, ChallengeGadget};
|
||||
use nifs::NIFS;
|
||||
use traits::NovaR1CS;
|
||||
|
||||
/// Number of points to be folded in the CycleFold circuit, in Nova's case, this is a fixed amount:
|
||||
/// 2 points to be folded.
|
||||
const NOVA_CF_N_POINTS: usize = 2_usize;
|
||||
struct NovaCycleFoldConfig<C: CurveGroup> {
|
||||
_c: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<C: CurveGroup> CycleFoldConfig for NovaCycleFoldConfig<C> {
|
||||
const RANDOMNESS_BIT_LENGTH: usize = NOVA_N_BITS_RO;
|
||||
const N_INPUT_POINTS: usize = 2;
|
||||
type C = C;
|
||||
type F = C::BaseField;
|
||||
}
|
||||
|
||||
type NovaCycleFoldCircuit<C, GC> = CycleFoldCircuit<NovaCycleFoldConfig<C>, GC>;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
|
||||
pub struct CommittedInstance<C: CurveGroup> {
|
||||
@@ -84,30 +98,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: CurveGroup> AbsorbNonNative<C::BaseField> for CommittedInstance<C>
|
||||
where
|
||||
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField + Absorb,
|
||||
{
|
||||
// Compatible with the in-circuit `CycleFoldCommittedInstanceVar::to_native_sponge_field_elements`
|
||||
// in `cyclefold.rs`.
|
||||
fn to_native_sponge_field_elements(&self, dest: &mut Vec<C::BaseField>) {
|
||||
[self.u].to_native_sponge_field_elements(dest);
|
||||
self.x.to_native_sponge_field_elements(dest);
|
||||
let (cmE_x, cmE_y) = match self.cmE.into_affine().xy() {
|
||||
Some((&x, &y)) => (x, y),
|
||||
None => (C::BaseField::zero(), C::BaseField::zero()),
|
||||
};
|
||||
let (cmW_x, cmW_y) = match self.cmW.into_affine().xy() {
|
||||
Some((&x, &y)) => (x, y),
|
||||
None => (C::BaseField::zero(), C::BaseField::zero()),
|
||||
};
|
||||
cmE_x.to_sponge_field_elements(dest);
|
||||
cmE_y.to_sponge_field_elements(dest);
|
||||
cmW_x.to_sponge_field_elements(dest);
|
||||
cmW_y.to_sponge_field_elements(dest);
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: CurveGroup> CommittedInstance<C>
|
||||
where
|
||||
<C as Group>::ScalarField: Absorb,
|
||||
@@ -135,25 +125,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: CurveGroup> CommittedInstance<C>
|
||||
where
|
||||
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField + Absorb,
|
||||
{
|
||||
/// hash_cyclefold implements the committed instance hash compatible with the gadget implemented in
|
||||
/// nova/cyclefold.rs::CycleFoldCommittedInstanceVar.hash.
|
||||
/// Returns `H(U_i)`, where `U_i` is the `CommittedInstance` for CycleFold.
|
||||
pub fn hash_cyclefold<T: Transcript<C::BaseField>>(
|
||||
&self,
|
||||
sponge: &T,
|
||||
pp_hash: C::BaseField, // public params hash
|
||||
) -> C::BaseField {
|
||||
let mut sponge = sponge.clone();
|
||||
sponge.absorb(&pp_hash);
|
||||
sponge.absorb_nonnative(self);
|
||||
sponge.squeeze_field_elements(1)[0]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
|
||||
pub struct Witness<C: CurveGroup> {
|
||||
pub E: Vec<C::ScalarField>,
|
||||
@@ -342,8 +313,8 @@ where
|
||||
pub U_i: CommittedInstance<C1>,
|
||||
|
||||
/// CycleFold running instance
|
||||
pub cf_W_i: Witness<C2>,
|
||||
pub cf_U_i: CommittedInstance<C2>,
|
||||
pub cf_W_i: CycleFoldWitness<C2>,
|
||||
pub cf_U_i: CycleFoldCommittedInstance<C2>,
|
||||
}
|
||||
|
||||
impl<C1, GC1, C2, GC2, FC, CS1, CS2, const H: bool> FoldingScheme<C1, C2, FC>
|
||||
@@ -370,7 +341,7 @@ where
|
||||
type RunningInstance = (CommittedInstance<C1>, Witness<C1>);
|
||||
type IncomingInstance = (CommittedInstance<C1>, Witness<C1>);
|
||||
type MultiCommittedInstanceWithWitness = ();
|
||||
type CFInstance = (CommittedInstance<C2>, Witness<C2>);
|
||||
type CFInstance = (CycleFoldCommittedInstance<C2>, CycleFoldWitness<C2>);
|
||||
|
||||
fn preprocess(
|
||||
mut rng: impl RngCore,
|
||||
@@ -428,7 +399,7 @@ where
|
||||
|
||||
let augmented_F_circuit =
|
||||
AugmentedFCircuit::<C1, C2, GC2, FC>::empty(&pp.poseidon_config, F.clone());
|
||||
let cf_circuit = CycleFoldCircuit::<C1, GC1>::empty(NOVA_CF_N_POINTS);
|
||||
let cf_circuit = NovaCycleFoldCircuit::<C1, GC1>::empty();
|
||||
|
||||
augmented_F_circuit.generate_constraints(cs.clone())?;
|
||||
cs.finalize();
|
||||
@@ -619,16 +590,14 @@ where
|
||||
]
|
||||
.concat();
|
||||
|
||||
let cfW_circuit = CycleFoldCircuit::<C1, GC1> {
|
||||
let cfW_circuit = NovaCycleFoldCircuit::<C1, GC1> {
|
||||
_gc: PhantomData,
|
||||
n_points: NOVA_CF_N_POINTS,
|
||||
r_bits: Some(vec![r_bits.clone()]),
|
||||
points: Some(vec![self.U_i.clone().cmW, self.u_i.clone().cmW]),
|
||||
x: Some(cfW_u_i_x.clone()),
|
||||
};
|
||||
let cfE_circuit = CycleFoldCircuit::<C1, GC1> {
|
||||
let cfE_circuit = NovaCycleFoldCircuit::<C1, GC1> {
|
||||
_gc: PhantomData,
|
||||
n_points: NOVA_CF_N_POINTS,
|
||||
r_bits: Some(vec![r_bits.clone()]),
|
||||
points: Some(vec![self.U_i.clone().cmE, cmT]),
|
||||
x: Some(cfE_u_i_x.clone()),
|
||||
@@ -855,24 +824,23 @@ where
|
||||
fn fold_cyclefold_circuit<T: Transcript<C1::ScalarField>>(
|
||||
&self,
|
||||
transcript: &mut T,
|
||||
cf_W_i: Witness<C2>, // witness of the running instance
|
||||
cf_U_i: CommittedInstance<C2>, // running instance
|
||||
cf_W_i: CycleFoldWitness<C2>, // witness of the running instance
|
||||
cf_U_i: CycleFoldCommittedInstance<C2>, // running instance
|
||||
cf_u_i_x: Vec<C2::ScalarField>,
|
||||
cf_circuit: CycleFoldCircuit<C1, GC1>,
|
||||
cf_circuit: NovaCycleFoldCircuit<C1, GC1>,
|
||||
rng: &mut impl RngCore,
|
||||
) -> Result<
|
||||
(
|
||||
Witness<C2>,
|
||||
CommittedInstance<C2>, // u_i
|
||||
Witness<C2>, // W_i1
|
||||
CommittedInstance<C2>, // U_i1
|
||||
C2, // cmT
|
||||
C2::ScalarField, // r_Fq
|
||||
CycleFoldWitness<C2>,
|
||||
CycleFoldCommittedInstance<C2>, // u_i
|
||||
CycleFoldWitness<C2>, // W_i1
|
||||
CycleFoldCommittedInstance<C2>, // U_i1
|
||||
C2, // cmT
|
||||
C2::ScalarField, // r_Fq
|
||||
),
|
||||
Error,
|
||||
> {
|
||||
fold_cyclefold_circuit::<C1, GC1, C2, GC2, FC, CS1, CS2, H>(
|
||||
NOVA_CF_N_POINTS,
|
||||
fold_cyclefold_circuit::<NovaCycleFoldConfig<C1>, C1, GC1, C2, GC2, CS2, H>(
|
||||
transcript,
|
||||
self.cf_r1cs.clone(),
|
||||
self.cf_cs_pp.clone(),
|
||||
@@ -920,7 +888,7 @@ where
|
||||
{
|
||||
let augmented_F_circuit =
|
||||
AugmentedFCircuit::<C1, C2, GC2, FC>::empty(poseidon_config, F_circuit);
|
||||
let cf_circuit = CycleFoldCircuit::<C1, GC1>::empty(NOVA_CF_N_POINTS);
|
||||
let cf_circuit = NovaCycleFoldCircuit::<C1, GC1>::empty();
|
||||
let r1cs = get_r1cs_from_cs::<C1::ScalarField>(augmented_F_circuit)?;
|
||||
let cf_r1cs = get_r1cs_from_cs::<C2::ScalarField>(cf_circuit)?;
|
||||
Ok((r1cs, cf_r1cs))
|
||||
|
||||
@@ -6,6 +6,7 @@ use std::marker::PhantomData;
|
||||
use super::{CommittedInstance, Witness};
|
||||
use crate::arith::r1cs::R1CS;
|
||||
use crate::commitment::CommitmentScheme;
|
||||
use crate::folding::circuits::cyclefold::{CycleFoldCommittedInstance, CycleFoldWitness};
|
||||
use crate::transcript::Transcript;
|
||||
use crate::utils::vec::{hadamard, mat_vec_mul, vec_add, vec_scalar_mul, vec_sub};
|
||||
use crate::Error;
|
||||
@@ -110,10 +111,10 @@ where
|
||||
pub fn compute_cyclefold_cmT(
|
||||
cs_prover_params: &CS::ProverParams,
|
||||
r1cs: &R1CS<C::ScalarField>, // R1CS over C2.Fr=C1.Fq (here C=C2)
|
||||
w1: &Witness<C>,
|
||||
ci1: &CommittedInstance<C>,
|
||||
w2: &Witness<C>,
|
||||
ci2: &CommittedInstance<C>,
|
||||
w1: &CycleFoldWitness<C>,
|
||||
ci1: &CycleFoldCommittedInstance<C>,
|
||||
w2: &CycleFoldWitness<C>,
|
||||
ci2: &CycleFoldCommittedInstance<C>,
|
||||
) -> Result<(Vec<C::ScalarField>, C), Error>
|
||||
where
|
||||
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
|
||||
|
||||
@@ -10,14 +10,14 @@ use ark_relations::r1cs::ConstraintSystem;
|
||||
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, SerializationError, Write};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use super::{circuits::AugmentedFCircuit, Nova, ProverParams};
|
||||
use super::{CommittedInstance, Witness};
|
||||
use crate::folding::{
|
||||
circuits::{cyclefold::CycleFoldCircuit, CF2},
|
||||
nova::NOVA_CF_N_POINTS,
|
||||
use super::{
|
||||
circuits::AugmentedFCircuit, CommittedInstance, Nova, NovaCycleFoldCircuit, ProverParams,
|
||||
Witness,
|
||||
};
|
||||
use crate::{
|
||||
arith::r1cs::extract_r1cs, commitment::CommitmentScheme, folding::circuits::CF1,
|
||||
arith::r1cs::extract_r1cs,
|
||||
commitment::CommitmentScheme,
|
||||
folding::circuits::{CF1, CF2},
|
||||
frontend::FCircuit,
|
||||
};
|
||||
|
||||
@@ -138,7 +138,7 @@ where
|
||||
let cs2 = ConstraintSystem::<C1::BaseField>::new_ref();
|
||||
let augmented_F_circuit =
|
||||
AugmentedFCircuit::<C1, C2, GC2, FC>::empty(&poseidon_config, f_circuit.clone());
|
||||
let cf_circuit = CycleFoldCircuit::<C1, GC1>::empty(NOVA_CF_N_POINTS);
|
||||
let cf_circuit = NovaCycleFoldCircuit::<C1, GC1>::empty();
|
||||
|
||||
augmented_F_circuit
|
||||
.generate_constraints(cs.clone())
|
||||
|
||||
@@ -226,7 +226,7 @@ pub mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_transcript_and_transcriptvar_nbits() {
|
||||
let nbits = crate::constants::N_BITS_RO;
|
||||
let nbits = crate::constants::NOVA_N_BITS_RO;
|
||||
|
||||
// use 'native' transcript
|
||||
let config = poseidon_canonical_config::<Fq>();
|
||||
|
||||
Reference in New Issue
Block a user