arnaucube
arnaucube/ark-r1cs-stdPublic Self-hosted repository
Add back `ToBytesGadget` and `ToBitsGadget` to `prelude` (#136)

Pratyush Mishra pratyush795@gmail.com
Sat, 06 Jan 2024 16:51:55 -0500
commit

a12499518c0d66a78f16580ece82b66cd1aaf050

parent

d011859416a2cdc4cc0b5868c505a5aaf1fb81f9

M CHANGELOG.md → CHANGELOG.md

@@ -4,13 +4,25 @@ ## Pending

### 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
M src/boolean/convert.rs → src/boolean/convert.rs

@@ -4,7 +4,7 @@

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();
M src/boolean/mod.rs → src/boolean/mod.rs

@@ -215,7 +215,7 @@ fn test_boolean_to_byte() -> Result<(), SynthesisError> {

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);
M src/boolean/not.rs → 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(()) } }
M src/cmp.rs → 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) }
M src/convert.rs → src/convert.rs

@@ -69,20 +69,24 @@ pub trait ToBytesGadget<F: Field> {

/// 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() } }
M src/fields/cubic_extension.rs → src/fields/cubic_extension.rs

@@ -435,10 +435,10 @@ for<'a> &'a BF: FieldOpsBounds<'a, P::BaseField, BF>,

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 @@ 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()?; - 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);
M src/fields/emulated_fp/allocated_field_var.rs → src/fields/emulated_fp/allocated_field_var.rs

@@ -681,7 +681,7 @@ impl<TargetF: PrimeField, BaseF: PrimeField> ToBytesGadget<BaseF>

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;
M src/fields/emulated_fp/field_var.rs → src/fields/emulated_fp/field_var.rs

@@ -297,23 +297,23 @@ {

/// 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(), } } }
M src/fields/fp/mod.rs → src/fields/fp/mod.rs

@@ -552,7 +552,7 @@ ///

/// 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 @@ Ok(bytes)

} #[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<F: PrimeField> ToBytesGadget<F> for FpVar<F> {

/// 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(), } } }
M src/fields/quadratic_extension.rs → src/fields/quadratic_extension.rs

@@ -435,17 +435,17 @@ for<'b> &'b BF: FieldOpsBounds<'b, P::BaseField, BF>,

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) }
M src/groups/curves/short_weierstrass/bls12/mod.rs → src/groups/curves/short_weierstrass/bls12/mod.rs

@@ -79,20 +79,20 @@

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<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) }
M src/groups/curves/short_weierstrass/mnt4/mod.rs → src/groups/curves/short_weierstrass/mnt4/mod.rs

@@ -105,11 +105,11 @@

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 @@ 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_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<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<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 @@ Ok(c_h)

} #[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<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)
M src/groups/curves/short_weierstrass/mnt6/mod.rs → src/groups/curves/short_weierstrass/mnt6/mod.rs

@@ -105,11 +105,11 @@

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 @@ 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_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<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<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 @@ Ok(c_h)

} #[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<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)
M src/groups/curves/short_weierstrass/mod.rs → src/groups/curves/short_weierstrass/mod.rs

@@ -953,22 +953,22 @@ F: FieldVar<P::BaseField, BasePrimeField<P>>,

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)
M src/groups/curves/twisted_edwards/mod.rs → src/groups/curves/twisted_edwards/mod.rs

@@ -920,17 +920,17 @@ F: FieldVar<P::BaseField, BasePrimeField<P>>,

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)
M src/lib.rs → 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},
M src/uint/and.rs → src/uint/and.rs

@@ -7,11 +7,16 @@

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 @@ /// # Ok(())

/// # } /// ``` #[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 @@ /// # }

/// ``` #[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 @@ /// # }

/// ``` #[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 @@ /// # }

/// ``` #[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 @@ /// # }

/// ``` #[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 @@ use super::*;

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 @@ }

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() } }
M src/uint/convert.rs → src/uint/convert.rs

@@ -72,6 +72,71 @@ }

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<const N: usize, T: PrimUInt, ConstraintF: Field> ToBytesGadget<ConstraintF>

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<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() } }
M src/uint/not.rs → src/uint/not.rs

@@ -7,11 +7,16 @@

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._not_in_place()?; + Ok(result) + } + + fn _not_in_place(&mut self) -> Result<(), SynthesisError> { + for a in &mut self.bits { + a.not_in_place()?; } - result.value = self.value.map(Not::not); - Ok(result) + self.value = self.value.map(Not::not); + Ok(()) } }

@@ -67,8 +72,9 @@ /// # Ok(())

/// # } /// ``` #[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 } }
M src/uint/or.rs → src/uint/or.rs

@@ -7,11 +7,16 @@

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<N, T, F> {

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 @@ type Output = UInt<N, T, F>;

#[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 @@ type Output = Self;

#[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 @@ /// # }

/// ``` #[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 @@ use super::*;

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 @@ }

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() } }
M src/uint/rotate.rs → src/uint/rotate.rs

@@ -22,12 +22,36 @@ /// # }

/// ``` #[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,12 +75,36 @@ /// # }

/// ``` #[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)); } }
M src/uint/shl.rs → src/uint/shl.rs

@@ -99,7 +99,7 @@ use super::*;

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 @@ }

#[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() } }
M src/uint/shr.rs → src/uint/shr.rs

@@ -99,7 +99,7 @@ use super::*;

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 @@ }

#[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() } }
M src/uint/test_utils.rs → src/uint/test_utils.rs

@@ -39,6 +39,29 @@ let a = UInt::<N, T, F>::new_variable(cs.clone(), || Ok(a), mode_a)?;

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 @@ }

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 @@ }

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
M src/uint/xor.rs → src/uint/xor.rs

@@ -7,11 +7,16 @@

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<N, T, F> {

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 @@ type Output = UInt<N, T, F>;

#[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 @@ type Output = Self;

#[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 @@ /// # }

/// ``` #[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 @@ use super::*;

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 @@ }

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() } }