From 013c8ba877473ad1e27497bc45d5499f0895e537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s?= <47506558+MegaRedHand@users.noreply.github.com> Date: Wed, 12 Jul 2023 16:28:58 -0300 Subject: [PATCH] feat: add from_bytes_(l|n)e methods (#1326) * Add from_bytes_(l|n)e methods * Add TODOs * Move the new methods to `FeltOps` * Add comment and clean up some code * Add tests --------- Co-authored-by: Pedro Fontana --- CHANGELOG.md | 1 + felt/src/bigint_felt.rs | 10 +++---- felt/src/lib_bigint_felt.rs | 48 +++++++++++++++++++++++++++++++ felt/src/lib_lambdaworks.rs | 57 ++++++++++++++++++++++++++++++++++++- fuzzer/Cargo.lock | 4 +-- 5 files changed, 112 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e540f8c4e3..53928153d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * perf: remove pointless iterator from rc limits tracking [#1316](https://github.com/lambdaclass/cairo-vm/pull/1316) +* feat: add `from_bytes_le` and `from_bytes_ne` methods [#1326](https://github.com/lambdaclass/cairo-vm/pull/1326) #### [0.8.2] - 2023-7-10 diff --git a/felt/src/bigint_felt.rs b/felt/src/bigint_felt.rs index fddcec44f2..c1c6505a78 100644 --- a/felt/src/bigint_felt.rs +++ b/felt/src/bigint_felt.rs @@ -174,11 +174,11 @@ impl FeltOps for FeltBigInt { } fn from_bytes_be(bytes: &[u8]) -> FeltBigInt { - let mut value = BigUint::from_bytes_be(bytes); - if value >= *CAIRO_PRIME_BIGUINT { - value = value.mod_floor(&CAIRO_PRIME_BIGUINT); - } - Self::from(value) + Self::from(BigUint::from_bytes_be(bytes)) + } + + fn from_bytes_le(bytes: &[u8]) -> FeltBigInt { + Self::from(BigUint::from_bytes_le(bytes)) } #[cfg(any(feature = "std", feature = "alloc"))] diff --git a/felt/src/lib_bigint_felt.rs b/felt/src/lib_bigint_felt.rs index 93652e0290..c539de2898 100644 --- a/felt/src/lib_bigint_felt.rs +++ b/felt/src/lib_bigint_felt.rs @@ -43,6 +43,8 @@ pub(crate) trait FeltOps { fn from_bytes_be(bytes: &[u8]) -> Self; + fn from_bytes_le(bytes: &[u8]) -> Self; + #[cfg(any(feature = "std", feature = "alloc"))] fn to_str_radix(&self, radix: u32) -> String; @@ -181,6 +183,19 @@ impl Felt252 { value: FeltBigInt::from_bytes_be(bytes), } } + pub fn from_bytes_le(bytes: &[u8]) -> Self { + Self { + value: FeltBigInt::from_bytes_le(bytes), + } + } + pub fn from_bytes_ne(bytes: &[u8]) -> Self { + // Call either version depending on target endianness + #[cfg(target_endian = "little")] + let res = Self::from_bytes_le(bytes); + #[cfg(target_endian = "big")] + let res = Self::from_bytes_be(bytes); + res + } #[cfg(any(feature = "std", feature = "alloc"))] pub fn to_str_radix(&self, radix: u32) -> String { self.value.to_str_radix(radix) @@ -1019,6 +1034,31 @@ mod test { prop_assert!(&x.to_biguint() < p); } + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn from_bytes_be(high: u128, low: u128) { + let expected = (Felt252::from(high) << 128_usize) + Felt252::from(low); + let mut bytes = [0; 32]; + // big-endian order: [ high, low ] + bytes[..16].copy_from_slice(&high.to_be_bytes()); + bytes[16..].copy_from_slice(&low.to_be_bytes()); + let got = Felt252::from_bytes_be(&bytes); + prop_assert_eq!(got, expected); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn from_bytes_le(high: u128, low: u128) { + let expected = (Felt252::from(high) << 128_usize) + Felt252::from(low); + let mut bytes = [0; 32]; + // little-endian order: [ low, high ] + bytes[..16].copy_from_slice(&low.to_le_bytes()); + bytes[16..].copy_from_slice(&high.to_le_bytes()); + let got = Felt252::from_bytes_le(&bytes); + prop_assert_eq!(got, expected); + } + + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn to_be_bytes(ref x in any::()) { @@ -1668,4 +1708,12 @@ mod test { let zero = Felt252::zero(); assert_eq!(&zero.signum(), &zero) } + + #[test] + fn from_bytes_ne() { + let expected = Felt252::zero(); + let bytes = [0; 32]; + let got = Felt252::from_bytes_ne(&bytes); + assert_eq!(got, expected); + } } diff --git a/felt/src/lib_lambdaworks.rs b/felt/src/lib_lambdaworks.rs index 88997f3d43..8c1f6335c5 100644 --- a/felt/src/lib_lambdaworks.rs +++ b/felt/src/lib_lambdaworks.rs @@ -259,9 +259,24 @@ impl Felt252 { } pub fn from_bytes_be(bytes: &[u8]) -> Self { + // TODO: use upstream's version when it's more lenient Self::from(BigUint::from_bytes_be(bytes)) } + pub fn from_bytes_le(bytes: &[u8]) -> Self { + // TODO: use upstream's version when it's more lenient + Self::from(BigUint::from_bytes_le(bytes)) + } + + pub fn from_bytes_ne(bytes: &[u8]) -> Self { + // Call either version depending on target endianness + #[cfg(target_endian = "little")] + let res = Self::from_bytes_le(bytes); + #[cfg(target_endian = "big")] + let res = Self::from_bytes_be(bytes); + res + } + #[cfg(any(feature = "std", feature = "alloc"))] pub fn to_str_radix(&self, radix: u32) -> String { if radix == 16 { @@ -995,12 +1010,44 @@ mod test { // In this and some of the following tests, The value of {x} can be either [0] or a // very large number, in order to try to overflow the value of {p} and thus ensure the // modular arithmetic is working correctly. - fn new_in_range(ref x in any::<[u8; 40]>()) { + fn new_in_range_be(ref x in any::<[u8; 40]>()) { let x = Felt252::from_bytes_be(x); let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap(); prop_assert!(&x.to_biguint() < p); } + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn new_in_range_le(ref x in any::<[u8; 40]>()) { + let x = Felt252::from_bytes_le(x); + let p = &BigUint::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap(); + prop_assert!(&x.to_biguint() < p); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn from_bytes_be(high: u128, low: u128) { + let expected = (Felt252::from(high) << 128_usize) + Felt252::from(low); + let mut bytes = [0; 32]; + // big-endian order: [ high, low ] + bytes[..16].copy_from_slice(&high.to_be_bytes()); + bytes[16..].copy_from_slice(&low.to_be_bytes()); + let got = Felt252::from_bytes_be(&bytes); + prop_assert_eq!(got, expected); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn from_bytes_le(high: u128, low: u128) { + let expected = (Felt252::from(high) << 128_usize) + Felt252::from(low); + let mut bytes = [0; 32]; + // little-endian order: [ low, high ] + bytes[..16].copy_from_slice(&low.to_le_bytes()); + bytes[16..].copy_from_slice(&high.to_le_bytes()); + let got = Felt252::from_bytes_le(&bytes); + prop_assert_eq!(got, expected); + } + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn to_be_bytes(ref x in any::()) { @@ -1702,4 +1749,12 @@ mod test { fn default_is_zero() { assert_eq!(Felt252::default(), Felt252::zero()) } + + #[test] + fn from_bytes_ne() { + let expected = Felt252::zero(); + let bytes = [0; 32]; + let got = Felt252::from_bytes_ne(&bytes); + assert_eq!(got, expected); + } } diff --git a/fuzzer/Cargo.lock b/fuzzer/Cargo.lock index 7c8377fb88..efdf8ae22a 100644 --- a/fuzzer/Cargo.lock +++ b/fuzzer/Cargo.lock @@ -167,7 +167,7 @@ checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "cairo-felt" -version = "0.8.1" +version = "0.8.2" dependencies = [ "lazy_static", "num-bigint", @@ -178,7 +178,7 @@ dependencies = [ [[package]] name = "cairo-vm" -version = "0.8.1" +version = "0.8.2" dependencies = [ "anyhow", "bincode",