Browse Source

Rename `NonNativeFieldVar` -> `EmulatedFpVar` (#135)

avoid_assigned_value
Pratyush Mishra 1 year ago
committed by GitHub
parent
commit
ed2d55e6ff
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 669 additions and 740 deletions
  1. +17
    -0
      CHANGELOG.md
  2. +1
    -1
      Cargo.toml
  3. +31
    -47
      benches/bench.rs
  4. +168
    -182
      src/fields/emulated_fp/allocated_field_var.rs
  5. +56
    -62
      src/fields/emulated_fp/allocated_mul_result.rs
  6. +99
    -126
      src/fields/emulated_fp/field_var.rs
  7. +18
    -18
      src/fields/emulated_fp/mod.rs
  8. +73
    -0
      src/fields/emulated_fp/mul_result.rs
  9. +1
    -1
      src/fields/emulated_fp/params.rs
  10. +57
    -72
      src/fields/emulated_fp/reduce.rs
  11. +2
    -2
      src/fields/mod.rs
  12. +0
    -79
      src/fields/nonnative/mul_result.rs
  13. +6
    -6
      src/groups/curves/short_weierstrass/mod.rs
  14. +4
    -4
      src/groups/curves/twisted_edwards/mod.rs
  15. +4
    -4
      src/groups/mod.rs
  16. +126
    -130
      tests/arithmetic_tests.rs
  17. +3
    -3
      tests/from_test.rs
  18. +3
    -3
      tests/to_constraint_field_test.rs

+ 17
- 0
CHANGELOG.md

@ -1,6 +1,23 @@
# CHANGELOG # CHANGELOG
## Pending ## Pending
### Breaking changes
- [\#134](https://github.com/arkworks-rs/r1cs-std/pull/134) Add `Mul<NonnativeFieldVar>` bounds and impls for `CurveVar`.
- [\#135](https://github.com/arkworks-rs/r1cs-std/pull/135)
- Rename `NonNativeFieldVar` to `EmulatedFpVar`.
- Rename `fields::nonnative` to `fields::emulated_fp`.
- Rename `fields::nonnative::{Allocated}NonNativeMulResultVar` to `fields::emulated_fp::{Allocated}MulResultVar`.
### Features
### Improvements
### Bug Fixes
## 0.4.0
- [\#117](https://github.com/arkworks-rs/r1cs-std/pull/117) Fix result of `precomputed_base_scalar_mul_le` to not discard previous value. - [\#117](https://github.com/arkworks-rs/r1cs-std/pull/117) Fix result of `precomputed_base_scalar_mul_le` to not discard previous value.
- [\#124](https://github.com/arkworks-rs/r1cs-std/pull/124) Fix `scalar_mul_le` constraints unsatisfiability when short Weierstrass point is zero. - [\#124](https://github.com/arkworks-rs/r1cs-std/pull/124) Fix `scalar_mul_le` constraints unsatisfiability when short Weierstrass point is zero.
- [\#127](https://github.com/arkworks-rs/r1cs-std/pull/127) Convert `NonNativeFieldVar` constants to little-endian bytes instead of big-endian (`ToBytesGadget`). - [\#127](https://github.com/arkworks-rs/r1cs-std/pull/127) Convert `NonNativeFieldVar` constants to little-endian bytes instead of big-endian (`ToBytesGadget`).

+ 1
- 1
Cargo.toml

@ -44,7 +44,7 @@ std = [ "ark-ff/std", "ark-relations/std", "ark-std/std", "num-bigint/std" ]
parallel = [ "std", "ark-ff/parallel", "ark-std/parallel"] parallel = [ "std", "ark-ff/parallel", "ark-std/parallel"]
[[bench]] [[bench]]
name = "nonnative-bench"
name = "emulated-bench"
path = "benches/bench.rs" path = "benches/bench.rs"
harness = false harness = false

+ 31
- 47
benches/bench.rs

@ -2,7 +2,7 @@ use ark_ff::PrimeField;
use ark_r1cs_std::{ use ark_r1cs_std::{
alloc::AllocVar, alloc::AllocVar,
eq::EqGadget, eq::EqGadget,
fields::{nonnative::NonNativeFieldVar, FieldVar},
fields::{emulated_fp::EmulatedFpVar, FieldVar},
}; };
use ark_relations::{ use ark_relations::{
ns, ns,
@ -26,20 +26,18 @@ fn get_density(cs: &ConstraintSystemRef) -> us
} }
} }
fn allocation<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>(
fn allocation<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>(
cs: ConstraintSystemRef<BaseField>, cs: ConstraintSystemRef<BaseField>,
rng: &mut R, rng: &mut R,
) -> (usize, usize) { ) -> (usize, usize) {
let a_native = TargetField::rand(rng);
let a_native = TargetF::rand(rng);
let constraints_before = cs.num_constraints(); let constraints_before = cs.num_constraints();
let nonzeros_before = get_density(&cs); let nonzeros_before = get_density(&cs);
// There will be a check that ensures it has the reasonable number of bits // There will be a check that ensures it has the reasonable number of bits
let _ = NonNativeFieldVar::<TargetField, BaseField>::new_witness(ns!(cs, "alloc a"), || {
Ok(a_native)
})
.unwrap();
let _ = EmulatedFpVar::<TargetF, BaseField>::new_witness(ns!(cs, "alloc a"), || Ok(a_native))
.unwrap();
let constraints_after = cs.num_constraints(); let constraints_after = cs.num_constraints();
let nonzeros_after = get_density(&cs); let nonzeros_after = get_density(&cs);
@ -50,21 +48,17 @@ fn allocation(
); );
} }
fn addition<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>(
fn addition<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>(
cs: ConstraintSystemRef<BaseField>, cs: ConstraintSystemRef<BaseField>,
rng: &mut R, rng: &mut R,
) -> (usize, usize) { ) -> (usize, usize) {
let a_native = TargetField::rand(rng);
let a = NonNativeFieldVar::<TargetField, BaseField>::new_witness(ns!(cs, "alloc a"), || {
Ok(a_native)
})
.unwrap();
let b_native = TargetField::rand(rng);
let b = NonNativeFieldVar::<TargetField, BaseField>::new_witness(ns!(cs, "alloc b"), || {
Ok(b_native)
})
.unwrap();
let a_native = TargetF::rand(rng);
let a = EmulatedFpVar::<TargetF, BaseField>::new_witness(ns!(cs, "alloc a"), || Ok(a_native))
.unwrap();
let b_native = TargetF::rand(rng);
let b = EmulatedFpVar::<TargetF, BaseField>::new_witness(ns!(cs, "alloc b"), || Ok(b_native))
.unwrap();
let constraints_before = cs.num_constraints(); let constraints_before = cs.num_constraints();
let nonzeros_before = get_density(&cs); let nonzeros_before = get_density(&cs);
@ -80,19 +74,15 @@ fn addition(
); );
} }
fn equality<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>(
fn equality<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>(
cs: ConstraintSystemRef<BaseField>, cs: ConstraintSystemRef<BaseField>,
rng: &mut R, rng: &mut R,
) -> (usize, usize) { ) -> (usize, usize) {
let a_native = TargetField::rand(rng);
let a1 = NonNativeFieldVar::<TargetField, BaseField>::new_witness(ns!(cs, "alloc a1"), || {
Ok(a_native)
})
.unwrap();
let a2 = NonNativeFieldVar::<TargetField, BaseField>::new_witness(ns!(cs, "alloc a2"), || {
Ok(a_native)
})
.unwrap();
let a_native = TargetF::rand(rng);
let a1 = EmulatedFpVar::<TargetF, BaseField>::new_witness(ns!(cs, "alloc a1"), || Ok(a_native))
.unwrap();
let a2 = EmulatedFpVar::<TargetF, BaseField>::new_witness(ns!(cs, "alloc a2"), || Ok(a_native))
.unwrap();
let constraints_before = cs.num_constraints(); let constraints_before = cs.num_constraints();
let nonzeros_before = get_density(&cs); let nonzeros_before = get_density(&cs);
@ -108,21 +98,17 @@ fn equality(
); );
} }
fn multiplication<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>(
fn multiplication<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>(
cs: ConstraintSystemRef<BaseField>, cs: ConstraintSystemRef<BaseField>,
rng: &mut R, rng: &mut R,
) -> (usize, usize) { ) -> (usize, usize) {
let a_native = TargetField::rand(rng);
let a = NonNativeFieldVar::<TargetField, BaseField>::new_witness(ns!(cs, "initial a"), || {
Ok(a_native)
})
.unwrap();
let b_native = TargetField::rand(rng);
let b = NonNativeFieldVar::<TargetField, BaseField>::new_witness(ns!(cs, "initial b"), || {
Ok(b_native)
})
.unwrap();
let a_native = TargetF::rand(rng);
let a = EmulatedFpVar::<TargetF, BaseField>::new_witness(ns!(cs, "initial a"), || Ok(a_native))
.unwrap();
let b_native = TargetF::rand(rng);
let b = EmulatedFpVar::<TargetF, BaseField>::new_witness(ns!(cs, "initial b"), || Ok(b_native))
.unwrap();
let constraints_before = cs.num_constraints(); let constraints_before = cs.num_constraints();
let nonzeros_before = get_density(&cs); let nonzeros_before = get_density(&cs);
@ -138,15 +124,13 @@ fn multiplication(
); );
} }
fn inverse<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>(
fn inverse<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>(
cs: ConstraintSystemRef<BaseField>, cs: ConstraintSystemRef<BaseField>,
rng: &mut R, rng: &mut R,
) -> (usize, usize) { ) -> (usize, usize) {
let num_native = TargetField::rand(rng);
let num = NonNativeFieldVar::<TargetField, BaseField>::new_witness(ns!(cs, "alloc"), || {
Ok(num_native)
})
.unwrap();
let num_native = TargetF::rand(rng);
let num = EmulatedFpVar::<TargetF, BaseField>::new_witness(ns!(cs, "alloc"), || Ok(num_native))
.unwrap();
let constraints_before = cs.num_constraints(); let constraints_before = cs.num_constraints();
let nonzeros_before = get_density(&cs); let nonzeros_before = get_density(&cs);

src/fields/nonnative/allocated_field_var.rs → src/fields/emulated_fp/allocated_field_var.rs

@ -1,7 +1,7 @@
use super::{ use super::{
params::{get_params, OptimizationType}, params::{get_params, OptimizationType},
reduce::{bigint_to_basefield, limbs_to_bigint, Reducer}, reduce::{bigint_to_basefield, limbs_to_bigint, Reducer},
AllocatedNonNativeFieldMulResultVar,
AllocatedMulResultVar,
}; };
use crate::{fields::fp::FpVar, prelude::*, ToConstraintFieldGadget}; use crate::{fields::fp::FpVar, prelude::*, ToConstraintFieldGadget};
use ark_ff::{BigInteger, PrimeField}; use ark_ff::{BigInteger, PrimeField};
@ -19,58 +19,53 @@ use ark_std::{
vec::Vec, vec::Vec,
}; };
/// The allocated version of `NonNativeFieldVar` (introduced below)
/// The allocated version of `EmulatedFpVar` (introduced below)
#[derive(Debug)] #[derive(Debug)]
#[must_use] #[must_use]
pub struct AllocatedNonNativeFieldVar<TargetField: PrimeField, BaseField: PrimeField> {
pub struct AllocatedEmulatedFpVar<TargetF: PrimeField, BaseF: PrimeField> {
/// Constraint system reference /// Constraint system reference
pub cs: ConstraintSystemRef<BaseField>,
/// The limbs, each of which is a BaseField gadget.
pub limbs: Vec<FpVar<BaseField>>,
pub cs: ConstraintSystemRef<BaseF>,
/// The limbs, each of which is a BaseF gadget.
pub limbs: Vec<FpVar<BaseF>>,
/// Number of additions done over this gadget, using which the gadget /// Number of additions done over this gadget, using which the gadget
/// decides when to reduce. /// decides when to reduce.
pub num_of_additions_over_normal_form: BaseField,
pub num_of_additions_over_normal_form: BaseF,
/// Whether the limb representation is the normal form (using only the bits /// Whether the limb representation is the normal form (using only the bits
/// specified in the parameters, and the representation is strictly within /// specified in the parameters, and the representation is strictly within
/// the range of TargetField).
/// the range of TargetF).
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<TargetF>,
} }
impl<TargetField: PrimeField, BaseField: PrimeField>
AllocatedNonNativeFieldVar<TargetField, BaseField>
{
impl<TargetF: PrimeField, BaseF: PrimeField> AllocatedEmulatedFpVar<TargetF, BaseF> {
/// Return cs /// Return cs
pub fn cs(&self) -> ConstraintSystemRef<BaseField> {
pub fn cs(&self) -> ConstraintSystemRef<BaseF> {
self.cs.clone() self.cs.clone()
} }
/// Obtain the value of limbs /// Obtain the value of limbs
pub fn limbs_to_value(
limbs: Vec<BaseField>,
optimization_type: OptimizationType,
) -> TargetField {
pub fn limbs_to_value(limbs: Vec<BaseF>, optimization_type: OptimizationType) -> TargetF {
let params = get_params( let params = get_params(
TargetField::MODULUS_BIT_SIZE as usize,
BaseField::MODULUS_BIT_SIZE as usize,
TargetF::MODULUS_BIT_SIZE as usize,
BaseF::MODULUS_BIT_SIZE as usize,
optimization_type, optimization_type,
); );
// Convert 2^{(params.bits_per_limb - 1)} into the TargetField and then double
// Convert 2^{(params.bits_per_limb - 1)} into the TargetF and then double
// the base This is because 2^{(params.bits_per_limb)} might indeed be // the base This is because 2^{(params.bits_per_limb)} might indeed be
// larger than the target field's prime. // larger than the target field's prime.
let base_repr = TargetField::ONE.into_bigint() << (params.bits_per_limb - 1) as u32;
let base_repr = TargetF::ONE.into_bigint() << (params.bits_per_limb - 1) as u32;
let mut base = TargetField::from_bigint(base_repr).unwrap();
let mut base = TargetF::from_bigint(base_repr).unwrap();
base.double_in_place(); base.double_in_place();
let mut result = TargetField::zero();
let mut power = TargetField::one();
let mut result = TargetF::zero();
let mut power = TargetF::one();
for limb in limbs.iter().rev() { for limb in limbs.iter().rev() {
let mut val = TargetField::zero();
let mut cur = TargetField::one();
let mut val = TargetF::zero();
let mut cur = TargetF::one();
for bit in limb.into_bigint().to_bits_be().iter().rev() { for bit in limb.into_bigint().to_bits_be().iter().rev() {
if *bit { if *bit {
@ -86,8 +81,8 @@ impl
result result
} }
/// Obtain the value of a nonnative field element
pub fn value(&self) -> R1CSResult<TargetField> {
/// Obtain the value of a emulated field element
pub fn value(&self) -> R1CSResult<TargetF> {
let mut limbs = Vec::new(); let mut limbs = Vec::new();
for limb in self.limbs.iter() { for limb in self.limbs.iter() {
limbs.push(limb.value()?); limbs.push(limb.value()?);
@ -96,8 +91,8 @@ impl
Ok(Self::limbs_to_value(limbs, self.get_optimization_type())) Ok(Self::limbs_to_value(limbs, self.get_optimization_type()))
} }
/// Obtain the nonnative field element of a constant value
pub fn constant(cs: ConstraintSystemRef<BaseField>, value: TargetField) -> R1CSResult<Self> {
/// Obtain the emulated field element of a constant value
pub fn constant(cs: ConstraintSystemRef<BaseF>, value: TargetF) -> R1CSResult<Self> {
let optimization_type = match cs.optimization_goal() { let optimization_type = match cs.optimization_goal() {
OptimizationGoal::None => OptimizationType::Constraints, OptimizationGoal::None => OptimizationType::Constraints,
OptimizationGoal::Constraints => OptimizationType::Constraints, OptimizationGoal::Constraints => OptimizationType::Constraints,
@ -109,32 +104,29 @@ impl
let mut limbs = Vec::new(); let mut limbs = Vec::new();
for limb_value in limbs_value.iter() { for limb_value in limbs_value.iter() {
limbs.push(FpVar::<BaseField>::new_constant(
ns!(cs, "limb"),
limb_value,
)?);
limbs.push(FpVar::<BaseF>::new_constant(ns!(cs, "limb"), limb_value)?);
} }
Ok(Self { Ok(Self {
cs, cs,
limbs, limbs,
num_of_additions_over_normal_form: BaseField::zero(),
num_of_additions_over_normal_form: BaseF::zero(),
is_in_the_normal_form: true, is_in_the_normal_form: true,
target_phantom: PhantomData, target_phantom: PhantomData,
}) })
} }
/// Obtain the nonnative field element of one
pub fn one(cs: ConstraintSystemRef<BaseField>) -> R1CSResult<Self> {
Self::constant(cs, TargetField::one())
/// Obtain the emulated field element of one
pub fn one(cs: ConstraintSystemRef<BaseF>) -> R1CSResult<Self> {
Self::constant(cs, TargetF::one())
} }
/// Obtain the nonnative field element of zero
pub fn zero(cs: ConstraintSystemRef<BaseField>) -> R1CSResult<Self> {
Self::constant(cs, TargetField::zero())
/// Obtain the emulated field element of zero
pub fn zero(cs: ConstraintSystemRef<BaseF>) -> R1CSResult<Self> {
Self::constant(cs, TargetF::zero())
} }
/// Add a nonnative field element
/// Add a emulated field element
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
pub fn add(&self, other: &Self) -> R1CSResult<Self> { pub fn add(&self, other: &Self) -> R1CSResult<Self> {
assert_eq!(self.get_optimization_type(), other.get_optimization_type()); assert_eq!(self.get_optimization_type(), other.get_optimization_type());
@ -150,18 +142,18 @@ impl
num_of_additions_over_normal_form: self num_of_additions_over_normal_form: self
.num_of_additions_over_normal_form .num_of_additions_over_normal_form
.add(&other.num_of_additions_over_normal_form) .add(&other.num_of_additions_over_normal_form)
.add(&BaseField::one()),
.add(&BaseF::one()),
is_in_the_normal_form: false, is_in_the_normal_form: false,
target_phantom: PhantomData, target_phantom: PhantomData,
}; };
Reducer::<TargetField, BaseField>::post_add_reduce(&mut res)?;
Reducer::<TargetF, BaseF>::post_add_reduce(&mut res)?;
Ok(res) Ok(res)
} }
/// Add a constant /// Add a constant
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
pub fn add_constant(&self, other: &TargetField) -> R1CSResult<Self> {
pub fn add_constant(&self, other: &TargetF) -> R1CSResult<Self> {
let other_limbs = Self::get_limbs_representations(other, self.get_optimization_type())?; let other_limbs = Self::get_limbs_representations(other, self.get_optimization_type())?;
let mut limbs = Vec::new(); let mut limbs = Vec::new();
@ -174,50 +166,50 @@ impl
limbs, limbs,
num_of_additions_over_normal_form: self num_of_additions_over_normal_form: self
.num_of_additions_over_normal_form .num_of_additions_over_normal_form
.add(&BaseField::one()),
.add(&BaseF::one()),
is_in_the_normal_form: false, is_in_the_normal_form: false,
target_phantom: PhantomData, target_phantom: PhantomData,
}; };
Reducer::<TargetField, BaseField>::post_add_reduce(&mut res)?;
Reducer::<TargetF, BaseF>::post_add_reduce(&mut res)?;
Ok(res) Ok(res)
} }
/// Subtract a nonnative field element, without the final reduction step
/// Subtract a emulated field element, without the final reduction step
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
pub fn sub_without_reduce(&self, other: &Self) -> R1CSResult<Self> { pub fn sub_without_reduce(&self, other: &Self) -> R1CSResult<Self> {
assert_eq!(self.get_optimization_type(), other.get_optimization_type()); assert_eq!(self.get_optimization_type(), other.get_optimization_type());
let params = get_params( let params = get_params(
TargetField::MODULUS_BIT_SIZE as usize,
BaseField::MODULUS_BIT_SIZE as usize,
TargetF::MODULUS_BIT_SIZE as usize,
BaseF::MODULUS_BIT_SIZE as usize,
self.get_optimization_type(), self.get_optimization_type(),
); );
// Step 1: reduce the `other` if needed // Step 1: reduce the `other` if needed
let mut surfeit = overhead!(other.num_of_additions_over_normal_form + BaseField::one()) + 1;
let mut surfeit = overhead!(other.num_of_additions_over_normal_form + BaseF::one()) + 1;
let mut other = other.clone(); let mut other = other.clone();
if (surfeit + params.bits_per_limb > BaseField::MODULUS_BIT_SIZE as usize - 1)
if (surfeit + params.bits_per_limb > BaseF::MODULUS_BIT_SIZE as usize - 1)
|| (surfeit || (surfeit
+ (TargetField::MODULUS_BIT_SIZE as usize
+ (TargetF::MODULUS_BIT_SIZE as usize
- params.bits_per_limb * (params.num_limbs - 1)) - params.bits_per_limb * (params.num_limbs - 1))
> BaseField::MODULUS_BIT_SIZE as usize - 1)
> BaseF::MODULUS_BIT_SIZE as usize - 1)
{ {
Reducer::reduce(&mut other)?; Reducer::reduce(&mut other)?;
surfeit = overhead!(other.num_of_additions_over_normal_form + BaseField::ONE) + 1;
surfeit = overhead!(other.num_of_additions_over_normal_form + BaseF::ONE) + 1;
} }
// Step 2: construct the padding // Step 2: construct the padding
let mut pad_non_top_limb = BaseField::ONE.into_bigint();
let mut pad_non_top_limb = BaseF::ONE.into_bigint();
let mut pad_top_limb = pad_non_top_limb; let mut pad_top_limb = pad_non_top_limb;
pad_non_top_limb <<= (surfeit + params.bits_per_limb) as u32; pad_non_top_limb <<= (surfeit + params.bits_per_limb) as u32;
let pad_non_top_limb = BaseField::from_bigint(pad_non_top_limb).unwrap();
let pad_non_top_limb = BaseF::from_bigint(pad_non_top_limb).unwrap();
pad_top_limb <<= (surfeit + TargetField::MODULUS_BIT_SIZE as usize
pad_top_limb <<= (surfeit + TargetF::MODULUS_BIT_SIZE as usize
- params.bits_per_limb * (params.num_limbs - 1)) as u32; - params.bits_per_limb * (params.num_limbs - 1)) as u32;
let pad_top_limb = BaseField::from_bigint(pad_top_limb).unwrap();
let pad_top_limb = BaseF::from_bigint(pad_top_limb).unwrap();
let mut pad_limbs = Vec::with_capacity(self.limbs.len()); let mut pad_limbs = Vec::with_capacity(self.limbs.len());
pad_limbs.push(pad_top_limb); pad_limbs.push(pad_top_limb);
@ -246,12 +238,12 @@ impl
} }
} }
let result = AllocatedNonNativeFieldVar::<TargetField, BaseField> {
let result = AllocatedEmulatedFpVar::<TargetF, BaseF> {
cs: self.cs(), cs: self.cs(),
limbs, limbs,
num_of_additions_over_normal_form: self.num_of_additions_over_normal_form num_of_additions_over_normal_form: self.num_of_additions_over_normal_form
+ (other.num_of_additions_over_normal_form + BaseField::one())
+ (other.num_of_additions_over_normal_form + BaseField::one()),
+ (other.num_of_additions_over_normal_form + BaseF::one())
+ (other.num_of_additions_over_normal_form + BaseF::one()),
is_in_the_normal_form: false, is_in_the_normal_form: false,
target_phantom: PhantomData, target_phantom: PhantomData,
}; };
@ -259,23 +251,23 @@ impl
Ok(result) Ok(result)
} }
/// Subtract a nonnative field element
/// Subtract a emulated field element
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
pub fn sub(&self, other: &Self) -> R1CSResult<Self> { pub fn sub(&self, other: &Self) -> R1CSResult<Self> {
assert_eq!(self.get_optimization_type(), other.get_optimization_type()); assert_eq!(self.get_optimization_type(), other.get_optimization_type());
let mut result = self.sub_without_reduce(other)?; let mut result = self.sub_without_reduce(other)?;
Reducer::<TargetField, BaseField>::post_add_reduce(&mut result)?;
Reducer::<TargetF, BaseF>::post_add_reduce(&mut result)?;
Ok(result) Ok(result)
} }
/// Subtract a constant /// Subtract a constant
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
pub fn sub_constant(&self, other: &TargetField) -> R1CSResult<Self> {
pub fn sub_constant(&self, other: &TargetF) -> R1CSResult<Self> {
self.sub(&Self::constant(self.cs(), *other)?) self.sub(&Self::constant(self.cs(), *other)?)
} }
/// Multiply a nonnative field element
/// Multiply a emulated field element
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
pub fn mul(&self, other: &Self) -> R1CSResult<Self> { pub fn mul(&self, other: &Self) -> R1CSResult<Self> {
assert_eq!(self.get_optimization_type(), other.get_optimization_type()); assert_eq!(self.get_optimization_type(), other.get_optimization_type());
@ -284,21 +276,21 @@ impl
} }
/// Multiply a constant /// Multiply a constant
pub fn mul_constant(&self, other: &TargetField) -> R1CSResult<Self> {
pub fn mul_constant(&self, other: &TargetF) -> R1CSResult<Self> {
self.mul(&Self::constant(self.cs(), *other)?) self.mul(&Self::constant(self.cs(), *other)?)
} }
/// Compute the negate of a nonnative field element
/// Compute the negate of a emulated field element
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
pub fn negate(&self) -> R1CSResult<Self> { pub fn negate(&self) -> R1CSResult<Self> {
Self::zero(self.cs())?.sub(self) Self::zero(self.cs())?.sub(self)
} }
/// Compute the inverse of a nonnative field element
/// Compute the inverse of a emulated field element
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
pub fn inverse(&self) -> R1CSResult<Self> { pub fn inverse(&self) -> R1CSResult<Self> {
let inverse = Self::new_witness(self.cs(), || { let inverse = Self::new_witness(self.cs(), || {
Ok(self.value()?.inverse().unwrap_or_else(TargetField::zero))
Ok(self.value()?.inverse().unwrap_or_else(TargetF::zero))
})?; })?;
let actual_result = self.clone().mul(&inverse)?; let actual_result = self.clone().mul(&inverse)?;
@ -306,36 +298,36 @@ impl
Ok(inverse) Ok(inverse)
} }
/// Convert a `TargetField` element into limbs (not constraints)
/// Convert a `TargetF` element into limbs (not constraints)
/// This is an internal function that would be reused by a number of other /// This is an internal function that would be reused by a number of other
/// functions /// functions
pub fn get_limbs_representations( pub fn get_limbs_representations(
elem: &TargetField,
elem: &TargetF,
optimization_type: OptimizationType, optimization_type: OptimizationType,
) -> R1CSResult<Vec<BaseField>> {
) -> R1CSResult<Vec<BaseF>> {
Self::get_limbs_representations_from_big_integer(&elem.into_bigint(), optimization_type) Self::get_limbs_representations_from_big_integer(&elem.into_bigint(), optimization_type)
} }
/// Obtain the limbs directly from a big int /// Obtain the limbs directly from a big int
pub fn get_limbs_representations_from_big_integer( pub fn get_limbs_representations_from_big_integer(
elem: &<TargetField as PrimeField>::BigInt,
elem: &<TargetF as PrimeField>::BigInt,
optimization_type: OptimizationType, optimization_type: OptimizationType,
) -> R1CSResult<Vec<BaseField>> {
) -> R1CSResult<Vec<BaseF>> {
let params = get_params( let params = get_params(
TargetField::MODULUS_BIT_SIZE as usize,
BaseField::MODULUS_BIT_SIZE as usize,
TargetF::MODULUS_BIT_SIZE as usize,
BaseF::MODULUS_BIT_SIZE as usize,
optimization_type, optimization_type,
); );
// push the lower limbs first // push the lower limbs first
let mut limbs: Vec<BaseField> = Vec::new();
let mut limbs: Vec<BaseF> = Vec::new();
let mut cur = *elem; let mut cur = *elem;
for _ in 0..params.num_limbs { for _ in 0..params.num_limbs {
let cur_bits = cur.to_bits_be(); // `to_bits` is big endian let cur_bits = cur.to_bits_be(); // `to_bits` is big endian
let cur_mod_r = <BaseField as PrimeField>::BigInt::from_bits_be(
let cur_mod_r = <BaseF as PrimeField>::BigInt::from_bits_be(
&cur_bits[cur_bits.len() - params.bits_per_limb..], &cur_bits[cur_bits.len() - params.bits_per_limb..],
); // therefore, the lowest `bits_per_non_top_limb` bits is what we want. ); // therefore, the lowest `bits_per_non_top_limb` bits is what we want.
limbs.push(BaseField::from_bigint(cur_mod_r).unwrap());
limbs.push(BaseF::from_bigint(cur_mod_r).unwrap());
cur >>= params.bits_per_limb as u32; cur >>= params.bits_per_limb as u32;
} }
@ -348,28 +340,28 @@ impl
/// for advanced use, multiply and output the intermediate representations /// for advanced use, multiply and output the intermediate representations
/// (without reduction) This intermediate representations can be added /// (without reduction) This intermediate representations can be added
/// with each other, and they can later be reduced back to the /// with each other, and they can later be reduced back to the
/// `NonNativeFieldVar`.
/// `EmulatedFpVar`.
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
pub fn mul_without_reduce( pub fn mul_without_reduce(
&self, &self,
other: &Self, other: &Self,
) -> R1CSResult<AllocatedNonNativeFieldMulResultVar<TargetField, BaseField>> {
) -> R1CSResult<AllocatedMulResultVar<TargetF, BaseF>> {
assert_eq!(self.get_optimization_type(), other.get_optimization_type()); assert_eq!(self.get_optimization_type(), other.get_optimization_type());
let params = get_params( let params = get_params(
TargetField::MODULUS_BIT_SIZE as usize,
BaseField::MODULUS_BIT_SIZE as usize,
TargetF::MODULUS_BIT_SIZE as usize,
BaseF::MODULUS_BIT_SIZE as usize,
self.get_optimization_type(), self.get_optimization_type(),
); );
// Step 1: reduce `self` and `other` if neceessary // Step 1: reduce `self` and `other` if neceessary
let mut self_reduced = self.clone(); let mut self_reduced = self.clone();
let mut other_reduced = other.clone(); let mut other_reduced = other.clone();
Reducer::<TargetField, BaseField>::pre_mul_reduce(&mut self_reduced, &mut other_reduced)?;
Reducer::<TargetF, BaseF>::pre_mul_reduce(&mut self_reduced, &mut other_reduced)?;
let mut prod_limbs = Vec::new(); let mut prod_limbs = Vec::new();
if self.get_optimization_type() == OptimizationType::Weight { if self.get_optimization_type() == OptimizationType::Weight {
let zero = FpVar::<BaseField>::zero();
let zero = FpVar::<BaseF>::zero();
for _ in 0..2 * params.num_limbs - 1 { for _ in 0..2 * params.num_limbs - 1 {
prod_limbs.push(zero.clone()); prod_limbs.push(zero.clone());
@ -386,7 +378,7 @@ impl
for z_index in 0..2 * params.num_limbs - 1 { for z_index in 0..2 * params.num_limbs - 1 {
prod_limbs.push(FpVar::new_witness(ns!(cs, "limb product"), || { prod_limbs.push(FpVar::new_witness(ns!(cs, "limb product"), || {
let mut z_i = BaseField::zero();
let mut z_i = BaseF::zero();
for i in 0..=min(params.num_limbs - 1, z_index) { for i in 0..=min(params.num_limbs - 1, z_index) {
let j = z_index - i; let j = z_index - i;
if j < params.num_limbs { if j < params.num_limbs {
@ -402,7 +394,7 @@ impl
for c in 0..(2 * params.num_limbs - 1) { for c in 0..(2 * params.num_limbs - 1) {
let c_pows: Vec<_> = (0..(2 * params.num_limbs - 1)) let c_pows: Vec<_> = (0..(2 * params.num_limbs - 1))
.map(|i| BaseField::from((c + 1) as u128).pow(&vec![i as u64]))
.map(|i| BaseF::from((c + 1) as u128).pow(&vec![i as u64]))
.collect(); .collect();
let x = self_reduced let x = self_reduced
@ -429,12 +421,12 @@ impl
} }
} }
Ok(AllocatedNonNativeFieldMulResultVar {
Ok(AllocatedMulResultVar {
cs: self.cs(), cs: self.cs(),
limbs: prod_limbs, limbs: prod_limbs,
prod_of_num_of_additions: (self_reduced.num_of_additions_over_normal_form prod_of_num_of_additions: (self_reduced.num_of_additions_over_normal_form
+ BaseField::one())
* (other_reduced.num_of_additions_over_normal_form + BaseField::one()),
+ BaseF::one())
* (other_reduced.num_of_additions_over_normal_form + BaseF::one()),
target_phantom: PhantomData, target_phantom: PhantomData,
}) })
} }
@ -446,32 +438,32 @@ impl
pub(crate) fn conditional_enforce_equal( pub(crate) fn conditional_enforce_equal(
&self, &self,
other: &Self, other: &Self,
should_enforce: &Boolean<BaseField>,
should_enforce: &Boolean<BaseF>,
) -> R1CSResult<()> { ) -> R1CSResult<()> {
assert_eq!(self.get_optimization_type(), other.get_optimization_type()); assert_eq!(self.get_optimization_type(), other.get_optimization_type());
let params = get_params( let params = get_params(
TargetField::MODULUS_BIT_SIZE as usize,
BaseField::MODULUS_BIT_SIZE as usize,
TargetF::MODULUS_BIT_SIZE as usize,
BaseF::MODULUS_BIT_SIZE as usize,
self.get_optimization_type(), self.get_optimization_type(),
); );
// Get p // Get p
let p_representations = let p_representations =
AllocatedNonNativeFieldVar::<TargetField, BaseField>::get_limbs_representations_from_big_integer(
&<TargetField as PrimeField>::MODULUS,
self.get_optimization_type()
AllocatedEmulatedFpVar::<TargetF, BaseF>::get_limbs_representations_from_big_integer(
&<TargetF as PrimeField>::MODULUS,
self.get_optimization_type(),
)?; )?;
let p_bigint = limbs_to_bigint(params.bits_per_limb, &p_representations); let p_bigint = limbs_to_bigint(params.bits_per_limb, &p_representations);
let mut p_gadget_limbs = Vec::new(); let mut p_gadget_limbs = Vec::new();
for limb in p_representations.iter() { for limb in p_representations.iter() {
p_gadget_limbs.push(FpVar::<BaseField>::Constant(*limb));
p_gadget_limbs.push(FpVar::<BaseF>::Constant(*limb));
} }
let p_gadget = AllocatedNonNativeFieldVar::<TargetField, BaseField> {
let p_gadget = AllocatedEmulatedFpVar::<TargetF, BaseF> {
cs: self.cs(), cs: self.cs(),
limbs: p_gadget_limbs, limbs: p_gadget_limbs,
num_of_additions_over_normal_form: BaseField::one(),
num_of_additions_over_normal_form: BaseF::one(),
is_in_the_normal_form: false, is_in_the_normal_form: false,
target_phantom: PhantomData, target_phantom: PhantomData,
}; };
@ -482,19 +474,19 @@ impl
delta = should_enforce.select(&delta, &Self::zero(cs.clone())?)?; delta = should_enforce.select(&delta, &Self::zero(cs.clone())?)?;
// Allocate k = delta / p // Allocate k = delta / p
let k_gadget = FpVar::<BaseField>::new_witness(ns!(cs, "k"), || {
let mut delta_limbs_values = Vec::<BaseField>::new();
let k_gadget = FpVar::<BaseF>::new_witness(ns!(cs, "k"), || {
let mut delta_limbs_values = Vec::<BaseF>::new();
for limb in delta.limbs.iter() { for limb in delta.limbs.iter() {
delta_limbs_values.push(limb.value()?); delta_limbs_values.push(limb.value()?);
} }
let delta_bigint = limbs_to_bigint(params.bits_per_limb, &delta_limbs_values); let delta_bigint = limbs_to_bigint(params.bits_per_limb, &delta_limbs_values);
Ok(bigint_to_basefield::<BaseField>(&(delta_bigint / p_bigint)))
Ok(bigint_to_basefield::<BaseF>(&(delta_bigint / p_bigint)))
})?; })?;
let surfeit = overhead!(delta.num_of_additions_over_normal_form + BaseField::one()) + 1;
Reducer::<TargetField, BaseField>::limb_to_bits(&k_gadget, surfeit)?;
let surfeit = overhead!(delta.num_of_additions_over_normal_form + BaseF::one()) + 1;
Reducer::<TargetF, BaseF>::limb_to_bits(&k_gadget, surfeit)?;
// Compute k * p // Compute k * p
let mut kp_gadget_limbs = Vec::new(); let mut kp_gadget_limbs = Vec::new();
@ -503,7 +495,7 @@ impl
} }
// Enforce delta = kp // Enforce delta = kp
Reducer::<TargetField, BaseField>::group_and_check_equality(
Reducer::<TargetF, BaseF>::group_and_check_equality(
surfeit, surfeit,
params.bits_per_limb, params.bits_per_limb,
params.bits_per_limb, params.bits_per_limb,
@ -518,7 +510,7 @@ impl
pub(crate) fn conditional_enforce_not_equal( pub(crate) fn conditional_enforce_not_equal(
&self, &self,
other: &Self, other: &Self,
should_enforce: &Boolean<BaseField>,
should_enforce: &Boolean<BaseF>,
) -> R1CSResult<()> { ) -> R1CSResult<()> {
assert_eq!(self.get_optimization_type(), other.get_optimization_type()); assert_eq!(self.get_optimization_type(), other.get_optimization_type());
@ -541,8 +533,8 @@ impl
/// Allocates a new variable, but does not check that the allocation's limbs /// Allocates a new variable, but does not check that the allocation's limbs
/// are in-range. /// are in-range.
fn new_variable_unchecked<T: Borrow<TargetField>>(
cs: impl Into<Namespace<BaseField>>,
fn new_variable_unchecked<T: Borrow<TargetF>>(
cs: impl Into<Namespace<BaseF>>,
f: impl FnOnce() -> Result<T, SynthesisError>, f: impl FnOnce() -> Result<T, SynthesisError>,
mode: AllocationMode, mode: AllocationMode,
) -> R1CSResult<Self> { ) -> R1CSResult<Self> {
@ -555,7 +547,7 @@ impl
OptimizationGoal::Weight => OptimizationType::Weight, OptimizationGoal::Weight => OptimizationType::Weight,
}; };
let zero = TargetField::zero();
let zero = TargetF::zero();
let elem = match f() { let elem = match f() {
Ok(t) => *(t.borrow()), Ok(t) => *(t.borrow()),
@ -565,7 +557,7 @@ impl
let mut limbs = Vec::new(); let mut limbs = Vec::new();
for limb in elem_representations.iter() { for limb in elem_representations.iter() {
limbs.push(FpVar::<BaseField>::new_variable(
limbs.push(FpVar::<BaseF>::new_variable(
ark_relations::ns!(cs, "alloc"), ark_relations::ns!(cs, "alloc"),
|| Ok(limb), || Ok(limb),
mode, mode,
@ -573,9 +565,9 @@ impl
} }
let num_of_additions_over_normal_form = if mode != AllocationMode::Witness { let num_of_additions_over_normal_form = if mode != AllocationMode::Witness {
BaseField::zero()
BaseF::zero()
} else { } else {
BaseField::one()
BaseF::one()
}; };
Ok(Self { Ok(Self {
@ -591,10 +583,7 @@ impl
/// the whole number is 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(
&self,
cs: impl Into<Namespace<BaseField>>,
) -> R1CSResult<Vec<Boolean<BaseField>>> {
fn enforce_in_range(&self, cs: impl Into<Namespace<BaseF>>) -> R1CSResult<Vec<Boolean<BaseF>>> {
let ns = cs.into(); let ns = cs.into();
let cs = ns.cs(); let cs = ns.cs();
let optimization_type = match cs.optimization_goal() { let optimization_type = match cs.optimization_goal() {
@ -603,24 +592,23 @@ impl
OptimizationGoal::Weight => OptimizationType::Weight, OptimizationGoal::Weight => OptimizationType::Weight,
}; };
let params = get_params( let params = get_params(
TargetField::MODULUS_BIT_SIZE as usize,
BaseField::MODULUS_BIT_SIZE as usize,
TargetF::MODULUS_BIT_SIZE as usize,
BaseF::MODULUS_BIT_SIZE as usize,
optimization_type, optimization_type,
); );
let mut bits = Vec::new(); let mut bits = Vec::new();
for limb in self.limbs.iter().rev().take(params.num_limbs - 1) { for limb in self.limbs.iter().rev().take(params.num_limbs - 1) {
bits.extend( bits.extend(
Reducer::<TargetField, BaseField>::limb_to_bits(limb, params.bits_per_limb)?
Reducer::<TargetF, BaseF>::limb_to_bits(limb, params.bits_per_limb)?
.into_iter() .into_iter()
.rev(), .rev(),
); );
} }
bits.extend( bits.extend(
Reducer::<TargetField, BaseField>::limb_to_bits(
Reducer::<TargetF, BaseF>::limb_to_bits(
&self.limbs[0], &self.limbs[0],
TargetField::MODULUS_BIT_SIZE as usize
- (params.num_limbs - 1) * params.bits_per_limb,
TargetF::MODULUS_BIT_SIZE as usize - (params.num_limbs - 1) * params.bits_per_limb,
)? )?
.into_iter() .into_iter()
.rev(), .rev(),
@ -633,10 +621,10 @@ impl
/// and returns the bits of its binary representation. /// 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 /// 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>>(
cs: impl Into<Namespace<BaseField>>,
pub fn new_witness_with_le_bits<T: Borrow<TargetF>>(
cs: impl Into<Namespace<BaseF>>,
f: impl FnOnce() -> Result<T, SynthesisError>, f: impl FnOnce() -> Result<T, SynthesisError>,
) -> R1CSResult<(Self, Vec<Boolean<BaseField>>)> {
) -> R1CSResult<(Self, Vec<Boolean<BaseF>>)> {
let ns = cs.into(); let ns = cs.into();
let cs = ns.cs(); let cs = ns.cs();
let this = Self::new_variable_unchecked(ns!(cs, "alloc"), f, AllocationMode::Witness)?; let this = Self::new_variable_unchecked(ns!(cs, "alloc"), f, AllocationMode::Witness)?;
@ -645,36 +633,36 @@ impl
} }
} }
impl<TargetField: PrimeField, BaseField: PrimeField> ToBitsGadget<BaseField>
for AllocatedNonNativeFieldVar<TargetField, BaseField>
impl<TargetF: PrimeField, BaseF: PrimeField> ToBitsGadget<BaseF>
for AllocatedEmulatedFpVar<TargetF, BaseF>
{ {
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
fn to_bits_le(&self) -> R1CSResult<Vec<Boolean<BaseField>>> {
fn to_bits_le(&self) -> R1CSResult<Vec<Boolean<BaseF>>> {
let params = get_params( let params = get_params(
TargetField::MODULUS_BIT_SIZE as usize,
BaseField::MODULUS_BIT_SIZE as usize,
TargetF::MODULUS_BIT_SIZE as usize,
BaseF::MODULUS_BIT_SIZE as usize,
self.get_optimization_type(), self.get_optimization_type(),
); );
// Reduce to the normal form // Reduce to the normal form
// Though, a malicious prover can make it slightly larger than p // Though, a malicious prover can make it slightly larger than p
let mut self_normal = self.clone(); let mut self_normal = self.clone();
Reducer::<TargetField, BaseField>::pre_eq_reduce(&mut self_normal)?;
Reducer::<TargetF, BaseF>::pre_eq_reduce(&mut self_normal)?;
// Therefore, we convert it to bits and enforce that it is in the field // Therefore, we convert it to bits and enforce that it is in the field
let mut bits = Vec::<Boolean<BaseField>>::new();
let mut bits = Vec::<Boolean<BaseF>>::new();
for limb in self_normal.limbs.iter() { for limb in self_normal.limbs.iter() {
bits.extend_from_slice(&Reducer::<TargetField, BaseField>::limb_to_bits(
bits.extend_from_slice(&Reducer::<TargetF, BaseF>::limb_to_bits(
&limb, &limb,
params.bits_per_limb, params.bits_per_limb,
)?); )?);
} }
bits.reverse(); bits.reverse();
let mut b = TargetField::characteristic().to_vec();
let mut b = TargetF::characteristic().to_vec();
assert_eq!(b[0] % 2, 1); assert_eq!(b[0] % 2, 1);
b[0] -= 1; // This works, because the LSB is one, so there's no borrows. b[0] -= 1; // This works, because the LSB is one, so there's no borrows.
let run = Boolean::<BaseField>::enforce_smaller_or_equal_than_le(&bits, b)?;
let run = Boolean::<BaseF>::enforce_smaller_or_equal_than_le(&bits, b)?;
// We should always end in a "run" of zeros, because // We should always end in a "run" of zeros, because
// the characteristic is an odd prime. So, this should // the characteristic is an odd prime. So, this should
@ -685,14 +673,14 @@ impl ToBitsGadget
} }
} }
impl<TargetField: PrimeField, BaseField: PrimeField> ToBytesGadget<BaseField>
for AllocatedNonNativeFieldVar<TargetField, BaseField>
impl<TargetF: PrimeField, BaseF: PrimeField> ToBytesGadget<BaseF>
for AllocatedEmulatedFpVar<TargetF, BaseF>
{ {
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
fn to_bytes(&self) -> R1CSResult<Vec<UInt8<BaseField>>> {
fn to_bytes(&self) -> R1CSResult<Vec<UInt8<BaseF>>> {
let mut bits = self.to_bits_le()?; let mut bits = self.to_bits_le()?;
let num_bits = TargetField::BigInt::NUM_LIMBS * 64;
let num_bits = TargetF::BigInt::NUM_LIMBS * 64;
assert!(bits.len() <= num_bits); assert!(bits.len() <= num_bits);
bits.resize_with(num_bits, || Boolean::constant(false)); bits.resize_with(num_bits, || Boolean::constant(false));
@ -701,12 +689,12 @@ impl ToBytesGadget
} }
} }
impl<TargetField: PrimeField, BaseField: PrimeField> CondSelectGadget<BaseField>
for AllocatedNonNativeFieldVar<TargetField, BaseField>
impl<TargetF: PrimeField, BaseF: PrimeField> CondSelectGadget<BaseF>
for AllocatedEmulatedFpVar<TargetF, BaseF>
{ {
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
fn conditionally_select( fn conditionally_select(
cond: &Boolean<BaseField>,
cond: &Boolean<BaseF>,
true_value: &Self, true_value: &Self,
false_value: &Self, false_value: &Self,
) -> R1CSResult<Self> { ) -> R1CSResult<Self> {
@ -718,7 +706,7 @@ impl CondSelectGadget
let mut limbs_sel = Vec::with_capacity(true_value.limbs.len()); let mut limbs_sel = Vec::with_capacity(true_value.limbs.len());
for (x, y) in true_value.limbs.iter().zip(&false_value.limbs) { for (x, y) in true_value.limbs.iter().zip(&false_value.limbs) {
limbs_sel.push(FpVar::<BaseField>::conditionally_select(cond, x, y)?);
limbs_sel.push(FpVar::<BaseF>::conditionally_select(cond, x, y)?);
} }
Ok(Self { Ok(Self {
@ -735,14 +723,14 @@ impl CondSelectGadget
} }
} }
impl<TargetField: PrimeField, BaseField: PrimeField> TwoBitLookupGadget<BaseField>
for AllocatedNonNativeFieldVar<TargetField, BaseField>
impl<TargetF: PrimeField, BaseF: PrimeField> TwoBitLookupGadget<BaseF>
for AllocatedEmulatedFpVar<TargetF, BaseF>
{ {
type TableConstant = TargetField;
type TableConstant = TargetF;
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
fn two_bit_lookup( fn two_bit_lookup(
bits: &[Boolean<BaseField>],
bits: &[Boolean<BaseF>],
constants: &[Self::TableConstant], constants: &[Self::TableConstant],
) -> R1CSResult<Self> { ) -> R1CSResult<Self> {
debug_assert!(bits.len() == 2); debug_assert!(bits.len() == 2);
@ -757,8 +745,8 @@ impl TwoBitLookupGadget
}; };
let params = get_params( let params = get_params(
TargetField::MODULUS_BIT_SIZE as usize,
BaseField::MODULUS_BIT_SIZE as usize,
TargetF::MODULUS_BIT_SIZE as usize,
BaseF::MODULUS_BIT_SIZE as usize,
optimization_type, optimization_type,
); );
let mut limbs_constants = Vec::new(); let mut limbs_constants = Vec::new();
@ -768,7 +756,7 @@ impl TwoBitLookupGadget
for constant in constants.iter() { for constant in constants.iter() {
let representations = let representations =
AllocatedNonNativeFieldVar::<TargetField, BaseField>::get_limbs_representations(
AllocatedEmulatedFpVar::<TargetF, BaseF>::get_limbs_representations(
constant, constant,
optimization_type, optimization_type,
)?; )?;
@ -780,28 +768,28 @@ impl TwoBitLookupGadget
let mut limbs = Vec::new(); let mut limbs = Vec::new();
for limbs_constant in limbs_constants.iter() { for limbs_constant in limbs_constants.iter() {
limbs.push(FpVar::<BaseField>::two_bit_lookup(bits, limbs_constant)?);
limbs.push(FpVar::<BaseF>::two_bit_lookup(bits, limbs_constant)?);
} }
Ok(AllocatedNonNativeFieldVar::<TargetField, BaseField> {
Ok(AllocatedEmulatedFpVar::<TargetF, BaseF> {
cs, cs,
limbs, limbs,
num_of_additions_over_normal_form: BaseField::zero(),
num_of_additions_over_normal_form: BaseF::zero(),
is_in_the_normal_form: true, is_in_the_normal_form: true,
target_phantom: PhantomData, target_phantom: PhantomData,
}) })
} }
} }
impl<TargetField: PrimeField, BaseField: PrimeField> ThreeBitCondNegLookupGadget<BaseField>
for AllocatedNonNativeFieldVar<TargetField, BaseField>
impl<TargetF: PrimeField, BaseF: PrimeField> ThreeBitCondNegLookupGadget<BaseF>
for AllocatedEmulatedFpVar<TargetF, BaseF>
{ {
type TableConstant = TargetField;
type TableConstant = TargetF;
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
fn three_bit_cond_neg_lookup( fn three_bit_cond_neg_lookup(
bits: &[Boolean<BaseField>],
b0b1: &Boolean<BaseField>,
bits: &[Boolean<BaseF>],
b0b1: &Boolean<BaseF>,
constants: &[Self::TableConstant], constants: &[Self::TableConstant],
) -> R1CSResult<Self> { ) -> R1CSResult<Self> {
debug_assert!(bits.len() == 3); debug_assert!(bits.len() == 3);
@ -816,8 +804,8 @@ impl ThreeBitCondNegLookupGadget
}; };
let params = get_params( let params = get_params(
TargetField::MODULUS_BIT_SIZE as usize,
BaseField::MODULUS_BIT_SIZE as usize,
TargetF::MODULUS_BIT_SIZE as usize,
BaseF::MODULUS_BIT_SIZE as usize,
optimization_type, optimization_type,
); );
@ -828,7 +816,7 @@ impl ThreeBitCondNegLookupGadget
for constant in constants.iter() { for constant in constants.iter() {
let representations = let representations =
AllocatedNonNativeFieldVar::<TargetField, BaseField>::get_limbs_representations(
AllocatedEmulatedFpVar::<TargetF, BaseF>::get_limbs_representations(
constant, constant,
optimization_type, optimization_type,
)?; )?;
@ -840,28 +828,28 @@ impl ThreeBitCondNegLookupGadget
let mut limbs = Vec::new(); let mut limbs = Vec::new();
for limbs_constant in limbs_constants.iter() { for limbs_constant in limbs_constants.iter() {
limbs.push(FpVar::<BaseField>::three_bit_cond_neg_lookup(
limbs.push(FpVar::<BaseF>::three_bit_cond_neg_lookup(
bits, bits,
b0b1, b0b1,
limbs_constant, limbs_constant,
)?); )?);
} }
Ok(AllocatedNonNativeFieldVar::<TargetField, BaseField> {
Ok(AllocatedEmulatedFpVar::<TargetF, BaseF> {
cs, cs,
limbs, limbs,
num_of_additions_over_normal_form: BaseField::zero(),
num_of_additions_over_normal_form: BaseF::zero(),
is_in_the_normal_form: true, is_in_the_normal_form: true,
target_phantom: PhantomData, target_phantom: PhantomData,
}) })
} }
} }
impl<TargetField: PrimeField, BaseField: PrimeField> AllocVar<TargetField, BaseField>
for AllocatedNonNativeFieldVar<TargetField, BaseField>
impl<TargetF: PrimeField, BaseF: PrimeField> AllocVar<TargetF, BaseF>
for AllocatedEmulatedFpVar<TargetF, BaseF>
{ {
fn new_variable<T: Borrow<TargetField>>(
cs: impl Into<Namespace<BaseField>>,
fn new_variable<T: Borrow<TargetF>>(
cs: impl Into<Namespace<BaseF>>,
f: impl FnOnce() -> Result<T, SynthesisError>, f: impl FnOnce() -> Result<T, SynthesisError>,
mode: AllocationMode, mode: AllocationMode,
) -> R1CSResult<Self> { ) -> R1CSResult<Self> {
@ -875,18 +863,18 @@ impl AllocVar
} }
} }
impl<TargetField: PrimeField, BaseField: PrimeField> ToConstraintFieldGadget<BaseField>
for AllocatedNonNativeFieldVar<TargetField, BaseField>
impl<TargetF: PrimeField, BaseF: PrimeField> ToConstraintFieldGadget<BaseF>
for AllocatedEmulatedFpVar<TargetF, BaseF>
{ {
fn to_constraint_field(&self) -> R1CSResult<Vec<FpVar<BaseField>>> {
// provide a unique representation of the nonnative variable
fn to_constraint_field(&self) -> R1CSResult<Vec<FpVar<BaseF>>> {
// provide a unique representation of the emulated variable
// step 1: convert it into a bit sequence // step 1: convert it into a bit sequence
let bits = self.to_bits_le()?; let bits = self.to_bits_le()?;
// step 2: obtain the parameters for weight-optimized (often, fewer limbs) // step 2: obtain the parameters for weight-optimized (often, fewer limbs)
let params = get_params( let params = get_params(
TargetField::MODULUS_BIT_SIZE as usize,
BaseField::MODULUS_BIT_SIZE as usize,
TargetF::MODULUS_BIT_SIZE as usize,
BaseF::MODULUS_BIT_SIZE as usize,
OptimizationType::Weight, OptimizationType::Weight,
); );
@ -894,15 +882,15 @@ impl ToConstraintFieldGadget
let mut limbs = bits let mut limbs = bits
.chunks(params.bits_per_limb) .chunks(params.bits_per_limb)
.map(|chunk| { .map(|chunk| {
let mut limb = FpVar::<BaseField>::zero();
let mut w = BaseField::one();
let mut limb = FpVar::<BaseF>::zero();
let mut w = BaseF::one();
for b in chunk.iter() { for b in chunk.iter() {
limb += FpVar::from(b.clone()) * w; limb += FpVar::from(b.clone()) * w;
w.double_in_place(); w.double_in_place();
} }
limb limb
}) })
.collect::<Vec<FpVar<BaseField>>>();
.collect::<Vec<FpVar<BaseF>>>();
limbs.reverse(); limbs.reverse();
@ -913,11 +901,9 @@ impl ToConstraintFieldGadget
// Implementation of a few traits // Implementation of a few traits
impl<TargetField: PrimeField, BaseField: PrimeField> Clone
for AllocatedNonNativeFieldVar<TargetField, BaseField>
{
impl<TargetF: PrimeField, BaseF: PrimeField> Clone for AllocatedEmulatedFpVar<TargetF, BaseF> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
AllocatedNonNativeFieldVar {
AllocatedEmulatedFpVar {
cs: self.cs(), cs: self.cs(),
limbs: self.limbs.clone(), limbs: self.limbs.clone(),
num_of_additions_over_normal_form: self.num_of_additions_over_normal_form, num_of_additions_over_normal_form: self.num_of_additions_over_normal_form,

src/fields/nonnative/allocated_mul_result.rs → src/fields/emulated_fp/allocated_mul_result.rs

@ -1,7 +1,7 @@
use super::{ use super::{
params::{get_params, OptimizationType}, params::{get_params, OptimizationType},
reduce::{bigint_to_basefield, limbs_to_bigint, Reducer}, reduce::{bigint_to_basefield, limbs_to_bigint, Reducer},
AllocatedNonNativeFieldVar,
AllocatedEmulatedFpVar,
}; };
use crate::{fields::fp::FpVar, prelude::*}; use crate::{fields::fp::FpVar, prelude::*};
use ark_ff::PrimeField; use ark_ff::PrimeField;
@ -12,37 +12,36 @@ use ark_relations::{
use ark_std::{marker::PhantomData, 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 `MulResultVar` (introduced below)
#[derive(Debug)] #[derive(Debug)]
#[must_use] #[must_use]
pub struct AllocatedNonNativeFieldMulResultVar<TargetField: PrimeField, BaseField: PrimeField> {
pub struct AllocatedMulResultVar<TargetF: PrimeField, BaseF: PrimeField> {
/// Constraint system reference /// Constraint system reference
pub cs: ConstraintSystemRef<BaseField>,
pub cs: ConstraintSystemRef<BaseF>,
/// Limbs of the intermediate representations /// Limbs of the intermediate representations
pub limbs: Vec<FpVar<BaseField>>,
pub limbs: Vec<FpVar<BaseF>>,
/// The cumulative num of additions /// The cumulative num of additions
pub prod_of_num_of_additions: BaseField,
pub prod_of_num_of_additions: BaseF,
#[doc(hidden)] #[doc(hidden)]
pub target_phantom: PhantomData<TargetField>,
pub target_phantom: PhantomData<TargetF>,
} }
impl<TargetField: PrimeField, BaseField: PrimeField>
From<&AllocatedNonNativeFieldVar<TargetField, BaseField>>
for AllocatedNonNativeFieldMulResultVar<TargetField, BaseField>
impl<TargetF: PrimeField, BaseF: PrimeField> From<&AllocatedEmulatedFpVar<TargetF, BaseF>>
for AllocatedMulResultVar<TargetF, BaseF>
{ {
fn from(src: &AllocatedNonNativeFieldVar<TargetField, BaseField>) -> Self {
fn from(src: &AllocatedEmulatedFpVar<TargetF, BaseF>) -> Self {
let params = get_params( let params = get_params(
TargetField::MODULUS_BIT_SIZE as usize,
BaseField::MODULUS_BIT_SIZE as usize,
TargetF::MODULUS_BIT_SIZE as usize,
BaseF::MODULUS_BIT_SIZE as usize,
src.get_optimization_type(), src.get_optimization_type(),
); );
let mut limbs = src.limbs.clone(); let mut limbs = src.limbs.clone();
limbs.reverse(); limbs.reverse();
limbs.resize(2 * params.num_limbs - 1, FpVar::<BaseField>::zero());
limbs.resize(2 * params.num_limbs - 1, FpVar::<BaseF>::zero());
limbs.reverse(); limbs.reverse();
let prod_of_num_of_additions = src.num_of_additions_over_normal_form + &BaseField::one();
let prod_of_num_of_additions = src.num_of_additions_over_normal_form + &BaseF::one();
Self { Self {
cs: src.cs(), cs: src.cs(),
@ -53,76 +52,74 @@ impl
} }
} }
impl<TargetField: PrimeField, BaseField: PrimeField>
AllocatedNonNativeFieldMulResultVar<TargetField, BaseField>
{
impl<TargetF: PrimeField, BaseF: PrimeField> AllocatedMulResultVar<TargetF, BaseF> {
/// Get the CS /// Get the CS
pub fn cs(&self) -> ConstraintSystemRef<BaseField> {
pub fn cs(&self) -> ConstraintSystemRef<BaseF> {
self.cs.clone() self.cs.clone()
} }
/// Get the value of the multiplication result /// Get the value of the multiplication result
pub fn value(&self) -> R1CSResult<TargetField> {
pub fn value(&self) -> R1CSResult<TargetF> {
let params = get_params( let params = get_params(
TargetField::MODULUS_BIT_SIZE as usize,
BaseField::MODULUS_BIT_SIZE as usize,
TargetF::MODULUS_BIT_SIZE as usize,
BaseF::MODULUS_BIT_SIZE as usize,
self.get_optimization_type(), self.get_optimization_type(),
); );
let p_representations = let p_representations =
AllocatedNonNativeFieldVar::<TargetField, BaseField>::get_limbs_representations_from_big_integer(
&<TargetField as PrimeField>::MODULUS,
self.get_optimization_type()
AllocatedEmulatedFpVar::<TargetF, BaseF>::get_limbs_representations_from_big_integer(
&<TargetF as PrimeField>::MODULUS,
self.get_optimization_type(),
)?; )?;
let p_bigint = limbs_to_bigint(params.bits_per_limb, &p_representations); let p_bigint = limbs_to_bigint(params.bits_per_limb, &p_representations);
let mut limbs_values = Vec::<BaseField>::new();
let mut limbs_values = Vec::<BaseF>::new();
for limb in self.limbs.iter() { for limb in self.limbs.iter() {
limbs_values.push(limb.value().unwrap_or_default()); limbs_values.push(limb.value().unwrap_or_default());
} }
let value_bigint = limbs_to_bigint(params.bits_per_limb, &limbs_values); let value_bigint = limbs_to_bigint(params.bits_per_limb, &limbs_values);
let res = bigint_to_basefield::<TargetField>(&(value_bigint % p_bigint));
let res = bigint_to_basefield::<TargetF>(&(value_bigint % p_bigint));
Ok(res) Ok(res)
} }
/// Constraints for reducing the result of a multiplication mod p, to get an /// Constraints for reducing the result of a multiplication mod p, to get an
/// original representation. /// original representation.
pub fn reduce(&self) -> R1CSResult<AllocatedNonNativeFieldVar<TargetField, BaseField>> {
pub fn reduce(&self) -> R1CSResult<AllocatedEmulatedFpVar<TargetF, BaseF>> {
let params = get_params( let params = get_params(
TargetField::MODULUS_BIT_SIZE as usize,
BaseField::MODULUS_BIT_SIZE as usize,
TargetF::MODULUS_BIT_SIZE as usize,
BaseF::MODULUS_BIT_SIZE as usize,
self.get_optimization_type(), self.get_optimization_type(),
); );
// Step 1: get p // Step 1: get p
let p_representations = let p_representations =
AllocatedNonNativeFieldVar::<TargetField, BaseField>::get_limbs_representations_from_big_integer(
&<TargetField as PrimeField>::MODULUS,
self.get_optimization_type()
AllocatedEmulatedFpVar::<TargetF, BaseF>::get_limbs_representations_from_big_integer(
&<TargetF as PrimeField>::MODULUS,
self.get_optimization_type(),
)?; )?;
let p_bigint = limbs_to_bigint(params.bits_per_limb, &p_representations); let p_bigint = limbs_to_bigint(params.bits_per_limb, &p_representations);
let mut p_gadget_limbs = Vec::new(); let mut p_gadget_limbs = Vec::new();
for limb in p_representations.iter() { for limb in p_representations.iter() {
p_gadget_limbs.push(FpVar::<BaseField>::new_constant(self.cs(), limb)?);
p_gadget_limbs.push(FpVar::<BaseF>::new_constant(self.cs(), limb)?);
} }
let p_gadget = AllocatedNonNativeFieldVar::<TargetField, BaseField> {
let p_gadget = AllocatedEmulatedFpVar::<TargetF, BaseF> {
cs: self.cs(), cs: self.cs(),
limbs: p_gadget_limbs, limbs: p_gadget_limbs,
num_of_additions_over_normal_form: BaseField::one(),
num_of_additions_over_normal_form: BaseF::one(),
is_in_the_normal_form: false, is_in_the_normal_form: false,
target_phantom: PhantomData, target_phantom: PhantomData,
}; };
// Step 2: compute surfeit // Step 2: compute surfeit
let surfeit = overhead!(self.prod_of_num_of_additions + BaseField::one()) + 1 + 1;
let surfeit = overhead!(self.prod_of_num_of_additions + BaseF::one()) + 1 + 1;
// Step 3: allocate k // Step 3: allocate k
let k_bits = { let k_bits = {
let mut res = Vec::new(); let mut res = Vec::new();
let mut limbs_values = Vec::<BaseField>::new();
let mut limbs_values = Vec::<BaseF>::new();
for limb in self.limbs.iter() { for limb in self.limbs.iter() {
limbs_values.push(limb.value().unwrap_or_default()); limbs_values.push(limb.value().unwrap_or_default());
} }
@ -130,10 +127,10 @@ impl
let value_bigint = limbs_to_bigint(params.bits_per_limb, &limbs_values); let value_bigint = limbs_to_bigint(params.bits_per_limb, &limbs_values);
let mut k_cur = value_bigint / p_bigint; let mut k_cur = value_bigint / p_bigint;
let total_len = TargetField::MODULUS_BIT_SIZE as usize + surfeit;
let total_len = TargetF::MODULUS_BIT_SIZE as usize + surfeit;
for _ in 0..total_len { for _ in 0..total_len {
res.push(Boolean::<BaseField>::new_witness(self.cs(), || {
res.push(Boolean::<BaseF>::new_witness(self.cs(), || {
Ok(&k_cur % 2u64 == BigUint::from(1u64)) Ok(&k_cur % 2u64 == BigUint::from(1u64))
})?); })?);
k_cur /= 2u64; k_cur /= 2u64;
@ -142,7 +139,7 @@ impl
}; };
let k_limbs = { let k_limbs = {
let zero = FpVar::Constant(BaseField::zero());
let zero = FpVar::Constant(BaseF::zero());
let mut limbs = Vec::new(); let mut limbs = Vec::new();
let mut k_bits_cur = k_bits.clone(); let mut k_bits_cur = k_bits.clone();
@ -158,10 +155,10 @@ impl
k_bits_cur = k_bits_cur[this_limb_size..].to_vec(); k_bits_cur = k_bits_cur[this_limb_size..].to_vec();
let mut limb = zero.clone(); let mut limb = zero.clone();
let mut cur = BaseField::one();
let mut cur = BaseF::one();
for bit in this_limb_bits.iter() { for bit in this_limb_bits.iter() {
limb += &(FpVar::<BaseField>::from(bit.clone()) * cur);
limb += &(FpVar::<BaseF>::from(bit.clone()) * cur);
cur.double_in_place(); cur.double_in_place();
} }
limbs.push(limb); limbs.push(limb);
@ -171,7 +168,7 @@ impl
limbs limbs
}; };
let k_gadget = AllocatedNonNativeFieldVar::<TargetField, BaseField> {
let k_gadget = AllocatedEmulatedFpVar::<TargetF, BaseF> {
cs: self.cs(), cs: self.cs(),
limbs: k_limbs, limbs: k_limbs,
num_of_additions_over_normal_form: self.prod_of_num_of_additions, num_of_additions_over_normal_form: self.prod_of_num_of_additions,
@ -181,20 +178,19 @@ impl
let cs = self.cs(); let cs = self.cs();
let r_gadget = AllocatedNonNativeFieldVar::<TargetField, BaseField>::new_witness(
ns!(cs, "r"),
|| Ok(self.value()?),
)?;
let r_gadget = AllocatedEmulatedFpVar::<TargetF, BaseF>::new_witness(ns!(cs, "r"), || {
Ok(self.value()?)
})?;
let params = get_params( let params = get_params(
TargetField::MODULUS_BIT_SIZE as usize,
BaseField::MODULUS_BIT_SIZE as usize,
TargetF::MODULUS_BIT_SIZE as usize,
BaseF::MODULUS_BIT_SIZE as usize,
self.get_optimization_type(), self.get_optimization_type(),
); );
// Step 1: reduce `self` and `other` if neceessary // Step 1: reduce `self` and `other` if neceessary
let mut prod_limbs = Vec::new(); let mut prod_limbs = Vec::new();
let zero = FpVar::<BaseField>::zero();
let zero = FpVar::<BaseF>::zero();
for _ in 0..2 * params.num_limbs - 1 { for _ in 0..2 * params.num_limbs - 1 {
prod_limbs.push(zero.clone()); prod_limbs.push(zero.clone());
@ -209,9 +205,8 @@ impl
let mut kp_plus_r_gadget = Self { let mut kp_plus_r_gadget = Self {
cs, cs,
limbs: prod_limbs, limbs: prod_limbs,
prod_of_num_of_additions: (p_gadget.num_of_additions_over_normal_form
+ BaseField::one())
* (k_gadget.num_of_additions_over_normal_form + BaseField::one()),
prod_of_num_of_additions: (p_gadget.num_of_additions_over_normal_form + BaseF::one())
* (k_gadget.num_of_additions_over_normal_form + BaseF::one()),
target_phantom: PhantomData, target_phantom: PhantomData,
}; };
@ -220,7 +215,7 @@ impl
kp_plus_r_gadget.limbs[kp_plus_r_limbs_len - 1 - i] += limb; kp_plus_r_gadget.limbs[kp_plus_r_limbs_len - 1 - i] += limb;
} }
Reducer::<TargetField, BaseField>::group_and_check_equality(
Reducer::<TargetF, BaseF>::group_and_check_equality(
surfeit, surfeit,
2 * params.bits_per_limb, 2 * params.bits_per_limb,
params.bits_per_limb, params.bits_per_limb,
@ -254,12 +249,11 @@ impl
/// Add native constant elem /// Add native constant elem
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
pub fn add_constant(&self, other: &TargetField) -> R1CSResult<Self> {
let mut other_limbs =
AllocatedNonNativeFieldVar::<TargetField, BaseField>::get_limbs_representations(
other,
self.get_optimization_type(),
)?;
pub fn add_constant(&self, other: &TargetF) -> R1CSResult<Self> {
let mut other_limbs = AllocatedEmulatedFpVar::<TargetF, BaseF>::get_limbs_representations(
other,
self.get_optimization_type(),
)?;
other_limbs.reverse(); other_limbs.reverse();
let mut new_limbs = Vec::new(); let mut new_limbs = Vec::new();
@ -277,7 +271,7 @@ impl
Ok(Self { Ok(Self {
cs: self.cs(), cs: self.cs(),
limbs: new_limbs, limbs: new_limbs,
prod_of_num_of_additions: self.prod_of_num_of_additions + BaseField::one(),
prod_of_num_of_additions: self.prod_of_num_of_additions + BaseF::one(),
target_phantom: PhantomData, target_phantom: PhantomData,
}) })
} }

src/fields/nonnative/field_var.rs → src/fields/emulated_fp/field_var.rs

@ -1,4 +1,4 @@
use super::{params::OptimizationType, AllocatedNonNativeFieldVar, NonNativeFieldMulResultVar};
use super::{params::OptimizationType, AllocatedEmulatedFpVar, MulResultVar};
use crate::{ use crate::{
boolean::Boolean, boolean::Boolean,
fields::{fp::FpVar, FieldVar}, fields::{fp::FpVar, FieldVar},
@ -13,20 +13,18 @@ use ark_std::{
vec::Vec, vec::Vec,
}; };
/// A gadget for representing non-native (`TargetField`) field elements over the
/// constraint field (`BaseField`).
/// A gadget for representing non-native (`TargetF`) field elements over the
/// constraint field (`BaseF`).
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
#[must_use] #[must_use]
pub enum NonNativeFieldVar<TargetField: PrimeField, BaseField: PrimeField> {
pub enum EmulatedFpVar<TargetF: PrimeField, BaseF: PrimeField> {
/// Constant /// Constant
Constant(TargetField),
Constant(TargetF),
/// Allocated gadget /// Allocated gadget
Var(AllocatedNonNativeFieldVar<TargetField, BaseField>),
Var(AllocatedEmulatedFpVar<TargetF, BaseF>),
} }
impl<TargetField: PrimeField, BaseField: PrimeField> PartialEq
for NonNativeFieldVar<TargetField, BaseField>
{
impl<TargetF: PrimeField, BaseF: PrimeField> PartialEq for EmulatedFpVar<TargetF, BaseF> {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.value() self.value()
.unwrap_or_default() .unwrap_or_default()
@ -34,25 +32,18 @@ impl PartialEq
} }
} }
impl<TargetField: PrimeField, BaseField: PrimeField> Eq
for NonNativeFieldVar<TargetField, BaseField>
{
}
impl<TargetF: PrimeField, BaseF: PrimeField> Eq for EmulatedFpVar<TargetF, BaseF> {}
impl<TargetField: PrimeField, BaseField: PrimeField> Hash
for NonNativeFieldVar<TargetField, BaseField>
{
impl<TargetF: PrimeField, BaseF: PrimeField> Hash for EmulatedFpVar<TargetF, BaseF> {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
self.value().unwrap_or_default().hash(state); self.value().unwrap_or_default().hash(state);
} }
} }
impl<TargetField: PrimeField, BaseField: PrimeField> R1CSVar<BaseField>
for NonNativeFieldVar<TargetField, BaseField>
{
type Value = TargetField;
impl<TargetF: PrimeField, BaseF: PrimeField> R1CSVar<BaseF> for EmulatedFpVar<TargetF, BaseF> {
type Value = TargetF;
fn cs(&self) -> ConstraintSystemRef<BaseField> {
fn cs(&self) -> ConstraintSystemRef<BaseF> {
match self { match self {
Self::Constant(_) => ConstraintSystemRef::None, Self::Constant(_) => ConstraintSystemRef::None,
Self::Var(a) => a.cs(), Self::Var(a) => a.cs(),
@ -67,53 +58,52 @@ impl R1CSVar
} }
} }
impl<TargetField: PrimeField, BaseField: PrimeField> From<Boolean<BaseField>>
for NonNativeFieldVar<TargetField, BaseField>
impl<TargetF: PrimeField, BaseF: PrimeField> From<Boolean<BaseF>>
for EmulatedFpVar<TargetF, BaseF>
{ {
fn from(other: Boolean<BaseField>) -> Self {
fn from(other: Boolean<BaseF>) -> Self {
if let Boolean::Constant(b) = other { if let Boolean::Constant(b) = other {
Self::Constant(<TargetField as From<u128>>::from(b as u128))
Self::Constant(<TargetF as From<u128>>::from(b as u128))
} else { } else {
// `other` is a variable // `other` is a variable
let one = Self::Constant(TargetField::one());
let zero = Self::Constant(TargetField::zero());
let one = Self::Constant(TargetF::one());
let zero = Self::Constant(TargetF::zero());
Self::conditionally_select(&other, &one, &zero).unwrap() Self::conditionally_select(&other, &one, &zero).unwrap()
} }
} }
} }
impl<TargetField: PrimeField, BaseField: PrimeField>
From<AllocatedNonNativeFieldVar<TargetField, BaseField>>
for NonNativeFieldVar<TargetField, BaseField>
impl<TargetF: PrimeField, BaseF: PrimeField> From<AllocatedEmulatedFpVar<TargetF, BaseF>>
for EmulatedFpVar<TargetF, BaseF>
{ {
fn from(other: AllocatedNonNativeFieldVar<TargetField, BaseField>) -> Self {
fn from(other: AllocatedEmulatedFpVar<TargetF, BaseF>) -> Self {
Self::Var(other) Self::Var(other)
} }
} }
impl<'a, TargetField: PrimeField, BaseField: PrimeField> FieldOpsBounds<'a, TargetField, Self>
for NonNativeFieldVar<TargetField, BaseField>
impl<'a, TargetF: PrimeField, BaseF: PrimeField> FieldOpsBounds<'a, TargetF, Self>
for EmulatedFpVar<TargetF, BaseF>
{ {
} }
impl<'a, TargetField: PrimeField, BaseField: PrimeField>
FieldOpsBounds<'a, TargetField, NonNativeFieldVar<TargetField, BaseField>>
for &'a NonNativeFieldVar<TargetField, BaseField>
impl<'a, TargetF: PrimeField, BaseF: PrimeField>
FieldOpsBounds<'a, TargetF, EmulatedFpVar<TargetF, BaseF>>
for &'a EmulatedFpVar<TargetF, BaseF>
{ {
} }
impl<TargetField: PrimeField, BaseField: PrimeField> FieldVar<TargetField, BaseField>
for NonNativeFieldVar<TargetField, BaseField>
impl<TargetF: PrimeField, BaseF: PrimeField> FieldVar<TargetF, BaseF>
for EmulatedFpVar<TargetF, BaseF>
{ {
fn zero() -> Self { fn zero() -> Self {
Self::Constant(TargetField::zero())
Self::Constant(TargetF::zero())
} }
fn one() -> Self { fn one() -> Self {
Self::Constant(TargetField::one())
Self::Constant(TargetF::one())
} }
fn constant(v: TargetField) -> Self {
fn constant(v: TargetF) -> Self {
Self::Constant(v) Self::Constant(v)
} }
@ -147,33 +137,33 @@ impl FieldVar
} }
impl_bounded_ops!( impl_bounded_ops!(
NonNativeFieldVar<TargetField, BaseField>,
TargetField,
EmulatedFpVar<TargetF, BaseF>,
TargetF,
Add, Add,
add, add,
AddAssign, AddAssign,
add_assign, add_assign,
|this: &'a NonNativeFieldVar<TargetField, BaseField>, other: &'a NonNativeFieldVar<TargetField, BaseField>| {
use NonNativeFieldVar::*;
|this: &'a EmulatedFpVar<TargetF, BaseF>, other: &'a EmulatedFpVar<TargetF, BaseF>| {
use EmulatedFpVar::*;
match (this, other) { match (this, other) {
(Constant(c1), Constant(c2)) => Constant(*c1 + c2), (Constant(c1), Constant(c2)) => Constant(*c1 + c2),
(Constant(c), Var(v)) | (Var(v), Constant(c)) => Var(v.add_constant(c).unwrap()), (Constant(c), Var(v)) | (Var(v), Constant(c)) => Var(v.add_constant(c).unwrap()),
(Var(v1), Var(v2)) => Var(v1.add(v2).unwrap()), (Var(v1), Var(v2)) => Var(v1.add(v2).unwrap()),
} }
}, },
|this: &'a NonNativeFieldVar<TargetField, BaseField>, other: TargetField| { this + &NonNativeFieldVar::Constant(other) },
(TargetField: PrimeField, BaseField: PrimeField),
|this: &'a EmulatedFpVar<TargetF, BaseF>, other: TargetF| { this + &EmulatedFpVar::Constant(other) },
(TargetF: PrimeField, BaseF: PrimeField),
); );
impl_bounded_ops!( impl_bounded_ops!(
NonNativeFieldVar<TargetField, BaseField>,
TargetField,
EmulatedFpVar<TargetF, BaseF>,
TargetF,
Sub, Sub,
sub, sub,
SubAssign, SubAssign,
sub_assign, sub_assign,
|this: &'a NonNativeFieldVar<TargetField, BaseField>, other: &'a NonNativeFieldVar<TargetField, BaseField>| {
use NonNativeFieldVar::*;
|this: &'a EmulatedFpVar<TargetF, BaseF>, other: &'a EmulatedFpVar<TargetF, BaseF>| {
use EmulatedFpVar::*;
match (this, other) { match (this, other) {
(Constant(c1), Constant(c2)) => Constant(*c1 - c2), (Constant(c1), Constant(c2)) => Constant(*c1 - c2),
(Var(v), Constant(c)) => Var(v.sub_constant(c).unwrap()), (Var(v), Constant(c)) => Var(v.sub_constant(c).unwrap()),
@ -181,45 +171,43 @@ impl_bounded_ops!(
(Var(v1), Var(v2)) => Var(v1.sub(v2).unwrap()), (Var(v1), Var(v2)) => Var(v1.sub(v2).unwrap()),
} }
}, },
|this: &'a NonNativeFieldVar<TargetField, BaseField>, other: TargetField| {
this - &NonNativeFieldVar::Constant(other)
|this: &'a EmulatedFpVar<TargetF, BaseF>, other: TargetF| {
this - &EmulatedFpVar::Constant(other)
}, },
(TargetField: PrimeField, BaseField: PrimeField),
(TargetF: PrimeField, BaseF: PrimeField),
); );
impl_bounded_ops!( impl_bounded_ops!(
NonNativeFieldVar<TargetField, BaseField>,
TargetField,
EmulatedFpVar<TargetF, BaseF>,
TargetF,
Mul, Mul,
mul, mul,
MulAssign, MulAssign,
mul_assign, mul_assign,
|this: &'a NonNativeFieldVar<TargetField, BaseField>, other: &'a NonNativeFieldVar<TargetField, BaseField>| {
use NonNativeFieldVar::*;
|this: &'a EmulatedFpVar<TargetF, BaseF>, other: &'a EmulatedFpVar<TargetF, BaseF>| {
use EmulatedFpVar::*;
match (this, other) { match (this, other) {
(Constant(c1), Constant(c2)) => Constant(*c1 * c2), (Constant(c1), Constant(c2)) => Constant(*c1 * c2),
(Constant(c), Var(v)) | (Var(v), Constant(c)) => Var(v.mul_constant(c).unwrap()), (Constant(c), Var(v)) | (Var(v), Constant(c)) => Var(v.mul_constant(c).unwrap()),
(Var(v1), Var(v2)) => Var(v1.mul(v2).unwrap()), (Var(v1), Var(v2)) => Var(v1.mul(v2).unwrap()),
} }
}, },
|this: &'a NonNativeFieldVar<TargetField, BaseField>, other: TargetField| {
|this: &'a EmulatedFpVar<TargetF, BaseF>, other: TargetF| {
if other.is_zero() { if other.is_zero() {
NonNativeFieldVar::zero()
EmulatedFpVar::zero()
} else { } else {
this * &NonNativeFieldVar::Constant(other)
this * &EmulatedFpVar::Constant(other)
} }
}, },
(TargetField: PrimeField, BaseField: PrimeField),
(TargetF: PrimeField, BaseF: PrimeField),
); );
/// ************************************************************************* /// *************************************************************************
/// ************************************************************************* /// *************************************************************************
impl<TargetField: PrimeField, BaseField: PrimeField> EqGadget<BaseField>
for NonNativeFieldVar<TargetField, BaseField>
{
impl<TargetF: PrimeField, BaseF: PrimeField> EqGadget<BaseF> for EmulatedFpVar<TargetF, BaseF> {
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
fn is_eq(&self, other: &Self) -> R1CSResult<Boolean<BaseField>> {
fn is_eq(&self, other: &Self) -> R1CSResult<Boolean<BaseF>> {
let cs = self.cs().or(other.cs()); let cs = self.cs().or(other.cs());
if cs == ConstraintSystemRef::None { if cs == ConstraintSystemRef::None {
@ -239,7 +227,7 @@ impl EqGadget
fn conditional_enforce_equal( fn conditional_enforce_equal(
&self, &self,
other: &Self, other: &Self,
should_enforce: &Boolean<BaseField>,
should_enforce: &Boolean<BaseF>,
) -> R1CSResult<()> { ) -> R1CSResult<()> {
match (self, other) { match (self, other) {
(Self::Constant(c1), Self::Constant(c2)) => { (Self::Constant(c1), Self::Constant(c2)) => {
@ -250,7 +238,7 @@ impl EqGadget
}, },
(Self::Constant(c), Self::Var(v)) | (Self::Var(v), Self::Constant(c)) => { (Self::Constant(c), Self::Var(v)) | (Self::Var(v), Self::Constant(c)) => {
let cs = v.cs(); let cs = v.cs();
let c = AllocatedNonNativeFieldVar::new_constant(cs, c)?;
let c = AllocatedEmulatedFpVar::new_constant(cs, c)?;
c.conditional_enforce_equal(v, should_enforce) c.conditional_enforce_equal(v, should_enforce)
}, },
(Self::Var(v1), Self::Var(v2)) => v1.conditional_enforce_equal(v2, should_enforce), (Self::Var(v1), Self::Var(v2)) => v1.conditional_enforce_equal(v2, should_enforce),
@ -261,7 +249,7 @@ impl EqGadget
fn conditional_enforce_not_equal( fn conditional_enforce_not_equal(
&self, &self,
other: &Self, other: &Self,
should_enforce: &Boolean<BaseField>,
should_enforce: &Boolean<BaseF>,
) -> R1CSResult<()> { ) -> R1CSResult<()> {
match (self, other) { match (self, other) {
(Self::Constant(c1), Self::Constant(c2)) => { (Self::Constant(c1), Self::Constant(c2)) => {
@ -272,7 +260,7 @@ impl EqGadget
}, },
(Self::Constant(c), Self::Var(v)) | (Self::Var(v), Self::Constant(c)) => { (Self::Constant(c), Self::Var(v)) | (Self::Var(v), Self::Constant(c)) => {
let cs = v.cs(); let cs = v.cs();
let c = AllocatedNonNativeFieldVar::new_constant(cs, c)?;
let c = AllocatedEmulatedFpVar::new_constant(cs, c)?;
c.conditional_enforce_not_equal(v, should_enforce) c.conditional_enforce_not_equal(v, should_enforce)
}, },
(Self::Var(v1), Self::Var(v2)) => v1.conditional_enforce_not_equal(v2, should_enforce), (Self::Var(v1), Self::Var(v2)) => v1.conditional_enforce_not_equal(v2, should_enforce),
@ -280,11 +268,9 @@ impl EqGadget
} }
} }
impl<TargetField: PrimeField, BaseField: PrimeField> ToBitsGadget<BaseField>
for NonNativeFieldVar<TargetField, BaseField>
{
impl<TargetF: PrimeField, BaseF: PrimeField> ToBitsGadget<BaseF> for EmulatedFpVar<TargetF, BaseF> {
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
fn to_bits_le(&self) -> R1CSResult<Vec<Boolean<BaseField>>> {
fn to_bits_le(&self) -> R1CSResult<Vec<Boolean<BaseF>>> {
match self { match self {
Self::Constant(_) => self.to_non_unique_bits_le(), Self::Constant(_) => self.to_non_unique_bits_le(),
Self::Var(v) => v.to_bits_le(), Self::Var(v) => v.to_bits_le(),
@ -292,11 +278,11 @@ impl ToBitsGadget
} }
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
fn to_non_unique_bits_le(&self) -> R1CSResult<Vec<Boolean<BaseField>>> {
fn to_non_unique_bits_le(&self) -> R1CSResult<Vec<Boolean<BaseF>>> {
use ark_ff::BitIteratorLE; use ark_ff::BitIteratorLE;
match self { match self {
Self::Constant(c) => Ok(BitIteratorLE::new(&c.into_bigint()) Self::Constant(c) => Ok(BitIteratorLE::new(&c.into_bigint())
.take((TargetField::MODULUS_BIT_SIZE) as usize)
.take((TargetF::MODULUS_BIT_SIZE) as usize)
.map(Boolean::constant) .map(Boolean::constant)
.collect::<Vec<_>>()), .collect::<Vec<_>>()),
Self::Var(v) => v.to_non_unique_bits_le(), Self::Var(v) => v.to_non_unique_bits_le(),
@ -304,13 +290,13 @@ impl ToBitsGadget
} }
} }
impl<TargetField: PrimeField, BaseField: PrimeField> ToBytesGadget<BaseField>
for NonNativeFieldVar<TargetField, BaseField>
impl<TargetF: PrimeField, BaseF: PrimeField> ToBytesGadget<BaseF>
for EmulatedFpVar<TargetF, BaseF>
{ {
/// Outputs the unique byte decomposition of `self` in *little-endian* /// Outputs the unique byte decomposition of `self` in *little-endian*
/// form. /// form.
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
fn to_bytes(&self) -> R1CSResult<Vec<UInt8<BaseField>>> {
fn to_bytes(&self) -> R1CSResult<Vec<UInt8<BaseF>>> {
match self { match self {
Self::Constant(c) => Ok(UInt8::constant_vec( Self::Constant(c) => Ok(UInt8::constant_vec(
c.into_bigint().to_bytes_le().as_slice(), c.into_bigint().to_bytes_le().as_slice(),
@ -321,7 +307,7 @@ impl ToBytesGadget
} }
#[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<BaseF>>> {
match self { match self {
Self::Constant(c) => Ok(UInt8::constant_vec( Self::Constant(c) => Ok(UInt8::constant_vec(
c.into_bigint().to_bytes_le().as_slice(), c.into_bigint().to_bytes_le().as_slice(),
@ -331,12 +317,12 @@ impl ToBytesGadget
} }
} }
impl<TargetField: PrimeField, BaseField: PrimeField> CondSelectGadget<BaseField>
for NonNativeFieldVar<TargetField, BaseField>
impl<TargetF: PrimeField, BaseF: PrimeField> CondSelectGadget<BaseF>
for EmulatedFpVar<TargetF, BaseF>
{ {
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
fn conditionally_select( fn conditionally_select(
cond: &Boolean<BaseField>,
cond: &Boolean<BaseF>,
true_value: &Self, true_value: &Self,
false_value: &Self, false_value: &Self,
) -> R1CSResult<Self> { ) -> R1CSResult<Self> {
@ -346,11 +332,11 @@ impl CondSelectGadget
_ => { _ => {
let cs = cond.cs(); let cs = cond.cs();
let true_value = match true_value { let true_value = match true_value {
Self::Constant(f) => AllocatedNonNativeFieldVar::new_constant(cs.clone(), f)?,
Self::Constant(f) => AllocatedEmulatedFpVar::new_constant(cs.clone(), f)?,
Self::Var(v) => v.clone(), Self::Var(v) => v.clone(),
}; };
let false_value = match false_value { let false_value = match false_value {
Self::Constant(f) => AllocatedNonNativeFieldVar::new_constant(cs, f)?,
Self::Constant(f) => AllocatedEmulatedFpVar::new_constant(cs, f)?,
Self::Var(v) => v.clone(), Self::Var(v) => v.clone(),
}; };
cond.select(&true_value, &false_value).map(Self::Var) cond.select(&true_value, &false_value).map(Self::Var)
@ -361,13 +347,13 @@ impl CondSelectGadget
/// Uses two bits to perform a lookup into a table /// Uses two bits to perform a lookup into a table
/// `b` is little-endian: `b[0]` is LSB. /// `b` is little-endian: `b[0]` is LSB.
impl<TargetField: PrimeField, BaseField: PrimeField> TwoBitLookupGadget<BaseField>
for NonNativeFieldVar<TargetField, BaseField>
impl<TargetF: PrimeField, BaseF: PrimeField> TwoBitLookupGadget<BaseF>
for EmulatedFpVar<TargetF, BaseF>
{ {
type TableConstant = TargetField;
type TableConstant = TargetF;
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
fn two_bit_lookup(b: &[Boolean<BaseField>], c: &[Self::TableConstant]) -> R1CSResult<Self> {
fn two_bit_lookup(b: &[Boolean<BaseF>], c: &[Self::TableConstant]) -> R1CSResult<Self> {
debug_assert_eq!(b.len(), 2); debug_assert_eq!(b.len(), 2);
debug_assert_eq!(c.len(), 4); debug_assert_eq!(c.len(), 4);
if b.cs().is_none() { if b.cs().is_none() {
@ -378,20 +364,20 @@ impl TwoBitLookupGadget
let index = lsb + (msb << 1); let index = lsb + (msb << 1);
Ok(Self::Constant(c[index])) Ok(Self::Constant(c[index]))
} else { } else {
AllocatedNonNativeFieldVar::two_bit_lookup(b, c).map(Self::Var)
AllocatedEmulatedFpVar::two_bit_lookup(b, c).map(Self::Var)
} }
} }
} }
impl<TargetField: PrimeField, BaseField: PrimeField> ThreeBitCondNegLookupGadget<BaseField>
for NonNativeFieldVar<TargetField, BaseField>
impl<TargetF: PrimeField, BaseF: PrimeField> ThreeBitCondNegLookupGadget<BaseF>
for EmulatedFpVar<TargetF, BaseF>
{ {
type TableConstant = TargetField;
type TableConstant = TargetF;
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
fn three_bit_cond_neg_lookup( fn three_bit_cond_neg_lookup(
b: &[Boolean<BaseField>],
b0b1: &Boolean<BaseField>,
b: &[Boolean<BaseF>],
b0b1: &Boolean<BaseF>,
c: &[Self::TableConstant], c: &[Self::TableConstant],
) -> R1CSResult<Self> { ) -> R1CSResult<Self> {
debug_assert_eq!(b.len(), 3); debug_assert_eq!(b.len(), 3);
@ -413,16 +399,16 @@ impl ThreeBitCondNegLookupGadget
}; };
Ok(Self::Constant(y)) Ok(Self::Constant(y))
} else { } else {
AllocatedNonNativeFieldVar::three_bit_cond_neg_lookup(b, b0b1, c).map(Self::Var)
AllocatedEmulatedFpVar::three_bit_cond_neg_lookup(b, b0b1, c).map(Self::Var)
} }
} }
} }
impl<TargetField: PrimeField, BaseField: PrimeField> AllocVar<TargetField, BaseField>
for NonNativeFieldVar<TargetField, BaseField>
impl<TargetF: PrimeField, BaseF: PrimeField> AllocVar<TargetF, BaseF>
for EmulatedFpVar<TargetF, BaseF>
{ {
fn new_variable<T: Borrow<TargetField>>(
cs: impl Into<Namespace<BaseField>>,
fn new_variable<T: Borrow<TargetF>>(
cs: impl Into<Namespace<BaseF>>,
f: impl FnOnce() -> Result<T, SynthesisError>, f: impl FnOnce() -> Result<T, SynthesisError>,
mode: AllocationMode, mode: AllocationMode,
) -> R1CSResult<Self> { ) -> R1CSResult<Self> {
@ -432,22 +418,22 @@ impl AllocVar
if cs == ConstraintSystemRef::None || mode == AllocationMode::Constant { if cs == ConstraintSystemRef::None || mode == AllocationMode::Constant {
Ok(Self::Constant(*f()?.borrow())) Ok(Self::Constant(*f()?.borrow()))
} else { } else {
AllocatedNonNativeFieldVar::new_variable(cs, f, mode).map(Self::Var)
AllocatedEmulatedFpVar::new_variable(cs, f, mode).map(Self::Var)
} }
} }
} }
impl<TargetField: PrimeField, BaseField: PrimeField> ToConstraintFieldGadget<BaseField>
for NonNativeFieldVar<TargetField, BaseField>
impl<TargetF: PrimeField, BaseF: PrimeField> ToConstraintFieldGadget<BaseF>
for EmulatedFpVar<TargetF, BaseF>
{ {
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
fn to_constraint_field(&self) -> R1CSResult<Vec<FpVar<BaseField>>> {
fn to_constraint_field(&self) -> R1CSResult<Vec<FpVar<BaseF>>> {
// 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 // By default, the constant is converted in the weight-optimized type, because
// it results in fewer elements. // it results in fewer elements.
match self { match self {
Self::Constant(c) => Ok(AllocatedNonNativeFieldVar::get_limbs_representations(
Self::Constant(c) => Ok(AllocatedEmulatedFpVar::get_limbs_representations(
c, c,
OptimizationType::Weight, OptimizationType::Weight,
)? )?
@ -459,40 +445,27 @@ impl ToConstraintFieldGadget
} }
} }
impl<TargetField: PrimeField, BaseField: PrimeField> NonNativeFieldVar<TargetField, BaseField> {
/// The `mul_without_reduce` for `NonNativeFieldVar`
impl<TargetF: PrimeField, BaseF: PrimeField> EmulatedFpVar<TargetF, BaseF> {
/// The `mul_without_reduce` for `EmulatedFpVar`
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
pub fn mul_without_reduce(
&self,
other: &Self,
) -> R1CSResult<NonNativeFieldMulResultVar<TargetField, BaseField>> {
pub fn mul_without_reduce(&self, other: &Self) -> R1CSResult<MulResultVar<TargetF, BaseF>> {
match self { match self {
Self::Constant(c) => match other { Self::Constant(c) => match other {
Self::Constant(other_c) => Ok(NonNativeFieldMulResultVar::Constant(*c * other_c)),
Self::Constant(other_c) => Ok(MulResultVar::Constant(*c * other_c)),
Self::Var(other_v) => { Self::Var(other_v) => {
let self_v = let self_v =
AllocatedNonNativeFieldVar::<TargetField, BaseField>::new_constant(
self.cs(),
c,
)?;
Ok(NonNativeFieldMulResultVar::Var(
other_v.mul_without_reduce(&self_v)?,
))
AllocatedEmulatedFpVar::<TargetF, BaseF>::new_constant(self.cs(), c)?;
Ok(MulResultVar::Var(other_v.mul_without_reduce(&self_v)?))
}, },
}, },
Self::Var(v) => { Self::Var(v) => {
let other_v = match other { let other_v = match other {
Self::Constant(other_c) => { Self::Constant(other_c) => {
AllocatedNonNativeFieldVar::<TargetField, BaseField>::new_constant(
self.cs(),
other_c,
)?
AllocatedEmulatedFpVar::<TargetF, BaseF>::new_constant(self.cs(), other_c)?
}, },
Self::Var(other_v) => other_v.clone(), Self::Var(other_v) => other_v.clone(),
}; };
Ok(NonNativeFieldMulResultVar::Var(
v.mul_without_reduce(&other_v)?,
))
Ok(MulResultVar::Var(v.mul_without_reduce(&other_v)?))
}, },
} }
} }

src/fields/nonnative/mod.rs → src/fields/emulated_fp/mod.rs

@ -17,8 +17,8 @@
//! //!
//! ## Usage //! ## Usage
//! //!
//! Because [`NonNativeFieldVar`] implements the [`FieldVar`] trait in arkworks,
//! we can treat it like a native field variable ([`FpVar`]).
//! Because [`EmulatedFpVar`] implements the [`FieldVar`] trait in arkworks,
//! we can treat it like a native prime field variable ([`FpVar`]).
//! //!
//! We can do the standard field operations, such as `+`, `-`, and `*`. See the //! We can do the standard field operations, such as `+`, `-`, and `*`. See the
//! following example: //! following example:
@ -28,7 +28,7 @@
//! # use ark_std::UniformRand; //! # use ark_std::UniformRand;
//! # use ark_relations::{ns, r1cs::ConstraintSystem}; //! # use ark_relations::{ns, r1cs::ConstraintSystem};
//! # use ark_r1cs_std::prelude::*; //! # use ark_r1cs_std::prelude::*;
//! use ark_r1cs_std::fields::nonnative::NonNativeFieldVar;
//! use ark_r1cs_std::fields::emulated_fp::EmulatedFpVar;
//! use ark_bls12_377::{Fr, Fq}; //! use ark_bls12_377::{Fr, Fq};
//! //!
//! # let mut rng = ark_std::test_rng(); //! # let mut rng = ark_std::test_rng();
@ -36,8 +36,8 @@
//! # let b_value = Fr::rand(&mut rng); //! # let b_value = Fr::rand(&mut rng);
//! # let cs = ConstraintSystem::<Fq>::new_ref(); //! # let cs = ConstraintSystem::<Fq>::new_ref();
//! //!
//! let a = NonNativeFieldVar::<Fr, Fq>::new_witness(ns!(cs, "a"), || Ok(a_value))?;
//! let b = NonNativeFieldVar::<Fr, Fq>::new_witness(ns!(cs, "b"), || Ok(b_value))?;
//! let a = EmulatedFpVar::<Fr, Fq>::new_witness(ns!(cs, "a"), || Ok(a_value))?;
//! let b = EmulatedFpVar::<Fr, Fq>::new_witness(ns!(cs, "b"), || Ok(b_value))?;
//! //!
//! // add //! // add
//! let a_plus_b = &a + &b; //! let a_plus_b = &a + &b;
@ -57,15 +57,15 @@
//! ## Advanced optimization //! ## Advanced optimization
//! //!
//! After each multiplication, our library internally performs a *reduce* //! After each multiplication, our library internally performs a *reduce*
//! operation, which reduces an intermediate type [`NonNativeFieldMulResultVar`]
//! to the normalized type [`NonNativeFieldVar`]. This enables a user to
//! operation, which reduces an intermediate type [`MulResultVar`]
//! to the normalized type [`EmulatedFpVar`]. This enables a user to
//! seamlessly perform a sequence of operations without worrying about the //! seamlessly perform a sequence of operations without worrying about the
//! underlying details. //! underlying details.
//! //!
//! However, this operation is expensive and is sometimes avoidable. We can //! However, this operation is expensive and is sometimes avoidable. We can
//! reduce the number of constraints by using this intermediate type, which only //! reduce the number of constraints by using this intermediate type, which only
//! supports additions. To multiply, it must be reduced back to //! supports additions. To multiply, it must be reduced back to
//! [`NonNativeFieldVar`]. See below for a skeleton example.
//! [`EmulatedFpVar`]. See below for a skeleton example.
//! //!
//! --- //! ---
//! //!
@ -82,7 +82,7 @@
//! //!
//! --- //! ---
//! //!
//! We can save one reduction by using the [`NonNativeFieldMulResultVar`], as
//! We can save one reduction by using [`MulResultVar`], as
//! follows: //! follows:
//! //!
//! ```ignore //! ```ignore
@ -98,11 +98,11 @@
//! //!
//! This implementation employs the standard idea of using multiple **limbs** to //! This implementation employs the standard idea of using multiple **limbs** to
//! represent an element of the target field. For example, an element in the //! represent an element of the target field. For example, an element in the
//! TargetField may be represented by three BaseField elements (i.e., the
//! TargetF may be represented by three BaseF elements (i.e., the
//! limbs). //! limbs).
//! //!
//! ```text //! ```text
//! TargetField -> limb 1, limb 2, and limb 3 (each is a BaseField element)
//! TargetF -> limb 1, limb 2, and limb 3 (each is a BaseF element)
//! ``` //! ```
//! //!
//! After some computation, the limbs become saturated and need to be //! After some computation, the limbs become saturated and need to be
@ -122,8 +122,8 @@
//! //!
//! \[OWWB20\]: A. Ozdemir, R. S. Wahby, B. Whitehat, and D. Boneh. "Scaling verifiable computation using efficient set accumulators," in *Proceedings of the 29th USENIX Security Symposium*, ser. Security ’20, 2020. //! \[OWWB20\]: A. Ozdemir, R. S. Wahby, B. Whitehat, and D. Boneh. "Scaling verifiable computation using efficient set accumulators," in *Proceedings of the 29th USENIX Security Symposium*, ser. Security ’20, 2020.
//! //!
//! [`NonNativeFieldVar`]: crate::fields::nonnative::NonNativeFieldVar
//! [`NonNativeFieldMulResultVar`]: crate::fields::nonnative::NonNativeFieldMulResultVar
//! [`EmulatedFpVar`]: crate::fields::emulated_fp::EmulatedFpVar
//! [`MulResultVar`]: crate::fields::emulated_fp::MulResultVar
//! [`FpVar`]: crate::fields::fp::FpVar //! [`FpVar`]: crate::fields::fp::FpVar
#![allow( #![allow(
@ -138,8 +138,8 @@ use ark_std::fmt::Debug;
/// Utilities for sampling parameters for non-native field gadgets /// Utilities for sampling parameters for non-native field gadgets
/// ///
/// - `BaseField`: the constraint field
/// - `TargetField`: the field being simulated
/// - `BaseF`: the constraint field
/// - `TargetF`: the field being simulated
/// - `num_limbs`: how many limbs are used /// - `num_limbs`: how many limbs are used
/// - `bits_per_limb`: the size of the limbs /// - `bits_per_limb`: the size of the limbs
pub mod params; pub mod params;
@ -178,11 +178,11 @@ macro_rules! overhead {
pub(crate) use overhead; pub(crate) use overhead;
/// Parameters for a specific `NonNativeFieldVar` instantiation
/// Parameters for a specific `EmulatedFpVar` 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 (`BaseF` elements) used to represent a
/// `TargetF` 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

+ 73
- 0
src/fields/emulated_fp/mul_result.rs

@ -0,0 +1,73 @@
use super::{AllocatedMulResultVar, EmulatedFpVar};
use ark_ff::PrimeField;
use ark_relations::r1cs::Result as R1CSResult;
/// An intermediate representation especially for the result of a
/// 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
/// obtain this intermediate representation, which can still be added.
/// Then, one can call `reduce` to reduce it back to `EmulatedFpVar`.
/// This may help cut the number of reduce operations.
#[derive(Debug)]
#[must_use]
pub enum MulResultVar<TargetF: PrimeField, BaseF: PrimeField> {
/// as a constant
Constant(TargetF),
/// as an allocated gadget
Var(AllocatedMulResultVar<TargetF, BaseF>),
}
impl<TargetF: PrimeField, BaseF: PrimeField> MulResultVar<TargetF, BaseF> {
/// Create a zero `MulResultVar` (used for additions)
pub fn zero() -> Self {
Self::Constant(TargetF::zero())
}
/// Create an `MulResultVar` from a constant
pub fn constant(v: TargetF) -> Self {
Self::Constant(v)
}
/// Reduce the `MulResultVar` back to EmulatedFpVar
#[tracing::instrument(target = "r1cs")]
pub fn reduce(&self) -> R1CSResult<EmulatedFpVar<TargetF, BaseF>> {
match self {
Self::Constant(c) => Ok(EmulatedFpVar::Constant(*c)),
Self::Var(v) => Ok(EmulatedFpVar::Var(v.reduce()?)),
}
}
}
impl<TargetF: PrimeField, BaseF: PrimeField> From<&EmulatedFpVar<TargetF, BaseF>>
for MulResultVar<TargetF, BaseF>
{
fn from(src: &EmulatedFpVar<TargetF, BaseF>) -> Self {
match src {
EmulatedFpVar::Constant(c) => MulResultVar::Constant(*c),
EmulatedFpVar::Var(v) => {
MulResultVar::Var(AllocatedMulResultVar::<TargetF, BaseF>::from(v))
},
}
}
}
impl_bounded_ops!(
MulResultVar<TargetF, BaseF>,
TargetF,
Add,
add,
AddAssign,
add_assign,
|this: &'a MulResultVar<TargetF, BaseF>, other: &'a MulResultVar<TargetF, BaseF>| {
use MulResultVar::*;
match (this, other) {
(Constant(c1), Constant(c2)) => Constant(*c1 + c2),
(Constant(c), Var(v)) | (Var(v), Constant(c)) => Var(v.add_constant(c).unwrap()),
(Var(v1), Var(v2)) => Var(v1.add(v2).unwrap()),
}
},
|this: &'a MulResultVar<TargetF, BaseF>, other: TargetF| { this + &MulResultVar::Constant(other) },
(TargetF: PrimeField, BaseF: PrimeField),
);

src/fields/nonnative/params.rs → src/fields/emulated_fp/params.rs

@ -25,7 +25,7 @@ pub enum OptimizationType {
Weight, Weight,
} }
/// A function to search for parameters for nonnative field gadgets
/// A function to search for parameters for emulated field gadgets
pub const fn find_parameters( pub const fn find_parameters(
base_field_prime_length: usize, base_field_prime_length: usize,
target_field_prime_bit_length: usize, target_field_prime_bit_length: usize,

src/fields/nonnative/reduce.rs → src/fields/emulated_fp/reduce.rs

@ -1,4 +1,4 @@
use super::{overhead, params::get_params, AllocatedNonNativeFieldVar};
use super::{overhead, params::get_params, AllocatedEmulatedFpVar};
use crate::{ use crate::{
alloc::AllocVar, alloc::AllocVar,
boolean::Boolean, boolean::Boolean,
@ -15,10 +15,7 @@ use ark_std::{cmp::min, marker::PhantomData, vec, vec::Vec};
use num_bigint::BigUint; use num_bigint::BigUint;
use num_integer::Integer; use num_integer::Integer;
pub fn limbs_to_bigint<BaseField: PrimeField>(
bits_per_limb: usize,
limbs: &[BaseField],
) -> BigUint {
pub fn limbs_to_bigint<BaseF: PrimeField>(bits_per_limb: usize, limbs: &[BaseF]) -> BigUint {
let mut val = BigUint::zero(); let mut val = BigUint::zero();
let mut big_cur = BigUint::one(); let mut big_cur = BigUint::one();
let two = BigUint::from(2u32); let two = BigUint::from(2u32);
@ -37,16 +34,15 @@ pub fn limbs_to_bigint(
val val
} }
pub fn bigint_to_basefield<BaseField: PrimeField>(bigint: &BigUint) -> BaseField {
let mut val = BaseField::zero();
let mut cur = BaseField::one();
pub fn bigint_to_basefield<BaseF: PrimeField>(bigint: &BigUint) -> BaseF {
let mut val = BaseF::zero();
let mut cur = BaseF::one();
let bytes = bigint.to_bytes_be(); let bytes = bigint.to_bytes_be();
let basefield_256 =
BaseField::from_bigint(<BaseField as PrimeField>::BigInt::from(256u64)).unwrap();
let basefield_256 = BaseF::from_bigint(<BaseF as PrimeField>::BigInt::from(256u64)).unwrap();
for byte in bytes.iter().rev() { for byte in bytes.iter().rev() {
let bytes_basefield = BaseField::from(*byte as u128);
let bytes_basefield = BaseF::from(*byte as u128);
val += cur * bytes_basefield; val += cur * bytes_basefield;
cur *= &basefield_256; cur *= &basefield_256;
@ -56,32 +52,28 @@ pub fn bigint_to_basefield(bigint: &BigUint) -> BaseField
} }
/// the collections of methods for reducing the presentations /// the collections of methods for reducing the presentations
pub struct Reducer<TargetField: PrimeField, BaseField: PrimeField> {
pub target_phantom: PhantomData<TargetField>,
pub base_phantom: PhantomData<BaseField>,
pub struct Reducer<TargetF: PrimeField, BaseF: PrimeField> {
pub target_phantom: PhantomData<TargetF>,
pub base_phantom: PhantomData<BaseF>,
} }
impl<TargetField: PrimeField, BaseField: PrimeField> Reducer<TargetField, BaseField> {
/// convert limbs to bits (take at most `BaseField::MODULUS_BIT_SIZE as
impl<TargetF: PrimeField, BaseF: PrimeField> Reducer<TargetF, BaseF> {
/// convert limbs to bits (take at most `BaseF::MODULUS_BIT_SIZE as
/// usize - 1` bits) This implementation would be more efficient than /// usize - 1` bits) This implementation would be more efficient than
/// the original `to_bits` or `to_non_unique_bits` since we enforce that /// the original `to_bits` or `to_non_unique_bits` since we enforce that
/// some bits are always zero. /// some bits are always zero.
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
pub fn limb_to_bits(
limb: &FpVar<BaseField>,
num_bits: usize,
) -> R1CSResult<Vec<Boolean<BaseField>>> {
pub fn limb_to_bits(limb: &FpVar<BaseF>, num_bits: usize) -> R1CSResult<Vec<Boolean<BaseF>>> {
let cs = limb.cs(); let cs = limb.cs();
let num_bits = min(BaseField::MODULUS_BIT_SIZE as usize - 1, num_bits);
let num_bits = min(BaseF::MODULUS_BIT_SIZE as usize - 1, num_bits);
let mut bits_considered = Vec::with_capacity(num_bits); let mut bits_considered = Vec::with_capacity(num_bits);
let limb_value = limb.value().unwrap_or_default(); let limb_value = limb.value().unwrap_or_default();
let num_bits_to_shave =
BaseField::BigInt::NUM_LIMBS * 64 - (BaseField::MODULUS_BIT_SIZE as usize);
let num_bits_to_shave = BaseF::BigInt::NUM_LIMBS * 64 - (BaseF::MODULUS_BIT_SIZE as usize);
for b in BitIteratorBE::new(limb_value.into_bigint()) for b in BitIteratorBE::new(limb_value.into_bigint())
.skip(num_bits_to_shave + (BaseField::MODULUS_BIT_SIZE as usize - num_bits))
.skip(num_bits_to_shave + (BaseF::MODULUS_BIT_SIZE as usize - num_bits))
{ {
bits_considered.push(b); bits_considered.push(b);
} }
@ -89,25 +81,24 @@ impl Reducer
if cs == ConstraintSystemRef::None { if cs == ConstraintSystemRef::None {
let mut bits = vec![]; let mut bits = vec![];
for b in bits_considered { for b in bits_considered {
bits.push(Boolean::<BaseField>::Constant(b));
bits.push(Boolean::<BaseF>::Constant(b));
} }
Ok(bits) Ok(bits)
} else { } else {
let mut bits = vec![]; let mut bits = vec![];
for b in bits_considered { for b in bits_considered {
bits.push(Boolean::<BaseField>::new_witness(
bits.push(Boolean::<BaseF>::new_witness(
ark_relations::ns!(cs, "bit"), ark_relations::ns!(cs, "bit"),
|| Ok(b), || Ok(b),
)?); )?);
} }
let mut bit_sum = FpVar::<BaseField>::zero();
let mut coeff = BaseField::one();
let mut bit_sum = FpVar::<BaseF>::zero();
let mut coeff = BaseF::one();
for bit in bits.iter().rev() { for bit in bits.iter().rev() {
bit_sum +=
<FpVar<BaseField> as From<Boolean<BaseField>>>::from((*bit).clone()) * coeff;
bit_sum += <FpVar<BaseF> as From<Boolean<BaseF>>>::from((*bit).clone()) * coeff;
coeff.double_in_place(); coeff.double_in_place();
} }
@ -119,11 +110,10 @@ impl Reducer
/// Reduction to the normal form /// Reduction to the normal form
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
pub fn reduce(elem: &mut AllocatedNonNativeFieldVar<TargetField, BaseField>) -> R1CSResult<()> {
let new_elem =
AllocatedNonNativeFieldVar::new_witness(ns!(elem.cs(), "normal_form"), || {
Ok(elem.value().unwrap_or_default())
})?;
pub fn reduce(elem: &mut AllocatedEmulatedFpVar<TargetF, BaseF>) -> R1CSResult<()> {
let new_elem = AllocatedEmulatedFpVar::new_witness(ns!(elem.cs(), "normal_form"), || {
Ok(elem.value().unwrap_or_default())
})?;
elem.conditional_enforce_equal(&new_elem, &Boolean::TRUE)?; elem.conditional_enforce_equal(&new_elem, &Boolean::TRUE)?;
*elem = new_elem; *elem = new_elem;
@ -132,17 +122,15 @@ impl Reducer
/// Reduction to be enforced after additions /// Reduction to be enforced after additions
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
pub fn post_add_reduce(
elem: &mut AllocatedNonNativeFieldVar<TargetField, BaseField>,
) -> R1CSResult<()> {
pub fn post_add_reduce(elem: &mut AllocatedEmulatedFpVar<TargetF, BaseF>) -> R1CSResult<()> {
let params = get_params( let params = get_params(
TargetField::MODULUS_BIT_SIZE as usize,
BaseField::MODULUS_BIT_SIZE as usize,
TargetF::MODULUS_BIT_SIZE as usize,
BaseF::MODULUS_BIT_SIZE as usize,
elem.get_optimization_type(), elem.get_optimization_type(),
); );
let surfeit = overhead!(elem.num_of_additions_over_normal_form + BaseField::one()) + 1;
let surfeit = overhead!(elem.num_of_additions_over_normal_form + BaseF::one()) + 1;
if BaseField::MODULUS_BIT_SIZE as usize > 2 * params.bits_per_limb + surfeit + 1 {
if BaseF::MODULUS_BIT_SIZE as usize > 2 * params.bits_per_limb + surfeit + 1 {
Ok(()) Ok(())
} else { } else {
Self::reduce(elem) Self::reduce(elem)
@ -153,8 +141,8 @@ impl Reducer
/// way that allows efficient multiplication /// 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_other: &mut AllocatedNonNativeFieldVar<TargetField, BaseField>,
elem: &mut AllocatedEmulatedFpVar<TargetF, BaseF>,
elem_other: &mut AllocatedEmulatedFpVar<TargetF, BaseF>,
) -> R1CSResult<()> { ) -> R1CSResult<()> {
assert_eq!( assert_eq!(
elem.get_optimization_type(), elem.get_optimization_type(),
@ -162,30 +150,29 @@ impl Reducer
); );
let params = get_params( let params = get_params(
TargetField::MODULUS_BIT_SIZE as usize,
BaseField::MODULUS_BIT_SIZE as usize,
TargetF::MODULUS_BIT_SIZE as usize,
BaseF::MODULUS_BIT_SIZE as usize,
elem.get_optimization_type(), elem.get_optimization_type(),
); );
if 2 * params.bits_per_limb + ark_std::log2(params.num_limbs) as usize if 2 * params.bits_per_limb + ark_std::log2(params.num_limbs) as usize
> BaseField::MODULUS_BIT_SIZE as usize - 1
> BaseF::MODULUS_BIT_SIZE as usize - 1
{ {
panic!("The current limb parameters do not support multiplication."); panic!("The current limb parameters do not support multiplication.");
} }
loop { loop {
let prod_of_num_of_additions = (elem.num_of_additions_over_normal_form
+ BaseField::one())
* (elem_other.num_of_additions_over_normal_form + BaseField::one());
let prod_of_num_of_additions = (elem.num_of_additions_over_normal_form + BaseF::one())
* (elem_other.num_of_additions_over_normal_form + BaseF::one());
let overhead_limb = overhead!(prod_of_num_of_additions.mul( let overhead_limb = overhead!(prod_of_num_of_additions.mul(
&BaseField::from_bigint(<BaseField as PrimeField>::BigInt::from(
&BaseF::from_bigint(<BaseF as PrimeField>::BigInt::from(
(params.num_limbs) as u64 (params.num_limbs) as u64
)) ))
.unwrap() .unwrap()
)); ));
let bits_per_mulresult_limb = 2 * (params.bits_per_limb + 1) + overhead_limb; let bits_per_mulresult_limb = 2 * (params.bits_per_limb + 1) + overhead_limb;
if bits_per_mulresult_limb < BaseField::MODULUS_BIT_SIZE as usize {
if bits_per_mulresult_limb < BaseF::MODULUS_BIT_SIZE as usize {
break; break;
} }
@ -203,9 +190,7 @@ impl Reducer
/// Reduction to the normal form /// Reduction to the normal form
#[tracing::instrument(target = "r1cs")] #[tracing::instrument(target = "r1cs")]
pub fn pre_eq_reduce(
elem: &mut AllocatedNonNativeFieldVar<TargetField, BaseField>,
) -> R1CSResult<()> {
pub fn pre_eq_reduce(elem: &mut AllocatedEmulatedFpVar<TargetF, BaseF>) -> R1CSResult<()> {
if elem.is_in_the_normal_form { if elem.is_in_the_normal_form {
return Ok(()); return Ok(());
} }
@ -219,14 +204,14 @@ impl Reducer
surfeit: usize, surfeit: usize,
bits_per_limb: usize, bits_per_limb: usize,
shift_per_limb: usize, shift_per_limb: usize,
left: &[FpVar<BaseField>],
right: &[FpVar<BaseField>],
left: &[FpVar<BaseF>],
right: &[FpVar<BaseF>],
) -> R1CSResult<()> { ) -> R1CSResult<()> {
let cs = left.cs().or(right.cs()); let cs = left.cs().or(right.cs());
let zero = FpVar::<BaseField>::zero();
let zero = FpVar::<BaseF>::zero();
let mut limb_pairs = Vec::<(FpVar<BaseField>, FpVar<BaseField>)>::new();
let num_limb_in_a_group = (BaseField::MODULUS_BIT_SIZE as usize
let mut limb_pairs = Vec::<(FpVar<BaseF>, FpVar<BaseF>)>::new();
let num_limb_in_a_group = (BaseF::MODULUS_BIT_SIZE as usize
- 1 - 1
- surfeit - surfeit
- 1 - 1
@ -237,9 +222,9 @@ impl Reducer
let shift_array = { let shift_array = {
let mut array = Vec::new(); let mut array = Vec::new();
let mut cur = BaseField::one().into_bigint();
let mut cur = BaseF::one().into_bigint();
for _ in 0..num_limb_in_a_group { for _ in 0..num_limb_in_a_group {
array.push(BaseField::from_bigint(cur).unwrap());
array.push(BaseF::from_bigint(cur).unwrap());
cur <<= shift_per_limb as u32; cur <<= shift_per_limb as u32;
} }
@ -252,7 +237,7 @@ impl Reducer
limb_pairs.push((left_limb.clone(), right_limb.clone())); limb_pairs.push((left_limb.clone(), right_limb.clone()));
} }
let mut groupped_limb_pairs = Vec::<(FpVar<BaseField>, FpVar<BaseField>, usize)>::new();
let mut groupped_limb_pairs = Vec::<(FpVar<BaseF>, FpVar<BaseF>, usize)>::new();
for limb_pairs_in_a_group in limb_pairs.chunks(num_limb_in_a_group) { for limb_pairs_in_a_group in limb_pairs.chunks(num_limb_in_a_group) {
let mut left_total_limb = zero.clone(); let mut left_total_limb = zero.clone();
@ -275,19 +260,19 @@ impl Reducer
// This part we mostly use the techniques in bellman-bignat // This part we mostly use the techniques in bellman-bignat
// The following code is adapted from https://github.com/alex-ozdemir/bellman-bignat/blob/master/src/mp/bignat.rs#L567 // The following code is adapted from https://github.com/alex-ozdemir/bellman-bignat/blob/master/src/mp/bignat.rs#L567
let mut carry_in = zero; let mut carry_in = zero;
let mut carry_in_value = BaseField::zero();
let mut carry_in_value = BaseF::zero();
let mut accumulated_extra = BigUint::zero(); let mut accumulated_extra = BigUint::zero();
for (group_id, (left_total_limb, right_total_limb, num_limb_in_this_group)) in for (group_id, (left_total_limb, right_total_limb, num_limb_in_this_group)) in
groupped_limb_pairs.iter().enumerate() groupped_limb_pairs.iter().enumerate()
{ {
let mut pad_limb_repr = BaseField::ONE.into_bigint();
let mut pad_limb_repr = BaseF::ONE.into_bigint();
pad_limb_repr <<= (surfeit pad_limb_repr <<= (surfeit
+ (bits_per_limb - shift_per_limb) + (bits_per_limb - shift_per_limb)
+ shift_per_limb * num_limb_in_this_group + shift_per_limb * num_limb_in_this_group
+ 1 + 1
+ 1) as u32; + 1) as u32;
let pad_limb = BaseField::from_bigint(pad_limb_repr).unwrap();
let pad_limb = BaseF::from_bigint(pad_limb_repr).unwrap();
let left_total_limb_value = left_total_limb.value().unwrap_or_default(); let left_total_limb_value = left_total_limb.value().unwrap_or_default();
let right_total_limb_value = right_total_limb.value().unwrap_or_default(); let right_total_limb_value = right_total_limb.value().unwrap_or_default();
@ -298,7 +283,7 @@ impl Reducer
let carry_repr = let carry_repr =
carry_value.into_bigint() >> (shift_per_limb * num_limb_in_this_group) as u32; carry_value.into_bigint() >> (shift_per_limb * num_limb_in_this_group) as u32;
carry_value = BaseField::from_bigint(carry_repr).unwrap();
carry_value = BaseF::from_bigint(carry_repr).unwrap();
let carry = FpVar::new_witness(cs.clone(), || Ok(carry_value))?; let carry = FpVar::new_witness(cs.clone(), || Ok(carry_value))?;
@ -307,7 +292,7 @@ impl Reducer
let (new_accumulated_extra, remainder) = accumulated_extra.div_rem( let (new_accumulated_extra, remainder) = accumulated_extra.div_rem(
&BigUint::from(2u64).pow((shift_per_limb * num_limb_in_this_group) as u32), &BigUint::from(2u64).pow((shift_per_limb * num_limb_in_this_group) as u32),
); );
let remainder_limb = bigint_to_basefield::<BaseField>(&remainder);
let remainder_limb = bigint_to_basefield::<BaseF>(&remainder);
// Now check // Now check
// left_total_limb + pad_limb + carry_in - right_total_limb // left_total_limb + pad_limb + carry_in - right_total_limb
@ -316,21 +301,21 @@ impl Reducer
let eqn_left = left_total_limb + pad_limb + &carry_in - right_total_limb; let eqn_left = left_total_limb + pad_limb + &carry_in - right_total_limb;
let eqn_right = &carry let eqn_right = &carry
* BaseField::from(2u64).pow(&[(shift_per_limb * num_limb_in_this_group) as u64])
* BaseF::from(2u64).pow(&[(shift_per_limb * num_limb_in_this_group) as u64])
+ remainder_limb; + remainder_limb;
eqn_left.conditional_enforce_equal(&eqn_right, &Boolean::<BaseField>::TRUE)?;
eqn_left.conditional_enforce_equal(&eqn_right, &Boolean::<BaseF>::TRUE)?;
accumulated_extra = new_accumulated_extra; accumulated_extra = new_accumulated_extra;
carry_in = carry.clone(); carry_in = carry.clone();
carry_in_value = carry_value; carry_in_value = carry_value;
if group_id == groupped_limb_pairs.len() - 1 { if group_id == groupped_limb_pairs.len() - 1 {
carry.enforce_equal(&FpVar::<BaseField>::Constant(bigint_to_basefield(
carry.enforce_equal(&FpVar::<BaseF>::Constant(bigint_to_basefield(
&accumulated_extra, &accumulated_extra,
)))?; )))?;
} else { } else {
Reducer::<TargetField, BaseField>::limb_to_bits(&carry, surfeit + bits_per_limb)?;
Reducer::<TargetF, BaseF>::limb_to_bits(&carry, surfeit + bits_per_limb)?;
} }
} }

+ 2
- 2
src/fields/mod.rs

@ -20,9 +20,9 @@ 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
/// This module contains a generic implementation of "emulated" prime field
/// variables. 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 emulated_fp;
/// This module contains a generic implementation of the degree-12 tower /// This module contains a generic implementation of the degree-12 tower
/// extension field. That is, it implements the R1CS equivalent of /// extension field. That is, it implements the R1CS equivalent of

+ 0
- 79
src/fields/nonnative/mul_result.rs

@ -1,79 +0,0 @@
use super::{AllocatedNonNativeFieldMulResultVar, NonNativeFieldVar};
use ark_ff::PrimeField;
use ark_relations::r1cs::Result as R1CSResult;
/// An intermediate representation especially for the result of a
/// 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
/// obtain this intermediate representation, which can still be added.
/// Then, one can call `reduce` to reduce it back to `NonNativeFieldVar`.
/// This may help cut the number of reduce operations.
#[derive(Debug)]
#[must_use]
pub enum NonNativeFieldMulResultVar<TargetField: PrimeField, BaseField: PrimeField> {
/// as a constant
Constant(TargetField),
/// as an allocated gadget
Var(AllocatedNonNativeFieldMulResultVar<TargetField, BaseField>),
}
impl<TargetField: PrimeField, BaseField: PrimeField>
NonNativeFieldMulResultVar<TargetField, BaseField>
{
/// Create a zero `NonNativeFieldMulResultVar` (used for additions)
pub fn zero() -> Self {
Self::Constant(TargetField::zero())
}
/// Create an `NonNativeFieldMulResultVar` from a constant
pub fn constant(v: TargetField) -> Self {
Self::Constant(v)
}
/// Reduce the `NonNativeFieldMulResultVar` back to NonNativeFieldVar
#[tracing::instrument(target = "r1cs")]
pub fn reduce(&self) -> R1CSResult<NonNativeFieldVar<TargetField, BaseField>> {
match self {
Self::Constant(c) => Ok(NonNativeFieldVar::Constant(*c)),
Self::Var(v) => Ok(NonNativeFieldVar::Var(v.reduce()?)),
}
}
}
impl<TargetField: PrimeField, BaseField: PrimeField>
From<&NonNativeFieldVar<TargetField, BaseField>>
for NonNativeFieldMulResultVar<TargetField, BaseField>
{
fn from(src: &NonNativeFieldVar<TargetField, BaseField>) -> Self {
match src {
NonNativeFieldVar::Constant(c) => NonNativeFieldMulResultVar::Constant(*c),
NonNativeFieldVar::Var(v) => {
NonNativeFieldMulResultVar::Var(AllocatedNonNativeFieldMulResultVar::<
TargetField,
BaseField,
>::from(v))
},
}
}
}
impl_bounded_ops!(
NonNativeFieldMulResultVar<TargetField, BaseField>,
TargetField,
Add,
add,
AddAssign,
add_assign,
|this: &'a NonNativeFieldMulResultVar<TargetField, BaseField>, other: &'a NonNativeFieldMulResultVar<TargetField, BaseField>| {
use NonNativeFieldMulResultVar::*;
match (this, other) {
(Constant(c1), Constant(c2)) => Constant(*c1 + c2),
(Constant(c), Var(v)) | (Var(v), Constant(c)) => Var(v.add_constant(c).unwrap()),
(Var(v1), Var(v2)) => Var(v1.add(v2).unwrap()),
}
},
|this: &'a NonNativeFieldMulResultVar<TargetField, BaseField>, other: TargetField| { this + &NonNativeFieldMulResultVar::Constant(other) },
(TargetField: PrimeField, BaseField: PrimeField),
);

+ 6
- 6
src/groups/curves/short_weierstrass/mod.rs

@ -7,7 +7,7 @@ use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError};
use ark_std::{borrow::Borrow, marker::PhantomData, ops::Mul}; use ark_std::{borrow::Borrow, marker::PhantomData, ops::Mul};
use non_zero_affine::NonZeroAffineVar; use non_zero_affine::NonZeroAffineVar;
use crate::fields::nonnative::NonNativeFieldVar;
use crate::fields::emulated_fp::EmulatedFpVar;
use crate::{fields::fp::FpVar, prelude::*, ToConstraintFieldGadget, Vec}; use crate::{fields::fp::FpVar, prelude::*, ToConstraintFieldGadget, Vec};
/// This module provides a generic implementation of G1 and G2 for /// This module provides a generic implementation of G1 and G2 for
@ -688,13 +688,13 @@ impl_bounded_ops!(
impl_bounded_ops_diff!( impl_bounded_ops_diff!(
ProjectiveVar<P, F>, ProjectiveVar<P, F>,
SWProjective<P>, SWProjective<P>,
NonNativeFieldVar<P::ScalarField, BasePrimeField<P>>,
EmulatedFpVar<P::ScalarField, BasePrimeField<P>>,
P::ScalarField, P::ScalarField,
Mul, Mul,
mul, mul,
MulAssign, MulAssign,
mul_assign, mul_assign,
|this: &'a ProjectiveVar<P, F>, other: &'a NonNativeFieldVar<P::ScalarField, BasePrimeField<P>>| {
|this: &'a ProjectiveVar<P, F>, other: &'a EmulatedFpVar<P::ScalarField, BasePrimeField<P>>| {
if this.is_constant() && other.is_constant() { if this.is_constant() && other.is_constant() {
assert!(this.is_constant() && other.is_constant()); assert!(this.is_constant() && other.is_constant());
ProjectiveVar::constant(this.value().unwrap() * &other.value().unwrap()) ProjectiveVar::constant(this.value().unwrap() * &other.value().unwrap())
@ -703,7 +703,7 @@ impl_bounded_ops_diff!(
this.scalar_mul_le(bits.iter()).unwrap() this.scalar_mul_le(bits.iter()).unwrap()
} }
}, },
|this: &'a ProjectiveVar<P, F>, other: P::ScalarField| this * NonNativeFieldVar::constant(other),
|this: &'a ProjectiveVar<P, F>, other: P::ScalarField| this * EmulatedFpVar::constant(other),
(F: FieldVar<P::BaseField, BasePrimeField<P>>, P: SWCurveConfig), (F: FieldVar<P::BaseField, BasePrimeField<P>>, P: SWCurveConfig),
for <'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>, for <'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>,
); );
@ -981,7 +981,7 @@ mod test_sw_curve {
use crate::{ use crate::{
alloc::AllocVar, alloc::AllocVar,
eq::EqGadget, eq::EqGadget,
fields::{fp::FpVar, nonnative::NonNativeFieldVar},
fields::{emulated_fp::EmulatedFpVar, fp::FpVar},
groups::{curves::short_weierstrass::ProjectiveVar, CurveVar}, groups::{curves::short_weierstrass::ProjectiveVar, CurveVar},
ToBitsGadget, ToBitsGadget,
}; };
@ -1015,7 +1015,7 @@ mod test_sw_curve {
ProjectiveVar::<G::Config, FpVar<G::BaseField>>::new_input(cs.clone(), || { ProjectiveVar::<G::Config, FpVar<G::BaseField>>::new_input(cs.clone(), || {
Ok(point_out) Ok(point_out)
})?; })?;
let scalar = NonNativeFieldVar::new_input(cs.clone(), || Ok(scalar))?;
let scalar = EmulatedFpVar::new_input(cs.clone(), || Ok(scalar))?;
let mul = point_in.scalar_mul_le(scalar.to_bits_le().unwrap().iter())?; let mul = point_in.scalar_mul_le(scalar.to_bits_le().unwrap().iter())?;

+ 4
- 4
src/groups/curves/twisted_edwards/mod.rs

@ -8,7 +8,7 @@ use ark_ec::{
use ark_ff::{BitIteratorBE, Field, One, PrimeField, Zero}; use ark_ff::{BitIteratorBE, Field, One, PrimeField, Zero};
use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError}; use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError};
use crate::fields::nonnative::NonNativeFieldVar;
use crate::fields::emulated_fp::EmulatedFpVar;
use crate::{prelude::*, ToConstraintFieldGadget, Vec}; use crate::{prelude::*, ToConstraintFieldGadget, Vec};
use crate::fields::fp::FpVar; use crate::fields::fp::FpVar;
@ -787,13 +787,13 @@ impl_bounded_ops!(
impl_bounded_ops_diff!( impl_bounded_ops_diff!(
AffineVar<P, F>, AffineVar<P, F>,
TEProjective<P>, TEProjective<P>,
NonNativeFieldVar<P::ScalarField, BasePrimeField<P>>,
EmulatedFpVar<P::ScalarField, BasePrimeField<P>>,
P::ScalarField, P::ScalarField,
Mul, Mul,
mul, mul,
MulAssign, MulAssign,
mul_assign, mul_assign,
|this: &'a AffineVar<P, F>, other: &'a NonNativeFieldVar<P::ScalarField, BasePrimeField<P>>| {
|this: &'a AffineVar<P, F>, other: &'a EmulatedFpVar<P::ScalarField, BasePrimeField<P>>| {
if this.is_constant() && other.is_constant() { if this.is_constant() && other.is_constant() {
assert!(this.is_constant() && other.is_constant()); assert!(this.is_constant() && other.is_constant());
AffineVar::constant(this.value().unwrap() * &other.value().unwrap()) AffineVar::constant(this.value().unwrap() * &other.value().unwrap())
@ -802,7 +802,7 @@ impl_bounded_ops_diff!(
this.scalar_mul_le(bits.iter()).unwrap() this.scalar_mul_le(bits.iter()).unwrap()
} }
}, },
|this: &'a AffineVar<P, F>, other: P::ScalarField| this * NonNativeFieldVar::constant(other),
|this: &'a AffineVar<P, F>, other: P::ScalarField| this * EmulatedFpVar::constant(other),
( (
F :FieldVar<P::BaseField, BasePrimeField<P>> F :FieldVar<P::BaseField, BasePrimeField<P>>
+ TwoBitLookupGadget<BasePrimeField<P>, TableConstant = P::BaseField>, + TwoBitLookupGadget<BasePrimeField<P>, TableConstant = P::BaseField>,

+ 4
- 4
src/groups/mod.rs

@ -1,4 +1,4 @@
use crate::{fields::nonnative::NonNativeFieldVar, prelude::*};
use crate::{fields::emulated_fp::EmulatedFpVar, prelude::*};
use ark_ff::PrimeField; use ark_ff::PrimeField;
use ark_relations::r1cs::{Namespace, SynthesisError}; use ark_relations::r1cs::{Namespace, SynthesisError};
use core::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign}; use core::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign};
@ -44,9 +44,9 @@ pub trait CurveVar:
+ SubAssign<C> + SubAssign<C>
+ AddAssign<Self> + AddAssign<Self>
+ SubAssign<Self> + SubAssign<Self>
+ Mul<NonNativeFieldVar<C::ScalarField, ConstraintF>, Output = Self>
+ for<'a> Mul<&'a NonNativeFieldVar<C::ScalarField, ConstraintF>, Output = Self>
+ MulAssign<NonNativeFieldVar<C::ScalarField, ConstraintF>>
+ Mul<EmulatedFpVar<C::ScalarField, ConstraintF>, Output = Self>
+ for<'a> Mul<&'a EmulatedFpVar<C::ScalarField, ConstraintF>, Output = Self>
+ MulAssign<EmulatedFpVar<C::ScalarField, ConstraintF>>
{ {
/// Returns the constant `F::zero()`. This is the identity /// Returns the constant `F::zero()`. This is the identity
/// of the group. /// of the group.

+ 126
- 130
tests/arithmetic_tests.rs

@ -10,7 +10,7 @@ use ark_r1cs_std::{
alloc::AllocVar, alloc::AllocVar,
eq::EqGadget, eq::EqGadget,
fields::{ fields::{
nonnative::{AllocatedNonNativeFieldVar, NonNativeFieldVar},
emulated_fp::{AllocatedEmulatedFpVar, EmulatedFpVar},
FieldVar, FieldVar,
}, },
R1CSVar, R1CSVar,
@ -28,16 +28,16 @@ const TEST_COUNT: usize = 100;
#[cfg(ci)] #[cfg(ci)]
const TEST_COUNT: usize = 1; const TEST_COUNT: usize = 1;
fn allocation_test<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>(
fn allocation_test<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>(
cs: ConstraintSystemRef<BaseField>, cs: ConstraintSystemRef<BaseField>,
rng: &mut R, rng: &mut R,
) { ) {
let a_native = TargetField::rand(rng);
let a = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
ark_relations::ns!(cs, "alloc a"),
|| Ok(a_native),
)
.unwrap();
let a_native = TargetF::rand(rng);
let a =
EmulatedFpVar::<TargetF, BaseField>::new_witness(ark_relations::ns!(cs, "alloc a"), || {
Ok(a_native)
})
.unwrap();
let a_actual = a.value().unwrap(); let a_actual = a.value().unwrap();
let a_expected = a_native; let a_expected = a_native;
@ -46,39 +46,38 @@ fn allocation_test(
"allocated value does not equal the expected value" "allocated value does not equal the expected value"
); );
let (_a, a_bits) =
AllocatedNonNativeFieldVar::<TargetField, BaseField>::new_witness_with_le_bits(
ark_relations::ns!(cs, "alloc a2"),
|| Ok(a_native),
)
.unwrap();
let (_a, a_bits) = AllocatedEmulatedFpVar::<TargetF, BaseField>::new_witness_with_le_bits(
ark_relations::ns!(cs, "alloc a2"),
|| Ok(a_native),
)
.unwrap();
let a_bits_actual: Vec<bool> = a_bits.into_iter().map(|b| b.value().unwrap()).collect(); let a_bits_actual: Vec<bool> = a_bits.into_iter().map(|b| b.value().unwrap()).collect();
let mut a_bits_expected = a_native.into_bigint().to_bits_le(); let mut a_bits_expected = a_native.into_bigint().to_bits_le();
a_bits_expected.truncate(TargetField::MODULUS_BIT_SIZE as usize);
a_bits_expected.truncate(TargetF::MODULUS_BIT_SIZE as usize);
assert_eq!( assert_eq!(
a_bits_actual, a_bits_expected, a_bits_actual, a_bits_expected,
"allocated bits does not equal the expected bits" "allocated bits does not equal the expected bits"
); );
} }
fn addition_test<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>(
fn addition_test<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>(
cs: ConstraintSystemRef<BaseField>, cs: ConstraintSystemRef<BaseField>,
rng: &mut R, rng: &mut R,
) { ) {
let a_native = TargetField::rand(rng);
let a = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
ark_relations::ns!(cs, "alloc a"),
|| Ok(a_native),
)
.unwrap();
let a_native = TargetF::rand(rng);
let a =
EmulatedFpVar::<TargetF, BaseField>::new_witness(ark_relations::ns!(cs, "alloc a"), || {
Ok(a_native)
})
.unwrap();
let b_native = TargetField::rand(rng);
let b = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
ark_relations::ns!(cs, "alloc b"),
|| Ok(b_native),
)
.unwrap();
let b_native = TargetF::rand(rng);
let b =
EmulatedFpVar::<TargetF, BaseField>::new_witness(ark_relations::ns!(cs, "alloc b"), || {
Ok(b_native)
})
.unwrap();
let a_plus_b = a + &b; let a_plus_b = a + &b;
@ -87,23 +86,23 @@ fn addition_test(
assert!(a_plus_b_actual.eq(&a_plus_b_expected), "a + b failed"); assert!(a_plus_b_actual.eq(&a_plus_b_expected), "a + b failed");
} }
fn multiplication_test<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>(
fn multiplication_test<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>(
cs: ConstraintSystemRef<BaseField>, cs: ConstraintSystemRef<BaseField>,
rng: &mut R, rng: &mut R,
) { ) {
let a_native = TargetField::rand(rng);
let a = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
ark_relations::ns!(cs, "alloc a"),
|| Ok(a_native),
)
.unwrap();
let a_native = TargetF::rand(rng);
let a =
EmulatedFpVar::<TargetF, BaseField>::new_witness(ark_relations::ns!(cs, "alloc a"), || {
Ok(a_native)
})
.unwrap();
let b_native = TargetField::rand(rng);
let b = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
ark_relations::ns!(cs, "alloc b"),
|| Ok(b_native),
)
.unwrap();
let b_native = TargetF::rand(rng);
let b =
EmulatedFpVar::<TargetF, BaseField>::new_witness(ark_relations::ns!(cs, "alloc b"), || {
Ok(b_native)
})
.unwrap();
let a_times_b = a * &b; let a_times_b = a * &b;
@ -119,28 +118,28 @@ fn multiplication_test
); );
} }
fn equality_test<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>(
fn equality_test<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>(
cs: ConstraintSystemRef<BaseField>, cs: ConstraintSystemRef<BaseField>,
rng: &mut R, rng: &mut R,
) { ) {
let a_native = TargetField::rand(rng);
let a = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
ark_relations::ns!(cs, "alloc a"),
|| Ok(a_native),
)
.unwrap();
let a_native = TargetF::rand(rng);
let a =
EmulatedFpVar::<TargetF, BaseField>::new_witness(ark_relations::ns!(cs, "alloc a"), || {
Ok(a_native)
})
.unwrap();
let b_native = TargetField::rand(rng);
let b = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
ark_relations::ns!(cs, "alloc b"),
|| Ok(b_native),
)
.unwrap();
let b_native = TargetF::rand(rng);
let b =
EmulatedFpVar::<TargetF, BaseField>::new_witness(ark_relations::ns!(cs, "alloc b"), || {
Ok(b_native)
})
.unwrap();
let a_times_b = a * &b; let a_times_b = a * &b;
let a_times_b_expected = a_native * &b_native; let a_times_b_expected = a_native * &b_native;
let a_times_b_expected_gadget = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
let a_times_b_expected_gadget = EmulatedFpVar::<TargetF, BaseField>::new_witness(
ark_relations::ns!(cs, "alloc a * b"), ark_relations::ns!(cs, "alloc a * b"),
|| Ok(a_times_b_expected), || Ok(a_times_b_expected),
) )
@ -149,21 +148,21 @@ fn equality_test(
a_times_b.enforce_equal(&a_times_b_expected_gadget).unwrap(); a_times_b.enforce_equal(&a_times_b_expected_gadget).unwrap();
} }
fn edge_cases_test<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>(
fn edge_cases_test<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>(
cs: ConstraintSystemRef<BaseField>, cs: ConstraintSystemRef<BaseField>,
rng: &mut R, rng: &mut R,
) { ) {
let zero_native = TargetField::zero();
let zero = NonNativeFieldVar::<TargetField, BaseField>::zero();
let one = NonNativeFieldVar::<TargetField, BaseField>::one();
let a_native = TargetField::rand(rng);
let minus_a_native = TargetField::zero() - &a_native;
let a = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
ark_relations::ns!(cs, "alloc a"),
|| Ok(a_native),
)
.unwrap();
let zero_native = TargetF::zero();
let zero = EmulatedFpVar::<TargetF, BaseField>::zero();
let one = EmulatedFpVar::<TargetF, BaseField>::one();
let a_native = TargetF::rand(rng);
let minus_a_native = TargetF::zero() - &a_native;
let a =
EmulatedFpVar::<TargetF, BaseField>::new_witness(ark_relations::ns!(cs, "alloc a"), || {
Ok(a_native)
})
.unwrap();
let a_plus_zero = &a + &zero; let a_plus_zero = &a + &zero;
let a_minus_zero = &a - &zero; let a_minus_zero = &a - &zero;
@ -235,13 +234,13 @@ fn edge_cases_test(
); );
} }
fn distribution_law_test<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>(
fn distribution_law_test<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>(
cs: ConstraintSystemRef<BaseField>, cs: ConstraintSystemRef<BaseField>,
rng: &mut R, rng: &mut R,
) { ) {
let a_native = TargetField::rand(rng);
let b_native = TargetField::rand(rng);
let c_native = TargetField::rand(rng);
let a_native = TargetF::rand(rng);
let b_native = TargetF::rand(rng);
let c_native = TargetF::rand(rng);
let a_plus_b_native = a_native.clone() + &b_native; let a_plus_b_native = a_native.clone() + &b_native;
let a_times_c_native = a_native.clone() * &c_native; let a_times_c_native = a_native.clone() * &c_native;
@ -254,20 +253,17 @@ fn distribution_law_test
"(a + b) * c doesn't equal (a * c) + (b * c)" "(a + b) * c doesn't equal (a * c) + (b * c)"
); );
let a = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
ark_relations::ns!(cs, "a"),
|| Ok(a_native),
)
let a = EmulatedFpVar::<TargetF, BaseField>::new_witness(ark_relations::ns!(cs, "a"), || {
Ok(a_native)
})
.unwrap(); .unwrap();
let b = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
ark_relations::ns!(cs, "b"),
|| Ok(b_native),
)
let b = EmulatedFpVar::<TargetF, BaseField>::new_witness(ark_relations::ns!(cs, "b"), || {
Ok(b_native)
})
.unwrap(); .unwrap();
let c = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
ark_relations::ns!(cs, "c"),
|| Ok(c_native),
)
let c = EmulatedFpVar::<TargetF, BaseField>::new_witness(ark_relations::ns!(cs, "c"), || {
Ok(c_native)
})
.unwrap(); .unwrap();
let a_plus_b = &a + &b; let a_plus_b = &a + &b;
@ -308,7 +304,7 @@ fn distribution_law_test
); );
} }
fn randomized_arithmetic_test<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>(
fn randomized_arithmetic_test<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>(
cs: ConstraintSystemRef<BaseField>, cs: ConstraintSystemRef<BaseField>,
rng: &mut R, rng: &mut R,
) { ) {
@ -317,15 +313,15 @@ fn randomized_arithmetic_test
operations.push(rng.next_u32() % 3); operations.push(rng.next_u32() % 3);
} }
let mut num_native = TargetField::rand(rng);
let mut num = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
let mut num_native = TargetF::rand(rng);
let mut num = EmulatedFpVar::<TargetF, BaseField>::new_witness(
ark_relations::ns!(cs, "initial num"), ark_relations::ns!(cs, "initial num"),
|| Ok(num_native), || Ok(num_native),
) )
.unwrap(); .unwrap();
for op in operations.iter() { for op in operations.iter() {
let next_native = TargetField::rand(rng);
let next = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
let next_native = TargetF::rand(rng);
let next = EmulatedFpVar::<TargetF, BaseField>::new_witness(
ark_relations::ns!(cs, "next num for repetition"), ark_relations::ns!(cs, "next num for repetition"),
|| Ok(next_native), || Ok(next_native),
) )
@ -353,17 +349,17 @@ fn randomized_arithmetic_test
} }
} }
fn addition_stress_test<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>(
fn addition_stress_test<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>(
cs: ConstraintSystemRef<BaseField>, cs: ConstraintSystemRef<BaseField>,
rng: &mut R, rng: &mut R,
) { ) {
let mut num_native = TargetField::rand(rng);
let mut num_native = TargetF::rand(rng);
let mut num = let mut num =
NonNativeFieldVar::new_witness(ark_relations::ns!(cs, "initial num"), || Ok(num_native))
EmulatedFpVar::new_witness(ark_relations::ns!(cs, "initial num"), || Ok(num_native))
.unwrap(); .unwrap();
for _ in 0..TEST_COUNT { for _ in 0..TEST_COUNT {
let next_native = TargetField::rand(rng);
let next = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
let next_native = TargetF::rand(rng);
let next = EmulatedFpVar::<TargetF, BaseField>::new_witness(
ark_relations::ns!(cs, "next num for repetition"), ark_relations::ns!(cs, "next num for repetition"),
|| Ok(next_native), || Ok(next_native),
) )
@ -375,19 +371,19 @@ fn addition_stress_test
} }
} }
fn multiplication_stress_test<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>(
fn multiplication_stress_test<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>(
cs: ConstraintSystemRef<BaseField>, cs: ConstraintSystemRef<BaseField>,
rng: &mut R, rng: &mut R,
) { ) {
let mut num_native = TargetField::rand(rng);
let mut num = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
let mut num_native = TargetF::rand(rng);
let mut num = EmulatedFpVar::<TargetF, BaseField>::new_witness(
ark_relations::ns!(cs, "initial num"), ark_relations::ns!(cs, "initial num"),
|| Ok(num_native), || Ok(num_native),
) )
.unwrap(); .unwrap();
for _ in 0..TEST_COUNT { for _ in 0..TEST_COUNT {
let next_native = TargetField::rand(rng);
let next = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
let next_native = TargetF::rand(rng);
let next = EmulatedFpVar::<TargetF, BaseField>::new_witness(
ark_relations::ns!(cs, "next num for repetition"), ark_relations::ns!(cs, "next num for repetition"),
|| Ok(next_native), || Ok(next_native),
) )
@ -399,25 +395,25 @@ fn multiplication_stress_test
} }
} }
fn mul_and_add_stress_test<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>(
fn mul_and_add_stress_test<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>(
cs: ConstraintSystemRef<BaseField>, cs: ConstraintSystemRef<BaseField>,
rng: &mut R, rng: &mut R,
) { ) {
let mut num_native = TargetField::rand(rng);
let mut num = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
let mut num_native = TargetF::rand(rng);
let mut num = EmulatedFpVar::<TargetF, BaseField>::new_witness(
ark_relations::ns!(cs, "initial num"), ark_relations::ns!(cs, "initial num"),
|| Ok(num_native), || Ok(num_native),
) )
.unwrap(); .unwrap();
for _ in 0..TEST_COUNT { for _ in 0..TEST_COUNT {
let next_add_native = TargetField::rand(rng);
let next_add = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
let next_add_native = TargetF::rand(rng);
let next_add = EmulatedFpVar::<TargetF, BaseField>::new_witness(
ark_relations::ns!(cs, "next to add num for repetition"), ark_relations::ns!(cs, "next to add num for repetition"),
|| Ok(next_add_native), || Ok(next_add_native),
) )
.unwrap(); .unwrap();
let next_mul_native = TargetField::rand(rng);
let next_mul = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
let next_mul_native = TargetF::rand(rng);
let next_mul = EmulatedFpVar::<TargetF, BaseField>::new_witness(
ark_relations::ns!(cs, "next to mul num for repetition"), ark_relations::ns!(cs, "next to mul num for repetition"),
|| Ok(next_mul_native), || Ok(next_mul_native),
) )
@ -430,25 +426,25 @@ fn mul_and_add_stress_test
} }
} }
fn square_mul_add_stress_test<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>(
fn square_mul_add_stress_test<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>(
cs: ConstraintSystemRef<BaseField>, cs: ConstraintSystemRef<BaseField>,
rng: &mut R, rng: &mut R,
) { ) {
let mut num_native = TargetField::rand(rng);
let mut num = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
let mut num_native = TargetF::rand(rng);
let mut num = EmulatedFpVar::<TargetF, BaseField>::new_witness(
ark_relations::ns!(cs, "initial num"), ark_relations::ns!(cs, "initial num"),
|| Ok(num_native), || Ok(num_native),
) )
.unwrap(); .unwrap();
for _ in 0..TEST_COUNT { for _ in 0..TEST_COUNT {
let next_add_native = TargetField::rand(rng);
let next_add = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
let next_add_native = TargetF::rand(rng);
let next_add = EmulatedFpVar::<TargetF, BaseField>::new_witness(
ark_relations::ns!(cs, "next to add num for repetition"), ark_relations::ns!(cs, "next to add num for repetition"),
|| Ok(next_add_native), || Ok(next_add_native),
) )
.unwrap(); .unwrap();
let next_mul_native = TargetField::rand(rng);
let next_mul = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
let next_mul_native = TargetF::rand(rng);
let next_mul = EmulatedFpVar::<TargetF, BaseField>::new_witness(
ark_relations::ns!(cs, "next to mul num for repetition"), ark_relations::ns!(cs, "next to mul num for repetition"),
|| Ok(next_mul_native), || Ok(next_mul_native),
) )
@ -461,12 +457,12 @@ fn square_mul_add_stress_test
} }
} }
fn double_stress_test_1<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>(
fn double_stress_test_1<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>(
cs: ConstraintSystemRef<BaseField>, cs: ConstraintSystemRef<BaseField>,
rng: &mut R, rng: &mut R,
) { ) {
let mut num_native = TargetField::rand(rng);
let mut num = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
let mut num_native = TargetF::rand(rng);
let mut num = EmulatedFpVar::<TargetF, BaseField>::new_witness(
ark_relations::ns!(cs, "initial num"), ark_relations::ns!(cs, "initial num"),
|| Ok(num_native), || Ok(num_native),
) )
@ -482,12 +478,12 @@ fn double_stress_test_1
} }
} }
fn double_stress_test_2<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>(
fn double_stress_test_2<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>(
cs: ConstraintSystemRef<BaseField>, cs: ConstraintSystemRef<BaseField>,
rng: &mut R, rng: &mut R,
) { ) {
let mut num_native = TargetField::rand(rng);
let mut num = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
let mut num_native = TargetF::rand(rng);
let mut num = EmulatedFpVar::<TargetF, BaseField>::new_witness(
ark_relations::ns!(cs, "initial num"), ark_relations::ns!(cs, "initial num"),
|| Ok(num_native), || Ok(num_native),
) )
@ -506,12 +502,12 @@ fn double_stress_test_2
} }
} }
fn double_stress_test_3<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>(
fn double_stress_test_3<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>(
cs: ConstraintSystemRef<BaseField>, cs: ConstraintSystemRef<BaseField>,
rng: &mut R, rng: &mut R,
) { ) {
let mut num_native = TargetField::rand(rng);
let mut num = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
let mut num_native = TargetF::rand(rng);
let mut num = EmulatedFpVar::<TargetF, BaseField>::new_witness(
ark_relations::ns!(cs, "initial num"), ark_relations::ns!(cs, "initial num"),
|| Ok(num_native), || Ok(num_native),
) )
@ -526,7 +522,7 @@ fn double_stress_test_3
// square // square
let num_square_native = num_native * &num_native; let num_square_native = num_native * &num_native;
let num_square = &num * &num; let num_square = &num * &num;
let num_square_native_gadget = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
let num_square_native_gadget = EmulatedFpVar::<TargetF, BaseField>::new_witness(
ark_relations::ns!(cs, "repetition: alloc_native num"), ark_relations::ns!(cs, "repetition: alloc_native num"),
|| Ok(num_square_native), || Ok(num_square_native),
) )
@ -536,19 +532,19 @@ fn double_stress_test_3
} }
} }
fn inverse_stress_test<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>(
fn inverse_stress_test<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>(
cs: ConstraintSystemRef<BaseField>, cs: ConstraintSystemRef<BaseField>,
rng: &mut R, rng: &mut R,
) { ) {
for _ in 0..TEST_COUNT { for _ in 0..TEST_COUNT {
let num_native = TargetField::rand(rng);
let num = NonNativeFieldVar::<TargetField, BaseField>::new_witness(
ark_relations::ns!(cs, "num"),
|| Ok(num_native),
)
.unwrap();
let num_native = TargetF::rand(rng);
let num =
EmulatedFpVar::<TargetF, BaseField>::new_witness(ark_relations::ns!(cs, "num"), || {
Ok(num_native)
})
.unwrap();
if num_native == TargetField::zero() {
if num_native == TargetF::zero() {
continue; continue;
} }

+ 3
- 3
tests/from_test.rs

@ -1,6 +1,6 @@
use ark_r1cs_std::{ use ark_r1cs_std::{
alloc::AllocVar, alloc::AllocVar,
fields::nonnative::{NonNativeFieldMulResultVar, NonNativeFieldVar},
fields::emulated_fp::{EmulatedFpVar, MulResultVar},
R1CSVar, R1CSVar,
}; };
use ark_relations::r1cs::ConstraintSystem; use ark_relations::r1cs::ConstraintSystem;
@ -15,8 +15,8 @@ fn from_test() {
let cs = ConstraintSystem::<CF>::new_ref(); let cs = ConstraintSystem::<CF>::new_ref();
let f = F::rand(&mut rng); let f = F::rand(&mut rng);
let f_var = NonNativeFieldVar::<F, CF>::new_input(cs.clone(), || Ok(f)).unwrap();
let f_var_converted = NonNativeFieldMulResultVar::<F, CF>::from(&f_var);
let f_var = EmulatedFpVar::<F, CF>::new_input(cs.clone(), || Ok(f)).unwrap();
let f_var_converted = MulResultVar::<F, CF>::from(&f_var);
let f_var_converted_reduced = f_var_converted.reduce().unwrap(); let f_var_converted_reduced = f_var_converted.reduce().unwrap();
let f_var_value = f_var.value().unwrap(); let f_var_value = f_var.value().unwrap();

+ 3
- 3
tests/to_constraint_field_test.rs

@ -1,5 +1,5 @@
use ark_r1cs_std::{ use ark_r1cs_std::{
alloc::AllocVar, fields::nonnative::NonNativeFieldVar, R1CSVar, ToConstraintFieldGadget,
alloc::AllocVar, fields::emulated_fp::EmulatedFpVar, R1CSVar, ToConstraintFieldGadget,
}; };
use ark_relations::r1cs::ConstraintSystem; use ark_relations::r1cs::ConstraintSystem;
@ -10,8 +10,8 @@ fn to_constraint_field_test() {
let cs = ConstraintSystem::<CF>::new_ref(); let cs = ConstraintSystem::<CF>::new_ref();
let a = NonNativeFieldVar::Constant(F::from(12u8));
let b = NonNativeFieldVar::new_input(cs.clone(), || Ok(F::from(6u8))).unwrap();
let a = EmulatedFpVar::Constant(F::from(12u8));
let b = EmulatedFpVar::new_input(cs.clone(), || Ok(F::from(6u8))).unwrap();
let b2 = &b + &b; let b2 = &b + &b;

Loading…
Cancel
Save