Fix with latest arkworks version. (#95)

Co-authored-by: Pratyush Mishra <pratyushmishra@berkeley.edu>
This commit is contained in:
Michele Orrù
2022-08-01 19:15:17 +02:00
committed by GitHub
parent 4e1e8d048d
commit 6d64f379a2
35 changed files with 446 additions and 389 deletions

View File

@@ -58,7 +58,6 @@ ark-std = { git = "https://github.com/arkworks-rs/std" }
ark-ec = { git = "https://github.com/arkworks-rs/algebra" } ark-ec = { git = "https://github.com/arkworks-rs/algebra" }
ark-ff = { git = "https://github.com/arkworks-rs/algebra" } ark-ff = { git = "https://github.com/arkworks-rs/algebra" }
ark-poly = { git = "https://github.com/arkworks-rs/algebra" } ark-poly = { git = "https://github.com/arkworks-rs/algebra" }
ark-serialize = { git = "https://github.com/arkworks-rs/algebra" }
ark-test-curves = { git = "https://github.com/arkworks-rs/algebra" } ark-test-curves = { git = "https://github.com/arkworks-rs/algebra" }
ark-bls12-381 = { git = "https://github.com/arkworks-rs/curves" } ark-bls12-381 = { git = "https://github.com/arkworks-rs/curves" }
ark-bls12-377 = { git = "https://github.com/arkworks-rs/curves" } ark-bls12-377 = { git = "https://github.com/arkworks-rs/curves" }

View File

@@ -1,6 +1,9 @@
use ark_ff::PrimeField; use ark_ff::PrimeField;
use ark_r1cs_std::fields::nonnative::NonNativeFieldVar; use ark_r1cs_std::{
use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget, fields::FieldVar}; alloc::AllocVar,
eq::EqGadget,
fields::{nonnative::NonNativeFieldVar, FieldVar},
};
use ark_relations::{ use ark_relations::{
ns, ns,
r1cs::{ConstraintSystem, ConstraintSystemRef, OptimizationGoal}, r1cs::{ConstraintSystem, ConstraintSystemRef, OptimizationGoal},

View File

@@ -608,7 +608,8 @@ impl<F: Field> Boolean<F> {
} }
} }
/// Convert a little-endian bitwise representation of a field element to `FpVar<F>` /// Convert a little-endian bitwise representation of a field element to
/// `FpVar<F>`
#[tracing::instrument(target = "r1cs", skip(bits))] #[tracing::instrument(target = "r1cs", skip(bits))]
pub fn le_bits_to_fp_var(bits: &[Self]) -> Result<FpVar<F>, SynthesisError> pub fn le_bits_to_fp_var(bits: &[Self]) -> Result<FpVar<F>, SynthesisError>
where where
@@ -761,7 +762,6 @@ impl<F: Field> Boolean<F> {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
///
#[tracing::instrument(target = "r1cs", skip(first, second))] #[tracing::instrument(target = "r1cs", skip(first, second))]
pub fn select<T: CondSelectGadget<F>>( pub fn select<T: CondSelectGadget<F>>(
&self, &self,

View File

@@ -7,8 +7,7 @@ macro_rules! make_uint {
#[doc = " type."] #[doc = " type."]
pub mod $mod_name { pub mod $mod_name {
use ark_ff::{Field, One, PrimeField, Zero}; use ark_ff::{Field, One, PrimeField, Zero};
use core::borrow::Borrow; use core::{borrow::Borrow, convert::TryFrom};
use core::convert::TryFrom;
use num_bigint::BigUint; use num_bigint::BigUint;
use num_traits::cast::ToPrimitive; use num_traits::cast::ToPrimitive;
@@ -87,7 +86,6 @@ macro_rules! make_uint {
/// Construct `Self` from a slice of `Boolean`s. /// Construct `Self` from a slice of `Boolean`s.
/// ///
/// # Panics /// # Panics
///
#[doc = "This method panics if `bits.len() != "] #[doc = "This method panics if `bits.len() != "]
#[doc = $num_bits_doc] #[doc = $num_bits_doc]
#[doc = "`."] #[doc = "`."]
@@ -142,8 +140,8 @@ macro_rules! make_uint {
/// Outputs `self ^ other`. /// Outputs `self ^ other`.
/// ///
/// If at least one of `self` and `other` are constants, then this method /// If at least one of `self` and `other` are constants, then this
/// *does not* create any constraints or variables. /// method *does not* create any constraints or variables.
#[tracing::instrument(target = "r1cs", skip(self, other))] #[tracing::instrument(target = "r1cs", skip(self, other))]
pub fn xor(&self, other: &Self) -> Result<Self, SynthesisError> { pub fn xor(&self, other: &Self) -> Result<Self, SynthesisError> {
let mut result = self.clone(); let mut result = self.clone();
@@ -225,7 +223,8 @@ macro_rules! make_uint {
Boolean::Not(ref bit) => { Boolean::Not(ref bit) => {
all_constants = false; all_constants = false;
// Add coeff * (1 - bit_gadget) = coeff * ONE - coeff * bit_gadget // Add coeff * (1 - bit_gadget) = coeff * ONE - coeff *
// bit_gadget
lc = lc + (coeff, Variable::One) - (coeff, bit.variable()); lc = lc + (coeff, Variable::One) - (coeff, bit.variable());
}, },
Boolean::Constant(bit) => { Boolean::Constant(bit) => {

View File

@@ -2,8 +2,11 @@ use ark_ff::{Field, PrimeField, ToConstraintField};
use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError}; use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError};
use crate::fields::fp::{AllocatedFp, FpVar}; use crate::{
use crate::{prelude::*, Assignment, ToConstraintFieldGadget, Vec}; fields::fp::{AllocatedFp, FpVar},
prelude::*,
Assignment, ToConstraintFieldGadget, Vec,
};
use core::{borrow::Borrow, convert::TryFrom}; use core::{borrow::Borrow, convert::TryFrom};
/// Represents an interpretation of 8 `Boolean` objects as an /// Represents an interpretation of 8 `Boolean` objects as an
@@ -335,9 +338,9 @@ impl<ConstraintF: Field> AllocVar<u8, ConstraintF> for UInt8<ConstraintF> {
} }
} }
/// Parses the `Vec<UInt8<ConstraintF>>` in fixed-sized `ConstraintF::MODULUS_BIT_SIZE - 1` chunks and /// Parses the `Vec<UInt8<ConstraintF>>` in fixed-sized
/// converts each chunk, which is assumed to be little-endian, to its `FpVar<ConstraintF>` /// `ConstraintF::MODULUS_BIT_SIZE - 1` chunks and converts each chunk, which is
/// representation. /// assumed to be little-endian, to its `FpVar<ConstraintF>` representation.
/// This is the gadget counterpart to the `[u8]` implementation of /// This is the gadget counterpart to the `[u8]` implementation of
/// [ToConstraintField](ark_ff::ToConstraintField). /// [ToConstraintField](ark_ff::ToConstraintField).
impl<ConstraintF: PrimeField> ToConstraintFieldGadget<ConstraintF> for [UInt8<ConstraintF>] { impl<ConstraintF: PrimeField> ToConstraintFieldGadget<ConstraintF> for [UInt8<ConstraintF>] {
@@ -360,13 +363,17 @@ impl<ConstraintF: PrimeField> ToConstraintFieldGadget<ConstraintF> for Vec<UInt8
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::UInt8; use super::UInt8;
use crate::fields::fp::FpVar; use crate::{
use crate::prelude::AllocationMode::{Constant, Input, Witness}; fields::fp::FpVar,
use crate::{prelude::*, ToConstraintFieldGadget, Vec}; prelude::{
AllocationMode::{Constant, Input, Witness},
*,
},
ToConstraintFieldGadget, Vec,
};
use ark_ff::{PrimeField, ToConstraintField}; use ark_ff::{PrimeField, ToConstraintField};
use ark_relations::r1cs::{ConstraintSystem, SynthesisError}; use ark_relations::r1cs::{ConstraintSystem, SynthesisError};
use ark_std::rand::distributions::Uniform; use ark_std::rand::{distributions::Uniform, Rng};
use ark_std::rand::Rng;
use ark_test_curves::bls12_381::Fr; use ark_test_curves::bls12_381::Fr;
#[test] #[test]

View File

@@ -153,8 +153,7 @@ impl<F: PrimeField> FpVar<F> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use ark_std::cmp::Ordering; use ark_std::{cmp::Ordering, rand::Rng};
use ark_std::rand::Rng;
use crate::{alloc::AllocVar, fields::fp::FpVar}; use crate::{alloc::AllocVar, fields::fp::FpVar};
use ark_ff::{PrimeField, UniformRand}; use ark_ff::{PrimeField, UniformRand};

View File

@@ -127,7 +127,8 @@ impl<F: PrimeField> AllocatedFp<F> {
/// Add many allocated Fp elements together. /// Add many allocated Fp elements together.
/// ///
/// This does not create any constraints and only creates one linear combination. /// This does not create any constraints and only creates one linear
/// combination.
pub fn addmany<'a, I: Iterator<Item = &'a Self>>(iter: I) -> Self { pub fn addmany<'a, I: Iterator<Item = &'a Self>>(iter: I) -> Self {
let mut cs = ConstraintSystemRef::None; let mut cs = ConstraintSystemRef::None;
let mut has_value = true; let mut has_value = true;
@@ -918,7 +919,9 @@ impl<F: PrimeField> ToBytesGadget<F> for FpVar<F> {
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
fn to_bytes(&self) -> Result<Vec<UInt8<F>>, SynthesisError> { fn to_bytes(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
match self { match self {
Self::Constant(c) => Ok(UInt8::constant_vec(&ark_ff::to_bytes![c].unwrap())), Self::Constant(c) => Ok(UInt8::constant_vec(
c.into_bigint().to_bytes_be().as_slice(),
)),
Self::Var(v) => v.to_bytes(), Self::Var(v) => v.to_bytes(),
} }
} }
@@ -926,7 +929,9 @@ impl<F: PrimeField> ToBytesGadget<F> for FpVar<F> {
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
fn to_non_unique_bytes(&self) -> Result<Vec<UInt8<F>>, SynthesisError> { fn to_non_unique_bytes(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
match self { match self {
Self::Constant(c) => Ok(UInt8::constant_vec(&ark_ff::to_bytes![c].unwrap())), Self::Constant(c) => Ok(UInt8::constant_vec(
c.into_bigint().to_bytes_be().as_slice(),
)),
Self::Var(v) => v.to_non_unique_bytes(), Self::Var(v) => v.to_non_unique_bytes(),
} }
} }
@@ -1060,10 +1065,12 @@ impl<'a, F: PrimeField> Sum<&'a FpVar<F>> for FpVar<F> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::alloc::{AllocVar, AllocationMode}; use crate::{
use crate::eq::EqGadget; alloc::{AllocVar, AllocationMode},
use crate::fields::fp::FpVar; eq::EqGadget,
use crate::R1CSVar; fields::fp::FpVar,
R1CSVar,
};
use ark_relations::r1cs::ConstraintSystem; use ark_relations::r1cs::ConstraintSystem;
use ark_std::{UniformRand, Zero}; use ark_std::{UniformRand, Zero};
use ark_test_curves::bls12_381::Fr; use ark_test_curves::bls12_381::Fr;

View File

@@ -1,7 +1,9 @@
use crate::fields::{fp2::Fp2Var, fp6_3over2::Fp6Var, quadratic_extension::*, FieldVar}; use crate::fields::{fp2::Fp2Var, fp6_3over2::Fp6Var, quadratic_extension::*, FieldVar};
use ark_ff::fields::{fp12_2over3over2::*, Field}; use ark_ff::{
use ark_ff::fp6_3over2::Fp6Config; fields::{fp12_2over3over2::*, Field},
use ark_ff::QuadExtConfig; fp6_3over2::Fp6Config,
QuadExtConfig,
};
use ark_relations::r1cs::SynthesisError; use ark_relations::r1cs::SynthesisError;
/// A degree-12 extension field constructed as the tower of a /// A degree-12 extension field constructed as the tower of a

View File

@@ -1,6 +1,8 @@
use crate::fields::{cubic_extension::*, fp::FpVar}; use crate::fields::{cubic_extension::*, fp::FpVar};
use ark_ff::fields::{CubicExtConfig, Fp3ConfigWrapper}; use ark_ff::{
use ark_ff::Fp3Config; fields::{CubicExtConfig, Fp3ConfigWrapper},
Fp3Config,
};
/// A cubic extension field constructed over a prime field. /// A cubic extension field constructed over a prime field.
/// This is the R1CS equivalent of `ark_ff::Fp3<P>`. /// This is the R1CS equivalent of `ark_ff::Fp3<P>`.

View File

@@ -1,6 +1,8 @@
use crate::fields::{fp2::Fp2Var, quadratic_extension::*}; use crate::fields::{fp2::Fp2Var, quadratic_extension::*};
use ark_ff::fields::{Fp4ConfigWrapper, QuadExtConfig}; use ark_ff::{
use ark_ff::Fp4Config; fields::{Fp4ConfigWrapper, QuadExtConfig},
Fp4Config,
};
/// A quartic extension field constructed as the tower of a /// A quartic extension field constructed as the tower of a
/// quadratic extension over a quadratic extension field. /// quadratic extension over a quadratic extension field.

View File

@@ -1,6 +1,5 @@
use crate::fields::{fp3::Fp3Var, quadratic_extension::*}; use crate::fields::{fp3::Fp3Var, quadratic_extension::*};
use ark_ff::fields::fp6_2over3::*; use ark_ff::{fields::fp6_2over3::*, QuadExtConfig};
use ark_ff::QuadExtConfig;
/// A sextic extension field constructed as the tower of a /// A sextic extension field constructed as the tower of a
/// quadratic extension over a cubic extension field. /// quadratic extension over a cubic extension field.

View File

@@ -1,6 +1,8 @@
use crate::fields::{cubic_extension::*, fp2::*}; use crate::fields::{cubic_extension::*, fp2::*};
use ark_ff::fields::{fp6_3over2::*, Fp2}; use ark_ff::{
use ark_ff::CubicExtConfig; fields::{fp6_3over2::*, Fp2},
CubicExtConfig,
};
use ark_relations::r1cs::SynthesisError; use ark_relations::r1cs::SynthesisError;
use ark_std::ops::MulAssign; use ark_std::ops::MulAssign;

View File

@@ -20,8 +20,8 @@ pub mod quadratic_extension;
/// That is, it implements the R1CS equivalent of `ark_ff::Fp*`. /// That is, it implements the R1CS equivalent of `ark_ff::Fp*`.
pub mod fp; pub mod fp;
/// This module contains a generic implementation of "nonnative" prime field variables. /// This module contains a generic implementation of "nonnative" prime field
/// It emulates `Fp` arithmetic using `Fq` operations, where `p != q`. /// variables. It emulates `Fp` arithmetic using `Fq` operations, where `p != q`.
pub mod nonnative; pub mod nonnative;
/// This module contains a generic implementation of the degree-12 tower /// This module contains a generic implementation of the degree-12 tower

View File

@@ -1,18 +1,23 @@
use super::params::{get_params, OptimizationType}; use super::{
use super::reduce::{bigint_to_basefield, limbs_to_bigint, Reducer}; params::{get_params, OptimizationType},
use super::AllocatedNonNativeFieldMulResultVar; reduce::{bigint_to_basefield, limbs_to_bigint, Reducer},
use crate::fields::fp::FpVar; AllocatedNonNativeFieldMulResultVar,
use crate::prelude::*; };
use crate::ToConstraintFieldGadget; use crate::{fields::fp::FpVar, prelude::*, ToConstraintFieldGadget};
use ark_ff::{BigInteger, PrimeField}; use ark_ff::{BigInteger, PrimeField};
use ark_relations::r1cs::{OptimizationGoal, Result as R1CSResult};
use ark_relations::{ use ark_relations::{
ns, ns,
r1cs::{ConstraintSystemRef, Namespace, SynthesisError}, r1cs::{
ConstraintSystemRef, Namespace, OptimizationGoal, Result as R1CSResult, SynthesisError,
},
};
use ark_std::{
borrow::Borrow,
cmp::{max, min},
marker::PhantomData,
vec,
vec::Vec,
}; };
use ark_std::cmp::{max, min};
use ark_std::marker::PhantomData;
use ark_std::{borrow::Borrow, vec, vec::Vec};
/// The allocated version of `NonNativeFieldVar` (introduced below) /// The allocated version of `NonNativeFieldVar` (introduced below)
#[derive(Debug)] #[derive(Debug)]
@@ -22,9 +27,12 @@ pub struct AllocatedNonNativeFieldVar<TargetField: PrimeField, BaseField: PrimeF
pub cs: ConstraintSystemRef<BaseField>, pub cs: ConstraintSystemRef<BaseField>,
/// The limbs, each of which is a BaseField gadget. /// The limbs, each of which is a BaseField gadget.
pub limbs: Vec<FpVar<BaseField>>, pub limbs: Vec<FpVar<BaseField>>,
/// Number of additions done over this gadget, using which the gadget decides when to reduce. /// Number of additions done over this gadget, using which the gadget
/// decides when to reduce.
pub num_of_additions_over_normal_form: BaseField, pub num_of_additions_over_normal_form: BaseField,
/// Whether the limb representation is the normal form (using only the bits specified in the parameters, and the representation is strictly within the range of TargetField). /// Whether the limb representation is the normal form (using only the bits
/// specified in the parameters, and the representation is strictly within
/// the range of TargetField).
pub is_in_the_normal_form: bool, pub is_in_the_normal_form: bool,
#[doc(hidden)] #[doc(hidden)]
pub target_phantom: PhantomData<TargetField>, pub target_phantom: PhantomData<TargetField>,
@@ -51,8 +59,9 @@ impl<TargetField: PrimeField, BaseField: PrimeField>
let mut base_repr: <TargetField as PrimeField>::BigInt = TargetField::one().into_bigint(); let mut base_repr: <TargetField as PrimeField>::BigInt = TargetField::one().into_bigint();
// Convert 2^{(params.bits_per_limb - 1)} into the TargetField and then double the base // Convert 2^{(params.bits_per_limb - 1)} into the TargetField and then double
// This is because 2^{(params.bits_per_limb)} might indeed be larger than the target field's prime. // the base This is because 2^{(params.bits_per_limb)} might indeed be
// larger than the target field's prime.
base_repr.muln((params.bits_per_limb - 1) as u32); base_repr.muln((params.bits_per_limb - 1) as u32);
let mut base: TargetField = TargetField::from_bigint(base_repr).unwrap(); let mut base: TargetField = TargetField::from_bigint(base_repr).unwrap();
base = base + &base; base = base + &base;
@@ -303,7 +312,8 @@ impl<TargetField: PrimeField, BaseField: PrimeField>
} }
/// Convert a `TargetField` element into limbs (not constraints) /// Convert a `TargetField` element into limbs (not constraints)
/// This is an internal function that would be reused by a number of other functions /// This is an internal function that would be reused by a number of other
/// functions
pub fn get_limbs_representations( pub fn get_limbs_representations(
elem: &TargetField, elem: &TargetField,
optimization_type: OptimizationType, optimization_type: OptimizationType,
@@ -340,8 +350,10 @@ impl<TargetField: PrimeField, BaseField: PrimeField>
Ok(limbs) Ok(limbs)
} }
/// for advanced use, multiply and output the intermediate representations (without reduction) /// for advanced use, multiply and output the intermediate representations
/// This intermediate representations can be added with each other, and they can later be reduced back to the `NonNativeFieldVar`. /// (without reduction) This intermediate representations can be added
/// with each other, and they can later be reduced back to the
/// `NonNativeFieldVar`.
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
pub fn mul_without_reduce( pub fn mul_without_reduce(
&self, &self,
@@ -532,7 +544,8 @@ impl<TargetField: PrimeField, BaseField: PrimeField>
} }
} }
/// Allocates a new variable, but does not check that the allocation's limbs are in-range. /// Allocates a new variable, but does not check that the allocation's limbs
/// are in-range.
fn new_variable_unchecked<T: Borrow<TargetField>>( fn new_variable_unchecked<T: Borrow<TargetField>>(
cs: impl Into<Namespace<BaseField>>, cs: impl Into<Namespace<BaseField>>,
f: impl FnOnce() -> Result<T, SynthesisError>, f: impl FnOnce() -> Result<T, SynthesisError>,
@@ -579,8 +592,8 @@ impl<TargetField: PrimeField, BaseField: PrimeField>
}) })
} }
/// Check that this element is in-range; i.e., each limb is in-range, and the whole number is /// Check that this element is in-range; i.e., each limb is in-range, and
/// less than the modulus. /// the whole number is less than the modulus.
/// ///
/// Returns the bits of the element, in little-endian form /// Returns the bits of the element, in little-endian form
fn enforce_in_range( fn enforce_in_range(
@@ -620,9 +633,10 @@ impl<TargetField: PrimeField, BaseField: PrimeField>
Ok(bits) Ok(bits)
} }
/// Allocates a new non-native field witness with value given by the function `f`. Enforces /// Allocates a new non-native field witness with value given by the
/// that the field element has value in `[0, modulus)`, and returns the bits of its binary /// function `f`. Enforces that the field element has value in `[0, modulus)`,
/// representation. The bits are in little-endian (i.e., the bit at index 0 is the LSB) and the /// and returns the bits of its binary representation.
/// The bits are in little-endian (i.e., the bit at index 0 is the LSB) and the
/// bit-vector is empty in non-witness allocation modes. /// bit-vector is empty in non-witness allocation modes.
pub fn new_witness_with_le_bits<T: Borrow<TargetField>>( pub fn new_witness_with_le_bits<T: Borrow<TargetField>>(
cs: impl Into<Namespace<BaseField>>, cs: impl Into<Namespace<BaseField>>,
@@ -902,9 +916,7 @@ impl<TargetField: PrimeField, BaseField: PrimeField> ToConstraintFieldGadget<Bas
} }
} }
/* // Implementation of a few traits
* Implementation of a few traits
*/
impl<TargetField: PrimeField, BaseField: PrimeField> Clone impl<TargetField: PrimeField, BaseField: PrimeField> Clone
for AllocatedNonNativeFieldVar<TargetField, BaseField> for AllocatedNonNativeFieldVar<TargetField, BaseField>

View File

@@ -1,13 +1,15 @@
use super::params::{get_params, OptimizationType}; use super::{
use super::reduce::{bigint_to_basefield, limbs_to_bigint, Reducer}; params::{get_params, OptimizationType},
use super::AllocatedNonNativeFieldVar; reduce::{bigint_to_basefield, limbs_to_bigint, Reducer},
use crate::fields::fp::FpVar; AllocatedNonNativeFieldVar,
use crate::prelude::*; };
use crate::{fields::fp::FpVar, prelude::*};
use ark_ff::PrimeField; use ark_ff::PrimeField;
use ark_relations::r1cs::{OptimizationGoal, Result as R1CSResult}; use ark_relations::{
use ark_relations::{ns, r1cs::ConstraintSystemRef}; ns,
use ark_std::marker::PhantomData; r1cs::{ConstraintSystemRef, OptimizationGoal, Result as R1CSResult},
use ark_std::vec::Vec; };
use ark_std::{marker::PhantomData, vec::Vec};
use num_bigint::BigUint; use num_bigint::BigUint;
/// The allocated form of `NonNativeFieldMulResultVar` (introduced below) /// The allocated form of `NonNativeFieldMulResultVar` (introduced below)
@@ -84,7 +86,8 @@ impl<TargetField: PrimeField, BaseField: PrimeField>
Ok(res) Ok(res)
} }
/// Constraints for reducing the result of a multiplication mod p, to get an original representation. /// Constraints for reducing the result of a multiplication mod p, to get an
/// original representation.
pub fn reduce(&self) -> R1CSResult<AllocatedNonNativeFieldVar<TargetField, BaseField>> { pub fn reduce(&self) -> R1CSResult<AllocatedNonNativeFieldVar<TargetField, BaseField>> {
let params = get_params( let params = get_params(
TargetField::MODULUS_BIT_SIZE as usize, TargetField::MODULUS_BIT_SIZE as usize,

View File

@@ -1,18 +1,20 @@
use super::params::OptimizationType; use super::{params::OptimizationType, AllocatedNonNativeFieldVar, NonNativeFieldMulResultVar};
use super::{AllocatedNonNativeFieldVar, NonNativeFieldMulResultVar}; use crate::{
use crate::boolean::Boolean; boolean::Boolean,
use crate::fields::fp::FpVar; fields::{fp::FpVar, FieldVar},
use crate::fields::FieldVar; prelude::*,
use crate::prelude::*; R1CSVar, ToConstraintFieldGadget,
use crate::{R1CSVar, ToConstraintFieldGadget}; };
use ark_ff::to_bytes; use ark_ff::{BigInteger, PrimeField};
use ark_ff::PrimeField; use ark_relations::r1cs::{ConstraintSystemRef, Namespace, Result as R1CSResult, SynthesisError};
use ark_relations::r1cs::Result as R1CSResult; use ark_std::{
use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError}; borrow::Borrow,
use ark_std::hash::{Hash, Hasher}; hash::{Hash, Hasher},
use ark_std::{borrow::Borrow, vec::Vec}; vec::Vec,
};
/// A gadget for representing non-native (`TargetField`) field elements over the constraint field (`BaseField`). /// A gadget for representing non-native (`TargetField`) field elements over the
/// constraint field (`BaseField`).
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
#[must_use] #[must_use]
pub enum NonNativeFieldVar<TargetField: PrimeField, BaseField: PrimeField> { pub enum NonNativeFieldVar<TargetField: PrimeField, BaseField: PrimeField> {
@@ -144,9 +146,6 @@ impl<TargetField: PrimeField, BaseField: PrimeField> FieldVar<TargetField, BaseF
} }
} }
/****************************************************************************/
/****************************************************************************/
impl_bounded_ops!( impl_bounded_ops!(
NonNativeFieldVar<TargetField, BaseField>, NonNativeFieldVar<TargetField, BaseField>,
TargetField, TargetField,
@@ -213,8 +212,8 @@ impl_bounded_ops!(
(TargetField: PrimeField, BaseField: PrimeField), (TargetField: PrimeField, BaseField: PrimeField),
); );
/****************************************************************************/ /// *************************************************************************
/****************************************************************************/ /// *************************************************************************
impl<TargetField: PrimeField, BaseField: PrimeField> EqGadget<BaseField> impl<TargetField: PrimeField, BaseField: PrimeField> EqGadget<BaseField>
for NonNativeFieldVar<TargetField, BaseField> for NonNativeFieldVar<TargetField, BaseField>
@@ -313,7 +312,10 @@ impl<TargetField: PrimeField, BaseField: PrimeField> ToBytesGadget<BaseField>
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
fn to_bytes(&self) -> R1CSResult<Vec<UInt8<BaseField>>> { fn to_bytes(&self) -> R1CSResult<Vec<UInt8<BaseField>>> {
match self { match self {
Self::Constant(c) => Ok(UInt8::constant_vec(&to_bytes![c].unwrap())), Self::Constant(c) => Ok(UInt8::constant_vec(
c.into_bigint().to_bytes_be().as_slice(),
)),
Self::Var(v) => v.to_bytes(), Self::Var(v) => v.to_bytes(),
} }
} }
@@ -321,7 +323,9 @@ impl<TargetField: PrimeField, BaseField: PrimeField> ToBytesGadget<BaseField>
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
fn to_non_unique_bytes(&self) -> R1CSResult<Vec<UInt8<BaseField>>> { fn to_non_unique_bytes(&self) -> R1CSResult<Vec<UInt8<BaseField>>> {
match self { match self {
Self::Constant(c) => Ok(UInt8::constant_vec(&to_bytes![c].unwrap())), Self::Constant(c) => Ok(UInt8::constant_vec(
c.into_bigint().to_bytes_be().as_slice(),
)),
Self::Var(v) => v.to_non_unique_bytes(), Self::Var(v) => v.to_non_unique_bytes(),
} }
} }
@@ -440,7 +444,8 @@ impl<TargetField: PrimeField, BaseField: PrimeField> ToConstraintFieldGadget<Bas
fn to_constraint_field(&self) -> R1CSResult<Vec<FpVar<BaseField>>> { fn to_constraint_field(&self) -> R1CSResult<Vec<FpVar<BaseField>>> {
// Use one group element to represent the optimization type. // Use one group element to represent the optimization type.
// //
// By default, the constant is converted in the weight-optimized type, because it results in fewer elements. // By default, the constant is converted in the weight-optimized type, because
// it results in fewer elements.
match self { match self {
Self::Constant(c) => Ok(AllocatedNonNativeFieldVar::get_limbs_representations( Self::Constant(c) => Ok(AllocatedNonNativeFieldVar::get_limbs_representations(
c, c,

View File

@@ -1,18 +1,27 @@
//!
//! ## Overview //! ## Overview
//! //!
//! This module implements a field gadget for a prime field `Fp` over another prime field `Fq` where `p != q`. //! This module implements a field gadget for a prime field `Fp` over another
//! prime field `Fq` where `p != q`.
//! //!
//! When writing constraint systems for many cryptographic proofs, we are restricted to a native field (e.g., the scalar field of the pairing-friendly curve). //! When writing constraint systems for many cryptographic proofs, we are
//! This can be inconvenient; for example, the recursive composition of proofs via cycles of curves requires the verifier to compute over a non-native field. //! restricted to a native field (e.g., the scalar field of the pairing-friendly
//! curve). This can be inconvenient; for example, the recursive composition of
//! proofs via cycles of curves requires the verifier to compute over a
//! non-native field.
//! //!
//! The library makes it possible to write computations over a non-native field in the same way one would write computations over the native field. This naturally introduces additional overhead, which we minimize using a variety of optimizations. (Nevertheless, the overhead is still substantial, and native fields should be used where possible.) //! The library makes it possible to write computations over a non-native field
//! in the same way one would write computations over the native field. This
//! naturally introduces additional overhead, which we minimize using a variety
//! of optimizations. (Nevertheless, the overhead is still substantial, and
//! native fields should be used where possible.)
//! //!
//! ## Usage //! ## Usage
//! //!
//! Because [`NonNativeFieldVar`] implements the [`FieldVar`] trait in arkworks, we can treat it like a native field variable ([`FpVar`]). //! Because [`NonNativeFieldVar`] implements the [`FieldVar`] trait in arkworks,
//! we can treat it like a native field variable ([`FpVar`]).
//! //!
//! We can do the standard field operations, such as `+`, `-`, and `*`. See the following example: //! We can do the standard field operations, such as `+`, `-`, and `*`. See the
//! following example:
//! //!
//! ```rust //! ```rust
//! # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> { //! # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> {
@@ -47,15 +56,21 @@
//! //!
//! ## Advanced optimization //! ## Advanced optimization
//! //!
//! After each multiplication, our library internally performs a *reduce* operation, //! After each multiplication, our library internally performs a *reduce*
//! which reduces an intermediate type [`NonNativeFieldMulResultVar`] to the normalized type [`NonNativeFieldVar`]. //! operation, which reduces an intermediate type [`NonNativeFieldMulResultVar`]
//! This enables a user to seamlessly perform a sequence of operations without worrying about the underlying details. //! to the normalized type [`NonNativeFieldVar`]. This enables a user to
//! seamlessly perform a sequence of operations without worrying about the
//! underlying details.
//! //!
//! However, this operation is expensive and is sometimes avoidable. We can reduce the number of constraints by using this intermediate type, which only supports additions. To multiply, it must be reduced back to [`NonNativeFieldVar`]. See below for a skeleton example. //! However, this operation is expensive and is sometimes avoidable. We can
//! reduce the number of constraints by using this intermediate type, which only
//! supports additions. To multiply, it must be reduced back to
//! [`NonNativeFieldVar`]. See below for a skeleton example.
//! //!
//! --- //! ---
//! //!
//! To compute `a * b + c * d`, the straightforward (but more expensive) implementation is as follows: //! To compute `a * b + c * d`, the straightforward (but more expensive)
//! implementation is as follows:
//! //!
//! ```ignore //! ```ignore
//! let a_times_b = &a * &b; //! let a_times_b = &a * &b;
@@ -67,7 +82,8 @@
//! //!
//! --- //! ---
//! //!
//! We can save one reduction by using the [`NonNativeFieldMulResultVar`], as follows: //! We can save one reduction by using the [`NonNativeFieldMulResultVar`], as
//! follows:
//! //!
//! ```ignore //! ```ignore
//! let a_times_b = a.mul_without_reduce(&b)?; //! let a_times_b = a.mul_without_reduce(&b)?;
@@ -75,26 +91,31 @@
//! let res = (&a_times_b + &c_times_d)?.reduce()?; //! let res = (&a_times_b + &c_times_d)?.reduce()?;
//! ``` //! ```
//! //!
//! It performs only one *reduce* operation and is roughly 2x faster than the first implementation. //! It performs only one *reduce* operation and is roughly 2x faster than the
//! first implementation.
//! //!
//! ## Inspiration and basic design //! ## Inspiration and basic design
//! //!
//! This implementation employs the standard idea of using multiple **limbs** to represent an element of the target field. For example, an element in the TargetField may be represented by three BaseField elements (i.e., the limbs). //! This implementation employs the standard idea of using multiple **limbs** to
//! represent an element of the target field. For example, an element in the
//! TargetField may be represented by three BaseField elements (i.e., the
//! limbs).
//! //!
//! ```text //! ```text
//! TargetField -> limb 1, limb 2, and limb 3 (each is a BaseField element) //! TargetField -> limb 1, limb 2, and limb 3 (each is a BaseField element)
//! ``` //! ```
//! //!
//! After some computation, the limbs become saturated and need to be **reduced**, in order to engage in more computation. //! After some computation, the limbs become saturated and need to be
//! **reduced**, in order to engage in more computation.
//! //!
//! We heavily use the optimization techniques in [\[KPS18\]](https://akosba.github.io/papers/xjsnark.pdf) and [\[OWWB20\]](https://eprint.iacr.org/2019/1494). //! We heavily use the optimization techniques in [\[KPS18\]](https://akosba.github.io/papers/xjsnark.pdf) and [\[OWWB20\]](https://eprint.iacr.org/2019/1494).
//! Both works have their own open-source libraries: //! Both works have their own open-source libraries:
//! [xJsnark](https://github.com/akosba/xjsnark) and //! [xJsnark](https://github.com/akosba/xjsnark) and
//! [bellman-bignat](https://github.com/alex-ozdemir/bellman-bignat). //! [bellman-bignat](https://github.com/alex-ozdemir/bellman-bignat).
//! Compared with these, this module works with the `arkworks` ecosystem. //! Compared with these, this module works with the `arkworks` ecosystem.
//! It also provides the option (based on an `optimization_goal` for the constraint system) to optimize //! It also provides the option (based on an `optimization_goal` for the
//! for constraint density instead of number of constraints, which improves efficiency in //! constraint system) to optimize for constraint density instead of number of
//! proof systems like [Marlin](https://github.com/arkworks-rs/marlin). //! constraints, which improves efficiency in proof systems like [Marlin](https://github.com/arkworks-rs/marlin).
//! //!
//! ## References //! ## References
//! \[KPS18\]: A. E. Kosba, C. Papamanthou, and E. Shi. "xJsnark: a framework for efficient verifiable computation," in *Proceedings of the 39th Symposium on Security and Privacy*, ser. S&P 18, 2018, pp. 944961. //! \[KPS18\]: A. E. Kosba, C. Papamanthou, and E. Shi. "xJsnark: a framework for efficient verifiable computation," in *Proceedings of the 39th Symposium on Security and Privacy*, ser. S&P 18, 2018, pp. 944961.
@@ -160,7 +181,8 @@ pub(crate) use overhead;
/// Parameters for a specific `NonNativeFieldVar` instantiation /// Parameters for a specific `NonNativeFieldVar` instantiation
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct NonNativeFieldConfig { pub struct NonNativeFieldConfig {
/// The number of limbs (`BaseField` elements) used to represent a `TargetField` element. Highest limb first. /// The number of limbs (`BaseField` elements) used to represent a
/// `TargetField` element. Highest limb first.
pub num_limbs: usize, pub num_limbs: usize,
/// The number of bits of the limb /// The number of bits of the limb

View File

@@ -2,8 +2,9 @@ use super::{AllocatedNonNativeFieldMulResultVar, NonNativeFieldVar};
use ark_ff::PrimeField; use ark_ff::PrimeField;
use ark_relations::r1cs::Result as R1CSResult; use ark_relations::r1cs::Result as R1CSResult;
/// An intermediate representation especially for the result of a multiplication, containing more limbs. /// An intermediate representation especially for the result of a
/// It is intended for advanced usage to improve the efficiency. /// multiplication, containing more limbs. It is intended for advanced usage to
/// improve the efficiency.
/// ///
/// That is, instead of calling `mul`, one can call `mul_without_reduce` to /// That is, instead of calling `mul`, one can call `mul_without_reduce` to
/// obtain this intermediate representation, which can still be added. /// obtain this intermediate representation, which can still be added.

View File

@@ -1,6 +1,7 @@
use super::NonNativeFieldConfig; use super::NonNativeFieldConfig;
/// Obtain the parameters from a `ConstraintSystem`'s cache or generate a new one /// Obtain the parameters from a `ConstraintSystem`'s cache or generate a new
/// one
#[must_use] #[must_use]
pub const fn get_params( pub const fn get_params(
target_field_size: usize, target_field_size: usize,
@@ -64,7 +65,7 @@ pub const fn find_parameters(
OptimizationType::Constraints => { OptimizationType::Constraints => {
this_cost += target_field_prime_bit_length; // allocation of k this_cost += target_field_prime_bit_length; // allocation of k
this_cost += target_field_prime_bit_length + num_of_limbs; // allocation of r this_cost += target_field_prime_bit_length + num_of_limbs; // allocation of r
//this_cost += 2 * num_of_limbs - 1; // compute kp // this_cost += 2 * num_of_limbs - 1; // compute kp
this_cost += num_of_groups + (num_of_groups - 1) * (limb_size * 2 + surfeit) + 1; this_cost += num_of_groups + (num_of_groups - 1) * (limb_size * 2 + surfeit) + 1;
// equality check // equality check
}, },

View File

@@ -1,10 +1,11 @@
use super::overhead; use super::{overhead, params::get_params, AllocatedNonNativeFieldVar};
use super::params::get_params; use crate::{
use super::AllocatedNonNativeFieldVar; alloc::AllocVar,
use crate::eq::EqGadget; boolean::Boolean,
use crate::fields::fp::FpVar; eq::EqGadget,
use crate::fields::FieldVar; fields::{fp::FpVar, FieldVar},
use crate::{alloc::AllocVar, boolean::Boolean, R1CSVar}; R1CSVar,
};
use ark_ff::{biginteger::BigInteger, BitIteratorBE, One, PrimeField, Zero}; use ark_ff::{biginteger::BigInteger, BitIteratorBE, One, PrimeField, Zero};
use ark_relations::{ use ark_relations::{
ns, ns,
@@ -61,9 +62,10 @@ pub struct Reducer<TargetField: PrimeField, BaseField: PrimeField> {
} }
impl<TargetField: PrimeField, BaseField: PrimeField> Reducer<TargetField, BaseField> { impl<TargetField: PrimeField, BaseField: PrimeField> Reducer<TargetField, BaseField> {
/// convert limbs to bits (take at most `BaseField::MODULUS_BIT_SIZE as usize - 1` bits) /// convert limbs to bits (take at most `BaseField::MODULUS_BIT_SIZE as
/// This implementation would be more efficient than the original `to_bits` /// usize - 1` bits) This implementation would be more efficient than
/// or `to_non_unique_bits` since we enforce that some bits are always zero. /// the original `to_bits` or `to_non_unique_bits` since we enforce that
/// some bits are always zero.
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
pub fn limb_to_bits( pub fn limb_to_bits(
limb: &FpVar<BaseField>, limb: &FpVar<BaseField>,
@@ -147,7 +149,8 @@ impl<TargetField: PrimeField, BaseField: PrimeField> Reducer<TargetField, BaseFi
} }
} }
/// Reduction used before multiplication to reduce the representations in a way that allows efficient multiplication /// Reduction used before multiplication to reduce the representations in a
/// way that allows efficient multiplication
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
pub fn pre_mul_reduce( pub fn pre_mul_reduce(
elem: &mut AllocatedNonNativeFieldVar<TargetField, BaseField>, elem: &mut AllocatedNonNativeFieldVar<TargetField, BaseField>,
@@ -244,7 +247,8 @@ impl<TargetField: PrimeField, BaseField: PrimeField> Reducer<TargetField, BaseFi
}; };
for (left_limb, right_limb) in left.iter().zip(right.iter()).rev() { for (left_limb, right_limb) in left.iter().zip(right.iter()).rev() {
// note: the `rev` operation is here, so that the first limb (and the first groupped limb) will be the least significant limb. // note: the `rev` operation is here, so that the first limb (and the first
// groupped limb) will be the least significant limb.
limb_pairs.push((left_limb.clone(), right_limb.clone())); limb_pairs.push((left_limb.clone(), right_limb.clone()));
} }

View File

@@ -1,6 +1,6 @@
use ark_ec::{ use ark_ec::{
bls12::{Bls12Parameters, G1Prepared, G2Prepared, TwistType}, bls12::{Bls12Parameters, G1Prepared, G2Prepared, TwistType},
short_weierstrass_jacobian::GroupAffine, short_weierstrass::Affine as GroupAffine,
}; };
use ark_ff::{BitIteratorBE, Field, One}; use ark_ff::{BitIteratorBE, Field, One};
use ark_relations::r1cs::{Namespace, SynthesisError}; use ark_relations::r1cs::{Namespace, SynthesisError};
@@ -42,8 +42,11 @@ impl<P: Bls12Parameters> G1PreparedVar<P> {
let x = self.0.x.value()?; let x = self.0.x.value()?;
let y = self.0.y.value()?; let y = self.0.y.value()?;
let infinity = self.0.infinity.value()?; let infinity = self.0.infinity.value()?;
let g = GroupAffine::new(x, y, infinity); let g = infinity
Ok(g.into()) .then_some(GroupAffine::zero())
.unwrap_or(GroupAffine::new(x, y))
.into();
Ok(g)
} }
/// Constructs `Self` from a `G1Var`. /// Constructs `Self` from a `G1Var`.
@@ -139,7 +142,7 @@ impl<P: Bls12Parameters> AllocVar<G2Prepared<P>, P::Fp> for G2PreparedVar<P> {
TwistType::D => { TwistType::D => {
let mut z_s = projective_coeffs let mut z_s = projective_coeffs
.iter() .iter()
.map(|(z, _, _)| *z) .map(|(z, ..)| *z)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
ark_ff::fields::batch_inversion(&mut z_s); ark_ff::fields::batch_inversion(&mut z_s);
projective_coeffs projective_coeffs

View File

@@ -1,6 +1,8 @@
use ark_ec::{ use ark_ec::{
short_weierstrass_jacobian::{GroupAffine as SWAffine, GroupProjective as SWProjective}, short_weierstrass::{
AffineCurve, ProjectiveCurve, SWModelParameters, Affine as SWAffine, Projective as SWProjective, SWCurveConfig as SWModelParameters,
},
AffineCurve, ProjectiveCurve,
}; };
use ark_ff::{BigInteger, BitIteratorBE, Field, One, PrimeField, Zero}; use ark_ff::{BigInteger, BitIteratorBE, Field, One, PrimeField, Zero};
use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError}; use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError};
@@ -22,12 +24,14 @@ pub mod mnt4;
/// family of bilinear groups. /// family of bilinear groups.
pub mod mnt6; pub mod mnt6;
/// This module provides a generic implementation of elliptic curve operations for points on /// This module provides a generic implementation of elliptic curve operations
/// short-weierstrass curves in affine coordinates that **are not** equal to zero. /// for points on short-weierstrass curves in affine coordinates that **are
/// not** equal to zero.
/// ///
/// Note: this module is **unsafe** in general: it can synthesize unsatisfiable or /// Note: this module is **unsafe** in general: it can synthesize unsatisfiable
/// underconstrained constraint systems when a represented point _is_ equal to zero. /// or underconstrained constraint systems when a represented point _is_ equal
/// The [ProjectiveVar] gadget is the recommended way of working with elliptic curve points. /// to zero. The [ProjectiveVar] gadget is the recommended way of working with
/// elliptic curve points.
pub mod non_zero_affine; pub mod non_zero_affine;
/// An implementation of arithmetic for Short Weierstrass curves that relies on /// An implementation of arithmetic for Short Weierstrass curves that relies on
/// the complete formulae derived in the paper of /// the complete formulae derived in the paper of
@@ -89,11 +93,10 @@ where
/// Returns the value assigned to `self` in the underlying /// Returns the value assigned to `self` in the underlying
/// constraint system. /// constraint system.
pub fn value(&self) -> Result<SWAffine<P>, SynthesisError> { pub fn value(&self) -> Result<SWAffine<P>, SynthesisError> {
Ok(SWAffine::new( Ok(match self.infinity.value()? {
self.x.value()?, true => SWAffine::zero(),
self.y.value()?, false => SWAffine::new(self.x.value()?, self.y.value()?),
self.infinity.value()?, })
))
} }
} }
@@ -132,7 +135,7 @@ where
fn value(&self) -> Result<Self::Value, SynthesisError> { fn value(&self) -> Result<Self::Value, SynthesisError> {
let (x, y, z) = (self.x.value()?, self.y.value()?, self.z.value()?); let (x, y, z) = (self.x.value()?, self.y.value()?, self.z.value()?);
let result = if let Some(z_inv) = z.inverse() { let result = if let Some(z_inv) = z.inverse() {
SWAffine::new(x * &z_inv, y * &z_inv, false) SWAffine::new(x * &z_inv, y * &z_inv)
} else { } else {
SWAffine::zero() SWAffine::zero()
}; };
@@ -169,8 +172,8 @@ where
let infinity = self.is_zero()?; let infinity = self.is_zero()?;
let zero_x = F::zero(); let zero_x = F::zero();
let zero_y = F::one(); let zero_y = F::one();
// Allocate a variable whose value is either `self.z.inverse()` if the inverse exists, // Allocate a variable whose value is either `self.z.inverse()` if the inverse
// and is zero otherwise. // exists, and is zero otherwise.
let z_inv = F::new_witness(ark_relations::ns!(cs, "z_inverse"), || { let z_inv = F::new_witness(ark_relations::ns!(cs, "z_inverse"), || {
Ok(self.z.value()?.inverse().unwrap_or_else(P::BaseField::zero)) Ok(self.z.value()?.inverse().unwrap_or_else(P::BaseField::zero))
})?; })?;
@@ -230,7 +233,8 @@ where
Ok(Self::new(x, y, z)) Ok(Self::new(x, y, z))
} }
/// Mixed addition, which is useful when `other = (x2, y2)` is known to have z = 1. /// Mixed addition, which is useful when `other = (x2, y2)` is known to have
/// z = 1.
#[tracing::instrument(target = "r1cs", skip(self, other))] #[tracing::instrument(target = "r1cs", skip(self, other))]
pub(crate) fn add_mixed(&self, other: &NonZeroAffineVar<P, F>) -> Result<Self, SynthesisError> { pub(crate) fn add_mixed(&self, other: &NonZeroAffineVar<P, F>) -> Result<Self, SynthesisError> {
// Complete mixed addition formula from Renes-Costello-Batina 2015 // Complete mixed addition formula from Renes-Costello-Batina 2015
@@ -271,7 +275,8 @@ where
Ok(ProjectiveVar::new(x, y, z)) Ok(ProjectiveVar::new(x, y, z))
} }
/// Computes a scalar multiplication with a little-endian scalar of size `P::ScalarField::MODULUS_BITS`. /// Computes a scalar multiplication with a little-endian scalar of size
/// `P::ScalarField::MODULUS_BITS`.
#[tracing::instrument( #[tracing::instrument(
target = "r1cs", target = "r1cs",
skip(self, mul_result, multiple_of_power_of_two, bits) skip(self, mul_result, multiple_of_power_of_two, bits)
@@ -293,27 +298,30 @@ where
// We rely on *incomplete* affine formulae for partially computing this. // We rely on *incomplete* affine formulae for partially computing this.
// However, we avoid exceptional edge cases because we partition the scalar // However, we avoid exceptional edge cases because we partition the scalar
// into two chunks: one guaranteed to be less than p - 2, and the rest. // into two chunks: one guaranteed to be less than p - 2, and the rest.
// We only use incomplete formulae for the first chunk, which means we avoid exceptions: // We only use incomplete formulae for the first chunk, which means we avoid
// exceptions:
// //
// `add_unchecked(a, b)` is incomplete when either `b.is_zero()`, or when // `add_unchecked(a, b)` is incomplete when either `b.is_zero()`, or when
// `b = ±a`. During scalar multiplication, we don't hit either case: // `b = ±a`. During scalar multiplication, we don't hit either case:
// * `b = ±a`: `b = accumulator = k * a`, where `2 <= k < p - 1`. // * `b = ±a`: `b = accumulator = k * a`, where `2 <= k < p - 1`. This implies
// This implies that `k != p ± 1`, and so `b != (p ± 1) * a`. // that `k != p ± 1`, and so `b != (p ± 1) * a`. Because the group is finite,
// Because the group is finite, this in turn means that `b != ±a`, as required. // this in turn means that `b != ±a`, as required.
// * `a` or `b` is zero: for `a`, we handle the zero case after the loop; for `b`, notice // * `a` or `b` is zero: for `a`, we handle the zero case after the loop; for
// that it is monotonically increasing, and furthermore, equals `k * a`, where // `b`, notice that it is monotonically increasing, and furthermore, equals `k
// `k != p = 0 mod p`. // * a`, where `k != p = 0 mod p`.
// Unlike normal double-and-add, here we start off with a non-zero `accumulator`, // Unlike normal double-and-add, here we start off with a non-zero
// because `NonZeroAffineVar::add_unchecked` doesn't support addition with `zero`. // `accumulator`, because `NonZeroAffineVar::add_unchecked` doesn't
// In more detail, we initialize `accumulator` to be the initial value of // support addition with `zero`. In more detail, we initialize
// `multiple_of_power_of_two`. This ensures that all unchecked additions of `accumulator` // `accumulator` to be the initial value of `multiple_of_power_of_two`.
// with later values of `multiple_of_power_of_two` are safe. // This ensures that all unchecked additions of `accumulator` with later
// However, to do this correctly, we need to perform two steps: // values of `multiple_of_power_of_two` are safe. However, to do this
// * We must skip the LSB, and instead proceed assuming that it was 1. Later, we will // correctly, we need to perform two steps:
// conditionally subtract the initial value of `accumulator`: // * We must skip the LSB, and instead proceed assuming that it was 1. Later, we
// if LSB == 0: subtract initial_acc_value; else, subtract 0. // will conditionally subtract the initial value of `accumulator`: if LSB ==
// * Because we are assuming the first bit, we must double `multiple_of_power_of_two`. // 0: subtract initial_acc_value; else, subtract 0.
// * Because we are assuming the first bit, we must double
// `multiple_of_power_of_two`.
let mut accumulator = multiple_of_power_of_two.clone(); let mut accumulator = multiple_of_power_of_two.clone();
let initial_acc_value = accumulator.into_projective(); let initial_acc_value = accumulator.into_projective();
@@ -321,7 +329,8 @@ where
// The powers start at 2 (instead of 1) because we're skipping the first bit. // The powers start at 2 (instead of 1) because we're skipping the first bit.
multiple_of_power_of_two.double_in_place()?; multiple_of_power_of_two.double_in_place()?;
// As mentioned, we will skip the LSB, and will later handle it via a conditional subtraction. // As mentioned, we will skip the LSB, and will later handle it via a
// conditional subtraction.
for bit in affine_bits.iter().skip(1) { for bit in affine_bits.iter().skip(1) {
if bit.is_constant() { if bit.is_constant() {
if *bit == &Boolean::TRUE { if *bit == &Boolean::TRUE {
@@ -335,8 +344,9 @@ where
} }
// Perform conditional subtraction: // Perform conditional subtraction:
// We can convert to projective safely because the result is guaranteed to be non-zero // We can convert to projective safely because the result is guaranteed to be
// by the condition on `affine_bits.len()`, and by the fact that `accumulator` is non-zero // non-zero by the condition on `affine_bits.len()`, and by the fact
// that `accumulator` is non-zero
let result = accumulator.into_projective(); let result = accumulator.into_projective();
// If bits[0] is 0, then we have to subtract `self`; else, we subtract zero. // If bits[0] is 0, then we have to subtract `self`; else, we subtract zero.
let subtrahend = bits[0].select(&Self::zero(), &initial_acc_value)?; let subtrahend = bits[0].select(&Self::zero(), &initial_acc_value)?;

View File

@@ -55,8 +55,9 @@ where
// y3 = lambda * (x1 - x3) - y1 // y3 = lambda * (x1 - x3) - y1
let numerator = y2 - y1; let numerator = y2 - y1;
let denominator = x2 - x1; let denominator = x2 - x1;
// It's okay to use `unchecked` here, because the precondition of `add_unchecked` is that // It's okay to use `unchecked` here, because the precondition of
// self != ±other, which means that `numerator` and `denominator` are both non-zero. // `add_unchecked` is that self != ±other, which means that
// `numerator` and `denominator` are both non-zero.
let lambda = numerator.mul_by_inverse_unchecked(&denominator)?; let lambda = numerator.mul_by_inverse_unchecked(&denominator)?;
let x3 = lambda.square()? - x1 - x2; let x3 = lambda.square()? - x1 - x2;
let y3 = lambda * &(x1 - &x3) - y1; let y3 = lambda * &(x1 - &x3) - y1;
@@ -82,8 +83,8 @@ where
// y3 = lambda * (x1 - x3) - y1 // y3 = lambda * (x1 - x3) - y1
let numerator = x1_sqr.double()? + &x1_sqr + P::COEFF_A; let numerator = x1_sqr.double()? + &x1_sqr + P::COEFF_A;
let denominator = y1.double()?; let denominator = y1.double()?;
// It's okay to use `unchecked` here, because the precondition of `double` is that // It's okay to use `unchecked` here, because the precondition of `double` is
// self != zero. // that self != zero.
let lambda = numerator.mul_by_inverse_unchecked(&denominator)?; let lambda = numerator.mul_by_inverse_unchecked(&denominator)?;
let x3 = lambda.square()? - x1.double()?; let x3 = lambda.square()? - x1.double()?;
let y3 = lambda * &(x1 - &x3) - y1; let y3 = lambda * &(x1 - &x3) - y1;
@@ -91,8 +92,9 @@ where
} }
} }
/// Computes `(self + other) + self`. This method requires only 5 constraints, /// Computes `(self + other) + self`. This method requires only 5
/// less than the 7 required when computing via `self.double() + other`. /// constraints, less than the 7 required when computing via
/// `self.double() + other`.
/// ///
/// This follows the formulae from [\[ELM03\]](https://arxiv.org/abs/math/0208038). /// This follows the formulae from [\[ELM03\]](https://arxiv.org/abs/math/0208038).
#[tracing::instrument(target = "r1cs", skip(self))] #[tracing::instrument(target = "r1cs", skip(self))]
@@ -100,7 +102,8 @@ where
if [self].is_constant() || other.is_constant() { if [self].is_constant() || other.is_constant() {
self.double()?.add_unchecked(other) self.double()?.add_unchecked(other)
} else { } else {
// It's okay to use `unchecked` the precondition is that `self != ±other` (i.e. same logic as in `add_unchecked`) // It's okay to use `unchecked` the precondition is that `self != ±other` (i.e.
// same logic as in `add_unchecked`)
let (x1, y1) = (&self.x, &self.y); let (x1, y1) = (&self.x, &self.y);
let (x2, y2) = (&other.x, &other.y); let (x2, y2) = (&other.x, &other.y);
@@ -145,7 +148,7 @@ where
} }
fn value(&self) -> Result<SWAffine<P>, SynthesisError> { fn value(&self) -> Result<SWAffine<P>, SynthesisError> {
Ok(SWAffine::new(self.x.value()?, self.y.value()?, false)) Ok(SWAffine::new(self.x.value()?, self.y.value()?))
} }
} }
@@ -223,14 +226,17 @@ where
#[cfg(test)] #[cfg(test)]
mod test_non_zero_affine { mod test_non_zero_affine {
use crate::alloc::AllocVar; use crate::{
use crate::eq::EqGadget; alloc::AllocVar,
use crate::fields::fp::{AllocatedFp, FpVar}; eq::EqGadget,
use crate::groups::curves::short_weierstrass::non_zero_affine::NonZeroAffineVar; fields::fp::{AllocatedFp, FpVar},
use crate::groups::curves::short_weierstrass::ProjectiveVar; groups::{
use crate::groups::CurveVar; curves::short_weierstrass::{non_zero_affine::NonZeroAffineVar, ProjectiveVar},
use crate::R1CSVar; CurveVar,
use ark_ec::{ProjectiveCurve, SWModelParameters}; },
R1CSVar,
};
use ark_ec::{models::short_weierstrass::SWCurveConfig, ProjectiveCurve};
use ark_relations::r1cs::ConstraintSystem; use ark_relations::r1cs::ConstraintSystem;
use ark_std::{vec::Vec, One}; use ark_std::{vec::Vec, One};
use ark_test_curves::bls12_381::{g1::Parameters as G1Parameters, Fq}; use ark_test_curves::bls12_381::{g1::Parameters as G1Parameters, Fq};
@@ -240,16 +246,10 @@ mod test_non_zero_affine {
let cs = ConstraintSystem::<Fq>::new_ref(); let cs = ConstraintSystem::<Fq>::new_ref();
let x = FpVar::Var( let x = FpVar::Var(
AllocatedFp::<Fq>::new_witness(cs.clone(), || { AllocatedFp::<Fq>::new_witness(cs.clone(), || Ok(G1Parameters::GENERATOR.x)).unwrap(),
Ok(G1Parameters::AFFINE_GENERATOR_COEFFS.0)
})
.unwrap(),
); );
let y = FpVar::Var( let y = FpVar::Var(
AllocatedFp::<Fq>::new_witness(cs.clone(), || { AllocatedFp::<Fq>::new_witness(cs.clone(), || Ok(G1Parameters::GENERATOR.y)).unwrap(),
Ok(G1Parameters::AFFINE_GENERATOR_COEFFS.1)
})
.unwrap(),
); );
// The following code uses `double` and `add` (`add_unchecked`) to compute // The following code uses `double` and `add` (`add_unchecked`) to compute
@@ -307,16 +307,10 @@ mod test_non_zero_affine {
let cs = ConstraintSystem::<Fq>::new_ref(); let cs = ConstraintSystem::<Fq>::new_ref();
let x = FpVar::Var( let x = FpVar::Var(
AllocatedFp::<Fq>::new_witness(cs.clone(), || { AllocatedFp::<Fq>::new_witness(cs.clone(), || Ok(G1Parameters::GENERATOR.x)).unwrap(),
Ok(G1Parameters::AFFINE_GENERATOR_COEFFS.0)
})
.unwrap(),
); );
let y = FpVar::Var( let y = FpVar::Var(
AllocatedFp::<Fq>::new_witness(cs.clone(), || { AllocatedFp::<Fq>::new_witness(cs.clone(), || Ok(G1Parameters::GENERATOR.y)).unwrap(),
Ok(G1Parameters::AFFINE_GENERATOR_COEFFS.1)
})
.unwrap(),
); );
// The following code tests `double_and_add`. // The following code tests `double_and_add`.
@@ -359,16 +353,10 @@ mod test_non_zero_affine {
let cs = ConstraintSystem::<Fq>::new_ref(); let cs = ConstraintSystem::<Fq>::new_ref();
let x = FpVar::Var( let x = FpVar::Var(
AllocatedFp::<Fq>::new_witness(cs.clone(), || { AllocatedFp::<Fq>::new_witness(cs.clone(), || Ok(G1Parameters::GENERATOR.x)).unwrap(),
Ok(G1Parameters::AFFINE_GENERATOR_COEFFS.0)
})
.unwrap(),
); );
let y = FpVar::Var( let y = FpVar::Var(
AllocatedFp::<Fq>::new_witness(cs.clone(), || { AllocatedFp::<Fq>::new_witness(cs.clone(), || Ok(G1Parameters::GENERATOR.y)).unwrap(),
Ok(G1Parameters::AFFINE_GENERATOR_COEFFS.1)
})
.unwrap(),
); );
let a = NonZeroAffineVar::<G1Parameters, FpVar<Fq>>::new(x, y); let a = NonZeroAffineVar::<G1Parameters, FpVar<Fq>>::new(x, y);

View File

@@ -1,6 +1,9 @@
use ark_ec::{ use ark_ec::{
twisted_edwards_extended::{GroupAffine as TEAffine, GroupProjective as TEProjective}, twisted_edwards::{
AffineCurve, MontgomeryModelParameters, ProjectiveCurve, TEModelParameters, Affine as TEAffine, MontCurveConfig as MontgomeryModelParameter,
Projective as TEProjective, TECurveConfig as TEModelParameters,
},
AffineCurve, ProjectiveCurve,
}; };
use ark_ff::{BigInteger, BitIteratorBE, Field, One, PrimeField, Zero}; use ark_ff::{BigInteger, BitIteratorBE, Field, One, PrimeField, Zero};
@@ -36,7 +39,7 @@ pub struct MontgomeryAffineVar<
mod montgomery_affine_impl { mod montgomery_affine_impl {
use super::*; use super::*;
use ark_ec::twisted_edwards_extended::GroupAffine; use ark_ec::twisted_edwards::Affine as GroupAffine;
use ark_ff::Field; use ark_ff::Field;
use core::ops::Add; use core::ops::Add;
@@ -173,8 +176,8 @@ mod montgomery_affine_impl {
AllocationMode::Witness AllocationMode::Witness
}; };
let coeff_b = P::MontgomeryModelParameters::COEFF_B; let coeff_b = P::MontCurveConfig::COEFF_B;
let coeff_a = P::MontgomeryModelParameters::COEFF_A; let coeff_a = P::MontCurveConfig::COEFF_A;
let lambda = F::new_variable( let lambda = F::new_variable(
ark_relations::ns!(cs, "lambda"), ark_relations::ns!(cs, "lambda"),

View File

@@ -13,6 +13,9 @@
#[macro_use] #[macro_use]
extern crate ark_std; extern crate ark_std;
#[macro_use]
extern crate ark_ff;
#[macro_use] #[macro_use]
extern crate ark_relations; extern crate ark_relations;
@@ -49,7 +52,8 @@ pub mod pairing;
pub mod alloc; pub mod alloc;
/// This module describes a trait for checking equality of variables. /// This module describes a trait for checking equality of variables.
pub mod eq; pub mod eq;
/// This module implements functions for manipulating polynomial variables over finite fields. /// This module implements functions for manipulating polynomial variables over
/// finite fields.
pub mod poly; pub mod poly;
/// This module describes traits for conditionally selecting a variable from a /// This module describes traits for conditionally selecting a variable from a
/// list of variables. /// list of variables.

View File

@@ -1,7 +1,8 @@
use crate::boolean::Boolean; use crate::{
use crate::eq::EqGadget; boolean::Boolean,
use crate::fields::fp::FpVar; eq::EqGadget,
use crate::fields::FieldVar; fields::{fp::FpVar, FieldVar},
};
use ark_ff::PrimeField; use ark_ff::PrimeField;
use ark_relations::r1cs::SynthesisError; use ark_relations::r1cs::SynthesisError;
use ark_std::vec::Vec; use ark_std::vec::Vec;
@@ -9,11 +10,11 @@ use ark_std::vec::Vec;
pub mod vanishing_poly; pub mod vanishing_poly;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
/// Defines an evaluation domain over a prime field. The domain is a coset of size `1<<dim`. /// Defines an evaluation domain over a prime field. The domain is a coset of
/// /// size `1<<dim`.
/// Native code corresponds to `ark-poly::univariate::domain::radix2`, but `ark-poly` only supports
/// subgroup for now.
/// ///
/// Native code corresponds to `ark-poly::univariate::domain::radix2`, but
/// `ark-poly` only supports subgroup for now.
// TODO: support cosets in `ark-poly`. // TODO: support cosets in `ark-poly`.
pub struct Radix2DomainVar<F: PrimeField> { pub struct Radix2DomainVar<F: PrimeField> {
/// generator of subgroup g /// generator of subgroup g
@@ -72,12 +73,14 @@ impl<F: PrimeField> Radix2DomainVar<F> {
1 << self.dim 1 << self.dim
} }
/// For domain `h<g>` with dimension `n`, `position` represented by `query_pos` in big endian form, /// For domain `h<g>` with dimension `n`, `position` represented by
/// returns all points of `h*g^{position}<g^{2^{n-coset_dim}}>`. The result is the query coset at index `query_pos` /// `query_pos` in big endian form, returns all points of
/// for the FRI protocol. /// `h*g^{position}<g^{2^{n-coset_dim}}>`. The result is the query coset at
/// index `query_pos` for the FRI protocol.
/// ///
/// # Panics /// # Panics
/// This function panics when `query_pos.len() != coset_dim` or `query_pos.len() != self.dim`. /// This function panics when `query_pos.len() != coset_dim` or
/// `query_pos.len() != self.dim`.
pub fn query_position_to_coset_elements( pub fn query_position_to_coset_elements(
&self, &self,
query_pos: &[Boolean<F>], query_pos: &[Boolean<F>],
@@ -88,8 +91,9 @@ impl<F: PrimeField> Radix2DomainVar<F> {
.elements()) .elements())
} }
/// For domain `h<g>` with dimension `n`, `position` represented by `query_pos` in big endian form, /// For domain `h<g>` with dimension `n`, `position` represented by
/// returns all points of `h*g^{position}<g^{n-query_pos.len()}>` /// `query_pos` in big endian form, returns all points of
/// `h*g^{position}<g^{n-query_pos.len()}>`
/// ///
/// # Panics /// # Panics
/// This function panics when `query_pos.len() < log2_num_cosets`. /// This function panics when `query_pos.len() < log2_num_cosets`.

View File

@@ -1,12 +1,11 @@
use crate::fields::fp::FpVar; use crate::fields::{fp::FpVar, FieldVar};
use crate::fields::FieldVar;
use ark_ff::{Field, PrimeField}; use ark_ff::{Field, PrimeField};
use ark_relations::r1cs::SynthesisError; use ark_relations::r1cs::SynthesisError;
use ark_std::ops::Sub; use ark_std::ops::Sub;
/// Struct describing vanishing polynomial for a multiplicative coset H where |H| is a power of 2. /// Struct describing vanishing polynomial for a multiplicative coset H where
/// As H is a coset, every element can be described as h*g^i and therefore /// |H| is a power of 2. As H is a coset, every element can be described as
/// has vanishing polynomial Z_H(x) = x^|H| - h^|H| /// h*g^i and therefore has vanishing polynomial Z_H(x) = x^|H| - h^|H|
#[derive(Clone)] #[derive(Clone)]
pub struct VanishingPolynomial<F: Field> { pub struct VanishingPolynomial<F: Field> {
/// h^|H| /// h^|H|
@@ -37,7 +36,8 @@ impl<F: PrimeField> VanishingPolynomial<F> {
} }
/// Evaluates the constraints and just gives you the gadget for the result. /// Evaluates the constraints and just gives you the gadget for the result.
/// Caution for use in holographic lincheck: The output has 2 entries in one matrix /// Caution for use in holographic lincheck: The output has 2 entries in one
/// matrix
pub fn evaluate_constraints(&self, x: &FpVar<F>) -> Result<FpVar<F>, SynthesisError> { pub fn evaluate_constraints(&self, x: &FpVar<F>) -> Result<FpVar<F>, SynthesisError> {
if self.dim_h == 1 { if self.dim_h == 1 {
let result = x.sub(x); let result = x.sub(x);
@@ -55,10 +55,10 @@ impl<F: PrimeField> VanishingPolynomial<F> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::alloc::AllocVar; use crate::{
use crate::fields::fp::FpVar; alloc::AllocVar, fields::fp::FpVar, poly::domain::vanishing_poly::VanishingPolynomial,
use crate::poly::domain::vanishing_poly::VanishingPolynomial; R1CSVar,
use crate::R1CSVar; };
use ark_relations::r1cs::ConstraintSystem; use ark_relations::r1cs::ConstraintSystem;
use ark_std::{test_rng, UniformRand}; use ark_std::{test_rng, UniformRand};
use ark_test_curves::bls12_381::Fr; use ark_test_curves::bls12_381::Fr;

View File

@@ -31,16 +31,14 @@ impl<F: PrimeField> LagrangeInterpolator<F> {
cur_elem *= domain_generator; cur_elem *= domain_generator;
all_domain_elems.push(cur_elem); all_domain_elems.push(cur_elem);
} }
/* // By computing the following elements as constants,
By computing the following elements as constants, // we can further reduce the interpolation costs.
we can further reduce the interpolation costs. //
// m = order of the interpolation domain
m = order of the interpolation domain // v_inv[i] = prod_{j != i} h(g^i - g^j)
v_inv[i] = prod_{j != i} h(g^i - g^j) // We use the following facts to compute this:
We use the following facts to compute this: // v_inv[0] = m*h^{m-1}
v_inv[0] = m*h^{m-1} // v_inv[i] = g^{-1} * v_inv[i-1]
v_inv[i] = g^{-1} * v_inv[i-1]
*/
// TODO: Include proof of the above two points // TODO: Include proof of the above two points
let g_inv = domain_generator.inverse().unwrap(); let g_inv = domain_generator.inverse().unwrap();
let m = F::from((1 << domain_dim) as u128); let m = F::from((1 << domain_dim) as u128);
@@ -64,16 +62,14 @@ impl<F: PrimeField> LagrangeInterpolator<F> {
} }
pub(crate) fn compute_lagrange_coefficients(&self, interpolation_point: F) -> Vec<F> { pub(crate) fn compute_lagrange_coefficients(&self, interpolation_point: F) -> Vec<F> {
/* // Let t be the interpolation point, H be the multiplicative coset, with
* Let t be the interpolation point, H be the multiplicative coset, with elements of the form h*g^i. // elements of the form h*g^i. Compute each L_{i,H}(t) as Z_{H}(t) * v_i
Compute each L_{i,H}(t) as Z_{H}(t) * v_i / (t- h g^i) // / (t- h g^i) where:
where: // - Z_{H}(t) = \prod_{j} (t-h*g^j) = (t^m-h^m), and
- Z_{H}(t) = \prod_{j} (t-h*g^j) = (t^m-h^m), and // - v_{i} = 1 / \prod_{j \neq i} h(g^i-g^j).
- v_{i} = 1 / \prod_{j \neq i} h(g^i-g^j). // Below we use the fact that v_{0} = 1/(m * h^(m-1)) and v_{i+1} = g * v_{i}.
Below we use the fact that v_{0} = 1/(m * h^(m-1)) and v_{i+1} = g * v_{i}. // We first compute the inverse of each coefficient, except for the Z_H(t) term.
We first compute the inverse of each coefficient, except for the Z_H(t) term. // We then batch invert the entire result, and multiply by Z_H(t).
We then batch invert the entire result, and multiply by Z_H(t).
*/
let mut inverted_lagrange_coeffs: Vec<F> = Vec::with_capacity(self.all_domain_elems.len()); let mut inverted_lagrange_coeffs: Vec<F> = Vec::with_capacity(self.all_domain_elems.len());
for i in 0..self.domain_order { for i in 0..self.domain_order {
let l = self.v_inv_elems[i]; let l = self.v_inv_elems[i];
@@ -98,14 +94,16 @@ impl<F: PrimeField> LagrangeInterpolator<F> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::fields::fp::FpVar; use crate::{
use crate::fields::FieldVar; fields::{fp::FpVar, FieldVar},
use crate::poly::domain::Radix2DomainVar; poly::{
use crate::poly::evaluations::univariate::lagrange_interpolator::LagrangeInterpolator; domain::Radix2DomainVar,
use crate::R1CSVar; evaluations::univariate::lagrange_interpolator::LagrangeInterpolator,
},
R1CSVar,
};
use ark_ff::{FftField, Field, One}; use ark_ff::{FftField, Field, One};
use ark_poly::univariate::DensePolynomial; use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, Polynomial};
use ark_poly::{Polynomial, UVPolynomial};
use ark_std::{test_rng, UniformRand}; use ark_std::{test_rng, UniformRand};
use ark_test_curves::bls12_381::Fr; use ark_test_curves::bls12_381::Fr;

View File

@@ -1,16 +1,21 @@
pub mod lagrange_interpolator; pub mod lagrange_interpolator;
use crate::alloc::AllocVar; use crate::{
use crate::eq::EqGadget; alloc::AllocVar,
use crate::fields::fp::FpVar; eq::EqGadget,
use crate::fields::FieldVar; fields::{fp::FpVar, FieldVar},
use crate::poly::domain::Radix2DomainVar; poly::{
use crate::poly::evaluations::univariate::lagrange_interpolator::LagrangeInterpolator; domain::Radix2DomainVar,
use crate::R1CSVar; evaluations::univariate::lagrange_interpolator::LagrangeInterpolator,
},
R1CSVar,
};
use ark_ff::{batch_inversion, PrimeField}; use ark_ff::{batch_inversion, PrimeField};
use ark_relations::r1cs::SynthesisError; use ark_relations::r1cs::SynthesisError;
use ark_std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; use ark_std::{
use ark_std::vec::Vec; ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign},
vec::Vec,
};
#[derive(Clone)] #[derive(Clone)]
/// Stores a UV polynomial in evaluation form. /// Stores a UV polynomial in evaluation form.
@@ -22,8 +27,9 @@ pub struct EvaluationsVar<F: PrimeField> {
domain: Radix2DomainVar<F>, domain: Radix2DomainVar<F>,
/// Contains all domain elements of `domain.base_domain`. /// Contains all domain elements of `domain.base_domain`.
/// ///
/// This is a cache for lagrange interpolation when offset is non-constant. Will be `None` if offset is constant /// This is a cache for lagrange interpolation when offset is non-constant.
/// or `interpolate` is set to `false`. /// Will be `None` if offset is constant or `interpolate` is set to
/// `false`.
subgroup_points: Option<Vec<F>>, subgroup_points: Option<Vec<F>>,
} }
@@ -54,7 +60,8 @@ impl<F: PrimeField> EvaluationsVar<F> {
ev ev
} }
/// Precompute necessary calculation for lagrange interpolation and mark it ready to interpolate /// Precompute necessary calculation for lagrange interpolation and mark it
/// ready to interpolate
pub fn generate_interpolation_cache(&mut self) { pub fn generate_interpolation_cache(&mut self) {
if self.domain.offset().is_constant() { if self.domain.offset().is_constant() {
let poly_evaluations_val: Vec<_> = let poly_evaluations_val: Vec<_> =
@@ -67,7 +74,8 @@ impl<F: PrimeField> EvaluationsVar<F> {
}; };
self.lagrange_interpolator = Some(lagrange_interpolator) self.lagrange_interpolator = Some(lagrange_interpolator)
} else { } else {
// calculate all elements of base subgroup so that in later part we don't need to calculate the exponents again // calculate all elements of base subgroup so that in later part we don't need
// to calculate the exponents again
let mut subgroup_points = Vec::with_capacity(self.domain.size() as usize); let mut subgroup_points = Vec::with_capacity(self.domain.size() as usize);
subgroup_points.push(F::one()); subgroup_points.push(F::one());
for i in 1..self.domain.size() as usize { for i in 1..self.domain.size() as usize {
@@ -77,8 +85,8 @@ impl<F: PrimeField> EvaluationsVar<F> {
} }
} }
/// Compute lagrange coefficients for each evaluation, given `interpolation_point`. /// Compute lagrange coefficients for each evaluation, given
/// Only valid if the domain offset is constant. /// `interpolation_point`. Only valid if the domain offset is constant.
fn compute_lagrange_coefficients( fn compute_lagrange_coefficients(
&self, &self,
interpolation_point: &FpVar<F>, interpolation_point: &FpVar<F>,
@@ -94,8 +102,8 @@ impl<F: PrimeField> EvaluationsVar<F> {
let lagrange_coeffs = let lagrange_coeffs =
lagrange_interpolator.compute_lagrange_coefficients(t.value().unwrap()); lagrange_interpolator.compute_lagrange_coefficients(t.value().unwrap());
let mut lagrange_coeffs_fg = Vec::new(); let mut lagrange_coeffs_fg = Vec::new();
// Now we convert these lagrange coefficients to gadgets, and then constrain them. // Now we convert these lagrange coefficients to gadgets, and then constrain
// The i-th lagrange coefficients constraint is: // them. The i-th lagrange coefficients constraint is:
// (v_inv[i] * t - v_inv[i] * domain_elem[i]) * (coeff) = 1/Z_I(t) // (v_inv[i] * t - v_inv[i] * domain_elem[i]) * (coeff) = 1/Z_I(t)
let vp_t = lagrange_interpolator.domain_vp.evaluate_constraints(t)?; let vp_t = lagrange_interpolator.domain_vp.evaluate_constraints(t)?;
// let inv_vp_t = vp_t.inverse()?; // let inv_vp_t = vp_t.inverse()?;
@@ -124,16 +132,19 @@ impl<F: PrimeField> EvaluationsVar<F> {
Ok(lagrange_coeffs_fg) Ok(lagrange_coeffs_fg)
} }
/// Returns constraints for Interpolating and evaluating at `interpolation_point` /// Returns constraints for Interpolating and evaluating at
/// `interpolation_point`
pub fn interpolate_and_evaluate( pub fn interpolate_and_evaluate(
&self, &self,
interpolation_point: &FpVar<F>, interpolation_point: &FpVar<F>,
) -> Result<FpVar<F>, SynthesisError> { ) -> Result<FpVar<F>, SynthesisError> {
// specialize: if domain offset is constant, we can optimize to have fewer constraints // specialize: if domain offset is constant, we can optimize to have fewer
// constraints
if self.domain.offset().is_constant() { if self.domain.offset().is_constant() {
self.lagrange_interpolate_with_constant_offset(interpolation_point) self.lagrange_interpolate_with_constant_offset(interpolation_point)
} else { } else {
// if domain offset is not constant, then we use standard lagrange interpolation code // if domain offset is not constant, then we use standard lagrange interpolation
// code
self.lagrange_interpolate_with_non_constant_offset(interpolation_point) self.lagrange_interpolate_with_non_constant_offset(interpolation_point)
} }
} }
@@ -156,7 +167,8 @@ impl<F: PrimeField> EvaluationsVar<F> {
Ok(interpolation) Ok(interpolation)
} }
/// Generate interpolation constraints. We assume at compile time we know the base coset (i.e. `gen`) but not know `offset`. /// Generate interpolation constraints. We assume at compile time we know
/// the base coset (i.e. `gen`) but not know `offset`.
fn lagrange_interpolate_with_non_constant_offset( fn lagrange_interpolate_with_non_constant_offset(
&self, &self,
interpolation_point: &FpVar<F>, // = alpha in the following code interpolation_point: &FpVar<F>, // = alpha in the following code
@@ -167,10 +179,12 @@ impl<F: PrimeField> EvaluationsVar<F> {
Call `self.generate_interpolation_cache` first or set `interpolate` to true in constructor. "); Call `self.generate_interpolation_cache` first or set `interpolate` to true in constructor. ");
// Let denote interpolation_point as alpha. // Let denote interpolation_point as alpha.
// Lagrange polynomial for coset element `a` is // Lagrange polynomial for coset element `a` is
// \frac{1}{size * offset ^ size} * \frac{alpha^size - offset^size}{alpha * a^{-1} - 1} // \frac{1}{size * offset ^ size} * \frac{alpha^size - offset^size}{alpha *
// Notice that a = (offset * a') where a' is the corresponding element of base coset // a^{-1} - 1} Notice that a = (offset * a') where a' is the
// corresponding element of base coset
// let `lhs` be \frac{alpha^size - offset^size}{size * offset ^ size}. This part is shared by all lagrange polynomials // let `lhs` be \frac{alpha^size - offset^size}{size * offset ^ size}. This part
// is shared by all lagrange polynomials
let coset_offset_to_size = self let coset_offset_to_size = self
.domain .domain
.offset() .offset()
@@ -181,14 +195,16 @@ impl<F: PrimeField> EvaluationsVar<F> {
// This also means that the denominator is // This also means that the denominator is
lhs_numerator.enforce_not_equal(&FpVar::zero())?; lhs_numerator.enforce_not_equal(&FpVar::zero())?;
// `domain.offset()` is non-zero by construction, so `coset_offset_to_size` is also non-zero, which means `lhs_denominator` is non-zero // `domain.offset()` is non-zero by construction, so `coset_offset_to_size` is
// also non-zero, which means `lhs_denominator` is non-zero
let lhs_denominator = &coset_offset_to_size * FpVar::constant(F::from(self.domain.size())); let lhs_denominator = &coset_offset_to_size * FpVar::constant(F::from(self.domain.size()));
// unchecked is okay because the denominator is non-zero. // unchecked is okay because the denominator is non-zero.
let lhs = lhs_numerator.mul_by_inverse_unchecked(&lhs_denominator)?; let lhs = lhs_numerator.mul_by_inverse_unchecked(&lhs_denominator)?;
// `rhs` for coset element `a` is \frac{1}{alpha * a^{-1} - 1} = \frac{1}{alpha * offset^{-1} * a'^{-1} - 1} // `rhs` for coset element `a` is \frac{1}{alpha * a^{-1} - 1} = \frac{1}{alpha
// domain.offset() is non-zero by construction. // * offset^{-1} * a'^{-1} - 1} domain.offset() is non-zero by
// construction.
let alpha_coset_offset_inv = let alpha_coset_offset_inv =
interpolation_point.mul_by_inverse_unchecked(&self.domain.offset())?; interpolation_point.mul_by_inverse_unchecked(&self.domain.offset())?;
@@ -204,12 +220,14 @@ impl<F: PrimeField> EvaluationsVar<F> {
let lag_denom = &alpha_coset_offset_inv * subgroup_point_inv - F::one(); let lag_denom = &alpha_coset_offset_inv * subgroup_point_inv - F::one();
// lag_denom cannot be zero, so we use `unchecked`. // lag_denom cannot be zero, so we use `unchecked`.
// //
// Proof: lag_denom is zero if and only if alpha * (coset_offset * subgroup_point)^{-1} == 1. // Proof: lag_denom is zero if and only if alpha * (coset_offset *
// This can happen only if `alpha` is itself in the coset. // subgroup_point)^{-1} == 1. This can happen only if `alpha` is
// itself in the coset.
// //
// Earlier we asserted that `lhs_numerator` is not zero. // Earlier we asserted that `lhs_numerator` is not zero.
// Since `lhs_numerator` is just the vanishing polynomial for the coset evaluated at `alpha`, // Since `lhs_numerator` is just the vanishing polynomial for the coset
// and since this is non-zero, `alpha` is not in the coset. // evaluated at `alpha`, and since this is non-zero, `alpha` is not
// in the coset.
let lag_coeff = lhs.mul_by_inverse_unchecked(&lag_denom)?; let lag_coeff = lhs.mul_by_inverse_unchecked(&lag_denom)?;
let lag_interpoland = &self.evals[i] * lag_coeff; let lag_interpoland = &self.evals[i] * lag_coeff;
@@ -346,15 +364,14 @@ impl<'a, F: PrimeField> DivAssign<&'a EvaluationsVar<F>> for EvaluationsVar<F> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::alloc::AllocVar; use crate::{
use crate::fields::fp::FpVar; alloc::AllocVar,
use crate::fields::FieldVar; fields::{fp::FpVar, FieldVar},
use crate::poly::domain::Radix2DomainVar; poly::{domain::Radix2DomainVar, evaluations::univariate::EvaluationsVar},
use crate::poly::evaluations::univariate::EvaluationsVar; R1CSVar,
use crate::R1CSVar; };
use ark_ff::{FftField, Field, One, UniformRand}; use ark_ff::{FftField, Field, One, UniformRand};
use ark_poly::polynomial::univariate::DensePolynomial; use ark_poly::{polynomial::univariate::DensePolynomial, DenseUVPolynomial, Polynomial};
use ark_poly::{Polynomial, UVPolynomial};
use ark_relations::r1cs::ConstraintSystem; use ark_relations::r1cs::ConstraintSystem;
use ark_std::test_rng; use ark_std::test_rng;
use ark_test_curves::bls12_381::Fr; use ark_test_curves::bls12_381::Fr;

View File

@@ -1,11 +1,11 @@
use ark_ff::PrimeField; use ark_ff::PrimeField;
use ark_relations::r1cs::SynthesisError; use ark_relations::r1cs::SynthesisError;
use crate::fields::fp::FpVar; use crate::fields::{fp::FpVar, FieldVar};
use crate::fields::FieldVar;
use ark_std::vec::Vec; use ark_std::vec::Vec;
/// Stores a polynomial in coefficient form, where coeffcient is represented by a list of `Fpvar<F>`. /// Stores a polynomial in coefficient form, where coeffcient is represented by
/// a list of `Fpvar<F>`.
pub struct DensePolynomialVar<F: PrimeField> { pub struct DensePolynomialVar<F: PrimeField> {
/// The coefficient of `x^i` is stored at location `i` in `self.coeffs`. /// The coefficient of `x^i` is stored at location `i` in `self.coeffs`.
pub coeffs: Vec<FpVar<F>>, pub coeffs: Vec<FpVar<F>>,
@@ -22,8 +22,9 @@ impl<F: PrimeField> DensePolynomialVar<F> {
Self { coeffs } Self { coeffs }
} }
/// Evaluates `self` at the given `point` and just gives you the gadget for the result. /// Evaluates `self` at the given `point` and just gives you the gadget for
/// Caution for use in holographic lincheck: The output has 2 entries in one matrix /// the result. Caution for use in holographic lincheck: The output has
/// 2 entries in one matrix
pub fn evaluate(&self, point: &FpVar<F>) -> Result<FpVar<F>, SynthesisError> { pub fn evaluate(&self, point: &FpVar<F>) -> Result<FpVar<F>, SynthesisError> {
let mut result: FpVar<F> = FpVar::zero(); let mut result: FpVar<F> = FpVar::zero();
// current power of point // current power of point
@@ -40,15 +41,13 @@ impl<F: PrimeField> DensePolynomialVar<F> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::alloc::AllocVar; use crate::{
use crate::fields::fp::FpVar; alloc::AllocVar, fields::fp::FpVar,
use crate::poly::polynomial::univariate::dense::DensePolynomialVar; poly::polynomial::univariate::dense::DensePolynomialVar, R1CSVar,
use crate::R1CSVar; };
use ark_poly::polynomial::univariate::DensePolynomial; use ark_poly::{polynomial::univariate::DensePolynomial, DenseUVPolynomial, Polynomial};
use ark_poly::{Polynomial, UVPolynomial};
use ark_relations::r1cs::ConstraintSystem; use ark_relations::r1cs::ConstraintSystem;
use ark_std::vec::Vec; use ark_std::{test_rng, vec::Vec, UniformRand};
use ark_std::{test_rng, UniformRand};
use ark_test_curves::bls12_381::Fr; use ark_test_curves::bls12_381::Fr;
#[test] #[test]

View File

@@ -21,10 +21,12 @@ where
) -> Result<Self, SynthesisError>; ) -> Result<Self, SynthesisError>;
/// Returns an element of `values` whose index in represented by `position`. /// Returns an element of `values` whose index in represented by `position`.
/// `position` is an array of boolean that represents an unsigned integer in big endian order. /// `position` is an array of boolean that represents an unsigned integer in
/// big endian order.
/// ///
/// # Example /// # Example
/// To get the 6th element of `values`, convert unsigned integer 6 (`0b110`) to `position = [True, True, False]`, /// To get the 6th element of `values`, convert unsigned integer 6 (`0b110`)
/// to `position = [True, True, False]`,
/// and call `conditionally_select_power_of_two_vector(position, values)`. /// and call `conditionally_select_power_of_two_vector(position, values)`.
fn conditionally_select_power_of_two_vector( fn conditionally_select_power_of_two_vector(
position: &[Boolean<ConstraintF>], position: &[Boolean<ConstraintF>],

View File

@@ -6,8 +6,15 @@ use ark_mnt4_753::MNT4_753;
use ark_mnt6_298::MNT6_298; use ark_mnt6_298::MNT6_298;
use ark_mnt6_753::MNT6_753; use ark_mnt6_753::MNT6_753;
use ark_r1cs_std::fields::nonnative::{AllocatedNonNativeFieldVar, NonNativeFieldVar}; use ark_r1cs_std::{
use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget, fields::FieldVar, R1CSVar}; alloc::AllocVar,
eq::EqGadget,
fields::{
nonnative::{AllocatedNonNativeFieldVar, NonNativeFieldVar},
FieldVar,
},
R1CSVar,
};
use ark_relations::r1cs::{ConstraintSystem, ConstraintSystemRef}; use ark_relations::r1cs::{ConstraintSystem, ConstraintSystemRef};
use ark_std::rand::RngCore; use ark_std::rand::RngCore;
@@ -464,7 +471,8 @@ fn double_stress_test_1<TargetField: PrimeField, BaseField: PrimeField, R: RngCo
|| Ok(num_native), || Ok(num_native),
) )
.unwrap(); .unwrap();
// Add to at least BaseField::size_in_bits() to ensure that we teat the overflowing // Add to at least BaseField::size_in_bits() to ensure that we teat the
// overflowing
for _ in 0..TEST_COUNT + BaseField::MODULUS_BIT_SIZE as usize { for _ in 0..TEST_COUNT + BaseField::MODULUS_BIT_SIZE as usize {
// double // double
num_native = num_native + &num_native; num_native = num_native + &num_native;

View File

@@ -1,6 +1,8 @@
use ark_r1cs_std::alloc::AllocVar; use ark_r1cs_std::{
use ark_r1cs_std::fields::nonnative::{NonNativeFieldMulResultVar, NonNativeFieldVar}; alloc::AllocVar,
use ark_r1cs_std::R1CSVar; fields::nonnative::{NonNativeFieldMulResultVar, NonNativeFieldVar},
R1CSVar,
};
use ark_relations::r1cs::ConstraintSystem; use ark_relations::r1cs::ConstraintSystem;
use ark_std::UniformRand; use ark_std::UniformRand;

View File

@@ -1,50 +0,0 @@
use ark_ec::PairingEngine;
use ark_ff::{to_bytes, Zero};
use ark_mnt4_298::MNT4_298;
use ark_mnt6_298::MNT6_298;
use ark_r1cs_std::alloc::AllocVar;
use ark_r1cs_std::fields::nonnative::NonNativeFieldVar;
use ark_r1cs_std::{R1CSVar, ToBitsGadget, ToBytesGadget};
use ark_relations::r1cs::ConstraintSystem;
#[test]
fn to_bytes_test() {
let cs = ConstraintSystem::<<MNT6_298 as PairingEngine>::Fr>::new_ref();
let target_test_elem = <MNT4_298 as PairingEngine>::Fr::from(123456u128);
let target_test_gadget = NonNativeFieldVar::<
<MNT4_298 as PairingEngine>::Fr,
<MNT6_298 as PairingEngine>::Fr,
>::new_witness(cs, || Ok(target_test_elem))
.unwrap();
let target_to_bytes: Vec<u8> = target_test_gadget
.to_bytes()
.unwrap()
.iter()
.map(|v| v.value().unwrap())
.collect();
// 123456 = 65536 + 226 * 256 + 64
assert_eq!(target_to_bytes[0], 64);
assert_eq!(target_to_bytes[1], 226);
assert_eq!(target_to_bytes[2], 1);
for byte in target_to_bytes.iter().skip(3) {
assert_eq!(*byte, 0);
}
assert_eq!(to_bytes!(target_test_elem).unwrap(), target_to_bytes);
}
#[test]
fn to_bits_test() {
type F = ark_bls12_377::Fr;
type CF = ark_bls12_377::Fq;
let cs = ConstraintSystem::<CF>::new_ref();
let f = F::zero();
let f_var = NonNativeFieldVar::<F, CF>::new_input(cs.clone(), || Ok(f)).unwrap();
f_var.to_bits_le().unwrap();
}

View File

@@ -1,6 +1,6 @@
use ark_r1cs_std::alloc::AllocVar; use ark_r1cs_std::{
use ark_r1cs_std::fields::nonnative::NonNativeFieldVar; alloc::AllocVar, fields::nonnative::NonNativeFieldVar, R1CSVar, ToConstraintFieldGadget,
use ark_r1cs_std::{R1CSVar, ToConstraintFieldGadget}; };
use ark_relations::r1cs::ConstraintSystem; use ark_relations::r1cs::ConstraintSystem;
#[test] #[test]