diff --git a/Cargo.toml b/Cargo.toml index 3006c7abbf8..3217430bd9c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,8 +84,8 @@ alloy-eip2930 = { version = "0.1.0", default-features = false } alloy-eip7702 = { version = "0.1.0", default-features = false } # ethereum -ethereum_ssz_derive = "0.7.1" -ethereum_ssz = "0.7.1" +ethereum_ssz_derive = "0.8" +ethereum_ssz = "0.8" # crypto c-kzg = { version = "1.0", default-features = false } @@ -112,7 +112,7 @@ rustls = { version = "0.23", default-features = false, features = [ "tls12", ] } tokio-test = "0.4" -tokio-tungstenite = "0.23" +tokio-tungstenite = "0.24" tower = { version = "0.5", features = ["util"] } # tracing @@ -155,4 +155,4 @@ assert_matches = "1.5" serial_test = "3.0" similar-asserts = "1.5" tempfile = "3.10" -tower-http = "0.5.2" +tower-http = "0.6.1" diff --git a/crates/consensus/src/block.rs b/crates/consensus/src/block.rs new file mode 100644 index 00000000000..9a144ddec7b --- /dev/null +++ b/crates/consensus/src/block.rs @@ -0,0 +1,96 @@ +//! Genesic Block Type + +use crate::{Header, Requests}; +use alloc::vec::Vec; +use alloy_eips::eip4895::Withdrawal; +use alloy_rlp::{Decodable, Encodable, RlpDecodable, RlpEncodable}; + +/// Ethereum full block. +/// +/// Withdrawals can be optionally included at the end of the RLP encoded message. +/// +/// Taken from [reth-primitives](https://github.com/paradigmxyz/reth) +/// +/// See p2p block encoding reference: +#[derive(Debug, Clone, PartialEq, Eq, Default)] +pub struct Block { + /// Block header. + pub header: Header, + /// Block body. + pub body: BlockBody, +} + +/// A response to `GetBlockBodies`, containing bodies if any bodies were found. +/// +/// Withdrawals can be optionally included at the end of the RLP encoded message. +#[derive(Debug, Clone, PartialEq, Eq, Default, RlpEncodable, RlpDecodable)] +#[rlp(trailing)] +pub struct BlockBody { + /// Transactions in this block. + pub transactions: Vec, + /// Ommers/uncles header. + pub ommers: Vec
, + /// Block withdrawals. + pub withdrawals: Option>, + /// Block requests + pub requests: Option, +} + +/// We need to implement RLP traits manually because we currently don't have a way to flatten +/// [`BlockBody`] into [`Block`]. +mod block_rlp { + use super::*; + + #[derive(RlpDecodable)] + #[rlp(trailing)] + struct Helper { + header: Header, + transactions: Vec, + ommers: Vec
, + withdrawals: Option>, + requests: Option, + } + + #[derive(RlpEncodable)] + #[rlp(trailing)] + struct HelperRef<'a, T> { + header: &'a Header, + transactions: &'a Vec, + ommers: &'a Vec
, + withdrawals: Option<&'a Vec>, + requests: Option<&'a Requests>, + } + + impl<'a, T> From<&'a Block> for HelperRef<'a, T> { + fn from(block: &'a Block) -> Self { + let Block { header, body: BlockBody { transactions, ommers, withdrawals, requests } } = + block; + Self { + header, + transactions, + ommers, + withdrawals: withdrawals.as_ref(), + requests: requests.as_ref(), + } + } + } + + impl Encodable for Block { + fn length(&self) -> usize { + let helper: HelperRef<'_, T> = self.into(); + helper.length() + } + + fn encode(&self, out: &mut dyn alloy_rlp::bytes::BufMut) { + let helper: HelperRef<'_, T> = self.into(); + helper.encode(out) + } + } + + impl Decodable for Block { + fn decode(b: &mut &[u8]) -> alloy_rlp::Result { + let Helper { header, transactions, ommers, withdrawals, requests } = Helper::decode(b)?; + Ok(Self { header, body: BlockBody { transactions, ommers, withdrawals, requests } }) + } + } +} diff --git a/crates/consensus/src/header.rs b/crates/consensus/src/header.rs index a234a158036..8ab3a9f99c7 100644 --- a/crates/consensus/src/header.rs +++ b/crates/consensus/src/header.rs @@ -345,10 +345,19 @@ impl Header { } /// Returns the parent block's number and hash + /// + /// Note: for the genesis block the parent number is 0 and the parent hash is the zero hash. pub const fn parent_num_hash(&self) -> BlockNumHash { BlockNumHash { number: self.number.saturating_sub(1), hash: self.parent_hash } } + /// Returns the block's number and hash. + /// + /// Note: this hashes the header. + pub fn num_hash_slow(&self) -> BlockNumHash { + BlockNumHash { number: self.number, hash: self.hash_slow() } + } + /// Checks if the block's difficulty is set to zero, indicating a Proof-of-Stake header. /// /// This function is linked to EIP-3675, proposing the consensus upgrade to Proof-of-Stake: diff --git a/crates/consensus/src/lib.rs b/crates/consensus/src/lib.rs index 7d1a95371f5..5d24cca9999 100644 --- a/crates/consensus/src/lib.rs +++ b/crates/consensus/src/lib.rs @@ -12,6 +12,9 @@ extern crate alloc; mod account; pub use account::Account; +mod block; +pub use block::{Block, BlockBody}; + pub mod constants; mod encodable_signature; diff --git a/crates/eips/src/eip4844/mod.rs b/crates/eips/src/eip4844/mod.rs index 99e315555ea..91ee3f3d678 100644 --- a/crates/eips/src/eip4844/mod.rs +++ b/crates/eips/src/eip4844/mod.rs @@ -85,6 +85,20 @@ pub const BYTES_PER_PROOF: usize = 48; /// A Blob serialized as 0x-prefixed hex string pub type Blob = FixedBytes; +/// Helper function to deserialize boxed blobs. +#[cfg(feature = "serde")] +pub fn deserialize_blob<'de, D>(deserializer: D) -> Result, D::Error> +where + D: serde::de::Deserializer<'de>, +{ + use serde::Deserialize; + let raw_blob = ::deserialize(deserializer)?; + let blob = alloc::boxed::Box::new( + Blob::try_from(raw_blob.as_ref()).map_err(serde::de::Error::custom)?, + ); + Ok(blob) +} + /// A commitment/proof serialized as 0x-prefixed hex string pub type Bytes48 = FixedBytes<48>; diff --git a/crates/eips/src/eip4844/sidecar.rs b/crates/eips/src/eip4844/sidecar.rs index 33695da1629..d9d2b875851 100644 --- a/crates/eips/src/eip4844/sidecar.rs +++ b/crates/eips/src/eip4844/sidecar.rs @@ -3,6 +3,7 @@ use crate::eip4844::{ kzg_to_versioned_hash, Blob, Bytes48, BYTES_PER_BLOB, BYTES_PER_COMMITMENT, BYTES_PER_PROOF, }; +use alloc::boxed::Box; use alloy_primitives::{bytes::BufMut, B256}; use alloy_rlp::{Decodable, Encodable}; @@ -12,6 +13,10 @@ use crate::eip4844::MAX_BLOBS_PER_BLOCK; #[cfg(not(feature = "std"))] use alloc::vec::Vec; +/// The versioned hash version for KZG. +#[cfg(feature = "kzg")] +pub(crate) const VERSIONED_HASH_VERSION_KZG: u8 = 0x01; + /// This represents a set of blobs, and its corresponding commitments and proofs. /// /// This type encodes and decodes the fields without an rlp header. @@ -32,6 +37,96 @@ pub struct BlobTransactionSidecar { pub proofs: Vec, } +impl IntoIterator for BlobTransactionSidecar { + type Item = BlobTransactionSidecarItem; + type IntoIter = alloc::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.blobs + .into_iter() + .zip(self.commitments) + .zip(self.proofs) + .enumerate() + .map(|(index, ((blob, commitment), proof))| BlobTransactionSidecarItem { + index, + blob: Box::new(blob), + kzg_commitment: commitment, + kzg_proof: proof, + }) + .collect::>() + .into_iter() + } +} + +/// A single blob sidecar. +#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +#[repr(C)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct BlobTransactionSidecarItem { + /// The index of this item within the [BlobTransactionSidecar]. + pub index: usize, + /// The blob in this sidecar item. + #[cfg_attr(feature = "serde", serde(deserialize_with = "super::deserialize_blob"))] + pub blob: Box, + /// The KZG commitment. + pub kzg_commitment: Bytes48, + /// The KZG proof. + pub kzg_proof: Bytes48, +} + +#[cfg(feature = "kzg")] +impl BlobTransactionSidecarItem { + /// `VERSIONED_HASH_VERSION_KZG ++ sha256(commitment)[1..]` + pub fn to_kzg_versioned_hash(&self) -> [u8; 32] { + use sha2::Digest; + let commitment = self.kzg_commitment.as_slice(); + let mut hash: [u8; 32] = sha2::Sha256::digest(commitment).into(); + hash[0] = VERSIONED_HASH_VERSION_KZG; + hash + } + + /// Verifies the KZG proof of a blob to ensure its integrity and correctness. + pub fn verify_blob_kzg_proof(&self) -> Result<(), BlobTransactionValidationError> { + let binding = crate::eip4844::env_settings::EnvKzgSettings::Default; + let settings = binding.get(); + + let blob = c_kzg::Blob::from_bytes(self.blob.as_slice()) + .map_err(BlobTransactionValidationError::KZGError)?; + + let commitment = c_kzg::Bytes48::from_bytes(self.kzg_commitment.as_slice()) + .map_err(BlobTransactionValidationError::KZGError)?; + + let proof = c_kzg::Bytes48::from_bytes(self.kzg_proof.as_slice()) + .map_err(BlobTransactionValidationError::KZGError)?; + + let result = c_kzg::KzgProof::verify_blob_kzg_proof(&blob, &commitment, &proof, settings) + .map_err(BlobTransactionValidationError::KZGError)?; + + result.then_some(()).ok_or(BlobTransactionValidationError::InvalidProof) + } + + /// Verify the blob sidecar against its [crate::NumHash]. + pub fn verify_blob(&self, hash: &crate::NumHash) -> Result<(), BlobTransactionValidationError> { + if self.index != hash.number as usize { + let blob_hash_part = B256::from_slice(&self.blob[0..32]); + return Err(BlobTransactionValidationError::WrongVersionedHash { + have: blob_hash_part, + expected: hash.hash, + }); + } + + let computed_hash = self.to_kzg_versioned_hash(); + if computed_hash != hash.hash { + return Err(BlobTransactionValidationError::WrongVersionedHash { + have: computed_hash.into(), + expected: hash.hash, + }); + } + + self.verify_blob_kzg_proof() + } +} + #[cfg(any(test, feature = "arbitrary"))] impl<'a> arbitrary::Arbitrary<'a> for BlobTransactionSidecar { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { @@ -139,11 +234,7 @@ impl BlobTransactionSidecar { } .map_err(BlobTransactionValidationError::KZGError)?; - if res { - Ok(()) - } else { - Err(BlobTransactionValidationError::InvalidProof) - } + res.then_some(()).ok_or(BlobTransactionValidationError::InvalidProof) } /// Returns an iterator over the versioned hashes of the commitments. diff --git a/crates/json-rpc/src/request.rs b/crates/json-rpc/src/request.rs index 2dac9b67092..4b413db44d6 100644 --- a/crates/json-rpc/src/request.rs +++ b/crates/json-rpc/src/request.rs @@ -1,11 +1,15 @@ -use crate::{common::Id, RpcParam}; +use crate::{common::Id, RpcObject, RpcParam}; use alloy_primitives::{keccak256, B256}; -use serde::{de::DeserializeOwned, ser::SerializeMap, Deserialize, Serialize}; +use serde::{ + de::{DeserializeOwned, MapAccess}, + ser::SerializeMap, + Deserialize, Serialize, +}; use serde_json::value::RawValue; -use std::borrow::Cow; +use std::{borrow::Cow, marker::PhantomData, mem::MaybeUninit}; /// `RequestMeta` contains the [`Id`] and method name of a request. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct RequestMeta { /// The method name. pub method: Cow<'static, str>, @@ -48,7 +52,7 @@ impl RequestMeta { /// ### Note /// /// The value of `method` should be known at compile time. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct Request { /// The request metadata (ID and method). pub meta: RequestMeta, @@ -182,6 +186,103 @@ where } } +impl<'de, Params> Deserialize<'de> for Request +where + Params: RpcObject, +{ + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct Visitor(PhantomData); + impl<'de, Params> serde::de::Visitor<'de> for Visitor + where + Params: RpcObject, + { + type Value = Request; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + formatter, + "a JSON-RPC 2.0 request object with params of type {}", + std::any::type_name::() + ) + } + + fn visit_map(self, mut map: A) -> Result + where + A: MapAccess<'de>, + { + let mut id = None; + let mut params = None; + let mut method = None; + let mut jsonrpc = None; + + while let Some(key) = map.next_key()? { + match key { + "id" => { + if id.is_some() { + return Err(serde::de::Error::duplicate_field("id")); + } + id = Some(map.next_value()?); + } + "params" => { + if params.is_some() { + return Err(serde::de::Error::duplicate_field("params")); + } + params = Some(map.next_value()?); + } + "method" => { + if method.is_some() { + return Err(serde::de::Error::duplicate_field("method")); + } + method = Some(map.next_value()?); + } + "jsonrpc" => { + let version: String = map.next_value()?; + if version != "2.0" { + return Err(serde::de::Error::custom(format!( + "unsupported JSON-RPC version: {}", + version + ))); + } + jsonrpc = Some(()); + } + other => { + return Err(serde::de::Error::unknown_field( + other, + &["id", "params", "method", "jsonrpc"], + )); + } + } + } + if jsonrpc.is_none() { + return Err(serde::de::Error::missing_field("jsonrpc")); + } + if method.is_none() { + return Err(serde::de::Error::missing_field("method")); + } + + if params.is_none() { + if std::mem::size_of::() == 0 { + // SAFETY: params is a ZST, so it's safe to fail to initialize it + unsafe { params = Some(MaybeUninit::::zeroed().assume_init()) } + } else { + return Err(serde::de::Error::missing_field("params")); + } + } + + Ok(Request { + meta: RequestMeta::new(method.unwrap(), id.unwrap_or(Id::None)), + params: params.unwrap(), + }) + } + } + + deserializer.deserialize_map(Visitor(PhantomData)) + } +} + /// A JSON-RPC 2.0 request object that has been serialized, with its [`Id`] and /// method preserved. /// @@ -285,3 +386,24 @@ impl Serialize for SerializedRequest { self.request.serialize(serializer) } } + +#[cfg(test)] +mod test { + use super::*; + + fn test_inner(t: T) { + let ser = serde_json::to_string(&t).unwrap(); + let de: T = serde_json::from_str(&ser).unwrap(); + let reser = serde_json::to_string(&de).unwrap(); + assert_eq!(de, t, "deser error for {}", std::any::type_name::()); + assert_eq!(ser, reser, "reser error for {}", std::any::type_name::()); + } + + #[test] + fn test_ser_deser() { + test_inner(Request::<()>::new("test", 1.into(), ())); + test_inner(Request::::new("test", "hello".to_string().into(), 1)); + test_inner(Request::::new("test", Id::None, "test".to_string())); + test_inner(Request::>::new("test", u64::MAX.into(), vec![1, 2, 3])); + } +} diff --git a/crates/rpc-types-beacon/src/sidecar.rs b/crates/rpc-types-beacon/src/sidecar.rs index 9c2866771a2..b511436fde2 100644 --- a/crates/rpc-types-beacon/src/sidecar.rs +++ b/crates/rpc-types-beacon/src/sidecar.rs @@ -1,6 +1,6 @@ use crate::header::Header; -use alloy_eips::eip4844::{Blob, BlobTransactionSidecar, Bytes48}; -use alloy_primitives::{Bytes, B256}; +use alloy_eips::eip4844::{deserialize_blob, Blob, BlobTransactionSidecar, Bytes48}; +use alloy_primitives::B256; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, DisplayFromStr}; use std::vec::IntoIter; @@ -101,18 +101,6 @@ pub struct BlobData { pub kzg_commitment_inclusion_proof: Vec, } -/// Helper function to deserialize boxed blobs -fn deserialize_blob<'de, D>(deserializer: D) -> Result, D::Error> -where - D: serde::de::Deserializer<'de>, -{ - let raw_blob = ::deserialize(deserializer)?; - - let blob = Box::new(Blob::try_from(raw_blob.as_ref()).map_err(serde::de::Error::custom)?); - - Ok(blob) -} - #[cfg(test)] mod tests { use super::*; diff --git a/crates/rpc-types-debug/src/debug.rs b/crates/rpc-types-debug/src/debug.rs index ea3c1d770db..8b6dd044d30 100644 --- a/crates/rpc-types-debug/src/debug.rs +++ b/crates/rpc-types-debug/src/debug.rs @@ -11,6 +11,10 @@ pub struct ExecutionWitness { /// the block, including during state root recomputation. /// keccak(rlp(node)) => rlp(node) pub state: HashMap, + /// Map of all contract codes (created / accessed) to their preimages that were required during + /// the execution of the block, including during state root recomputation. + /// keccak(address) => bytecodes + pub codes: HashMap, /// Map of all hashed account and storage keys (addresses and slots) to their preimages /// (unhashed account addresses and storage slots, respectively) that were required during /// the execution of the block. during the execution of the block. diff --git a/crates/rpc-types-eth/src/account.rs b/crates/rpc-types-eth/src/account.rs index 54e8651a39d..6f281c7edb9 100644 --- a/crates/rpc-types-eth/src/account.rs +++ b/crates/rpc-types-eth/src/account.rs @@ -1,6 +1,7 @@ -use alloy_primitives::{Address, Bytes, B256, B512, U256}; +#![allow(unused_imports)] use alloc::{string::String, vec::Vec}; +use alloy_primitives::{Address, Bytes, B256, B512, U256}; // re-export account type for `eth_getAccount` pub use alloy_consensus::Account; diff --git a/crates/rpc-types-eth/src/call.rs b/crates/rpc-types-eth/src/call.rs index 1020894da57..8ca950dad75 100644 --- a/crates/rpc-types-eth/src/call.rs +++ b/crates/rpc-types-eth/src/call.rs @@ -1,11 +1,9 @@ use crate::{request::TransactionRequest, BlockId, BlockOverrides}; -use alloy_primitives::Bytes; - use alloc::{ - format, string::{String, ToString}, vec::Vec, }; +use alloy_primitives::Bytes; /// Bundle of transactions #[derive(Clone, Debug, Default, PartialEq, Eq)] diff --git a/crates/rpc-types-eth/src/fee.rs b/crates/rpc-types-eth/src/fee.rs index d6222da37f6..b045542f1dc 100644 --- a/crates/rpc-types-eth/src/fee.rs +++ b/crates/rpc-types-eth/src/fee.rs @@ -68,7 +68,7 @@ pub struct FeeHistory { serde( default, skip_serializing_if = "Option::is_none", - with = "alloy_serde::u128_vec_vec_opt_via_ruint" + with = "alloy_serde::quantity::u128_vec_vec_opt" ) )] pub reward: Option>>, diff --git a/crates/rpc-types-eth/src/filter.rs b/crates/rpc-types-eth/src/filter.rs index ae11424cda4..15eeac71c48 100644 --- a/crates/rpc-types-eth/src/filter.rs +++ b/crates/rpc-types-eth/src/filter.rs @@ -1,5 +1,5 @@ use crate::{BlockNumberOrTag, Log as RpcLog, Transaction}; -use alloc::{format, string::String, vec::Vec}; +use alloc::{string::String, vec::Vec}; use alloy_primitives::{keccak256, Address, BlockHash, Bloom, BloomInput, B256, U256, U64}; use itertools::{EitherOrBoth::*, Itertools}; @@ -584,15 +584,15 @@ impl serde::Serialize for Filter { } } -type RawAddressFilter = ValueOrArray>; -type RawTopicsFilter = Vec>>>; - #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for Filter { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { + type RawAddressFilter = ValueOrArray>; + type RawTopicsFilter = Vec>>>; + struct FilterVisitor; impl<'de> serde::de::Visitor<'de> for FilterVisitor { diff --git a/crates/rpc-types-eth/src/index.rs b/crates/rpc-types-eth/src/index.rs index 322591e08e7..b1cd2c19b94 100644 --- a/crates/rpc-types-eth/src/index.rs +++ b/crates/rpc-types-eth/src/index.rs @@ -1,6 +1,4 @@ -use alloc::{format, string::String}; use alloy_primitives::U256; -use core::fmt; /// A hex encoded or decimal index that's intended to be used as a rust index, hence it's /// deserialized into a `usize`. @@ -46,7 +44,7 @@ impl<'a> serde::Deserialize<'a> for Index { impl<'a> serde::de::Visitor<'a> for IndexVisitor { type Value = Index; - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(formatter, "hex-encoded or decimal index") } @@ -76,13 +74,6 @@ impl<'a> serde::Deserialize<'a> for Index { }, ) } - - fn visit_string(self, value: String) -> Result - where - E: serde::de::Error, - { - self.visit_str(value.as_ref()) - } } deserializer.deserialize_any(IndexVisitor) diff --git a/crates/rpc-types-eth/src/lib.rs b/crates/rpc-types-eth/src/lib.rs index b8ab655726b..1351f3412a9 100644 --- a/crates/rpc-types-eth/src/lib.rs +++ b/crates/rpc-types-eth/src/lib.rs @@ -7,6 +7,8 @@ #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #![cfg_attr(not(any(test, feature = "std")), no_std)] +#[macro_use] +#[allow(unused_imports)] extern crate alloc; /// Standardized collections across `std` and `no_std` environments. diff --git a/crates/rpc-types-eth/src/transaction/receipt.rs b/crates/rpc-types-eth/src/transaction/receipt.rs index 48353e6fba3..27fbf703820 100644 --- a/crates/rpc-types-eth/src/transaction/receipt.rs +++ b/crates/rpc-types-eth/src/transaction/receipt.rs @@ -1,11 +1,10 @@ use crate::Log; -use alloy_consensus::{AnyReceiptEnvelope, ReceiptEnvelope, TxReceipt, TxType}; +use alloc::vec::Vec; +use alloy_consensus::{ReceiptEnvelope, TxReceipt, TxType}; use alloy_eips::eip7702::SignedAuthorization; use alloy_network_primitives::ReceiptResponse; use alloy_primitives::{Address, BlockHash, TxHash, B256}; -use alloc::vec::Vec; - /// Transaction receipt /// /// This type is generic over an inner [`ReceiptEnvelope`] which contains @@ -145,7 +144,7 @@ impl TransactionReceipt { #[doc(alias = "AnyTxReceipt")] #[cfg(feature = "serde")] pub type AnyTransactionReceipt = - alloy_serde::WithOtherFields>>; + alloy_serde::WithOtherFields>>; impl> ReceiptResponse for TransactionReceipt { fn contract_address(&self) -> Option
{ diff --git a/crates/rpc-types-eth/src/transaction/signature.rs b/crates/rpc-types-eth/src/transaction/signature.rs index 29cde635399..ef631380938 100644 --- a/crates/rpc-types-eth/src/transaction/signature.rs +++ b/crates/rpc-types-eth/src/transaction/signature.rs @@ -1,7 +1,6 @@ -//! Signature related RPC values -use alloy_primitives::U256; +//! Signature related RPC values. -use alloc::{format, string::String}; +use alloy_primitives::U256; /// Container type for all signature fields in RPC #[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))] @@ -64,7 +63,7 @@ where D: serde::Deserializer<'de>, { use serde::Deserialize; - let s = String::deserialize(deserializer)?; + let s = alloc::string::String::deserialize(deserializer)?; match s.as_str() { "0x0" => Ok(false), "0x1" => Ok(true), diff --git a/crates/rpc-types-eth/src/work.rs b/crates/rpc-types-eth/src/work.rs index 1120b6bf9fb..ff70885097f 100644 --- a/crates/rpc-types-eth/src/work.rs +++ b/crates/rpc-types-eth/src/work.rs @@ -1,5 +1,4 @@ -use alloy_primitives::{B256, U256}; -use core::fmt; +use alloy_primitives::B256; /// The result of an `eth_getWork` request #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] @@ -20,10 +19,8 @@ impl serde::Serialize for Work { where S: serde::Serializer, { - match self.number.as_ref() { - Some(num) => { - (&self.pow_hash, &self.seed_hash, &self.target, U256::from(*num)).serialize(s) - } + match self.number.map(alloy_primitives::U64::from) { + Some(num) => (&self.pow_hash, &self.seed_hash, &self.target, num).serialize(s), None => (&self.pow_hash, &self.seed_hash, &self.target).serialize(s), } } @@ -40,7 +37,7 @@ impl<'a> serde::Deserialize<'a> for Work { impl<'a> serde::de::Visitor<'a> for WorkVisitor { type Value = Work; - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { write!(formatter, "Work object") } diff --git a/crates/serde/src/lib.rs b/crates/serde/src/lib.rs index 633216fc8b8..603300801fa 100644 --- a/crates/serde/src/lib.rs +++ b/crates/serde/src/lib.rs @@ -21,11 +21,6 @@ pub use self::bool::*; mod optional; pub use self::optional::*; -#[cfg_attr(not(test), deprecated = "use `quantity::{self, opt, vec}` instead")] -pub mod num; -#[allow(deprecated)] -pub use num::*; - pub mod quantity; /// Storage related helpers. diff --git a/crates/serde/src/num.rs b/crates/serde/src/num.rs deleted file mode 100644 index a87a170059e..00000000000 --- a/crates/serde/src/num.rs +++ /dev/null @@ -1,307 +0,0 @@ -//! Numeric serde helpers. - -/// serde functions for handling `u8` via [U8](alloy_primitives::U8) -pub mod u8_via_ruint { - use alloy_primitives::U8; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - - /// Deserializes an `u8` from [U8] accepting a hex quantity string with optional 0x prefix - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - U8::deserialize(deserializer).map(|val| val.to()) - } - - /// Serializes u64 as hex string - pub fn serialize(value: &u8, s: S) -> Result { - U8::from(*value).serialize(s) - } -} - -/// serde functions for handling `Option` via [U8](alloy_primitives::U8) -pub mod u8_opt_via_ruint { - use alloy_primitives::U8; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - - /// Serializes u64 as hex string - pub fn serialize(value: &Option, s: S) -> Result { - match value { - Some(val) => U8::from(*val).serialize(s), - None => s.serialize_none(), - } - } - - /// Deserializes an `Option` from [U8] accepting a hex quantity string with optional 0x prefix - pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, - { - Ok(U8::deserialize(deserializer).map_or(None, |v| Some(u8::from_be_bytes(v.to_be_bytes())))) - } -} - -/// serde functions for handling `u64` via [U64](alloy_primitives::U64) -pub mod u64_via_ruint { - 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` via [U64](alloy_primitives::U64) -pub mod u64_opt_via_ruint { - 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 `u128` via [U128](alloy_primitives::U128) -pub mod u128_via_ruint { - use alloy_primitives::U128; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - - /// Deserializes an `u128` accepting a hex quantity string with optional 0x prefix or - /// a number - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - U128::deserialize(deserializer).map(|val| val.to()) - } - - /// Serializes u128 as hex string - pub fn serialize(value: &u128, s: S) -> Result { - U128::from(*value).serialize(s) - } -} - -/// serde functions for handling primitive optional `u128` via [U128](alloy_primitives::U128) -pub mod u128_opt_via_ruint { - use alloy_primitives::U128; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - - /// Deserializes an `Option` 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>, - { - Option::::deserialize(deserializer)? - .map_or_else(|| Ok(None), |val| Ok(Some(val.to()))) - } - - /// Serializes `Option` as hex string - pub fn serialize(value: &Option, s: S) -> Result { - match value { - Some(val) => U128::from(*val).serialize(s), - None => s.serialize_none(), - } - } -} - -/// serde functions for handling `Vec` via [U128](alloy_primitives::U128) -pub mod u128_vec_via_ruint { - #[cfg(not(feature = "std"))] - use alloc::vec::Vec; - use alloy_primitives::U128; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - - /// Deserializes an `u128` 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>, - { - let vec = Vec::::deserialize(deserializer)?; - Ok(vec.into_iter().map(|val| val.to()).collect()) - } - - /// Serializes u128 as hex string - pub fn serialize(value: &[u128], s: S) -> Result { - let vec = value.iter().map(|val| U128::from(*val)).collect::>(); - vec.serialize(s) - } -} - -/// serde functions for handling `Vec>` via [U128](alloy_primitives::U128) -pub mod u128_vec_vec_opt_via_ruint { - use alloy_primitives::U128; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - - #[cfg(not(feature = "std"))] - use alloc::vec::Vec; - - /// Deserializes an `u128` 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>, - { - Option::>>::deserialize(deserializer)?.map_or_else( - || Ok(None), - |vec| { - Ok(Some( - vec.into_iter().map(|v| v.into_iter().map(|val| val.to()).collect()).collect(), - )) - }, - ) - } - - /// Serializes u128 as hex string - pub fn serialize( - value: &Option>>, - s: S, - ) -> Result { - match value { - Some(vec) => { - let vec = vec - .iter() - .map(|v| v.iter().map(|val| U128::from(*val)).collect::>()) - .collect::>(); - vec.serialize(s) - } - None => s.serialize_none(), - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use serde::{Deserialize, Serialize}; - - #[cfg(not(feature = "std"))] - use alloc::{string::ToString, vec, vec::Vec}; - - #[test] - fn test_hex_u64() { - #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] - struct Value { - #[serde(with = "u64_via_ruint")] - 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); - } - - #[test] - fn test_u128_via_ruint() { - #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] - struct Value { - #[serde(with = "u128_via_ruint")] - inner: u128, - } - - 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); - - let s = "{\"inner\":\"1000\"}".to_string(); - let deserialized: Value = serde_json::from_str(&s).unwrap(); - - assert_eq!(val, deserialized); - } - - #[test] - fn test_u128_opt_via_ruint() { - #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] - struct Value { - #[serde(with = "u128_opt_via_ruint")] - inner: Option, - } - - let val = Value { inner: Some(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); - - let s = "{\"inner\":\"1000\"}".to_string(); - let deserialized: Value = serde_json::from_str(&s).unwrap(); - - assert_eq!(val, deserialized); - - let val = Value { inner: None }; - let s = serde_json::to_string(&val).unwrap(); - assert_eq!(s, "{\"inner\":null}"); - - let deserialized: Value = serde_json::from_str(&s).unwrap(); - assert_eq!(val, deserialized); - } - - #[test] - fn test_u128_vec_via_ruint() { - #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] - struct Value { - #[serde(with = "u128_vec_via_ruint")] - inner: Vec, - } - - let val = Value { inner: vec![1000, 2000] }; - let s = serde_json::to_string(&val).unwrap(); - assert_eq!(s, "{\"inner\":[\"0x3e8\",\"0x7d0\"]}"); - - let deserialized: Value = serde_json::from_str(&s).unwrap(); - assert_eq!(val, deserialized); - } - - #[test] - fn test_u128_vec_vec_opt_via_ruint() { - #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] - struct Value { - #[serde(with = "u128_vec_vec_opt_via_ruint")] - inner: Option>>, - } - - let val = Value { inner: Some(vec![vec![1000, 2000], vec![3000, 4000]]) }; - let s = serde_json::to_string(&val).unwrap(); - assert_eq!(s, "{\"inner\":[[\"0x3e8\",\"0x7d0\"],[\"0xbb8\",\"0xfa0\"]]}"); - - let deserialized: Value = serde_json::from_str(&s).unwrap(); - assert_eq!(val, deserialized); - - let val = Value { inner: None }; - let s = serde_json::to_string(&val).unwrap(); - assert_eq!(s, "{\"inner\":null}"); - - let deserialized: Value = serde_json::from_str(&s).unwrap(); - assert_eq!(val, deserialized); - } -} diff --git a/crates/serde/src/optional.rs b/crates/serde/src/optional.rs index 5ef3288461c..87362352022 100644 --- a/crates/serde/src/optional.rs +++ b/crates/serde/src/optional.rs @@ -1,4 +1,5 @@ //! Serde functions for encoding optional values. + use serde::{Deserialize, Deserializer}; /// For use with serde's `deserialize_with` on a sequence that must be @@ -8,6 +9,5 @@ where T: Deserialize<'de> + Default, D: Deserializer<'de>, { - let s: Option = Deserialize::deserialize(deserializer)?; - Ok(s.unwrap_or_default()) + Option::::deserialize(deserializer).map(Option::unwrap_or_default) } diff --git a/crates/serde/src/quantity.rs b/crates/serde/src/quantity.rs index 53974b9a684..12096750395 100644 --- a/crates/serde/src/quantity.rs +++ b/crates/serde/src/quantity.rs @@ -43,7 +43,7 @@ pub mod opt { S: Serializer, { match value { - Some(value) => super::serialize(value, serializer), + Some(value) => serializer.serialize_some(&value.into_ruint()), None => serializer.serialize_none(), } } @@ -89,6 +89,48 @@ pub mod vec { } } +/// serde functions for handling `Vec>` via [U128](alloy_primitives::U128) +pub mod u128_vec_vec_opt { + use alloy_primitives::U128; + use serde::{Deserialize, Deserializer, Serializer}; + + #[cfg(not(feature = "std"))] + use alloc::vec::Vec; + + /// Deserializes an `u128` 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>, + { + Option::>>::deserialize(deserializer)?.map_or_else( + || Ok(None), + |vec| { + Ok(Some( + vec.into_iter().map(|v| v.into_iter().map(|val| val.to()).collect()).collect(), + )) + }, + ) + } + + /// Serializes u128 as hex string + pub fn serialize( + value: &Option>>, + s: S, + ) -> Result { + match value { + Some(vec) => { + let vec = vec + .iter() + .map(|v| v.iter().map(|val| U128::from(*val)).collect::>()) + .collect::>(); + s.serialize_some(&vec) + } + None => s.serialize_none(), + } + } +} + /// Private implementation details of the [`quantity`](self) module. #[allow(unnameable_types)] mod private { @@ -133,48 +175,6 @@ mod private { } } -/// serde functions for handling `Vec>` via [U128](alloy_primitives::U128) -pub mod u128_vec_vec_opt { - use alloy_primitives::U128; - use serde::{Deserialize, Deserializer, Serialize, Serializer}; - - #[cfg(not(feature = "std"))] - use alloc::vec::Vec; - - /// Deserializes an `u128` 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>, - { - Option::>>::deserialize(deserializer)?.map_or_else( - || Ok(None), - |vec| { - Ok(Some( - vec.into_iter().map(|v| v.into_iter().map(|val| val.to()).collect()).collect(), - )) - }, - ) - } - - /// Serializes u128 as hex string - pub fn serialize( - value: &Option>>, - s: S, - ) -> Result { - match value { - Some(vec) => { - let vec = vec - .iter() - .map(|v| v.iter().map(|val| U128::from(*val)).collect::>()) - .collect::>(); - vec.serialize(s) - } - None => s.serialize_none(), - } - } -} - #[cfg(test)] mod tests { use serde::{Deserialize, Serialize}; diff --git a/crates/serde/src/storage.rs b/crates/serde/src/storage.rs index 1ab46a6c693..1d32a6becc1 100644 --- a/crates/serde/src/storage.rs +++ b/crates/serde/src/storage.rs @@ -1,7 +1,8 @@ -#[cfg(not(feature = "std"))] -use alloc::string::{String, ToString}; -use alloc::{collections::BTreeMap, fmt::Write}; - +use alloc::{ + collections::BTreeMap, + fmt::Write, + string::{String, ToString}, +}; use alloy_primitives::{Bytes, B256, U256}; use serde::{Deserialize, Deserializer, Serialize};