diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 895a804..a8ecc99 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @spapinistarkware @abdelhamidbakhta @dan-starkware @unbalancedparentheses @xJonathanLEI \ No newline at end of file +* @LucasLvy @pefontana diff --git a/crates/starknet-types-core/Cargo.toml b/crates/starknet-types-core/Cargo.toml index 1b61b80..09a1903 100644 --- a/crates/starknet-types-core/Cargo.toml +++ b/crates/starknet-types-core/Cargo.toml @@ -21,12 +21,15 @@ 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"] curve = [] hash = ["dep:lambdaworks-crypto"] -std = [] +std = ["alloc"] alloc = ["serde?/alloc"] arbitrary = ["std", "dep:arbitrary"] diff --git a/crates/starknet-types-core/src/felt.rs b/crates/starknet-types-core/src/felt.rs index 86a905d..dc5820b 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]; @@ -200,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 { @@ -346,6 +363,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 +464,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 { @@ -1019,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); @@ -1600,4 +1654,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() + ); + } + } }