From 4eda1082a34e49b69a234c97e40bcb902ff53da1 Mon Sep 17 00:00:00 2001 From: Pedro Fontana Date: Wed, 20 Dec 2023 10:03:56 -0300 Subject: [PATCH 1/3] Implement felt to bigint functions (#23) --- crates/starknet-types-core/Cargo.toml | 3 + crates/starknet-types-core/src/felt.rs | 104 ++++++++++++++++++++++++- 2 files changed, 106 insertions(+), 1 deletion(-) diff --git a/crates/starknet-types-core/Cargo.toml b/crates/starknet-types-core/Cargo.toml index 1b61b80..a280e72 100644 --- a/crates/starknet-types-core/Cargo.toml +++ b/crates/starknet-types-core/Cargo.toml @@ -21,6 +21,9 @@ arbitrary = { version = "1.3.0", optional = true, default-features = false } num-traits = { version = "0.2.16", default-features = false } num-bigint = {version = "0.4.4", default-features = false} num-integer = {version = "0.1.45", default-features = false} +lazy_static = { version = "1.4.0", default-features = false, features = [ + "spin_no_std", +] } [features] default = ["std", "serde", "curve"] diff --git a/crates/starknet-types-core/src/felt.rs b/crates/starknet-types-core/src/felt.rs index 86a905d..a354a6f 100644 --- a/crates/starknet-types-core/src/felt.rs +++ b/crates/starknet-types-core/src/felt.rs @@ -1,10 +1,20 @@ use core::ops::{Add, Mul, Neg}; use bitvec::array::BitArray; -use num_bigint::BigInt; +use lazy_static::lazy_static; +use num_bigint::{BigInt, BigUint, Sign}; use num_integer::Integer; +use num_traits::Num; use num_traits::{FromPrimitive, One, ToPrimitive, Zero}; +lazy_static! { + pub static ref CAIRO_PRIME_BIGINT: BigInt = BigInt::from_str_radix( + "800000000000011000000000000000000000000000000000000000000000001", + 16 + ) + .unwrap(); +} + #[cfg(target_pointer_width = "64")] pub type BitArrayStore = [u64; 4]; @@ -346,6 +356,19 @@ impl Felt { pub fn bits(&self) -> usize { self.0.representative().bits_le() } + + pub fn to_biguint(&self) -> BigUint { + let big_digits = self + .to_le_digits() + .into_iter() + .flat_map(|limb| [limb as u32, (limb >> 32) as u32]) + .collect(); + BigUint::new(big_digits) + } + + pub fn to_bigint(&self) -> BigInt { + self.to_biguint().into() + } } #[cfg(feature = "arbitrary")] @@ -434,6 +457,24 @@ impl From for Felt { } } +impl From<&BigInt> for Felt { + fn from(bigint: &BigInt) -> Felt { + let (sign, bytes) = bigint.mod_floor(&CAIRO_PRIME_BIGINT).to_bytes_le(); + let felt = Felt::from_bytes_le_slice(&bytes); + if sign == Sign::Minus { + felt.neg() + } else { + felt + } + } +} + +impl From<&BigUint> for Felt { + fn from(biguint: &BigUint) -> Felt { + Felt::from_bytes_le_slice(&biguint.to_bytes_le()) + } +} + macro_rules! impl_from { ($from:ty, $with:ty) => { impl From<$from> for Felt { @@ -1600,4 +1641,65 @@ mod test { assert_eq!(x.mul_mod(&y, &p), expected_result); } + + #[test] + fn bigints_to_felt() { + let one = &*CAIRO_PRIME_BIGINT + BigInt::from(1_u32); + assert_eq!(Felt::from(&one.to_biguint().unwrap()), Felt::from(1)); + assert_eq!(Felt::from(&one), Felt::from(1)); + + let zero = &*CAIRO_PRIME_BIGINT * 99_u32; + assert_eq!(Felt::from(&zero.to_biguint().unwrap()), Felt::from(0)); + assert_eq!(Felt::from(&zero), Felt::from(0)); + + assert_eq!( + Felt::from(&BigInt::from(-1)), + Felt::from_hex("0x800000000000011000000000000000000000000000000000000000000000000") + .unwrap() + ); + + let numbers_str = [ + "0x0", + "0x1", + "0x10", + "0x8000000000000110000000000", + "0xffffffffffffff", + "0xffffffffefff12380777abcd", + ]; + + for number_str in numbers_str { + assert_eq!( + Felt::from(&BigInt::from_str_radix(&number_str[2..], 16).unwrap()), + Felt::from_hex(number_str).unwrap() + ); + assert_eq!( + Felt::from(&BigUint::from_str_radix(&number_str[2..], 16).unwrap()), + Felt::from_hex(number_str).unwrap() + ) + } + } + + #[test] + fn felt_to_bigints() { + let numbers_str = [ + "0x0", + "0x1", + "0x10", + "0x8000000000000110000000000", + "0xffffffffffffff", + "0xffffffffefff12380777abcd", + ]; + + for number_str in numbers_str { + assert_eq!( + Felt::from_hex(number_str).unwrap().to_bigint(), + BigInt::from_str_radix(&number_str[2..], 16).unwrap() + ); + + assert_eq!( + Felt::from_hex(number_str).unwrap().to_biguint(), + BigUint::from_str_radix(&number_str[2..], 16).unwrap() + ); + } + } } From 668643615fa4199b09b589ebf73ffd07cef19a06 Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Wed, 20 Dec 2023 10:06:50 -0300 Subject: [PATCH 2/3] fix: std requires alloc (#27) --- crates/starknet-types-core/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/starknet-types-core/Cargo.toml b/crates/starknet-types-core/Cargo.toml index a280e72..09a1903 100644 --- a/crates/starknet-types-core/Cargo.toml +++ b/crates/starknet-types-core/Cargo.toml @@ -29,7 +29,7 @@ lazy_static = { version = "1.4.0", default-features = false, features = [ default = ["std", "serde", "curve"] curve = [] hash = ["dep:lambdaworks-crypto"] -std = [] +std = ["alloc"] alloc = ["serde?/alloc"] arbitrary = ["std", "dep:arbitrary"] From 33c8fbdc7dbb9efce3937b0a35a90800c52899ce Mon Sep 17 00:00:00 2001 From: Mario Rugiero Date: Wed, 20 Dec 2023 10:12:38 -0300 Subject: [PATCH 3/3] feat: hex formatting helper (#26) --- crates/starknet-types-core/src/felt.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/crates/starknet-types-core/src/felt.rs b/crates/starknet-types-core/src/felt.rs index a354a6f..dc5820b 100644 --- a/crates/starknet-types-core/src/felt.rs +++ b/crates/starknet-types-core/src/felt.rs @@ -210,6 +210,13 @@ impl Felt { BitArray::new(limbs) } + /// Helper to produce a hexadecimal formatted string. + /// Equivalent to calling `format!("{self:#x}")`. + #[cfg(feature = "alloc")] + pub fn to_hex_string(&self) -> alloc::string::String { + alloc::format!("{self:#x}") + } + /// Converts to little-endian bit representation. /// This is as performant as [to_bits_be](Felt::to_bits_be) pub fn to_bits_le(&self) -> BitArray { @@ -1060,6 +1067,12 @@ mod test { prop_assert_eq!(x, y); } + #[test] + #[cfg(feature = "alloc")] + fn to_hex_string_is_same_as_format(ref x in any::()) { + prop_assert_eq!(alloc::format!("{x:#x}"), x.to_hex_string()); + } + #[test] fn from_bytes_le_slice_works_for_all_lengths(x in 0..1000usize) { let bytes: [u8; 1000] = core::array::from_fn(|i| i as u8);