mirror of
https://github.com/arnaucube/sonobe.git
synced 2026-01-07 14:31:31 +01:00
Add the digest of the Relaxed R1CS instance for CycleFold as a public input to AugmentedFCircuit (#84)
* Treat (the digest of) `cf_U_i1` as an additional public input to `AugmentedFCircuit` for full soundness * Fix the y-coordinate in the affine form of zero points This in turn fixes the inconsistency between the digest of a constant affine point and that of a witness affine point in circuits. * Set `cf_u_i1_x` to the correct value * Fix the number of public inputs in dummy instance and witness * Unify the logic behind `CycleFoldCommittedInstanceVar::hash` and `CycleFoldChallengeGadget::get_challenge_gadget` * Add `ToConstraintFieldGadget` bound to `GC2` * Remove unnecessary code used for debugging * Make clippy and rustfmt happy * Move conversion methods for `NonNativeFieldVar` to `folding/circuits/nonnative.rs` * Simplify the check of zero coordinates * Gracefully handle the result of `nonnative_field_var_to_constraint_field` * Make clippy happy again
This commit is contained in:
10
Cargo.toml
10
Cargo.toml
@@ -8,11 +8,13 @@ resolver = "2"
|
||||
|
||||
[patch.crates-io]
|
||||
# The following patch is to use a version of ark-r1cs-std compatible with
|
||||
# v0.4.0 but that includes a cherry-picked commit from after v0.4.0 which fixes
|
||||
# the in-circuit scalar multiplication of the zero point. The commit is from
|
||||
# https://github.com/arkworks-rs/r1cs-std/pull/124, without including other
|
||||
# v0.4.0 but that includes two cherry-picked commits from after v0.4.0 which
|
||||
# fixes the in-circuit scalar multiplication of the zero point and the
|
||||
# y-coordinate of the zero point. The commits are respectively from
|
||||
# https://github.com/arkworks-rs/r1cs-std/pull/124 and
|
||||
# https://github.com/arkworks-rs/r1cs-std/pull/126, without including other
|
||||
# changes done between v0.4.0 and this fix which would break compatibility.
|
||||
ark-r1cs-std = { git = "https://github.com/arnaucube/ark-r1cs-std-cherry-picked/" }
|
||||
ark-r1cs-std = { git = "https://github.com/winderica/r1cs-std", branch="cherry-pick" }
|
||||
# patch ark_curves to use a cherry-picked version which contains
|
||||
# bn254::constraints & grumpkin for v0.4.0 (once arkworks v0.5.0 is released
|
||||
# this will no longer be needed)
|
||||
|
||||
@@ -1,12 +1,72 @@
|
||||
use ark_ec::{AffineRepr, CurveGroup};
|
||||
use ark_ff::{BigInteger, PrimeField};
|
||||
use ark_r1cs_std::{
|
||||
alloc::{AllocVar, AllocationMode},
|
||||
fields::nonnative::{params::OptimizationType, AllocatedNonNativeFieldVar, NonNativeFieldVar},
|
||||
fields::{
|
||||
fp::FpVar,
|
||||
nonnative::{params::OptimizationType, AllocatedNonNativeFieldVar, NonNativeFieldVar},
|
||||
FieldVar,
|
||||
},
|
||||
ToBitsGadget,
|
||||
};
|
||||
use ark_relations::r1cs::{Namespace, SynthesisError};
|
||||
use ark_std::{One, Zero};
|
||||
use ark_std::Zero;
|
||||
use core::borrow::Borrow;
|
||||
|
||||
/// A more efficient version of `NonNativeFieldVar::to_constraint_field`
|
||||
pub fn nonnative_field_var_to_constraint_field<TargetField: PrimeField, BaseField: PrimeField>(
|
||||
f: &NonNativeFieldVar<TargetField, BaseField>,
|
||||
) -> Result<Vec<FpVar<BaseField>>, SynthesisError> {
|
||||
let bits = f.to_bits_le()?;
|
||||
|
||||
let bits_per_limb = BaseField::MODULUS_BIT_SIZE as usize - 1;
|
||||
let num_limbs = (TargetField::MODULUS_BIT_SIZE as usize).div_ceil(bits_per_limb);
|
||||
|
||||
let mut limbs = bits
|
||||
.chunks(bits_per_limb)
|
||||
.map(|chunk| {
|
||||
let mut limb = FpVar::<BaseField>::zero();
|
||||
let mut w = BaseField::one();
|
||||
for b in chunk.iter() {
|
||||
limb += FpVar::from(b.clone()) * w;
|
||||
w.double_in_place();
|
||||
}
|
||||
limb
|
||||
})
|
||||
.collect::<Vec<FpVar<BaseField>>>();
|
||||
limbs.resize(num_limbs, FpVar::zero());
|
||||
limbs.reverse();
|
||||
|
||||
Ok(limbs)
|
||||
}
|
||||
|
||||
/// The out-circuit counterpart of `nonnative_field_var_to_constraint_field`
|
||||
pub fn nonnative_field_to_field_elements<TargetField: PrimeField, BaseField: PrimeField>(
|
||||
f: &TargetField,
|
||||
) -> Vec<BaseField> {
|
||||
let bits = f.into_bigint().to_bits_le();
|
||||
|
||||
let bits_per_limb = BaseField::MODULUS_BIT_SIZE as usize - 1;
|
||||
let num_limbs = (TargetField::MODULUS_BIT_SIZE as usize).div_ceil(bits_per_limb);
|
||||
|
||||
let mut limbs = bits
|
||||
.chunks(bits_per_limb)
|
||||
.map(|chunk| {
|
||||
let mut limb = BaseField::zero();
|
||||
let mut w = BaseField::one();
|
||||
for &b in chunk.iter() {
|
||||
limb += BaseField::from(b) * w;
|
||||
w.double_in_place();
|
||||
}
|
||||
limb
|
||||
})
|
||||
.collect::<Vec<BaseField>>();
|
||||
limbs.resize(num_limbs, BaseField::zero());
|
||||
limbs.reverse();
|
||||
|
||||
limbs
|
||||
}
|
||||
|
||||
/// NonNativeAffineVar represents an elliptic curve point in Affine representation in the non-native
|
||||
/// field, over the constraint field. It is not intended to perform operations, but just to contain
|
||||
/// the affine coordinates in order to perform hash operations of the point.
|
||||
@@ -33,7 +93,7 @@ where
|
||||
let cs = cs.into();
|
||||
|
||||
let affine = val.borrow().into_affine();
|
||||
let zero_point = (&C::BaseField::zero(), &C::BaseField::one());
|
||||
let zero_point = (&C::BaseField::zero(), &C::BaseField::zero());
|
||||
let xy = affine.xy().unwrap_or(zero_point);
|
||||
|
||||
let x = NonNativeFieldVar::<C::BaseField, C::ScalarField>::new_variable(
|
||||
@@ -84,7 +144,7 @@ where
|
||||
)?;
|
||||
let y =
|
||||
AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
|
||||
&C::BaseField::one(),
|
||||
&C::BaseField::zero(),
|
||||
optimization_type,
|
||||
)?;
|
||||
return Ok((x, y));
|
||||
|
||||
@@ -21,7 +21,7 @@ use ark_r1cs_std::{
|
||||
};
|
||||
use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, Namespace, SynthesisError};
|
||||
use ark_std::fmt::Debug;
|
||||
use ark_std::{One, Zero};
|
||||
use ark_std::Zero;
|
||||
use core::{borrow::Borrow, marker::PhantomData};
|
||||
|
||||
use super::{
|
||||
@@ -254,7 +254,7 @@ pub struct AugmentedFCircuit<
|
||||
pub r_nonnat: Option<CF2<C1>>,
|
||||
pub cmT: Option<C1>,
|
||||
pub F: FC, // F circuit
|
||||
pub x: Option<CF1<C1>>, // public inputs (u_{i+1}.x)
|
||||
pub x: Option<CF1<C1>>, // public input (u_{i+1}.x[0])
|
||||
|
||||
// cyclefold verifier on C1
|
||||
// Here 'cf1, cf2' are for each of the CycleFold circuits, corresponding to the fold of cmW and
|
||||
@@ -268,6 +268,7 @@ pub struct AugmentedFCircuit<
|
||||
pub cf2_cmT: Option<C2>,
|
||||
pub cf1_r_nonnat: Option<C2::ScalarField>,
|
||||
pub cf2_r_nonnat: Option<C2::ScalarField>,
|
||||
pub cf_x: Option<CF1<C1>>, // public input (u_{i+1}.x[1])
|
||||
}
|
||||
|
||||
impl<C1: CurveGroup, C2: CurveGroup, GC2: CurveVar<C2, CF2<C2>>, FC: FCircuit<CF1<C1>>>
|
||||
@@ -300,6 +301,7 @@ where
|
||||
cf2_cmT: None,
|
||||
cf1_r_nonnat: None,
|
||||
cf2_r_nonnat: None,
|
||||
cf_x: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -308,7 +310,7 @@ impl<C1, C2, GC2, FC> ConstraintSynthesizer<CF1<C1>> for AugmentedFCircuit<C1, C
|
||||
where
|
||||
C1: CurveGroup,
|
||||
C2: CurveGroup,
|
||||
GC2: CurveVar<C2, CF2<C2>>,
|
||||
GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>,
|
||||
FC: FCircuit<CF1<C1>>,
|
||||
<C1 as CurveGroup>::BaseField: PrimeField,
|
||||
<C2 as CurveGroup>::BaseField: PrimeField,
|
||||
@@ -332,7 +334,7 @@ where
|
||||
.unwrap_or(vec![CF1::<C1>::zero(); self.F.state_len()]))
|
||||
})?;
|
||||
|
||||
let u_dummy_native = CommittedInstance::<C1>::dummy(1);
|
||||
let u_dummy_native = CommittedInstance::<C1>::dummy(2);
|
||||
let u_i = CommittedInstanceVar::<C1>::new_witness(cs.clone(), || {
|
||||
Ok(self.u_i.unwrap_or(u_dummy_native.clone()))
|
||||
})?;
|
||||
@@ -365,24 +367,16 @@ where
|
||||
let zero = FpVar::<CF1<C1>>::new_constant(cs.clone(), CF1::<C1>::zero())?;
|
||||
let is_not_basecase = i.is_neq(&zero)?;
|
||||
|
||||
// 1. u_i.x == H(i, z_0, z_i, U_i)
|
||||
// 1.a u_i.x[0] == H(i, z_0, z_i, U_i)
|
||||
let (u_i_x, U_i_vec) =
|
||||
U_i.clone()
|
||||
.hash(&crh_params, i.clone(), z_0.clone(), z_i.clone())?;
|
||||
// check that h == u_i.x
|
||||
// check that h == u_i.x[0]
|
||||
(u_i.x[0]).conditional_enforce_equal(&u_i_x, &is_not_basecase)?;
|
||||
|
||||
// 2. u_i.cmE==cm(0), u_i.u==1
|
||||
let zero_x = NonNativeFieldVar::<C1::BaseField, C1::ScalarField>::new_constant(
|
||||
cs.clone(),
|
||||
C1::BaseField::zero(),
|
||||
)?;
|
||||
let zero_y = NonNativeFieldVar::<C1::BaseField, C1::ScalarField>::new_constant(
|
||||
cs.clone(),
|
||||
C1::BaseField::one(),
|
||||
)?;
|
||||
(u_i.cmE.x.is_eq(&zero_x)?).conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?;
|
||||
(u_i.cmE.y.is_eq(&zero_y)?).conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?;
|
||||
(u_i.cmE.x.is_zero()?).conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?;
|
||||
(u_i.cmE.y.is_zero()?).conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?;
|
||||
(u_i.u.is_one()?).conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?;
|
||||
|
||||
// 3. nifs.verify, checks that folding u_i & U_i obtains U_{i+1}.
|
||||
@@ -406,7 +400,7 @@ where
|
||||
let nifs_check = NIFSGadget::<C1>::verify(r, U_i.clone(), u_i.clone(), U_i1.clone())?;
|
||||
nifs_check.conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?;
|
||||
|
||||
// 4. u_{i+1}.x = H(i+1, z_0, z_i+1, U_{i+1}), this is the output of F'
|
||||
// 4.a u_{i+1}.x[0] = H(i+1, z_0, z_i+1, U_{i+1}), this is the first output of F'
|
||||
let (u_i1_x, _) = U_i1.clone().hash(
|
||||
&crh_params,
|
||||
i + FpVar::<CF1<C1>>::one(),
|
||||
@@ -444,6 +438,9 @@ where
|
||||
NonNativeFieldVar::<C1::BaseField, C1::ScalarField>::new_witness(cs.clone(), || {
|
||||
Ok(self.cf2_r_nonnat.unwrap_or_else(C2::ScalarField::zero))
|
||||
})?;
|
||||
let cf_x = FpVar::<CF1<C1>>::new_input(cs.clone(), || {
|
||||
Ok(self.cf_x.unwrap_or_else(C1::ScalarField::zero))
|
||||
})?;
|
||||
|
||||
let cfW_x: Vec<NonNativeFieldVar<C1::BaseField, C1::ScalarField>> = vec![
|
||||
r_nonnat.clone(),
|
||||
@@ -458,6 +455,15 @@ where
|
||||
r_nonnat, U_i.cmE.x, U_i.cmE.y, cmT.x, cmT.y, U_i1.cmE.x, U_i1.cmE.y,
|
||||
];
|
||||
|
||||
// 1.b u_i.x[1] == H(cf_U_i)
|
||||
let (cf_u_i_x, _) = cf_U_i.clone().hash(&crh_params)?;
|
||||
// check that h == u_i.x[1]
|
||||
(u_i.x[1]).conditional_enforce_equal(&cf_u_i_x, &is_not_basecase)?;
|
||||
|
||||
// 4.b u_{i+1}.x[1] = H(cf_U_{i+1}), this is the second output of F'
|
||||
let (cf_u_i1_x, _) = cf_U_i1.clone().hash(&crh_params)?;
|
||||
cf_u_i1_x.enforce_equal(&cf_x)?;
|
||||
|
||||
// ensure that cf1_u & cf2_u have as public inputs the cmW & cmE from main instances U_i,
|
||||
// u_i, U_i+1 coordinates of the commitments
|
||||
cf1_u_i
|
||||
@@ -527,7 +533,7 @@ pub mod tests {
|
||||
use super::*;
|
||||
use ark_bn254::{Fr, G1Projective as Projective};
|
||||
use ark_ff::BigInteger;
|
||||
use ark_r1cs_std::{alloc::AllocVar, R1CSVar};
|
||||
use ark_r1cs_std::R1CSVar;
|
||||
use ark_relations::r1cs::ConstraintSystem;
|
||||
use ark_std::UniformRand;
|
||||
|
||||
|
||||
@@ -1,30 +1,35 @@
|
||||
/// contains [CycleFold](https://eprint.iacr.org/2023/1192.pdf) related circuits
|
||||
use ark_crypto_primitives::sponge::{
|
||||
constraints::CryptographicSpongeVar,
|
||||
poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig, PoseidonSponge},
|
||||
Absorb, CryptographicSponge,
|
||||
use ark_crypto_primitives::{
|
||||
crh::{
|
||||
poseidon::constraints::{CRHGadget, CRHParametersVar},
|
||||
CRHSchemeGadget,
|
||||
},
|
||||
sponge::{
|
||||
constraints::CryptographicSpongeVar,
|
||||
poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig, PoseidonSponge},
|
||||
Absorb, CryptographicSponge,
|
||||
},
|
||||
};
|
||||
use ark_ec::CurveGroup;
|
||||
use ark_ff::PrimeField;
|
||||
use ark_ec::{AffineRepr, CurveGroup};
|
||||
use ark_ff::{Field, PrimeField, ToConstraintField};
|
||||
use ark_r1cs_std::{
|
||||
alloc::{AllocVar, AllocationMode},
|
||||
bits::uint8::UInt8,
|
||||
boolean::Boolean,
|
||||
eq::EqGadget,
|
||||
fields::{fp::FpVar, nonnative::NonNativeFieldVar},
|
||||
fields::{fp::FpVar, nonnative::NonNativeFieldVar, FieldVar},
|
||||
groups::GroupOpsBounds,
|
||||
prelude::CurveVar,
|
||||
ToBytesGadget, ToConstraintFieldGadget,
|
||||
ToConstraintFieldGadget,
|
||||
};
|
||||
use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, Namespace, SynthesisError};
|
||||
use ark_serialize::CanonicalSerialize;
|
||||
use ark_std::fmt::Debug;
|
||||
use ark_std::Zero;
|
||||
use ark_std::{One, Zero};
|
||||
use core::{borrow::Borrow, marker::PhantomData};
|
||||
|
||||
use super::circuits::CF2;
|
||||
use super::CommittedInstance;
|
||||
use crate::constants::N_BITS_RO;
|
||||
use crate::folding::circuits::nonnative::nonnative_field_var_to_constraint_field;
|
||||
use crate::Error;
|
||||
|
||||
// public inputs length for the CycleFoldCircuit: |[r, p1.x,y, p2.x,y, p3.x,y]|
|
||||
@@ -82,6 +87,61 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, GC> ToConstraintFieldGadget<CF2<C>> for CycleFoldCommittedInstanceVar<C, GC>
|
||||
where
|
||||
C: CurveGroup,
|
||||
GC: CurveVar<C, CF2<C>> + ToConstraintFieldGadget<CF2<C>>,
|
||||
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField + Absorb,
|
||||
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
||||
{
|
||||
// Extract the underlying field elements from `CycleFoldCommittedInstanceVar`, in the order of
|
||||
// `u`, `x`, `cmE.x`, `cmE.y`, `cmW.x`, `cmW.y`, `cmE.is_inf || cmW.is_inf` (|| is for concat).
|
||||
fn to_constraint_field(&self) -> Result<Vec<FpVar<CF2<C>>>, SynthesisError> {
|
||||
let mut cmE_elems = self.cmE.to_constraint_field()?;
|
||||
let mut cmW_elems = self.cmW.to_constraint_field()?;
|
||||
|
||||
let cmE_is_inf = cmE_elems.pop().unwrap();
|
||||
let cmW_is_inf = cmW_elems.pop().unwrap();
|
||||
// Concatenate `cmE_is_inf` and `cmW_is_inf` to save constraints for CRHGadget::evaluate
|
||||
let is_inf = cmE_is_inf.double()? + cmW_is_inf;
|
||||
|
||||
Ok([
|
||||
nonnative_field_var_to_constraint_field(&self.u)?,
|
||||
self.x
|
||||
.iter()
|
||||
.map(nonnative_field_var_to_constraint_field)
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
.concat(),
|
||||
cmE_elems,
|
||||
cmW_elems,
|
||||
vec![is_inf],
|
||||
]
|
||||
.concat())
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, GC> CycleFoldCommittedInstanceVar<C, GC>
|
||||
where
|
||||
C: CurveGroup,
|
||||
GC: CurveVar<C, CF2<C>> + ToConstraintFieldGadget<CF2<C>>,
|
||||
<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.
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn hash(
|
||||
self,
|
||||
crh_params: &CRHParametersVar<CF2<C>>,
|
||||
) -> Result<(FpVar<CF2<C>>, Vec<FpVar<CF2<C>>>), SynthesisError> {
|
||||
let U_vec = self.to_constraint_field()?;
|
||||
Ok((CRHGadget::evaluate(crh_params, &U_vec)?, U_vec))
|
||||
}
|
||||
}
|
||||
|
||||
/// CommittedInstanceInCycleFoldVar represents the Nova CommittedInstance in the CycleFold circuit,
|
||||
/// where the commitments to E and W (cmW and cmW) from the CommittedInstance on the E2,
|
||||
/// represented as native points, which are folded on the auxiliary curve constraints field (E2::Fr
|
||||
@@ -184,7 +244,7 @@ pub struct CycleFoldChallengeGadget<C: CurveGroup, GC: CurveVar<C, CF2<C>>> {
|
||||
impl<C, GC> CycleFoldChallengeGadget<C, GC>
|
||||
where
|
||||
C: CurveGroup,
|
||||
GC: CurveVar<C, CF2<C>>,
|
||||
GC: CurveVar<C, CF2<C>> + ToConstraintFieldGadget<CF2<C>>,
|
||||
<C as CurveGroup>::BaseField: PrimeField,
|
||||
<C as CurveGroup>::BaseField: Absorb,
|
||||
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
||||
@@ -197,39 +257,30 @@ where
|
||||
) -> Result<Vec<bool>, Error> {
|
||||
let mut sponge = PoseidonSponge::<C::BaseField>::new(poseidon_config);
|
||||
|
||||
let U_i_cmE_bytes = point_to_bytes(U_i.cmE)?;
|
||||
let U_i_cmW_bytes = point_to_bytes(U_i.cmW)?;
|
||||
let u_i_cmE_bytes = point_to_bytes(u_i.cmE)?;
|
||||
let u_i_cmW_bytes = point_to_bytes(u_i.cmW)?;
|
||||
let cmT_bytes = point_to_bytes(cmT)?;
|
||||
let mut U_vec = U_i.to_field_elements().unwrap();
|
||||
let mut u_vec = u_i.to_field_elements().unwrap();
|
||||
let (cmT_x, cmT_y, cmT_is_inf) = match cmT.into_affine().xy() {
|
||||
Some((&x, &y)) => (x, y, C::BaseField::zero()),
|
||||
None => (
|
||||
C::BaseField::zero(),
|
||||
C::BaseField::zero(),
|
||||
C::BaseField::one(),
|
||||
),
|
||||
};
|
||||
|
||||
let mut U_i_u_bytes = Vec::new();
|
||||
U_i.u.serialize_uncompressed(&mut U_i_u_bytes)?;
|
||||
let mut U_i_x_bytes = Vec::new();
|
||||
U_i.x.serialize_uncompressed(&mut U_i_x_bytes)?;
|
||||
U_i_x_bytes = U_i_x_bytes[8..].to_vec();
|
||||
let mut u_i_u_bytes = Vec::new();
|
||||
u_i.u.serialize_uncompressed(&mut u_i_u_bytes)?;
|
||||
let mut u_i_x_bytes = Vec::new();
|
||||
u_i.x.serialize_uncompressed(&mut u_i_x_bytes)?;
|
||||
u_i_x_bytes = u_i_x_bytes[8..].to_vec();
|
||||
let U_cm_is_inf = U_vec.pop().unwrap();
|
||||
let u_cm_is_inf = u_vec.pop().unwrap();
|
||||
|
||||
let input: Vec<u8> = [
|
||||
U_i_cmE_bytes,
|
||||
U_i_u_bytes,
|
||||
U_i_cmW_bytes,
|
||||
U_i_x_bytes,
|
||||
u_i_cmE_bytes,
|
||||
u_i_u_bytes,
|
||||
u_i_cmW_bytes,
|
||||
u_i_x_bytes,
|
||||
cmT_bytes,
|
||||
]
|
||||
.concat();
|
||||
// Concatenate `U_i.cmE_is_inf`, `U_i.cmW_is_inf`, `u_i.cmE_is_inf`, `u_i.cmW_is_inf`, `cmT_is_inf`
|
||||
// to save constraints for sponge.squeeze_bits in the corresponding circuit
|
||||
let is_inf = U_cm_is_inf * CF2::<C>::from(8u8) + u_cm_is_inf.double() + cmT_is_inf;
|
||||
|
||||
let input = [U_vec, u_vec, vec![cmT_x, cmT_y, is_inf]].concat();
|
||||
sponge.absorb(&input);
|
||||
let bits = sponge.squeeze_bits(N_BITS_RO);
|
||||
Ok(bits)
|
||||
}
|
||||
|
||||
// compatible with the native get_challenge_native
|
||||
pub fn get_challenge_gadget(
|
||||
cs: ConstraintSystemRef<C::BaseField>,
|
||||
@@ -240,57 +291,25 @@ where
|
||||
) -> Result<Vec<Boolean<C::BaseField>>, SynthesisError> {
|
||||
let mut sponge = PoseidonSpongeVar::<C::BaseField>::new(cs, poseidon_config);
|
||||
|
||||
let U_i_x_bytes: Vec<UInt8<CF2<C>>> = U_i
|
||||
.x
|
||||
.iter()
|
||||
.flat_map(|e| e.to_bytes().unwrap_or(vec![]))
|
||||
.collect::<Vec<UInt8<CF2<C>>>>();
|
||||
let u_i_x_bytes: Vec<UInt8<CF2<C>>> = u_i
|
||||
.x
|
||||
.iter()
|
||||
.flat_map(|e| e.to_bytes().unwrap_or(vec![]))
|
||||
.collect::<Vec<UInt8<CF2<C>>>>();
|
||||
let mut U_vec = U_i.to_constraint_field()?;
|
||||
let mut u_vec = u_i.to_constraint_field()?;
|
||||
let mut cmT_vec = cmT.to_constraint_field()?;
|
||||
|
||||
let input: Vec<UInt8<CF2<C>>> = [
|
||||
pointvar_to_bytes(U_i.cmE)?,
|
||||
U_i.u.to_bytes()?,
|
||||
pointvar_to_bytes(U_i.cmW)?,
|
||||
U_i_x_bytes,
|
||||
pointvar_to_bytes(u_i.cmE)?,
|
||||
u_i.u.to_bytes()?,
|
||||
pointvar_to_bytes(u_i.cmW)?,
|
||||
u_i_x_bytes,
|
||||
pointvar_to_bytes(cmT)?,
|
||||
]
|
||||
.concat();
|
||||
let U_cm_is_inf = U_vec.pop().unwrap();
|
||||
let u_cm_is_inf = u_vec.pop().unwrap();
|
||||
let cmT_is_inf = cmT_vec.pop().unwrap();
|
||||
|
||||
// Concatenate `U_i.cmE_is_inf`, `U_i.cmW_is_inf`, `u_i.cmE_is_inf`, `u_i.cmW_is_inf`, `cmT_is_inf`
|
||||
// to save constraints for sponge.squeeze_bits
|
||||
let is_inf = U_cm_is_inf * CF2::<C>::from(8u8) + u_cm_is_inf.double()? + cmT_is_inf;
|
||||
|
||||
let input = [U_vec, u_vec, cmT_vec, vec![is_inf]].concat();
|
||||
sponge.absorb(&input)?;
|
||||
let bits = sponge.squeeze_bits(N_BITS_RO)?;
|
||||
Ok(bits)
|
||||
}
|
||||
}
|
||||
|
||||
/// returns the bytes being compatible with the pointvar_to_bytes method.
|
||||
/// These methods are temporary once arkworks has the fix to prevent different to_bytes behaviour
|
||||
/// across different curves. Eg, in pasta and bn254: pasta returns 65 bytes both native and gadget,
|
||||
/// whereas bn254 returns 64 bytes native and 65 in gadget, also the penultimate byte is different
|
||||
/// natively than in gadget.
|
||||
fn point_to_bytes<C: CurveGroup>(p: C) -> Result<Vec<u8>, Error> {
|
||||
let l = p.uncompressed_size();
|
||||
let mut b = Vec::new();
|
||||
p.serialize_uncompressed(&mut b)?;
|
||||
if p.is_zero() {
|
||||
b[l / 2] = 1;
|
||||
b[l - 1] = 1;
|
||||
}
|
||||
Ok(b[..63].to_vec())
|
||||
}
|
||||
fn pointvar_to_bytes<C: CurveGroup, GC: CurveVar<C, CF2<C>>>(
|
||||
p: GC,
|
||||
) -> Result<Vec<UInt8<CF2<C>>>, SynthesisError> {
|
||||
let b = p.to_bytes()?;
|
||||
Ok(b[..63].to_vec())
|
||||
}
|
||||
|
||||
/// 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}
|
||||
@@ -362,7 +381,7 @@ pub mod tests {
|
||||
use super::*;
|
||||
use ark_bn254::{constraints::GVar, Fq, Fr, G1Projective as Projective};
|
||||
use ark_ff::BigInteger;
|
||||
use ark_r1cs_std::{alloc::AllocVar, R1CSVar};
|
||||
use ark_r1cs_std::R1CSVar;
|
||||
use ark_relations::r1cs::ConstraintSystem;
|
||||
use ark_std::UniformRand;
|
||||
|
||||
@@ -480,21 +499,6 @@ pub mod tests {
|
||||
assert!(cs.is_satisfied().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_point_bytes() {
|
||||
let mut rng = ark_std::test_rng();
|
||||
|
||||
let p = Projective::rand(&mut rng);
|
||||
let p_bytes = point_to_bytes(p).unwrap();
|
||||
|
||||
let cs = ConstraintSystem::<Fq>::new_ref();
|
||||
let pVar = GVar::new_witness(cs.clone(), || Ok(p)).unwrap();
|
||||
assert_eq!(pVar.value().unwrap(), p);
|
||||
|
||||
let p_bytesVar = &pointvar_to_bytes(pVar).unwrap();
|
||||
assert_eq!(p_bytesVar.value().unwrap(), p_bytes);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cyclefold_challenge_gadget() {
|
||||
let mut rng = ark_std::test_rng();
|
||||
@@ -556,4 +560,33 @@ pub mod tests {
|
||||
assert_eq!(rVar.value().unwrap(), r);
|
||||
assert_eq!(r_bitsVar.value().unwrap(), r_bits);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cyclefold_hash_gadget() {
|
||||
let mut rng = ark_std::test_rng();
|
||||
let poseidon_config = poseidon_test_config::<Fq>();
|
||||
|
||||
let U_i = CommittedInstance::<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(CF_IO_LEN)
|
||||
.collect(),
|
||||
};
|
||||
let h = U_i.hash_cyclefold(&poseidon_config).unwrap();
|
||||
|
||||
let cs = ConstraintSystem::<Fq>::new_ref();
|
||||
let U_iVar =
|
||||
CycleFoldCommittedInstanceVar::<Projective, GVar>::new_witness(cs.clone(), || {
|
||||
Ok(U_i.clone())
|
||||
})
|
||||
.unwrap();
|
||||
let (hVar, _) = U_iVar
|
||||
.hash(&CRHParametersVar::new_constant(cs.clone(), poseidon_config).unwrap())
|
||||
.unwrap();
|
||||
hVar.enforce_equal(&FpVar::new_witness(cs.clone(), || Ok(h)).unwrap())
|
||||
.unwrap();
|
||||
assert!(cs.is_satisfied().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ where
|
||||
C1: CurveGroup,
|
||||
C2: CurveGroup,
|
||||
GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
|
||||
GC2: CurveVar<C2, CF2<C2>>,
|
||||
GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>,
|
||||
FC: FCircuit<C1::ScalarField>,
|
||||
CS1: CommitmentScheme<
|
||||
C1,
|
||||
|
||||
@@ -16,7 +16,7 @@ use ark_r1cs_std::{
|
||||
ToConstraintFieldGadget,
|
||||
};
|
||||
use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, Namespace, SynthesisError};
|
||||
use ark_std::{log2, One, Zero};
|
||||
use ark_std::{log2, Zero};
|
||||
use core::{borrow::Borrow, marker::PhantomData};
|
||||
|
||||
use super::{circuits::ChallengeGadget, nifs::NIFS};
|
||||
@@ -245,7 +245,7 @@ where
|
||||
C1: CurveGroup,
|
||||
C2: CurveGroup,
|
||||
GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
|
||||
GC2: CurveVar<C2, CF2<C2>>,
|
||||
GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>,
|
||||
CS1: CommitmentScheme<C1>,
|
||||
// enforce that the CS2 is Pedersen commitment scheme, since we're at Ethereum's EVM decider
|
||||
CS2: CommitmentScheme<C2, ProverParams = PedersenParams<C2>>,
|
||||
@@ -337,7 +337,7 @@ where
|
||||
C1: CurveGroup,
|
||||
C2: CurveGroup,
|
||||
GC1: CurveVar<C1, CF2<C1>>,
|
||||
GC2: CurveVar<C2, CF2<C2>>,
|
||||
GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>,
|
||||
CS1: CommitmentScheme<C1>,
|
||||
CS2: CommitmentScheme<C2>,
|
||||
<C1 as CurveGroup>::BaseField: PrimeField,
|
||||
@@ -362,9 +362,9 @@ where
|
||||
Ok(self.z_i.unwrap_or(vec![CF1::<C1>::zero()]))
|
||||
})?;
|
||||
|
||||
let u_dummy_native = CommittedInstance::<C1>::dummy(1);
|
||||
let u_dummy_native = CommittedInstance::<C1>::dummy(2);
|
||||
let w_dummy_native = Witness::<C1>::new(
|
||||
vec![C1::ScalarField::zero(); self.r1cs.A.n_cols - 2 /* (2=1+1, since u_i.x.len=1) */],
|
||||
vec![C1::ScalarField::zero(); self.r1cs.A.n_cols - 3 /* (3=2+1, since u_i.x.len=2) */],
|
||||
self.E_len,
|
||||
);
|
||||
|
||||
@@ -412,20 +412,13 @@ where
|
||||
)?;
|
||||
|
||||
// 2. u_i.cmE==cm(0), u_i.u==1
|
||||
// Here zero_x & zero_y are the x & y coordinates of the zero point affine representation.
|
||||
let zero_x = NonNativeFieldVar::<C1::BaseField, C1::ScalarField>::new_constant(
|
||||
cs.clone(),
|
||||
C1::BaseField::zero(),
|
||||
)?;
|
||||
let zero_y = NonNativeFieldVar::<C1::BaseField, C1::ScalarField>::new_constant(
|
||||
cs.clone(),
|
||||
C1::BaseField::one(),
|
||||
)?;
|
||||
u_i.cmE.x.enforce_equal(&zero_x)?;
|
||||
u_i.cmE.y.enforce_equal(&zero_y)?;
|
||||
// Here zero is the x & y coordinates of the zero point affine representation.
|
||||
let zero = NonNativeFieldVar::<C1::BaseField, C1::ScalarField>::zero();
|
||||
u_i.cmE.x.enforce_equal(&zero)?;
|
||||
u_i.cmE.y.enforce_equal(&zero)?;
|
||||
(u_i.u.is_one()?).enforce_equal(&Boolean::TRUE)?;
|
||||
|
||||
// 3. u_i.x == H(i, z_0, z_i, U_i)
|
||||
// 3.a u_i.x[0] == H(i, z_0, z_i, U_i)
|
||||
let (u_i_x, U_i_vec) =
|
||||
U_i.clone()
|
||||
.hash(&crh_params, i.clone(), z_0.clone(), z_i.clone())?;
|
||||
@@ -454,6 +447,10 @@ where
|
||||
Ok(self.cf_W_i.unwrap_or(w_dummy_native.clone()))
|
||||
})?;
|
||||
|
||||
// 3.b u_i.x[1] == H(cf_U_i)
|
||||
let (cf_u_i_x, _) = cf_U_i.clone().hash(&crh_params)?;
|
||||
(u_i.x[1]).enforce_equal(&cf_u_i_x)?;
|
||||
|
||||
// 4. check Pedersen commitments of cf_U_i.{cmE, cmW}
|
||||
let H = GC2::new_constant(cs.clone(), self.cf_pedersen_params.h)?;
|
||||
let G = Vec::<GC2>::new_constant(cs.clone(), self.cf_pedersen_params.generators)?;
|
||||
@@ -615,16 +612,10 @@ pub mod tests {
|
||||
},
|
||||
CRHScheme, CRHSchemeGadget,
|
||||
};
|
||||
use ark_ff::BigInteger;
|
||||
use ark_pallas::{constraints::GVar, Fq, Fr, Projective};
|
||||
use ark_r1cs_std::{
|
||||
alloc::AllocVar,
|
||||
bits::uint8::UInt8,
|
||||
eq::EqGadget,
|
||||
fields::{fp::FpVar, nonnative::NonNativeFieldVar},
|
||||
};
|
||||
use ark_r1cs_std::bits::uint8::UInt8;
|
||||
use ark_relations::r1cs::ConstraintSystem;
|
||||
use ark_std::UniformRand;
|
||||
use ark_std::{One, UniformRand};
|
||||
use ark_vesta::{constraints::GVar as GVar2, Projective as Projective2};
|
||||
|
||||
use crate::commitment::pedersen::Pedersen;
|
||||
@@ -633,11 +624,8 @@ pub mod tests {
|
||||
use crate::transcript::poseidon::poseidon_test_config;
|
||||
use crate::FoldingScheme;
|
||||
|
||||
use crate::ccs::r1cs::tests::{get_test_r1cs, get_test_z};
|
||||
use crate::ccs::r1cs::{extract_r1cs, extract_w_x};
|
||||
use crate::ccs::r1cs::{
|
||||
tests::{get_test_r1cs, get_test_z},
|
||||
R1CS,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_relaxed_r1cs_small_gadget_handcrafted() {
|
||||
|
||||
@@ -5,7 +5,7 @@ use ark_crypto_primitives::{
|
||||
sponge::{poseidon::PoseidonConfig, Absorb},
|
||||
};
|
||||
use ark_ec::{AffineRepr, CurveGroup, Group};
|
||||
use ark_ff::{BigInteger, PrimeField};
|
||||
use ark_ff::{BigInteger, Field, PrimeField, ToConstraintField};
|
||||
use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldGadget};
|
||||
use ark_std::fmt::Debug;
|
||||
use ark_std::{One, Zero};
|
||||
@@ -15,7 +15,9 @@ use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem};
|
||||
|
||||
use crate::ccs::r1cs::{extract_r1cs, extract_w_x, R1CS};
|
||||
use crate::commitment::CommitmentScheme;
|
||||
use crate::folding::circuits::nonnative::point_to_nonnative_limbs;
|
||||
use crate::folding::circuits::nonnative::{
|
||||
nonnative_field_to_field_elements, point_to_nonnative_limbs,
|
||||
};
|
||||
use crate::frontend::FCircuit;
|
||||
use crate::utils::vec::is_zero_vec;
|
||||
use crate::Error;
|
||||
@@ -93,6 +95,56 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: CurveGroup> ToConstraintField<C::BaseField> for CommittedInstance<C>
|
||||
where
|
||||
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField + Absorb,
|
||||
{
|
||||
fn to_field_elements(&self) -> Option<Vec<C::BaseField>> {
|
||||
let u = nonnative_field_to_field_elements(&self.u);
|
||||
let x = self
|
||||
.x
|
||||
.iter()
|
||||
.flat_map(nonnative_field_to_field_elements)
|
||||
.collect::<Vec<_>>();
|
||||
let (cmE_x, cmE_y, cmE_is_inf) = match self.cmE.into_affine().xy() {
|
||||
Some((&x, &y)) => (x, y, C::BaseField::zero()),
|
||||
None => (
|
||||
C::BaseField::zero(),
|
||||
C::BaseField::zero(),
|
||||
C::BaseField::one(),
|
||||
),
|
||||
};
|
||||
let (cmW_x, cmW_y, cmW_is_inf) = match self.cmW.into_affine().xy() {
|
||||
Some((&x, &y)) => (x, y, C::BaseField::zero()),
|
||||
None => (
|
||||
C::BaseField::zero(),
|
||||
C::BaseField::zero(),
|
||||
C::BaseField::one(),
|
||||
),
|
||||
};
|
||||
// Concatenate `cmE_is_inf` and `cmW_is_inf` to save constraints for CRHGadget::evaluate in the corresponding circuit
|
||||
let is_inf = cmE_is_inf.double() + cmW_is_inf;
|
||||
|
||||
Some([u, x, vec![cmE_x, cmE_y, cmW_x, cmW_y, is_inf]].concat())
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
&self,
|
||||
poseidon_config: &PoseidonConfig<C::BaseField>,
|
||||
) -> Result<C::BaseField, Error> {
|
||||
CRH::<C::BaseField>::evaluate(poseidon_config, self.to_field_elements().unwrap())
|
||||
.map_err(|e| Error::Other(e.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct Witness<C: CurveGroup> {
|
||||
pub E: Vec<C::ScalarField>,
|
||||
@@ -203,7 +255,7 @@ where
|
||||
C1: CurveGroup,
|
||||
GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
|
||||
C2: CurveGroup,
|
||||
GC2: CurveVar<C2, CF2<C2>>,
|
||||
GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>,
|
||||
FC: FCircuit<C1::ScalarField>,
|
||||
CS1: CommitmentScheme<C1>,
|
||||
CS2: CommitmentScheme<C2>,
|
||||
@@ -320,15 +372,18 @@ where
|
||||
)?;
|
||||
|
||||
// folded instance output (public input, x)
|
||||
// u_{i+1}.x = H(i+1, z_0, z_{i+1}, U_{i+1})
|
||||
// u_{i+1}.x[0] = H(i+1, z_0, z_{i+1}, U_{i+1})
|
||||
let u_i1_x = U_i1.hash(
|
||||
&self.poseidon_config,
|
||||
self.i + C1::ScalarField::one(),
|
||||
self.z_0.clone(),
|
||||
z_i1.clone(),
|
||||
)?;
|
||||
// u_{i+1}.x[1] = H(cf_U_{i+1})
|
||||
let cf_u_i1_x: C1::ScalarField;
|
||||
|
||||
if self.i == C1::ScalarField::zero() {
|
||||
cf_u_i1_x = self.cf_U_i.hash_cyclefold(&self.poseidon_config)?;
|
||||
// base case
|
||||
augmented_F_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> {
|
||||
_gc2: PhantomData,
|
||||
@@ -353,6 +408,7 @@ where
|
||||
cf2_cmT: None,
|
||||
cf1_r_nonnat: None,
|
||||
cf2_r_nonnat: None,
|
||||
cf_x: Some(cf_u_i1_x),
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -406,6 +462,8 @@ where
|
||||
let (_cfE_w_i, cfE_u_i, cf_W_i1, cf_U_i1, cf_cmT, cf_r2_Fq) =
|
||||
self.fold_cyclefold_circuit(cfW_W_i1, cfW_U_i1.clone(), cfE_u_i_x, cfE_circuit)?;
|
||||
|
||||
cf_u_i1_x = cf_U_i1.hash_cyclefold(&self.poseidon_config)?;
|
||||
|
||||
augmented_F_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> {
|
||||
_gc2: PhantomData,
|
||||
poseidon_config: self.poseidon_config.clone(),
|
||||
@@ -430,6 +488,7 @@ where
|
||||
cf2_cmT: Some(cf_cmT),
|
||||
cf1_r_nonnat: Some(cfW_r1_Fq),
|
||||
cf2_r_nonnat: Some(cf_r2_Fq),
|
||||
cf_x: Some(cf_u_i1_x),
|
||||
};
|
||||
|
||||
self.cf_W_i = cf_W_i1.clone();
|
||||
@@ -453,20 +512,20 @@ where
|
||||
|
||||
let cs = cs.into_inner().ok_or(Error::NoInnerConstraintSystem)?;
|
||||
let (w_i1, x_i1) = extract_w_x::<C1::ScalarField>(&cs);
|
||||
if x_i1[0] != u_i1_x {
|
||||
if x_i1[0] != u_i1_x || x_i1[1] != cf_u_i1_x {
|
||||
return Err(Error::NotEqual);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
if x_i1.len() != 1 {
|
||||
return Err(Error::NotExpectedLength(x_i1.len(), 1));
|
||||
if x_i1.len() != 2 {
|
||||
return Err(Error::NotExpectedLength(x_i1.len(), 2));
|
||||
}
|
||||
|
||||
// set values for next iteration
|
||||
self.i += C1::ScalarField::one();
|
||||
self.z_i = z_i1.clone();
|
||||
self.w_i = Witness::<C1>::new(w_i1, self.r1cs.A.n_rows);
|
||||
self.u_i = self.w_i.commit::<CS1>(&self.cs_params, vec![u_i1_x])?;
|
||||
self.u_i = self.w_i.commit::<CS1>(&self.cs_params, x_i1)?;
|
||||
self.W_i = W_i1.clone();
|
||||
self.U_i = U_i1.clone();
|
||||
|
||||
@@ -511,16 +570,21 @@ where
|
||||
let (u_i, w_i) = incoming_instance;
|
||||
let (cf_U_i, cf_W_i) = cyclefold_instance;
|
||||
|
||||
if u_i.x.len() != 1 || U_i.x.len() != 1 {
|
||||
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 == H(i, z_0, z_i, U_i)
|
||||
// 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 u_i.cmE==0, u_i.u==1 (=u_i is a un-relaxed instance)
|
||||
if !u_i.cmE.is_zero() || !u_i.u.is_one() {
|
||||
@@ -589,7 +653,7 @@ where
|
||||
C1: CurveGroup,
|
||||
GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
|
||||
C2: CurveGroup,
|
||||
GC2: CurveVar<C2, CF2<C2>>,
|
||||
GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>,
|
||||
FC: FCircuit<C1::ScalarField>,
|
||||
CS1: CommitmentScheme<C1>,
|
||||
CS2: CommitmentScheme<C2>,
|
||||
@@ -680,7 +744,7 @@ where
|
||||
C1: CurveGroup,
|
||||
GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
|
||||
C2: CurveGroup,
|
||||
GC2: CurveVar<C2, CF2<C2>>,
|
||||
GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>,
|
||||
FC: FCircuit<C1::ScalarField>,
|
||||
<C1 as CurveGroup>::BaseField: PrimeField,
|
||||
<C2 as CurveGroup>::BaseField: PrimeField,
|
||||
@@ -708,7 +772,7 @@ where
|
||||
C1: CurveGroup,
|
||||
GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>,
|
||||
C2: CurveGroup,
|
||||
GC2: CurveVar<C2, CF2<C2>>,
|
||||
GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>,
|
||||
FC: FCircuit<C1::ScalarField>,
|
||||
<C1 as CurveGroup>::BaseField: PrimeField,
|
||||
<C2 as CurveGroup>::BaseField: PrimeField,
|
||||
@@ -725,7 +789,7 @@ where
|
||||
/// returns the coordinates of a commitment point. This is compatible with the arkworks
|
||||
/// GC.to_constraint_field()[..2]
|
||||
pub(crate) fn get_cm_coordinates<C: CurveGroup>(cm: &C) -> Vec<C::BaseField> {
|
||||
let zero = (&C::BaseField::zero(), &C::BaseField::one());
|
||||
let zero = (&C::BaseField::zero(), &C::BaseField::zero());
|
||||
let cm = cm.into_affine();
|
||||
let (cm_x, cm_y) = cm.xy().unwrap_or(zero);
|
||||
vec![*cm_x, *cm_y]
|
||||
|
||||
@@ -7,7 +7,7 @@ use ark_ec::{AffineRepr, CurveGroup, Group};
|
||||
use ark_ff::{BigInteger, Field, PrimeField};
|
||||
use ark_r1cs_std::{boolean::Boolean, fields::fp::FpVar};
|
||||
use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
|
||||
use ark_std::{One, Zero};
|
||||
use ark_std::Zero;
|
||||
|
||||
use crate::transcript::Transcript;
|
||||
use crate::Error;
|
||||
@@ -61,7 +61,7 @@ where
|
||||
// over bytes in order to have a logic that can be reproduced in-circuit.
|
||||
fn prepare_point<C: CurveGroup>(p: &C) -> Result<Vec<C::ScalarField>, Error> {
|
||||
let affine = p.into_affine();
|
||||
let zero_point = (&C::BaseField::zero(), &C::BaseField::one());
|
||||
let zero_point = (&C::BaseField::zero(), &C::BaseField::zero());
|
||||
let xy = affine.xy().unwrap_or(zero_point);
|
||||
|
||||
let x_bi =
|
||||
@@ -145,7 +145,7 @@ pub fn poseidon_test_config<F: PrimeField>() -> PoseidonConfig<F> {
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use ark_pallas::{constraints::GVar, Fq, Fr, Projective};
|
||||
use ark_r1cs_std::{alloc::AllocVar, fields::fp::FpVar, groups::CurveVar, R1CSVar};
|
||||
use ark_r1cs_std::{alloc::AllocVar, groups::CurveVar, R1CSVar};
|
||||
use ark_relations::r1cs::ConstraintSystem;
|
||||
use ark_vesta::Projective as E2Projective;
|
||||
use std::ops::Mul;
|
||||
|
||||
Reference in New Issue
Block a user