Implement HyperNova's IVC into the FoldingScheme trait (#116)

- implement the IVC `FoldingScheme` trait for HyperNova
- refactor Nova's preprocess logic to make it simplier to use
- add to Decider trait (& Nova's DeciderEth) a preprocess method
- get rid of the `init_nova_ivc_params` and `init_ivc_and_decider_params` methods in `examples` since this is achieved with the `FS::preprocess` & `Decider::preprocess` methods
  - (update the examples code to the simplified interface using
    FS::preprocess & Decider::preprocess)
This commit is contained in:
2024-07-04 11:14:31 +02:00
committed by GitHub
parent 456dc9f7a1
commit b5667968f4
25 changed files with 1144 additions and 465 deletions

View File

@@ -8,10 +8,7 @@ use ark_std::rand::Rng;
use super::Witness;
use crate::ccs::CCS;
use crate::commitment::{
pedersen::{Params as PedersenParams, Pedersen},
CommitmentScheme,
};
use crate::commitment::CommitmentScheme;
use crate::utils::mle::dense_vec_to_dense_mle;
use crate::utils::vec::mat_vec_mul;
use crate::utils::virtual_polynomial::{build_eq_x_r_vec, VirtualPolynomial};
@@ -27,10 +24,10 @@ pub struct CCCS<C: CurveGroup> {
}
impl<F: PrimeField> CCS<F> {
pub fn to_cccs<R: Rng, C: CurveGroup>(
pub fn to_cccs<R: Rng, C: CurveGroup, CS: CommitmentScheme<C>>(
&self,
rng: &mut R,
pedersen_params: &PedersenParams<C>,
cs_params: &CS::ProverParams,
z: &[C::ScalarField],
) -> Result<(CCCS<C>, Witness<C::ScalarField>), Error>
where
@@ -38,8 +35,14 @@ impl<F: PrimeField> CCS<F> {
C: CurveGroup<ScalarField = F>,
{
let w: Vec<C::ScalarField> = z[(1 + self.l)..].to_vec();
let r_w = C::ScalarField::rand(rng);
let C = Pedersen::<C, true>::commit(pedersen_params, &w, &r_w)?;
// if the commitment scheme is set to be hiding, set the random blinding parameter
let r_w = if CS::is_hiding() {
C::ScalarField::rand(rng)
} else {
C::ScalarField::zero()
};
let C = CS::commit(cs_params, &w, &r_w)?;
Ok((
CCCS::<C> {
@@ -95,19 +98,13 @@ impl<C: CurveGroup> CCCS<C> {
}
}
/// Perform the check of the CCCS instance described at section 4.1
/// Perform the check of the CCCS instance described at section 4.1,
/// notice that this method does not check the commitment correctness
pub fn check_relation(
&self,
pedersen_params: &PedersenParams<C>,
ccs: &CCS<C::ScalarField>,
w: &Witness<C::ScalarField>,
) -> Result<(), Error> {
// check that C is the commitment of w. Notice that this is not verifying a Pedersen
// opening, but checking that the commitment comes from committing to the witness.
if self.C != Pedersen::<C, true>::commit(pedersen_params, &w.w, &w.r_w)? {
return Err(Error::NotSatisfied);
}
// check CCCS relation
let z: Vec<C::ScalarField> =
[vec![C::ScalarField::one()], self.x.clone(), w.w.to_vec()].concat();

View File

@@ -596,7 +596,7 @@ where
/// Returns the cs (ConstraintSystem) and the CCS out of the AugmentedFCircuit
#[allow(clippy::type_complexity)]
fn compute_cs_ccs(
pub fn compute_cs_ccs(
&self,
) -> Result<(ConstraintSystem<C1::ScalarField>, CCS<C1::ScalarField>), Error> {
let cs = ConstraintSystem::<C1::ScalarField>::new_ref();
@@ -720,8 +720,14 @@ where
z_0.clone(),
z_i1.clone(),
)?;
let x = FpVar::new_input(cs.clone(), || Ok(self.x.unwrap_or(C1::ScalarField::zero())))?;
x.enforce_equal(&u_i1_x)?;
let (u_i1_x_base, _) = LCCCSVar::new_constant(cs.clone(), U_dummy)?.hash(
&crh_params,
FpVar::<CF1<C1>>::one(),
z_0.clone(),
z_i1.clone(),
)?;
let x = FpVar::new_input(cs.clone(), || Ok(self.x.unwrap_or(u_i1_x_base.value()?)))?;
x.enforce_equal(&is_basecase.select(&u_i1_x_base, &u_i1_x)?)?;
// convert rho_bits to a `NonNativeFieldVar`
let rho_nonnat = {
@@ -817,15 +823,14 @@ mod tests {
utils::{compute_c, compute_sigmas_thetas},
Witness,
},
nova::{
get_cm_coordinates, traits::NovaR1CS, CommittedInstance, Witness as NovaWitness,
},
nova::{traits::NovaR1CS, CommittedInstance, Witness as NovaWitness},
},
frontend::tests::CubicFCircuit,
transcript::{
poseidon::{poseidon_canonical_config, PoseidonTranscript, PoseidonTranscriptVar},
Transcript,
},
utils::get_cm_coordinates,
};
#[test]
@@ -858,13 +863,17 @@ mod tests {
// Create the LCCCS instances out of z_lcccs
let mut lcccs_instances = Vec::new();
for z_i in z_lcccs.iter() {
let (inst, _) = ccs.to_lcccs(&mut rng, &pedersen_params, z_i).unwrap();
let (inst, _) = ccs
.to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, z_i)
.unwrap();
lcccs_instances.push(inst);
}
// Create the CCCS instance out of z_cccs
let mut cccs_instances = Vec::new();
for z_i in z_cccs.iter() {
let (inst, _) = ccs.to_cccs(&mut rng, &pedersen_params, z_i).unwrap();
let (inst, _) = ccs
.to_cccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, z_i)
.unwrap();
cccs_instances.push(inst);
}
@@ -950,7 +959,9 @@ mod tests {
let mut lcccs_instances = Vec::new();
let mut w_lcccs = Vec::new();
for z_i in z_lcccs.iter() {
let (running_instance, w) = ccs.to_lcccs(&mut rng, &pedersen_params, z_i).unwrap();
let (running_instance, w) = ccs
.to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, z_i)
.unwrap();
lcccs_instances.push(running_instance);
w_lcccs.push(w);
}
@@ -958,7 +969,9 @@ mod tests {
let mut cccs_instances = Vec::new();
let mut w_cccs = Vec::new();
for z_i in z_cccs.iter() {
let (new_instance, w) = ccs.to_cccs(&mut rng, &pedersen_params, z_i).unwrap();
let (new_instance, w) = ccs
.to_cccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, z_i)
.unwrap();
cccs_instances.push(new_instance);
w_cccs.push(w);
}
@@ -996,9 +1009,7 @@ mod tests {
assert_eq!(folded_lcccs, folded_lcccs_v);
// Check that the folded LCCCS instance is a valid instance with respect to the folded witness
folded_lcccs
.check_relation(&pedersen_params, &ccs, &folded_witness)
.unwrap();
folded_lcccs.check_relation(&ccs, &folded_witness).unwrap();
// allocate circuit inputs
let cs = ConstraintSystem::<Fr>::new_ref();
@@ -1042,7 +1053,9 @@ mod tests {
let i = Fr::from(3_u32);
let z_0 = vec![Fr::from(3_u32)];
let z_i = vec![Fr::from(3_u32)];
let (lcccs, _) = ccs.to_lcccs(&mut rng, &pedersen_params, &z1).unwrap();
let (lcccs, _) = ccs
.to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, &z1)
.unwrap();
let h = lcccs
.clone()
.hash(&poseidon_config, i, z_0.clone(), z_i.clone())
@@ -1104,11 +1117,11 @@ mod tests {
let mut z_i = z_0.clone();
// prepare the dummy instances
let W_dummy = Witness::<Fr>::new(vec![Fr::zero(); ccs.n - ccs.l - 1]);
let W_dummy = Witness::<Fr>::dummy(&ccs);
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>) =
let (cf_W_dummy, cf_U_dummy): (NovaWitness<Projective2>, CommittedInstance<Projective2>) =
cf_r1cs.dummy_instance();
// set the initial dummy instances
@@ -1116,8 +1129,8 @@ mod tests {
let mut U_i = U_dummy.clone();
let mut w_i = w_dummy.clone();
let mut u_i = u_dummy.clone();
let mut cf_W_i = cf_w_dummy.clone();
let mut cf_U_i = cf_u_dummy.clone();
let mut cf_W_i = cf_W_dummy.clone();
let mut cf_U_i = cf_U_dummy.clone();
u_i.x = vec![
U_i.hash(&poseidon_config, Fr::zero(), z_0.clone(), z_i.clone())
.unwrap(),
@@ -1128,28 +1141,19 @@ mod tests {
let mut iFr = Fr::zero();
for i in 0..n_steps {
let start = Instant::now();
let mut transcript_p: PoseidonTranscript<Projective> =
PoseidonTranscript::<Projective>::new(&poseidon_config.clone());
let (nimfs_proof, U_i1, W_i1, rho_bits) =
NIMFS::<Projective, PoseidonTranscript<Projective>>::prove(
&mut transcript_p,
&ccs,
&[U_i.clone()],
&[u_i.clone()],
&[W_i.clone()],
&[w_i.clone()],
)
.unwrap();
// sanity check: check the folded instance relation
U_i1.check_relation(&pedersen_params, &ccs, &W_i1).unwrap();
let z_i1 = F_circuit.step_native(i, z_i.clone(), vec![]).unwrap();
let u_i1_x = U_i1
.hash(&poseidon_config, iFr + Fr::one(), z_0.clone(), z_i1.clone())
.unwrap();
let (U_i1, W_i1);
if i == 0 {
W_i1 = Witness::<Fr>::dummy(&ccs);
U_i1 = LCCCS::dummy(ccs.l, ccs.t, ccs.s);
let u_i1_x = U_i1
.hash(&poseidon_config, Fr::one(), z_0.clone(), z_i1.clone())
.unwrap();
// hash the initial (dummy) CycleFold instance, which is used as the 2nd public
// input in the AugmentedFCircuit
let cf_u_i1_x = cf_U_i.hash_cyclefold(&poseidon_config).unwrap();
@@ -1160,8 +1164,8 @@ mod tests {
_gc2: PhantomData,
poseidon_config: poseidon_config.clone(),
ccs: ccs.clone(),
i: Some(iFr),
i_usize: Some(i),
i: Some(Fr::zero()),
i_usize: Some(0),
z_0: Some(z_0.clone()),
z_i: Some(z_i.clone()),
external_inputs: Some(vec![]),
@@ -1170,7 +1174,7 @@ mod tests {
U_i1_C: Some(U_i1.C),
F: F_circuit,
x: Some(u_i1_x),
nimfs_proof: Some(nimfs_proof),
nimfs_proof: None,
// cyclefold values
cf_u_i_cmW: None,
@@ -1179,6 +1183,27 @@ mod tests {
cf_cmT: None,
};
} else {
let mut transcript_p: PoseidonTranscript<Projective> =
PoseidonTranscript::<Projective>::new(&poseidon_config.clone());
let (rho_bits, nimfs_proof);
(nimfs_proof, U_i1, W_i1, rho_bits) =
NIMFS::<Projective, PoseidonTranscript<Projective>>::prove(
&mut transcript_p,
&ccs,
&[U_i.clone()],
&[u_i.clone()],
&[W_i.clone()],
&[w_i.clone()],
)
.unwrap();
// sanity check: check the folded instance relation
U_i1.check_relation(&ccs, &W_i1).unwrap();
let u_i1_x = U_i1
.hash(&poseidon_config, iFr + Fr::one(), z_0.clone(), z_i1.clone())
.unwrap();
let rho_Fq = Fq::from_bigint(BigInteger::from_bits_le(&rho_bits)).unwrap();
// CycleFold part:
// get the vector used as public inputs 'x' in the CycleFold circuit
@@ -1260,8 +1285,10 @@ mod tests {
let r1cs_z = [vec![Fr::one()], r1cs_x_i1.clone(), r1cs_w_i1.clone()].concat();
// compute committed instances, w_{i+1}, u_{i+1}, which will be used as w_i, u_i, so we
// assign them directly to w_i, u_i.
(u_i, w_i) = ccs.to_cccs(&mut rng, &pedersen_params, &r1cs_z).unwrap();
u_i.check_relation(&pedersen_params, &ccs, &w_i).unwrap();
(u_i, w_i) = ccs
.to_cccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, &r1cs_z)
.unwrap();
u_i.check_relation(&ccs, &w_i).unwrap();
// sanity checks
assert_eq!(w_i.w, r1cs_w_i1);
@@ -1284,9 +1311,9 @@ mod tests {
W_i = W_i1.clone();
// check the new LCCCS instance relation
U_i.check_relation(&pedersen_params, &ccs, &W_i).unwrap();
U_i.check_relation(&ccs, &W_i).unwrap();
// check the new CCCS instance relation
u_i.check_relation(&pedersen_params, &ccs, &w_i).unwrap();
u_i.check_relation(&ccs, &w_i).unwrap();
// check the CycleFold instance relation
cf_r1cs

View File

@@ -11,10 +11,7 @@ use ark_std::Zero;
use super::Witness;
use crate::ccs::CCS;
use crate::commitment::{
pedersen::{Params as PedersenParams, Pedersen},
CommitmentScheme,
};
use crate::commitment::CommitmentScheme;
use crate::folding::circuits::nonnative::affine::nonnative_affine_to_field_elements;
use crate::utils::mle::dense_vec_to_dense_mle;
use crate::utils::vec::mat_vec_mul;
@@ -36,10 +33,10 @@ pub struct LCCCS<C: CurveGroup> {
}
impl<F: PrimeField> CCS<F> {
pub fn to_lcccs<R: Rng, C: CurveGroup>(
pub fn to_lcccs<R: Rng, C: CurveGroup, CS: CommitmentScheme<C>>(
&self,
rng: &mut R,
pedersen_params: &PedersenParams<C>,
cs_params: &CS::ProverParams,
z: &[C::ScalarField],
) -> Result<(LCCCS<C>, Witness<C::ScalarField>), Error>
where
@@ -47,8 +44,13 @@ impl<F: PrimeField> CCS<F> {
C: CurveGroup<ScalarField = F>,
{
let w: Vec<C::ScalarField> = z[(1 + self.l)..].to_vec();
let r_w = C::ScalarField::rand(rng);
let C = Pedersen::<C, true>::commit(pedersen_params, &w, &r_w)?;
// if the commitment scheme is set to be hiding, set the random blinding parameter
let r_w = if CS::is_hiding() {
C::ScalarField::rand(rng)
} else {
C::ScalarField::zero()
};
let C = CS::commit(cs_params, &w, &r_w)?;
let r_x: Vec<C::ScalarField> = (0..self.s).map(|_| C::ScalarField::rand(rng)).collect();
@@ -91,19 +93,13 @@ impl<C: CurveGroup> LCCCS<C> {
}
}
/// Perform the check of the LCCCS instance described at section 4.2
/// Perform the check of the LCCCS instance described at section 4.2,
/// notice that this method does not check the commitment correctness
pub fn check_relation(
&self,
pedersen_params: &PedersenParams<C>,
ccs: &CCS<C::ScalarField>,
w: &Witness<C::ScalarField>,
) -> Result<(), Error> {
// check that C is the commitment of w. Notice that this is not verifying a Pedersen
// opening, but checking that the Commitment comes from committing to the witness.
if self.C != Pedersen::<C, true>::commit(pedersen_params, &w.w, &w.r_w)? {
return Err(Error::NotSatisfied);
}
// check CCS relation
let z: Vec<C::ScalarField> = [vec![self.u], self.x.clone(), w.w.to_vec()].concat();
@@ -172,6 +168,7 @@ pub mod tests {
r1cs::R1CS,
tests::{get_test_ccs, get_test_z},
};
use crate::commitment::pedersen::Pedersen;
use crate::utils::hypercube::BooleanHypercube;
use crate::utils::virtual_polynomial::{build_eq_x_r_vec, VirtualPolynomial};
@@ -206,14 +203,16 @@ pub mod tests {
let n_rows = 2_u32.pow(5) as usize;
let n_cols = 2_u32.pow(5) as usize;
let r1cs = R1CS::rand(&mut rng, n_rows, n_cols);
let r1cs = R1CS::<Fr>::rand(&mut rng, n_rows, n_cols);
let ccs = CCS::from_r1cs(r1cs);
let z: Vec<Fr> = (0..n_cols).map(|_| Fr::rand(&mut rng)).collect();
let (pedersen_params, _) =
Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap();
let (lcccs, _) = ccs.to_lcccs(&mut rng, &pedersen_params, &z).unwrap();
let (lcccs, _) = ccs
.to_lcccs::<_, Projective, Pedersen<Projective>>(&mut rng, &pedersen_params, &z)
.unwrap();
// with our test vector coming from R1CS, v should have length 3
assert_eq!(lcccs.v.len(), 3);
@@ -245,7 +244,9 @@ pub mod tests {
let (pedersen_params, _) =
Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap();
// Compute v_j with the right z
let (lcccs, _) = ccs.to_lcccs(&mut rng, &pedersen_params, &z).unwrap();
let (lcccs, _) = ccs
.to_lcccs::<_, Projective, Pedersen<Projective>>(&mut rng, &pedersen_params, &z)
.unwrap();
// with our test vector coming from R1CS, v should have length 3
assert_eq!(lcccs.v.len(), 3);

View File

@@ -1,12 +1,42 @@
/// Implements the scheme described in [HyperNova](https://eprint.iacr.org/2023/573.pdf)
use crate::ccs::CCS;
use ark_ff::PrimeField;
use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb};
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;
pub mod cccs;
pub mod circuits;
use circuits::AugmentedFCircuit;
pub mod lcccs;
pub mod nimfs;
pub mod utils;
use cccs::CCCS;
use lcccs::LCCCS;
use nimfs::NIMFS;
use crate::commitment::CommitmentScheme;
use crate::folding::circuits::{
cyclefold::{fold_cyclefold_circuit, CycleFoldCircuit},
CF2,
};
use crate::folding::nova::{
get_r1cs_from_cs, traits::NovaR1CS, CommittedInstance, Witness as NovaWitness,
};
use crate::frontend::FCircuit;
use crate::utils::get_cm_coordinates;
use crate::Error;
use crate::FoldingScheme;
use crate::{
ccs::{
r1cs::{extract_w_x, R1CS},
CCS,
},
transcript::{poseidon::PoseidonTranscript, Transcript},
};
/// 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)]
@@ -25,3 +55,556 @@ impl<F: PrimeField> Witness<F> {
Witness::<F>::new(vec![F::zero(); ccs.n - ccs.l - 1])
}
}
#[derive(Debug, Clone)]
pub struct PreprocessorParam<C1, C2, FC, CS1, CS2>
where
C1: CurveGroup,
C2: CurveGroup,
FC: FCircuit<C1::ScalarField>,
CS1: CommitmentScheme<C1>,
CS2: CommitmentScheme<C2>,
{
pub poseidon_config: PoseidonConfig<C1::ScalarField>,
pub F: FC,
// cs_params & cf_cs_params: if not provided, will be generated at the preprocess method
pub cs_params: Option<CS1::ProverParams>,
pub cf_cs_params: Option<CS2::ProverParams>,
}
#[derive(Debug, Clone)]
pub struct ProverParams<C1, C2, CS1, CS2>
where
C1: CurveGroup,
C2: CurveGroup,
CS1: CommitmentScheme<C1>,
CS2: CommitmentScheme<C2>,
{
pub poseidon_config: PoseidonConfig<C1::ScalarField>,
pub cs_params: CS1::ProverParams,
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>>,
}
#[derive(Debug, Clone)]
pub struct VerifierParams<
C1: CurveGroup,
C2: CurveGroup,
CS1: CommitmentScheme<C1>,
CS2: CommitmentScheme<C2>,
> {
pub poseidon_config: PoseidonConfig<C1::ScalarField>,
pub ccs: CCS<C1::ScalarField>,
pub cf_r1cs: R1CS<C2::ScalarField>,
pub cs_params: CS1::ProverParams,
pub cf_cs_params: CS2::ProverParams,
}
/// 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
#[derive(Clone, Debug)]
pub struct HyperNova<C1, GC1, C2, GC2, FC, CS1, CS2>
where
C1: CurveGroup,
GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
C2: CurveGroup,
GC2: CurveVar<C2, CF2<C2>>,
FC: FCircuit<C1::ScalarField>,
CS1: CommitmentScheme<C1>,
CS2: CommitmentScheme<C2>,
{
_gc1: PhantomData<GC1>,
_c2: PhantomData<C2>,
_gc2: PhantomData<GC2>,
/// CCS of the Augmented Function circuit
pub ccs: CCS<C1::ScalarField>,
/// R1CS of the CycleFold circuit
pub cf_r1cs: R1CS<C2::ScalarField>,
pub poseidon_config: PoseidonConfig<C1::ScalarField>,
/// CommitmentScheme::ProverParams over C1
pub cs_params: CS1::ProverParams,
/// CycleFold CommitmentScheme::ProverParams, over C2
pub cf_cs_params: CS2::ProverParams,
/// F circuit, the circuit that is being folded
pub F: FC,
pub i: C1::ScalarField,
/// initial state
pub z_0: Vec<C1::ScalarField>,
/// current i-th state
pub z_i: Vec<C1::ScalarField>,
/// HyperNova instances
pub W_i: Witness<C1::ScalarField>,
pub U_i: LCCCS<C1>,
pub w_i: Witness<C1::ScalarField>,
pub u_i: CCCS<C1>,
/// CycleFold running instance
pub cf_W_i: NovaWitness<C2>,
pub cf_U_i: CommittedInstance<C2>,
}
impl<C1, GC1, C2, GC2, FC, CS1, CS2> FoldingScheme<C1, C2, FC>
for HyperNova<C1, GC1, C2, GC2, FC, CS1, CS2>
where
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>,
CS2: CommitmentScheme<C2>,
<C1 as CurveGroup>::BaseField: PrimeField,
<C2 as CurveGroup>::BaseField: PrimeField,
<C1 as Group>::ScalarField: Absorb,
<C2 as Group>::ScalarField: Absorb,
C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,
for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>,
for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>,
{
type PreprocessorParam = PreprocessorParam<C1, C2, FC, CS1, CS2>;
type ProverParam = ProverParams<C1, C2, CS1, CS2>;
type VerifierParam = VerifierParams<C1, C2, CS1, CS2>;
type RunningInstance = (LCCCS<C1>, Witness<C1::ScalarField>);
type IncomingInstance = (CCCS<C1>, Witness<C1::ScalarField>);
type CFInstance = (CommittedInstance<C2>, NovaWitness<C2>);
fn preprocess(
mut rng: impl RngCore,
prep_param: &Self::PreprocessorParam,
) -> Result<(Self::ProverParam, Self::VerifierParam), Error> {
let augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC>::empty(
&prep_param.poseidon_config,
prep_param.F.clone(),
None,
)?;
let ccs = augmented_f_circuit.ccs.clone();
let cf_circuit = CycleFoldCircuit::<C1, GC1>::empty();
let cf_r1cs = get_r1cs_from_cs::<C2::ScalarField>(cf_circuit)?;
// if cs_params & cf_cs_params exist, use them, if not, generate new ones
let cs_params: CS1::ProverParams;
let cf_cs_params: CS2::ProverParams;
if prep_param.cs_params.is_some() && prep_param.cf_cs_params.is_some() {
cs_params = prep_param.clone().cs_params.unwrap();
cf_cs_params = prep_param.clone().cf_cs_params.unwrap();
} else {
(cs_params, _) = CS1::setup(&mut rng, ccs.n - ccs.l - 1).unwrap();
(cf_cs_params, _) = CS2::setup(&mut rng, cf_r1cs.A.n_cols - cf_r1cs.l - 1).unwrap();
}
let pp = ProverParams::<C1, C2, CS1, CS2> {
poseidon_config: prep_param.poseidon_config.clone(),
cs_params: cs_params.clone(),
cf_cs_params: cf_cs_params.clone(),
ccs: Some(ccs.clone()),
};
let vp = VerifierParams::<C1, C2, CS1, CS2> {
poseidon_config: prep_param.poseidon_config.clone(),
ccs,
cf_r1cs,
cs_params: cs_params.clone(),
cf_cs_params: cf_cs_params.clone(),
};
Ok((pp, vp))
}
/// Initializes the HyperNova+CycleFold's IVC for the given parameters and initial state `z_0`.
fn init(pp: &Self::ProverParam, F: FC, z_0: Vec<C1::ScalarField>) -> Result<Self, Error> {
// 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(
&pp.poseidon_config,
F.clone(),
pp.ccs.clone(),
)?;
let ccs = augmented_f_circuit.ccs.clone();
let cf_circuit = CycleFoldCircuit::<C1, GC1>::empty();
let cf_r1cs = get_r1cs_from_cs::<C2::ScalarField>(cf_circuit)?;
// setup the dummy instances
let W_dummy = Witness::<C1::ScalarField>::dummy(&ccs);
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>) =
cf_r1cs.dummy_instance();
u_dummy.x = vec![
U_dummy.hash(
&pp.poseidon_config,
C1::ScalarField::zero(),
z_0.clone(),
z_0.clone(),
)?,
cf_U_dummy.hash_cyclefold(&pp.poseidon_config)?,
];
// W_dummy=W_0 is a 'dummy witness', all zeroes, but with the size corresponding to the
// R1CS that we're working with.
Ok(Self {
_gc1: PhantomData,
_c2: PhantomData,
_gc2: PhantomData,
ccs,
cf_r1cs,
poseidon_config: pp.poseidon_config.clone(),
cs_params: pp.cs_params.clone(),
cf_cs_params: pp.cf_cs_params.clone(),
F,
i: C1::ScalarField::zero(),
z_0: z_0.clone(),
z_i: z_0,
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,
cf_U_i: cf_U_dummy,
})
}
/// Implements IVC.P of HyperNova+CycleFold
fn prove_step(
&mut self,
mut rng: impl RngCore,
external_inputs: Vec<C1::ScalarField>,
) -> Result<(), Error> {
let augmented_f_circuit: AugmentedFCircuit<C1, C2, GC2, FC>;
if self.z_i.len() != self.F.state_len() {
return Err(Error::NotSameLength(
"z_i.len()".to_string(),
self.z_i.len(),
"F.state_len()".to_string(),
self.F.state_len(),
));
}
if external_inputs.len() != self.F.external_inputs_len() {
return Err(Error::NotSameLength(
"F.external_inputs_len()".to_string(),
self.F.external_inputs_len(),
"external_inputs.len()".to_string(),
external_inputs.len(),
));
}
if self.i > C1::ScalarField::from_le_bytes_mod_order(&usize::MAX.to_le_bytes()) {
return Err(Error::MaxStep);
}
let mut i_bytes: [u8; 8] = [0; 8];
i_bytes.copy_from_slice(&self.i.into_bigint().to_bytes_le()[..8]);
let i_usize: usize = usize::from_le_bytes(i_bytes);
let z_i1 = self
.F
.step_native(i_usize, self.z_i.clone(), external_inputs.clone())?;
// u_{i+1}.x[1] = H(cf_U_{i+1})
let cf_u_i1_x: C1::ScalarField;
let (U_i1, W_i1);
if self.i == C1::ScalarField::zero() {
W_i1 = Witness::<C1::ScalarField>::dummy(&self.ccs);
U_i1 = LCCCS::dummy(self.ccs.l, self.ccs.t, self.ccs.s);
let u_i1_x = U_i1.hash(
&self.poseidon_config,
C1::ScalarField::one(),
self.z_0.clone(),
z_i1.clone(),
)?;
// hash the initial (dummy) CycleFold instance, which is used as the 2nd public
// input in the AugmentedFCircuit
cf_u_i1_x = self.cf_U_i.hash_cyclefold(&self.poseidon_config)?;
augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> {
_c2: PhantomData,
_gc2: PhantomData,
poseidon_config: self.poseidon_config.clone(),
ccs: self.ccs.clone(),
i: Some(C1::ScalarField::zero()),
i_usize: Some(0),
z_0: Some(self.z_0.clone()),
z_i: Some(self.z_i.clone()),
external_inputs: Some(external_inputs.clone()),
u_i_C: Some(self.u_i.C),
U_i: Some(self.U_i.clone()),
U_i1_C: Some(U_i1.C),
F: self.F.clone(),
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,
};
} else {
let mut transcript_p: PoseidonTranscript<C1> =
PoseidonTranscript::<C1>::new(&self.poseidon_config);
let (rho_bits, nimfs_proof);
(nimfs_proof, U_i1, W_i1, rho_bits) = NIMFS::<C1, PoseidonTranscript<C1>>::prove(
&mut transcript_p,
&self.ccs,
&[self.U_i.clone()],
&[self.u_i.clone()],
&[self.W_i.clone()],
&[self.w_i.clone()],
)?;
// sanity check: check the folded instance relation
#[cfg(test)]
U_i1.check_relation(&self.ccs, &W_i1)?;
let u_i1_x = U_i1.hash(
&self.poseidon_config,
self.i + C1::ScalarField::one(),
self.z_0.clone(),
z_i1.clone(),
)?;
let rho_Fq = C2::ScalarField::from_bigint(BigInteger::from_bits_le(&rho_bits))
.ok_or(Error::OutOfBounds)?;
// CycleFold part:
// get the vector used as public inputs 'x' in the CycleFold circuit
// cyclefold circuit for cmW
let cf_u_i_x = [
vec![rho_Fq],
get_cm_coordinates(&self.U_i.C),
get_cm_coordinates(&self.u_i.C),
get_cm_coordinates(&U_i1.C),
]
.concat();
let cf_circuit = CycleFoldCircuit::<C1, GC1> {
_gc: PhantomData,
r_bits: Some(rho_bits.clone()),
p1: Some(self.U_i.clone().C),
p2: Some(self.u_i.clone().C),
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>(
&self.poseidon_config,
self.cf_r1cs.clone(),
self.cf_cs_params.clone(),
self.cf_W_i.clone(), // CycleFold running instance witness
self.cf_U_i.clone(), // CycleFold running instance
cf_u_i_x,
cf_circuit,
)?;
cf_u_i1_x = cf_U_i1.hash_cyclefold(&self.poseidon_config)?;
augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> {
_c2: PhantomData,
_gc2: PhantomData,
poseidon_config: self.poseidon_config.clone(),
ccs: self.ccs.clone(),
i: Some(self.i),
i_usize: Some(i_usize),
z_0: Some(self.z_0.clone()),
z_i: Some(self.z_i.clone()),
external_inputs: Some(external_inputs),
u_i_C: Some(self.u_i.C),
U_i: Some(self.U_i.clone()),
U_i1_C: Some(U_i1.C),
F: self.F.clone(),
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(self.cf_U_i.clone()),
cf_x: Some(cf_u_i1_x),
cf_cmT: Some(cf_cmT),
};
// assign the next round instances
self.cf_W_i = cf_W_i1;
self.cf_U_i = cf_U_i1;
}
let (cs, _) = augmented_f_circuit.compute_cs_ccs()?;
#[cfg(test)]
assert!(cs.is_satisfied()?);
let (r1cs_w_i1, r1cs_x_i1) = extract_w_x::<C1::ScalarField>(&cs); // includes 1 and public inputs
let r1cs_z = [
vec![C1::ScalarField::one()],
r1cs_x_i1.clone(),
r1cs_w_i1.clone(),
]
.concat();
// compute committed instances, w_{i+1}, u_{i+1}, which will be used as w_i, u_i, so we
// assign them directly to w_i, u_i.
let (u_i, w_i) = self
.ccs
.to_cccs::<_, C1, CS1>(&mut rng, &self.cs_params, &r1cs_z)?;
self.u_i = u_i.clone();
self.w_i = w_i.clone();
// set values for next iteration
self.i += C1::ScalarField::one();
// assign z_{i+1} into z_i
self.z_i = z_i1.clone();
self.U_i = U_i1.clone();
self.W_i = W_i1.clone();
#[cfg(test)]
{
// check the new LCCCS instance relation
self.U_i.check_relation(&self.ccs, &self.W_i)?;
// check the new CCCS instance relation
self.u_i.check_relation(&self.ccs, &self.w_i)?;
}
Ok(())
}
fn state(&self) -> Vec<C1::ScalarField> {
self.z_i.clone()
}
fn instances(
&self,
) -> (
Self::RunningInstance,
Self::IncomingInstance,
Self::CFInstance,
) {
(
(self.U_i.clone(), self.W_i.clone()),
(self.u_i.clone(), self.w_i.clone()),
(self.cf_U_i.clone(), self.cf_W_i.clone()),
)
}
/// Implements IVC.V of HyperNova+CycleFold. Notice that this method does not include the
/// commitments verification, which is done in the Decider.
fn verify(
vp: Self::VerifierParam,
z_0: Vec<C1::ScalarField>, // initial state
z_i: Vec<C1::ScalarField>, // last state
num_steps: C1::ScalarField,
running_instance: Self::RunningInstance,
incoming_instance: Self::IncomingInstance,
cyclefold_instance: Self::CFInstance,
) -> Result<(), Error> {
if num_steps == C1::ScalarField::zero() {
if z_0 != z_i {
return Err(Error::IVCVerificationFail);
}
return Ok(());
}
let (U_i, W_i) = running_instance;
let (u_i, w_i) = incoming_instance;
let (cf_U_i, cf_W_i) = cyclefold_instance;
if u_i.x.len() != 2 || U_i.x.len() != 2 {
return Err(Error::IVCVerificationFail);
}
// check that u_i's output points to the running instance
// u_i.X[0] == H(i, z_0, z_i, U_i)
let expected_u_i_x = U_i.hash(&vp.poseidon_config, num_steps, z_0, z_i.clone())?;
if expected_u_i_x != u_i.x[0] {
return Err(Error::IVCVerificationFail);
}
// u_i.X[1] == H(cf_U_i)
let expected_cf_u_i_x = cf_U_i.hash_cyclefold(&vp.poseidon_config)?;
if expected_cf_u_i_x != u_i.x[1] {
return Err(Error::IVCVerificationFail);
}
// check LCCCS satisfiability
U_i.check_relation(&vp.ccs, &W_i)?;
// check CCCS satisfiability
u_i.check_relation(&vp.ccs, &w_i)?;
// check CycleFold's RelaxedR1CS satisfiability
vp.cf_r1cs
.check_relaxed_instance_relation(&cf_W_i, &cf_U_i)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use crate::commitment::kzg::KZG;
use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective};
use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2};
use super::*;
use crate::commitment::pedersen::Pedersen;
use crate::frontend::tests::CubicFCircuit;
use crate::transcript::poseidon::poseidon_canonical_config;
#[test]
pub fn test_ivc() {
let poseidon_config = poseidon_canonical_config::<Fr>();
let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap();
// run the test using Pedersen commitments on both sides of the curve cycle
test_ivc_opt::<Pedersen<Projective>, Pedersen<Projective2>>(
poseidon_config.clone(),
F_circuit,
);
// run the test using KZG for the commitments on the main curve, and Pedersen for the
// commitments on the secondary curve
test_ivc_opt::<KZG<Bn254>, Pedersen<Projective2>>(poseidon_config, F_circuit);
}
// test_ivc allowing to choose the CommitmentSchemes
fn test_ivc_opt<CS1: CommitmentScheme<Projective>, CS2: CommitmentScheme<Projective2>>(
poseidon_config: PoseidonConfig<Fr>,
F_circuit: CubicFCircuit<Fr>,
) {
let mut rng = ark_std::test_rng();
type HN<CS1, CS2> =
HyperNova<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2>;
let prep_param = PreprocessorParam::<Projective, Projective2, CubicFCircuit<Fr>, CS1, CS2> {
poseidon_config,
F: F_circuit,
cs_params: None,
cf_cs_params: None,
};
let (prover_params, verifier_params) = HN::preprocess(&mut rng, &prep_param).unwrap();
let z_0 = vec![Fr::from(3_u32)];
let mut hypernova = HN::init(&prover_params, F_circuit, z_0.clone()).unwrap();
let num_steps: usize = 3;
for _ in 0..num_steps {
hypernova.prove_step(&mut rng, vec![]).unwrap();
}
assert_eq!(Fr::from(num_steps as u32), hypernova.i);
let (running_instance, incoming_instance, cyclefold_instance) = hypernova.instances();
HN::verify(
verifier_params,
z_0,
hypernova.z_i,
hypernova.i,
running_instance,
incoming_instance,
cyclefold_instance,
)
.unwrap();
}
}

View File

@@ -32,19 +32,21 @@ pub struct NIMFSProof<C: CurveGroup> {
impl<C: CurveGroup> NIMFSProof<C> {
pub fn dummy(ccs: &CCS<C::ScalarField>, mu: usize, nu: usize) -> Self {
// use 'C::ScalarField::one()' instead of 'zero()' to enforce the NIMFSProof to have the
// same in-circuit representation to match the number of constraints of an actual proof.
NIMFSProof::<C> {
sc_proof: SumCheckProof::<C::ScalarField> {
point: vec![C::ScalarField::zero(); ccs.d],
point: vec![C::ScalarField::one(); ccs.s],
proofs: vec![
IOPProverMessage {
coeffs: vec![C::ScalarField::zero(); ccs.t + 1]
coeffs: vec![C::ScalarField::one(); ccs.t + 1]
};
ccs.s
],
},
sigmas_thetas: SigmasThetas(
vec![vec![C::ScalarField::zero(); ccs.t]; mu],
vec![vec![C::ScalarField::zero(); ccs.t]; nu],
vec![vec![C::ScalarField::one(); ccs.t]; mu],
vec![vec![C::ScalarField::one(); ccs.t]; nu],
),
}
}
@@ -432,11 +434,15 @@ pub mod tests {
let (pedersen_params, _) =
Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap();
let (lcccs, w1) = ccs.to_lcccs(&mut rng, &pedersen_params, &z1).unwrap();
let (cccs, w2) = ccs.to_cccs(&mut rng, &pedersen_params, &z2).unwrap();
let (lcccs, w1) = ccs
.to_lcccs::<_, Projective, Pedersen<Projective>>(&mut rng, &pedersen_params, &z1)
.unwrap();
let (cccs, w2) = ccs
.to_cccs::<_, Projective, Pedersen<Projective>>(&mut rng, &pedersen_params, &z2)
.unwrap();
lcccs.check_relation(&pedersen_params, &ccs, &w1).unwrap();
cccs.check_relation(&pedersen_params, &ccs, &w2).unwrap();
lcccs.check_relation(&ccs, &w1).unwrap();
cccs.check_relation(&ccs, &w2).unwrap();
let mut rng = test_rng();
let rho = Fr::rand(&mut rng);
@@ -453,9 +459,7 @@ pub mod tests {
NIMFS::<Projective, PoseidonTranscript<Projective>>::fold_witness(&[w1], &[w2], rho);
// check lcccs relation
folded
.check_relation(&pedersen_params, &ccs, &w_folded)
.unwrap();
folded.check_relation(&ccs, &w_folded).unwrap();
}
/// Perform multifolding of an LCCCS instance with a CCCS instance (as described in the paper)
@@ -474,9 +478,13 @@ pub mod tests {
let z_2 = get_test_z(4);
// Create the LCCCS instance out of z_1
let (running_instance, w1) = ccs.to_lcccs(&mut rng, &pedersen_params, &z_1).unwrap();
let (running_instance, w1) = ccs
.to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, &z_1)
.unwrap();
// Create the CCCS instance out of z_2
let (new_instance, w2) = ccs.to_cccs(&mut rng, &pedersen_params, &z_2).unwrap();
let (new_instance, w2) = ccs
.to_cccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, &z_2)
.unwrap();
// Prover's transcript
let poseidon_config = poseidon_canonical_config::<Fr>();
@@ -513,9 +521,7 @@ pub mod tests {
assert_eq!(folded_lcccs, folded_lcccs_v);
// Check that the folded LCCCS instance is a valid instance with respect to the folded witness
folded_lcccs
.check_relation(&pedersen_params, &ccs, &folded_witness)
.unwrap();
folded_lcccs.check_relation(&ccs, &folded_witness).unwrap();
}
/// Perform multiple steps of multifolding of an LCCCS instance with a CCCS instance
@@ -530,8 +536,9 @@ pub mod tests {
// LCCCS witness
let z_1 = get_test_z(2);
let (mut running_instance, mut w1) =
ccs.to_lcccs(&mut rng, &pedersen_params, &z_1).unwrap();
let (mut running_instance, mut w1) = ccs
.to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, &z_1)
.unwrap();
let poseidon_config = poseidon_canonical_config::<Fr>();
@@ -548,7 +555,9 @@ pub mod tests {
// CCS witness
let z_2 = get_test_z(i);
let (new_instance, w2) = ccs.to_cccs(&mut rng, &pedersen_params, &z_2).unwrap();
let (new_instance, w2) = ccs
.to_cccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, &z_2)
.unwrap();
// run the prover side of the multifolding
let (proof, folded_lcccs, folded_witness, _) =
@@ -574,9 +583,7 @@ pub mod tests {
assert_eq!(folded_lcccs, folded_lcccs_v);
// check that the folded instance with the folded witness holds the LCCCS relation
folded_lcccs
.check_relation(&pedersen_params, &ccs, &folded_witness)
.unwrap();
folded_lcccs.check_relation(&ccs, &folded_witness).unwrap();
running_instance = folded_lcccs;
w1 = folded_witness;
@@ -612,7 +619,9 @@ pub mod tests {
let mut lcccs_instances = Vec::new();
let mut w_lcccs = Vec::new();
for z_i in z_lcccs.iter() {
let (running_instance, w) = ccs.to_lcccs(&mut rng, &pedersen_params, z_i).unwrap();
let (running_instance, w) = ccs
.to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, z_i)
.unwrap();
lcccs_instances.push(running_instance);
w_lcccs.push(w);
}
@@ -620,7 +629,9 @@ pub mod tests {
let mut cccs_instances = Vec::new();
let mut w_cccs = Vec::new();
for z_i in z_cccs.iter() {
let (new_instance, w) = ccs.to_cccs(&mut rng, &pedersen_params, z_i).unwrap();
let (new_instance, w) = ccs
.to_cccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, z_i)
.unwrap();
cccs_instances.push(new_instance);
w_cccs.push(w);
}
@@ -660,9 +671,7 @@ pub mod tests {
assert_eq!(folded_lcccs, folded_lcccs_v);
// Check that the folded LCCCS instance is a valid instance with respect to the folded witness
folded_lcccs
.check_relation(&pedersen_params, &ccs, &folded_witness)
.unwrap();
folded_lcccs.check_relation(&ccs, &folded_witness).unwrap();
}
/// Test that generates mu>1 and nu>1 instances, and folds them in a single multifolding step
@@ -710,7 +719,9 @@ pub mod tests {
let mut lcccs_instances = Vec::new();
let mut w_lcccs = Vec::new();
for z_i in z_lcccs.iter() {
let (running_instance, w) = ccs.to_lcccs(&mut rng, &pedersen_params, z_i).unwrap();
let (running_instance, w) = ccs
.to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, z_i)
.unwrap();
lcccs_instances.push(running_instance);
w_lcccs.push(w);
}
@@ -718,7 +729,9 @@ pub mod tests {
let mut cccs_instances = Vec::new();
let mut w_cccs = Vec::new();
for z_i in z_cccs.iter() {
let (new_instance, w) = ccs.to_cccs(&mut rng, &pedersen_params, z_i).unwrap();
let (new_instance, w) = ccs
.to_cccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, z_i)
.unwrap();
cccs_instances.push(new_instance);
w_cccs.push(w);
}
@@ -748,9 +761,7 @@ pub mod tests {
assert_eq!(folded_lcccs, folded_lcccs_v);
// Check that the folded LCCCS instance is a valid instance with respect to the folded witness
folded_lcccs
.check_relation(&pedersen_params, &ccs, &folded_witness)
.unwrap();
folded_lcccs.check_relation(&ccs, &folded_witness).unwrap();
}
}
}

View File

@@ -239,7 +239,9 @@ pub mod tests {
// Initialize a multifolding object
let (pedersen_params, _) =
Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap();
let (lcccs_instance, _) = ccs.to_lcccs(&mut rng, &pedersen_params, &z1).unwrap();
let (lcccs_instance, _) = ccs
.to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, &z1)
.unwrap();
let sigmas_thetas =
compute_sigmas_thetas(&ccs, &[z1.clone()], &[z2.clone()], &r_x_prime).unwrap();
@@ -287,7 +289,9 @@ pub mod tests {
// Initialize a multifolding object
let (pedersen_params, _) =
Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap();
let (lcccs_instance, _) = ccs.to_lcccs(&mut rng, &pedersen_params, &z1).unwrap();
let (lcccs_instance, _) = ccs
.to_lcccs::<_, _, Pedersen<Projective>>(&mut rng, &pedersen_params, &z1)
.unwrap();
// Compute g(x) with that r_x
let g = compute_g::<Projective>(