You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

380 lines
12 KiB

use crate::convert::*;
use crate::fields::fp::FpVar;
use super::*;
impl<const N: usize, F: Field, T: PrimUInt> UInt<N, T, F> {
/// Converts `self` into a field element. The elements comprising `self` are
/// interpreted as a little-endian bit order representation of a field element.
///
/// # Panics
/// Assumes that `N` is equal to at most the number of bits in `F::MODULUS_BIT_SIZE - 1`, and panics otherwise.
pub fn to_fp(&self) -> Result<FpVar<F>, SynthesisError>
where
F: PrimeField,
{
assert!(N <= F::MODULUS_BIT_SIZE as usize - 1);
Boolean::le_bits_to_fp(&self.bits)
}
/// Converts a field element into its little-endian bit order representation.
///
/// # Panics
///
/// Assumes that `N` is at most the number of bits in `F::MODULUS_BIT_SIZE - 1`, and panics otherwise.
pub fn from_fp(other: &FpVar<F>) -> Result<(Self, FpVar<F>), SynthesisError>
where
F: PrimeField,
{
let (bits, rest) = other.to_bits_le_with_top_bits_zero(N)?;
let result = Self::from_bits_le(&bits);
Ok((result, rest))
}
/// Converts a little-endian byte order representation of bits 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 = UInt8::new_witness(cs.clone(), || Ok(128))?;
///
/// let f = Boolean::FALSE;
/// let t = Boolean::TRUE;
///
/// // Construct [0, 0, 0, 0, 0, 0, 0, 1]
/// let mut bits = vec![f.clone(); 7];
/// bits.push(t);
///
/// let mut c = UInt8::from_bits_le(&bits);
/// var.enforce_equal(&c)?;
/// assert!(cs.is_satisfied().unwrap());
/// # Ok(())
/// # }
/// ```
#[tracing::instrument(target = "r1cs")]
pub fn from_bits_le(bits: &[Boolean<F>]) -> Self {
assert_eq!(bits.len(), N);
let bits = <&[Boolean<F>; N]>::try_from(bits).unwrap().clone();
let value_exists = bits.iter().all(|b| b.value().is_ok());
let mut value = T::zero();
for (i, b) in bits.iter().enumerate() {
if let Ok(b) = b.value() {
value = value + (T::from(b as u8).unwrap() << i);
}
}
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> {
fn to_bits_le(&self) -> Result<Vec<Boolean<F>>, SynthesisError> {
Ok(self.bits.to_vec())
}
}
impl<const N: usize, T: PrimUInt, F: Field> ToBitsGadget<F> for [UInt<N, T, F>] {
/// Interprets `self` as an integer, and outputs the little-endian
/// bit-wise decomposition of that integer.
fn to_bits_le(&self) -> Result<Vec<Boolean<F>>, SynthesisError> {
let bits = self.iter().flat_map(|b| &b.bits).cloned().collect();
Ok(bits)
}
}
/*****************************************************************************************/
/********************************* Conversions to bytes. *********************************/
/*****************************************************************************************/
impl<const N: usize, T: PrimUInt, ConstraintF: Field> ToBytesGadget<ConstraintF>
for UInt<N, T, ConstraintF>
{
#[tracing::instrument(target = "r1cs", skip(self))]
fn to_bytes_le(&self) -> Result<Vec<UInt8<ConstraintF>>, SynthesisError> {
Ok(self
.to_bits_le()?
.chunks(8)
.map(UInt8::from_bits_le)
.collect())
}
}
impl<const N: usize, T: PrimUInt, F: Field> ToBytesGadget<F> for [UInt<N, T, F>] {
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_le()?);
}
Ok(bytes)
}
}
impl<const N: usize, T: PrimUInt, F: Field> ToBytesGadget<F> for Vec<UInt<N, T, F>> {
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_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()
}
}