chore: remove duplicate serde helpers (#5508)

This commit is contained in:
Matthias Seitz
2023-11-21 03:19:30 +01:00
committed by GitHub
parent 59251f0cae
commit 0dc90eba19
9 changed files with 105 additions and 434 deletions

View File

@@ -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,

View File

@@ -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<u64, D::Error>
where
D: Deserializer<'de>,
{
U64::deserialize(deserializer).map(|val| val.to())
}
/// Serializes u64 as hex string
pub fn serialize<S: Serializer>(value: &u64, s: S) -> Result<S::Ok, S::Error> {
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<S, T>(x: T, s: S) -> Result<S::Ok, S::Error>
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<S>(x: &B256, s: S) -> Result<S::Ok, S::Error>
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);
}
}

View File

@@ -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<u64> for U64HexOrNumber {
fn from(value: u64) -> Self {
Self(U64::from(value))
}
}
impl From<U64> for U64HexOrNumber {
fn from(value: U64) -> Self {
Self(value)
}
}
impl From<U64HexOrNumber> for u64 {
fn from(value: U64HexOrNumber) -> Self {
value.to()
}
}
impl From<U64HexOrNumber> for U64 {
fn from(value: U64HexOrNumber) -> Self {
value.0
}
}
impl<'de> Deserialize<'de> for U64HexOrNumber {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
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<u64, D::Error>
where
D: Deserializer<'de>,
{
U64HexOrNumber::deserialize(deserializer).map(Into::into)
}
/// Serializes u64 as hex string
pub fn serialize<S: Serializer>(value: &u64, s: S) -> Result<S::Ok, S::Error> {
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<Option<u64>, D::Error>
where
D: Deserializer<'de>,
{
match Option::<U64HexOrNumber>::deserialize(deserializer)? {
Some(val) => Ok(Some(val.into())),
None => Ok(None),
}
}
/// Serializes u64 as hex string
pub fn serialize<S: Serializer>(value: &Option<u64>, s: S) -> Result<S::Ok, S::Error> {
match value {
Some(val) => U64HexOrNumber::from(*val).serialize(s),
None => s.serialize_none(),
}
}
}
/// Deserializes the input into an `Option<U256>`, using [`from_int_or_hex`] to deserialize the
/// inner value.
pub fn from_int_or_hex_opt<'de, D>(deserializer: D) -> Result<Option<U256>, D::Error>
where
D: Deserializer<'de>,
{
match Option::<NumberOrHexU256>::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<U256, D::Error>
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<E: de::Error>(self) -> Result<U256, E> {
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<U256>);
let null = serde_json::to_string(&None::<U256>).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);
});
}
}

View File

@@ -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):
/// <https://github.com/ethereum/go-ethereum/blob/00a73fbcce3250b87fc4160f3deddc44390848f4/internal/ethapi/api.go#L658-L690>
///
/// 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<U256> 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<JsonStorageKey> 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:
// <https://github.com/ethereum/go-ethereum/blob/00a73fbcce3250b87fc4160f3deddc44390848f4/internal/ethapi/api.go#L658-L690>
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<B256, D::Error>
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<HashMap<B256, B256>>, using [from_bytes_to_b256] which
/// allows cropped values:
///
/// ```json
/// {
/// "0x0000000000000000000000000000000000000000000000000000000000000001": "0x22"
/// }
/// ```
pub fn deserialize_storage_map<'de, D>(
deserializer: D,
) -> Result<Option<HashMap<B256, B256>>, D::Error>
where
D: Deserializer<'de>,
{
let map = Option::<HashMap<Bytes, Bytes>>::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),
}
}

View File

@@ -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<u64>,
}

View File

@@ -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::*;

View File

@@ -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<U256, D::Error>
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<S>(x: &B256, s: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_str(&format!("{x:x}"))
}

View File

@@ -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<u64, D::Error>
where
D: Deserializer<'de>,
{
U64::deserialize(deserializer).map(|val| val.to())
}
/// Serializes u64 as hex string
pub fn serialize<S: Serializer>(value: &u64, s: S) -> Result<S::Ok, S::Error> {
U64::from(*value).serialize(s)
}
}
/// serde functions for handling `Option<u64>` 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<S: Serializer>(value: &Option<u64>, s: S) -> Result<S::Ok, S::Error> {
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<Option<u64>, 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<u64, D::Error>
where
D: Deserializer<'de>,
{
U64HexOrNumber::deserialize(deserializer).map(Into::into)
}
/// Serializes u64 as hex string
pub fn serialize<S: Serializer>(value: &u64, s: S) -> Result<S::Ok, S::Error> {
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);
}
}

View File

@@ -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<u64, D::Error>
where
D: Deserializer<'de>,
{
U64::deserialize(deserializer).map(|val| val.to())
}
/// Serializes u64 as hex string
pub fn serialize<S: Serializer>(value: &u64, s: S) -> Result<S::Ok, S::Error> {
U64::from(*value).serialize(s)
}
/// serde functions for handling `Option<u64>` 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<Option<u64>, D::Error>
where
D: Deserializer<'de>,
{
Ok(U64::deserialize(deserializer)
.map_or(None, |v| Some(u64::from_be_bytes(v.to_be_bytes()))))
}
}