From f35dce37dbd101d9475e811ddb997fd3a9532043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Delabrouille?= Date: Tue, 28 Nov 2023 18:22:41 +0100 Subject: [PATCH] feat: impl a few num-traits on Felt, behind a feature flag --- crates/starknet-types-core/Cargo.toml | 23 +++- .../src/{ => felt}/felt_arbitrary.rs | 3 +- .../src/{felt.rs => felt/mod.rs} | 79 ++--------- .../src/felt/num_traits_impl.rs | 123 ++++++++++++++++++ crates/starknet-types-core/src/lib.rs | 2 - 5 files changed, 154 insertions(+), 76 deletions(-) rename crates/starknet-types-core/src/{ => felt}/felt_arbitrary.rs (95%) rename crates/starknet-types-core/src/{felt.rs => felt/mod.rs} (96%) create mode 100644 crates/starknet-types-core/src/felt/num_traits_impl.rs diff --git a/crates/starknet-types-core/Cargo.toml b/crates/starknet-types-core/Cargo.toml index 91ab802..3a6adda 100644 --- a/crates/starknet-types-core/Cargo.toml +++ b/crates/starknet-types-core/Cargo.toml @@ -12,23 +12,34 @@ readme = "README.md" [dependencies] bitvec = { version = "1.0.1", default-features = false } -serde = { version = "1.0.163", optional = true, default-features = false } lambdaworks-math = { git = "https://github.com/lambdaclass/lambdaworks.git", rev = "f940e14ed17370d29fe129951448037d11b65ce8", default-features = false} -lambdaworks-crypto = { git = "https://github.com/lambdaclass/lambdaworks.git", rev = "f940e14ed17370d29fe129951448037d11b65ce8", default-features = false, optional = true} -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} +# Optional +arbitrary = { version = "1.3.0", optional = true } +serde = { version = "1.0.163", optional = true, default-features = false } +lambdaworks-crypto = { git = "https://github.com/lambdaclass/lambdaworks.git", rev = "f940e14ed17370d29fe129951448037d11b65ce8", default-features = false, optional = true} + [features] -default = ["std", "serde", "curve"] +default = ["std", "serde", "curve", "num-traits"] +std = [ + "bitvec/std", + "lambdaworks-math/std", + "num-traits/std", + "num-bigint/std", + "num-integer/std", + "serde?/std", +] curve = [] -hash = ["dep:lambdaworks-crypto"] -std = [] alloc = ["serde?/alloc"] +num-traits = [] arbitrary = ["std", "dep:arbitrary"] +serde = ["dep:serde"] +hash = ["dep:lambdaworks-crypto"] [dev-dependencies] proptest = "1.1.0" diff --git a/crates/starknet-types-core/src/felt_arbitrary.rs b/crates/starknet-types-core/src/felt/felt_arbitrary.rs similarity index 95% rename from crates/starknet-types-core/src/felt_arbitrary.rs rename to crates/starknet-types-core/src/felt/felt_arbitrary.rs index 4f2b0f4..aa83fd0 100644 --- a/crates/starknet-types-core/src/felt_arbitrary.rs +++ b/crates/starknet-types-core/src/felt/felt_arbitrary.rs @@ -1,5 +1,4 @@ use lambdaworks_math::{field::element::FieldElement, unsigned_integer::element::UnsignedInteger}; -use num_traits::Zero; use proptest::prelude::*; use crate::felt::Felt; @@ -37,7 +36,7 @@ fn any_felt() -> impl Strategy { /// Returns a [`Strategy`] that generates any nonzero Felt /// This is used to generate input values for proptests pub fn nonzero_felt() -> impl Strategy { - any_felt().prop_filter("is zero", |x| !x.is_zero()) + any_felt().prop_filter("is zero", |&x| x != Felt::ZERO) } impl Arbitrary for Felt { diff --git a/crates/starknet-types-core/src/felt.rs b/crates/starknet-types-core/src/felt/mod.rs similarity index 96% rename from crates/starknet-types-core/src/felt.rs rename to crates/starknet-types-core/src/felt/mod.rs index 86a905d..f2ccc25 100644 --- a/crates/starknet-types-core/src/felt.rs +++ b/crates/starknet-types-core/src/felt/mod.rs @@ -1,9 +1,17 @@ +#[cfg(test)] +mod felt_arbitrary; + use core::ops::{Add, Mul, Neg}; use bitvec::array::BitArray; use num_bigint::BigInt; + +use num_traits::{One, Zero}; + +#[cfg(feature = "num-traits")] +mod num_traits_impl; + use num_integer::Integer; -use num_traits::{FromPrimitive, One, ToPrimitive, Zero}; #[cfg(target_pointer_width = "64")] pub type BitArrayStore = [u64; 4]; @@ -396,7 +404,7 @@ impl TryFrom for NonZeroFelt { type Error = FeltIsZeroError; fn try_from(value: Felt) -> Result { - if value.is_zero() { + if value == Felt::ZERO { Err(FeltIsZeroError) } else { Ok(Self(value.0)) @@ -408,7 +416,7 @@ impl TryFrom<&Felt> for NonZeroFelt { type Error = FeltIsZeroError; fn try_from(value: &Felt) -> Result { - if value.is_zero() { + if *value == Felt::ZERO { Err(FeltIsZeroError) } else { Ok(Self(value.0)) @@ -455,57 +463,6 @@ impl_from!(i32, i128); impl_from!(i64, i128); impl_from!(isize, i128); -impl FromPrimitive for Felt { - fn from_i64(value: i64) -> Option { - Some(value.into()) - } - - fn from_u64(value: u64) -> Option { - Some(value.into()) - } - - fn from_i128(value: i128) -> Option { - Some(value.into()) - } - - fn from_u128(value: u128) -> Option { - Some(value.into()) - } -} - -// TODO: we need to decide whether we want conversions to signed primitives -// will support converting the high end of the field to negative. -impl ToPrimitive for Felt { - fn to_u64(&self) -> Option { - self.to_u128().and_then(|x| u64::try_from(x).ok()) - } - - fn to_i64(&self) -> Option { - self.to_u128().and_then(|x| i64::try_from(x).ok()) - } - - fn to_u128(&self) -> Option { - match self.0.representative().limbs { - [0, 0, hi, lo] => Some((lo as u128) | ((hi as u128) << 64)), - _ => None, - } - } - - fn to_i128(&self) -> Option { - self.to_u128().and_then(|x| i128::try_from(x).ok()) - } -} - -impl Zero for Felt { - fn is_zero(&self) -> bool { - *self == Felt::ZERO - } - - fn zero() -> Felt { - Felt::ZERO - } -} - impl Add<&Felt> for u64 { type Output = Option; @@ -863,7 +820,7 @@ mod formatting { /// Represents [Felt] in decimal by default. impl fmt::Display for Felt { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.is_zero() { + if *self == Felt::ZERO { return write!(f, "0"); } @@ -1172,7 +1129,7 @@ mod test { #[test] fn non_zero_is_not_zero(x in nonzero_felt()) { - prop_assert!(!x.is_zero()) + prop_assert!(x != Felt::ZERO) } #[test] @@ -1253,21 +1210,11 @@ mod test { assert_eq!(Felt::MAX.to_bytes_be(), max_bytes); } - #[test] - fn zero_is_zero() { - assert!(Felt::ZERO.is_zero()); - } - #[test] fn non_zero_felt_from_zero_should_fail() { assert!(NonZeroFelt::try_from(Felt::ZERO).is_err()); } - #[test] - fn default_is_zero() { - assert!(Felt::default().is_zero()); - } - #[test] fn mul_operations() { assert_eq!(Felt::ONE * Felt::THREE, Felt::THREE); diff --git a/crates/starknet-types-core/src/felt/num_traits_impl.rs b/crates/starknet-types-core/src/felt/num_traits_impl.rs new file mode 100644 index 0000000..886f13b --- /dev/null +++ b/crates/starknet-types-core/src/felt/num_traits_impl.rs @@ -0,0 +1,123 @@ +use super::Felt; +use num_traits::{FromPrimitive, Inv, One, Pow, ToPrimitive, Zero}; + +impl FromPrimitive for Felt { + fn from_i64(value: i64) -> Option { + Some(value.into()) + } + + fn from_u64(value: u64) -> Option { + Some(value.into()) + } + + fn from_i128(value: i128) -> Option { + Some(value.into()) + } + + fn from_u128(value: u128) -> Option { + Some(value.into()) + } +} + +// TODO: we need to decide whether we want conversions to signed primitives +// will support converting the high end of the field to negative. +impl ToPrimitive for Felt { + fn to_u64(&self) -> Option { + self.to_u128().and_then(|x| u64::try_from(x).ok()) + } + + fn to_i64(&self) -> Option { + self.to_u128().and_then(|x| i64::try_from(x).ok()) + } + + fn to_u128(&self) -> Option { + match self.0.representative().limbs { + [0, 0, hi, lo] => Some((lo as u128) | ((hi as u128) << 64)), + _ => None, + } + } + + fn to_i128(&self) -> Option { + self.to_u128().and_then(|x| i128::try_from(x).ok()) + } +} + +impl Zero for Felt { + fn is_zero(&self) -> bool { + *self == Felt::ZERO + } + + fn zero() -> Felt { + Felt::ZERO + } +} + +impl One for Felt { + fn one() -> Self { + Felt::ONE + } +} + +impl Inv for Felt { + type Output = Option; + + fn inv(self) -> Self::Output { + self.inverse() + } +} + +impl Pow for Felt { + type Output = Self; + + fn pow(self, rhs: u8) -> Self::Output { + Self(self.0.pow(rhs as u128)) + } +} +impl Pow for Felt { + type Output = Self; + + fn pow(self, rhs: u16) -> Self::Output { + Self(self.0.pow(rhs as u128)) + } +} +impl Pow for Felt { + type Output = Self; + + fn pow(self, rhs: u32) -> Self::Output { + Self(self.0.pow(rhs as u128)) + } +} +impl Pow for Felt { + type Output = Self; + + fn pow(self, rhs: u64) -> Self::Output { + Self(self.0.pow(rhs as u128)) + } +} +impl Pow for Felt { + type Output = Self; + + fn pow(self, rhs: u128) -> Self::Output { + Self(self.0.pow(rhs)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn zero_is_zero() { + assert!(Felt::ZERO.is_zero()); + } + + #[test] + fn one_is_one() { + assert!(Felt::ONE.is_one()); + } + + #[test] + fn default_is_zero() { + assert!(Felt::default().is_zero()); + } +} diff --git a/crates/starknet-types-core/src/lib.rs b/crates/starknet-types-core/src/lib.rs index 45bd805..bd641fc 100644 --- a/crates/starknet-types-core/src/lib.rs +++ b/crates/starknet-types-core/src/lib.rs @@ -5,5 +5,3 @@ pub mod curve; pub mod hash; pub mod felt; -#[cfg(test)] -mod felt_arbitrary;