Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add back ToBytesGadget and ToBitsGadget to prelude #136

Merged
merged 8 commits into from
Jan 6, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add big-endian conversions for UInt
Pratyush committed Jan 3, 2024
commit 24217f2e29a6b83334d5ea67915f67b31580c1de
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
257 changes: 257 additions & 0 deletions src/uint/convert.rs
Original file line number Diff line number Diff line change
@@ -72,6 +72,77 @@ impl<const N: usize, F: Field, T: PrimUInt> UInt<N, T, F> {
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)))?;
///
/// let f = Boolean::FALSE;
/// let t = Boolean::TRUE;
///
/// // Construct u8::MAX * 2
/// let bytes = UInt16::constant_vec(&(2 * (u8::MAX as u16)).to_be_bytes());
///
/// let c = UInt16::from_bytes_be(&bits);
/// 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)))?;
///
/// let f = Boolean::FALSE;
/// let t = Boolean::TRUE;
///
/// // Construct u8::MAX * 2
/// let bytes = UInt16::constant_vec(&(2 * (u8::MAX as u16)).to_le_bytes());
///
/// let c = UInt16::from_bytes_le(&bits);
/// 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> {
@@ -127,3 +198,189 @@ impl<'a, const N: usize, T: PrimUInt, F: Field> ToBytesGadget<F> for &'a [UInt<N
(*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()
}
}