mirror of
https://github.com/arnaucube/sonobe.git
synced 2026-01-09 07:21:28 +01:00
Reduce the number of constraints in DeciderEthCircuit (#88)
* Add a dedicated variant of `mat_vec_mul_sparse` for `NonNativeFieldVar` * Switch to a customized in-circuit nonnative implementation for efficiency * Comments and tests for `NonNativeUintVar` * Make `CycleFoldCircuit` a bit smaller * Faster trusted setup and proof generation by avoiding some nested LCs * Check the remaining limbs in a more safe way * Format * Disable the non-native checks in tests again * Clarify the group operation in `enforce_equal_unaligned` * Explain the rationale behind non-native mat-vec multiplication * Explain the difference with some other impls of `enforce_equal_unaligned` * Format
This commit is contained in:
@@ -18,6 +18,7 @@ ark-circom = { git = "https://github.com/arnaucube/circom-compat.git" }
|
|||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
rayon = "1.7.0"
|
rayon = "1.7.0"
|
||||||
num-bigint = "0.4"
|
num-bigint = "0.4"
|
||||||
|
num-integer = "0.1"
|
||||||
color-eyre = "=0.6.2"
|
color-eyre = "=0.6.2"
|
||||||
|
|
||||||
# tmp imports for espresso's sumcheck
|
# tmp imports for espresso's sumcheck
|
||||||
|
|||||||
@@ -1,306 +0,0 @@
|
|||||||
use ark_ec::{AffineRepr, CurveGroup};
|
|
||||||
use ark_ff::{BigInteger, PrimeField};
|
|
||||||
use ark_r1cs_std::{
|
|
||||||
alloc::{AllocVar, AllocationMode},
|
|
||||||
boolean::Boolean,
|
|
||||||
fields::{
|
|
||||||
fp::FpVar,
|
|
||||||
nonnative::{
|
|
||||||
params::{get_params, OptimizationType},
|
|
||||||
AllocatedNonNativeFieldVar, NonNativeFieldVar,
|
|
||||||
},
|
|
||||||
FieldVar,
|
|
||||||
},
|
|
||||||
ToBitsGadget, ToConstraintFieldGadget,
|
|
||||||
};
|
|
||||||
use ark_relations::r1cs::{ConstraintSystemRef, Namespace, OptimizationGoal, SynthesisError};
|
|
||||||
use ark_std::Zero;
|
|
||||||
use core::borrow::Borrow;
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
/// Compose a vector boolean into a `NonNativeFieldVar`
|
|
||||||
pub fn nonnative_field_var_from_le_bits<TargetField: PrimeField, BaseField: PrimeField>(
|
|
||||||
cs: ConstraintSystemRef<BaseField>,
|
|
||||||
bits: &[Boolean<BaseField>],
|
|
||||||
) -> Result<NonNativeFieldVar<TargetField, BaseField>, SynthesisError> {
|
|
||||||
let params = get_params(
|
|
||||||
TargetField::MODULUS_BIT_SIZE as usize,
|
|
||||||
BaseField::MODULUS_BIT_SIZE as usize,
|
|
||||||
match cs.optimization_goal() {
|
|
||||||
OptimizationGoal::None => OptimizationType::Constraints,
|
|
||||||
OptimizationGoal::Constraints => OptimizationType::Constraints,
|
|
||||||
OptimizationGoal::Weight => OptimizationType::Weight,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// push the lower limbs first
|
|
||||||
let mut limbs = bits
|
|
||||||
.chunks(params.bits_per_limb)
|
|
||||||
.map(Boolean::le_bits_to_fp_var)
|
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
|
||||||
limbs.resize(params.num_limbs, FpVar::zero());
|
|
||||||
limbs.reverse();
|
|
||||||
|
|
||||||
Ok(AllocatedNonNativeFieldVar {
|
|
||||||
cs,
|
|
||||||
limbs,
|
|
||||||
num_of_additions_over_normal_form: BaseField::one(),
|
|
||||||
is_in_the_normal_form: false,
|
|
||||||
target_phantom: PhantomData,
|
|
||||||
}
|
|
||||||
.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct NonNativeAffineVar<C: CurveGroup>
|
|
||||||
where
|
|
||||||
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
|
|
||||||
{
|
|
||||||
pub x: NonNativeFieldVar<C::BaseField, C::ScalarField>,
|
|
||||||
pub y: NonNativeFieldVar<C::BaseField, C::ScalarField>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C> AllocVar<C, C::ScalarField> for NonNativeAffineVar<C>
|
|
||||||
where
|
|
||||||
C: CurveGroup,
|
|
||||||
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
|
|
||||||
{
|
|
||||||
fn new_variable<T: Borrow<C>>(
|
|
||||||
cs: impl Into<Namespace<C::ScalarField>>,
|
|
||||||
f: impl FnOnce() -> Result<T, SynthesisError>,
|
|
||||||
mode: AllocationMode,
|
|
||||||
) -> Result<Self, SynthesisError> {
|
|
||||||
f().and_then(|val| {
|
|
||||||
let cs = cs.into();
|
|
||||||
|
|
||||||
let affine = val.borrow().into_affine();
|
|
||||||
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(
|
|
||||||
cs.clone(),
|
|
||||||
|| Ok(xy.0),
|
|
||||||
mode,
|
|
||||||
)?;
|
|
||||||
let y = NonNativeFieldVar::<C::BaseField, C::ScalarField>::new_variable(
|
|
||||||
cs.clone(),
|
|
||||||
|| Ok(xy.1),
|
|
||||||
mode,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(Self { x, y })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: CurveGroup> ToConstraintFieldGadget<C::ScalarField> for NonNativeAffineVar<C>
|
|
||||||
where
|
|
||||||
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
|
|
||||||
{
|
|
||||||
// A more efficient version of `point_to_nonnative_limbs_custom_opt`.
|
|
||||||
// Used for converting `NonNativeAffineVar` to a vector of `FpVar` with minimum length in
|
|
||||||
// the circuit.
|
|
||||||
fn to_constraint_field(&self) -> Result<Vec<FpVar<C::ScalarField>>, SynthesisError> {
|
|
||||||
let x = nonnative_field_var_to_constraint_field(&self.x)?;
|
|
||||||
let y = nonnative_field_var_to_constraint_field(&self.y)?;
|
|
||||||
Ok([x, y].concat())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The out-circuit counterpart of `NonNativeAffineVar::to_constraint_field`
|
|
||||||
#[allow(clippy::type_complexity)]
|
|
||||||
pub fn nonnative_affine_to_field_elements<C: CurveGroup>(
|
|
||||||
p: C,
|
|
||||||
) -> Result<(Vec<C::ScalarField>, Vec<C::ScalarField>), SynthesisError>
|
|
||||||
where
|
|
||||||
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
|
|
||||||
{
|
|
||||||
let affine = p.into_affine();
|
|
||||||
if affine.is_zero() {
|
|
||||||
let x = nonnative_field_to_field_elements(&C::BaseField::zero());
|
|
||||||
let y = nonnative_field_to_field_elements(&C::BaseField::zero());
|
|
||||||
return Ok((x, y));
|
|
||||||
}
|
|
||||||
|
|
||||||
let (x, y) = affine.xy().unwrap();
|
|
||||||
let x = nonnative_field_to_field_elements(x);
|
|
||||||
let y = nonnative_field_to_field_elements(y);
|
|
||||||
Ok((x, y))
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C: CurveGroup> NonNativeAffineVar<C>
|
|
||||||
where
|
|
||||||
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
|
|
||||||
{
|
|
||||||
// A wrapper of `point_to_nonnative_limbs_custom_opt` with constraints-focused optimization
|
|
||||||
// type (which is the default optimization type for arkworks' Groth16).
|
|
||||||
// Used for extracting a list of field elements of type `C::ScalarField` from the public input
|
|
||||||
// `p`, in exactly the same way as how `NonNativeAffineVar` is represented as limbs of type
|
|
||||||
// `FpVar` in-circuit.
|
|
||||||
#[allow(clippy::type_complexity)]
|
|
||||||
pub fn inputize(p: C) -> Result<(Vec<C::ScalarField>, Vec<C::ScalarField>), SynthesisError> {
|
|
||||||
point_to_nonnative_limbs_custom_opt(p, OptimizationType::Constraints)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used to compute (outside the circuit) the limbs representation of a point.
|
|
||||||
// For `OptimizationType::Constraints`, the result matches the one used in-circuit.
|
|
||||||
// For `OptimizationType::Weight`, the result vector is more dense and is suitable for hashing.
|
|
||||||
// It is possible to further optimize the length of the result vector (see
|
|
||||||
// `nonnative_affine_to_field_elements`)
|
|
||||||
#[allow(clippy::type_complexity)]
|
|
||||||
fn point_to_nonnative_limbs_custom_opt<C: CurveGroup>(
|
|
||||||
p: C,
|
|
||||||
optimization_type: OptimizationType,
|
|
||||||
) -> Result<(Vec<C::ScalarField>, Vec<C::ScalarField>), SynthesisError>
|
|
||||||
where
|
|
||||||
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
|
|
||||||
{
|
|
||||||
let affine = p.into_affine();
|
|
||||||
if affine.is_zero() {
|
|
||||||
let x =
|
|
||||||
AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
|
|
||||||
&C::BaseField::zero(),
|
|
||||||
optimization_type,
|
|
||||||
)?;
|
|
||||||
let y =
|
|
||||||
AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
|
|
||||||
&C::BaseField::zero(),
|
|
||||||
optimization_type,
|
|
||||||
)?;
|
|
||||||
return Ok((x, y));
|
|
||||||
}
|
|
||||||
|
|
||||||
let (x, y) = affine.xy().unwrap();
|
|
||||||
let x = AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
|
|
||||||
x,
|
|
||||||
optimization_type,
|
|
||||||
)?;
|
|
||||||
let y = AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
|
|
||||||
y,
|
|
||||||
optimization_type,
|
|
||||||
)?;
|
|
||||||
Ok((x, y))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use ark_pallas::{Fr, Projective};
|
|
||||||
use ark_r1cs_std::R1CSVar;
|
|
||||||
use ark_relations::r1cs::ConstraintSystem;
|
|
||||||
use ark_std::UniformRand;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_alloc_zero() {
|
|
||||||
let cs = ConstraintSystem::<Fr>::new_ref();
|
|
||||||
|
|
||||||
// dealing with the 'zero' point should not panic when doing the unwrap
|
|
||||||
let p = Projective::zero();
|
|
||||||
assert!(NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(p)).is_ok());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_arkworks_to_constraint_field() {
|
|
||||||
let cs = ConstraintSystem::<Fr>::new_ref();
|
|
||||||
|
|
||||||
// check that point_to_nonnative_limbs returns the expected values
|
|
||||||
let mut rng = ark_std::test_rng();
|
|
||||||
let p = Projective::rand(&mut rng);
|
|
||||||
let pVar = NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(p)).unwrap();
|
|
||||||
let (x, y) = point_to_nonnative_limbs_custom_opt(p, OptimizationType::Weight).unwrap();
|
|
||||||
assert_eq!(pVar.x.to_constraint_field().unwrap().value().unwrap(), x);
|
|
||||||
assert_eq!(pVar.y.to_constraint_field().unwrap().value().unwrap(), y);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_improved_to_constraint_field() {
|
|
||||||
let cs = ConstraintSystem::<Fr>::new_ref();
|
|
||||||
|
|
||||||
// check that point_to_nonnative_limbs returns the expected values
|
|
||||||
let mut rng = ark_std::test_rng();
|
|
||||||
let p = Projective::rand(&mut rng);
|
|
||||||
let pVar = NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(p)).unwrap();
|
|
||||||
let (x, y) = nonnative_affine_to_field_elements(p).unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
pVar.to_constraint_field().unwrap().value().unwrap(),
|
|
||||||
[x, y].concat()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_inputize() {
|
|
||||||
let cs = ConstraintSystem::<Fr>::new_ref();
|
|
||||||
|
|
||||||
// check that point_to_nonnative_limbs returns the expected values
|
|
||||||
let mut rng = ark_std::test_rng();
|
|
||||||
let p = Projective::rand(&mut rng);
|
|
||||||
let pVar = NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(p)).unwrap();
|
|
||||||
let (x, y) = NonNativeAffineVar::inputize(p).unwrap();
|
|
||||||
|
|
||||||
match (pVar.x, pVar.y) {
|
|
||||||
(NonNativeFieldVar::Var(p_x), NonNativeFieldVar::Var(p_y)) => {
|
|
||||||
assert_eq!(p_x.limbs.value().unwrap(), x);
|
|
||||||
assert_eq!(p_y.limbs.value().unwrap(), y);
|
|
||||||
}
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
161
folding-schemes/src/folding/circuits/nonnative/affine.rs
Normal file
161
folding-schemes/src/folding/circuits/nonnative/affine.rs
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
use ark_ec::{AffineRepr, CurveGroup};
|
||||||
|
use ark_ff::PrimeField;
|
||||||
|
use ark_r1cs_std::{
|
||||||
|
alloc::{AllocVar, AllocationMode},
|
||||||
|
fields::fp::FpVar,
|
||||||
|
ToConstraintFieldGadget,
|
||||||
|
};
|
||||||
|
use ark_relations::r1cs::{Namespace, SynthesisError};
|
||||||
|
use ark_std::Zero;
|
||||||
|
use core::borrow::Borrow;
|
||||||
|
|
||||||
|
use super::uint::{nonnative_field_to_field_elements, NonNativeUintVar};
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct NonNativeAffineVar<C: CurveGroup>
|
||||||
|
where
|
||||||
|
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
|
||||||
|
{
|
||||||
|
pub x: NonNativeUintVar<C::ScalarField>,
|
||||||
|
pub y: NonNativeUintVar<C::ScalarField>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C> AllocVar<C, C::ScalarField> for NonNativeAffineVar<C>
|
||||||
|
where
|
||||||
|
C: CurveGroup,
|
||||||
|
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
|
||||||
|
{
|
||||||
|
fn new_variable<T: Borrow<C>>(
|
||||||
|
cs: impl Into<Namespace<C::ScalarField>>,
|
||||||
|
f: impl FnOnce() -> Result<T, SynthesisError>,
|
||||||
|
mode: AllocationMode,
|
||||||
|
) -> Result<Self, SynthesisError> {
|
||||||
|
f().and_then(|val| {
|
||||||
|
let cs = cs.into();
|
||||||
|
|
||||||
|
let affine = val.borrow().into_affine();
|
||||||
|
let zero_point = (&C::BaseField::zero(), &C::BaseField::zero());
|
||||||
|
let xy = affine.xy().unwrap_or(zero_point);
|
||||||
|
|
||||||
|
let x = NonNativeUintVar::new_variable(cs.clone(), || Ok(*xy.0), mode)?;
|
||||||
|
let y = NonNativeUintVar::new_variable(cs.clone(), || Ok(*xy.1), mode)?;
|
||||||
|
|
||||||
|
Ok(Self { x, y })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: CurveGroup> ToConstraintFieldGadget<C::ScalarField> for NonNativeAffineVar<C>
|
||||||
|
where
|
||||||
|
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
|
||||||
|
{
|
||||||
|
// Used for converting `NonNativeAffineVar` to a vector of `FpVar` with minimum length in
|
||||||
|
// the circuit.
|
||||||
|
fn to_constraint_field(&self) -> Result<Vec<FpVar<C::ScalarField>>, SynthesisError> {
|
||||||
|
let x = self.x.to_constraint_field()?;
|
||||||
|
let y = self.y.to_constraint_field()?;
|
||||||
|
Ok([x, y].concat())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The out-circuit counterpart of `NonNativeAffineVar::to_constraint_field`
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
pub fn nonnative_affine_to_field_elements<C: CurveGroup>(
|
||||||
|
p: C,
|
||||||
|
) -> Result<(Vec<C::ScalarField>, Vec<C::ScalarField>), SynthesisError>
|
||||||
|
where
|
||||||
|
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
|
||||||
|
{
|
||||||
|
let affine = p.into_affine();
|
||||||
|
if affine.is_zero() {
|
||||||
|
let x = nonnative_field_to_field_elements(&C::BaseField::zero());
|
||||||
|
let y = nonnative_field_to_field_elements(&C::BaseField::zero());
|
||||||
|
return Ok((x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (x, y) = affine.xy().unwrap();
|
||||||
|
let x = nonnative_field_to_field_elements(x);
|
||||||
|
let y = nonnative_field_to_field_elements(y);
|
||||||
|
Ok((x, y))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: CurveGroup> NonNativeAffineVar<C>
|
||||||
|
where
|
||||||
|
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
|
||||||
|
{
|
||||||
|
// A wrapper of `point_to_nonnative_limbs_custom_opt` with constraints-focused optimization
|
||||||
|
// type (which is the default optimization type for arkworks' Groth16).
|
||||||
|
// Used for extracting a list of field elements of type `C::ScalarField` from the public input
|
||||||
|
// `p`, in exactly the same way as how `NonNativeAffineVar` is represented as limbs of type
|
||||||
|
// `FpVar` in-circuit.
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
pub fn inputize(p: C) -> Result<(Vec<C::ScalarField>, Vec<C::ScalarField>), SynthesisError> {
|
||||||
|
let affine = p.into_affine();
|
||||||
|
if affine.is_zero() {
|
||||||
|
let x = NonNativeUintVar::inputize(
|
||||||
|
&(C::ScalarField::zero()).into(),
|
||||||
|
C::ScalarField::MODULUS_BIT_SIZE as usize,
|
||||||
|
);
|
||||||
|
let y = NonNativeUintVar::inputize(
|
||||||
|
&(C::ScalarField::zero()).into(),
|
||||||
|
C::ScalarField::MODULUS_BIT_SIZE as usize,
|
||||||
|
);
|
||||||
|
return Ok((x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
let (x, y) = affine.xy().unwrap();
|
||||||
|
let x = NonNativeUintVar::inputize(&(*x).into(), C::ScalarField::MODULUS_BIT_SIZE as usize);
|
||||||
|
let y = NonNativeUintVar::inputize(&(*y).into(), C::ScalarField::MODULUS_BIT_SIZE as usize);
|
||||||
|
Ok((x, y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use ark_pallas::{Fr, Projective};
|
||||||
|
use ark_r1cs_std::R1CSVar;
|
||||||
|
use ark_relations::r1cs::ConstraintSystem;
|
||||||
|
use ark_std::UniformRand;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_alloc_zero() {
|
||||||
|
let cs = ConstraintSystem::<Fr>::new_ref();
|
||||||
|
|
||||||
|
// dealing with the 'zero' point should not panic when doing the unwrap
|
||||||
|
let p = Projective::zero();
|
||||||
|
assert!(NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(p)).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_improved_to_constraint_field() {
|
||||||
|
let cs = ConstraintSystem::<Fr>::new_ref();
|
||||||
|
|
||||||
|
// check that point_to_nonnative_limbs returns the expected values
|
||||||
|
let mut rng = ark_std::test_rng();
|
||||||
|
let p = Projective::rand(&mut rng);
|
||||||
|
let pVar = NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(p)).unwrap();
|
||||||
|
let (x, y) = nonnative_affine_to_field_elements(p).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
pVar.to_constraint_field().unwrap().value().unwrap(),
|
||||||
|
[x, y].concat()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_inputize() {
|
||||||
|
let cs = ConstraintSystem::<Fr>::new_ref();
|
||||||
|
|
||||||
|
// check that point_to_nonnative_limbs returns the expected values
|
||||||
|
let mut rng = ark_std::test_rng();
|
||||||
|
let p = Projective::rand(&mut rng);
|
||||||
|
let pVar = NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(p)).unwrap();
|
||||||
|
let (x, y) = NonNativeAffineVar::inputize(p).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(pVar.x.0.value().unwrap(), x);
|
||||||
|
assert_eq!(pVar.y.0.value().unwrap(), y);
|
||||||
|
}
|
||||||
|
}
|
||||||
2
folding-schemes/src/folding/circuits/nonnative/mod.rs
Normal file
2
folding-schemes/src/folding/circuits/nonnative/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
pub mod affine;
|
||||||
|
pub mod uint;
|
||||||
1019
folding-schemes/src/folding/circuits/nonnative/uint.rs
Normal file
1019
folding-schemes/src/folding/circuits/nonnative/uint.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -14,13 +14,13 @@ use ark_r1cs_std::{
|
|||||||
alloc::{AllocVar, AllocationMode},
|
alloc::{AllocVar, AllocationMode},
|
||||||
boolean::Boolean,
|
boolean::Boolean,
|
||||||
eq::EqGadget,
|
eq::EqGadget,
|
||||||
fields::{fp::FpVar, nonnative::NonNativeFieldVar, FieldVar},
|
fields::{fp::FpVar, FieldVar},
|
||||||
groups::GroupOpsBounds,
|
groups::GroupOpsBounds,
|
||||||
prelude::CurveVar,
|
prelude::CurveVar,
|
||||||
R1CSVar, ToConstraintFieldGadget,
|
R1CSVar, ToConstraintFieldGadget,
|
||||||
};
|
};
|
||||||
use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, Namespace, SynthesisError};
|
use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, Namespace, SynthesisError};
|
||||||
use ark_std::{fmt::Debug, Zero};
|
use ark_std::{fmt::Debug, One, Zero};
|
||||||
use core::{borrow::Borrow, marker::PhantomData};
|
use core::{borrow::Borrow, marker::PhantomData};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@@ -29,9 +29,12 @@ use super::{
|
|||||||
},
|
},
|
||||||
CommittedInstance,
|
CommittedInstance,
|
||||||
};
|
};
|
||||||
use crate::folding::circuits::nonnative::{nonnative_affine_to_field_elements, NonNativeAffineVar};
|
use crate::constants::N_BITS_RO;
|
||||||
|
use crate::folding::circuits::nonnative::{
|
||||||
|
affine::{nonnative_affine_to_field_elements, NonNativeAffineVar},
|
||||||
|
uint::NonNativeUintVar,
|
||||||
|
};
|
||||||
use crate::frontend::FCircuit;
|
use crate::frontend::FCircuit;
|
||||||
use crate::{constants::N_BITS_RO, folding::circuits::nonnative::nonnative_field_var_from_le_bits};
|
|
||||||
|
|
||||||
/// CF1 represents the ConstraintField used for the main Nova circuit which is over E1::Fr, where
|
/// CF1 represents the ConstraintField used for the main Nova circuit which is over E1::Fr, where
|
||||||
/// E1 is the main curve where we do the folding.
|
/// E1 is the main curve where we do the folding.
|
||||||
@@ -402,7 +405,11 @@ where
|
|||||||
)?;
|
)?;
|
||||||
let r = Boolean::le_bits_to_fp_var(&r_bits)?;
|
let r = Boolean::le_bits_to_fp_var(&r_bits)?;
|
||||||
// Also convert r_bits to a `NonNativeFieldVar`
|
// Also convert r_bits to a `NonNativeFieldVar`
|
||||||
let r_nonnat = nonnative_field_var_from_le_bits(cs.clone(), &r_bits)?;
|
let r_nonnat = {
|
||||||
|
let mut bits = r_bits;
|
||||||
|
bits.resize(C1::BaseField::MODULUS_BIT_SIZE as usize, Boolean::FALSE);
|
||||||
|
NonNativeUintVar::from(&bits)
|
||||||
|
};
|
||||||
|
|
||||||
// Notice that NIFSGadget::fold_committed_instance does not fold cmE & cmW.
|
// Notice that NIFSGadget::fold_committed_instance does not fold cmE & cmW.
|
||||||
// We set `U_i1.cmE` and `U_i1.cmW` to unconstrained witnesses `U_i1_cmE` and `U_i1_cmW`
|
// We set `U_i1.cmE` and `U_i1.cmW` to unconstrained witnesses `U_i1_cmE` and `U_i1_cmW`
|
||||||
@@ -452,7 +459,7 @@ where
|
|||||||
// cf1_u_i.cmE = 0
|
// cf1_u_i.cmE = 0
|
||||||
cmE: GC2::zero(),
|
cmE: GC2::zero(),
|
||||||
// cf1_u_i.u = 1
|
// cf1_u_i.u = 1
|
||||||
u: NonNativeFieldVar::one(),
|
u: NonNativeUintVar::new_constant(cs.clone(), C1::BaseField::one())?,
|
||||||
// cf1_u_i.cmW is provided by the prover as witness
|
// cf1_u_i.cmW is provided by the prover as witness
|
||||||
cmW: GC2::new_witness(cs.clone(), || Ok(self.cf1_u_i_cmW.unwrap_or(C2::zero())))?,
|
cmW: GC2::new_witness(cs.clone(), || Ok(self.cf1_u_i_cmW.unwrap_or(C2::zero())))?,
|
||||||
// cf1_u_i.x is computed in step 1
|
// cf1_u_i.x is computed in step 1
|
||||||
@@ -462,7 +469,7 @@ where
|
|||||||
// cf2_u_i.cmE = 0
|
// cf2_u_i.cmE = 0
|
||||||
cmE: GC2::zero(),
|
cmE: GC2::zero(),
|
||||||
// cf2_u_i.u = 1
|
// cf2_u_i.u = 1
|
||||||
u: NonNativeFieldVar::one(),
|
u: NonNativeUintVar::new_constant(cs.clone(), C1::BaseField::one())?,
|
||||||
// cf2_u_i.cmW is provided by the prover as witness
|
// cf2_u_i.cmW is provided by the prover as witness
|
||||||
cmW: GC2::new_witness(cs.clone(), || Ok(self.cf2_u_i_cmW.unwrap_or(C2::zero())))?,
|
cmW: GC2::new_witness(cs.clone(), || Ok(self.cf2_u_i_cmW.unwrap_or(C2::zero())))?,
|
||||||
// cf2_u_i.x is computed in step 1
|
// cf2_u_i.x is computed in step 1
|
||||||
@@ -482,7 +489,11 @@ where
|
|||||||
cf1_cmT.clone(),
|
cf1_cmT.clone(),
|
||||||
)?;
|
)?;
|
||||||
// Convert cf1_r_bits to a `NonNativeFieldVar`
|
// Convert cf1_r_bits to a `NonNativeFieldVar`
|
||||||
let cf1_r_nonnat = nonnative_field_var_from_le_bits(cs.clone(), &cf1_r_bits)?;
|
let cf1_r_nonnat = {
|
||||||
|
let mut bits = cf1_r_bits.clone();
|
||||||
|
bits.resize(C1::BaseField::MODULUS_BIT_SIZE as usize, Boolean::FALSE);
|
||||||
|
NonNativeUintVar::from(&bits)
|
||||||
|
};
|
||||||
// Fold cf1_u_i & cf_U_i into cf1_U_{i+1}
|
// Fold cf1_u_i & cf_U_i into cf1_U_{i+1}
|
||||||
let cf1_U_i1 = NIFSFullGadget::<C2, GC2>::fold_committed_instance(
|
let cf1_U_i1 = NIFSFullGadget::<C2, GC2>::fold_committed_instance(
|
||||||
cf1_r_bits,
|
cf1_r_bits,
|
||||||
@@ -500,7 +511,11 @@ where
|
|||||||
cf2_u_i.clone(),
|
cf2_u_i.clone(),
|
||||||
cf2_cmT.clone(),
|
cf2_cmT.clone(),
|
||||||
)?;
|
)?;
|
||||||
let cf2_r_nonnat = nonnative_field_var_from_le_bits(cs.clone(), &cf2_r_bits)?;
|
let cf2_r_nonnat = {
|
||||||
|
let mut bits = cf2_r_bits.clone();
|
||||||
|
bits.resize(C1::BaseField::MODULUS_BIT_SIZE as usize, Boolean::FALSE);
|
||||||
|
NonNativeUintVar::from(&bits)
|
||||||
|
};
|
||||||
let cf_U_i1 = NIFSFullGadget::<C2, GC2>::fold_committed_instance(
|
let cf_U_i1 = NIFSFullGadget::<C2, GC2>::fold_committed_instance(
|
||||||
cf2_r_bits,
|
cf2_r_bits,
|
||||||
cf2_r_nonnat,
|
cf2_r_nonnat,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use ark_r1cs_std::{
|
|||||||
alloc::{AllocVar, AllocationMode},
|
alloc::{AllocVar, AllocationMode},
|
||||||
boolean::Boolean,
|
boolean::Boolean,
|
||||||
eq::EqGadget,
|
eq::EqGadget,
|
||||||
fields::{fp::FpVar, nonnative::NonNativeFieldVar, FieldVar},
|
fields::{fp::FpVar, FieldVar},
|
||||||
groups::GroupOpsBounds,
|
groups::GroupOpsBounds,
|
||||||
prelude::CurveVar,
|
prelude::CurveVar,
|
||||||
ToConstraintFieldGadget,
|
ToConstraintFieldGadget,
|
||||||
@@ -29,7 +29,7 @@ use core::{borrow::Borrow, marker::PhantomData};
|
|||||||
use super::circuits::CF2;
|
use super::circuits::CF2;
|
||||||
use super::CommittedInstance;
|
use super::CommittedInstance;
|
||||||
use crate::constants::N_BITS_RO;
|
use crate::constants::N_BITS_RO;
|
||||||
use crate::folding::circuits::nonnative::nonnative_field_var_to_constraint_field;
|
use crate::folding::circuits::nonnative::uint::NonNativeUintVar;
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
// public inputs length for the CycleFoldCircuit: |[r, p1.x,y, p2.x,y, p3.x,y]|
|
// public inputs length for the CycleFoldCircuit: |[r, p1.x,y, p2.x,y, p3.x,y]|
|
||||||
@@ -43,9 +43,9 @@ where
|
|||||||
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
||||||
{
|
{
|
||||||
pub cmE: GC,
|
pub cmE: GC,
|
||||||
pub u: NonNativeFieldVar<C::ScalarField, CF2<C>>,
|
pub u: NonNativeUintVar<CF2<C>>,
|
||||||
pub cmW: GC,
|
pub cmW: GC,
|
||||||
pub x: Vec<NonNativeFieldVar<C::ScalarField, CF2<C>>>,
|
pub x: Vec<NonNativeUintVar<CF2<C>>>,
|
||||||
}
|
}
|
||||||
impl<C, GC> AllocVar<CommittedInstance<C>, CF2<C>> for CycleFoldCommittedInstanceVar<C, GC>
|
impl<C, GC> AllocVar<CommittedInstance<C>, CF2<C>> for CycleFoldCommittedInstanceVar<C, GC>
|
||||||
where
|
where
|
||||||
@@ -64,16 +64,8 @@ where
|
|||||||
|
|
||||||
let cmE = GC::new_variable(cs.clone(), || Ok(val.borrow().cmE), mode)?;
|
let cmE = GC::new_variable(cs.clone(), || Ok(val.borrow().cmE), mode)?;
|
||||||
let cmW = GC::new_variable(cs.clone(), || Ok(val.borrow().cmW), mode)?;
|
let cmW = GC::new_variable(cs.clone(), || Ok(val.borrow().cmW), mode)?;
|
||||||
let u = NonNativeFieldVar::<C::ScalarField, CF2<C>>::new_variable(
|
let u = NonNativeUintVar::new_variable(cs.clone(), || Ok(val.borrow().u), mode)?;
|
||||||
cs.clone(),
|
let x = Vec::new_variable(cs.clone(), || Ok(val.borrow().x.clone()), mode)?;
|
||||||
|| Ok(val.borrow().u),
|
|
||||||
mode,
|
|
||||||
)?;
|
|
||||||
let x = Vec::<NonNativeFieldVar<C::ScalarField, CF2<C>>>::new_variable(
|
|
||||||
cs.clone(),
|
|
||||||
|| Ok(val.borrow().x.clone()),
|
|
||||||
mode,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(Self { cmE, u, cmW, x })
|
Ok(Self { cmE, u, cmW, x })
|
||||||
})
|
})
|
||||||
@@ -99,10 +91,10 @@ where
|
|||||||
let is_inf = cmE_is_inf.double()? + cmW_is_inf;
|
let is_inf = cmE_is_inf.double()? + cmW_is_inf;
|
||||||
|
|
||||||
Ok([
|
Ok([
|
||||||
nonnative_field_var_to_constraint_field(&self.u)?,
|
self.u.to_constraint_field()?,
|
||||||
self.x
|
self.x
|
||||||
.iter()
|
.iter()
|
||||||
.map(nonnative_field_var_to_constraint_field)
|
.map(|i| i.to_constraint_field())
|
||||||
.collect::<Result<Vec<_>, _>>()?
|
.collect::<Result<Vec<_>, _>>()?
|
||||||
.concat(),
|
.concat(),
|
||||||
cmE_elems,
|
cmE_elems,
|
||||||
@@ -193,7 +185,7 @@ where
|
|||||||
pub fn fold_committed_instance(
|
pub fn fold_committed_instance(
|
||||||
// assumes that r_bits is equal to r_nonnat just that in a different format
|
// assumes that r_bits is equal to r_nonnat just that in a different format
|
||||||
r_bits: Vec<Boolean<CF2<C>>>,
|
r_bits: Vec<Boolean<CF2<C>>>,
|
||||||
r_nonnat: NonNativeFieldVar<C::ScalarField, CF2<C>>,
|
r_nonnat: NonNativeUintVar<CF2<C>>,
|
||||||
cmT: GC,
|
cmT: GC,
|
||||||
ci1: CycleFoldCommittedInstanceVar<C, GC>,
|
ci1: CycleFoldCommittedInstanceVar<C, GC>,
|
||||||
// ci2 is assumed to be always with cmE=0, u=1 (checks done previous to this method)
|
// ci2 is assumed to be always with cmE=0, u=1 (checks done previous to this method)
|
||||||
@@ -202,20 +194,23 @@ where
|
|||||||
Ok(CycleFoldCommittedInstanceVar {
|
Ok(CycleFoldCommittedInstanceVar {
|
||||||
cmE: cmT.scalar_mul_le(r_bits.iter())? + ci1.cmE,
|
cmE: cmT.scalar_mul_le(r_bits.iter())? + ci1.cmE,
|
||||||
cmW: ci1.cmW + ci2.cmW.scalar_mul_le(r_bits.iter())?,
|
cmW: ci1.cmW + ci2.cmW.scalar_mul_le(r_bits.iter())?,
|
||||||
u: ci1.u + r_nonnat.clone(),
|
u: ci1.u.add_no_align(&r_nonnat).modulo::<C::ScalarField>()?,
|
||||||
x: ci1
|
x: ci1
|
||||||
.x
|
.x
|
||||||
.iter()
|
.iter()
|
||||||
.zip(ci2.x)
|
.zip(ci2.x)
|
||||||
.map(|(a, b)| a + &r_nonnat * &b)
|
.map(|(a, b)| {
|
||||||
.collect::<Vec<_>>(),
|
a.add_no_align(&r_nonnat.mul_no_align(&b)?)
|
||||||
|
.modulo::<C::ScalarField>()
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn verify(
|
pub fn verify(
|
||||||
// assumes that r_bits is equal to r_nonnat just that in a different format
|
// assumes that r_bits is equal to r_nonnat just that in a different format
|
||||||
r_bits: Vec<Boolean<CF2<C>>>,
|
r_bits: Vec<Boolean<CF2<C>>>,
|
||||||
r_nonnat: NonNativeFieldVar<C::ScalarField, CF2<C>>,
|
r_nonnat: NonNativeUintVar<CF2<C>>,
|
||||||
cmT: GC,
|
cmT: GC,
|
||||||
ci1: CycleFoldCommittedInstanceVar<C, GC>,
|
ci1: CycleFoldCommittedInstanceVar<C, GC>,
|
||||||
// ci2 is assumed to be always with cmE=0, u=1 (checks done previous to this method)
|
// ci2 is assumed to be always with cmE=0, u=1 (checks done previous to this method)
|
||||||
@@ -225,9 +220,11 @@ where
|
|||||||
let ci = Self::fold_committed_instance(r_bits, r_nonnat, cmT, ci1, ci2)?;
|
let ci = Self::fold_committed_instance(r_bits, r_nonnat, cmT, ci1, ci2)?;
|
||||||
|
|
||||||
ci.cmE.enforce_equal(&ci3.cmE)?;
|
ci.cmE.enforce_equal(&ci3.cmE)?;
|
||||||
ci.u.enforce_equal(&ci3.u)?;
|
ci.u.enforce_equal_unaligned(&ci3.u)?;
|
||||||
ci.cmW.enforce_equal(&ci3.cmW)?;
|
ci.cmW.enforce_equal(&ci3.cmW)?;
|
||||||
ci.x.enforce_equal(&ci3.x)?;
|
for (x, y) in ci.x.iter().zip(ci3.x.iter()) {
|
||||||
|
x.enforce_equal_unaligned(y)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -316,7 +313,6 @@ pub struct CycleFoldCircuit<C: CurveGroup, GC: CurveVar<C, CF2<C>>> {
|
|||||||
pub r_bits: Option<Vec<bool>>,
|
pub r_bits: Option<Vec<bool>>,
|
||||||
pub p1: Option<C>,
|
pub p1: Option<C>,
|
||||||
pub p2: Option<C>,
|
pub p2: Option<C>,
|
||||||
pub p3: Option<C>,
|
|
||||||
pub x: Option<Vec<CF2<C>>>, // public inputs (cf_u_{i+1}.x)
|
pub x: Option<Vec<CF2<C>>>, // public inputs (cf_u_{i+1}.x)
|
||||||
}
|
}
|
||||||
impl<C: CurveGroup, GC: CurveVar<C, CF2<C>>> CycleFoldCircuit<C, GC> {
|
impl<C: CurveGroup, GC: CurveVar<C, CF2<C>>> CycleFoldCircuit<C, GC> {
|
||||||
@@ -326,7 +322,6 @@ impl<C: CurveGroup, GC: CurveVar<C, CF2<C>>> CycleFoldCircuit<C, GC> {
|
|||||||
r_bits: None,
|
r_bits: None,
|
||||||
p1: None,
|
p1: None,
|
||||||
p2: None,
|
p2: None,
|
||||||
p3: None,
|
|
||||||
x: None,
|
x: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -344,7 +339,11 @@ where
|
|||||||
})?;
|
})?;
|
||||||
let p1 = GC::new_witness(cs.clone(), || Ok(self.p1.unwrap_or(C::zero())))?;
|
let p1 = GC::new_witness(cs.clone(), || Ok(self.p1.unwrap_or(C::zero())))?;
|
||||||
let p2 = GC::new_witness(cs.clone(), || Ok(self.p2.unwrap_or(C::zero())))?;
|
let p2 = GC::new_witness(cs.clone(), || Ok(self.p2.unwrap_or(C::zero())))?;
|
||||||
let p3 = GC::new_witness(cs.clone(), || Ok(self.p3.unwrap_or(C::zero())))?;
|
// Fold the original Nova instances natively in CycleFold
|
||||||
|
// 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
|
||||||
|
// is assumed to be 0, so, U_i1.cmE = U_i.cmE + r * cmT
|
||||||
|
let p3 = &p1 + p2.scalar_mul_le(r_bits.iter())?;
|
||||||
|
|
||||||
let x = Vec::<FpVar<CF2<C>>>::new_input(cs.clone(), || {
|
let x = Vec::<FpVar<CF2<C>>>::new_input(cs.clone(), || {
|
||||||
Ok(self.x.unwrap_or(vec![CF2::<C>::zero(); CF_IO_LEN]))
|
Ok(self.x.unwrap_or(vec![CF2::<C>::zero(); CF_IO_LEN]))
|
||||||
@@ -356,19 +355,13 @@ where
|
|||||||
let r: FpVar<CF2<C>> = Boolean::le_bits_to_fp_var(&r_bits)?;
|
let r: FpVar<CF2<C>> = Boolean::le_bits_to_fp_var(&r_bits)?;
|
||||||
let points_coords: Vec<FpVar<CF2<C>>> = [
|
let points_coords: Vec<FpVar<CF2<C>>> = [
|
||||||
vec![r],
|
vec![r],
|
||||||
p1.clone().to_constraint_field()?[..2].to_vec(),
|
p1.to_constraint_field()?[..2].to_vec(),
|
||||||
p2.clone().to_constraint_field()?[..2].to_vec(),
|
p2.to_constraint_field()?[..2].to_vec(),
|
||||||
p3.clone().to_constraint_field()?[..2].to_vec(),
|
p3.to_constraint_field()?[..2].to_vec(),
|
||||||
]
|
]
|
||||||
.concat();
|
.concat();
|
||||||
points_coords.enforce_equal(&x)?;
|
points_coords.enforce_equal(&x)?;
|
||||||
|
|
||||||
// Fold the original Nova instances natively in CycleFold
|
|
||||||
// For the cmW we're checking: U_i1.cmW == U_i.cmW + r * u_i.cmW
|
|
||||||
// For the cmE we're checking: 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
|
|
||||||
p3.enforce_equal(&(p1 + p2.scalar_mul_le(r_bits.iter())?))?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -429,7 +422,6 @@ pub mod tests {
|
|||||||
r_bits: Some(r_bits.clone()),
|
r_bits: Some(r_bits.clone()),
|
||||||
p1: Some(ci1.clone().cmW),
|
p1: Some(ci1.clone().cmW),
|
||||||
p2: Some(ci2.clone().cmW),
|
p2: Some(ci2.clone().cmW),
|
||||||
p3: Some(ci3.clone().cmW),
|
|
||||||
x: Some(cfW_u_i_x.clone()),
|
x: Some(cfW_u_i_x.clone()),
|
||||||
};
|
};
|
||||||
cfW_circuit.generate_constraints(cs.clone()).unwrap();
|
cfW_circuit.generate_constraints(cs.clone()).unwrap();
|
||||||
@@ -449,7 +441,6 @@ pub mod tests {
|
|||||||
r_bits: Some(r_bits.clone()),
|
r_bits: Some(r_bits.clone()),
|
||||||
p1: Some(ci1.clone().cmE),
|
p1: Some(ci1.clone().cmE),
|
||||||
p2: Some(cmT),
|
p2: Some(cmT),
|
||||||
p3: Some(ci3.clone().cmE),
|
|
||||||
x: Some(cfE_u_i_x.clone()),
|
x: Some(cfE_u_i_x.clone()),
|
||||||
};
|
};
|
||||||
cfE_circuit.generate_constraints(cs.clone()).unwrap();
|
cfE_circuit.generate_constraints(cs.clone()).unwrap();
|
||||||
@@ -462,8 +453,7 @@ pub mod tests {
|
|||||||
|
|
||||||
let cs = ConstraintSystem::<Fq>::new_ref();
|
let cs = ConstraintSystem::<Fq>::new_ref();
|
||||||
|
|
||||||
let r_nonnatVar =
|
let r_nonnatVar = NonNativeUintVar::<Fq>::new_witness(cs.clone(), || Ok(r_Fr)).unwrap();
|
||||||
NonNativeFieldVar::<Fr, Fq>::new_witness(cs.clone(), || Ok(r_Fr)).unwrap();
|
|
||||||
let r_bitsVar = Vec::<Boolean<Fq>>::new_witness(cs.clone(), || Ok(r_bits)).unwrap();
|
let r_bitsVar = Vec::<Boolean<Fq>>::new_witness(cs.clone(), || Ok(r_bits)).unwrap();
|
||||||
|
|
||||||
let ci1Var =
|
let ci1Var =
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ use super::{circuits::CF2, nifs::NIFS, CommittedInstance, Nova};
|
|||||||
use crate::commitment::{
|
use crate::commitment::{
|
||||||
kzg::Proof as KZGProof, pedersen::Params as PedersenParams, CommitmentScheme,
|
kzg::Proof as KZGProof, pedersen::Params as PedersenParams, CommitmentScheme,
|
||||||
};
|
};
|
||||||
use crate::folding::circuits::nonnative::NonNativeAffineVar;
|
use crate::folding::circuits::nonnative::affine::NonNativeAffineVar;
|
||||||
use crate::frontend::FCircuit;
|
use crate::frontend::FCircuit;
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
use crate::{Decider as DeciderTrait, FoldingScheme};
|
use crate::{Decider as DeciderTrait, FoldingScheme};
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use ark_r1cs_std::{
|
|||||||
alloc::{AllocVar, AllocationMode},
|
alloc::{AllocVar, AllocationMode},
|
||||||
boolean::Boolean,
|
boolean::Boolean,
|
||||||
eq::EqGadget,
|
eq::EqGadget,
|
||||||
fields::{fp::FpVar, nonnative::NonNativeFieldVar, FieldVar},
|
fields::{fp::FpVar, FieldVar},
|
||||||
groups::GroupOpsBounds,
|
groups::GroupOpsBounds,
|
||||||
poly::{domain::Radix2DomainVar, evaluations::univariate::EvaluationsVar},
|
poly::{domain::Radix2DomainVar, evaluations::univariate::EvaluationsVar},
|
||||||
prelude::CurveVar,
|
prelude::CurveVar,
|
||||||
@@ -22,7 +22,10 @@ use core::{borrow::Borrow, marker::PhantomData};
|
|||||||
use super::{circuits::ChallengeGadget, nifs::NIFS};
|
use super::{circuits::ChallengeGadget, nifs::NIFS};
|
||||||
use crate::ccs::r1cs::R1CS;
|
use crate::ccs::r1cs::R1CS;
|
||||||
use crate::commitment::{pedersen::Params as PedersenParams, CommitmentScheme};
|
use crate::commitment::{pedersen::Params as PedersenParams, CommitmentScheme};
|
||||||
use crate::folding::circuits::nonnative::{nonnative_affine_to_field_elements, NonNativeAffineVar};
|
use crate::folding::circuits::nonnative::{
|
||||||
|
affine::{nonnative_affine_to_field_elements, NonNativeAffineVar},
|
||||||
|
uint::NonNativeUintVar,
|
||||||
|
};
|
||||||
use crate::folding::nova::{
|
use crate::folding::nova::{
|
||||||
circuits::{CommittedInstanceVar, CF1, CF2},
|
circuits::{CommittedInstanceVar, CF1, CF2},
|
||||||
CommittedInstance, Nova, Witness,
|
CommittedInstance, Nova, Witness,
|
||||||
@@ -33,40 +36,54 @@ use crate::transcript::{
|
|||||||
Transcript, TranscriptVar,
|
Transcript, TranscriptVar,
|
||||||
};
|
};
|
||||||
use crate::utils::{
|
use crate::utils::{
|
||||||
gadgets::{hadamard, mat_vec_mul_sparse, vec_add, vec_scalar_mul, SparseMatrixVar},
|
gadgets::{MatrixGadget, SparseMatrixVar, VectorGadget},
|
||||||
vec::poly_from_vec,
|
vec::poly_from_vec,
|
||||||
};
|
};
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct RelaxedR1CSGadget<F: PrimeField, CF: PrimeField, FV: FieldVar<F, CF>> {
|
pub struct RelaxedR1CSGadget {}
|
||||||
_f: PhantomData<F>,
|
impl RelaxedR1CSGadget {
|
||||||
_cf: PhantomData<CF>,
|
/// performs the RelaxedR1CS check for native variables (Az∘Bz==uCz+E)
|
||||||
_fv: PhantomData<FV>,
|
pub fn check_native<F: PrimeField>(
|
||||||
}
|
r1cs: R1CSVar<F, F, FpVar<F>>,
|
||||||
impl<F: PrimeField, CF: PrimeField, FV: FieldVar<F, CF>> RelaxedR1CSGadget<F, CF, FV> {
|
E: Vec<FpVar<F>>,
|
||||||
/// performs the RelaxedR1CS check (Az∘Bz==uCz+E)
|
u: FpVar<F>,
|
||||||
pub fn check(
|
z: Vec<FpVar<F>>,
|
||||||
r1cs: R1CSVar<F, CF, FV>,
|
|
||||||
E: Vec<FV>,
|
|
||||||
u: FV,
|
|
||||||
z: Vec<FV>,
|
|
||||||
) -> Result<(), SynthesisError> {
|
) -> Result<(), SynthesisError> {
|
||||||
let Az = mat_vec_mul_sparse(r1cs.A, z.clone());
|
let Az = r1cs.A.mul_vector(&z)?;
|
||||||
let Bz = mat_vec_mul_sparse(r1cs.B, z.clone());
|
let Bz = r1cs.B.mul_vector(&z)?;
|
||||||
let Cz = mat_vec_mul_sparse(r1cs.C, z.clone());
|
let Cz = r1cs.C.mul_vector(&z)?;
|
||||||
let uCz = vec_scalar_mul(&Cz, &u);
|
let uCzE = Cz.mul_scalar(&u)?.add(&E)?;
|
||||||
let uCzE = vec_add(&uCz, &E)?;
|
let AzBz = Az.hadamard(&Bz)?;
|
||||||
let AzBz = hadamard(&Az, &Bz)?;
|
AzBz.enforce_equal(&uCzE)?;
|
||||||
for i in 0..AzBz.len() {
|
|
||||||
AzBz[i].enforce_equal(&uCzE[i].clone())?;
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// performs the RelaxedR1CS check for non-native variables (Az∘Bz==uCz+E)
|
||||||
|
pub fn check_nonnative<F: PrimeField, CF: PrimeField>(
|
||||||
|
r1cs: R1CSVar<F, CF, NonNativeUintVar<CF>>,
|
||||||
|
E: Vec<NonNativeUintVar<CF>>,
|
||||||
|
u: NonNativeUintVar<CF>,
|
||||||
|
z: Vec<NonNativeUintVar<CF>>,
|
||||||
|
) -> Result<(), SynthesisError> {
|
||||||
|
// First we do addition and multiplication without mod F's order
|
||||||
|
let Az = r1cs.A.mul_vector(&z)?;
|
||||||
|
let Bz = r1cs.B.mul_vector(&z)?;
|
||||||
|
let Cz = r1cs.C.mul_vector(&z)?;
|
||||||
|
let uCzE = Cz.mul_scalar(&u)?.add(&E)?;
|
||||||
|
let AzBz = Az.hadamard(&Bz)?;
|
||||||
|
|
||||||
|
// Then we compare the results by checking if they are congruent
|
||||||
|
// modulo the field order
|
||||||
|
AzBz.into_iter()
|
||||||
|
.zip(uCzE)
|
||||||
|
.try_for_each(|(a, b)| a.enforce_congruent::<F>(&b))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct R1CSVar<F: PrimeField, CF: PrimeField, FV: FieldVar<F, CF>> {
|
pub struct R1CSVar<F: PrimeField, CF: PrimeField, FV: AllocVar<F, CF>> {
|
||||||
_f: PhantomData<F>,
|
_f: PhantomData<F>,
|
||||||
_cf: PhantomData<CF>,
|
_cf: PhantomData<CF>,
|
||||||
_fv: PhantomData<FV>,
|
_fv: PhantomData<FV>,
|
||||||
@@ -79,7 +96,7 @@ impl<F, CF, FV> AllocVar<R1CS<F>, CF> for R1CSVar<F, CF, FV>
|
|||||||
where
|
where
|
||||||
F: PrimeField,
|
F: PrimeField,
|
||||||
CF: PrimeField,
|
CF: PrimeField,
|
||||||
FV: FieldVar<F, CF>,
|
FV: AllocVar<F, CF>,
|
||||||
{
|
{
|
||||||
fn new_variable<T: Borrow<R1CS<F>>>(
|
fn new_variable<T: Borrow<R1CS<F>>>(
|
||||||
cs: impl Into<Namespace<CF>>,
|
cs: impl Into<Namespace<CF>>,
|
||||||
@@ -146,10 +163,10 @@ where
|
|||||||
/// non-native representation, since it is used to represent the CycleFold witness.
|
/// non-native representation, since it is used to represent the CycleFold witness.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct CycleFoldWitnessVar<C: CurveGroup> {
|
pub struct CycleFoldWitnessVar<C: CurveGroup> {
|
||||||
pub E: Vec<NonNativeFieldVar<C::ScalarField, CF2<C>>>,
|
pub E: Vec<NonNativeUintVar<CF2<C>>>,
|
||||||
pub rE: NonNativeFieldVar<C::ScalarField, CF2<C>>,
|
pub rE: NonNativeUintVar<CF2<C>>,
|
||||||
pub W: Vec<NonNativeFieldVar<C::ScalarField, CF2<C>>>,
|
pub W: Vec<NonNativeUintVar<CF2<C>>>,
|
||||||
pub rW: NonNativeFieldVar<C::ScalarField, CF2<C>>,
|
pub rW: NonNativeUintVar<CF2<C>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C> AllocVar<Witness<C>, CF2<C>> for CycleFoldWitnessVar<C>
|
impl<C> AllocVar<Witness<C>, CF2<C>> for CycleFoldWitnessVar<C>
|
||||||
@@ -165,21 +182,11 @@ where
|
|||||||
f().and_then(|val| {
|
f().and_then(|val| {
|
||||||
let cs = cs.into();
|
let cs = cs.into();
|
||||||
|
|
||||||
let E: Vec<NonNativeFieldVar<C::ScalarField, CF2<C>>> =
|
let E = Vec::new_variable(cs.clone(), || Ok(val.borrow().E.clone()), mode)?;
|
||||||
Vec::new_variable(cs.clone(), || Ok(val.borrow().E.clone()), mode)?;
|
let rE = NonNativeUintVar::new_variable(cs.clone(), || Ok(val.borrow().rE), mode)?;
|
||||||
let rE = NonNativeFieldVar::<C::ScalarField, CF2<C>>::new_variable(
|
|
||||||
cs.clone(),
|
|
||||||
|| Ok(val.borrow().rE),
|
|
||||||
mode,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let W: Vec<NonNativeFieldVar<C::ScalarField, CF2<C>>> =
|
let W = Vec::new_variable(cs.clone(), || Ok(val.borrow().W.clone()), mode)?;
|
||||||
Vec::new_variable(cs.clone(), || Ok(val.borrow().W.clone()), mode)?;
|
let rW = NonNativeUintVar::new_variable(cs.clone(), || Ok(val.borrow().rW), mode)?;
|
||||||
let rW = NonNativeFieldVar::<C::ScalarField, CF2<C>>::new_variable(
|
|
||||||
cs.clone(),
|
|
||||||
|| Ok(val.borrow().rW),
|
|
||||||
mode,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(Self { E, rE, W, rW })
|
Ok(Self { E, rE, W, rW })
|
||||||
})
|
})
|
||||||
@@ -404,18 +411,13 @@ where
|
|||||||
// 1. check RelaxedR1CS of U_{i+1}
|
// 1. check RelaxedR1CS of U_{i+1}
|
||||||
let z_U1: Vec<FpVar<CF1<C1>>> =
|
let z_U1: Vec<FpVar<CF1<C1>>> =
|
||||||
[vec![U_i1.u.clone()], U_i1.x.to_vec(), W_i1.W.to_vec()].concat();
|
[vec![U_i1.u.clone()], U_i1.x.to_vec(), W_i1.W.to_vec()].concat();
|
||||||
RelaxedR1CSGadget::<C1::ScalarField, CF1<C1>, FpVar<CF1<C1>>>::check(
|
RelaxedR1CSGadget::check_native(r1cs, W_i1.E.clone(), U_i1.u.clone(), z_U1)?;
|
||||||
r1cs,
|
|
||||||
W_i1.E.clone(),
|
|
||||||
U_i1.u.clone(),
|
|
||||||
z_U1,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// 2. u_i.cmE==cm(0), u_i.u==1
|
// 2. u_i.cmE==cm(0), u_i.u==1
|
||||||
// Here zero is the x & y coordinates of the zero point affine representation.
|
// Here zero is the x & y coordinates of the zero point affine representation.
|
||||||
let zero = NonNativeFieldVar::<C1::BaseField, C1::ScalarField>::zero();
|
let zero = NonNativeUintVar::new_constant(cs.clone(), C1::BaseField::zero())?;
|
||||||
u_i.cmE.x.enforce_equal(&zero)?;
|
u_i.cmE.x.enforce_equal_unaligned(&zero)?;
|
||||||
u_i.cmE.y.enforce_equal(&zero)?;
|
u_i.cmE.y.enforce_equal_unaligned(&zero)?;
|
||||||
(u_i.u.is_one()?).enforce_equal(&Boolean::TRUE)?;
|
(u_i.u.is_one()?).enforce_equal(&Boolean::TRUE)?;
|
||||||
|
|
||||||
// 3.a u_i.x[0] == H(i, z_0, z_i, U_i)
|
// 3.a u_i.x[0] == H(i, z_0, z_i, U_i)
|
||||||
@@ -470,20 +472,15 @@ where
|
|||||||
PedersenGadget::<C2, GC2>::commit(H, G, cf_W_i_W_bits?, cf_W_i.rW.to_bits_le()?)?;
|
PedersenGadget::<C2, GC2>::commit(H, G, cf_W_i_W_bits?, cf_W_i.rW.to_bits_le()?)?;
|
||||||
cf_U_i.cmW.enforce_equal(&computed_cmW)?;
|
cf_U_i.cmW.enforce_equal(&computed_cmW)?;
|
||||||
|
|
||||||
let cf_r1cs = R1CSVar::<
|
let cf_r1cs =
|
||||||
C1::BaseField,
|
R1CSVar::<C1::BaseField, CF1<C1>, NonNativeUintVar<CF1<C1>>>::new_witness(
|
||||||
CF1<C1>,
|
cs.clone(),
|
||||||
NonNativeFieldVar<C1::BaseField, CF1<C1>>,
|
|| Ok(self.cf_r1cs.clone()),
|
||||||
>::new_witness(cs.clone(), || Ok(self.cf_r1cs.clone()))?;
|
)?;
|
||||||
|
|
||||||
// 5. check RelaxedR1CS of cf_U_i
|
// 5. check RelaxedR1CS of cf_U_i
|
||||||
let cf_z_U: Vec<NonNativeFieldVar<C2::ScalarField, CF1<C1>>> =
|
let cf_z_U = [vec![cf_U_i.u.clone()], cf_U_i.x.to_vec(), cf_W_i.W.to_vec()].concat();
|
||||||
[vec![cf_U_i.u.clone()], cf_U_i.x.to_vec(), cf_W_i.W.to_vec()].concat();
|
RelaxedR1CSGadget::check_nonnative(cf_r1cs, cf_W_i.E, cf_U_i.u.clone(), cf_z_U)?;
|
||||||
RelaxedR1CSGadget::<
|
|
||||||
C2::ScalarField,
|
|
||||||
CF1<C1>,
|
|
||||||
NonNativeFieldVar<C2::ScalarField, CF1<C1>>,
|
|
||||||
>::check(cf_r1cs, cf_W_i.E, cf_U_i.u.clone(), cf_z_U)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. check KZG challenges
|
// 6. check KZG challenges
|
||||||
@@ -632,7 +629,7 @@ pub mod tests {
|
|||||||
let uVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(rel_r1cs.u)).unwrap();
|
let uVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(rel_r1cs.u)).unwrap();
|
||||||
let r1csVar = R1CSVar::<Fr, Fr, FpVar<Fr>>::new_witness(cs.clone(), || Ok(r1cs)).unwrap();
|
let r1csVar = R1CSVar::<Fr, Fr, FpVar<Fr>>::new_witness(cs.clone(), || Ok(r1cs)).unwrap();
|
||||||
|
|
||||||
RelaxedR1CSGadget::<Fr, Fr, FpVar<Fr>>::check(r1csVar, EVar, uVar, zVar).unwrap();
|
RelaxedR1CSGadget::check_native(r1csVar, EVar, uVar, zVar).unwrap();
|
||||||
assert!(cs.is_satisfied().unwrap());
|
assert!(cs.is_satisfied().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -663,7 +660,7 @@ pub mod tests {
|
|||||||
let uVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(relaxed_r1cs.u)).unwrap();
|
let uVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(relaxed_r1cs.u)).unwrap();
|
||||||
let r1csVar = R1CSVar::<Fr, Fr, FpVar<Fr>>::new_witness(cs.clone(), || Ok(r1cs)).unwrap();
|
let r1csVar = R1CSVar::<Fr, Fr, FpVar<Fr>>::new_witness(cs.clone(), || Ok(r1cs)).unwrap();
|
||||||
|
|
||||||
RelaxedR1CSGadget::<Fr, Fr, FpVar<Fr>>::check(r1csVar, EVar, uVar, zVar).unwrap();
|
RelaxedR1CSGadget::check_native(r1csVar, EVar, uVar, zVar).unwrap();
|
||||||
assert!(cs.is_satisfied().unwrap());
|
assert!(cs.is_satisfied().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -752,20 +749,16 @@ pub mod tests {
|
|||||||
let uVar = FpVar::<Fq>::new_witness(cs.clone(), || Ok(relaxed_r1cs.u)).unwrap();
|
let uVar = FpVar::<Fq>::new_witness(cs.clone(), || Ok(relaxed_r1cs.u)).unwrap();
|
||||||
let r1csVar =
|
let r1csVar =
|
||||||
R1CSVar::<Fq, Fq, FpVar<Fq>>::new_witness(cs.clone(), || Ok(r1cs.clone())).unwrap();
|
R1CSVar::<Fq, Fq, FpVar<Fq>>::new_witness(cs.clone(), || Ok(r1cs.clone())).unwrap();
|
||||||
RelaxedR1CSGadget::<Fq, Fq, FpVar<Fq>>::check(r1csVar, EVar, uVar, zVar).unwrap();
|
RelaxedR1CSGadget::check_native(r1csVar, EVar, uVar, zVar).unwrap();
|
||||||
|
|
||||||
// non-natively
|
// non-natively
|
||||||
let cs = ConstraintSystem::<Fr>::new_ref();
|
let cs = ConstraintSystem::<Fr>::new_ref();
|
||||||
let zVar = Vec::<NonNativeFieldVar<Fq, Fr>>::new_witness(cs.clone(), || Ok(z)).unwrap();
|
let zVar = Vec::new_witness(cs.clone(), || Ok(z)).unwrap();
|
||||||
let EVar = Vec::<NonNativeFieldVar<Fq, Fr>>::new_witness(cs.clone(), || Ok(relaxed_r1cs.E))
|
let EVar = Vec::new_witness(cs.clone(), || Ok(relaxed_r1cs.E)).unwrap();
|
||||||
.unwrap();
|
let uVar = NonNativeUintVar::<Fr>::new_witness(cs.clone(), || Ok(relaxed_r1cs.u)).unwrap();
|
||||||
let uVar =
|
|
||||||
NonNativeFieldVar::<Fq, Fr>::new_witness(cs.clone(), || Ok(relaxed_r1cs.u)).unwrap();
|
|
||||||
let r1csVar =
|
let r1csVar =
|
||||||
R1CSVar::<Fq, Fr, NonNativeFieldVar<Fq, Fr>>::new_witness(cs.clone(), || Ok(r1cs))
|
R1CSVar::<Fq, Fr, NonNativeUintVar<Fr>>::new_witness(cs.clone(), || Ok(r1cs)).unwrap();
|
||||||
.unwrap();
|
RelaxedR1CSGadget::check_nonnative(r1csVar, EVar, uVar, zVar).unwrap();
|
||||||
RelaxedR1CSGadget::<Fq, Fr, NonNativeFieldVar<Fq, Fr>>::check(r1csVar, EVar, uVar, zVar)
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem};
|
|||||||
use crate::ccs::r1cs::{extract_r1cs, extract_w_x, R1CS};
|
use crate::ccs::r1cs::{extract_r1cs, extract_w_x, R1CS};
|
||||||
use crate::commitment::CommitmentScheme;
|
use crate::commitment::CommitmentScheme;
|
||||||
use crate::folding::circuits::nonnative::{
|
use crate::folding::circuits::nonnative::{
|
||||||
nonnative_affine_to_field_elements, nonnative_field_to_field_elements,
|
affine::nonnative_affine_to_field_elements, uint::nonnative_field_to_field_elements,
|
||||||
};
|
};
|
||||||
use crate::frontend::FCircuit;
|
use crate::frontend::FCircuit;
|
||||||
use crate::utils::vec::is_zero_vec;
|
use crate::utils::vec::is_zero_vec;
|
||||||
@@ -434,7 +434,6 @@ where
|
|||||||
r_bits: Some(r_bits.clone()),
|
r_bits: Some(r_bits.clone()),
|
||||||
p1: Some(self.U_i.clone().cmW),
|
p1: Some(self.U_i.clone().cmW),
|
||||||
p2: Some(self.u_i.clone().cmW),
|
p2: Some(self.u_i.clone().cmW),
|
||||||
p3: Some(U_i1.clone().cmW),
|
|
||||||
x: Some(cfW_u_i_x.clone()),
|
x: Some(cfW_u_i_x.clone()),
|
||||||
};
|
};
|
||||||
let cfE_circuit = CycleFoldCircuit::<C1, GC1> {
|
let cfE_circuit = CycleFoldCircuit::<C1, GC1> {
|
||||||
@@ -442,7 +441,6 @@ where
|
|||||||
r_bits: Some(r_bits.clone()),
|
r_bits: Some(r_bits.clone()),
|
||||||
p1: Some(self.U_i.clone().cmE),
|
p1: Some(self.U_i.clone().cmE),
|
||||||
p2: Some(cmT),
|
p2: Some(cmT),
|
||||||
p3: Some(U_i1.clone().cmE),
|
|
||||||
x: Some(cfE_u_i_x.clone()),
|
x: Some(cfE_u_i_x.clone()),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -482,8 +480,8 @@ where
|
|||||||
cf_x: Some(cf_u_i1_x),
|
cf_x: Some(cf_u_i1_x),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.cf_W_i = cf_W_i1.clone();
|
self.cf_W_i = cf_W_i1;
|
||||||
self.cf_U_i = cf_U_i1.clone();
|
self.cf_U_i = cf_U_i1;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,69 +1,48 @@
|
|||||||
use ark_ff::PrimeField;
|
use ark_ff::PrimeField;
|
||||||
use ark_r1cs_std::{
|
use ark_r1cs_std::{
|
||||||
alloc::{AllocVar, AllocationMode},
|
alloc::{AllocVar, AllocationMode},
|
||||||
fields::FieldVar,
|
fields::{fp::FpVar, FieldVar},
|
||||||
|
R1CSVar,
|
||||||
};
|
};
|
||||||
use ark_relations::r1cs::{Namespace, SynthesisError};
|
use ark_relations::r1cs::{Namespace, SynthesisError};
|
||||||
use core::{borrow::Borrow, marker::PhantomData};
|
use core::{borrow::Borrow, marker::PhantomData};
|
||||||
|
|
||||||
use crate::utils::vec::SparseMatrix;
|
use crate::utils::vec::SparseMatrix;
|
||||||
|
|
||||||
pub fn mat_vec_mul_sparse<F: PrimeField, CF: PrimeField, FV: FieldVar<F, CF>>(
|
pub trait MatrixGadget<FV> {
|
||||||
m: SparseMatrixVar<F, CF, FV>,
|
fn mul_vector(&self, v: &[FV]) -> Result<Vec<FV>, SynthesisError>;
|
||||||
v: Vec<FV>,
|
}
|
||||||
) -> Vec<FV> {
|
|
||||||
let mut res = vec![FV::zero(); m.n_rows];
|
pub trait VectorGadget<FV> {
|
||||||
for (row_i, row) in m.coeffs.iter().enumerate() {
|
fn add(&self, other: &Self) -> Result<Vec<FV>, SynthesisError>;
|
||||||
for (value, col_i) in row.iter() {
|
|
||||||
if value.value().unwrap() == F::one() {
|
fn mul_scalar(&self, other: &FV) -> Result<Vec<FV>, SynthesisError>;
|
||||||
// no need to multiply by 1
|
|
||||||
res[row_i] += v[*col_i].clone();
|
fn hadamard(&self, other: &Self) -> Result<Vec<FV>, SynthesisError>;
|
||||||
continue;
|
}
|
||||||
}
|
|
||||||
res[row_i] += value.clone().mul(&v[*col_i].clone());
|
impl<F: PrimeField> VectorGadget<FpVar<F>> for [FpVar<F>] {
|
||||||
|
fn add(&self, other: &Self) -> Result<Vec<FpVar<F>>, SynthesisError> {
|
||||||
|
if self.len() != other.len() {
|
||||||
|
return Err(SynthesisError::Unsatisfiable);
|
||||||
}
|
}
|
||||||
|
Ok(self.iter().zip(other.iter()).map(|(a, b)| a + b).collect())
|
||||||
}
|
}
|
||||||
res
|
|
||||||
}
|
fn mul_scalar(&self, c: &FpVar<F>) -> Result<Vec<FpVar<F>>, SynthesisError> {
|
||||||
pub fn vec_add<F: PrimeField, CF: PrimeField, FV: FieldVar<F, CF>>(
|
Ok(self.iter().map(|a| a * c).collect())
|
||||||
a: &Vec<FV>,
|
|
||||||
b: &Vec<FV>,
|
|
||||||
) -> Result<Vec<FV>, SynthesisError> {
|
|
||||||
if a.len() != b.len() {
|
|
||||||
return Err(SynthesisError::Unsatisfiable);
|
|
||||||
}
|
}
|
||||||
let mut r: Vec<FV> = vec![FV::zero(); a.len()];
|
|
||||||
for i in 0..a.len() {
|
fn hadamard(&self, other: &Self) -> Result<Vec<FpVar<F>>, SynthesisError> {
|
||||||
r[i] = a[i].clone() + b[i].clone();
|
if self.len() != other.len() {
|
||||||
|
return Err(SynthesisError::Unsatisfiable);
|
||||||
|
}
|
||||||
|
Ok(self.iter().zip(other.iter()).map(|(a, b)| a * b).collect())
|
||||||
}
|
}
|
||||||
Ok(r)
|
|
||||||
}
|
|
||||||
pub fn vec_scalar_mul<F: PrimeField, CF: PrimeField, FV: FieldVar<F, CF>>(
|
|
||||||
vec: &Vec<FV>,
|
|
||||||
c: &FV,
|
|
||||||
) -> Vec<FV> {
|
|
||||||
let mut result = vec![FV::zero(); vec.len()];
|
|
||||||
for (i, a) in vec.iter().enumerate() {
|
|
||||||
result[i] = a.clone() * c;
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
pub fn hadamard<F: PrimeField, CF: PrimeField, FV: FieldVar<F, CF>>(
|
|
||||||
a: &Vec<FV>,
|
|
||||||
b: &Vec<FV>,
|
|
||||||
) -> Result<Vec<FV>, SynthesisError> {
|
|
||||||
if a.len() != b.len() {
|
|
||||||
return Err(SynthesisError::Unsatisfiable);
|
|
||||||
}
|
|
||||||
let mut r: Vec<FV> = vec![FV::zero(); a.len()];
|
|
||||||
for i in 0..a.len() {
|
|
||||||
r[i] = a[i].clone() * b[i].clone();
|
|
||||||
}
|
|
||||||
Ok(r)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct SparseMatrixVar<F: PrimeField, CF: PrimeField, FV: FieldVar<F, CF>> {
|
pub struct SparseMatrixVar<F: PrimeField, CF: PrimeField, FV: AllocVar<F, CF>> {
|
||||||
_f: PhantomData<F>,
|
_f: PhantomData<F>,
|
||||||
_cf: PhantomData<CF>,
|
_cf: PhantomData<CF>,
|
||||||
_fv: PhantomData<FV>,
|
_fv: PhantomData<FV>,
|
||||||
@@ -77,7 +56,7 @@ impl<F, CF, FV> AllocVar<SparseMatrix<F>, CF> for SparseMatrixVar<F, CF, FV>
|
|||||||
where
|
where
|
||||||
F: PrimeField,
|
F: PrimeField,
|
||||||
CF: PrimeField,
|
CF: PrimeField,
|
||||||
FV: FieldVar<F, CF>,
|
FV: AllocVar<F, CF>,
|
||||||
{
|
{
|
||||||
fn new_variable<T: Borrow<SparseMatrix<F>>>(
|
fn new_variable<T: Borrow<SparseMatrix<F>>>(
|
||||||
cs: impl Into<Namespace<CF>>,
|
cs: impl Into<Namespace<CF>>,
|
||||||
@@ -108,3 +87,23 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<F: PrimeField> MatrixGadget<FpVar<F>> for SparseMatrixVar<F, F, FpVar<F>> {
|
||||||
|
fn mul_vector(&self, v: &[FpVar<F>]) -> Result<Vec<FpVar<F>>, SynthesisError> {
|
||||||
|
Ok(self
|
||||||
|
.coeffs
|
||||||
|
.iter()
|
||||||
|
.map(|row| {
|
||||||
|
let products = row
|
||||||
|
.iter()
|
||||||
|
.map(|(value, col_i)| value * &v[*col_i])
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if products.is_constant() {
|
||||||
|
FpVar::constant(products.value().unwrap_or_default().into_iter().sum())
|
||||||
|
} else {
|
||||||
|
products.iter().sum()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user