mirror of
https://github.com/arnaucube/sonobe.git
synced 2026-01-09 07:21:28 +01:00
Optimize CycleFold circuit MSM approach (#143)
In CycleFold we want to compute
$P_{folded} = P_0 + r ⋅ P_1 + r^2 ⋅ P_2 + r^3 ⋅ P_3 + ... + r^{n-2} ⋅ P_{n-2} + r^{n-1} ⋅ P_{n-1}$,
since the scalars follow the pattern r^i Youssef El Housni (@yelhousni)
proposed to update the approach of the CycleFold circuit to reduce the
number of constraints needed, by computing
$P_{folded} = (((P_{n-1} ⋅ r + P_{n-2}) ⋅ r + P_{n-3})... ) ⋅ r + P_0$.
By itself, this update reduces the number of constraints as the number
of points being folded in the CycleFold circuit grows. But it also has
impact at the HyperNova circuit, where it removes the need of using the
bit representations of the powers of the random value, substancially
reducing the amount of constraints used by the HyperNova
AugmentedFCircuit.
The number of constraints difference in the CycleFold circuit and in
the HyperNova's AugmentedFCircuit:
- CycleFold circuit:
| num points* | old | new | diff |
|-------------|-----------|-----------|----------|
| 2 | 1_354 | 1_354 | 0 |
| 3 | 2_683 | 2_554 | -129 |
| 4 | 4_012 | 3_754 | -258 |
| 8 | 9_328 | 8_554 | -744 |
| 16 | 19_960 | 18_154 | -1_806 |
| 32 | 41_224 | 37_354 | -3_870 |
| 64 | 83_752 | 75_754 | -7_998 |
| 128 | 168_808 | 152_554 | -16_254 |
| 1024 | 1_359_592 | 1_227_754 | -131_838 |
*num points: number of points being folded by the CycleFold circuit.
- HyperNova AugmentedFCircuit circuit
| folded instances* | old | new | diff |
|-------------------|---------|---------|----------|
| 5 | 90_285 | 80_150 | -10_135 |
| 10 | 144_894 | 117_655 | -27_239 |
| 20 | 249_839 | 192_949 | -56_890 |
| 40 | 463_078 | 344_448 | -118_630 |
*folded instances: folded instances per step, half of them being LCCCS
and the other half CCCS.
Co-authored-by: Youssef El Housni <youssef.housni21@gmail.com>
This commit is contained in:
@@ -376,14 +376,12 @@ pub trait CycleFoldConfig {
|
|||||||
|
|
||||||
/// Public inputs length for the CycleFoldCircuit.
|
/// Public inputs length for the CycleFoldCircuit.
|
||||||
/// * For Nova this is: `|[r, p1.x,y, p2.x,y, p3.x,y]|`
|
/// * 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]|`.
|
/// * In general, `|[r, (p_i.x,y)*n_points, p_folded.x,y]|`.
|
||||||
///
|
///
|
||||||
/// Thus, `IO_LEN` is:
|
/// Thus, `IO_LEN` is:
|
||||||
/// `RANDOMNESS_BIT_LENGTH / FIELD_CAPACITY * (N_INPUT_POINTS - 1) + 2 * N_INPUT_POINTS + 2`
|
/// `RANDOMNESS_BIT_LENGTH / FIELD_CAPACITY + 2 * N_INPUT_POINTS + 2`
|
||||||
const IO_LEN: usize = {
|
const IO_LEN: usize = {
|
||||||
Self::RANDOMNESS_BIT_LENGTH.div_ceil(Self::FIELD_CAPACITY) * (Self::N_INPUT_POINTS - 1)
|
Self::RANDOMNESS_BIT_LENGTH.div_ceil(Self::FIELD_CAPACITY) + 2 * Self::N_INPUT_POINTS + 2
|
||||||
+ 2 * Self::N_INPUT_POINTS
|
|
||||||
+ 2
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type F: Field;
|
type F: Field;
|
||||||
@@ -396,10 +394,9 @@ pub trait CycleFoldConfig {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct CycleFoldCircuit<CFG: CycleFoldConfig, GC: CurveVar<CFG::C, CFG::F>> {
|
pub struct CycleFoldCircuit<CFG: CycleFoldConfig, GC: CurveVar<CFG::C, CFG::F>> {
|
||||||
pub _gc: PhantomData<GC>,
|
pub _gc: PhantomData<GC>,
|
||||||
/// r_bits is a vector containing the r_bits, one for each point except for the first one. They
|
/// r_bits is the bit representation of the r whose powers are used in the
|
||||||
/// are used for the scalar multiplication of the points. The r_bits are the bit
|
/// random-linear-combination inside the CycleFoldCircuit
|
||||||
/// representation of each power of r (in Fr, while the CycleFoldCircuit is in Fq).
|
pub r_bits: Option<Vec<bool>>,
|
||||||
pub r_bits: Option<Vec<Vec<bool>>>,
|
|
||||||
/// points to be folded in the CycleFoldCircuit
|
/// points to be folded in the CycleFoldCircuit
|
||||||
pub points: Option<Vec<CFG::C>>,
|
pub points: Option<Vec<CFG::C>>,
|
||||||
/// public inputs (cf_u_{i+1}.x)
|
/// public inputs (cf_u_{i+1}.x)
|
||||||
@@ -426,18 +423,11 @@ where
|
|||||||
for<'a> &'a GC: GroupOpsBounds<'a, CFG::C, GC>,
|
for<'a> &'a GC: GroupOpsBounds<'a, CFG::C, GC>,
|
||||||
{
|
{
|
||||||
fn generate_constraints(self, cs: ConstraintSystemRef<CFG::F>) -> Result<(), SynthesisError> {
|
fn generate_constraints(self, cs: ConstraintSystemRef<CFG::F>) -> Result<(), SynthesisError> {
|
||||||
let r_bits: Vec<Vec<Boolean<CFG::F>>> = self
|
let r_bits = Vec::<Boolean<CFG::F>>::new_witness(cs.clone(), || {
|
||||||
|
Ok(self
|
||||||
.r_bits
|
.r_bits
|
||||||
// n_points-1, bcs is one for each point except for the first one
|
.unwrap_or(vec![false; CFG::RANDOMNESS_BIT_LENGTH]))
|
||||||
.unwrap_or(vec![
|
})?;
|
||||||
vec![false; CFG::RANDOMNESS_BIT_LENGTH];
|
|
||||||
CFG::N_INPUT_POINTS - 1
|
|
||||||
])
|
|
||||||
.iter()
|
|
||||||
.map(|r_bits_i| {
|
|
||||||
Vec::<Boolean<CFG::F>>::new_witness(cs.clone(), || Ok(r_bits_i.clone()))
|
|
||||||
})
|
|
||||||
.collect::<Result<_, _>>()?;
|
|
||||||
let points = Vec::<GC>::new_witness(cs.clone(), || {
|
let points = Vec::<GC>::new_witness(cs.clone(), || {
|
||||||
Ok(self
|
Ok(self
|
||||||
.points
|
.points
|
||||||
@@ -447,10 +437,7 @@ where
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
{
|
{
|
||||||
assert_eq!(CFG::N_INPUT_POINTS, points.len());
|
assert_eq!(CFG::N_INPUT_POINTS, points.len());
|
||||||
assert_eq!(CFG::N_INPUT_POINTS - 1, r_bits.len());
|
assert_eq!(CFG::RANDOMNESS_BIT_LENGTH, 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.
|
// Fold the original points of the instances natively in CycleFold.
|
||||||
@@ -458,11 +445,13 @@ where
|
|||||||
// - for the cmW we're computing: U_i1.cmW = U_i.cmW + r * u_i.cmW
|
// - for the cmW we're computing: U_i1.cmW = U_i.cmW + r * u_i.cmW
|
||||||
// - for the cmE we're computing: U_i1.cmE = U_i.cmE + r * cmT + r^2 * u_i.cmE, where u_i.cmE
|
// - for the cmE we're computing: U_i1.cmE = U_i.cmE + r * cmT + r^2 * u_i.cmE, where u_i.cmE
|
||||||
// is assumed to be 0, so, U_i1.cmE = U_i.cmE + r * cmT
|
// is assumed to be 0, so, U_i1.cmE = U_i.cmE + r * cmT
|
||||||
let mut p_folded: GC = points[0].clone();
|
// We want to compute
|
||||||
// iter over n_points-1 because the first point is not multiplied by r^i (it is multiplied
|
// P_folded = p_0 + r * P_1 + r^2 * P_2 + r^3 * P_3 + ... + r^{n-2} * P_{n-2} + r^{n-1} * P_{n-1}
|
||||||
// by r^0=1)
|
// so in order to do it more efficiently (less constraints) we do
|
||||||
for i in 0..CFG::N_INPUT_POINTS - 1 {
|
// P_folded = (((P_{n-1} * r + P_{n-2}) * r + P_{n-3})... ) * r + P_0
|
||||||
p_folded += points[i + 1].scalar_mul_le(r_bits[i].iter())?;
|
let mut p_folded: GC = points[CFG::N_INPUT_POINTS - 1].clone();
|
||||||
|
for i in (0..CFG::N_INPUT_POINTS - 1).rev() {
|
||||||
|
p_folded = p_folded.scalar_mul_le(r_bits.iter())? + points[i].clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
let x = Vec::<FpVar<CFG::F>>::new_input(cs.clone(), || {
|
let x = Vec::<FpVar<CFG::F>>::new_input(cs.clone(), || {
|
||||||
@@ -474,23 +463,22 @@ where
|
|||||||
// Check that the points coordinates are placed as the public input x:
|
// 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 Nova, this is: x == [r, p1, p2, p3] (wheere p3 is the p_folded).
|
||||||
// In multifolding schemes such as HyperNova, this is:
|
// 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],
|
// computed_x = [r, p_0, p_1, p_2, ..., p_n, p_folded],
|
||||||
// where each p_i is in fact p_i.to_constraint_field()
|
// where each p_i is in fact p_i.to_constraint_field()
|
||||||
let computed_x: Vec<FpVar<CFG::F>> = r_bits
|
let r_fp = Boolean::le_bits_to_fp_var(&r_bits)?;
|
||||||
|
let points_aux: Vec<FpVar<CFG::F>> = points
|
||||||
.iter()
|
.iter()
|
||||||
.map(|r_bits_i| {
|
.map(|p_i| Ok(p_i.to_constraint_field()?[..2].to_vec()))
|
||||||
r_bits_i
|
.collect::<Result<Vec<_>, SynthesisError>>()?
|
||||||
.chunks(CFG::FIELD_CAPACITY)
|
.into_iter()
|
||||||
.map(Boolean::le_bits_to_fp_var)
|
.flatten()
|
||||||
.collect::<Result<Vec<_>, _>>()
|
.collect();
|
||||||
})
|
|
||||||
.chain(
|
let computed_x: Vec<FpVar<CFG::F>> = [
|
||||||
points
|
vec![r_fp],
|
||||||
.iter()
|
points_aux,
|
||||||
.chain(&[p_folded])
|
p_folded.to_constraint_field()?[..2].to_vec(),
|
||||||
.map(|p_i| Ok(p_i.to_constraint_field()?[..2].to_vec())),
|
]
|
||||||
)
|
|
||||||
.collect::<Result<Vec<_>, _>>()?
|
|
||||||
.concat();
|
.concat();
|
||||||
computed_x.enforce_equal(&x)?;
|
computed_x.enforce_equal(&x)?;
|
||||||
|
|
||||||
@@ -596,13 +584,13 @@ pub mod tests {
|
|||||||
use crate::transcript::poseidon::poseidon_canonical_config;
|
use crate::transcript::poseidon::poseidon_canonical_config;
|
||||||
use crate::utils::get_cm_coordinates;
|
use crate::utils::get_cm_coordinates;
|
||||||
|
|
||||||
struct TestCycleFoldConfig<C: CurveGroup> {
|
struct TestCycleFoldConfig<C: CurveGroup, const N: usize> {
|
||||||
_c: PhantomData<C>,
|
_c: PhantomData<C>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: CurveGroup> CycleFoldConfig for TestCycleFoldConfig<C> {
|
impl<C: CurveGroup, const N: usize> CycleFoldConfig for TestCycleFoldConfig<C, N> {
|
||||||
const RANDOMNESS_BIT_LENGTH: usize = NOVA_N_BITS_RO;
|
const RANDOMNESS_BIT_LENGTH: usize = NOVA_N_BITS_RO;
|
||||||
const N_INPUT_POINTS: usize = 2;
|
const N_INPUT_POINTS: usize = N;
|
||||||
type C = C;
|
type C = C;
|
||||||
type F = C::BaseField;
|
type F = C::BaseField;
|
||||||
}
|
}
|
||||||
@@ -630,46 +618,45 @@ pub mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_CycleFoldCircuit_constraints() {
|
fn test_CycleFoldCircuit_n_points_constraints() {
|
||||||
let (_, _, _, _, ci1, _, ci2, _, ci3, _, cmT, r_bits, _) = prepare_simple_fold_inputs();
|
const n: usize = 16;
|
||||||
let r_Fq = Fq::from_bigint(BigInteger::from_bits_le(&r_bits)).unwrap();
|
let mut rng = ark_std::test_rng();
|
||||||
|
|
||||||
|
// points to random-linear-combine
|
||||||
|
let points: Vec<Projective> = std::iter::repeat_with(|| Projective::rand(&mut rng))
|
||||||
|
.take(n)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
use std::ops::Mul;
|
||||||
|
let rho_raw = Fq::rand(&mut rng);
|
||||||
|
let rho_bits = rho_raw.into_bigint().to_bits_le()[..NOVA_N_BITS_RO].to_vec();
|
||||||
|
let rho_Fq = Fq::from_bigint(BigInteger::from_bits_le(&rho_bits)).unwrap();
|
||||||
|
let rho_Fr = Fr::from_bigint(BigInteger::from_bits_le(&rho_bits)).unwrap();
|
||||||
|
let mut res = Projective::zero();
|
||||||
|
use ark_std::One;
|
||||||
|
let mut rho_i = Fr::one();
|
||||||
|
for point_i in points.iter() {
|
||||||
|
res += point_i.mul(rho_i);
|
||||||
|
rho_i *= rho_Fr;
|
||||||
|
}
|
||||||
|
|
||||||
// cs is the Constraint System on the Curve Cycle auxiliary curve constraints field
|
// cs is the Constraint System on the Curve Cycle auxiliary curve constraints field
|
||||||
// (E1::Fq=E2::Fr)
|
// (E1::Fq=E2::Fr)
|
||||||
let cs = ConstraintSystem::<Fq>::new_ref();
|
let cs = ConstraintSystem::<Fq>::new_ref();
|
||||||
|
|
||||||
let cfW_u_i_x: Vec<Fq> = [
|
let x: Vec<Fq> = [
|
||||||
vec![r_Fq],
|
vec![rho_Fq],
|
||||||
get_cm_coordinates(&ci1.cmW),
|
points.iter().flat_map(get_cm_coordinates).collect(),
|
||||||
get_cm_coordinates(&ci2.cmW),
|
get_cm_coordinates(&res),
|
||||||
get_cm_coordinates(&ci3.cmW),
|
|
||||||
]
|
]
|
||||||
.concat();
|
.concat();
|
||||||
let cfW_circuit = CycleFoldCircuit::<TestCycleFoldConfig<Projective>, GVar> {
|
let cf_circuit = CycleFoldCircuit::<TestCycleFoldConfig<Projective, n>, GVar> {
|
||||||
_gc: PhantomData,
|
_gc: PhantomData,
|
||||||
r_bits: Some(vec![r_bits.clone()]),
|
r_bits: Some(rho_bits),
|
||||||
points: Some(vec![ci1.clone().cmW, ci2.clone().cmW]),
|
points: Some(points),
|
||||||
x: Some(cfW_u_i_x.clone()),
|
x: Some(x.clone()),
|
||||||
};
|
};
|
||||||
cfW_circuit.generate_constraints(cs.clone()).unwrap();
|
cf_circuit.generate_constraints(cs.clone()).unwrap();
|
||||||
assert!(cs.is_satisfied().unwrap());
|
|
||||||
|
|
||||||
// same for E:
|
|
||||||
let cs = ConstraintSystem::<Fq>::new_ref();
|
|
||||||
let cfE_u_i_x = [
|
|
||||||
vec![r_Fq],
|
|
||||||
get_cm_coordinates(&ci1.cmE),
|
|
||||||
get_cm_coordinates(&cmT),
|
|
||||||
get_cm_coordinates(&ci3.cmE),
|
|
||||||
]
|
|
||||||
.concat();
|
|
||||||
let cfE_circuit = CycleFoldCircuit::<TestCycleFoldConfig<Projective>, GVar> {
|
|
||||||
_gc: PhantomData,
|
|
||||||
r_bits: Some(vec![r_bits.clone()]),
|
|
||||||
points: Some(vec![ci1.clone().cmE, cmT]),
|
|
||||||
x: Some(cfE_u_i_x.clone()),
|
|
||||||
};
|
|
||||||
cfE_circuit.generate_constraints(cs.clone()).unwrap();
|
|
||||||
assert!(cs.is_satisfied().unwrap());
|
assert!(cs.is_satisfied().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -714,7 +701,7 @@ pub mod tests {
|
|||||||
u: Fr::zero(),
|
u: Fr::zero(),
|
||||||
cmW: Projective::rand(&mut rng),
|
cmW: Projective::rand(&mut rng),
|
||||||
x: std::iter::repeat_with(|| Fr::rand(&mut rng))
|
x: std::iter::repeat_with(|| Fr::rand(&mut rng))
|
||||||
.take(TestCycleFoldConfig::<Projective>::IO_LEN)
|
.take(TestCycleFoldConfig::<Projective, 2>::IO_LEN)
|
||||||
.collect(),
|
.collect(),
|
||||||
};
|
};
|
||||||
let U_i = CycleFoldCommittedInstance::<Projective> {
|
let U_i = CycleFoldCommittedInstance::<Projective> {
|
||||||
@@ -722,7 +709,7 @@ pub mod tests {
|
|||||||
u: Fr::rand(&mut rng),
|
u: Fr::rand(&mut rng),
|
||||||
cmW: Projective::rand(&mut rng),
|
cmW: Projective::rand(&mut rng),
|
||||||
x: std::iter::repeat_with(|| Fr::rand(&mut rng))
|
x: std::iter::repeat_with(|| Fr::rand(&mut rng))
|
||||||
.take(TestCycleFoldConfig::<Projective>::IO_LEN)
|
.take(TestCycleFoldConfig::<Projective, 2>::IO_LEN)
|
||||||
.collect(),
|
.collect(),
|
||||||
};
|
};
|
||||||
let cmT = Projective::rand(&mut rng);
|
let cmT = Projective::rand(&mut rng);
|
||||||
@@ -781,7 +768,7 @@ pub mod tests {
|
|||||||
u: Fr::rand(&mut rng),
|
u: Fr::rand(&mut rng),
|
||||||
cmW: Projective::rand(&mut rng),
|
cmW: Projective::rand(&mut rng),
|
||||||
x: std::iter::repeat_with(|| Fr::rand(&mut rng))
|
x: std::iter::repeat_with(|| Fr::rand(&mut rng))
|
||||||
.take(TestCycleFoldConfig::<Projective>::IO_LEN)
|
.take(TestCycleFoldConfig::<Projective, 2>::IO_LEN)
|
||||||
.collect(),
|
.collect(),
|
||||||
};
|
};
|
||||||
let pp_hash = Fq::from(42u32); // only for test
|
let pp_hash = Fq::from(42u32); // only for test
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use ark_r1cs_std::{
|
|||||||
fields::{fp::FpVar, FieldVar},
|
fields::{fp::FpVar, FieldVar},
|
||||||
groups::GroupOpsBounds,
|
groups::GroupOpsBounds,
|
||||||
prelude::CurveVar,
|
prelude::CurveVar,
|
||||||
R1CSVar, ToBitsGadget, ToConstraintFieldGadget,
|
R1CSVar, ToConstraintFieldGadget,
|
||||||
};
|
};
|
||||||
use ark_relations::r1cs::{
|
use ark_relations::r1cs::{
|
||||||
ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, Namespace, SynthesisError,
|
ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, Namespace, SynthesisError,
|
||||||
@@ -234,7 +234,7 @@ where
|
|||||||
new_instances: &[CCCSVar<C>], // u
|
new_instances: &[CCCSVar<C>], // u
|
||||||
proof: ProofVar<C>,
|
proof: ProofVar<C>,
|
||||||
enabled: Boolean<C::ScalarField>,
|
enabled: Boolean<C::ScalarField>,
|
||||||
) -> Result<(LCCCSVar<C>, Vec<Vec<Boolean<CF1<C>>>>), SynthesisError> {
|
) -> Result<(LCCCSVar<C>, Vec<Boolean<CF1<C>>>), SynthesisError> {
|
||||||
// absorb instances to transcript
|
// absorb instances to transcript
|
||||||
for U_i in running_instances {
|
for U_i in running_instances {
|
||||||
let v = [
|
let v = [
|
||||||
@@ -317,15 +317,16 @@ where
|
|||||||
let rho_bits: Vec<Boolean<CF1<C>>> = transcript.get_challenge_nbits(NOVA_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)?;
|
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
|
// Self::fold will return the folded instance
|
||||||
// they can be used in other parts of the AugmentedFCircuit
|
let folded_lcccs = Self::fold(
|
||||||
Self::fold(
|
|
||||||
running_instances,
|
running_instances,
|
||||||
new_instances,
|
new_instances,
|
||||||
proof.sigmas_thetas,
|
proof.sigmas_thetas,
|
||||||
r_x_prime,
|
r_x_prime,
|
||||||
rho,
|
rho,
|
||||||
)
|
)?;
|
||||||
|
// return the rho_bits so it can be used in other parts of the AugmentedFCircuit
|
||||||
|
Ok((folded_lcccs, rho_bits))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs (in-circuit) the verifier side of the fold, computing the new folded LCCCS instance
|
/// Runs (in-circuit) the verifier side of the fold, computing the new folded LCCCS instance
|
||||||
@@ -336,14 +337,12 @@ where
|
|||||||
sigmas_thetas: (Vec<Vec<FpVar<CF1<C>>>>, Vec<Vec<FpVar<CF1<C>>>>),
|
sigmas_thetas: (Vec<Vec<FpVar<CF1<C>>>>, Vec<Vec<FpVar<CF1<C>>>>),
|
||||||
r_x_prime: Vec<FpVar<CF1<C>>>,
|
r_x_prime: Vec<FpVar<CF1<C>>>,
|
||||||
rho: FpVar<CF1<C>>,
|
rho: FpVar<CF1<C>>,
|
||||||
) -> Result<(LCCCSVar<C>, Vec<Vec<Boolean<CF1<C>>>>), SynthesisError> {
|
) -> Result<LCCCSVar<C>, SynthesisError> {
|
||||||
let (sigmas, thetas) = (sigmas_thetas.0.clone(), sigmas_thetas.1.clone());
|
let (sigmas, thetas) = (sigmas_thetas.0.clone(), sigmas_thetas.1.clone());
|
||||||
let mut u_folded: FpVar<CF1<C>> = FpVar::zero();
|
let mut u_folded: FpVar<CF1<C>> = FpVar::zero();
|
||||||
let mut x_folded: Vec<FpVar<CF1<C>>> = vec![FpVar::zero(); lcccs[0].x.len()];
|
let mut x_folded: Vec<FpVar<CF1<C>>> = vec![FpVar::zero(); lcccs[0].x.len()];
|
||||||
let mut v_folded: Vec<FpVar<CF1<C>>> = vec![FpVar::zero(); sigmas[0].len()];
|
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; NOVA_N_BITS_RO]; lcccs.len() + cccs.len() - 1];
|
|
||||||
let mut rho_i = FpVar::one();
|
let mut rho_i = FpVar::one();
|
||||||
for i in 0..(lcccs.len() + cccs.len()) {
|
for i in 0..(lcccs.len() + cccs.len()) {
|
||||||
let u: FpVar<CF1<C>>;
|
let u: FpVar<CF1<C>>;
|
||||||
@@ -382,28 +381,18 @@ where
|
|||||||
|
|
||||||
// compute the next power of rho
|
// compute the next power of rho
|
||||||
rho_i *= rho.clone();
|
rho_i *= rho.clone();
|
||||||
// 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[..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[..NOVA_N_BITS_RO].to_vec();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// return the folded instance, together with the rho's powers vector so they can be used in
|
// return the folded instance, together with the rho's powers vector so they can be used in
|
||||||
// other parts of the AugmentedFCircuit
|
// other parts of the AugmentedFCircuit
|
||||||
Ok((
|
Ok(LCCCSVar::<C> {
|
||||||
LCCCSVar::<C> {
|
|
||||||
// C this is later overwritten by the U_{i+1}.C value checked by the cyclefold circuit
|
// C this is later overwritten by the U_{i+1}.C value checked by the cyclefold circuit
|
||||||
C: lcccs[0].C.clone(),
|
C: lcccs[0].C.clone(),
|
||||||
u: u_folded,
|
u: u_folded,
|
||||||
x: x_folded,
|
x: x_folded,
|
||||||
r_x: r_x_prime,
|
r_x: r_x_prime,
|
||||||
v: v_folded,
|
v: v_folded,
|
||||||
},
|
})
|
||||||
rho_vec,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -734,7 +723,7 @@ where
|
|||||||
Ok(self.Us.unwrap_or(vec![U_dummy.clone(); MU - 1]))
|
Ok(self.Us.unwrap_or(vec![U_dummy.clone(); MU - 1]))
|
||||||
})?;
|
})?;
|
||||||
let us = Vec::<CCCSVar<C1>>::new_witness(cs.clone(), || {
|
let us = Vec::<CCCSVar<C1>>::new_witness(cs.clone(), || {
|
||||||
Ok(self.us.unwrap_or(vec![u_dummy.clone(); MU - 1]))
|
Ok(self.us.unwrap_or(vec![u_dummy.clone(); NU - 1]))
|
||||||
})?;
|
})?;
|
||||||
let U_i1_C = NonNativeAffineVar::new_witness(cs.clone(), || {
|
let U_i1_C = NonNativeAffineVar::new_witness(cs.clone(), || {
|
||||||
Ok(self.U_i1_C.unwrap_or_else(C1::zero))
|
Ok(self.U_i1_C.unwrap_or_else(C1::zero))
|
||||||
@@ -794,7 +783,7 @@ where
|
|||||||
// other curve.
|
// other curve.
|
||||||
let mut transcript = PoseidonSpongeVar::new(cs.clone(), &self.poseidon_config);
|
let mut transcript = PoseidonSpongeVar::new(cs.clone(), &self.poseidon_config);
|
||||||
transcript.absorb(&pp_hash)?;
|
transcript.absorb(&pp_hash)?;
|
||||||
let (mut U_i1, rho_vec) = NIMFSGadget::<C1>::verify(
|
let (mut U_i1, rho_bits) = NIMFSGadget::<C1>::verify(
|
||||||
cs.clone(),
|
cs.clone(),
|
||||||
&self.ccs.clone(),
|
&self.ccs.clone(),
|
||||||
&mut transcript,
|
&mut transcript,
|
||||||
@@ -824,19 +813,14 @@ where
|
|||||||
x.enforce_equal(&is_basecase.select(&u_i1_x_base, &u_i1_x)?)?;
|
x.enforce_equal(&is_basecase.select(&u_i1_x_base, &u_i1_x)?)?;
|
||||||
|
|
||||||
// convert rho_bits of the rho_vec to a `NonNativeFieldVar`
|
// convert rho_bits of the rho_vec to a `NonNativeFieldVar`
|
||||||
let rho_vec_nonnat = rho_vec
|
let mut rho_bits_resized = rho_bits.clone();
|
||||||
.iter()
|
rho_bits_resized.resize(C1::BaseField::MODULUS_BIT_SIZE as usize, Boolean::FALSE);
|
||||||
.map(|rho_i| {
|
let rho_nonnat = NonNativeUintVar::from(&rho_bits_resized);
|
||||||
let mut bits = rho_i.clone();
|
|
||||||
bits.resize(C1::BaseField::MODULUS_BIT_SIZE as usize, Boolean::FALSE);
|
|
||||||
NonNativeUintVar::from(&bits)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// CycleFold part
|
// CycleFold part
|
||||||
// C.1. Compute cf1_u_i.x and cf2_u_i.x
|
// C.1. Compute cf1_u_i.x and cf2_u_i.x
|
||||||
let cf_x: Vec<NonNativeUintVar<CF2<C2>>> = [
|
let cf_x: Vec<NonNativeUintVar<CF2<C2>>> = [
|
||||||
rho_vec_nonnat,
|
vec![rho_nonnat],
|
||||||
all_Us
|
all_Us
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|U| vec![U.C.x.clone(), U.C.y.clone()])
|
.flat_map(|U| vec![U.C.x.clone(), U.C.y.clone()])
|
||||||
@@ -1312,9 +1296,8 @@ mod tests {
|
|||||||
let mut transcript_p: PoseidonSponge<Fr> =
|
let mut transcript_p: PoseidonSponge<Fr> =
|
||||||
PoseidonSponge::<Fr>::new(&poseidon_config.clone());
|
PoseidonSponge::<Fr>::new(&poseidon_config.clone());
|
||||||
transcript_p.absorb(&pp_hash);
|
transcript_p.absorb(&pp_hash);
|
||||||
let (rho_powers, nimfs_proof);
|
let (rho, nimfs_proof);
|
||||||
(nimfs_proof, U_i1, W_i1, rho_powers) =
|
(nimfs_proof, U_i1, W_i1, rho) = NIMFS::<Projective, PoseidonSponge<Fr>>::prove(
|
||||||
NIMFS::<Projective, PoseidonSponge<Fr>>::prove(
|
|
||||||
&mut transcript_p,
|
&mut transcript_p,
|
||||||
&ccs,
|
&ccs,
|
||||||
&all_Us,
|
&all_Us,
|
||||||
@@ -1330,23 +1313,13 @@ mod tests {
|
|||||||
let u_i1_x =
|
let u_i1_x =
|
||||||
U_i1.hash(&sponge, pp_hash, iFr + Fr::one(), z_0.clone(), z_i1.clone());
|
U_i1.hash(&sponge, pp_hash, iFr + Fr::one(), z_0.clone(), z_i1.clone());
|
||||||
|
|
||||||
let rho_powers_Fq: Vec<Fq> = rho_powers
|
let rho_bits = rho.into_bigint().to_bits_le()[..NOVA_N_BITS_RO].to_vec();
|
||||||
.iter()
|
let rho_Fq = Fq::from_bigint(BigInteger::from_bits_le(&rho_bits)).unwrap();
|
||||||
.map(|rho_i| {
|
|
||||||
Fq::from_bigint(BigInteger::from_bits_le(&rho_i.into_bigint().to_bits_le()))
|
|
||||||
.unwrap()
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let rho_powers_bits: Vec<Vec<bool>> = rho_powers
|
|
||||||
.iter()
|
|
||||||
.map(|rho_i| rho_i.into_bigint().to_bits_le()[..NOVA_N_BITS_RO].to_vec())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// CycleFold part:
|
// CycleFold part:
|
||||||
// get the vector used as public inputs 'x' in the CycleFold circuit
|
// get the vector used as public inputs 'x' in the CycleFold circuit
|
||||||
let cf_u_i_x = [
|
let cf_u_i_x = [
|
||||||
// all values for multiple instances
|
vec![rho_Fq],
|
||||||
rho_powers_Fq,
|
|
||||||
get_cm_coordinates(&U_i.C),
|
get_cm_coordinates(&U_i.C),
|
||||||
Us.iter()
|
Us.iter()
|
||||||
.flat_map(|Us_i| get_cm_coordinates(&Us_i.C))
|
.flat_map(|Us_i| get_cm_coordinates(&Us_i.C))
|
||||||
@@ -1361,7 +1334,7 @@ mod tests {
|
|||||||
|
|
||||||
let cf_circuit = HyperNovaCycleFoldCircuit::<Projective, GVar, MU, NU> {
|
let cf_circuit = HyperNovaCycleFoldCircuit::<Projective, GVar, MU, NU> {
|
||||||
_gc: PhantomData,
|
_gc: PhantomData,
|
||||||
r_bits: Some(rho_powers_bits.clone()),
|
r_bits: Some(rho_bits.clone()),
|
||||||
points: Some(
|
points: Some(
|
||||||
[
|
[
|
||||||
vec![U_i.clone().C],
|
vec![U_i.clone().C],
|
||||||
@@ -1375,10 +1348,6 @@ mod tests {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// ensure that the CycleFoldCircuit is well defined
|
// ensure that the CycleFoldCircuit is well defined
|
||||||
assert_eq!(
|
|
||||||
cf_circuit.r_bits.clone().unwrap().len(),
|
|
||||||
HyperNovaCycleFoldConfig::<Projective, MU, NU>::N_INPUT_POINTS - 1
|
|
||||||
);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
cf_circuit.points.clone().unwrap().len(),
|
cf_circuit.points.clone().unwrap().len(),
|
||||||
HyperNovaCycleFoldConfig::<Projective, MU, NU>::N_INPUT_POINTS
|
HyperNovaCycleFoldConfig::<Projective, MU, NU>::N_INPUT_POINTS
|
||||||
|
|||||||
@@ -168,9 +168,8 @@ where
|
|||||||
pub U_i1: Option<LCCCS<C1>>,
|
pub U_i1: Option<LCCCS<C1>>,
|
||||||
pub W_i1: Option<Witness<C1::ScalarField>>,
|
pub W_i1: Option<Witness<C1::ScalarField>>,
|
||||||
pub nimfs_proof: Option<NIMFSProof<C1>>,
|
pub nimfs_proof: Option<NIMFSProof<C1>>,
|
||||||
// rho_0 is the first and only rho in the 'rho_powers' array, since it comes from NIMFS-folding
|
// rho is the 'random' value used for the fold of the last 2 instances
|
||||||
// only 2 instances.
|
pub rho: Option<C1::ScalarField>,
|
||||||
pub rho_0: Option<C1::ScalarField>,
|
|
||||||
/// CycleFold running instance
|
/// CycleFold running instance
|
||||||
pub cf_U_i: Option<CycleFoldCommittedInstance<C2>>,
|
pub cf_U_i: Option<CycleFoldCommittedInstance<C2>>,
|
||||||
pub cf_W_i: Option<CycleFoldWitness<C2>>,
|
pub cf_W_i: Option<CycleFoldWitness<C2>>,
|
||||||
@@ -199,8 +198,7 @@ where
|
|||||||
// compute the U_{i+1}, W_{i+1}, by folding the last running & incoming instances
|
// compute the U_{i+1}, W_{i+1}, by folding the last running & incoming instances
|
||||||
let mut transcript = PoseidonSponge::<C1::ScalarField>::new(&hn.poseidon_config);
|
let mut transcript = PoseidonSponge::<C1::ScalarField>::new(&hn.poseidon_config);
|
||||||
transcript.absorb(&hn.pp_hash);
|
transcript.absorb(&hn.pp_hash);
|
||||||
let (nimfs_proof, U_i1, W_i1, rho_powers) =
|
let (nimfs_proof, U_i1, W_i1, rho) = NIMFS::<C1, PoseidonSponge<C1::ScalarField>>::prove(
|
||||||
NIMFS::<C1, PoseidonSponge<C1::ScalarField>>::prove(
|
|
||||||
&mut transcript,
|
&mut transcript,
|
||||||
&hn.ccs,
|
&hn.ccs,
|
||||||
&[hn.U_i.clone()],
|
&[hn.U_i.clone()],
|
||||||
@@ -222,11 +220,6 @@ where
|
|||||||
let p_W = poly_from_vec(W.to_vec())?;
|
let p_W = poly_from_vec(W.to_vec())?;
|
||||||
let eval_W = p_W.evaluate(&kzg_challenge);
|
let eval_W = p_W.evaluate(&kzg_challenge);
|
||||||
|
|
||||||
// ensure that we only have 1 element in rho_powers, since we only NIMFS-fold 2 instances
|
|
||||||
if rho_powers.len() != 1 {
|
|
||||||
return Err(Error::NotExpectedLength(rho_powers.len(), 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
_c1: PhantomData,
|
_c1: PhantomData,
|
||||||
_gc1: PhantomData,
|
_gc1: PhantomData,
|
||||||
@@ -251,7 +244,7 @@ where
|
|||||||
U_i1: Some(U_i1),
|
U_i1: Some(U_i1),
|
||||||
W_i1: Some(W_i1),
|
W_i1: Some(W_i1),
|
||||||
nimfs_proof: Some(nimfs_proof),
|
nimfs_proof: Some(nimfs_proof),
|
||||||
rho_0: Some(rho_powers[0]),
|
rho: Some(rho),
|
||||||
cf_U_i: Some(hn.cf_U_i),
|
cf_U_i: Some(hn.cf_U_i),
|
||||||
cf_W_i: Some(hn.cf_W_i),
|
cf_W_i: Some(hn.cf_W_i),
|
||||||
kzg_challenge: Some(kzg_challenge),
|
kzg_challenge: Some(kzg_challenge),
|
||||||
@@ -428,7 +421,7 @@ where
|
|||||||
// The following steps are in non-increasing order because the `computed_U_i1` is computed
|
// The following steps are in non-increasing order because the `computed_U_i1` is computed
|
||||||
// at step 8, and later used at step 6. Notice that in Nova, we compute U_i1 outside of the
|
// at step 8, and later used at step 6. Notice that in Nova, we compute U_i1 outside of the
|
||||||
// circuit, in the smart contract, but here we're computing it in-circuit, and we reuse the
|
// circuit, in the smart contract, but here we're computing it in-circuit, and we reuse the
|
||||||
// `rho_vec` computed along the way of computing `computed_U_i1` for the later `rho_powers`
|
// `rho_bits` computed along the way of computing `computed_U_i1` for the later `rho_powers`
|
||||||
// check (6.b).
|
// check (6.b).
|
||||||
|
|
||||||
// Check 7 is temporary disabled due
|
// Check 7 is temporary disabled due
|
||||||
@@ -445,7 +438,7 @@ where
|
|||||||
// Notice that the NIMFSGadget performs all the logic except of checking the fold of the
|
// Notice that the NIMFSGadget performs all the logic except of checking the fold of the
|
||||||
// instances C parameter, which would require non-native arithmetic, henceforth we perform
|
// instances C parameter, which would require non-native arithmetic, henceforth we perform
|
||||||
// that check outside the circuit.
|
// that check outside the circuit.
|
||||||
let (computed_U_i1, rho_vec) = NIMFSGadget::<C1>::verify(
|
let (computed_U_i1, rho_bits) = NIMFSGadget::<C1>::verify(
|
||||||
cs.clone(),
|
cs.clone(),
|
||||||
&self.ccs.clone(),
|
&self.ccs.clone(),
|
||||||
&mut transcript,
|
&mut transcript,
|
||||||
@@ -471,20 +464,11 @@ where
|
|||||||
computed_U_i1.v.enforce_equal(&U_i1.v)?;
|
computed_U_i1.v.enforce_equal(&U_i1.v)?;
|
||||||
|
|
||||||
// 8.b check that the in-circuit computed r is equal to the inputted r.
|
// 8.b check that the in-circuit computed r is equal to the inputted r.
|
||||||
// Notice that rho_vec only contains one element, since at the final fold we are only
|
|
||||||
// folding 2-to-1 instances.
|
|
||||||
|
|
||||||
// Ensure that rho_vec is of length 1, note that this is not enforced at the constraint
|
let rho = Boolean::le_bits_to_fp_var(&rho_bits)?;
|
||||||
// level but more as a check for the prover.
|
let external_rho =
|
||||||
if rho_vec.len() != 1 {
|
FpVar::<CF1<C1>>::new_input(cs.clone(), || Ok(self.rho.unwrap_or(CF1::<C1>::zero())))?;
|
||||||
return Err(SynthesisError::Unsatisfiable);
|
rho.enforce_equal(&external_rho)?;
|
||||||
}
|
|
||||||
|
|
||||||
let rho_0 = Boolean::le_bits_to_fp_var(&rho_vec[0])?;
|
|
||||||
let external_rho_0 = FpVar::<CF1<C1>>::new_input(cs.clone(), || {
|
|
||||||
Ok(self.rho_0.unwrap_or(CF1::<C1>::zero()))
|
|
||||||
})?;
|
|
||||||
rho_0.enforce_equal(&external_rho_0)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -664,9 +664,8 @@ where
|
|||||||
let mut transcript_p: PoseidonSponge<C1::ScalarField> =
|
let mut transcript_p: PoseidonSponge<C1::ScalarField> =
|
||||||
PoseidonSponge::<C1::ScalarField>::new(&self.poseidon_config);
|
PoseidonSponge::<C1::ScalarField>::new(&self.poseidon_config);
|
||||||
transcript_p.absorb(&self.pp_hash);
|
transcript_p.absorb(&self.pp_hash);
|
||||||
let (rho_powers, nimfs_proof);
|
let (rho, nimfs_proof);
|
||||||
(nimfs_proof, U_i1, W_i1, rho_powers) =
|
(nimfs_proof, U_i1, W_i1, rho) = NIMFS::<C1, PoseidonSponge<C1::ScalarField>>::prove(
|
||||||
NIMFS::<C1, PoseidonSponge<C1::ScalarField>>::prove(
|
|
||||||
&mut transcript_p,
|
&mut transcript_p,
|
||||||
&self.ccs,
|
&self.ccs,
|
||||||
&[vec![self.U_i.clone()], Us.clone()].concat(),
|
&[vec![self.U_i.clone()], Us.clone()].concat(),
|
||||||
@@ -687,29 +686,18 @@ where
|
|||||||
z_i1.clone(),
|
z_i1.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let rho_powers_Fq: Vec<C1::BaseField> = rho_powers
|
let rho_bits = rho.into_bigint().to_bits_le()[..NOVA_N_BITS_RO].to_vec();
|
||||||
.iter()
|
let rho_Fq = C1::BaseField::from_bigint(BigInteger::from_bits_le(&rho_bits)).unwrap();
|
||||||
.map(|rho_i| {
|
|
||||||
C1::BaseField::from_bigint(BigInteger::from_bits_le(
|
|
||||||
&rho_i.into_bigint().to_bits_le(),
|
|
||||||
))
|
|
||||||
.unwrap()
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let rho_powers_bits: Vec<Vec<bool>> = rho_powers
|
|
||||||
.iter()
|
|
||||||
.map(|rho_i| rho_i.into_bigint().to_bits_le()[..NOVA_N_BITS_RO].to_vec())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// CycleFold part:
|
// CycleFold part:
|
||||||
// get the vector used as public inputs 'x' in the CycleFold circuit.
|
// get the vector used as public inputs 'x' in the CycleFold circuit.
|
||||||
// Place the random values and the points coordinates as the public input x:
|
// Place the random values and the points coordinates as the public input x:
|
||||||
// In Nova, this is: x == [r, p1, p2, p3].
|
// In Nova, this is: x == [r, p1, p2, p3].
|
||||||
// In multifolding schemes such as HyperNova, this is:
|
// 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],
|
// computed_x = [r, p_0, p_1, p_2, ..., p_n],
|
||||||
// where each p_i is in fact p_i.to_constraint_field()
|
// where each p_i is in fact p_i.to_constraint_field()
|
||||||
let cf_u_i_x = [
|
let cf_u_i_x = [
|
||||||
rho_powers_Fq,
|
vec![rho_Fq],
|
||||||
get_cm_coordinates(&self.U_i.C),
|
get_cm_coordinates(&self.U_i.C),
|
||||||
Us.iter()
|
Us.iter()
|
||||||
.flat_map(|Us_i| get_cm_coordinates(&Us_i.C))
|
.flat_map(|Us_i| get_cm_coordinates(&Us_i.C))
|
||||||
@@ -724,7 +712,7 @@ where
|
|||||||
|
|
||||||
let cf_circuit = HyperNovaCycleFoldCircuit::<C1, GC1, MU, NU> {
|
let cf_circuit = HyperNovaCycleFoldCircuit::<C1, GC1, MU, NU> {
|
||||||
_gc: PhantomData,
|
_gc: PhantomData,
|
||||||
r_bits: Some(rho_powers_bits.clone()),
|
r_bits: Some(rho_bits.clone()),
|
||||||
points: Some(
|
points: Some(
|
||||||
[
|
[
|
||||||
vec![self.U_i.clone().C],
|
vec![self.U_i.clone().C],
|
||||||
@@ -991,7 +979,6 @@ mod tests {
|
|||||||
cccs.push((u, w));
|
cccs.push((u, w));
|
||||||
}
|
}
|
||||||
|
|
||||||
dbg!(&hypernova.i);
|
|
||||||
hypernova
|
hypernova
|
||||||
.prove_step(&mut rng, vec![], Some((lcccs, cccs)))
|
.prove_step(&mut rng, vec![], Some((lcccs, cccs)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ where
|
|||||||
sigmas_thetas: &SigmasThetas<C::ScalarField>,
|
sigmas_thetas: &SigmasThetas<C::ScalarField>,
|
||||||
r_x_prime: Vec<C::ScalarField>,
|
r_x_prime: Vec<C::ScalarField>,
|
||||||
rho: C::ScalarField,
|
rho: C::ScalarField,
|
||||||
) -> (LCCCS<C>, Vec<C::ScalarField>) {
|
) -> LCCCS<C> {
|
||||||
let (sigmas, thetas) = (sigmas_thetas.0.clone(), sigmas_thetas.1.clone());
|
let (sigmas, thetas) = (sigmas_thetas.0.clone(), sigmas_thetas.1.clone());
|
||||||
let mut C_folded = C::zero();
|
let mut C_folded = C::zero();
|
||||||
let mut u_folded = C::ScalarField::zero();
|
let mut u_folded = C::ScalarField::zero();
|
||||||
@@ -81,7 +81,6 @@ where
|
|||||||
let mut v_folded: Vec<C::ScalarField> = vec![C::ScalarField::zero(); sigmas[0].len()];
|
let mut v_folded: Vec<C::ScalarField> = vec![C::ScalarField::zero(); sigmas[0].len()];
|
||||||
|
|
||||||
let mut rho_i = C::ScalarField::one();
|
let mut rho_i = C::ScalarField::one();
|
||||||
let mut rho_powers = vec![C::ScalarField::zero(); lcccs.len() + cccs.len() - 1];
|
|
||||||
for i in 0..(lcccs.len() + cccs.len()) {
|
for i in 0..(lcccs.len() + cccs.len()) {
|
||||||
let c: C;
|
let c: C;
|
||||||
let u: C::ScalarField;
|
let u: C::ScalarField;
|
||||||
@@ -123,28 +122,15 @@ where
|
|||||||
|
|
||||||
// compute the next power of rho
|
// compute the next power of rho
|
||||||
rho_i *= rho;
|
rho_i *= rho;
|
||||||
// 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[..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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(
|
|
||||||
LCCCS::<C> {
|
LCCCS::<C> {
|
||||||
C: C_folded,
|
C: C_folded,
|
||||||
u: u_folded,
|
u: u_folded,
|
||||||
x: x_folded,
|
x: x_folded,
|
||||||
r_x: r_x_prime,
|
r_x: r_x_prime,
|
||||||
v: v_folded,
|
v: v_folded,
|
||||||
},
|
}
|
||||||
rho_powers,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fold_witness(
|
pub fn fold_witness(
|
||||||
@@ -183,12 +169,6 @@ where
|
|||||||
|
|
||||||
// compute the next power of rho
|
// compute the next power of rho
|
||||||
rho_i *= rho;
|
rho_i *= rho;
|
||||||
// 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[..NOVA_N_BITS_RO],
|
|
||||||
))
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
Witness {
|
Witness {
|
||||||
w: w_folded,
|
w: w_folded,
|
||||||
@@ -213,8 +193,7 @@ where
|
|||||||
NIMFSProof<C>,
|
NIMFSProof<C>,
|
||||||
LCCCS<C>,
|
LCCCS<C>,
|
||||||
Witness<C::ScalarField>,
|
Witness<C::ScalarField>,
|
||||||
// Vec<bool>,
|
C::ScalarField, // rho
|
||||||
Vec<C::ScalarField>,
|
|
||||||
),
|
),
|
||||||
Error,
|
Error,
|
||||||
> {
|
> {
|
||||||
@@ -281,7 +260,7 @@ where
|
|||||||
C::ScalarField::from_bigint(BigInteger::from_bits_le(&rho_bits)).unwrap();
|
C::ScalarField::from_bigint(BigInteger::from_bits_le(&rho_bits)).unwrap();
|
||||||
|
|
||||||
// Step 7: Create the folded instance
|
// Step 7: Create the folded instance
|
||||||
let (folded_lcccs, rho_powers) = Self::fold(
|
let folded_lcccs = Self::fold(
|
||||||
running_instances,
|
running_instances,
|
||||||
new_instances,
|
new_instances,
|
||||||
&sigmas_thetas,
|
&sigmas_thetas,
|
||||||
@@ -299,7 +278,7 @@ where
|
|||||||
},
|
},
|
||||||
folded_lcccs,
|
folded_lcccs,
|
||||||
folded_witness,
|
folded_witness,
|
||||||
rho_powers,
|
rho,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,8 +385,7 @@ where
|
|||||||
&proof.sigmas_thetas,
|
&proof.sigmas_thetas,
|
||||||
r_x_prime,
|
r_x_prime,
|
||||||
rho,
|
rho,
|
||||||
)
|
))
|
||||||
.0)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,7 +435,7 @@ pub mod tests {
|
|||||||
let mut rng = test_rng();
|
let mut rng = test_rng();
|
||||||
let rho = Fr::rand(&mut rng);
|
let rho = Fr::rand(&mut rng);
|
||||||
|
|
||||||
let (folded, _) = NIMFS::<Projective, PoseidonSponge<Fr>>::fold(
|
let folded = NIMFS::<Projective, PoseidonSponge<Fr>>::fold(
|
||||||
&[lcccs],
|
&[lcccs],
|
||||||
&[cccs],
|
&[cccs],
|
||||||
&sigmas_thetas,
|
&sigmas_thetas,
|
||||||
|
|||||||
@@ -608,13 +608,13 @@ where
|
|||||||
|
|
||||||
let cfW_circuit = NovaCycleFoldCircuit::<C1, GC1> {
|
let cfW_circuit = NovaCycleFoldCircuit::<C1, GC1> {
|
||||||
_gc: PhantomData,
|
_gc: PhantomData,
|
||||||
r_bits: Some(vec![r_bits.clone()]),
|
r_bits: Some(r_bits.clone()),
|
||||||
points: Some(vec![self.U_i.clone().cmW, self.u_i.clone().cmW]),
|
points: Some(vec![self.U_i.clone().cmW, self.u_i.clone().cmW]),
|
||||||
x: Some(cfW_u_i_x.clone()),
|
x: Some(cfW_u_i_x.clone()),
|
||||||
};
|
};
|
||||||
let cfE_circuit = NovaCycleFoldCircuit::<C1, GC1> {
|
let cfE_circuit = NovaCycleFoldCircuit::<C1, GC1> {
|
||||||
_gc: PhantomData,
|
_gc: PhantomData,
|
||||||
r_bits: Some(vec![r_bits.clone()]),
|
r_bits: Some(r_bits.clone()),
|
||||||
points: Some(vec![self.U_i.clone().cmE, cmT]),
|
points: Some(vec![self.U_i.clone().cmE, cmT]),
|
||||||
x: Some(cfE_u_i_x.clone()),
|
x: Some(cfE_u_i_x.clone()),
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user