Rename `NonNativeFieldVar` -> `EmulatedFpVar` (#135)
jump to
@@ -1,6 +1,23 @@
# CHANGELOG ## 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. - [\#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`).
@@ -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"] [[bench]] -name = "nonnative-bench" +name = "emulated-bench" path = "benches/bench.rs" harness = false
@@ -2,7 +2,7 @@ use ark_ff::PrimeField;
use ark_r1cs_std::{ alloc::AllocVar, eq::EqGadget, - fields::{nonnative::NonNativeFieldVar, FieldVar}, + fields::{emulated_fp::EmulatedFpVar, FieldVar}, }; use ark_relations::{ ns,@@ -26,20 +26,18 @@ },
} } -fn allocation<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>( +fn allocation<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>( cs: ConstraintSystemRef<BaseField>, rng: &mut R, ) -> (usize, usize) { - let a_native = TargetField::rand(rng); + let a_native = TargetF::rand(rng); let constraints_before = cs.num_constraints(); let nonzeros_before = get_density(&cs); // 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 nonzeros_after = get_density(&cs);@@ -50,21 +48,17 @@ nonzeros_after - nonzeros_before,
); } -fn addition<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>( +fn addition<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>( cs: ConstraintSystemRef<BaseField>, rng: &mut R, ) -> (usize, usize) { - let a_native = TargetField::rand(rng); - let a = NonNativeFieldVar::<TargetField, BaseField>::new_witness(ns!(cs, "alloc a"), || { - Ok(a_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 = TargetField::rand(rng); - let b = NonNativeFieldVar::<TargetField, BaseField>::new_witness(ns!(cs, "alloc b"), || { - Ok(b_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 nonzeros_before = get_density(&cs);@@ -80,19 +74,15 @@ nonzeros_after - nonzeros_before,
); } -fn equality<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>( +fn equality<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>( cs: ConstraintSystemRef<BaseField>, rng: &mut R, ) -> (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 nonzeros_before = get_density(&cs);@@ -108,21 +98,17 @@ nonzeros_after - nonzeros_before,
); } -fn multiplication<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>( +fn multiplication<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>( cs: ConstraintSystemRef<BaseField>, rng: &mut R, ) -> (usize, usize) { - let a_native = TargetField::rand(rng); - let a = NonNativeFieldVar::<TargetField, BaseField>::new_witness(ns!(cs, "initial a"), || { - Ok(a_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 = TargetField::rand(rng); - let b = NonNativeFieldVar::<TargetField, BaseField>::new_witness(ns!(cs, "initial b"), || { - Ok(b_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 nonzeros_before = get_density(&cs);@@ -138,15 +124,13 @@ nonzeros_after - nonzeros_before,
); } -fn inverse<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>( +fn inverse<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>( cs: ConstraintSystemRef<BaseField>, rng: &mut R, ) -> (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 nonzeros_before = get_density(&cs);
@@ -0,0 +1,472 @@
+use super::{params::OptimizationType, AllocatedEmulatedFpVar, MulResultVar}; +use crate::{ + boolean::Boolean, + fields::{fp::FpVar, FieldVar}, + prelude::*, + R1CSVar, ToConstraintFieldGadget, +}; +use ark_ff::{BigInteger, PrimeField}; +use ark_relations::r1cs::{ConstraintSystemRef, Namespace, Result as R1CSResult, SynthesisError}; +use ark_std::{ + borrow::Borrow, + hash::{Hash, Hasher}, + vec::Vec, +}; + +/// A gadget for representing non-native (`TargetF`) field elements over the +/// constraint field (`BaseF`). +#[derive(Clone, Debug)] +#[must_use] +pub enum EmulatedFpVar<TargetF: PrimeField, BaseF: PrimeField> { + /// Constant + Constant(TargetF), + /// Allocated gadget + Var(AllocatedEmulatedFpVar<TargetF, BaseF>), +} + +impl<TargetF: PrimeField, BaseF: PrimeField> PartialEq for EmulatedFpVar<TargetF, BaseF> { + fn eq(&self, other: &Self) -> bool { + self.value() + .unwrap_or_default() + .eq(&other.value().unwrap_or_default()) + } +} + +impl<TargetF: PrimeField, BaseF: PrimeField> Eq for EmulatedFpVar<TargetF, BaseF> {} + +impl<TargetF: PrimeField, BaseF: PrimeField> Hash for EmulatedFpVar<TargetF, BaseF> { + fn hash<H: Hasher>(&self, state: &mut H) { + self.value().unwrap_or_default().hash(state); + } +} + +impl<TargetF: PrimeField, BaseF: PrimeField> R1CSVar<BaseF> for EmulatedFpVar<TargetF, BaseF> { + type Value = TargetF; + + fn cs(&self) -> ConstraintSystemRef<BaseF> { + match self { + Self::Constant(_) => ConstraintSystemRef::None, + Self::Var(a) => a.cs(), + } + } + + fn value(&self) -> R1CSResult<Self::Value> { + match self { + Self::Constant(v) => Ok(*v), + Self::Var(v) => v.value(), + } + } +} + +impl<TargetF: PrimeField, BaseF: PrimeField> From<Boolean<BaseF>> + for EmulatedFpVar<TargetF, BaseF> +{ + fn from(other: Boolean<BaseF>) -> Self { + if let Boolean::Constant(b) = other { + Self::Constant(<TargetF as From<u128>>::from(b as u128)) + } else { + // `other` is a variable + let one = Self::Constant(TargetF::one()); + let zero = Self::Constant(TargetF::zero()); + Self::conditionally_select(&other, &one, &zero).unwrap() + } + } +} + +impl<TargetF: PrimeField, BaseF: PrimeField> From<AllocatedEmulatedFpVar<TargetF, BaseF>> + for EmulatedFpVar<TargetF, BaseF> +{ + fn from(other: AllocatedEmulatedFpVar<TargetF, BaseF>) -> Self { + Self::Var(other) + } +} + +impl<'a, TargetF: PrimeField, BaseF: PrimeField> FieldOpsBounds<'a, TargetF, Self> + for EmulatedFpVar<TargetF, BaseF> +{ +} + +impl<'a, TargetF: PrimeField, BaseF: PrimeField> + FieldOpsBounds<'a, TargetF, EmulatedFpVar<TargetF, BaseF>> + for &'a EmulatedFpVar<TargetF, BaseF> +{ +} + +impl<TargetF: PrimeField, BaseF: PrimeField> FieldVar<TargetF, BaseF> + for EmulatedFpVar<TargetF, BaseF> +{ + fn zero() -> Self { + Self::Constant(TargetF::zero()) + } + + fn one() -> Self { + Self::Constant(TargetF::one()) + } + + fn constant(v: TargetF) -> Self { + Self::Constant(v) + } + + #[tracing::instrument(target = "r1cs")] + fn negate(&self) -> R1CSResult<Self> { + match self { + Self::Constant(c) => Ok(Self::Constant(-*c)), + Self::Var(v) => Ok(Self::Var(v.negate()?)), + } + } + + #[tracing::instrument(target = "r1cs")] + fn inverse(&self) -> R1CSResult<Self> { + match self { + Self::Constant(c) => Ok(Self::Constant(c.inverse().unwrap_or_default())), + Self::Var(v) => Ok(Self::Var(v.inverse()?)), + } + } + + #[tracing::instrument(target = "r1cs")] + fn frobenius_map(&self, power: usize) -> R1CSResult<Self> { + match self { + Self::Constant(c) => Ok(Self::Constant({ + let mut tmp = *c; + tmp.frobenius_map_in_place(power); + tmp + })), + Self::Var(v) => Ok(Self::Var(v.frobenius_map(power)?)), + } + } +} + +impl_bounded_ops!( + EmulatedFpVar<TargetF, BaseF>, + TargetF, + Add, + add, + AddAssign, + add_assign, + |this: &'a EmulatedFpVar<TargetF, BaseF>, other: &'a EmulatedFpVar<TargetF, BaseF>| { + use EmulatedFpVar::*; + 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 EmulatedFpVar<TargetF, BaseF>, other: TargetF| { this + &EmulatedFpVar::Constant(other) }, + (TargetF: PrimeField, BaseF: PrimeField), +); + +impl_bounded_ops!( + EmulatedFpVar<TargetF, BaseF>, + TargetF, + Sub, + sub, + SubAssign, + sub_assign, + |this: &'a EmulatedFpVar<TargetF, BaseF>, other: &'a EmulatedFpVar<TargetF, BaseF>| { + use EmulatedFpVar::*; + match (this, other) { + (Constant(c1), Constant(c2)) => Constant(*c1 - c2), + (Var(v), Constant(c)) => Var(v.sub_constant(c).unwrap()), + (Constant(c), Var(v)) => Var(v.sub_constant(c).unwrap().negate().unwrap()), + (Var(v1), Var(v2)) => Var(v1.sub(v2).unwrap()), + } + }, + |this: &'a EmulatedFpVar<TargetF, BaseF>, other: TargetF| { + this - &EmulatedFpVar::Constant(other) + }, + (TargetF: PrimeField, BaseF: PrimeField), +); + +impl_bounded_ops!( + EmulatedFpVar<TargetF, BaseF>, + TargetF, + Mul, + mul, + MulAssign, + mul_assign, + |this: &'a EmulatedFpVar<TargetF, BaseF>, other: &'a EmulatedFpVar<TargetF, BaseF>| { + use EmulatedFpVar::*; + match (this, other) { + (Constant(c1), Constant(c2)) => Constant(*c1 * c2), + (Constant(c), Var(v)) | (Var(v), Constant(c)) => Var(v.mul_constant(c).unwrap()), + (Var(v1), Var(v2)) => Var(v1.mul(v2).unwrap()), + } + }, + |this: &'a EmulatedFpVar<TargetF, BaseF>, other: TargetF| { + if other.is_zero() { + EmulatedFpVar::zero() + } else { + this * &EmulatedFpVar::Constant(other) + } + }, + (TargetF: PrimeField, BaseF: PrimeField), +); + +/// ************************************************************************* +/// ************************************************************************* + +impl<TargetF: PrimeField, BaseF: PrimeField> EqGadget<BaseF> for EmulatedFpVar<TargetF, BaseF> { + #[tracing::instrument(target = "r1cs")] + fn is_eq(&self, other: &Self) -> R1CSResult<Boolean<BaseF>> { + let cs = self.cs().or(other.cs()); + + if cs == ConstraintSystemRef::None { + Ok(Boolean::Constant(self.value()? == other.value()?)) + } else { + let should_enforce_equal = + Boolean::new_witness(cs, || Ok(self.value()? == other.value()?))?; + + self.conditional_enforce_equal(other, &should_enforce_equal)?; + self.conditional_enforce_not_equal(other, &should_enforce_equal.not())?; + + Ok(should_enforce_equal) + } + } + + #[tracing::instrument(target = "r1cs")] + fn conditional_enforce_equal( + &self, + other: &Self, + should_enforce: &Boolean<BaseF>, + ) -> R1CSResult<()> { + match (self, other) { + (Self::Constant(c1), Self::Constant(c2)) => { + if c1 != c2 { + should_enforce.enforce_equal(&Boolean::FALSE)?; + } + Ok(()) + }, + (Self::Constant(c), Self::Var(v)) | (Self::Var(v), Self::Constant(c)) => { + let cs = v.cs(); + let c = AllocatedEmulatedFpVar::new_constant(cs, c)?; + c.conditional_enforce_equal(v, should_enforce) + }, + (Self::Var(v1), Self::Var(v2)) => v1.conditional_enforce_equal(v2, should_enforce), + } + } + + #[tracing::instrument(target = "r1cs")] + fn conditional_enforce_not_equal( + &self, + other: &Self, + should_enforce: &Boolean<BaseF>, + ) -> R1CSResult<()> { + match (self, other) { + (Self::Constant(c1), Self::Constant(c2)) => { + if c1 == c2 { + should_enforce.enforce_equal(&Boolean::FALSE)?; + } + Ok(()) + }, + (Self::Constant(c), Self::Var(v)) | (Self::Var(v), Self::Constant(c)) => { + let cs = v.cs(); + let c = AllocatedEmulatedFpVar::new_constant(cs, c)?; + c.conditional_enforce_not_equal(v, should_enforce) + }, + (Self::Var(v1), Self::Var(v2)) => v1.conditional_enforce_not_equal(v2, should_enforce), + } + } +} + +impl<TargetF: PrimeField, BaseF: PrimeField> ToBitsGadget<BaseF> for EmulatedFpVar<TargetF, BaseF> { + #[tracing::instrument(target = "r1cs")] + fn to_bits_le(&self) -> R1CSResult<Vec<Boolean<BaseF>>> { + match self { + Self::Constant(_) => self.to_non_unique_bits_le(), + Self::Var(v) => v.to_bits_le(), + } + } + + #[tracing::instrument(target = "r1cs")] + fn to_non_unique_bits_le(&self) -> R1CSResult<Vec<Boolean<BaseF>>> { + use ark_ff::BitIteratorLE; + match self { + Self::Constant(c) => Ok(BitIteratorLE::new(&c.into_bigint()) + .take((TargetF::MODULUS_BIT_SIZE) as usize) + .map(Boolean::constant) + .collect::<Vec<_>>()), + Self::Var(v) => v.to_non_unique_bits_le(), + } + } +} + +impl<TargetF: PrimeField, BaseF: PrimeField> ToBytesGadget<BaseF> + for EmulatedFpVar<TargetF, BaseF> +{ + /// Outputs the unique byte decomposition of `self` in *little-endian* + /// form. + #[tracing::instrument(target = "r1cs")] + fn to_bytes(&self) -> R1CSResult<Vec<UInt8<BaseF>>> { + match self { + Self::Constant(c) => Ok(UInt8::constant_vec( + c.into_bigint().to_bytes_le().as_slice(), + )), + + Self::Var(v) => v.to_bytes(), + } + } + + #[tracing::instrument(target = "r1cs")] + fn to_non_unique_bytes(&self) -> R1CSResult<Vec<UInt8<BaseF>>> { + match self { + Self::Constant(c) => Ok(UInt8::constant_vec( + c.into_bigint().to_bytes_le().as_slice(), + )), + Self::Var(v) => v.to_non_unique_bytes(), + } + } +} + +impl<TargetF: PrimeField, BaseF: PrimeField> CondSelectGadget<BaseF> + for EmulatedFpVar<TargetF, BaseF> +{ + #[tracing::instrument(target = "r1cs")] + fn conditionally_select( + cond: &Boolean<BaseF>, + true_value: &Self, + false_value: &Self, + ) -> R1CSResult<Self> { + match cond { + Boolean::Constant(true) => Ok(true_value.clone()), + Boolean::Constant(false) => Ok(false_value.clone()), + _ => { + let cs = cond.cs(); + let true_value = match true_value { + Self::Constant(f) => AllocatedEmulatedFpVar::new_constant(cs.clone(), f)?, + Self::Var(v) => v.clone(), + }; + let false_value = match false_value { + Self::Constant(f) => AllocatedEmulatedFpVar::new_constant(cs, f)?, + Self::Var(v) => v.clone(), + }; + cond.select(&true_value, &false_value).map(Self::Var) + }, + } + } +} + +/// Uses two bits to perform a lookup into a table +/// `b` is little-endian: `b[0]` is LSB. +impl<TargetF: PrimeField, BaseF: PrimeField> TwoBitLookupGadget<BaseF> + for EmulatedFpVar<TargetF, BaseF> +{ + type TableConstant = TargetF; + + #[tracing::instrument(target = "r1cs")] + fn two_bit_lookup(b: &[Boolean<BaseF>], c: &[Self::TableConstant]) -> R1CSResult<Self> { + debug_assert_eq!(b.len(), 2); + debug_assert_eq!(c.len(), 4); + if b.cs().is_none() { + // We're in the constant case + + let lsb = b[0].value()? as usize; + let msb = b[1].value()? as usize; + let index = lsb + (msb << 1); + Ok(Self::Constant(c[index])) + } else { + AllocatedEmulatedFpVar::two_bit_lookup(b, c).map(Self::Var) + } + } +} + +impl<TargetF: PrimeField, BaseF: PrimeField> ThreeBitCondNegLookupGadget<BaseF> + for EmulatedFpVar<TargetF, BaseF> +{ + type TableConstant = TargetF; + + #[tracing::instrument(target = "r1cs")] + fn three_bit_cond_neg_lookup( + b: &[Boolean<BaseF>], + b0b1: &Boolean<BaseF>, + c: &[Self::TableConstant], + ) -> R1CSResult<Self> { + debug_assert_eq!(b.len(), 3); + debug_assert_eq!(c.len(), 4); + + if b.cs().or(b0b1.cs()).is_none() { + // We're in the constant case + + let lsb = b[0].value()? as usize; + let msb = b[1].value()? as usize; + let index = lsb + (msb << 1); + let intermediate = c[index]; + + let is_negative = b[2].value()?; + let y = if is_negative { + -intermediate + } else { + intermediate + }; + Ok(Self::Constant(y)) + } else { + AllocatedEmulatedFpVar::three_bit_cond_neg_lookup(b, b0b1, c).map(Self::Var) + } + } +} + +impl<TargetF: PrimeField, BaseF: PrimeField> AllocVar<TargetF, BaseF> + for EmulatedFpVar<TargetF, BaseF> +{ + fn new_variable<T: Borrow<TargetF>>( + cs: impl Into<Namespace<BaseF>>, + f: impl FnOnce() -> Result<T, SynthesisError>, + mode: AllocationMode, + ) -> R1CSResult<Self> { + let ns = cs.into(); + let cs = ns.cs(); + + if cs == ConstraintSystemRef::None || mode == AllocationMode::Constant { + Ok(Self::Constant(*f()?.borrow())) + } else { + AllocatedEmulatedFpVar::new_variable(cs, f, mode).map(Self::Var) + } + } +} + +impl<TargetF: PrimeField, BaseF: PrimeField> ToConstraintFieldGadget<BaseF> + for EmulatedFpVar<TargetF, BaseF> +{ + #[tracing::instrument(target = "r1cs")] + fn to_constraint_field(&self) -> R1CSResult<Vec<FpVar<BaseF>>> { + // Use one group element to represent the optimization type. + // + // By default, the constant is converted in the weight-optimized type, because + // it results in fewer elements. + match self { + Self::Constant(c) => Ok(AllocatedEmulatedFpVar::get_limbs_representations( + c, + OptimizationType::Weight, + )? + .into_iter() + .map(FpVar::constant) + .collect()), + Self::Var(v) => v.to_constraint_field(), + } + } +} + +impl<TargetF: PrimeField, BaseF: PrimeField> EmulatedFpVar<TargetF, BaseF> { + /// The `mul_without_reduce` for `EmulatedFpVar` + #[tracing::instrument(target = "r1cs")] + pub fn mul_without_reduce(&self, other: &Self) -> R1CSResult<MulResultVar<TargetF, BaseF>> { + match self { + Self::Constant(c) => match other { + Self::Constant(other_c) => Ok(MulResultVar::Constant(*c * other_c)), + Self::Var(other_v) => { + let self_v = + AllocatedEmulatedFpVar::<TargetF, BaseF>::new_constant(self.cs(), c)?; + Ok(MulResultVar::Var(other_v.mul_without_reduce(&self_v)?)) + }, + }, + Self::Var(v) => { + let other_v = match other { + Self::Constant(other_c) => { + AllocatedEmulatedFpVar::<TargetF, BaseF>::new_constant(self.cs(), other_c)? + }, + Self::Var(other_v) => other_v.clone(), + }; + Ok(MulResultVar::Var(v.mul_without_reduce(&other_v)?)) + }, + } + } +}
@@ -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), +);
@@ -20,9 +20,9 @@ /// This module contains a generic implementation of prime field variables.
/// That is, it implements the R1CS equivalent of `ark_ff::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`. -pub mod nonnative; +pub mod emulated_fp; /// This module contains a generic implementation of the degree-12 tower /// extension field. That is, it implements the R1CS equivalent of
@@ -1,7 +1,7 @@
use super::{ params::{get_params, OptimizationType}, reduce::{bigint_to_basefield, limbs_to_bigint, Reducer}, - AllocatedNonNativeFieldMulResultVar, + AllocatedMulResultVar, }; use crate::{fields::fp::FpVar, prelude::*, ToConstraintFieldGadget}; use ark_ff::{BigInteger, PrimeField};@@ -19,58 +19,53 @@ vec,
vec::Vec, }; -/// The allocated version of `NonNativeFieldVar` (introduced below) +/// The allocated version of `EmulatedFpVar` (introduced below) #[derive(Debug)] #[must_use] -pub struct AllocatedNonNativeFieldVar<TargetField: PrimeField, BaseField: PrimeField> { +pub struct AllocatedEmulatedFpVar<TargetF: PrimeField, BaseF: PrimeField> { /// 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 /// 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 /// 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, #[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 - pub fn cs(&self) -> ConstraintSystemRef<BaseField> { + pub fn cs(&self) -> ConstraintSystemRef<BaseF> { self.cs.clone() } /// 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( - 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, ); - // 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 // 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(); - 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() { - 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() { if *bit {@@ -86,8 +81,8 @@
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(); for limb in self.limbs.iter() { limbs.push(limb.value()?);@@ -96,8 +91,8 @@
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() { OptimizationGoal::None => OptimizationType::Constraints, OptimizationGoal::Constraints => OptimizationType::Constraints,@@ -109,32 +104,29 @@
let mut limbs = Vec::new(); 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 { cs, limbs, - num_of_additions_over_normal_form: BaseField::zero(), + num_of_additions_over_normal_form: BaseF::zero(), is_in_the_normal_form: true, 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")] pub fn add(&self, other: &Self) -> R1CSResult<Self> { assert_eq!(self.get_optimization_type(), other.get_optimization_type());@@ -150,18 +142,18 @@ limbs,
num_of_additions_over_normal_form: self .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, target_phantom: PhantomData, }; - Reducer::<TargetField, BaseField>::post_add_reduce(&mut res)?; + Reducer::<TargetF, BaseF>::post_add_reduce(&mut res)?; Ok(res) } /// Add a constant #[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 mut limbs = Vec::new();@@ -174,50 +166,50 @@ cs: self.cs(),
limbs, num_of_additions_over_normal_form: self .num_of_additions_over_normal_form - .add(&BaseField::one()), + .add(&BaseF::one()), is_in_the_normal_form: false, target_phantom: PhantomData, }; - Reducer::<TargetField, BaseField>::post_add_reduce(&mut res)?; + Reducer::<TargetF, BaseF>::post_add_reduce(&mut 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")] pub fn sub_without_reduce(&self, other: &Self) -> R1CSResult<Self> { assert_eq!(self.get_optimization_type(), other.get_optimization_type()); 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(), ); // 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(); - 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 - + (TargetField::MODULUS_BIT_SIZE as usize + + (TargetF::MODULUS_BIT_SIZE as usize - 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)?; - 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 - 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; 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; - 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()); pad_limbs.push(pad_top_limb);@@ -246,12 +238,12 @@ limbs.push(this_limb + pad_top_limb + *pad_to_kp_limb - other_limb);
} } - let result = AllocatedNonNativeFieldVar::<TargetField, BaseField> { + let result = AllocatedEmulatedFpVar::<TargetF, BaseF> { cs: self.cs(), limbs, 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, target_phantom: PhantomData, };@@ -259,23 +251,23 @@
Ok(result) } - /// Subtract a nonnative field element + /// Subtract a emulated field element #[tracing::instrument(target = "r1cs")] pub fn sub(&self, other: &Self) -> R1CSResult<Self> { assert_eq!(self.get_optimization_type(), other.get_optimization_type()); 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) } /// Subtract a constant #[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)?) } - /// Multiply a nonnative field element + /// Multiply a emulated field element #[tracing::instrument(target = "r1cs")] pub fn mul(&self, other: &Self) -> R1CSResult<Self> { assert_eq!(self.get_optimization_type(), other.get_optimization_type());@@ -284,21 +276,21 @@ self.mul_without_reduce(&other)?.reduce()
} /// 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)?) } - /// Compute the negate of a nonnative field element + /// Compute the negate of a emulated field element #[tracing::instrument(target = "r1cs")] pub fn negate(&self) -> R1CSResult<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")] pub fn inverse(&self) -> R1CSResult<Self> { 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)?;@@ -306,36 +298,36 @@ actual_result.conditional_enforce_equal(&Self::one(self.cs())?, &Boolean::TRUE)?;
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 /// functions pub fn get_limbs_representations( - elem: &TargetField, + elem: &TargetF, optimization_type: OptimizationType, - ) -> R1CSResult<Vec<BaseField>> { + ) -> R1CSResult<Vec<BaseF>> { Self::get_limbs_representations_from_big_integer(&elem.into_bigint(), optimization_type) } /// Obtain the limbs directly from a big int pub fn get_limbs_representations_from_big_integer( - elem: &<TargetField as PrimeField>::BigInt, + elem: &<TargetF as PrimeField>::BigInt, optimization_type: OptimizationType, - ) -> R1CSResult<Vec<BaseField>> { + ) -> R1CSResult<Vec<BaseF>> { 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, ); // push the lower limbs first - let mut limbs: Vec<BaseField> = Vec::new(); + let mut limbs: Vec<BaseF> = Vec::new(); let mut cur = *elem; for _ in 0..params.num_limbs { 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..], ); // 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; }@@ -348,28 +340,28 @@
/// for advanced use, multiply and output the intermediate representations /// (without reduction) This intermediate representations can be added /// with each other, and they can later be reduced back to the - /// `NonNativeFieldVar`. + /// `EmulatedFpVar`. #[tracing::instrument(target = "r1cs")] pub fn mul_without_reduce( &self, other: &Self, - ) -> R1CSResult<AllocatedNonNativeFieldMulResultVar<TargetField, BaseField>> { + ) -> R1CSResult<AllocatedMulResultVar<TargetF, BaseF>> { assert_eq!(self.get_optimization_type(), other.get_optimization_type()); 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(), ); // Step 1: reduce `self` and `other` if neceessary let mut self_reduced = self.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(); 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 { prod_limbs.push(zero.clone());@@ -386,7 +378,7 @@ let cs = self.cs().or(other.cs());
for z_index in 0..2 * params.num_limbs - 1 { 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) { let j = z_index - i; if j < params.num_limbs {@@ -402,7 +394,7 @@ }
for c in 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(); let x = self_reduced@@ -429,12 +421,12 @@ z.enforce_equal(&(x * y))?;
} } - Ok(AllocatedNonNativeFieldMulResultVar { + Ok(AllocatedMulResultVar { cs: self.cs(), limbs: prod_limbs, 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, }) }@@ -446,32 +438,32 @@
pub(crate) fn conditional_enforce_equal( &self, other: &Self, - should_enforce: &Boolean<BaseField>, + should_enforce: &Boolean<BaseF>, ) -> R1CSResult<()> { assert_eq!(self.get_optimization_type(), other.get_optimization_type()); 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(), ); // Get p 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 mut p_gadget_limbs = Vec::new(); 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(), 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, target_phantom: PhantomData, };@@ -482,19 +474,19 @@ let mut delta = self.sub_without_reduce(other)?;
delta = should_enforce.select(&delta, &Self::zero(cs.clone())?)?; // 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() { delta_limbs_values.push(limb.value()?); } 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 let mut kp_gadget_limbs = Vec::new();@@ -503,7 +495,7 @@ kp_gadget_limbs.push(limb * &k_gadget);
} // Enforce delta = kp - Reducer::<TargetField, BaseField>::group_and_check_equality( + Reducer::<TargetF, BaseF>::group_and_check_equality( surfeit, params.bits_per_limb, params.bits_per_limb,@@ -518,7 +510,7 @@ #[tracing::instrument(target = "r1cs")]
pub(crate) fn conditional_enforce_not_equal( &self, other: &Self, - should_enforce: &Boolean<BaseField>, + should_enforce: &Boolean<BaseF>, ) -> R1CSResult<()> { assert_eq!(self.get_optimization_type(), other.get_optimization_type());@@ -541,8 +533,8 @@ }
/// Allocates a new variable, but does not check that the allocation's limbs /// 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>, mode: AllocationMode, ) -> R1CSResult<Self> {@@ -555,7 +547,7 @@ OptimizationGoal::Constraints => OptimizationType::Constraints,
OptimizationGoal::Weight => OptimizationType::Weight, }; - let zero = TargetField::zero(); + let zero = TargetF::zero(); let elem = match f() { Ok(t) => *(t.borrow()),@@ -565,7 +557,7 @@ let elem_representations = Self::get_limbs_representations(&elem, optimization_type)?;
let mut limbs = Vec::new(); for limb in elem_representations.iter() { - limbs.push(FpVar::<BaseField>::new_variable( + limbs.push(FpVar::<BaseF>::new_variable( ark_relations::ns!(cs, "alloc"), || Ok(limb), mode,@@ -573,9 +565,9 @@ )?);
} let num_of_additions_over_normal_form = if mode != AllocationMode::Witness { - BaseField::zero() + BaseF::zero() } else { - BaseField::one() + BaseF::one() }; Ok(Self {@@ -591,10 +583,7 @@ /// Check that this element is in-range; i.e., each limb is in-range, and
/// the whole number is less than the modulus. /// /// 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 cs = ns.cs(); let optimization_type = match cs.optimization_goal() {@@ -603,24 +592,23 @@ OptimizationGoal::Constraints => OptimizationType::Constraints,
OptimizationGoal::Weight => OptimizationType::Weight, }; 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, ); let mut bits = Vec::new(); for limb in self.limbs.iter().rev().take(params.num_limbs - 1) { 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() .rev(), ); } bits.extend( - Reducer::<TargetField, BaseField>::limb_to_bits( + Reducer::<TargetF, BaseF>::limb_to_bits( &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() .rev(),@@ -633,10 +621,10 @@ /// function `f`. Enforces that the field element has value in `[0, modulus)`,
/// and returns the bits of its binary representation. /// The bits are in little-endian (i.e., the bit at index 0 is the LSB) and the /// bit-vector is empty in non-witness allocation modes. - 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>, - ) -> R1CSResult<(Self, Vec<Boolean<BaseField>>)> { + ) -> R1CSResult<(Self, Vec<Boolean<BaseF>>)> { let ns = cs.into(); let cs = ns.cs(); let this = Self::new_variable_unchecked(ns!(cs, "alloc"), f, AllocationMode::Witness)?;@@ -645,36 +633,36 @@ Ok((this, bits))
} } -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")] - fn to_bits_le(&self) -> R1CSResult<Vec<Boolean<BaseField>>> { + fn to_bits_le(&self) -> R1CSResult<Vec<Boolean<BaseF>>> { 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(), ); // Reduce to the normal form // Though, a malicious prover can make it slightly larger than p 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 - let mut bits = Vec::<Boolean<BaseField>>::new(); + let mut bits = Vec::<Boolean<BaseF>>::new(); 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, params.bits_per_limb, )?); } bits.reverse(); - let mut b = TargetField::characteristic().to_vec(); + let mut b = TargetF::characteristic().to_vec(); assert_eq!(b[0] % 2, 1); 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 // the characteristic is an odd prime. So, this should@@ -685,14 +673,14 @@ Ok(bits)
} } -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")] - fn to_bytes(&self) -> R1CSResult<Vec<UInt8<BaseField>>> { + fn to_bytes(&self) -> R1CSResult<Vec<UInt8<BaseF>>> { 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); bits.resize_with(num_bits, || Boolean::constant(false));@@ -701,12 +689,12 @@ Ok(bytes)
} } -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")] fn conditionally_select( - cond: &Boolean<BaseField>, + cond: &Boolean<BaseF>, true_value: &Self, false_value: &Self, ) -> R1CSResult<Self> {@@ -718,7 +706,7 @@
let mut limbs_sel = Vec::with_capacity(true_value.limbs.len()); 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 {@@ -735,14 +723,14 @@ })
} } -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")] fn two_bit_lookup( - bits: &[Boolean<BaseField>], + bits: &[Boolean<BaseF>], constants: &[Self::TableConstant], ) -> R1CSResult<Self> { debug_assert!(bits.len() == 2);@@ -757,8 +745,8 @@ OptimizationGoal::Weight => OptimizationType::Weight,
}; 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, ); let mut limbs_constants = Vec::new();@@ -768,7 +756,7 @@ }
for constant in constants.iter() { let representations = - AllocatedNonNativeFieldVar::<TargetField, BaseField>::get_limbs_representations( + AllocatedEmulatedFpVar::<TargetF, BaseF>::get_limbs_representations( constant, optimization_type, )?;@@ -780,28 +768,28 @@ }
let mut limbs = Vec::new(); 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, limbs, - num_of_additions_over_normal_form: BaseField::zero(), + num_of_additions_over_normal_form: BaseF::zero(), is_in_the_normal_form: true, 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")] fn three_bit_cond_neg_lookup( - bits: &[Boolean<BaseField>], - b0b1: &Boolean<BaseField>, + bits: &[Boolean<BaseF>], + b0b1: &Boolean<BaseF>, constants: &[Self::TableConstant], ) -> R1CSResult<Self> { debug_assert!(bits.len() == 3);@@ -816,8 +804,8 @@ OptimizationGoal::Weight => OptimizationType::Weight,
}; 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, );@@ -828,7 +816,7 @@ }
for constant in constants.iter() { let representations = - AllocatedNonNativeFieldVar::<TargetField, BaseField>::get_limbs_representations( + AllocatedEmulatedFpVar::<TargetF, BaseF>::get_limbs_representations( constant, optimization_type, )?;@@ -840,28 +828,28 @@ }
let mut limbs = Vec::new(); 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, b0b1, limbs_constant, )?); } - Ok(AllocatedNonNativeFieldVar::<TargetField, BaseField> { + Ok(AllocatedEmulatedFpVar::<TargetF, BaseF> { cs, limbs, - num_of_additions_over_normal_form: BaseField::zero(), + num_of_additions_over_normal_form: BaseF::zero(), is_in_the_normal_form: true, 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>, mode: AllocationMode, ) -> R1CSResult<Self> {@@ -875,18 +863,18 @@ Ok(this)
} } -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 let bits = self.to_bits_le()?; // step 2: obtain the parameters for weight-optimized (often, fewer limbs) 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, );@@ -894,15 +882,15 @@ // step 3: assemble the limbs
let mut limbs = bits .chunks(params.bits_per_limb) .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() { limb += FpVar::from(b.clone()) * w; w.double_in_place(); } limb }) - .collect::<Vec<FpVar<BaseField>>>(); + .collect::<Vec<FpVar<BaseF>>>(); limbs.reverse();@@ -913,11 +901,9 @@ }
// 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 { - AllocatedNonNativeFieldVar { + AllocatedEmulatedFpVar { cs: self.cs(), limbs: self.limbs.clone(), num_of_additions_over_normal_form: self.num_of_additions_over_normal_form,
@@ -1,7 +1,7 @@
use super::{ params::{get_params, OptimizationType}, reduce::{bigint_to_basefield, limbs_to_bigint, Reducer}, - AllocatedNonNativeFieldVar, + AllocatedEmulatedFpVar, }; use crate::{fields::fp::FpVar, prelude::*}; use ark_ff::PrimeField;@@ -12,37 +12,36 @@ };
use ark_std::{marker::PhantomData, vec::Vec}; use num_bigint::BigUint; -/// The allocated form of `NonNativeFieldMulResultVar` (introduced below) +/// The allocated form of `MulResultVar` (introduced below) #[derive(Debug)] #[must_use] -pub struct AllocatedNonNativeFieldMulResultVar<TargetField: PrimeField, BaseField: PrimeField> { +pub struct AllocatedMulResultVar<TargetF: PrimeField, BaseF: PrimeField> { /// Constraint system reference - pub cs: ConstraintSystemRef<BaseField>, + pub cs: ConstraintSystemRef<BaseF>, /// Limbs of the intermediate representations - pub limbs: Vec<FpVar<BaseField>>, + pub limbs: Vec<FpVar<BaseF>>, /// The cumulative num of additions - pub prod_of_num_of_additions: BaseField, + pub prod_of_num_of_additions: BaseF, #[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( - 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(), ); let mut limbs = src.limbs.clone(); limbs.reverse(); - limbs.resize(2 * params.num_limbs - 1, FpVar::<BaseField>::zero()); + limbs.resize(2 * params.num_limbs - 1, FpVar::<BaseF>::zero()); 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 { cs: src.cs(),@@ -53,76 +52,74 @@ }
} } -impl<TargetField: PrimeField, BaseField: PrimeField> - AllocatedNonNativeFieldMulResultVar<TargetField, BaseField> -{ +impl<TargetF: PrimeField, BaseF: PrimeField> AllocatedMulResultVar<TargetF, BaseF> { /// Get the CS - pub fn cs(&self) -> ConstraintSystemRef<BaseField> { + pub fn cs(&self) -> ConstraintSystemRef<BaseF> { self.cs.clone() } /// Get the value of the multiplication result - pub fn value(&self) -> R1CSResult<TargetField> { + pub fn value(&self) -> R1CSResult<TargetF> { 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(), ); 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 mut limbs_values = Vec::<BaseField>::new(); + let mut limbs_values = Vec::<BaseF>::new(); for limb in self.limbs.iter() { limbs_values.push(limb.value().unwrap_or_default()); } 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) } /// Constraints for reducing the result of a multiplication mod p, to get an /// original representation. - pub fn reduce(&self) -> R1CSResult<AllocatedNonNativeFieldVar<TargetField, BaseField>> { + pub fn reduce(&self) -> R1CSResult<AllocatedEmulatedFpVar<TargetF, BaseF>> { 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(), ); // Step 1: get p 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 mut p_gadget_limbs = Vec::new(); 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(), 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, target_phantom: PhantomData, }; // 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 let k_bits = { 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() { limbs_values.push(limb.value().unwrap_or_default()); }@@ -130,10 +127,10 @@
let value_bigint = limbs_to_bigint(params.bits_per_limb, &limbs_values); 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 { - res.push(Boolean::<BaseField>::new_witness(self.cs(), || { + res.push(Boolean::<BaseF>::new_witness(self.cs(), || { Ok(&k_cur % 2u64 == BigUint::from(1u64)) })?); k_cur /= 2u64;@@ -142,7 +139,7 @@ res
}; let k_limbs = { - let zero = FpVar::Constant(BaseField::zero()); + let zero = FpVar::Constant(BaseF::zero()); let mut limbs = Vec::new(); let mut k_bits_cur = k_bits.clone();@@ -158,10 +155,10 @@ let this_limb_bits = k_bits_cur[0..this_limb_size].to_vec();
k_bits_cur = k_bits_cur[this_limb_size..].to_vec(); let mut limb = zero.clone(); - let mut cur = BaseField::one(); + let mut cur = BaseF::one(); 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(); } limbs.push(limb);@@ -171,7 +168,7 @@ limbs.reverse();
limbs }; - let k_gadget = AllocatedNonNativeFieldVar::<TargetField, BaseField> { + let k_gadget = AllocatedEmulatedFpVar::<TargetF, BaseF> { cs: self.cs(), limbs: k_limbs, num_of_additions_over_normal_form: self.prod_of_num_of_additions,@@ -181,20 +178,19 @@ };
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( - 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(), ); // Step 1: reduce `self` and `other` if neceessary let mut prod_limbs = Vec::new(); - let zero = FpVar::<BaseField>::zero(); + let zero = FpVar::<BaseF>::zero(); for _ in 0..2 * params.num_limbs - 1 { prod_limbs.push(zero.clone());@@ -209,9 +205,8 @@
let mut kp_plus_r_gadget = Self { cs, 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, };@@ -220,7 +215,7 @@ for (i, limb) in r_gadget.limbs.iter().rev().enumerate() {
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, 2 * params.bits_per_limb, params.bits_per_limb,@@ -254,12 +249,11 @@ }
/// Add native constant elem #[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(); let mut new_limbs = Vec::new();@@ -277,7 +271,7 @@
Ok(Self { cs: self.cs(), 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, }) }
@@ -1,499 +0,0 @@
-use super::{params::OptimizationType, AllocatedNonNativeFieldVar, NonNativeFieldMulResultVar}; -use crate::{ - boolean::Boolean, - fields::{fp::FpVar, FieldVar}, - prelude::*, - R1CSVar, ToConstraintFieldGadget, -}; -use ark_ff::{BigInteger, PrimeField}; -use ark_relations::r1cs::{ConstraintSystemRef, Namespace, Result as R1CSResult, SynthesisError}; -use ark_std::{ - borrow::Borrow, - hash::{Hash, Hasher}, - vec::Vec, -}; - -/// A gadget for representing non-native (`TargetField`) field elements over the -/// constraint field (`BaseField`). -#[derive(Clone, Debug)] -#[must_use] -pub enum NonNativeFieldVar<TargetField: PrimeField, BaseField: PrimeField> { - /// Constant - Constant(TargetField), - /// Allocated gadget - Var(AllocatedNonNativeFieldVar<TargetField, BaseField>), -} - -impl<TargetField: PrimeField, BaseField: PrimeField> PartialEq - for NonNativeFieldVar<TargetField, BaseField> -{ - fn eq(&self, other: &Self) -> bool { - self.value() - .unwrap_or_default() - .eq(&other.value().unwrap_or_default()) - } -} - -impl<TargetField: PrimeField, BaseField: PrimeField> Eq - for NonNativeFieldVar<TargetField, BaseField> -{ -} - -impl<TargetField: PrimeField, BaseField: PrimeField> Hash - for NonNativeFieldVar<TargetField, BaseField> -{ - fn hash<H: Hasher>(&self, state: &mut H) { - self.value().unwrap_or_default().hash(state); - } -} - -impl<TargetField: PrimeField, BaseField: PrimeField> R1CSVar<BaseField> - for NonNativeFieldVar<TargetField, BaseField> -{ - type Value = TargetField; - - fn cs(&self) -> ConstraintSystemRef<BaseField> { - match self { - Self::Constant(_) => ConstraintSystemRef::None, - Self::Var(a) => a.cs(), - } - } - - fn value(&self) -> R1CSResult<Self::Value> { - match self { - Self::Constant(v) => Ok(*v), - Self::Var(v) => v.value(), - } - } -} - -impl<TargetField: PrimeField, BaseField: PrimeField> From<Boolean<BaseField>> - for NonNativeFieldVar<TargetField, BaseField> -{ - fn from(other: Boolean<BaseField>) -> Self { - if let Boolean::Constant(b) = other { - Self::Constant(<TargetField as From<u128>>::from(b as u128)) - } else { - // `other` is a variable - let one = Self::Constant(TargetField::one()); - let zero = Self::Constant(TargetField::zero()); - Self::conditionally_select(&other, &one, &zero).unwrap() - } - } -} - -impl<TargetField: PrimeField, BaseField: PrimeField> - From<AllocatedNonNativeFieldVar<TargetField, BaseField>> - for NonNativeFieldVar<TargetField, BaseField> -{ - fn from(other: AllocatedNonNativeFieldVar<TargetField, BaseField>) -> Self { - Self::Var(other) - } -} - -impl<'a, TargetField: PrimeField, BaseField: PrimeField> FieldOpsBounds<'a, TargetField, Self> - for NonNativeFieldVar<TargetField, BaseField> -{ -} - -impl<'a, TargetField: PrimeField, BaseField: PrimeField> - FieldOpsBounds<'a, TargetField, NonNativeFieldVar<TargetField, BaseField>> - for &'a NonNativeFieldVar<TargetField, BaseField> -{ -} - -impl<TargetField: PrimeField, BaseField: PrimeField> FieldVar<TargetField, BaseField> - for NonNativeFieldVar<TargetField, BaseField> -{ - fn zero() -> Self { - Self::Constant(TargetField::zero()) - } - - fn one() -> Self { - Self::Constant(TargetField::one()) - } - - fn constant(v: TargetField) -> Self { - Self::Constant(v) - } - - #[tracing::instrument(target = "r1cs")] - fn negate(&self) -> R1CSResult<Self> { - match self { - Self::Constant(c) => Ok(Self::Constant(-*c)), - Self::Var(v) => Ok(Self::Var(v.negate()?)), - } - } - - #[tracing::instrument(target = "r1cs")] - fn inverse(&self) -> R1CSResult<Self> { - match self { - Self::Constant(c) => Ok(Self::Constant(c.inverse().unwrap_or_default())), - Self::Var(v) => Ok(Self::Var(v.inverse()?)), - } - } - - #[tracing::instrument(target = "r1cs")] - fn frobenius_map(&self, power: usize) -> R1CSResult<Self> { - match self { - Self::Constant(c) => Ok(Self::Constant({ - let mut tmp = *c; - tmp.frobenius_map_in_place(power); - tmp - })), - Self::Var(v) => Ok(Self::Var(v.frobenius_map(power)?)), - } - } -} - -impl_bounded_ops!( - NonNativeFieldVar<TargetField, BaseField>, - TargetField, - Add, - add, - AddAssign, - add_assign, - |this: &'a NonNativeFieldVar<TargetField, BaseField>, other: &'a NonNativeFieldVar<TargetField, BaseField>| { - use NonNativeFieldVar::*; - 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 NonNativeFieldVar<TargetField, BaseField>, other: TargetField| { this + &NonNativeFieldVar::Constant(other) }, - (TargetField: PrimeField, BaseField: PrimeField), -); - -impl_bounded_ops!( - NonNativeFieldVar<TargetField, BaseField>, - TargetField, - Sub, - sub, - SubAssign, - sub_assign, - |this: &'a NonNativeFieldVar<TargetField, BaseField>, other: &'a NonNativeFieldVar<TargetField, BaseField>| { - use NonNativeFieldVar::*; - match (this, other) { - (Constant(c1), Constant(c2)) => Constant(*c1 - c2), - (Var(v), Constant(c)) => Var(v.sub_constant(c).unwrap()), - (Constant(c), Var(v)) => Var(v.sub_constant(c).unwrap().negate().unwrap()), - (Var(v1), Var(v2)) => Var(v1.sub(v2).unwrap()), - } - }, - |this: &'a NonNativeFieldVar<TargetField, BaseField>, other: TargetField| { - this - &NonNativeFieldVar::Constant(other) - }, - (TargetField: PrimeField, BaseField: PrimeField), -); - -impl_bounded_ops!( - NonNativeFieldVar<TargetField, BaseField>, - TargetField, - Mul, - mul, - MulAssign, - mul_assign, - |this: &'a NonNativeFieldVar<TargetField, BaseField>, other: &'a NonNativeFieldVar<TargetField, BaseField>| { - use NonNativeFieldVar::*; - match (this, other) { - (Constant(c1), Constant(c2)) => Constant(*c1 * c2), - (Constant(c), Var(v)) | (Var(v), Constant(c)) => Var(v.mul_constant(c).unwrap()), - (Var(v1), Var(v2)) => Var(v1.mul(v2).unwrap()), - } - }, - |this: &'a NonNativeFieldVar<TargetField, BaseField>, other: TargetField| { - if other.is_zero() { - NonNativeFieldVar::zero() - } else { - this * &NonNativeFieldVar::Constant(other) - } - }, - (TargetField: PrimeField, BaseField: PrimeField), -); - -/// ************************************************************************* -/// ************************************************************************* - -impl<TargetField: PrimeField, BaseField: PrimeField> EqGadget<BaseField> - for NonNativeFieldVar<TargetField, BaseField> -{ - #[tracing::instrument(target = "r1cs")] - fn is_eq(&self, other: &Self) -> R1CSResult<Boolean<BaseField>> { - let cs = self.cs().or(other.cs()); - - if cs == ConstraintSystemRef::None { - Ok(Boolean::Constant(self.value()? == other.value()?)) - } else { - let should_enforce_equal = - Boolean::new_witness(cs, || Ok(self.value()? == other.value()?))?; - - self.conditional_enforce_equal(other, &should_enforce_equal)?; - self.conditional_enforce_not_equal(other, &should_enforce_equal.not())?; - - Ok(should_enforce_equal) - } - } - - #[tracing::instrument(target = "r1cs")] - fn conditional_enforce_equal( - &self, - other: &Self, - should_enforce: &Boolean<BaseField>, - ) -> R1CSResult<()> { - match (self, other) { - (Self::Constant(c1), Self::Constant(c2)) => { - if c1 != c2 { - should_enforce.enforce_equal(&Boolean::FALSE)?; - } - Ok(()) - }, - (Self::Constant(c), Self::Var(v)) | (Self::Var(v), Self::Constant(c)) => { - let cs = v.cs(); - let c = AllocatedNonNativeFieldVar::new_constant(cs, c)?; - c.conditional_enforce_equal(v, should_enforce) - }, - (Self::Var(v1), Self::Var(v2)) => v1.conditional_enforce_equal(v2, should_enforce), - } - } - - #[tracing::instrument(target = "r1cs")] - fn conditional_enforce_not_equal( - &self, - other: &Self, - should_enforce: &Boolean<BaseField>, - ) -> R1CSResult<()> { - match (self, other) { - (Self::Constant(c1), Self::Constant(c2)) => { - if c1 == c2 { - should_enforce.enforce_equal(&Boolean::FALSE)?; - } - Ok(()) - }, - (Self::Constant(c), Self::Var(v)) | (Self::Var(v), Self::Constant(c)) => { - let cs = v.cs(); - let c = AllocatedNonNativeFieldVar::new_constant(cs, c)?; - c.conditional_enforce_not_equal(v, should_enforce) - }, - (Self::Var(v1), Self::Var(v2)) => v1.conditional_enforce_not_equal(v2, should_enforce), - } - } -} - -impl<TargetField: PrimeField, BaseField: PrimeField> ToBitsGadget<BaseField> - for NonNativeFieldVar<TargetField, BaseField> -{ - #[tracing::instrument(target = "r1cs")] - fn to_bits_le(&self) -> R1CSResult<Vec<Boolean<BaseField>>> { - match self { - Self::Constant(_) => self.to_non_unique_bits_le(), - Self::Var(v) => v.to_bits_le(), - } - } - - #[tracing::instrument(target = "r1cs")] - fn to_non_unique_bits_le(&self) -> R1CSResult<Vec<Boolean<BaseField>>> { - use ark_ff::BitIteratorLE; - match self { - Self::Constant(c) => Ok(BitIteratorLE::new(&c.into_bigint()) - .take((TargetField::MODULUS_BIT_SIZE) as usize) - .map(Boolean::constant) - .collect::<Vec<_>>()), - Self::Var(v) => v.to_non_unique_bits_le(), - } - } -} - -impl<TargetField: PrimeField, BaseField: PrimeField> ToBytesGadget<BaseField> - for NonNativeFieldVar<TargetField, BaseField> -{ - /// Outputs the unique byte decomposition of `self` in *little-endian* - /// form. - #[tracing::instrument(target = "r1cs")] - fn to_bytes(&self) -> R1CSResult<Vec<UInt8<BaseField>>> { - match self { - Self::Constant(c) => Ok(UInt8::constant_vec( - c.into_bigint().to_bytes_le().as_slice(), - )), - - Self::Var(v) => v.to_bytes(), - } - } - - #[tracing::instrument(target = "r1cs")] - fn to_non_unique_bytes(&self) -> R1CSResult<Vec<UInt8<BaseField>>> { - match self { - Self::Constant(c) => Ok(UInt8::constant_vec( - c.into_bigint().to_bytes_le().as_slice(), - )), - Self::Var(v) => v.to_non_unique_bytes(), - } - } -} - -impl<TargetField: PrimeField, BaseField: PrimeField> CondSelectGadget<BaseField> - for NonNativeFieldVar<TargetField, BaseField> -{ - #[tracing::instrument(target = "r1cs")] - fn conditionally_select( - cond: &Boolean<BaseField>, - true_value: &Self, - false_value: &Self, - ) -> R1CSResult<Self> { - match cond { - Boolean::Constant(true) => Ok(true_value.clone()), - Boolean::Constant(false) => Ok(false_value.clone()), - _ => { - let cs = cond.cs(); - let true_value = match true_value { - Self::Constant(f) => AllocatedNonNativeFieldVar::new_constant(cs.clone(), f)?, - Self::Var(v) => v.clone(), - }; - let false_value = match false_value { - Self::Constant(f) => AllocatedNonNativeFieldVar::new_constant(cs, f)?, - Self::Var(v) => v.clone(), - }; - cond.select(&true_value, &false_value).map(Self::Var) - }, - } - } -} - -/// Uses two bits to perform a lookup into a table -/// `b` is little-endian: `b[0]` is LSB. -impl<TargetField: PrimeField, BaseField: PrimeField> TwoBitLookupGadget<BaseField> - for NonNativeFieldVar<TargetField, BaseField> -{ - type TableConstant = TargetField; - - #[tracing::instrument(target = "r1cs")] - fn two_bit_lookup(b: &[Boolean<BaseField>], c: &[Self::TableConstant]) -> R1CSResult<Self> { - debug_assert_eq!(b.len(), 2); - debug_assert_eq!(c.len(), 4); - if b.cs().is_none() { - // We're in the constant case - - let lsb = b[0].value()? as usize; - let msb = b[1].value()? as usize; - let index = lsb + (msb << 1); - Ok(Self::Constant(c[index])) - } else { - AllocatedNonNativeFieldVar::two_bit_lookup(b, c).map(Self::Var) - } - } -} - -impl<TargetField: PrimeField, BaseField: PrimeField> ThreeBitCondNegLookupGadget<BaseField> - for NonNativeFieldVar<TargetField, BaseField> -{ - type TableConstant = TargetField; - - #[tracing::instrument(target = "r1cs")] - fn three_bit_cond_neg_lookup( - b: &[Boolean<BaseField>], - b0b1: &Boolean<BaseField>, - c: &[Self::TableConstant], - ) -> R1CSResult<Self> { - debug_assert_eq!(b.len(), 3); - debug_assert_eq!(c.len(), 4); - - if b.cs().or(b0b1.cs()).is_none() { - // We're in the constant case - - let lsb = b[0].value()? as usize; - let msb = b[1].value()? as usize; - let index = lsb + (msb << 1); - let intermediate = c[index]; - - let is_negative = b[2].value()?; - let y = if is_negative { - -intermediate - } else { - intermediate - }; - Ok(Self::Constant(y)) - } else { - AllocatedNonNativeFieldVar::three_bit_cond_neg_lookup(b, b0b1, c).map(Self::Var) - } - } -} - -impl<TargetField: PrimeField, BaseField: PrimeField> AllocVar<TargetField, BaseField> - for NonNativeFieldVar<TargetField, BaseField> -{ - fn new_variable<T: Borrow<TargetField>>( - cs: impl Into<Namespace<BaseField>>, - f: impl FnOnce() -> Result<T, SynthesisError>, - mode: AllocationMode, - ) -> R1CSResult<Self> { - let ns = cs.into(); - let cs = ns.cs(); - - if cs == ConstraintSystemRef::None || mode == AllocationMode::Constant { - Ok(Self::Constant(*f()?.borrow())) - } else { - AllocatedNonNativeFieldVar::new_variable(cs, f, mode).map(Self::Var) - } - } -} - -impl<TargetField: PrimeField, BaseField: PrimeField> ToConstraintFieldGadget<BaseField> - for NonNativeFieldVar<TargetField, BaseField> -{ - #[tracing::instrument(target = "r1cs")] - fn to_constraint_field(&self) -> R1CSResult<Vec<FpVar<BaseField>>> { - // Use one group element to represent the optimization type. - // - // By default, the constant is converted in the weight-optimized type, because - // it results in fewer elements. - match self { - Self::Constant(c) => Ok(AllocatedNonNativeFieldVar::get_limbs_representations( - c, - OptimizationType::Weight, - )? - .into_iter() - .map(FpVar::constant) - .collect()), - Self::Var(v) => v.to_constraint_field(), - } - } -} - -impl<TargetField: PrimeField, BaseField: PrimeField> NonNativeFieldVar<TargetField, BaseField> { - /// The `mul_without_reduce` for `NonNativeFieldVar` - #[tracing::instrument(target = "r1cs")] - pub fn mul_without_reduce( - &self, - other: &Self, - ) -> R1CSResult<NonNativeFieldMulResultVar<TargetField, BaseField>> { - match self { - Self::Constant(c) => match other { - Self::Constant(other_c) => Ok(NonNativeFieldMulResultVar::Constant(*c * other_c)), - Self::Var(other_v) => { - let self_v = - AllocatedNonNativeFieldVar::<TargetField, BaseField>::new_constant( - self.cs(), - c, - )?; - Ok(NonNativeFieldMulResultVar::Var( - other_v.mul_without_reduce(&self_v)?, - )) - }, - }, - Self::Var(v) => { - let other_v = match other { - Self::Constant(other_c) => { - AllocatedNonNativeFieldVar::<TargetField, BaseField>::new_constant( - self.cs(), - other_c, - )? - }, - Self::Var(other_v) => other_v.clone(), - }; - Ok(NonNativeFieldMulResultVar::Var( - v.mul_without_reduce(&other_v)?, - )) - }, - } - } -}
@@ -17,8 +17,8 @@ //! native fields should be used where possible.)
//! //! ## 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 //! following example:@@ -28,7 +28,7 @@ //! # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> {
//! # use ark_std::UniformRand; //! # use ark_relations::{ns, r1cs::ConstraintSystem}; //! # 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}; //! //! # let mut rng = ark_std::test_rng();@@ -36,8 +36,8 @@ //! # let a_value = Fr::rand(&mut rng);
//! # let b_value = Fr::rand(&mut rng); //! # 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 //! let a_plus_b = &a + &b;@@ -57,15 +57,15 @@ //!
//! ## Advanced optimization //! //! 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 //! underlying details. //! //! However, this operation is expensive and is sometimes avoidable. We can //! reduce the number of constraints by using this intermediate type, which only //! supports additions. To multiply, it must be reduced back to -//! [`NonNativeFieldVar`]. See below for a skeleton example. +//! [`EmulatedFpVar`]. See below for a skeleton example. //! //! --- //!@@ -82,7 +82,7 @@ //! This performs two *reduce* operations in total, one for each multiplication.
//! //! --- //! -//! We can save one reduction by using the [`NonNativeFieldMulResultVar`], as +//! We can save one reduction by using [`MulResultVar`], as //! follows: //! //! ```ignore@@ -98,11 +98,11 @@ //! ## Inspiration and basic design
//! //! This implementation employs the standard idea of using multiple **limbs** to //! represent an element of the target field. For example, an element in the -//! TargetField may be represented by three BaseField elements (i.e., the +//! TargetF may be represented by three BaseF elements (i.e., the //! limbs). //! //! ```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@@ -122,8 +122,8 @@ //! \[KPS18\]: A. E. Kosba, C. Papamanthou, and E. Shi. "xJsnark: a framework for efficient verifiable computation," in *Proceedings of the 39th Symposium on Security and Privacy*, ser. S&P ’18, 2018, pp. 944–961.
//! //! \[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 #![allow(@@ -138,8 +138,8 @@ use ark_std::fmt::Debug;
/// 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 /// - `bits_per_limb`: the size of the limbs pub mod params;@@ -178,11 +178,11 @@ }
pub(crate) use overhead; -/// Parameters for a specific `NonNativeFieldVar` instantiation +/// Parameters for a specific `EmulatedFpVar` instantiation #[derive(Clone, Debug)] 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, /// The number of bits of the limb
@@ -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), -);
@@ -25,7 +25,7 @@ /// Optimized for 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( base_field_prime_length: usize, target_field_prime_bit_length: usize,
@@ -1,4 +1,4 @@
-use super::{overhead, params::get_params, AllocatedNonNativeFieldVar}; +use super::{overhead, params::get_params, AllocatedEmulatedFpVar}; use crate::{ alloc::AllocVar, boolean::Boolean,@@ -15,10 +15,7 @@ use ark_std::{cmp::min, marker::PhantomData, vec, vec::Vec};
use num_bigint::BigUint; 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 big_cur = BigUint::one(); let two = BigUint::from(2u32);@@ -37,16 +34,15 @@
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 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() { - let bytes_basefield = BaseField::from(*byte as u128); + let bytes_basefield = BaseF::from(*byte as u128); val += cur * bytes_basefield; cur *= &basefield_256;@@ -56,32 +52,28 @@ val
} /// 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 /// the original `to_bits` or `to_non_unique_bits` since we enforce that /// some bits are always zero. #[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 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 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()) - .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); }@@ -89,25 +81,24 @@
if cs == ConstraintSystemRef::None { let mut bits = vec![]; for b in bits_considered { - bits.push(Boolean::<BaseField>::Constant(b)); + bits.push(Boolean::<BaseF>::Constant(b)); } Ok(bits) } else { let mut bits = vec![]; for b in bits_considered { - bits.push(Boolean::<BaseField>::new_witness( + bits.push(Boolean::<BaseF>::new_witness( ark_relations::ns!(cs, "bit"), || 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() { - 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(); }@@ -119,11 +110,10 @@ }
/// Reduction to the normal form #[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 = new_elem;@@ -132,17 +122,15 @@ }
/// Reduction to be enforced after additions #[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( - 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(), ); - 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(()) } else { Self::reduce(elem)@@ -153,8 +141,8 @@ /// Reduction used before multiplication to reduce the representations in a
/// way that allows efficient multiplication #[tracing::instrument(target = "r1cs")] 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<()> { assert_eq!( elem.get_optimization_type(),@@ -162,30 +150,29 @@ elem_other.get_optimization_type()
); 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(), ); 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."); } 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( - &BaseField::from_bigint(<BaseField as PrimeField>::BigInt::from( + &BaseF::from_bigint(<BaseF as PrimeField>::BigInt::from( (params.num_limbs) as u64 )) .unwrap() )); 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; }@@ -203,9 +190,7 @@ }
/// Reduction to the normal form #[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 { return Ok(()); }@@ -219,14 +204,14 @@ pub fn group_and_check_equality(
surfeit: usize, bits_per_limb: usize, shift_per_limb: usize, - left: &[FpVar<BaseField>], - right: &[FpVar<BaseField>], + left: &[FpVar<BaseF>], + right: &[FpVar<BaseF>], ) -> R1CSResult<()> { 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 - surfeit - 1@@ -237,9 +222,9 @@ / shift_per_limb;
let shift_array = { 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 { - array.push(BaseField::from_bigint(cur).unwrap()); + array.push(BaseF::from_bigint(cur).unwrap()); cur <<= shift_per_limb as u32; }@@ -252,7 +237,7 @@ // groupped limb) will be the least significant limb.
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) { let mut left_total_limb = zero.clone();@@ -275,19 +260,19 @@
// 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 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(); for (group_id, (left_total_limb, right_total_limb, num_limb_in_this_group)) in 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 + (bits_per_limb - shift_per_limb) + shift_per_limb * num_limb_in_this_group + 1 + 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 right_total_limb_value = right_total_limb.value().unwrap_or_default();@@ -298,7 +283,7 @@
let carry_repr = 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))?;@@ -307,7 +292,7 @@
let (new_accumulated_extra, remainder) = accumulated_extra.div_rem( &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 // left_total_limb + pad_limb + carry_in - right_total_limb@@ -316,21 +301,21 @@
let eqn_left = left_total_limb + pad_limb + &carry_in - right_total_limb; 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; - 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; carry_in = carry.clone(); carry_in_value = carry_value; 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, )))?; } else { - Reducer::<TargetField, BaseField>::limb_to_bits(&carry, surfeit + bits_per_limb)?; + Reducer::<TargetF, BaseF>::limb_to_bits(&carry, surfeit + bits_per_limb)?; } }
@@ -7,7 +7,7 @@ use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError};
use ark_std::{borrow::Borrow, marker::PhantomData, ops::Mul}; use non_zero_affine::NonZeroAffineVar; -use crate::fields::nonnative::NonNativeFieldVar; +use crate::fields::emulated_fp::EmulatedFpVar; use crate::{fields::fp::FpVar, prelude::*, ToConstraintFieldGadget, Vec}; /// This module provides a generic implementation of G1 and G2 for@@ -688,13 +688,13 @@
impl_bounded_ops_diff!( ProjectiveVar<P, F>, SWProjective<P>, - NonNativeFieldVar<P::ScalarField, BasePrimeField<P>>, + EmulatedFpVar<P::ScalarField, BasePrimeField<P>>, P::ScalarField, Mul, mul, MulAssign, 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() { assert!(this.is_constant() && other.is_constant()); ProjectiveVar::constant(this.value().unwrap() * &other.value().unwrap())@@ -703,7 +703,7 @@ let bits = other.to_bits_le().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), for <'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>, );@@ -981,7 +981,7 @@ mod test_sw_curve {
use crate::{ alloc::AllocVar, eq::EqGadget, - fields::{fp::FpVar, nonnative::NonNativeFieldVar}, + fields::{emulated_fp::EmulatedFpVar, fp::FpVar}, groups::{curves::short_weierstrass::ProjectiveVar, CurveVar}, ToBitsGadget, };@@ -1015,7 +1015,7 @@ let point_out =
ProjectiveVar::<G::Config, FpVar<G::BaseField>>::new_input(cs.clone(), || { 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())?;
@@ -8,7 +8,7 @@ };
use ark_ff::{BitIteratorBE, Field, One, PrimeField, Zero}; 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::fields::fp::FpVar;@@ -787,13 +787,13 @@
impl_bounded_ops_diff!( AffineVar<P, F>, TEProjective<P>, - NonNativeFieldVar<P::ScalarField, BasePrimeField<P>>, + EmulatedFpVar<P::ScalarField, BasePrimeField<P>>, P::ScalarField, Mul, mul, MulAssign, 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() { assert!(this.is_constant() && other.is_constant()); AffineVar::constant(this.value().unwrap() * &other.value().unwrap())@@ -802,7 +802,7 @@ let bits = other.to_bits_le().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>> + TwoBitLookupGadget<BasePrimeField<P>, TableConstant = P::BaseField>,
@@ -1,4 +1,4 @@
-use crate::{fields::nonnative::NonNativeFieldVar, prelude::*}; +use crate::{fields::emulated_fp::EmulatedFpVar, prelude::*}; use ark_ff::PrimeField; use ark_relations::r1cs::{Namespace, SynthesisError}; use core::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign};@@ -44,9 +44,9 @@ + AddAssign<C>
+ SubAssign<C> + AddAssign<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 /// of the group.
@@ -10,7 +10,7 @@ use ark_r1cs_std::{
alloc::AllocVar, eq::EqGadget, fields::{ - nonnative::{AllocatedNonNativeFieldVar, NonNativeFieldVar}, + emulated_fp::{AllocatedEmulatedFpVar, EmulatedFpVar}, FieldVar, }, R1CSVar,@@ -28,16 +28,16 @@ const TEST_COUNT: usize = 100;
#[cfg(ci)] 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>, 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_expected = a_native;@@ -46,39 +46,38 @@ a_actual.eq(&a_expected),
"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 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!( a_bits_actual, a_bits_expected, "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>, 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;@@ -87,23 +86,23 @@ let a_plus_b_expected = a_native + &b_native;
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>, 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;@@ -119,28 +118,28 @@ a_times_b_expected.into_bigint().as_ref()
); } -fn equality_test<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>( +fn equality_test<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>( cs: ConstraintSystemRef<BaseField>, 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_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"), || Ok(a_times_b_expected), )@@ -149,21 +148,21 @@
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>, rng: &mut R, ) { - let zero_native = TargetField::zero(); - let zero = NonNativeFieldVar::<TargetField, BaseField>::zero(); - let one = NonNativeFieldVar::<TargetField, BaseField>::one(); + let zero_native = TargetF::zero(); + let zero = EmulatedFpVar::<TargetF, BaseField>::zero(); + let one = EmulatedFpVar::<TargetF, 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 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_minus_zero = &a - &zero;@@ -235,13 +234,13 @@ a_native.into_bigint().as_ref()
); } -fn distribution_law_test<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>( +fn distribution_law_test<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>( cs: ConstraintSystemRef<BaseField>, 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_times_c_native = a_native.clone() * &c_native;@@ -254,20 +253,17 @@ a_plus_b_times_c_native.eq(&a_times_c_plus_b_times_c_native),
"(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(); - 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(); - 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(); let a_plus_b = &a + &b;@@ -308,7 +304,7 @@ "(a + b) * c != (a * c) + (b * c)"
); } -fn randomized_arithmetic_test<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>( +fn randomized_arithmetic_test<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>( cs: ConstraintSystemRef<BaseField>, rng: &mut R, ) {@@ -317,15 +313,15 @@ for _ in 0..TEST_COUNT {
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"), || Ok(num_native), ) .unwrap(); 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"), || Ok(next_native), )@@ -353,17 +349,17 @@ );
} } -fn addition_stress_test<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>( +fn addition_stress_test<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>( cs: ConstraintSystemRef<BaseField>, rng: &mut R, ) { - let mut num_native = TargetField::rand(rng); + let mut num_native = TargetF::rand(rng); 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(); 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"), || Ok(next_native), )@@ -375,19 +371,19 @@ assert!(num.value().unwrap().eq(&num_native));
} } -fn multiplication_stress_test<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>( +fn multiplication_stress_test<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>( cs: ConstraintSystemRef<BaseField>, 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"), || Ok(num_native), ) .unwrap(); 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"), || Ok(next_native), )@@ -399,25 +395,25 @@ assert!(num.value().unwrap().eq(&num_native));
} } -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>, 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"), || Ok(num_native), ) .unwrap(); 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"), || Ok(next_add_native), ) .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"), || Ok(next_mul_native), )@@ -430,25 +426,25 @@ assert!(num.value().unwrap().eq(&num_native));
} } -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>, 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"), || Ok(num_native), ) .unwrap(); 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"), || Ok(next_add_native), ) .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"), || Ok(next_mul_native), )@@ -461,12 +457,12 @@ assert!(num.value().unwrap().eq(&num_native));
} } -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>, 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"), || Ok(num_native), )@@ -482,12 +478,12 @@ assert!(num.value().unwrap().eq(&num_native), "result incorrect");
} } -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>, 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"), || Ok(num_native), )@@ -506,12 +502,12 @@ assert!(num_square.value().unwrap().eq(&num_square_native));
} } -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>, 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"), || Ok(num_native), )@@ -526,7 +522,7 @@
// square let num_square_native = num_native * &num_native; let num_square = &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"), || Ok(num_square_native), )@@ -536,19 +532,19 @@ num_square.enforce_equal(&num_square_native_gadget).unwrap();
} } -fn inverse_stress_test<TargetField: PrimeField, BaseField: PrimeField, R: RngCore>( +fn inverse_stress_test<TargetF: PrimeField, BaseField: PrimeField, R: RngCore>( cs: ConstraintSystemRef<BaseField>, rng: &mut R, ) { 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; }
@@ -1,6 +1,6 @@
use ark_r1cs_std::{ alloc::AllocVar, - fields::nonnative::{NonNativeFieldMulResultVar, NonNativeFieldVar}, + fields::emulated_fp::{EmulatedFpVar, MulResultVar}, R1CSVar, }; use ark_relations::r1cs::ConstraintSystem;@@ -15,8 +15,8 @@ let mut rng = ark_std::test_rng();
let cs = ConstraintSystem::<CF>::new_ref(); 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_value = f_var.value().unwrap();
@@ -1,5 +1,5 @@
use ark_r1cs_std::{ - alloc::AllocVar, fields::nonnative::NonNativeFieldVar, R1CSVar, ToConstraintFieldGadget, + alloc::AllocVar, fields::emulated_fp::EmulatedFpVar, R1CSVar, ToConstraintFieldGadget, }; use ark_relations::r1cs::ConstraintSystem;@@ -10,8 +10,8 @@ type CF = ark_bls12_377::Fq;
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;