diff --git a/crates/primitives/src/genesis.rs b/crates/primitives/src/genesis.rs index dd1f4a0ffd..02b4700fe8 100644 --- a/crates/primitives/src/genesis.rs +++ b/crates/primitives/src/genesis.rs @@ -2,8 +2,9 @@ use crate::{ constants::EMPTY_ROOT_HASH, keccak256, serde_helper::{ - deserialize_json_ttd_opt, deserialize_json_u256, deserialize_storage_map, + json_u256::{deserialize_json_ttd_opt, deserialize_json_u256}, num::{u64_hex_or_decimal, u64_hex_or_decimal_opt}, + storage::deserialize_storage_map, }, trie::{HashBuilder, Nibbles}, Account, Address, Bytes, B256, KECCAK_EMPTY, U256, diff --git a/crates/primitives/src/serde_helper/mod.rs b/crates/primitives/src/serde_helper/mod.rs index 6e78c5a798..2e897ebcda 100644 --- a/crates/primitives/src/serde_helper/mod.rs +++ b/crates/primitives/src/serde_helper/mod.rs @@ -1,73 +1,6 @@ //! [serde] utilities. -use crate::{B256, U64}; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; - -mod storage; -pub use storage::*; - -pub use reth_rpc_types::json_u256::*; - -pub mod num; +pub use reth_rpc_types::serde_helpers::*; mod prune; pub use prune::deserialize_opt_prune_mode_with_min_blocks; - -/// serde functions for handling primitive `u64` as [`U64`]. -pub mod u64_hex { - use super::*; - - /// Deserializes an `u64` from [U64] accepting a hex quantity string with optional 0x prefix - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - U64::deserialize(deserializer).map(|val| val.to()) - } - - /// Serializes u64 as hex string - pub fn serialize(value: &u64, s: S) -> Result { - U64::from(*value).serialize(s) - } -} - -/// Serialize a byte vec as a hex string _without_ the "0x" prefix. -/// -/// This behaves the same as [`hex::encode`](crate::hex::encode). -pub fn serialize_hex_string_no_prefix(x: T, s: S) -> Result -where - S: Serializer, - T: AsRef<[u8]>, -{ - s.serialize_str(&crate::hex::encode(x.as_ref())) -} - -/// Serialize a [B256] as a hex string _without_ the "0x" prefix. -pub fn serialize_b256_hex_string_no_prefix(x: &B256, s: S) -> Result -where - S: Serializer, -{ - s.serialize_str(&format!("{x:x}")) -} - -#[cfg(test)] -mod tests { - use super::*; - use serde::{Deserialize, Serialize}; - - #[test] - fn test_hex_u64() { - #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] - struct Value { - #[serde(with = "u64_hex")] - inner: u64, - } - - let val = Value { inner: 1000 }; - let s = serde_json::to_string(&val).unwrap(); - assert_eq!(s, "{\"inner\":\"0x3e8\"}"); - - let deserialized: Value = serde_json::from_str(&s).unwrap(); - assert_eq!(val, deserialized); - } -} diff --git a/crates/primitives/src/serde_helper/num.rs b/crates/primitives/src/serde_helper/num.rs deleted file mode 100644 index e2262ccca7..0000000000 --- a/crates/primitives/src/serde_helper/num.rs +++ /dev/null @@ -1,216 +0,0 @@ -//! Numeric helpers - -use crate::{U256, U64}; -use serde::{de, Deserialize, Deserializer, Serialize}; -use std::str::FromStr; - -/// A `u64` wrapper type that deserializes from hex or a u64 and serializes as hex. -/// -/// -/// ```rust -/// use reth_primitives::serde_helper::num::U64HexOrNumber; -/// let number_json = "100"; -/// let hex_json = "\"0x64\""; -/// -/// let number: U64HexOrNumber = serde_json::from_str(number_json).unwrap(); -/// let hex: U64HexOrNumber = serde_json::from_str(hex_json).unwrap(); -/// assert_eq!(number, hex); -/// assert_eq!(hex.to(), 100); -/// ``` -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] -pub struct U64HexOrNumber(U64); - -impl U64HexOrNumber { - /// Returns the wrapped u64 - pub fn to(self) -> u64 { - self.0.to() - } -} - -impl From for U64HexOrNumber { - fn from(value: u64) -> Self { - Self(U64::from(value)) - } -} - -impl From for U64HexOrNumber { - fn from(value: U64) -> Self { - Self(value) - } -} - -impl From for u64 { - fn from(value: U64HexOrNumber) -> Self { - value.to() - } -} - -impl From for U64 { - fn from(value: U64HexOrNumber) -> Self { - value.0 - } -} - -impl<'de> Deserialize<'de> for U64HexOrNumber { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - #[derive(Deserialize)] - #[serde(untagged)] - enum NumberOrHexU64 { - Hex(U64), - Int(u64), - } - match NumberOrHexU64::deserialize(deserializer)? { - NumberOrHexU64::Int(val) => Ok(val.into()), - NumberOrHexU64::Hex(val) => Ok(val.into()), - } - } -} - -/// serde functions for handling primitive `u64` as [U64] -pub mod u64_hex_or_decimal { - use crate::serde_helper::num::U64HexOrNumber; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - - /// Deserializes an `u64` accepting a hex quantity string with optional 0x prefix or - /// a number - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - U64HexOrNumber::deserialize(deserializer).map(Into::into) - } - - /// Serializes u64 as hex string - pub fn serialize(value: &u64, s: S) -> Result { - U64HexOrNumber::from(*value).serialize(s) - } -} - -/// serde functions for handling primitive optional `u64` as [U64] -pub mod u64_hex_or_decimal_opt { - use crate::serde_helper::num::U64HexOrNumber; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - - /// Deserializes an `u64` accepting a hex quantity string with optional 0x prefix or - /// a number - pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - match Option::::deserialize(deserializer)? { - Some(val) => Ok(Some(val.into())), - None => Ok(None), - } - } - - /// Serializes u64 as hex string - pub fn serialize(value: &Option, s: S) -> Result { - match value { - Some(val) => U64HexOrNumber::from(*val).serialize(s), - None => s.serialize_none(), - } - } -} - -/// Deserializes the input into an `Option`, using [`from_int_or_hex`] to deserialize the -/// inner value. -pub fn from_int_or_hex_opt<'de, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - match Option::::deserialize(deserializer)? { - Some(val) => val.try_into_u256().map(Some), - None => Ok(None), - } -} - -/// Deserializes the input into a U256, accepting both 0x-prefixed hex and decimal strings with -/// arbitrary precision, defined by serde_json's [`Number`](serde_json::Number). -pub fn from_int_or_hex<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - NumberOrHexU256::deserialize(deserializer)?.try_into_u256() -} - -#[derive(Deserialize)] -#[serde(untagged)] -enum NumberOrHexU256 { - Int(serde_json::Number), - Hex(U256), -} - -impl NumberOrHexU256 { - fn try_into_u256(self) -> Result { - match self { - NumberOrHexU256::Int(num) => { - U256::from_str(num.to_string().as_str()).map_err(E::custom) - } - NumberOrHexU256::Hex(val) => Ok(val), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_u256_int_or_hex() { - #[derive(Debug, Deserialize, PartialEq, Eq)] - struct V(#[serde(deserialize_with = "from_int_or_hex")] U256); - - proptest::proptest!(|(value: u64)| { - let u256_val = U256::from(value); - - let num_obj = serde_json::to_string(&value).unwrap(); - let hex_obj = serde_json::to_string(&u256_val).unwrap(); - - let int_val:V = serde_json::from_str(&num_obj).unwrap(); - let hex_val = serde_json::from_str(&hex_obj).unwrap(); - assert_eq!(int_val, hex_val); - }); - } - - #[test] - fn test_u256_int_or_hex_opt() { - #[derive(Debug, Deserialize, PartialEq, Eq)] - struct V(#[serde(deserialize_with = "from_int_or_hex_opt")] Option); - - let null = serde_json::to_string(&None::).unwrap(); - let val: V = serde_json::from_str(&null).unwrap(); - assert!(val.0.is_none()); - - proptest::proptest!(|(value: u64)| { - let u256_val = U256::from(value); - - let num_obj = serde_json::to_string(&value).unwrap(); - let hex_obj = serde_json::to_string(&u256_val).unwrap(); - - let int_val:V = serde_json::from_str(&num_obj).unwrap(); - let hex_val = serde_json::from_str(&hex_obj).unwrap(); - assert_eq!(int_val, hex_val); - assert_eq!(int_val.0, Some(u256_val)); - }); - } - - #[test] - fn serde_hex_or_number_u64() { - #[derive(Debug, Deserialize, PartialEq, Eq)] - struct V(U64HexOrNumber); - - proptest::proptest!(|(value: u64)| { - let val = U64::from(value); - - let num_obj = serde_json::to_string(&value).unwrap(); - let hex_obj = serde_json::to_string(&val).unwrap(); - - let int_val:V = serde_json::from_str(&num_obj).unwrap(); - let hex_val = serde_json::from_str(&hex_obj).unwrap(); - assert_eq!(int_val, hex_val); - }); - } -} diff --git a/crates/primitives/src/serde_helper/storage.rs b/crates/primitives/src/serde_helper/storage.rs deleted file mode 100644 index 7d0b5045f4..0000000000 --- a/crates/primitives/src/serde_helper/storage.rs +++ /dev/null @@ -1,102 +0,0 @@ -use crate::{Bytes, B256, U256}; -use serde::{Deserialize, Deserializer, Serialize}; -use std::{collections::HashMap, fmt::Write}; - -/// A storage key type that can be serialized to and from a hex string up to 32 bytes. Used for -/// `eth_getStorageAt` and `eth_getProof` RPCs. -/// -/// This is a wrapper type meant to mirror geth's serialization and deserialization behavior for -/// storage keys. -/// -/// In `eth_getStorageAt`, this is used for deserialization of the `index` field. Internally, the -/// index is a [B256], but in `eth_getStorageAt` requests, its serialization can be _up to_ 32 -/// bytes. To support this, the storage key is deserialized first as a U256, and converted to a -/// B256 for use internally. -/// -/// `eth_getProof` also takes storage keys up to 32 bytes as input, so the `keys` field is -/// similarly deserialized. However, geth populates the storage proof `key` fields in the response -/// by mirroring the `key` field used in the input. -/// * See how `storageKey`s (the input) are populated in the `StorageResult` (the output): -/// -/// -/// The contained [B256] and From implementation for String are used to preserve the input and -/// implement this behavior from geth. -#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -#[serde(from = "U256", into = "String")] -pub struct JsonStorageKey(pub B256); - -impl From for JsonStorageKey { - fn from(value: U256) -> Self { - // SAFETY: Address (B256) and U256 have the same number of bytes - JsonStorageKey(B256::from(value.to_be_bytes())) - } -} - -impl From for String { - fn from(value: JsonStorageKey) -> Self { - // SAFETY: Address (B256) and U256 have the same number of bytes - let uint = U256::from_be_bytes(value.0 .0); - - // serialize byte by byte - // - // this is mainly so we can return an output that hive testing expects, because the - // `eth_getProof` implementation in geth simply mirrors the input - // - // see the use of `hexKey` in the `eth_getProof` response: - // - let bytes = uint.to_be_bytes_trimmed_vec(); - let mut hex = String::with_capacity(2 + bytes.len() * 2); - hex.push_str("0x"); - for byte in bytes { - write!(hex, "{:02x}", byte).unwrap(); - } - hex - } -} - -/// Converts a Bytes value into a B256, accepting inputs that are less than 32 bytes long. These -/// inputs will be left padded with zeros. -pub fn from_bytes_to_b256<'de, D>(bytes: Bytes) -> Result -where - D: Deserializer<'de>, -{ - if bytes.0.len() > 32 { - return Err(serde::de::Error::custom("input too long to be a B256")) - } - - // left pad with zeros to 32 bytes - let mut padded = [0u8; 32]; - padded[32 - bytes.0.len()..].copy_from_slice(&bytes.0); - - // then convert to B256 without a panic - Ok(B256::from_slice(&padded)) -} - -/// Deserializes the input into an Option>, using [from_bytes_to_b256] which -/// allows cropped values: -/// -/// ```json -/// { -/// "0x0000000000000000000000000000000000000000000000000000000000000001": "0x22" -/// } -/// ``` -pub fn deserialize_storage_map<'de, D>( - deserializer: D, -) -> Result>, D::Error> -where - D: Deserializer<'de>, -{ - let map = Option::>::deserialize(deserializer)?; - match map { - Some(mut map) => { - let mut res_map = HashMap::with_capacity(map.len()); - for (k, v) in map.drain() { - let k_deserialized = from_bytes_to_b256::<'de, D>(k)?; - let v_deserialized = from_bytes_to_b256::<'de, D>(v)?; - res_map.insert(k_deserialized, v_deserialized); - } - Ok(Some(res_map)) - } - None => Ok(None), - } -} diff --git a/crates/rpc/rpc-types/src/eth/engine/payload.rs b/crates/rpc/rpc-types/src/eth/engine/payload.rs index 739a6a10ad..9ae18a351b 100644 --- a/crates/rpc/rpc-types/src/eth/engine/payload.rs +++ b/crates/rpc/rpc-types/src/eth/engine/payload.rs @@ -447,7 +447,7 @@ pub struct OptimismPayloadAttributes { #[serde( default, skip_serializing_if = "Option::is_none", - deserialize_with = "crate::serde_helpers::u64_hex::u64_hex_opt::deserialize" + deserialize_with = "crate::serde_helpers::u64_hex_opt::deserialize" )] pub gas_limit: Option, } diff --git a/crates/rpc/rpc-types/src/lib.rs b/crates/rpc/rpc-types/src/lib.rs index 8c9e22485b..f71f766081 100644 --- a/crates/rpc/rpc-types/src/lib.rs +++ b/crates/rpc/rpc-types/src/lib.rs @@ -20,7 +20,7 @@ mod otterscan; mod peer; pub mod relay; mod rpc; -mod serde_helpers; +pub mod serde_helpers; pub use admin::*; pub use eth::*; diff --git a/crates/rpc/rpc-types/src/serde_helpers/mod.rs b/crates/rpc/rpc-types/src/serde_helpers/mod.rs index 1c45b0d56d..adeb4a2458 100644 --- a/crates/rpc/rpc-types/src/serde_helpers/mod.rs +++ b/crates/rpc/rpc-types/src/serde_helpers/mod.rs @@ -1,22 +1,18 @@ //! Serde helpers for primitive types. -use alloy_primitives::U256; -use serde::{Deserialize, Deserializer, Serializer}; +use alloy_primitives::B256; +use serde::Serializer; pub mod json_u256; +pub use json_u256::JsonU256; + +/// Helpers for dealing with numbers. pub mod num; +pub use num::*; + /// Storage related helpers. pub mod storage; -pub mod u64_hex; - -/// Deserializes the input into a U256, accepting both 0x-prefixed hex and decimal strings with -/// arbitrary precision, defined by serde_json's [`Number`](serde_json::Number). -pub fn from_int_or_hex<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - num::NumberOrHexU256::deserialize(deserializer)?.try_into_u256() -} +pub use storage::JsonStorageKey; /// Serialize a byte vec as a hex string _without_ the "0x" prefix. /// @@ -28,3 +24,11 @@ where { s.serialize_str(&alloy_primitives::hex::encode(x.as_ref())) } + +/// Serialize a [B256] as a hex string _without_ the "0x" prefix. +pub fn serialize_b256_hex_string_no_prefix(x: &B256, s: S) -> Result +where + S: Serializer, +{ + s.serialize_str(&format!("{x:x}")) +} diff --git a/crates/rpc/rpc-types/src/serde_helpers/num.rs b/crates/rpc/rpc-types/src/serde_helpers/num.rs index d1e6959065..4c34471cd7 100644 --- a/crates/rpc/rpc-types/src/serde_helpers/num.rs +++ b/crates/rpc/rpc-types/src/serde_helpers/num.rs @@ -69,6 +69,68 @@ impl<'de> Deserialize<'de> for U64HexOrNumber { } } +/// serde functions for handling `u64` as [U64] +pub mod u64_hex { + use alloy_primitives::U64; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + /// Deserializes an `u64` from [U64] accepting a hex quantity string with optional 0x prefix + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + U64::deserialize(deserializer).map(|val| val.to()) + } + + /// Serializes u64 as hex string + pub fn serialize(value: &u64, s: S) -> Result { + U64::from(*value).serialize(s) + } +} + +/// serde functions for handling `Option` as [U64] +pub mod u64_hex_opt { + use alloy_primitives::U64; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + /// Serializes u64 as hex string + pub fn serialize(value: &Option, s: S) -> Result { + match value { + Some(val) => U64::from(*val).serialize(s), + None => s.serialize_none(), + } + } + + /// Deserializes an `Option` from [U64] accepting a hex quantity string with optional 0x prefix + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + Ok(U64::deserialize(deserializer) + .map_or(None, |v| Some(u64::from_be_bytes(v.to_be_bytes())))) + } +} + +/// serde functions for handling primitive `u64` as [U64] +pub mod u64_hex_or_decimal { + use crate::serde_helpers::num::U64HexOrNumber; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + /// Deserializes an `u64` accepting a hex quantity string with optional 0x prefix or + /// a number + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + U64HexOrNumber::deserialize(deserializer).map(Into::into) + } + + /// Serializes u64 as hex string + pub fn serialize(value: &u64, s: S) -> Result { + U64HexOrNumber::from(*value).serialize(s) + } +} + /// serde functions for handling primitive optional `u64` as [U64] pub mod u64_hex_or_decimal_opt { use crate::serde_helpers::num::U64HexOrNumber; @@ -137,3 +199,25 @@ where { NumberOrHexU256::deserialize(deserializer)?.try_into_u256() } + +#[cfg(test)] +mod tests { + use super::*; + use serde::{Deserialize, Serialize}; + + #[test] + fn test_hex_u64() { + #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] + struct Value { + #[serde(with = "u64_hex")] + inner: u64, + } + + let val = Value { inner: 1000 }; + let s = serde_json::to_string(&val).unwrap(); + assert_eq!(s, "{\"inner\":\"0x3e8\"}"); + + let deserialized: Value = serde_json::from_str(&s).unwrap(); + assert_eq!(val, deserialized); + } +} diff --git a/crates/rpc/rpc-types/src/serde_helpers/u64_hex.rs b/crates/rpc/rpc-types/src/serde_helpers/u64_hex.rs deleted file mode 100644 index e73061cdc9..0000000000 --- a/crates/rpc/rpc-types/src/serde_helpers/u64_hex.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! Helper to deserialize an `u64` from [U64] accepting a hex quantity string with optional 0x -//! prefix - -use alloy_primitives::U64; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; - -/// Deserializes an `u64` from [U64] accepting a hex quantity string with optional 0x prefix -pub fn deserialize<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - U64::deserialize(deserializer).map(|val| val.to()) -} - -/// Serializes u64 as hex string -pub fn serialize(value: &u64, s: S) -> Result { - U64::from(*value).serialize(s) -} - -/// serde functions for handling `Option` as [U64] -pub mod u64_hex_opt { - use alloy_primitives::U64; - use serde::{Deserialize, Deserializer}; - - /// Deserializes an `Option` from [U64] accepting a hex quantity string with optional 0x prefix - pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - Ok(U64::deserialize(deserializer) - .map_or(None, |v| Some(u64::from_be_bytes(v.to_be_bytes())))) - } -}