Browse Source

Add back `ToBytesGadget` and `ToBitsGadget` to `prelude` (#136)

avoid_assigned_value
Pratyush Mishra 1 year ago
committed by GitHub
parent
commit
a12499518c
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 874 additions and 231 deletions
  1. +12
    -0
      CHANGELOG.md
  2. +1
    -1
      src/boolean/convert.rs
  3. +1
    -1
      src/boolean/mod.rs
  4. +9
    -2
      src/boolean/not.rs
  5. +2
    -2
      src/cmp.rs
  6. +10
    -6
      src/convert.rs
  7. +8
    -8
      src/fields/cubic_extension.rs
  8. +1
    -1
      src/fields/emulated_fp/allocated_field_var.rs
  9. +4
    -4
      src/fields/emulated_fp/field_var.rs
  10. +6
    -6
      src/fields/fp/mod.rs
  11. +6
    -6
      src/fields/quadratic_extension.rs
  12. +14
    -14
      src/groups/curves/short_weierstrass/bls12/mod.rs
  13. +40
    -40
      src/groups/curves/short_weierstrass/mnt4/mod.rs
  14. +40
    -40
      src/groups/curves/short_weierstrass/mnt6/mod.rs
  15. +8
    -8
      src/groups/curves/short_weierstrass/mod.rs
  16. +6
    -6
      src/groups/curves/twisted_edwards/mod.rs
  17. +1
    -0
      src/lib.rs
  18. +108
    -17
      src/uint/and.rs
  19. +258
    -7
      src/uint/convert.rs
  20. +12
    -6
      src/uint/not.rs
  21. +108
    -17
      src/uint/or.rs
  22. +56
    -8
      src/uint/rotate.rs
  23. +6
    -6
      src/uint/shl.rs
  24. +6
    -6
      src/uint/shr.rs
  25. +43
    -2
      src/uint/test_utils.rs
  26. +108
    -17
      src/uint/xor.rs

+ 12
- 0
CHANGELOG.md

@ -4,14 +4,26 @@
### Breaking changes
- [\#121](https://github.com/arkworks-rs/r1cs-std/pull/121)
- Refactor `UInt{8,16,64,128}` into one struct `UInt`.
- Remove `bits` module.
- Use `std::ops` traits for `UInt` and `Boolean`.
- [\#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`.
- [\#136](https://github.com/arkworks-rs/r1cs-std/pull/136)
- Rename `ToBytesGadget::to_{non_unique_}bytes``ToBytesGadget::to_{non_unique_}bytes_in_le`.
### Features
- [\#136](https://github.com/arkworks-rs/r1cs-std/pull/136)
- Add `{BitAnd,BitOr,BitXor,BitAndAssign,BitOrAssign,BitXorAssign}<T> for UInt<N, T, F>`.
- Add `UInt::rotate_{left,right}_in_place`.
- Add `{Boolean,UInt}::not_in_place`.
- Add `UInt::{from_bytes_le, from_bytes_be, to_bytes_be}`.
### Improvements
### Bug Fixes

+ 1
- 1
src/boolean/convert.rs

@ -4,7 +4,7 @@ use crate::convert::{ToBytesGadget, ToConstraintFieldGadget};
impl<F: Field> ToBytesGadget<F> for Boolean<F> {
/// Outputs `1u8` if `self` is true, and `0u8` otherwise.
#[tracing::instrument(target = "r1cs")]
fn to_bytes(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
fn to_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
let value = self.value().map(u8::from).ok();
let mut bits = [Boolean::FALSE; 8];
bits[0] = self.clone();

+ 1
- 1
src/boolean/mod.rs

@ -215,7 +215,7 @@ mod test {
for val in [true, false].iter() {
let cs = ConstraintSystem::<Fr>::new_ref();
let a = Boolean::new_witness(cs.clone(), || Ok(*val))?;
let bytes = a.to_bytes()?;
let bytes = a.to_bytes_le()?;
assert_eq!(bytes.len(), 1);
let byte = &bytes[0];
assert_eq!(byte.value()?, *val as u8);

+ 9
- 2
src/boolean/not.rs

@ -6,10 +6,17 @@ use super::Boolean;
impl<F: Field> Boolean<F> {
fn _not(&self) -> Result<Self, SynthesisError> {
let mut result = self.clone();
result.not_in_place()?;
Ok(result)
}
pub fn not_in_place(&mut self) -> Result<(), SynthesisError> {
match *self {
Boolean::Constant(c) => Ok(Boolean::Constant(!c)),
Boolean::Var(ref v) => Ok(Boolean::Var(v.not().unwrap())),
Boolean::Constant(ref mut c) => *c = !*c,
Boolean::Var(ref mut v) => *v = v.not()?,
}
Ok(())
}
}

+ 2
- 2
src/cmp.rs

@ -1,10 +1,10 @@
use ark_ff::Field;
use ark_relations::r1cs::SynthesisError;
use crate::boolean::Boolean;
use crate::{boolean::Boolean, R1CSVar};
/// Specifies how to generate constraints for comparing two variables.
pub trait CmpGadget<F: Field> {
pub trait CmpGadget<F: Field>: R1CSVar<F> {
fn is_gt(&self, other: &Self) -> Result<Boolean<F>, SynthesisError> {
other.is_lt(self)
}

+ 10
- 6
src/convert.rs

@ -69,20 +69,24 @@ pub trait ToBytesGadget {
/// Outputs a canonical, little-endian, byte decomposition of `self`.
///
/// This is the correct default for 99% of use cases.
fn to_bytes(&self) -> Result<Vec<UInt8<F>>, SynthesisError>;
fn to_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError>;
/// Outputs a possibly non-unique byte decomposition of `self`.
///
/// If you're not absolutely certain that your usecase can get away with a
/// non-canonical representation, please use `self.to_bytes(cs)` instead.
fn to_non_unique_bytes(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
self.to_bytes()
/// non-canonical representation, please use `self.to_bytes_le(cs)` instead.
fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
self.to_bytes_le()
}
}
impl<'a, F: Field, T: 'a + ToBytesGadget<F>> ToBytesGadget<F> for &'a T {
fn to_bytes(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
(*self).to_bytes()
fn to_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
(*self).to_bytes_le()
}
fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
(*self).to_non_unique_bytes_le()
}
}

+ 8
- 8
src/fields/cubic_extension.rs

@ -435,10 +435,10 @@ where
P: CubicExtVarConfig<BF>,
{
#[tracing::instrument(target = "r1cs")]
fn to_bytes(&self) -> Result<Vec<UInt8<P::BasePrimeField>>, SynthesisError> {
let mut c0 = self.c0.to_bytes()?;
let mut c1 = self.c1.to_bytes()?;
let mut c2 = self.c2.to_bytes()?;
fn to_bytes_le(&self) -> Result<Vec<UInt8<P::BasePrimeField>>, SynthesisError> {
let mut c0 = self.c0.to_bytes_le()?;
let mut c1 = self.c1.to_bytes_le()?;
let mut c2 = self.c2.to_bytes_le()?;
c0.append(&mut c1);
c0.append(&mut c2);
@ -446,10 +446,10 @@ where
}
#[tracing::instrument(target = "r1cs")]
fn to_non_unique_bytes(&self) -> Result<Vec<UInt8<P::BasePrimeField>>, SynthesisError> {
let mut c0 = self.c0.to_non_unique_bytes()?;
let mut c1 = self.c1.to_non_unique_bytes()?;
let mut c2 = self.c2.to_non_unique_bytes()?;
fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<P::BasePrimeField>>, SynthesisError> {
let mut c0 = self.c0.to_non_unique_bytes_le()?;
let mut c1 = self.c1.to_non_unique_bytes_le()?;
let mut c2 = self.c2.to_non_unique_bytes_le()?;
c0.append(&mut c1);
c0.append(&mut c2);

+ 1
- 1
src/fields/emulated_fp/allocated_field_var.rs

@ -681,7 +681,7 @@ impl ToBytesGadget
for AllocatedEmulatedFpVar<TargetF, BaseF>
{
#[tracing::instrument(target = "r1cs")]
fn to_bytes(&self) -> R1CSResult<Vec<UInt8<BaseF>>> {
fn to_bytes_le(&self) -> R1CSResult<Vec<UInt8<BaseF>>> {
let mut bits = self.to_bits_le()?;
let num_bits = TargetF::BigInt::NUM_LIMBS * 64;

+ 4
- 4
src/fields/emulated_fp/field_var.rs

@ -297,23 +297,23 @@ impl ToBytesGadget
/// Outputs the unique byte decomposition of `self` in *little-endian*
/// form.
#[tracing::instrument(target = "r1cs")]
fn to_bytes(&self) -> R1CSResult<Vec<UInt8<BaseF>>> {
fn to_bytes_le(&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(),
Self::Var(v) => v.to_bytes_le(),
}
}
#[tracing::instrument(target = "r1cs")]
fn to_non_unique_bytes(&self) -> R1CSResult<Vec<UInt8<BaseF>>> {
fn to_non_unique_bytes_le(&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(),
Self::Var(v) => v.to_non_unique_bytes_le(),
}
}
}

+ 6
- 6
src/fields/fp/mod.rs

@ -552,7 +552,7 @@ impl ToBytesGadget for AllocatedFp {
/// This method enforces that the decomposition represents
/// an integer that is less than `F::MODULUS`.
#[tracing::instrument(target = "r1cs")]
fn to_bytes(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
fn to_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
let num_bits = F::BigInt::NUM_LIMBS * 64;
let mut bits = self.to_bits_le()?;
let remainder = core::iter::repeat(Boolean::constant(false)).take(num_bits - bits.len());
@ -565,7 +565,7 @@ impl ToBytesGadget for AllocatedFp {
}
#[tracing::instrument(target = "r1cs")]
fn to_non_unique_bytes(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
let num_bits = F::BigInt::NUM_LIMBS * 64;
let mut bits = self.to_non_unique_bits_le()?;
let remainder = core::iter::repeat(Boolean::constant(false)).take(num_bits - bits.len());
@ -957,22 +957,22 @@ impl ToBytesGadget for FpVar {
/// Outputs the unique byte decomposition of `self` in *little-endian*
/// form.
#[tracing::instrument(target = "r1cs")]
fn to_bytes(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
fn to_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
match self {
Self::Constant(c) => Ok(UInt8::constant_vec(
c.into_bigint().to_bytes_le().as_slice(),
)),
Self::Var(v) => v.to_bytes(),
Self::Var(v) => v.to_bytes_le(),
}
}
#[tracing::instrument(target = "r1cs")]
fn to_non_unique_bytes(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
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(),
Self::Var(v) => v.to_non_unique_bytes_le(),
}
}
}

+ 6
- 6
src/fields/quadratic_extension.rs

@ -435,17 +435,17 @@ where
P: QuadExtVarConfig<BF>,
{
#[tracing::instrument(target = "r1cs")]
fn to_bytes(&self) -> Result<Vec<UInt8<P::BasePrimeField>>, SynthesisError> {
let mut c0 = self.c0.to_bytes()?;
let mut c1 = self.c1.to_bytes()?;
fn to_bytes_le(&self) -> Result<Vec<UInt8<P::BasePrimeField>>, SynthesisError> {
let mut c0 = self.c0.to_bytes_le()?;
let mut c1 = self.c1.to_bytes_le()?;
c0.append(&mut c1);
Ok(c0)
}
#[tracing::instrument(target = "r1cs")]
fn to_non_unique_bytes(&self) -> Result<Vec<UInt8<P::BasePrimeField>>, SynthesisError> {
let mut c0 = self.c0.to_non_unique_bytes()?;
let mut c1 = self.c1.to_non_unique_bytes()?;
fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<P::BasePrimeField>>, SynthesisError> {
let mut c0 = self.c0.to_non_unique_bytes_le()?;
let mut c1 = self.c1.to_non_unique_bytes_le()?;
c0.append(&mut c1);
Ok(c0)
}

+ 14
- 14
src/groups/curves/short_weierstrass/bls12/mod.rs

@ -79,20 +79,20 @@ impl AllocVar, P::Fp> for G1PreparedVar

{

impl<P: Bls12Config> ToBytesGadget<P::Fp> for G1PreparedVar<P> {
#[inline]
#[tracing::instrument(target = "r1cs")]
fn to_bytes(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut bytes = self.0.x.to_bytes()?;
let y_bytes = self.0.y.to_bytes()?;
let inf_bytes = self.0.infinity.to_bytes()?;
fn to_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut bytes = self.0.x.to_bytes_le()?;
let y_bytes = self.0.y.to_bytes_le()?;
let inf_bytes = self.0.infinity.to_bytes_le()?;
bytes.extend_from_slice(&y_bytes);
bytes.extend_from_slice(&inf_bytes);
Ok(bytes)
}
#[tracing::instrument(target = "r1cs")]
fn to_non_unique_bytes(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut bytes = self.0.x.to_non_unique_bytes()?;
let y_bytes = self.0.y.to_non_unique_bytes()?;
let inf_bytes = self.0.infinity.to_non_unique_bytes()?;
fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut bytes = self.0.x.to_non_unique_bytes_le()?;
let y_bytes = self.0.y.to_non_unique_bytes_le()?;
let inf_bytes = self.0.infinity.to_non_unique_bytes_le()?;
bytes.extend_from_slice(&y_bytes);
bytes.extend_from_slice(&inf_bytes);
Ok(bytes)
@ -174,21 +174,21 @@ impl AllocVar, P::Fp> for G2PreparedVar

{

impl<P: Bls12Config> ToBytesGadget<P::Fp> for G2PreparedVar<P> {
#[inline]
#[tracing::instrument(target = "r1cs")]
fn to_bytes(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
fn to_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut bytes = Vec::new();
for coeffs in &self.ell_coeffs {
bytes.extend_from_slice(&coeffs.0.to_bytes()?);
bytes.extend_from_slice(&coeffs.1.to_bytes()?);
bytes.extend_from_slice(&coeffs.0.to_bytes_le()?);
bytes.extend_from_slice(&coeffs.1.to_bytes_le()?);
}
Ok(bytes)
}
#[tracing::instrument(target = "r1cs")]
fn to_non_unique_bytes(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut bytes = Vec::new();
for coeffs in &self.ell_coeffs {
bytes.extend_from_slice(&coeffs.0.to_non_unique_bytes()?);
bytes.extend_from_slice(&coeffs.1.to_non_unique_bytes()?);
bytes.extend_from_slice(&coeffs.0.to_non_unique_bytes_le()?);
bytes.extend_from_slice(&coeffs.1.to_non_unique_bytes_le()?);
}
Ok(bytes)
}

+ 40
- 40
src/groups/curves/short_weierstrass/mnt4/mod.rs

@ -105,11 +105,11 @@ impl G1PreparedVar

{

impl<P: MNT4Config> ToBytesGadget<P::Fp> for G1PreparedVar<P> {
#[inline]
#[tracing::instrument(target = "r1cs")]
fn to_bytes(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut x = self.x.to_bytes()?;
let mut y = self.y.to_bytes()?;
let mut x_twist = self.x_twist.to_bytes()?;
let mut y_twist = self.y_twist.to_bytes()?;
fn to_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut x = self.x.to_bytes_le()?;
let mut y = self.y.to_bytes_le()?;
let mut x_twist = self.x_twist.to_bytes_le()?;
let mut y_twist = self.y_twist.to_bytes_le()?;
x.append(&mut y);
x.append(&mut x_twist);
@ -118,11 +118,11 @@ impl ToBytesGadget for G1PreparedVar

{

}
#[tracing::instrument(target = "r1cs")]
fn to_non_unique_bytes(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut x = self.x.to_non_unique_bytes()?;
let mut y = self.y.to_non_unique_bytes()?;
let mut x_twist = self.x_twist.to_non_unique_bytes()?;
let mut y_twist = self.y_twist.to_non_unique_bytes()?;
fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut x = self.x.to_non_unique_bytes_le()?;
let mut y = self.y.to_non_unique_bytes_le()?;
let mut x_twist = self.x_twist.to_non_unique_bytes_le()?;
let mut y_twist = self.y_twist.to_non_unique_bytes_le()?;
x.append(&mut y);
x.append(&mut x_twist);
@ -201,41 +201,41 @@ impl AllocVar, P::Fp> for G2PreparedVar

{

impl<P: MNT4Config> ToBytesGadget<P::Fp> for G2PreparedVar<P> {
#[inline]
#[tracing::instrument(target = "r1cs")]
fn to_bytes(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut x = self.x.to_bytes()?;
let mut y = self.y.to_bytes()?;
let mut x_over_twist = self.x_over_twist.to_bytes()?;
let mut y_over_twist = self.y_over_twist.to_bytes()?;
fn to_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut x = self.x.to_bytes_le()?;
let mut y = self.y.to_bytes_le()?;
let mut x_over_twist = self.x_over_twist.to_bytes_le()?;
let mut y_over_twist = self.y_over_twist.to_bytes_le()?;
x.append(&mut y);
x.append(&mut x_over_twist);
x.append(&mut y_over_twist);
for coeff in &self.double_coefficients {
x.extend_from_slice(&coeff.to_bytes()?);
x.extend_from_slice(&coeff.to_bytes_le()?);
}
for coeff in &self.addition_coefficients {
x.extend_from_slice(&coeff.to_bytes()?);
x.extend_from_slice(&coeff.to_bytes_le()?);
}
Ok(x)
}
#[tracing::instrument(target = "r1cs")]
fn to_non_unique_bytes(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut x = self.x.to_non_unique_bytes()?;
let mut y = self.y.to_non_unique_bytes()?;
let mut x_over_twist = self.x_over_twist.to_non_unique_bytes()?;
let mut y_over_twist = self.y_over_twist.to_non_unique_bytes()?;
fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut x = self.x.to_non_unique_bytes_le()?;
let mut y = self.y.to_non_unique_bytes_le()?;
let mut x_over_twist = self.x_over_twist.to_non_unique_bytes_le()?;
let mut y_over_twist = self.y_over_twist.to_non_unique_bytes_le()?;
x.append(&mut y);
x.append(&mut x_over_twist);
x.append(&mut y_over_twist);
for coeff in &self.double_coefficients {
x.extend_from_slice(&coeff.to_non_unique_bytes()?);
x.extend_from_slice(&coeff.to_non_unique_bytes_le()?);
}
for coeff in &self.addition_coefficients {
x.extend_from_slice(&coeff.to_non_unique_bytes()?);
x.extend_from_slice(&coeff.to_non_unique_bytes_le()?);
}
Ok(x)
}
@ -379,11 +379,11 @@ impl AllocVar, P::Fp> for AteDoubleCoeff
impl<P: MNT4Config> ToBytesGadget<P::Fp> for AteDoubleCoefficientsVar<P> {
#[inline]
#[tracing::instrument(target = "r1cs")]
fn to_bytes(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut c_h = self.c_h.to_bytes()?;
let mut c_4c = self.c_4c.to_bytes()?;
let mut c_j = self.c_j.to_bytes()?;
let mut c_l = self.c_l.to_bytes()?;
fn to_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut c_h = self.c_h.to_bytes_le()?;
let mut c_4c = self.c_4c.to_bytes_le()?;
let mut c_j = self.c_j.to_bytes_le()?;
let mut c_l = self.c_l.to_bytes_le()?;
c_h.append(&mut c_4c);
c_h.append(&mut c_j);
@ -392,11 +392,11 @@ impl ToBytesGadget for AteDoubleCoefficientsVar

{

}
#[tracing::instrument(target = "r1cs")]
fn to_non_unique_bytes(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut c_h = self.c_h.to_non_unique_bytes()?;
let mut c_4c = self.c_4c.to_non_unique_bytes()?;
let mut c_j = self.c_j.to_non_unique_bytes()?;
let mut c_l = self.c_l.to_non_unique_bytes()?;
fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut c_h = self.c_h.to_non_unique_bytes_le()?;
let mut c_4c = self.c_4c.to_non_unique_bytes_le()?;
let mut c_j = self.c_j.to_non_unique_bytes_le()?;
let mut c_l = self.c_l.to_non_unique_bytes_le()?;
c_h.append(&mut c_4c);
c_h.append(&mut c_j);
@ -456,18 +456,18 @@ impl AllocVar, P::Fp> for AteAdditionC
impl<P: MNT4Config> ToBytesGadget<P::Fp> for AteAdditionCoefficientsVar<P> {
#[inline]
#[tracing::instrument(target = "r1cs")]
fn to_bytes(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut c_l1 = self.c_l1.to_bytes()?;
let mut c_rz = self.c_rz.to_bytes()?;
fn to_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut c_l1 = self.c_l1.to_bytes_le()?;
let mut c_rz = self.c_rz.to_bytes_le()?;
c_l1.append(&mut c_rz);
Ok(c_l1)
}
#[tracing::instrument(target = "r1cs")]
fn to_non_unique_bytes(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut c_l1 = self.c_l1.to_non_unique_bytes()?;
let mut c_rz = self.c_rz.to_non_unique_bytes()?;
fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut c_l1 = self.c_l1.to_non_unique_bytes_le()?;
let mut c_rz = self.c_rz.to_non_unique_bytes_le()?;
c_l1.append(&mut c_rz);
Ok(c_l1)

+ 40
- 40
src/groups/curves/short_weierstrass/mnt6/mod.rs

@ -105,11 +105,11 @@ impl AllocVar, P::Fp> for G1PreparedVar

{

impl<P: MNT6Config> ToBytesGadget<P::Fp> for G1PreparedVar<P> {
#[inline]
#[tracing::instrument(target = "r1cs")]
fn to_bytes(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut x = self.x.to_bytes()?;
let mut y = self.y.to_bytes()?;
let mut x_twist = self.x_twist.to_bytes()?;
let mut y_twist = self.y_twist.to_bytes()?;
fn to_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut x = self.x.to_bytes_le()?;
let mut y = self.y.to_bytes_le()?;
let mut x_twist = self.x_twist.to_bytes_le()?;
let mut y_twist = self.y_twist.to_bytes_le()?;
x.append(&mut y);
x.append(&mut x_twist);
@ -118,11 +118,11 @@ impl ToBytesGadget for G1PreparedVar

{

}
#[tracing::instrument(target = "r1cs")]
fn to_non_unique_bytes(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut x = self.x.to_non_unique_bytes()?;
let mut y = self.y.to_non_unique_bytes()?;
let mut x_twist = self.x_twist.to_non_unique_bytes()?;
let mut y_twist = self.y_twist.to_non_unique_bytes()?;
fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut x = self.x.to_non_unique_bytes_le()?;
let mut y = self.y.to_non_unique_bytes_le()?;
let mut x_twist = self.x_twist.to_non_unique_bytes_le()?;
let mut y_twist = self.y_twist.to_non_unique_bytes_le()?;
x.append(&mut y);
x.append(&mut x_twist);
@ -201,41 +201,41 @@ impl AllocVar, P::Fp> for G2PreparedVar

{

impl<P: MNT6Config> ToBytesGadget<P::Fp> for G2PreparedVar<P> {
#[inline]
#[tracing::instrument(target = "r1cs")]
fn to_bytes(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut x = self.x.to_bytes()?;
let mut y = self.y.to_bytes()?;
let mut x_over_twist = self.x_over_twist.to_bytes()?;
let mut y_over_twist = self.y_over_twist.to_bytes()?;
fn to_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut x = self.x.to_bytes_le()?;
let mut y = self.y.to_bytes_le()?;
let mut x_over_twist = self.x_over_twist.to_bytes_le()?;
let mut y_over_twist = self.y_over_twist.to_bytes_le()?;
x.append(&mut y);
x.append(&mut x_over_twist);
x.append(&mut y_over_twist);
for coeff in self.double_coefficients.iter() {
x.extend_from_slice(&coeff.to_bytes()?);
x.extend_from_slice(&coeff.to_bytes_le()?);
}
for coeff in self.addition_coefficients.iter() {
x.extend_from_slice(&coeff.to_bytes()?);
x.extend_from_slice(&coeff.to_bytes_le()?);
}
Ok(x)
}
#[tracing::instrument(target = "r1cs")]
fn to_non_unique_bytes(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut x = self.x.to_non_unique_bytes()?;
let mut y = self.y.to_non_unique_bytes()?;
let mut x_over_twist = self.x_over_twist.to_non_unique_bytes()?;
let mut y_over_twist = self.y_over_twist.to_non_unique_bytes()?;
fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut x = self.x.to_non_unique_bytes_le()?;
let mut y = self.y.to_non_unique_bytes_le()?;
let mut x_over_twist = self.x_over_twist.to_non_unique_bytes_le()?;
let mut y_over_twist = self.y_over_twist.to_non_unique_bytes_le()?;
x.append(&mut y);
x.append(&mut x_over_twist);
x.append(&mut y_over_twist);
for coeff in self.double_coefficients.iter() {
x.extend_from_slice(&coeff.to_non_unique_bytes()?);
x.extend_from_slice(&coeff.to_non_unique_bytes_le()?);
}
for coeff in self.addition_coefficients.iter() {
x.extend_from_slice(&coeff.to_non_unique_bytes()?);
x.extend_from_slice(&coeff.to_non_unique_bytes_le()?);
}
Ok(x)
}
@ -379,11 +379,11 @@ impl AllocVar, P::Fp> for AteDoubleCoeff
impl<P: MNT6Config> ToBytesGadget<P::Fp> for AteDoubleCoefficientsVar<P> {
#[inline]
#[tracing::instrument(target = "r1cs")]
fn to_bytes(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut c_h = self.c_h.to_bytes()?;
let mut c_4c = self.c_4c.to_bytes()?;
let mut c_j = self.c_j.to_bytes()?;
let mut c_l = self.c_l.to_bytes()?;
fn to_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut c_h = self.c_h.to_bytes_le()?;
let mut c_4c = self.c_4c.to_bytes_le()?;
let mut c_j = self.c_j.to_bytes_le()?;
let mut c_l = self.c_l.to_bytes_le()?;
c_h.append(&mut c_4c);
c_h.append(&mut c_j);
@ -392,11 +392,11 @@ impl ToBytesGadget for AteDoubleCoefficientsVar

{

}
#[tracing::instrument(target = "r1cs")]
fn to_non_unique_bytes(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut c_h = self.c_h.to_non_unique_bytes()?;
let mut c_4c = self.c_4c.to_non_unique_bytes()?;
let mut c_j = self.c_j.to_non_unique_bytes()?;
let mut c_l = self.c_l.to_non_unique_bytes()?;
fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut c_h = self.c_h.to_non_unique_bytes_le()?;
let mut c_4c = self.c_4c.to_non_unique_bytes_le()?;
let mut c_j = self.c_j.to_non_unique_bytes_le()?;
let mut c_l = self.c_l.to_non_unique_bytes_le()?;
c_h.append(&mut c_4c);
c_h.append(&mut c_j);
@ -454,18 +454,18 @@ impl AllocVar, P::Fp> for AteAdditionC
impl<P: MNT6Config> ToBytesGadget<P::Fp> for AteAdditionCoefficientsVar<P> {
#[inline]
#[tracing::instrument(target = "r1cs")]
fn to_bytes(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut c_l1 = self.c_l1.to_bytes()?;
let mut c_rz = self.c_rz.to_bytes()?;
fn to_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut c_l1 = self.c_l1.to_bytes_le()?;
let mut c_rz = self.c_rz.to_bytes_le()?;
c_l1.append(&mut c_rz);
Ok(c_l1)
}
#[tracing::instrument(target = "r1cs")]
fn to_non_unique_bytes(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut c_l1 = self.c_l1.to_non_unique_bytes()?;
let mut c_rz = self.c_rz.to_non_unique_bytes()?;
fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<P::Fp>>, SynthesisError> {
let mut c_l1 = self.c_l1.to_non_unique_bytes_le()?;
let mut c_rz = self.c_rz.to_non_unique_bytes_le()?;
c_l1.append(&mut c_rz);
Ok(c_l1)

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

@ -953,22 +953,22 @@ where
for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
{
#[tracing::instrument(target = "r1cs")]
fn to_bytes(&self) -> Result<Vec<UInt8<BasePrimeField<P>>>, SynthesisError> {
fn to_bytes_le(&self) -> Result<Vec<UInt8<BasePrimeField<P>>>, SynthesisError> {
let g = self.to_affine()?;
let mut bytes = g.x.to_bytes()?;
let y_bytes = g.y.to_bytes()?;
let inf_bytes = g.infinity.to_bytes()?;
let mut bytes = g.x.to_bytes_le()?;
let y_bytes = g.y.to_bytes_le()?;
let inf_bytes = g.infinity.to_bytes_le()?;
bytes.extend_from_slice(&y_bytes);
bytes.extend_from_slice(&inf_bytes);
Ok(bytes)
}
#[tracing::instrument(target = "r1cs")]
fn to_non_unique_bytes(&self) -> Result<Vec<UInt8<BasePrimeField<P>>>, SynthesisError> {
fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<BasePrimeField<P>>>, SynthesisError> {
let g = self.to_affine()?;
let mut bytes = g.x.to_non_unique_bytes()?;
let y_bytes = g.y.to_non_unique_bytes()?;
let inf_bytes = g.infinity.to_non_unique_bytes()?;
let mut bytes = g.x.to_non_unique_bytes_le()?;
let y_bytes = g.y.to_non_unique_bytes_le()?;
let inf_bytes = g.infinity.to_non_unique_bytes_le()?;
bytes.extend_from_slice(&y_bytes);
bytes.extend_from_slice(&inf_bytes);
Ok(bytes)

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

@ -920,17 +920,17 @@ where
for<'b> &'b F: FieldOpsBounds<'b, P::BaseField, F>,
{
#[tracing::instrument(target = "r1cs")]
fn to_bytes(&self) -> Result<Vec<UInt8<BasePrimeField<P>>>, SynthesisError> {
let mut x_bytes = self.x.to_bytes()?;
let y_bytes = self.y.to_bytes()?;
fn to_bytes_le(&self) -> Result<Vec<UInt8<BasePrimeField<P>>>, SynthesisError> {
let mut x_bytes = self.x.to_bytes_le()?;
let y_bytes = self.y.to_bytes_le()?;
x_bytes.extend_from_slice(&y_bytes);
Ok(x_bytes)
}
#[tracing::instrument(target = "r1cs")]
fn to_non_unique_bytes(&self) -> Result<Vec<UInt8<BasePrimeField<P>>>, SynthesisError> {
let mut x_bytes = self.x.to_non_unique_bytes()?;
let y_bytes = self.y.to_non_unique_bytes()?;
fn to_non_unique_bytes_le(&self) -> Result<Vec<UInt8<BasePrimeField<P>>>, SynthesisError> {
let mut x_bytes = self.x.to_non_unique_bytes_le()?;
let y_bytes = self.y.to_non_unique_bytes_le()?;
x_bytes.extend_from_slice(&y_bytes);
Ok(x_bytes)

+ 1
- 0
src/lib.rs

@ -90,6 +90,7 @@ pub mod prelude {
pub use crate::{
alloc::*,
boolean::Boolean,
convert::{ToBitsGadget, ToBytesGadget},
eq::*,
fields::{FieldOpsBounds, FieldVar},
groups::{CurveVar, GroupOpsBounds},

+ 108
- 17
src/uint/and.rs

@ -7,11 +7,16 @@ use super::*;
impl<const N: usize, T: PrimUInt, F: Field> UInt<N, T, F> {
fn _and(&self, other: &Self) -> Result<Self, SynthesisError> {
let mut result = self.clone();
for (a, b) in result.bits.iter_mut().zip(&other.bits) {
result._and_in_place(other)?;
Ok(result)
}
fn _and_in_place(&mut self, other: &Self) -> Result<(), SynthesisError> {
for (a, b) in self.bits.iter_mut().zip(&other.bits) {
*a &= b;
}
result.value = self.value.and_then(|a| Some(a & other.value?));
Ok(result)
self.value = self.value.and_then(|a| Some(a & other.value?));
Ok(())
}
}
@ -70,8 +75,9 @@ impl<'a, const N: usize, T: PrimUInt, F: Field> BitAnd<&'a Self> for UInt
/// # }
/// ```
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitand(self, other: &Self) -> Self::Output {
self._and(&other).unwrap()
fn bitand(mut self, other: &Self) -> Self::Output {
self._and_in_place(other).unwrap();
self
}
}
@ -102,7 +108,7 @@ impl<'a, const N: usize, T: PrimUInt, F: Field> BitAnd> for &'a UI
/// ```
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitand(self, other: UInt<N, T, F>) -> Self::Output {
self._and(&other).unwrap()
other & self
}
}
@ -133,7 +139,43 @@ impl BitAnd for UInt {
/// ```
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitand(self, other: Self) -> Self::Output {
self._and(&other).unwrap()
self & &other
}
}
impl<'a, const N: usize, T: PrimUInt, F: Field> BitAnd<T> for UInt<N, T, F> {
type Output = UInt<N, T, F>;
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitand(self, other: T) -> Self::Output {
self & UInt::constant(other)
}
}
impl<'a, const N: usize, T: PrimUInt, F: Field> BitAnd<&'a T> for UInt<N, T, F> {
type Output = UInt<N, T, F>;
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitand(self, other: &'a T) -> Self::Output {
self & UInt::constant(*other)
}
}
impl<'a, const N: usize, T: PrimUInt, F: Field> BitAnd<&'a T> for &'a UInt<N, T, F> {
type Output = UInt<N, T, F>;
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitand(self, other: &'a T) -> Self::Output {
self & UInt::constant(*other)
}
}
impl<'a, const N: usize, T: PrimUInt, F: Field> BitAnd<T> for &'a UInt<N, T, F> {
type Output = UInt<N, T, F>;
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitand(self, other: T) -> Self::Output {
self & UInt::constant(other)
}
}
@ -163,8 +205,7 @@ impl BitAndAssign for UInt
/// ```
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitand_assign(&mut self, other: Self) {
let result = self._and(&other).unwrap();
*self = result;
self._and_in_place(&other).unwrap();
}
}
@ -194,8 +235,21 @@ impl<'a, const N: usize, T: PrimUInt, F: Field> BitAndAssign<&'a Self> for UInt<
/// ```
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitand_assign(&mut self, other: &'a Self) {
let result = self._and(other).unwrap();
*self = result;
self._and_in_place(&other).unwrap();
}
}
impl<const N: usize, T: PrimUInt, F: Field> BitAndAssign<T> for UInt<N, T, F> {
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitand_assign(&mut self, other: T) {
*self &= &Self::constant(other);
}
}
impl<'a, const N: usize, T: PrimUInt, F: Field> BitAndAssign<&'a T> for UInt<N, T, F> {
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitand_assign(&mut self, other: &'a T) {
*self &= &Self::constant(*other);
}
}
@ -205,7 +259,7 @@ mod tests {
use crate::{
alloc::{AllocVar, AllocationMode},
prelude::EqGadget,
uint::test_utils::{run_binary_exhaustive, run_binary_random},
uint::test_utils::{run_binary_exhaustive_both, run_binary_random_both},
R1CSVar,
};
use ark_ff::PrimeField;
@ -236,28 +290,65 @@ mod tests {
Ok(())
}
fn uint_and_native<T: PrimUInt, const N: usize, F: PrimeField>(
a: UInt<N, T, F>,
b: T,
) -> Result<(), SynthesisError> {
let cs = a.cs();
let computed = &a & b;
let expected_mode = if a.is_constant() {
AllocationMode::Constant
} else {
AllocationMode::Witness
};
let expected =
UInt::<N, T, F>::new_variable(cs.clone(), || Ok(a.value()? & b), expected_mode)?;
assert_eq!(expected.value(), computed.value());
expected.enforce_equal(&computed)?;
if !a.is_constant() {
assert!(cs.is_satisfied().unwrap());
}
Ok(())
}
#[test]
fn u8_and() {
run_binary_exhaustive(uint_and::<u8, 8, Fr>).unwrap()
run_binary_exhaustive_both(uint_and::<u8, 8, Fr>, uint_and_native::<u8, 8, Fr>).unwrap()
}
#[test]
fn u16_and() {
run_binary_random::<1000, 16, _, _>(uint_and::<u16, 16, Fr>).unwrap()
run_binary_random_both::<1000, 16, _, _>(
uint_and::<u16, 16, Fr>,
uint_and_native::<u16, 16, Fr>,
)
.unwrap()
}
#[test]
fn u32_and() {
run_binary_random::<1000, 32, _, _>(uint_and::<u32, 32, Fr>).unwrap()
run_binary_random_both::<1000, 32, _, _>(
uint_and::<u32, 32, Fr>,
uint_and_native::<u32, 32, Fr>,
)
.unwrap()
}
#[test]
fn u64_and() {
run_binary_random::<1000, 64, _, _>(uint_and::<u64, 64, Fr>).unwrap()
run_binary_random_both::<1000, 64, _, _>(
uint_and::<u64, 64, Fr>,
uint_and_native::<u64, 64, Fr>,
)
.unwrap()
}
#[test]
fn u128_and() {
run_binary_random::<1000, 128, _, _>(uint_and::<u128, 128, Fr>).unwrap()
run_binary_random_both::<1000, 128, _, _>(
uint_and::<u128, 128, Fr>,
uint_and_native::<u128, 128, Fr>,
)
.unwrap()
}
}

+ 258
- 7
src/uint/convert.rs

@ -72,6 +72,71 @@ impl UInt {
let value = value_exists.then_some(value);
Self { bits, value }
}
/// Converts a big-endian list of bytes into a `UInt`.
///
/// ```
/// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> {
/// // We'll use the BLS12-381 scalar field for our constraints.
/// use ark_test_curves::bls12_381::Fr;
/// use ark_relations::r1cs::*;
/// use ark_r1cs_std::prelude::*;
///
/// let cs = ConstraintSystem::<Fr>::new_ref();
/// let var = UInt16::new_witness(cs.clone(), || Ok(2 * (u8::MAX as u16)))?;
///
/// // Construct u8::MAX * 2
/// let bytes = UInt8::constant_vec(&(2 * (u8::MAX as u16)).to_be_bytes());
///
/// let c = UInt16::from_bytes_be(&bytes)?;
/// var.enforce_equal(&c)?;
/// assert!(cs.is_satisfied().unwrap());
/// # Ok(())
/// # }
/// ```
pub fn from_bytes_be(bytes: &[UInt8<F>]) -> Result<Self, SynthesisError> {
let bits = bytes
.iter()
.rev()
.flat_map(|b| b.to_bits_le().unwrap())
.collect::<Vec<_>>();
Ok(Self::from_bits_le(&bits))
}
/// Converts a little-endian byte order list of bytes into a `UInt`.
///
/// ```
/// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> {
/// // We'll use the BLS12-381 scalar field for our constraints.
/// use ark_test_curves::bls12_381::Fr;
/// use ark_relations::r1cs::*;
/// use ark_r1cs_std::prelude::*;
///
/// let cs = ConstraintSystem::<Fr>::new_ref();
/// let var = UInt16::new_witness(cs.clone(), || Ok(2 * (u8::MAX as u16)))?;
///
/// // Construct u8::MAX * 2
/// let bytes = UInt8::constant_vec(&(2 * (u8::MAX as u16)).to_le_bytes());
///
/// let c = UInt16::from_bytes_le(&bytes)?;
/// var.enforce_equal(&c)?;
/// assert!(cs.is_satisfied().unwrap());
/// # Ok(())
/// # }
/// ```
pub fn from_bytes_le(bytes: &[UInt8<F>]) -> Result<Self, SynthesisError> {
let bits = bytes
.iter()
.flat_map(|b| b.to_bits_le().unwrap())
.collect::<Vec<_>>();
Ok(Self::from_bits_le(&bits))
}
pub fn to_bytes_be(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
let mut bytes = self.to_bytes_le()?;
bytes.reverse();
Ok(bytes)
}
}
impl<const N: usize, T: PrimUInt, F: Field> ToBitsGadget<F> for UInt<N, T, F> {
@ -97,7 +162,7 @@ impl ToBytesGadget
for UInt<N, T, ConstraintF>
{
#[tracing::instrument(target = "r1cs", skip(self))]
fn to_bytes(&self) -> Result<Vec<UInt8<ConstraintF>>, SynthesisError> {
fn to_bytes_le(&self) -> Result<Vec<UInt8<ConstraintF>>, SynthesisError> {
Ok(self
.to_bits_le()?
.chunks(8)
@ -107,23 +172,209 @@ impl ToBytesGadget
}
impl<const N: usize, T: PrimUInt, F: Field> ToBytesGadget<F> for [UInt<N, T, F>] {
fn to_bytes(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
fn to_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
let mut bytes = Vec::with_capacity(self.len() * (N / 8));
for elem in self {
bytes.extend_from_slice(&elem.to_bytes()?);
bytes.extend_from_slice(&elem.to_bytes_le()?);
}
Ok(bytes)
}
}
impl<const N: usize, T: PrimUInt, F: Field> ToBytesGadget<F> for Vec<UInt<N, T, F>> {
fn to_bytes(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
self.as_slice().to_bytes()
fn to_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
self.as_slice().to_bytes_le()
}
}
impl<'a, const N: usize, T: PrimUInt, F: Field> ToBytesGadget<F> for &'a [UInt<N, T, F>] {
fn to_bytes(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
(*self).to_bytes()
fn to_bytes_le(&self) -> Result<Vec<UInt8<F>>, SynthesisError> {
(*self).to_bytes_le()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
prelude::EqGadget,
uint::test_utils::{run_unary_exhaustive, run_unary_random},
R1CSVar,
};
use ark_ff::PrimeField;
use ark_test_curves::bls12_381::Fr;
fn uint_to_bytes_le<T: PrimUInt, const N: usize, F: PrimeField>(
a: UInt<N, T, F>,
) -> Result<(), SynthesisError> {
let cs = a.cs();
let computed = a.to_bytes_le()?;
let expected = UInt8::constant_vec(a.value()?.to_le_bytes().as_ref());
assert_eq!(expected.len(), computed.len());
assert_eq!(expected.value(), computed.value());
expected.enforce_equal(&computed)?;
if !a.is_constant() {
assert!(cs.is_satisfied().unwrap());
}
Ok(())
}
fn uint_to_bytes_be<T: PrimUInt, const N: usize, F: PrimeField>(
a: UInt<N, T, F>,
) -> Result<(), SynthesisError> {
let cs = a.cs();
let computed = a.to_bytes_be()?;
let expected = UInt8::constant_vec(a.value()?.to_be_bytes().as_ref());
assert_eq!(expected.len(), computed.len());
assert_eq!(expected.value(), computed.value());
expected.enforce_equal(&computed)?;
if !a.is_constant() {
assert!(cs.is_satisfied().unwrap());
}
Ok(())
}
fn uint_from_bytes_le<T: PrimUInt, const N: usize, F: PrimeField>(
expected: UInt<N, T, F>,
) -> Result<(), SynthesisError> {
let cs = expected.cs();
let mode = if expected.is_constant() {
AllocationMode::Constant
} else {
AllocationMode::Witness
};
let computed = {
let value = expected.value()?.to_le_bytes();
let a = Vec::<UInt8<F>>::new_variable(cs.clone(), || Ok(value.as_ref()), mode)?;
UInt::from_bytes_le(&a)?
};
assert_eq!(expected.value(), computed.value());
expected.enforce_equal(&computed)?;
if !expected.is_constant() {
assert!(cs.is_satisfied().unwrap());
}
Ok(())
}
fn uint_from_bytes_be<T: PrimUInt, const N: usize, F: PrimeField>(
expected: UInt<N, T, F>,
) -> Result<(), SynthesisError> {
let cs = expected.cs();
let mode = if expected.is_constant() {
AllocationMode::Constant
} else {
AllocationMode::Witness
};
let computed = {
let value = expected.value()?.to_be_bytes();
let a = Vec::<UInt8<F>>::new_variable(cs.clone(), || Ok(value.as_ref()), mode)?;
UInt::from_bytes_be(&a)?
};
assert_eq!(expected.value(), computed.value());
expected.enforce_equal(&computed)?;
if !expected.is_constant() {
assert!(cs.is_satisfied().unwrap());
}
Ok(())
}
#[test]
fn u8_to_bytes_le() {
run_unary_exhaustive(uint_to_bytes_le::<u8, 8, Fr>).unwrap()
}
#[test]
fn u16_to_bytes_le() {
run_unary_random::<1000, 16, _, _>(uint_to_bytes_le::<u16, 16, Fr>).unwrap()
}
#[test]
fn u32_to_bytes_le() {
run_unary_random::<1000, 32, _, _>(uint_to_bytes_le::<u32, 32, Fr>).unwrap()
}
#[test]
fn u64_to_bytes_le() {
run_unary_random::<1000, 64, _, _>(uint_to_bytes_le::<u64, 64, Fr>).unwrap()
}
#[test]
fn u128_to_bytes_le() {
run_unary_random::<1000, 128, _, _>(uint_to_bytes_le::<u128, 128, Fr>).unwrap()
}
#[test]
fn u8_to_bytes_be() {
run_unary_exhaustive(uint_to_bytes_be::<u8, 8, Fr>).unwrap()
}
#[test]
fn u16_to_bytes_be() {
run_unary_random::<1000, 16, _, _>(uint_to_bytes_be::<u16, 16, Fr>).unwrap()
}
#[test]
fn u32_to_bytes_be() {
run_unary_random::<1000, 32, _, _>(uint_to_bytes_be::<u32, 32, Fr>).unwrap()
}
#[test]
fn u64_to_bytes_be() {
run_unary_random::<1000, 64, _, _>(uint_to_bytes_be::<u64, 64, Fr>).unwrap()
}
#[test]
fn u128_to_bytes_be() {
run_unary_random::<1000, 128, _, _>(uint_to_bytes_be::<u128, 128, Fr>).unwrap()
}
#[test]
fn u8_from_bytes_le() {
run_unary_exhaustive(uint_from_bytes_le::<u8, 8, Fr>).unwrap()
}
#[test]
fn u16_from_bytes_le() {
run_unary_random::<1000, 16, _, _>(uint_from_bytes_le::<u16, 16, Fr>).unwrap()
}
#[test]
fn u32_from_bytes_le() {
run_unary_random::<1000, 32, _, _>(uint_from_bytes_le::<u32, 32, Fr>).unwrap()
}
#[test]
fn u64_from_bytes_le() {
run_unary_random::<1000, 64, _, _>(uint_from_bytes_le::<u64, 64, Fr>).unwrap()
}
#[test]
fn u128_from_bytes_le() {
run_unary_random::<1000, 128, _, _>(uint_from_bytes_le::<u128, 128, Fr>).unwrap()
}
#[test]
fn u8_from_bytes_be() {
run_unary_exhaustive(uint_from_bytes_be::<u8, 8, Fr>).unwrap()
}
#[test]
fn u16_from_bytes_be() {
run_unary_random::<1000, 16, _, _>(uint_from_bytes_be::<u16, 16, Fr>).unwrap()
}
#[test]
fn u32_from_bytes_be() {
run_unary_random::<1000, 32, _, _>(uint_from_bytes_be::<u32, 32, Fr>).unwrap()
}
#[test]
fn u64_from_bytes_be() {
run_unary_random::<1000, 64, _, _>(uint_from_bytes_be::<u64, 64, Fr>).unwrap()
}
#[test]
fn u128_from_bytes_be() {
run_unary_random::<1000, 128, _, _>(uint_from_bytes_be::<u128, 128, Fr>).unwrap()
}
}

+ 12
- 6
src/uint/not.rs

@ -7,12 +7,17 @@ use super::*;
impl<const N: usize, T: PrimUInt, F: Field> UInt<N, T, F> {
fn _not(&self) -> Result<Self, SynthesisError> {
let mut result = self.clone();
for a in &mut result.bits {
*a = !&*a
}
result.value = self.value.map(Not::not);
result._not_in_place()?;
Ok(result)
}
fn _not_in_place(&mut self) -> Result<(), SynthesisError> {
for a in &mut self.bits {
a.not_in_place()?;
}
self.value = self.value.map(Not::not);
Ok(())
}
}
impl<'a, const N: usize, T: PrimUInt, F: Field> Not for &'a UInt<N, T, F> {
@ -67,8 +72,9 @@ impl<'a, const N: usize, T: PrimUInt, F: Field> Not for UInt {
/// # }
/// ```
#[tracing::instrument(target = "r1cs", skip(self))]
fn not(self) -> Self::Output {
self._not().unwrap()
fn not(mut self) -> Self::Output {
self._not_in_place().unwrap();
self
}
}

+ 108
- 17
src/uint/or.rs

@ -7,11 +7,16 @@ use super::{PrimUInt, UInt};
impl<const N: usize, T: PrimUInt, F: PrimeField> UInt<N, T, F> {
fn _or(&self, other: &Self) -> Result<Self, SynthesisError> {
let mut result = self.clone();
for (a, b) in result.bits.iter_mut().zip(&other.bits) {
result._or_in_place(other)?;
Ok(result)
}
fn _or_in_place(&mut self, other: &Self) -> Result<(), SynthesisError> {
for (a, b) in self.bits.iter_mut().zip(&other.bits) {
*a |= b;
}
result.value = self.value.and_then(|a| Some(a | other.value?));
Ok(result)
self.value = self.value.and_then(|a| Some(a | other.value?));
Ok(())
}
}
@ -50,8 +55,9 @@ impl<'a, const N: usize, T: PrimUInt, F: PrimeField> BitOr<&'a Self> for UInt
type Output = UInt<N, T, F>;
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitor(self, other: &Self) -> Self::Output {
self._or(&other).unwrap()
fn bitor(mut self, other: &Self) -> Self::Output {
self._or_in_place(&other).unwrap();
self
}
}
@ -60,7 +66,7 @@ impl<'a, const N: usize, T: PrimUInt, F: PrimeField> BitOr> for &'
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitor(self, other: UInt<N, T, F>) -> Self::Output {
self._or(&other).unwrap()
other | self
}
}
@ -69,7 +75,43 @@ impl BitOr for UInt {
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitor(self, other: Self) -> Self::Output {
self._or(&other).unwrap()
self | &other
}
}
impl<'a, const N: usize, T: PrimUInt, F: PrimeField> BitOr<T> for UInt<N, T, F> {
type Output = UInt<N, T, F>;
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitor(self, other: T) -> Self::Output {
self | &UInt::constant(other)
}
}
impl<'a, const N: usize, T: PrimUInt, F: PrimeField> BitOr<&'a T> for UInt<N, T, F> {
type Output = UInt<N, T, F>;
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitor(self, other: &'a T) -> Self::Output {
self | &UInt::constant(*other)
}
}
impl<'a, const N: usize, T: PrimUInt, F: PrimeField> BitOr<&'a T> for &'a UInt<N, T, F> {
type Output = UInt<N, T, F>;
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitor(self, other: &'a T) -> Self::Output {
self | &UInt::constant(*other)
}
}
impl<'a, const N: usize, T: PrimUInt, F: PrimeField> BitOr<T> for &'a UInt<N, T, F> {
type Output = UInt<N, T, F>;
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitor(self, other: T) -> Self::Output {
self | &UInt::constant(other)
}
}
@ -99,16 +141,28 @@ impl BitOrAssign for UInt
/// ```
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitor_assign(&mut self, other: Self) {
let result = self._or(&other).unwrap();
*self = result;
self._or_in_place(&other).unwrap();
}
}
impl<'a, const N: usize, T: PrimUInt, F: PrimeField> BitOrAssign<&'a Self> for UInt<N, T, F> {
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitor_assign(&mut self, other: &'a Self) {
let result = self._or(other).unwrap();
*self = result;
self._or_in_place(other).unwrap();
}
}
impl<const N: usize, T: PrimUInt, F: PrimeField> BitOrAssign<T> for UInt<N, T, F> {
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitor_assign(&mut self, other: T) {
*self |= &UInt::constant(other);
}
}
impl<'a, const N: usize, T: PrimUInt, F: PrimeField> BitOrAssign<&'a T> for UInt<N, T, F> {
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitor_assign(&mut self, other: &'a T) {
*self |= &UInt::constant(*other);
}
}
@ -118,7 +172,7 @@ mod tests {
use crate::{
alloc::{AllocVar, AllocationMode},
prelude::EqGadget,
uint::test_utils::{run_binary_exhaustive, run_binary_random},
uint::test_utils::{run_binary_exhaustive_both, run_binary_random_both},
R1CSVar,
};
use ark_ff::PrimeField;
@ -149,28 +203,65 @@ mod tests {
Ok(())
}
fn uint_or_native<T: PrimUInt, const N: usize, F: PrimeField>(
a: UInt<N, T, F>,
b: T,
) -> Result<(), SynthesisError> {
let cs = a.cs();
let computed = &a | &b;
let expected_mode = if a.is_constant() {
AllocationMode::Constant
} else {
AllocationMode::Witness
};
let expected =
UInt::<N, T, F>::new_variable(cs.clone(), || Ok(a.value()? | b), expected_mode)?;
assert_eq!(expected.value(), computed.value());
expected.enforce_equal(&computed)?;
if !a.is_constant() {
assert!(cs.is_satisfied().unwrap());
}
Ok(())
}
#[test]
fn u8_or() {
run_binary_exhaustive(uint_or::<u8, 8, Fr>).unwrap()
run_binary_exhaustive_both(uint_or::<u8, 8, Fr>, uint_or_native::<u8, 8, Fr>).unwrap()
}
#[test]
fn u16_or() {
run_binary_random::<1000, 16, _, _>(uint_or::<u16, 16, Fr>).unwrap()
run_binary_random_both::<1000, 16, _, _>(
uint_or::<u16, 16, Fr>,
uint_or_native::<u16, 16, Fr>,
)
.unwrap()
}
#[test]
fn u32_or() {
run_binary_random::<1000, 32, _, _>(uint_or::<u32, 32, Fr>).unwrap()
run_binary_random_both::<1000, 32, _, _>(
uint_or::<u32, 32, Fr>,
uint_or_native::<u32, 32, Fr>,
)
.unwrap()
}
#[test]
fn u64_or() {
run_binary_random::<1000, 64, _, _>(uint_or::<u64, 64, Fr>).unwrap()
run_binary_random_both::<1000, 64, _, _>(
uint_or::<u64, 64, Fr>,
uint_or_native::<u64, 64, Fr>,
)
.unwrap()
}
#[test]
fn u128_or() {
run_binary_random::<1000, 128, _, _>(uint_or::<u128, 128, Fr>).unwrap()
run_binary_random_both::<1000, 128, _, _>(
uint_or::<u128, 128, Fr>,
uint_or_native::<u128, 128, Fr>,
)
.unwrap()
}
}

+ 56
- 8
src/uint/rotate.rs

@ -22,13 +22,37 @@ impl UInt {
/// ```
#[tracing::instrument(target = "r1cs", skip(self))]
pub fn rotate_right(&self, by: usize) -> Self {
let by = by % N;
let mut result = self.clone();
// `[T]::rotate_left` corresponds to a `rotate_right` of the bits.
result.bits.rotate_left(by);
result.value = self.value.map(|v| v.rotate_right(by as u32));
result.rotate_right_in_place(by);
result
}
/// Rotates `self` to the right *in place* by `by` steps, wrapping around.
///
/// # Examples
/// ```
/// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> {
/// // We'll use the BLS12-381 scalar field for our constraints.
/// use ark_test_curves::bls12_381::Fr;
/// use ark_relations::r1cs::*;
/// use ark_r1cs_std::prelude::*;
///
/// let cs = ConstraintSystem::<Fr>::new_ref();
/// let mut a = UInt32::new_witness(cs.clone(), || Ok(0xb301u32))?;
/// let b = UInt32::new_witness(cs.clone(), || Ok(0x10000b3))?;
///
/// a.rotate_right_in_place(8);
/// a.enforce_equal(&b)?;
/// assert!(cs.is_satisfied().unwrap());
/// # Ok(())
/// # }
/// ```
#[tracing::instrument(target = "r1cs", skip(self))]
pub fn rotate_right_in_place(&mut self, by: usize) {
let by = by % N;
// `[T]::rotate_left` corresponds to a `rotate_right` of the bits.
self.bits.rotate_left(by);
self.value = self.value.map(|v| v.rotate_right(by as u32));
}
/// Rotates `self` to the left by `by` steps, wrapping around.
///
@ -51,13 +75,37 @@ impl UInt {
/// ```
#[tracing::instrument(target = "r1cs", skip(self))]
pub fn rotate_left(&self, by: usize) -> Self {
let by = by % N;
let mut result = self.clone();
// `[T]::rotate_right` corresponds to a `rotate_left` of the bits.
result.bits.rotate_right(by);
result.value = self.value.map(|v| v.rotate_left(by as u32));
result.rotate_left_in_place(by);
result
}
/// Rotates `self` to the left *in place* by `by` steps, wrapping around.
///
/// # Examples
/// ```
/// # fn main() -> Result<(), ark_relations::r1cs::SynthesisError> {
/// // We'll use the BLS12-381 scalar field for our constraints.
/// use ark_test_curves::bls12_381::Fr;
/// use ark_relations::r1cs::*;
/// use ark_r1cs_std::prelude::*;
///
/// let cs = ConstraintSystem::<Fr>::new_ref();
/// let mut a = UInt32::new_witness(cs.clone(), || Ok(0x10000b3))?;
/// let b = UInt32::new_witness(cs.clone(), || Ok(0xb301u32))?;
///
/// a.rotate_left_in_place(8);
/// a.enforce_equal(&b)?;
/// assert!(cs.is_satisfied().unwrap());
/// # Ok(())
/// # }
/// ```
pub fn rotate_left_in_place(&mut self, by: usize) {
let by = by % N;
// `[T]::rotate_right` corresponds to a `rotate_left` of the bits.
self.bits.rotate_right(by);
self.value = self.value.map(|v| v.rotate_left(by as u32));
}
}
#[cfg(test)]

+ 6
- 6
src/uint/shl.rs

@ -99,7 +99,7 @@ mod tests {
use crate::{
alloc::{AllocVar, AllocationMode},
prelude::EqGadget,
uint::test_utils::{run_binary_exhaustive_with_native, run_binary_random_with_native},
uint::test_utils::{run_binary_exhaustive_native_only, run_binary_random_native_only},
R1CSVar,
};
use ark_ff::PrimeField;
@ -129,26 +129,26 @@ mod tests {
#[test]
fn u8_shl() {
run_binary_exhaustive_with_native(uint_shl::<u8, 8, Fr>).unwrap()
run_binary_exhaustive_native_only(uint_shl::<u8, 8, Fr>).unwrap()
}
#[test]
fn u16_shl() {
run_binary_random_with_native::<1000, 16, _, _>(uint_shl::<u16, 16, Fr>).unwrap()
run_binary_random_native_only::<1000, 16, _, _>(uint_shl::<u16, 16, Fr>).unwrap()
}
#[test]
fn u32_shl() {
run_binary_random_with_native::<1000, 32, _, _>(uint_shl::<u32, 32, Fr>).unwrap()
run_binary_random_native_only::<1000, 32, _, _>(uint_shl::<u32, 32, Fr>).unwrap()
}
#[test]
fn u64_shl() {
run_binary_random_with_native::<1000, 64, _, _>(uint_shl::<u64, 64, Fr>).unwrap()
run_binary_random_native_only::<1000, 64, _, _>(uint_shl::<u64, 64, Fr>).unwrap()
}
#[test]
fn u128_shl() {
run_binary_random_with_native::<1000, 128, _, _>(uint_shl::<u128, 128, Fr>).unwrap()
run_binary_random_native_only::<1000, 128, _, _>(uint_shl::<u128, 128, Fr>).unwrap()
}
}

+ 6
- 6
src/uint/shr.rs

@ -99,7 +99,7 @@ mod tests {
use crate::{
alloc::{AllocVar, AllocationMode},
prelude::EqGadget,
uint::test_utils::{run_binary_exhaustive_with_native, run_binary_random_with_native},
uint::test_utils::{run_binary_exhaustive_native_only, run_binary_random_native_only},
R1CSVar,
};
use ark_ff::PrimeField;
@ -129,26 +129,26 @@ mod tests {
#[test]
fn u8_shr() {
run_binary_exhaustive_with_native(uint_shr::<u8, 8, Fr>).unwrap()
run_binary_exhaustive_native_only(uint_shr::<u8, 8, Fr>).unwrap()
}
#[test]
fn u16_shr() {
run_binary_random_with_native::<1000, 16, _, _>(uint_shr::<u16, 16, Fr>).unwrap()
run_binary_random_native_only::<1000, 16, _, _>(uint_shr::<u16, 16, Fr>).unwrap()
}
#[test]
fn u32_shr() {
run_binary_random_with_native::<1000, 32, _, _>(uint_shr::<u32, 32, Fr>).unwrap()
run_binary_random_native_only::<1000, 32, _, _>(uint_shr::<u32, 32, Fr>).unwrap()
}
#[test]
fn u64_shr() {
run_binary_random_with_native::<1000, 64, _, _>(uint_shr::<u64, 64, Fr>).unwrap()
run_binary_random_native_only::<1000, 64, _, _>(uint_shr::<u64, 64, Fr>).unwrap()
}
#[test]
fn u128_shr() {
run_binary_random_with_native::<1000, 128, _, _>(uint_shr::<u128, 128, Fr>).unwrap()
run_binary_random_native_only::<1000, 128, _, _>(uint_shr::<u128, 128, Fr>).unwrap()
}
}

+ 43
- 2
src/uint/test_utils.rs

@ -39,6 +39,29 @@ pub(crate) fn test_binary_op_with_native
test(a, b)
}
pub(crate) fn run_binary_random_both<const ITERATIONS: usize, const N: usize, T, F>(
test: impl Fn(UInt<N, T, F>, UInt<N, T, F>) -> Result<(), SynthesisError> + Copy,
test_native: impl Fn(UInt<N, T, F>, T) -> Result<(), SynthesisError> + Copy,
) -> Result<(), SynthesisError>
where
T: PrimUInt,
F: PrimeField,
{
let mut rng = ark_std::test_rng();
for _ in 0..ITERATIONS {
for mode_a in modes() {
let a = T::rand(&mut rng);
for mode_b in modes() {
let b = T::rand(&mut rng);
test_binary_op(a, b, mode_a, mode_b, test)?;
test_binary_op_with_native(a, b, mode_a, test_native)?;
}
}
}
Ok(())
}
pub(crate) fn run_binary_random<const ITERATIONS: usize, const N: usize, T, F>(
test: impl Fn(UInt<N, T, F>, UInt<N, T, F>) -> Result<(), SynthesisError> + Copy,
) -> Result<(), SynthesisError>
@ -76,7 +99,25 @@ where
Ok(())
}
pub(crate) fn run_binary_random_with_native<const ITERATIONS: usize, const N: usize, T, F>(
pub(crate) fn run_binary_exhaustive_both<const N: usize, T, F>(
test: impl Fn(UInt<N, T, F>, UInt<N, T, F>) -> Result<(), SynthesisError> + Copy,
test_native: impl Fn(UInt<N, T, F>, T) -> Result<(), SynthesisError> + Copy,
) -> Result<(), SynthesisError>
where
T: PrimUInt,
F: PrimeField,
RangeInclusive<T>: Iterator<Item = T>,
{
for (mode_a, a) in test_utils::combination(T::min_value()..=T::max_value()) {
for (mode_b, b) in test_utils::combination(T::min_value()..=T::max_value()) {
test_binary_op(a, b, mode_a, mode_b, test)?;
test_binary_op_with_native(a, b, mode_a, test_native)?;
}
}
Ok(())
}
pub(crate) fn run_binary_random_native_only<const ITERATIONS: usize, const N: usize, T, F>(
test: impl Fn(UInt<N, T, F>, T) -> Result<(), SynthesisError> + Copy,
) -> Result<(), SynthesisError>
where
@ -95,7 +136,7 @@ where
Ok(())
}
pub(crate) fn run_binary_exhaustive_with_native<const N: usize, T, F>(
pub(crate) fn run_binary_exhaustive_native_only<const N: usize, T, F>(
test: impl Fn(UInt<N, T, F>, T) -> Result<(), SynthesisError> + Copy,
) -> Result<(), SynthesisError>
where

+ 108
- 17
src/uint/xor.rs

@ -7,11 +7,16 @@ use super::*;
impl<const N: usize, T: PrimUInt, F: Field> UInt<N, T, F> {
fn _xor(&self, other: &Self) -> Result<Self, SynthesisError> {
let mut result = self.clone();
for (a, b) in result.bits.iter_mut().zip(&other.bits) {
result._xor_in_place(other)?;
Ok(result)
}
fn _xor_in_place(&mut self, other: &Self) -> Result<(), SynthesisError> {
for (a, b) in self.bits.iter_mut().zip(&other.bits) {
*a ^= b;
}
result.value = self.value.and_then(|a| Some(a ^ other.value?));
Ok(result)
self.value = self.value.and_then(|a| Some(a ^ other.value?));
Ok(())
}
}
@ -49,8 +54,9 @@ impl<'a, const N: usize, T: PrimUInt, F: Field> BitXor<&'a Self> for UInt
type Output = UInt<N, T, F>;
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitxor(self, other: &Self) -> Self::Output {
self._xor(&other).unwrap()
fn bitxor(mut self, other: &Self) -> Self::Output {
self._xor_in_place(&other).unwrap();
self
}
}
@ -59,7 +65,7 @@ impl<'a, const N: usize, T: PrimUInt, F: Field> BitXor> for &'a UI
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitxor(self, other: UInt<N, T, F>) -> Self::Output {
self._xor(&other).unwrap()
other ^ self
}
}
@ -68,7 +74,43 @@ impl BitXor for UInt {
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitxor(self, other: Self) -> Self::Output {
self._xor(&other).unwrap()
self ^ &other
}
}
impl<'a, const N: usize, T: PrimUInt, F: Field> BitXor<T> for UInt<N, T, F> {
type Output = UInt<N, T, F>;
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitxor(self, other: T) -> Self::Output {
self ^ &UInt::constant(other)
}
}
impl<'a, const N: usize, T: PrimUInt, F: Field> BitXor<&'a T> for UInt<N, T, F> {
type Output = UInt<N, T, F>;
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitxor(self, other: &'a T) -> Self::Output {
self ^ &UInt::constant(*other)
}
}
impl<'a, const N: usize, T: PrimUInt, F: Field> BitXor<&'a T> for &'a UInt<N, T, F> {
type Output = UInt<N, T, F>;
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitxor(self, other: &'a T) -> Self::Output {
self ^ UInt::constant(*other)
}
}
impl<'a, const N: usize, T: PrimUInt, F: Field> BitXor<T> for &'a UInt<N, T, F> {
type Output = UInt<N, T, F>;
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitxor(self, other: T) -> Self::Output {
self ^ UInt::constant(other)
}
}
@ -98,16 +140,28 @@ impl BitXorAssign for UInt
/// ```
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitxor_assign(&mut self, other: Self) {
let result = self._xor(&other).unwrap();
*self = result;
self._xor_in_place(&other).unwrap();
}
}
impl<'a, const N: usize, T: PrimUInt, F: Field> BitXorAssign<&'a Self> for UInt<N, T, F> {
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitxor_assign(&mut self, other: &'a Self) {
let result = self._xor(other).unwrap();
*self = result;
self._xor_in_place(other).unwrap();
}
}
impl<const N: usize, T: PrimUInt, F: Field> BitXorAssign<T> for UInt<N, T, F> {
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitxor_assign(&mut self, other: T) {
*self ^= Self::constant(other);
}
}
impl<'a, const N: usize, T: PrimUInt, F: Field> BitXorAssign<&'a T> for UInt<N, T, F> {
#[tracing::instrument(target = "r1cs", skip(self, other))]
fn bitxor_assign(&mut self, other: &'a T) {
*self ^= Self::constant(*other);
}
}
@ -117,7 +171,7 @@ mod tests {
use crate::{
alloc::{AllocVar, AllocationMode},
prelude::EqGadget,
uint::test_utils::{run_binary_exhaustive, run_binary_random},
uint::test_utils::{run_binary_exhaustive_both, run_binary_random_both},
R1CSVar,
};
use ark_ff::PrimeField;
@ -148,28 +202,65 @@ mod tests {
Ok(())
}
fn uint_xor_native<T: PrimUInt, const N: usize, F: PrimeField>(
a: UInt<N, T, F>,
b: T,
) -> Result<(), SynthesisError> {
let cs = a.cs();
let computed = &a ^ &b;
let expected_mode = if a.is_constant() {
AllocationMode::Constant
} else {
AllocationMode::Witness
};
let expected =
UInt::<N, T, F>::new_variable(cs.clone(), || Ok(a.value()? ^ b), expected_mode)?;
assert_eq!(expected.value(), computed.value());
expected.enforce_equal(&computed)?;
if !a.is_constant() {
assert!(cs.is_satisfied().unwrap());
}
Ok(())
}
#[test]
fn u8_xor() {
run_binary_exhaustive(uint_xor::<u8, 8, Fr>).unwrap()
run_binary_exhaustive_both(uint_xor::<u8, 8, Fr>, uint_xor_native::<u8, 8, Fr>).unwrap()
}
#[test]
fn u16_xor() {
run_binary_random::<1000, 16, _, _>(uint_xor::<u16, 16, Fr>).unwrap()
run_binary_random_both::<1000, 16, _, _>(
uint_xor::<u16, 16, Fr>,
uint_xor_native::<u16, 16, Fr>,
)
.unwrap()
}
#[test]
fn u32_xor() {
run_binary_random::<1000, 32, _, _>(uint_xor::<u32, 32, Fr>).unwrap()
run_binary_random_both::<1000, 32, _, _>(
uint_xor::<u32, 32, Fr>,
uint_xor_native::<u32, 32, Fr>,
)
.unwrap()
}
#[test]
fn u64_xor() {
run_binary_random::<1000, 64, _, _>(uint_xor::<u64, 64, Fr>).unwrap()
run_binary_random_both::<1000, 64, _, _>(
uint_xor::<u64, 64, Fr>,
uint_xor_native::<u64, 64, Fr>,
)
.unwrap()
}
#[test]
fn u128_xor() {
run_binary_random::<1000, 128, _, _>(uint_xor::<u128, 128, Fr>).unwrap()
run_binary_random_both::<1000, 128, _, _>(
uint_xor::<u128, 128, Fr>,
uint_xor_native::<u128, 128, Fr>,
)
.unwrap()
}
}

Loading…
Cancel
Save