diff --git a/Cargo.toml b/Cargo.toml index 137915ef..d892e2b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,8 @@ no_unknown_fields = [ [workspace.lints] rust.missing_debug_implementations = "warn" +rust.missing_docs = "allow" +rust.unreachable_pub = "allow" rust.unused_must_use = "deny" rust.rust_2018_idioms = { level = "deny", priority = -1 } rustdoc.all = "warn" @@ -102,7 +104,7 @@ unnecessary_struct_initialization = "warn" string_lit_as_bytes = "warn" explicit_into_iter_loop = "warn" explicit_iter_loop = "warn" -type_repetition_in_bounds = "warn" +type_repetition_in_bounds = "allow" manual_string_new = "warn" naive_bytecount = "warn" needless_bitwise_bool = "warn" diff --git a/starknet-curve/src/ec_point.rs b/starknet-curve/src/ec_point.rs index 385c10a4..ad9783c8 100644 --- a/starknet-curve/src/ec_point.rs +++ b/starknet-curve/src/ec_point.rs @@ -1,4 +1,3 @@ -#![allow(clippy::missing_const_for_fn)] use starknet_ff::FieldElement; use crate::curve_params::{ALPHA, BETA}; @@ -31,7 +30,7 @@ impl AffinePoint { }) } - fn identity() -> Self { + const fn identity() -> Self { Self { x: FieldElement::ZERO, y: FieldElement::ZERO, @@ -160,7 +159,7 @@ impl ProjectivePoint { } } - fn identity() -> Self { + const fn identity() -> Self { Self { x: FieldElement::ZERO, y: FieldElement::ZERO, diff --git a/starknet-ff/Cargo.toml b/starknet-ff/Cargo.toml new file mode 100644 index 00000000..2f77e9f7 --- /dev/null +++ b/starknet-ff/Cargo.toml @@ -0,0 +1,41 @@ +[package] +name = "starknet-ff" +version = "0.3.7" +authors = ["Jonathan LEI "] +license = "MIT OR Apache-2.0" +edition = "2021" +readme = "README.md" +repository = "https://github.com/xJonathanLEI/starknet-rs" +homepage = "https://starknet.rs/" +description = """ +Starknet field element type +""" +keywords = ["ethereum", "starknet", "web3", "no_std"] + +[package.metadata.docs.rs] +all-features = true + +[dependencies] +ark-ff = { version = "0.4.2", default-features = false } +crypto-bigint = { version = "0.5.1", default-features = false } +hex = { version = "0.4.3", default-features = false } +serde = { version = "1.0.160", optional = true, default-features = false } +bigdecimal = { version = "0.3.0", optional = true } +num-bigint = { version = "0.4.3", optional = true, default-features = false } + + +[features] +default = ["std", "serde"] +std = [] +alloc = ["serde?/alloc"] +serde = ["dep:serde", "alloc", "bigdecimal?/serde"] +bigdecimal = ["std", "dep:bigdecimal", "dep:num-bigint"] + +[target.'cfg(target_arch = "wasm32")'.dependencies] +getrandom = { version = "0.2.9", features = ["js"] } + +[target.'cfg(target_arch = "wasm32")'.dev-dependencies] +wasm-bindgen-test = "0.3.34" + +[lints] +workspace = true diff --git a/starknet-ff/src/lib.rs b/starknet-ff/src/lib.rs new file mode 100644 index 00000000..dabe2773 --- /dev/null +++ b/starknet-ff/src/lib.rs @@ -0,0 +1,1110 @@ +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::comparison_chain)] +#![allow(clippy::missing_const_for_fn)] +#[cfg(all(not(feature = "std"), any(test, feature = "alloc")))] +#[cfg_attr(test, macro_use)] +extern crate alloc; + +use core::{ + fmt, ops, + str::{self, FromStr}, +}; + +use crate::fr::Fr; + +use ark_ff::{ + fields::{Field, Fp256, PrimeField}, + BigInteger, BigInteger256, +}; +use crypto_bigint::{CheckedAdd, CheckedMul, NonZero, Zero, U256}; + +mod fr; + +const U256_BYTE_COUNT: usize = 32; + +#[derive(Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)] +pub struct FieldElement { + inner: Fr, +} + +mod from_str_error { + + #[derive(Debug)] + pub enum FromStrError { + InvalidCharacter, + OutOfRange, + } + + #[cfg(feature = "std")] + impl std::error::Error for FromStrError {} + + impl core::fmt::Display for FromStrError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::InvalidCharacter => write!(f, "invalid character"), + Self::OutOfRange => write!(f, "number out of range"), + } + } + } +} +pub use from_str_error::FromStrError; + +mod from_bytes_slice_error { + + #[derive(Debug)] + pub enum FromByteSliceError { + InvalidLength, + OutOfRange, + } + + #[cfg(feature = "std")] + impl std::error::Error for FromByteSliceError {} + + impl core::fmt::Display for FromByteSliceError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::InvalidLength => write!(f, "invalid length"), + Self::OutOfRange => write!(f, "number out of range"), + } + } + } +} +pub use from_bytes_slice_error::FromByteSliceError; + +mod from_byte_array_error { + #[derive(Debug)] + pub struct FromByteArrayError; + + #[cfg(feature = "std")] + impl std::error::Error for FromByteArrayError {} + + impl core::fmt::Display for FromByteArrayError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "number out of range") + } + } +} +pub use from_byte_array_error::FromByteArrayError; + +mod value_out_of_range_error { + #[derive(Debug)] + pub struct ValueOutOfRangeError; + + #[cfg(feature = "std")] + impl std::error::Error for ValueOutOfRangeError {} + + impl core::fmt::Display for ValueOutOfRangeError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "field element value out of range") + } + } +} +pub use value_out_of_range_error::ValueOutOfRangeError; + +struct InnerDebug<'a>(pub &'a FieldElement); + +impl FieldElement { + /// [`FieldElement`] constant that's equal to 0 + pub const ZERO: Self = Self::from_mont([0, 0, 0, 0]); + + /// [`FieldElement`] constant that's equal to 1 + pub const ONE: Self = Self::from_mont([ + 18446744073709551585, + 18446744073709551615, + 18446744073709551615, + 576460752303422960, + ]); + + /// [`FieldElement`] constant that's equal to 2 + pub const TWO: Self = Self::from_mont([ + 18446744073709551553, + 18446744073709551615, + 18446744073709551615, + 576460752303422416, + ]); + + /// [`FieldElement`] constant that's equal to 3 + pub const THREE: Self = Self::from_mont([ + 18446744073709551521, + 18446744073709551615, + 18446744073709551615, + 576460752303421872, + ]); + + /// Maximum value of [`FieldElement`]. Equals to 2^251 + 17 * 2^192. + pub const MAX: Self = Self::from_mont([32, 0, 0, 544]); + + /// Create a new [`FieldElement`] from its Montgomery representation + pub const fn from_mont(data: [u64; 4]) -> Self { + Self { + inner: Fp256::new_unchecked(BigInteger256::new(data)), + } + } + + pub fn from_dec_str(value: &str) -> Result { + // Ported from: + // https://github.com/paritytech/parity-common/blob/b37d0b312d39fa47c61c4430b30ca87d90e45a08/uint/src/uint.rs#L599 + + let mut res = U256::ZERO; + for b in value.bytes().map(|b| b.wrapping_sub(b'0')) { + if b > 9 { + return Err(FromStrError::InvalidCharacter); + } + let r = { + let product = res.checked_mul(&U256::from_u8(10)); + if product.is_some().into() { + product.unwrap() + } else { + return Err(FromStrError::OutOfRange); + } + }; + let r = { + let sum = r.checked_add(&U256::from_u8(b)); + if sum.is_some().into() { + sum.unwrap() + } else { + return Err(FromStrError::OutOfRange); + } + }; + res = r; + } + + Fr::from_bigint(u256_to_biginteger256(&res)) + .map(|inner| Self { inner }) + .ok_or(FromStrError::OutOfRange) + } + + pub fn from_hex_be(value: &str) -> Result { + let value = value.trim_start_matches("0x"); + + let hex_chars_len = value.len(); + let expected_hex_length = U256_BYTE_COUNT * 2; + + let parsed_bytes: [u8; U256_BYTE_COUNT] = if hex_chars_len == expected_hex_length { + let mut buffer = [0u8; U256_BYTE_COUNT]; + hex::decode_to_slice(value, &mut buffer).map_err(|_| FromStrError::InvalidCharacter)?; + buffer + } else if hex_chars_len < expected_hex_length { + let mut padded_hex = str::repeat("0", expected_hex_length - hex_chars_len); + padded_hex.push_str(value); + + let mut buffer = [0u8; U256_BYTE_COUNT]; + hex::decode_to_slice(&padded_hex, &mut buffer) + .map_err(|_| FromStrError::InvalidCharacter)?; + buffer + } else { + return Err(FromStrError::OutOfRange); + }; + + match Self::from_bytes_be(&parsed_bytes) { + Ok(value) => Ok(value), + Err(_) => Err(FromStrError::OutOfRange), + } + } + + /// Attempts to convert a big-endian byte representation of a field element into an element of + /// this prime field. Returns error if the input is not canonical (is not smaller than the + /// field's modulus). + /// + /// ### Arguments + /// + /// * `bytes`: The byte array in **big endian** format + pub fn from_bytes_be(bytes: &[u8; 32]) -> Result { + Self::from_byte_slice(bytes).ok_or(FromByteArrayError) + } + + /// Same as `from_bytes_be` except this function takes a slice. + pub fn from_byte_slice_be(bytes: &[u8]) -> Result { + if bytes.len() > U256_BYTE_COUNT { + Err(FromByteSliceError::InvalidLength) + } else { + let mut buffer = [0u8; U256_BYTE_COUNT]; + buffer[(U256_BYTE_COUNT - bytes.len())..].copy_from_slice(bytes); + Self::from_byte_slice(&buffer).ok_or(FromByteSliceError::OutOfRange) + } + } + + /// Interprets the field element as a decimal number of a certain decimal places. + #[cfg(feature = "bigdecimal")] + pub fn to_big_decimal>(&self, decimals: D) -> bigdecimal::BigDecimal { + use num_bigint::{BigInt, Sign}; + + bigdecimal::BigDecimal::new( + BigInt::from_bytes_be(Sign::Plus, &self.to_bytes_be()), + decimals.into(), + ) + } + + /// Transforms [`FieldElement`] into little endian bit representation. + pub fn to_bits_le(self) -> [bool; 256] { + let mut bits = [false; 256]; + for (ind_element, element) in self.inner.into_bigint().0.iter().enumerate() { + for ind_bit in 0..64 { + bits[ind_element * 64 + ind_bit] = (element >> ind_bit) & 1 == 1; + } + } + + bits + } + + /// Convert the field element into a big-endian byte representation + pub fn to_bytes_be(&self) -> [u8; 32] { + let mut buffer = [0u8; 32]; + buffer.copy_from_slice(&self.inner.into_bigint().to_bytes_be()); + + buffer + } + + /// Transforms [`FieldElement`] into its Montgomery representation + pub const fn into_mont(self) -> [u64; 4] { + self.inner.0 .0 + } + + pub fn invert(&self) -> Option { + self.inner.inverse().map(|inner| Self { inner }) + } + + pub fn sqrt(&self) -> Option { + self.inner.sqrt().map(|inner| Self { inner }) + } + + pub fn double(&self) -> Self { + *self + *self + } + + /// Performs a floor division. It's not implemented as the `Div` trait on purpose to + /// distinguish from the "felt division". + pub fn floor_div(&self, rhs: Self) -> Self { + let lhs: U256 = self.into(); + let rhs: U256 = (&rhs).into(); + let is_rhs_zero: bool = rhs.is_zero().into(); + + if !is_rhs_zero { + let rhs = NonZero::from_uint(rhs); + + let div_result = lhs.div_rem(&rhs); + let (quotient, _) = div_result; + + // It's safe to unwrap here since `rem` is never out of range + Self { + inner: Fr::from_bigint(u256_to_biginteger256("ient)).unwrap(), + } + } else { + // TODO: add `checked_floor_div` for panic-less use + panic!("division by zero"); + } + } + + /// For internal use only. The input must be of length [`U256_BYTE_COUNT`]. + fn from_byte_slice(bytes: &[u8]) -> Option { + let mut bits = [false; U256_BYTE_COUNT * 8]; + for (ind_byte, byte) in bytes.iter().enumerate() { + for ind_bit in 0..8 { + bits[ind_byte * 8 + ind_bit] = (byte >> (7 - ind_bit)) & 1 == 1; + } + } + + // No need to check range as `from_bigint` already does that + let big_int = BigInteger256::from_bits_be(&bits); + Fr::from_bigint(big_int).map(|inner| Self { inner }) + } +} + +impl Default for FieldElement { + fn default() -> Self { + Self::ZERO + } +} + +impl AsRef for FieldElement { + fn as_ref(&self) -> &Self { + self + } +} + +impl ops::Add for FieldElement { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self { + inner: self.inner + rhs.inner, + } + } +} + +impl ops::AddAssign for FieldElement { + fn add_assign(&mut self, rhs: Self) { + self.inner = self.inner + rhs.inner; + } +} + +impl ops::Sub for FieldElement { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + Self { + inner: self.inner - rhs.inner, + } + } +} + +impl ops::SubAssign for FieldElement { + fn sub_assign(&mut self, rhs: Self) { + self.inner = self.inner - rhs.inner; + } +} + +impl ops::Mul for FieldElement { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + Self { + inner: self.inner * rhs.inner, + } + } +} + +impl ops::MulAssign for FieldElement { + fn mul_assign(&mut self, rhs: Self) { + self.inner = self.inner * rhs.inner; + } +} + +impl ops::Neg for FieldElement { + type Output = Self; + + fn neg(self) -> Self::Output { + Self { inner: -self.inner } + } +} + +impl ops::Rem for FieldElement { + type Output = Self; + + fn rem(self, rhs: Self) -> Self::Output { + if self.inner < rhs.inner { + return self; + } + + let lhs: U256 = (&self).into(); + let rhs: U256 = (&rhs).into(); + let is_rhs_zero: bool = rhs.is_zero().into(); + + if !is_rhs_zero { + let rhs = NonZero::from_uint(rhs); + + let (_, rem) = lhs.div_rem(&rhs); + + // It's safe to unwrap here since `rem` is never out of range + Self { + inner: Fr::from_bigint(u256_to_biginteger256(&rem)).unwrap(), + } + } else { + // TODO: add `checked_rem` for panic-less use + panic!("division by zero"); + } + } +} + +impl ops::BitAnd for FieldElement { + type Output = Self; + + fn bitand(self, rhs: Self) -> Self::Output { + let lhs: U256 = (&self).into(); + let rhs: U256 = (&rhs).into(); + + // It's safe to unwrap here since the result is never out of range + Self { + inner: Fr::from_bigint(u256_to_biginteger256(&(lhs & rhs))).unwrap(), + } + } +} + +impl ops::BitOr for FieldElement { + type Output = Self; + + fn bitor(self, rhs: Self) -> Self::Output { + let lhs: U256 = (&self).into(); + let rhs: U256 = (&rhs).into(); + + // It's safe to unwrap here since the result is never out of range + Self { + inner: Fr::from_bigint(u256_to_biginteger256(&(lhs | rhs))).unwrap(), + } + } +} + +impl core::iter::Sum for FieldElement { + fn sum>(iter: I) -> Self { + let mut sum = Self::ZERO; + iter.for_each(|item| { + sum += item; + }); + sum + } +} + +impl<'a> core::iter::Sum<&'a Self> for FieldElement { + fn sum>(iter: I) -> Self { + iter.copied().sum() + } +} + +impl fmt::Debug for FieldElement { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FieldElement") + .field("inner", &InnerDebug(self)) + .finish() + } +} + +impl fmt::Display for FieldElement { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Ported from: + // https://github.com/paritytech/parity-common/blob/b37d0b312d39fa47c61c4430b30ca87d90e45a08/uint/src/uint.rs#L1650 + + let repr: U256 = self.into(); + + if repr.is_zero().into() { + return write!(f, "0"); + } + + let mut buf = [0u8; 4 * 20]; + let mut i = buf.len() - 1; + let mut current = repr; + let ten = U256::from_u8(10u8); + + loop { + let digit = if current < ten { + current.to_words()[0] as u8 + } else { + (current.checked_rem(&ten)).unwrap().to_words()[0] as u8 + }; + buf[i] = digit + b'0'; + current = current.checked_div(&ten).unwrap(); + if current.is_zero().into() { + break; + } + i -= 1; + } + + // sequence of `'0'..'9'` chars is guaranteed to be a valid UTF8 string + let s = unsafe { str::from_utf8_unchecked(&buf[i..]) }; + f.pad_integral(true, "", s) + } +} + +impl fmt::LowerHex for FieldElement { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let repr: U256 = self.into(); + + let width = if f.sign_aware_zero_pad() { + f.width().unwrap().min(64) + } else { + 1 + }; + if f.alternate() { + write!(f, "0x")?; + } + let mut latch = false; + let mut ind_nibble = 0; + for ch in u256_to_u64_array(&repr).iter().rev() { + for x in 0..16 { + let nibble = (ch & (15u64 << ((15 - x) * 4) as u64)) >> (((15 - x) * 4) as u64); + if !latch { + latch = nibble != 0 || (64 - ind_nibble <= width); + } + if latch { + write!(f, "{nibble:x}")?; + } + ind_nibble += 1; + } + } + Ok(()) + } +} + +impl fmt::UpperHex for FieldElement { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let repr: U256 = self.into(); + + let width = if f.sign_aware_zero_pad() { + f.width().unwrap().min(64) + } else { + 1 + }; + if f.alternate() { + write!(f, "0x")?; + } + let mut latch = false; + let mut ind_nibble = 0; + for ch in u256_to_u64_array(&repr).iter().rev() { + for x in 0..16 { + let nibble = (ch & (15u64 << ((15 - x) * 4) as u64)) >> (((15 - x) * 4) as u64); + if !latch { + latch = nibble != 0 || (64 - ind_nibble <= width); + } + if latch { + write!(f, "{nibble:X}")?; + } + ind_nibble += 1; + } + } + Ok(()) + } +} + +#[cfg(feature = "serde")] +mod serde_field_element { + #[cfg(feature = "std")] + use core::fmt::{Formatter, Result as FmtResult}; + + use super::*; + #[cfg(not(feature = "std"))] + use alloc::{ + fmt::{Formatter, Result as FmtResult}, + string::ToString, + }; + use serde::{de::Visitor, Deserialize, Serialize}; + + struct FieldElementVisitor; + + impl Serialize for FieldElement { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&ToString::to_string(&self)) + } + } + + impl<'de> Deserialize<'de> for FieldElement { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_str(FieldElementVisitor) + } + } + + impl<'de> Visitor<'de> for FieldElementVisitor { + type Value = FieldElement; + + fn expecting(&self, formatter: &mut Formatter<'_>) -> FmtResult { + write!(formatter, "string") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + FieldElement::from_str(v).map_err(serde::de::Error::custom) + } + } +} + +impl From for FieldElement { + fn from(value: u8) -> Self { + Self { + inner: Fr::from_bigint(BigInteger256::new([value as u64, 0, 0, 0])).unwrap(), + } + } +} + +impl From for FieldElement { + fn from(value: u16) -> Self { + Self { + inner: Fr::from_bigint(BigInteger256::new([value as u64, 0, 0, 0])).unwrap(), + } + } +} + +impl From for FieldElement { + fn from(value: u32) -> Self { + Self { + inner: Fr::from_bigint(BigInteger256::new([value as u64, 0, 0, 0])).unwrap(), + } + } +} + +impl From for FieldElement { + fn from(value: u64) -> Self { + Self { + inner: Fr::from_bigint(BigInteger256::new([value, 0, 0, 0])).unwrap(), + } + } +} + +impl From for FieldElement { + fn from(value: u128) -> Self { + let low = value % (u64::MAX as u128 + 1); + let high = value / (u64::MAX as u128 + 1); + + Self { + inner: Fr::from_bigint(BigInteger256::new([low as u64, high as u64, 0, 0])).unwrap(), + } + } +} + +impl From for FieldElement { + fn from(value: usize) -> Self { + Self { + inner: Fr::from_bigint(BigInteger256::new([value as u64, 0, 0, 0])).unwrap(), + } + } +} + +impl FromStr for FieldElement { + type Err = FromStrError; + + fn from_str(s: &str) -> Result { + if s.starts_with("0x") { + Self::from_hex_be(s) + } else { + Self::from_dec_str(s) + } + } +} + +impl TryFrom for u8 { + type Error = ValueOutOfRangeError; + + fn try_from(value: FieldElement) -> Result { + let repr = value.inner.into_bigint().0; + if repr[0] > Self::MAX as u64 || repr[1] > 0 || repr[2] > 0 || repr[3] > 0 { + Err(ValueOutOfRangeError) + } else { + Ok(repr[0] as Self) + } + } +} + +impl TryFrom for u16 { + type Error = ValueOutOfRangeError; + + fn try_from(value: FieldElement) -> Result { + let repr = value.inner.into_bigint().0; + if repr[0] > Self::MAX as u64 || repr[1] > 0 || repr[2] > 0 || repr[3] > 0 { + Err(ValueOutOfRangeError) + } else { + Ok(repr[0] as Self) + } + } +} + +impl TryFrom for u32 { + type Error = ValueOutOfRangeError; + + fn try_from(value: FieldElement) -> Result { + let repr = value.inner.into_bigint().0; + if repr[0] > Self::MAX as u64 || repr[1] > 0 || repr[2] > 0 || repr[3] > 0 { + Err(ValueOutOfRangeError) + } else { + Ok(repr[0] as Self) + } + } +} + +impl TryFrom for u64 { + type Error = ValueOutOfRangeError; + + fn try_from(value: FieldElement) -> Result { + let repr = value.inner.into_bigint().0; + if repr[1] > 0 || repr[2] > 0 || repr[3] > 0 { + Err(ValueOutOfRangeError) + } else { + Ok(repr[0]) + } + } +} + +impl TryFrom for u128 { + type Error = ValueOutOfRangeError; + + fn try_from(value: FieldElement) -> Result { + let repr = value.inner.into_bigint().0; + if repr[2] > 0 || repr[3] > 0 { + Err(ValueOutOfRangeError) + } else { + Ok((repr[0] as Self) + (repr[1] as Self) * (u64::MAX as Self + 1)) + } + } +} + +impl From<&FieldElement> for U256 { + #[cfg(target_pointer_width = "64")] + fn from(value: &FieldElement) -> Self { + U256::from_words(value.inner.into_bigint().0) + } + + #[cfg(target_pointer_width = "32")] + fn from(value: &FieldElement) -> Self { + U256::from_words(unsafe { + core::mem::transmute::<[u64; 4], [u32; 8]>(value.inner.into_bigint().0) + }) + } +} + +impl<'a> fmt::Debug for InnerDebug<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:#064x}", self.0) + } +} + +#[inline] +fn u256_to_biginteger256(num: &U256) -> BigInteger256 { + BigInteger256::new(u256_to_u64_array(num)) +} + +#[cfg(target_pointer_width = "64")] +#[inline] +fn u256_to_u64_array(num: &U256) -> [u64; 4] { + num.to_words() +} + +#[cfg(target_pointer_width = "32")] +#[inline] +fn u256_to_u64_array(num: &U256) -> [u64; 4] { + unsafe { core::mem::transmute::<[u32; 8], [u64; 4]>(num.to_words()) } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + fn test_default_value() { + assert_eq!(FieldElement::default(), FieldElement::ZERO) + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + fn test_dec_fmt() { + let nums = [ + "0", + "1", + "10", + "11", + "3618502788666131213697322783095070105623107215331596699973092056135872020480", + ]; + + for num in &nums { + assert_eq!( + &format!("{}", FieldElement::from_dec_str(num).unwrap()), + num + ); + } + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + fn test_zero_padded_hex_fmt() { + let fe = FieldElement::from_hex_be("0x1234abcd").unwrap(); + + assert_eq!(format!("{fe:011x}"), "0001234abcd"); + assert_eq!(format!("{fe:011X}"), "0001234ABCD"); + assert_eq!(format!("{fe:08x}"), "1234abcd"); + assert_eq!(format!("{fe:06x}"), "1234abcd"); + assert_eq!(format!("{fe:#x}"), "0x1234abcd"); + assert_eq!( + format!("{fe:#064x}"), + "0x000000000000000000000000000000000000000000000000000000001234abcd" + ); + + // Ignore if requesting more than 64 nibbles (or should we not?) + assert_eq!( + format!("{fe:#0100x}"), + "0x000000000000000000000000000000000000000000000000000000001234abcd" + ); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + fn test_addition() { + let additions = [ + ["1", "1", "2"], + [ + "3618502788666131213697322783095070105623107215331596699973092056135872020480", + "1", + "0", + ], + ]; + + for item in &additions { + let mut lhs = FieldElement::from_dec_str(item[0]).unwrap(); + let rhs = FieldElement::from_dec_str(item[1]).unwrap(); + let result = FieldElement::from_dec_str(item[2]).unwrap(); + assert_eq!(lhs + rhs, result); + + lhs += rhs; + assert_eq!(lhs, result); + } + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + fn test_subtraction() { + let subtractions = [ + ["10", "7", "3"], + [ + "0", + "3618502788666131213697322783095070105623107215331596699973092056135872020480", + "1", + ], + ]; + + for item in &subtractions { + let mut lhs = FieldElement::from_dec_str(item[0]).unwrap(); + let rhs = FieldElement::from_dec_str(item[1]).unwrap(); + let result = FieldElement::from_dec_str(item[2]).unwrap(); + assert_eq!(lhs - rhs, result); + + lhs -= rhs; + assert_eq!(lhs, result); + } + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + fn test_multiplication() { + let multiplications = [ + ["2", "3", "6"], + [ + "3618502788666131213697322783095070105623107215331596699973092056135872020480", + "3618502788666131213697322783095070105623107215331596699973092056135872020480", + "1", + ], + [ + "3141592653589793238462643383279502884197169399375105820974944592307", + "8164062862089986280348253421170679821480865132823066470938446095505", + "514834056922159274131066670130609582664841480950767778400381816737396274242", + ], + ]; + + for item in &multiplications { + let mut lhs = FieldElement::from_dec_str(item[0]).unwrap(); + let rhs = FieldElement::from_dec_str(item[1]).unwrap(); + let result = FieldElement::from_dec_str(item[2]).unwrap(); + assert_eq!(lhs * rhs, result); + + lhs *= rhs; + assert_eq!(lhs, result); + } + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + fn test_remainder() { + let remainders = [["123456", "100", "56"], ["7", "3", "1"], ["3", "6", "3"]]; + + for item in &remainders { + assert_eq!( + FieldElement::from_dec_str(item[0]).unwrap() + % FieldElement::from_dec_str(item[1]).unwrap(), + FieldElement::from_dec_str(item[2]).unwrap() + ); + } + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + fn test_bitwise_and() { + let operands = [[123456_u64, 567890], [613221132151, 4523451]]; + + for item in &operands { + let lhs: FieldElement = item[0].into(); + let rhs: FieldElement = item[1].into(); + assert_eq!(lhs & rhs, (item[0] & item[1]).into()); + } + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + fn test_bitwise_or() { + let operands = [[123456_u64, 567890], [613221132151, 4523451]]; + + for item in &operands { + let lhs: FieldElement = item[0].into(); + let rhs: FieldElement = item[1].into(); + assert_eq!(lhs | rhs, (item[0] | item[1]).into()); + } + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + fn test_iter_sum() { + let elements = [FieldElement::ONE, FieldElement::TWO, FieldElement::THREE]; + + assert_eq!( + elements.iter().sum::(), + FieldElement::from_dec_str("6").unwrap() + ); + assert_eq!( + elements.into_iter().sum::(), + FieldElement::from_dec_str("6").unwrap() + ); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + fn test_floor_division() { + let quotients = [["123456", "100", "1234"], ["7", "3", "2"], ["3", "6", "0"]]; + + for item in "ients { + assert_eq!( + FieldElement::from_dec_str(item[0]) + .unwrap() + .floor_div(FieldElement::from_dec_str(item[1]).unwrap()), + FieldElement::from_dec_str(item[2]).unwrap() + ); + } + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + fn test_to_primitives() { + let fe_256: FieldElement = 256u16.into(); + + if u8::try_from(fe_256).is_ok() { + panic!("invalid conversion"); + } + + assert_eq!(u16::try_from(fe_256).unwrap(), 256u16); + assert_eq!(u32::try_from(fe_256).unwrap(), 256u32); + assert_eq!(u64::try_from(fe_256).unwrap(), 256u64); + } + + #[test] + #[cfg(feature = "bigdecimal")] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + fn test_to_big_decimal() { + use bigdecimal::{BigDecimal, Num}; + + let nums = [ + ( + "134500", + 5, + BigDecimal::from_str_radix("1.345", 10).unwrap(), + ), + ( + "134500", + 0, + BigDecimal::from_str_radix("134500", 10).unwrap(), + ), + ( + "134500", + 10, + BigDecimal::from_str_radix("0.00001345", 10).unwrap(), + ), + ]; + + for num in nums { + assert_eq!( + FieldElement::from_dec_str(num.0) + .unwrap() + .to_big_decimal(num.1), + num.2 + ); + } + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + fn test_from_str() { + let nums = [ + ("134500", "0x20d64"), + ("9999999999999999", "0x2386f26fc0ffff"), + ]; + + for num in nums { + assert_eq!( + num.0.parse::().unwrap(), + num.1.parse::().unwrap(), + ); + } + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + fn test_from_byte_slice_be() { + let nums = [("25800", [100u8, 200u8])]; + + for num in nums { + assert_eq!( + num.0.parse::().unwrap(), + FieldElement::from_byte_slice_be(&num.1).unwrap() + ); + } + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + fn test_u8_conversion() { + let nums = [u8::MAX, u8::MAX / 3 * 2, u8::MAX / 3]; + + for num in nums { + let felt: FieldElement = num.into(); + assert_eq!(format!("{}", felt), format!("{}", num)); + + let back_to_num: u8 = felt.try_into().unwrap(); + assert_eq!(num, back_to_num); + } + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + fn test_u16_conversion() { + let nums = [u16::MAX, u16::MAX / 3 * 2, u16::MAX / 3]; + + for num in nums { + let felt: FieldElement = num.into(); + assert_eq!(format!("{}", felt), format!("{}", num)); + + let back_to_num: u16 = felt.try_into().unwrap(); + assert_eq!(num, back_to_num); + } + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + fn test_u32_conversion() { + let nums = [u32::MAX, u32::MAX / 3 * 2, u32::MAX / 3]; + + for num in nums { + let felt: FieldElement = num.into(); + assert_eq!(format!("{}", felt), format!("{}", num)); + + let back_to_num: u32 = felt.try_into().unwrap(); + assert_eq!(num, back_to_num); + } + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + fn test_u64_conversion() { + let nums = [u64::MAX, u64::MAX / 3 * 2, u64::MAX / 3]; + + for num in nums { + let felt: FieldElement = num.into(); + assert_eq!(format!("{}", felt), format!("{}", num)); + + let back_to_num: u64 = felt.try_into().unwrap(); + assert_eq!(num, back_to_num); + } + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + fn test_u128_conversion() { + let nums = [u128::MAX, u128::MAX / 3 * 2, u128::MAX / 3]; + + for num in nums { + let felt: FieldElement = num.into(); + assert_eq!(format!("{}", felt), format!("{}", num)); + + let back_to_num: u128 = felt.try_into().unwrap(); + assert_eq!(num, back_to_num); + } + } +} diff --git a/starknet-macros/Cargo.toml b/starknet-macros/Cargo.toml index 78b1a782..d3c79ae9 100644 --- a/starknet-macros/Cargo.toml +++ b/starknet-macros/Cargo.toml @@ -22,3 +22,6 @@ syn = "2.0.15" [features] default = [] use_imported_type = [] + +[lints] +workspace = true diff --git a/starknet-macros/src/lib.rs b/starknet-macros/src/lib.rs index ebeebe38..79d1aade 100644 --- a/starknet-macros/src/lib.rs +++ b/starknet-macros/src/lib.rs @@ -121,6 +121,6 @@ fn field_element_path() -> &'static str { } #[cfg(not(feature = "use_imported_type"))] -fn field_element_path() -> &'static str { +const fn field_element_path() -> &'static str { "::starknet::core::types::Felt" } diff --git a/starknet-providers/Cargo.toml b/starknet-providers/Cargo.toml index bfcf2275..deddbf1b 100644 --- a/starknet-providers/Cargo.toml +++ b/starknet-providers/Cargo.toml @@ -39,3 +39,6 @@ default = [] no_unknown_fields = [ "starknet-core/no_unknown_fields" ] + +[lints] +workspace = true diff --git a/starknet-providers/src/any.rs b/starknet-providers/src/any.rs index 7bbd7f13..beff9c47 100644 --- a/starknet-providers/src/any.rs +++ b/starknet-providers/src/any.rs @@ -20,7 +20,7 @@ use crate::{ /// the [Provider] trait itself cannot be Box-ed due to the use of associated type. /// /// A recommended pattern is to make your business logic code (e.g. functions) generic over the -/// [Provider] trait, while using this [AnyProvider] type for bootstrapping your application. +/// [Provider] trait, while using this [`AnyProvider`] type for bootstrapping your application. /// /// NOTE: This type was introduced when [Provider] was not Box-able. It should be reviewed whether /// it's still needed anymore. diff --git a/starknet-providers/src/jsonrpc/mod.rs b/starknet-providers/src/jsonrpc/mod.rs index e57550cc..be9b9571 100644 --- a/starknet-providers/src/jsonrpc/mod.rs +++ b/starknet-providers/src/jsonrpc/mod.rs @@ -155,7 +155,7 @@ pub enum JsonRpcResponse { Error { id: u64, error: JsonRpcError }, } -/// Failures trying to parse a [JsonRpcError] into [StarknetError]. +/// Failures trying to parse a [`JsonRpcError`] into [`StarknetError`]. #[derive(Debug, thiserror::Error)] pub enum JsonRpcErrorConversionError { #[error("unknown error code")] @@ -175,7 +175,7 @@ struct Felt(#[serde_as(as = "UfeHex")] pub FeltPrimitive); struct FeltArray(#[serde_as(as = "Vec")] pub Vec); impl JsonRpcClient { - pub fn new(transport: T) -> Self { + pub const fn new(transport: T) -> Self { Self { transport } } } @@ -884,16 +884,16 @@ impl TryFrom<&JsonRpcError> for StarknetError { fn try_from(value: &JsonRpcError) -> Result { match value.code { - 1 => Ok(StarknetError::FailedToReceiveTransaction), - 20 => Ok(StarknetError::ContractNotFound), - 24 => Ok(StarknetError::BlockNotFound), - 27 => Ok(StarknetError::InvalidTransactionIndex), - 28 => Ok(StarknetError::ClassHashNotFound), - 29 => Ok(StarknetError::TransactionHashNotFound), - 31 => Ok(StarknetError::PageSizeTooBig), - 32 => Ok(StarknetError::NoBlocks), - 33 => Ok(StarknetError::InvalidContinuationToken), - 34 => Ok(StarknetError::TooManyKeysInFilter), + 1 => Ok(Self::FailedToReceiveTransaction), + 20 => Ok(Self::ContractNotFound), + 24 => Ok(Self::BlockNotFound), + 27 => Ok(Self::InvalidTransactionIndex), + 28 => Ok(Self::ClassHashNotFound), + 29 => Ok(Self::TransactionHashNotFound), + 31 => Ok(Self::PageSizeTooBig), + 32 => Ok(Self::NoBlocks), + 33 => Ok(Self::InvalidContinuationToken), + 34 => Ok(Self::TooManyKeysInFilter), 40 => { let data = ContractErrorData::deserialize( value @@ -902,7 +902,7 @@ impl TryFrom<&JsonRpcError> for StarknetError { .ok_or(JsonRpcErrorConversionError::MissingData)?, ) .map_err(|_| JsonRpcErrorConversionError::DataParsingFailure)?; - Ok(StarknetError::ContractError(data)) + Ok(Self::ContractError(data)) } 41 => { let data = TransactionExecutionErrorData::deserialize( @@ -912,12 +912,12 @@ impl TryFrom<&JsonRpcError> for StarknetError { .ok_or(JsonRpcErrorConversionError::MissingData)?, ) .map_err(|_| JsonRpcErrorConversionError::DataParsingFailure)?; - Ok(StarknetError::TransactionExecutionError(data)) + Ok(Self::TransactionExecutionError(data)) } - 51 => Ok(StarknetError::ClassAlreadyDeclared), - 52 => Ok(StarknetError::InvalidTransactionNonce), - 53 => Ok(StarknetError::InsufficientMaxFee), - 54 => Ok(StarknetError::InsufficientAccountBalance), + 51 => Ok(Self::ClassAlreadyDeclared), + 52 => Ok(Self::InvalidTransactionNonce), + 53 => Ok(Self::InsufficientMaxFee), + 54 => Ok(Self::InsufficientAccountBalance), 55 => { let data = String::deserialize( value @@ -926,15 +926,15 @@ impl TryFrom<&JsonRpcError> for StarknetError { .ok_or(JsonRpcErrorConversionError::MissingData)?, ) .map_err(|_| JsonRpcErrorConversionError::DataParsingFailure)?; - Ok(StarknetError::ValidationFailure(data)) + Ok(Self::ValidationFailure(data)) } - 56 => Ok(StarknetError::CompilationFailed), - 57 => Ok(StarknetError::ContractClassSizeIsTooLarge), - 58 => Ok(StarknetError::NonAccount), - 59 => Ok(StarknetError::DuplicateTx), - 60 => Ok(StarknetError::CompiledClassHashMismatch), - 61 => Ok(StarknetError::UnsupportedTxVersion), - 62 => Ok(StarknetError::UnsupportedContractClassVersion), + 56 => Ok(Self::CompilationFailed), + 57 => Ok(Self::ContractClassSizeIsTooLarge), + 58 => Ok(Self::NonAccount), + 59 => Ok(Self::DuplicateTx), + 60 => Ok(Self::CompiledClassHashMismatch), + 61 => Ok(Self::UnsupportedTxVersion), + 62 => Ok(Self::UnsupportedContractClassVersion), 63 => { let data = String::deserialize( value @@ -943,7 +943,7 @@ impl TryFrom<&JsonRpcError> for StarknetError { .ok_or(JsonRpcErrorConversionError::MissingData)?, ) .map_err(|_| JsonRpcErrorConversionError::DataParsingFailure)?; - Ok(StarknetError::UnexpectedError(data)) + Ok(Self::UnexpectedError(data)) } 10 => { let data = NoTraceAvailableErrorData::deserialize( @@ -953,7 +953,7 @@ impl TryFrom<&JsonRpcError> for StarknetError { .ok_or(JsonRpcErrorConversionError::MissingData)?, ) .map_err(|_| JsonRpcErrorConversionError::DataParsingFailure)?; - Ok(StarknetError::NoTraceAvailable(data)) + Ok(Self::NoTraceAvailable(data)) } _ => Err(JsonRpcErrorConversionError::UnknownCode), } diff --git a/starknet-providers/src/jsonrpc/transports/http.rs b/starknet-providers/src/jsonrpc/transports/http.rs index bd54d8b4..f50bb46a 100644 --- a/starknet-providers/src/jsonrpc/transports/http.rs +++ b/starknet-providers/src/jsonrpc/transports/http.rs @@ -40,8 +40,8 @@ impl HttpTransport { } } - /// Consumes the current [HttpTransport] instance and returns a new one with the header - /// appended. Same as calling [add_header]. + /// Consumes the current [`HttpTransport`] instance and returns a new one with the header + /// appended. Same as calling [`add_header`]. pub fn with_header(self, name: String, value: String) -> Self { let mut headers = self.headers; headers.push((name, value)); @@ -88,7 +88,7 @@ impl JsonRpcTransport for HttpTransport { .post(self.url.clone()) .body(request_body) .header("Content-Type", "application/json"); - for (name, value) in self.headers.iter() { + for (name, value) in &self.headers { request = request.header(name, value); } diff --git a/starknet-providers/src/provider.rs b/starknet-providers/src/provider.rs index 0ee4958a..1d0588da 100644 --- a/starknet-providers/src/provider.rs +++ b/starknet-providers/src/provider.rs @@ -302,10 +302,10 @@ pub trait Provider { /// Trait for implementation-specific error type. These errors are irrelevant in most cases, /// assuming that users typically care more about the specifics of RPC errors instead of the -/// underlying transport. Therefore, it makes little sense to bloat [ProviderError] with a generic +/// underlying transport. Therefore, it makes little sense to bloat [`ProviderError`] with a generic /// parameter just for these errors. Instead, they're erased to this trait object. /// -/// This trait is used instead of a plain [std::error::Error] to allow downcasting, in case access +/// This trait is used instead of a plain [`std::error::Error`] to allow downcasting, in case access /// to the specific error type is indeed desired. This is achieved with the `as_any()` method. pub trait ProviderImplError: Error + Debug + Send + Sync { fn as_any(&self) -> &dyn Any; diff --git a/starknet-providers/src/sequencer/mod.rs b/starknet-providers/src/sequencer/mod.rs index 8e83a11e..6da6a47a 100644 --- a/starknet-providers/src/sequencer/mod.rs +++ b/starknet-providers/src/sequencer/mod.rs @@ -37,7 +37,7 @@ pub enum GatewayClientError { /// JSON serialization/deserialization error #[error(transparent)] Serde(SerdeJsonError), - /// Sequencer error responses not parsable into [StarknetError] + /// Sequencer error responses not parsable into [`StarknetError`] #[error(transparent)] SequencerError(SequencerError), /// Method is not supported (only when using as [Provider]) @@ -139,8 +139,8 @@ impl SequencerGatewayProvider { ) } - /// Consumes the current [SequencerGatewayProvider] instance and returns a new one with the - /// header appended. Same as calling [add_header]. + /// Consumes the current [`SequencerGatewayProvider`] instance and returns a new one with the + /// header appended. Same as calling [`add_header`]. pub fn with_header(self, name: String, value: String) -> Self { let mut headers = self.headers; headers.push((name, value)); @@ -197,7 +197,7 @@ impl SequencerGatewayProvider { trace!("Sending GET request to sequencer API ({})", url); let mut request = self.client.get(url); - for (name, value) in self.headers.iter() { + for (name, value) in &self.headers { request = request.header(name, value); } @@ -231,7 +231,7 @@ impl SequencerGatewayProvider { .post(url) .header("Content-Type", "application/json") .body(request_body); - for (name, value) in self.headers.iter() { + for (name, value) in &self.headers { request = request.header(name, value); } @@ -460,14 +460,16 @@ impl From for ProviderError { fn from(value: SequencerError) -> Self { let matching_code = match value.code { ErrorCode::BlockNotFound => Some(StarknetError::BlockNotFound), - ErrorCode::EntryPointNotFoundInContract => None, + ErrorCode::EntryPointNotFoundInContract | + ErrorCode::InvalidContractClass | + ErrorCode::DeprecatedEndpoint | + ErrorCode::MalformedRequest | ErrorCode::InvalidProgram => None, ErrorCode::TransactionFailed => { Some(StarknetError::ValidationFailure(value.message.clone())) } - ErrorCode::TransactionNotFound => Some(StarknetError::ContractNotFound), + ErrorCode::TransactionNotFound | ErrorCode::UninitializedContract => Some(StarknetError::ContractNotFound), - ErrorCode::MalformedRequest => None, ErrorCode::UndeclaredClass => Some(StarknetError::ClassHashNotFound), ErrorCode::InvalidTransactionNonce => Some(StarknetError::InvalidTransactionNonce), ErrorCode::ValidateFailure => { @@ -477,12 +479,10 @@ impl From for ProviderError { ErrorCode::CompilationFailed => Some(StarknetError::CompilationFailed), ErrorCode::InvalidCompiledClassHash => Some(StarknetError::CompiledClassHashMismatch), ErrorCode::DuplicatedTransaction => Some(StarknetError::DuplicateTx), - ErrorCode::InvalidContractClass => None, - ErrorCode::DeprecatedEndpoint => None, }; match matching_code { - Some(code) => ProviderError::StarknetError(code), + Some(code) => Self::StarknetError(code), None => GatewayClientError::SequencerError(value).into(), } } @@ -524,10 +524,10 @@ where { let temp_value = serde_json::Value::deserialize(deserializer)?; if let Ok(value) = T::deserialize(&temp_value) { - return Ok(GatewayResponse::Data(value)); + return Ok(Self::Data(value)); } if let Ok(value) = SequencerError::deserialize(&temp_value) { - return Ok(GatewayResponse::SequencerError(value)); + return Ok(Self::SequencerError(value)); } Err(serde::de::Error::custom( "data did not match any variant of enum GatewayResponse", @@ -569,7 +569,6 @@ mod tests { include_str!("../../test-data/raw_gateway_responses/get_class_by_hash/1_cairo_0.txt"), include_str!("../../test-data/raw_gateway_responses/get_class_by_hash/3_cairo_1.txt"), ] - .into_iter() { serde_json::from_str::>(raw).unwrap(); } diff --git a/starknet-providers/src/sequencer/models/contract.rs b/starknet-providers/src/sequencer/models/contract.rs index 04584d51..0ef6a2e6 100644 --- a/starknet-providers/src/sequencer/models/contract.rs +++ b/starknet-providers/src/sequencer/models/contract.rs @@ -91,7 +91,7 @@ impl CompressedSierraClass { let compressed_program = gzip_encoder.finish().map_err(DecompressProgramError::Io)?; - Ok(CompressedSierraClass { + Ok(Self { sierra_program: compressed_program, contract_class_version: flattened_class.contract_class_version.clone(), entry_points_by_type: flattened_class.entry_points_by_type.clone(), diff --git a/starknet-providers/src/sequencer/models/conversions.rs b/starknet-providers/src/sequencer/models/conversions.rs index c1c0d618..deb392b7 100644 --- a/starknet-providers/src/sequencer/models/conversions.rs +++ b/starknet-providers/src/sequencer/models/conversions.rs @@ -604,7 +604,7 @@ impl TryFrom for core::TransactionFinalityStatus { fn try_from(value: TransactionFinalityStatus) -> Result { match value { - TransactionFinalityStatus::NotReceived => Err(ConversionError), + TransactionFinalityStatus::NotReceived | TransactionFinalityStatus::Received => Err(ConversionError), TransactionFinalityStatus::AcceptedOnL2 => Ok(Self::AcceptedOnL2), TransactionFinalityStatus::AcceptedOnL1 => Ok(Self::AcceptedOnL1), @@ -902,7 +902,7 @@ impl TryFrom for core::TransactionStatus { type Error = ConversionError; fn try_from(value: TransactionStatusInfo) -> Result { - if let TransactionStatus::Rejected = value.status { + if value.status == TransactionStatus::Rejected { return Ok(Self::Rejected); } @@ -1209,7 +1209,7 @@ fn convert_execution_result( } } -fn convert_legacy_entry_point( +const fn convert_legacy_entry_point( value: core::LegacyContractEntryPoint, ) -> contract_legacy::RawLegacyEntryPoint { // WARNING: this causes pre-0.11.0 contract declaration to fail due to `offset` issue diff --git a/starknet-providers/src/sequencer/models/serde_impls.rs b/starknet-providers/src/sequencer/models/serde_impls.rs index f54d344f..fda5b8e7 100644 --- a/starknet-providers/src/sequencer/models/serde_impls.rs +++ b/starknet-providers/src/sequencer/models/serde_impls.rs @@ -20,7 +20,7 @@ pub(crate) mod u64_hex { impl<'de> Visitor<'de> for U64HexVisitor { type Value = u64; - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(formatter, "string") } @@ -56,7 +56,7 @@ pub(crate) mod u128_hex { impl<'de> Visitor<'de> for U128HexVisitor { type Value = u128; - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(formatter, "string") } @@ -95,7 +95,7 @@ pub(crate) mod u64_hex_opt { impl<'de> Visitor<'de> for U64HexOptVisitor { type Value = Option; - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(formatter, "null or string") } diff --git a/starknet-providers/src/sequencer/models/transaction.rs b/starknet-providers/src/sequencer/models/transaction.rs index db625f16..2b25c79a 100644 --- a/starknet-providers/src/sequencer/models/transaction.rs +++ b/starknet-providers/src/sequencer/models/transaction.rs @@ -246,13 +246,13 @@ pub enum DataAvailabilityMode { struct DataAvailabilityModeVisitor; impl TransactionType { - pub fn transaction_hash(&self) -> Felt { + pub const fn transaction_hash(&self) -> Felt { match self { - TransactionType::Declare(inner) => inner.transaction_hash, - TransactionType::Deploy(inner) => inner.transaction_hash, - TransactionType::DeployAccount(inner) => inner.transaction_hash, - TransactionType::InvokeFunction(inner) => inner.transaction_hash, - TransactionType::L1Handler(inner) => inner.transaction_hash, + Self::Declare(inner) => inner.transaction_hash, + Self::Deploy(inner) => inner.transaction_hash, + Self::DeployAccount(inner) => inner.transaction_hash, + Self::InvokeFunction(inner) => inner.transaction_hash, + Self::L1Handler(inner) => inner.transaction_hash, } } } @@ -281,7 +281,7 @@ impl<'de> Deserialize<'de> for DataAvailabilityMode { impl<'de> Visitor<'de> for DataAvailabilityModeVisitor { type Value = DataAvailabilityMode; - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(formatter, "integer") } diff --git a/starknet-providers/src/sequencer/provider.rs b/starknet-providers/src/sequencer/provider.rs index b6b44bcd..86ffac80 100644 --- a/starknet-providers/src/sequencer/provider.rs +++ b/starknet-providers/src/sequencer/provider.rs @@ -113,7 +113,7 @@ impl Provider for SequencerGatewayProvider { .await?; // `NotReceived` is not a valid status for JSON-RPC. It's an error. - if let Some(TransactionFinalityStatus::NotReceived) = &status.finality_status { + if matches!(&status.finality_status, Some(TransactionFinalityStatus::NotReceived)) { return Err(ProviderError::StarknetError( StarknetError::TransactionHashNotFound, )); diff --git a/starknet-providers/tests/jsonrpc.rs b/starknet-providers/tests/jsonrpc.rs index bddd2ab4..6910ce49 100644 --- a/starknet-providers/tests/jsonrpc.rs +++ b/starknet-providers/tests/jsonrpc.rs @@ -17,6 +17,7 @@ use starknet_providers::{ use url::Url; fn create_jsonrpc_client() -> JsonRpcClient { + #[allow(clippy::or_fun_call)] let rpc_url = std::env::var("STARKNET_RPC") .unwrap_or("https://pathfinder.rpc.sepolia.starknet.rs/rpc/v0_7".into()); JsonRpcClient::new(HttpTransport::new(Url::parse(&rpc_url).unwrap())) diff --git a/starknet-signers/Cargo.toml b/starknet-signers/Cargo.toml index 98e27026..63843fde 100644 --- a/starknet-signers/Cargo.toml +++ b/starknet-signers/Cargo.toml @@ -29,3 +29,6 @@ getrandom = { version = "0.2.9", features = ["js"] } [target.'cfg(target_arch = "wasm32")'.dev-dependencies] wasm-bindgen-test = "0.3.34" + +[lints] +workspace = true diff --git a/starknet-signers/src/key_pair.rs b/starknet-signers/src/key_pair.rs index 31a7cefe..65047f20 100644 --- a/starknet-signers/src/key_pair.rs +++ b/starknet-signers/src/key_pair.rs @@ -47,7 +47,7 @@ impl SigningKey { Self { secret_scalar } } - pub fn from_secret_scalar(secret_scalar: Felt) -> Self { + pub const fn from_secret_scalar(secret_scalar: Felt) -> Self { Self { secret_scalar } } @@ -92,7 +92,7 @@ impl SigningKey { Ok(()) } - pub fn secret_scalar(&self) -> Felt { + pub const fn secret_scalar(&self) -> Felt { self.secret_scalar } @@ -106,11 +106,11 @@ impl SigningKey { } impl VerifyingKey { - pub fn from_scalar(scalar: Felt) -> Self { + pub const fn from_scalar(scalar: Felt) -> Self { Self { scalar } } - pub fn scalar(&self) -> Felt { + pub const fn scalar(&self) -> Felt { self.scalar }