diff --git a/.changelog/evil-fish-smile.md b/.changelog/evil-fish-smile.md new file mode 100644 index 0000000000..e38129efdd --- /dev/null +++ b/.changelog/evil-fish-smile.md @@ -0,0 +1,6 @@ +--- +reth-rpc-convert: minor +reth-storage-rpc-provider: minor +--- + +Replaced the separate `TryFromBlockResponse`, `TryFromReceiptResponse`, and `TryFromTransactionResponse` traits with a unified `RpcResponseConverter` trait and default `EthRpcConverter` implementation. Removed the `op-alloy-network` dependency and refactored `RpcBlockchainProvider` to store a dynamic converter instance instead of relying on per-type trait bounds. diff --git a/Cargo.lock b/Cargo.lock index 4cc8c3965b..aa802f4ec4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9934,9 +9934,7 @@ dependencies = [ "dyn-clone", "jsonrpsee-types", "op-alloy-consensus", - "op-alloy-network", "op-alloy-rpc-types", - "reth-ethereum-primitives", "reth-evm", "reth-primitives-traits", "serde_json", @@ -10328,12 +10326,12 @@ dependencies = [ "reth-chainspec", "reth-db-api", "reth-errors", + "reth-ethereum-primitives", "reth-execution-types", "reth-node-types", "reth-primitives", "reth-provider", "reth-prune-types", - "reth-rpc-convert", "reth-stages-types", "reth-storage-api", "reth-trie", diff --git a/crates/rpc/rpc-convert/Cargo.toml b/crates/rpc/rpc-convert/Cargo.toml index 0f66d081bf..192db6b42f 100644 --- a/crates/rpc/rpc-convert/Cargo.toml +++ b/crates/rpc/rpc-convert/Cargo.toml @@ -15,7 +15,6 @@ workspace = true # reth reth-primitives-traits.workspace = true reth-evm.workspace = true -reth-ethereum-primitives.workspace = true # ethereum alloy-primitives.workspace = true @@ -29,7 +28,6 @@ alloy-evm = { workspace = true, features = ["rpc"] } # optimism op-alloy-consensus = { workspace = true, optional = true } op-alloy-rpc-types = { workspace = true, optional = true } -op-alloy-network = { workspace = true, optional = true } # io jsonrpsee-types.workspace = true @@ -48,7 +46,6 @@ default = [] op = [ "dep:op-alloy-consensus", "dep:op-alloy-rpc-types", - "dep:op-alloy-network", "reth-evm/op", "reth-primitives-traits/op", "alloy-evm/op", diff --git a/crates/rpc/rpc-convert/src/block.rs b/crates/rpc/rpc-convert/src/block.rs deleted file mode 100644 index 144bcdcac9..0000000000 --- a/crates/rpc/rpc-convert/src/block.rs +++ /dev/null @@ -1,47 +0,0 @@ -//! Conversion traits for block responses to primitive block types. - -use alloy_network::Network; -use std::convert::Infallible; - -/// Trait for converting network block responses to primitive block types. -pub trait TryFromBlockResponse { - /// The error type returned if the conversion fails. - type Error: core::error::Error + Send + Sync + Unpin; - - /// Converts a network block response to a primitive block type. - /// - /// # Returns - /// - /// Returns `Ok(Self)` on successful conversion, or `Err(Self::Error)` if the conversion fails. - fn from_block_response(block_response: N::BlockResponse) -> Result - where - Self: Sized; -} - -impl TryFromBlockResponse for alloy_consensus::Block -where - N::BlockResponse: Into, -{ - type Error = Infallible; - - fn from_block_response(block_response: N::BlockResponse) -> Result { - Ok(block_response.into()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use alloy_consensus::{Block, TxEnvelope}; - use alloy_network::Ethereum; - use alloy_rpc_types_eth::BlockTransactions; - - #[test] - fn test_try_from_block_response() { - let rpc_block: alloy_rpc_types_eth::Block = - alloy_rpc_types_eth::Block::new(Default::default(), BlockTransactions::Full(vec![])); - let result = - as TryFromBlockResponse>::from_block_response(rpc_block); - assert!(result.is_ok()); - } -} diff --git a/crates/rpc/rpc-convert/src/lib.rs b/crates/rpc/rpc-convert/src/lib.rs index 1c9ab80432..b905f00c24 100644 --- a/crates/rpc/rpc-convert/src/lib.rs +++ b/crates/rpc/rpc-convert/src/lib.rs @@ -10,17 +10,12 @@ #![cfg_attr(not(test), warn(unused_crate_dependencies))] #![cfg_attr(docsrs, feature(doc_cfg))] -pub mod block; -pub mod receipt; mod rpc; pub mod transaction; -pub use block::TryFromBlockResponse; -pub use receipt::TryFromReceiptResponse; pub use rpc::*; pub use transaction::{ - RpcConvert, RpcConverter, TransactionConversionError, TryFromTransactionResponse, TryIntoSimTx, - TxInfoMapper, + RpcConvert, RpcConverter, TransactionConversionError, TryIntoSimTx, TxInfoMapper, }; pub use alloy_evm::rpc::{CallFees, CallFeesError, EthTxEnvError, TryIntoTxEnv}; diff --git a/crates/rpc/rpc-convert/src/receipt.rs b/crates/rpc/rpc-convert/src/receipt.rs deleted file mode 100644 index c297414595..0000000000 --- a/crates/rpc/rpc-convert/src/receipt.rs +++ /dev/null @@ -1,102 +0,0 @@ -//! Conversion traits for receipt responses to primitive receipt types. - -use alloy_network::Network; -use std::convert::Infallible; - -/// Trait for converting network receipt responses to primitive receipt types. -pub trait TryFromReceiptResponse { - /// The error type returned if the conversion fails. - type Error: core::error::Error + Send + Sync + Unpin; - - /// Converts a network receipt response to a primitive receipt type. - /// - /// # Returns - /// - /// Returns `Ok(Self)` on successful conversion, or `Err(Self::Error)` if the conversion fails. - fn from_receipt_response(receipt_response: N::ReceiptResponse) -> Result - where - Self: Sized; -} - -impl TryFromReceiptResponse for reth_ethereum_primitives::Receipt { - type Error = Infallible; - - fn from_receipt_response( - receipt_response: alloy_rpc_types_eth::TransactionReceipt, - ) -> Result { - Ok(receipt_response.into_inner().into()) - } -} - -#[cfg(feature = "op")] -impl TryFromReceiptResponse for op_alloy_consensus::OpReceipt { - type Error = Infallible; - - fn from_receipt_response( - receipt_response: op_alloy_rpc_types::OpTransactionReceipt, - ) -> Result { - Ok(receipt_response.inner.inner.into_components().0.map_logs(Into::into)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use alloy_consensus::ReceiptEnvelope; - use alloy_network::Ethereum; - use reth_ethereum_primitives::Receipt; - - #[test] - fn test_try_from_receipt_response() { - let rpc_receipt = alloy_rpc_types_eth::TransactionReceipt { - inner: ReceiptEnvelope::Eip1559(Default::default()), - transaction_hash: Default::default(), - transaction_index: None, - block_hash: None, - block_number: None, - gas_used: 0, - effective_gas_price: 0, - blob_gas_used: None, - blob_gas_price: None, - from: Default::default(), - to: None, - contract_address: None, - }; - let result = - >::from_receipt_response(rpc_receipt); - assert!(result.is_ok()); - } - - #[cfg(feature = "op")] - #[test] - fn test_try_from_receipt_response_optimism() { - use alloy_consensus::ReceiptWithBloom; - use op_alloy_consensus::OpReceipt; - use op_alloy_network::Optimism; - use op_alloy_rpc_types::OpTransactionReceipt; - - let op_receipt = OpTransactionReceipt { - inner: alloy_rpc_types_eth::TransactionReceipt { - inner: ReceiptWithBloom { - receipt: OpReceipt::Eip1559(Default::default()), - logs_bloom: Default::default(), - }, - transaction_hash: Default::default(), - transaction_index: None, - block_hash: None, - block_number: None, - gas_used: 0, - effective_gas_price: 0, - blob_gas_used: None, - blob_gas_price: None, - from: Default::default(), - to: None, - contract_address: None, - }, - l1_block_info: Default::default(), - }; - let result = - >::from_receipt_response(op_receipt); - assert!(result.is_ok()); - } -} diff --git a/crates/rpc/rpc-convert/src/transaction.rs b/crates/rpc/rpc-convert/src/transaction.rs index 9e60f823c6..8706543d2e 100644 --- a/crates/rpc/rpc-convert/src/transaction.rs +++ b/crates/rpc/rpc-convert/src/transaction.rs @@ -5,7 +5,6 @@ use crate::{ use alloy_consensus::{ error::ValueError, transaction::Recovered, EthereumTxEnvelope, Sealable, TxEip4844, }; -use alloy_network::Network; use alloy_primitives::{Address, U256}; use alloy_rpc_types_eth::{request::TransactionRequest, Transaction, TransactionInfo}; use core::error; @@ -903,112 +902,3 @@ pub mod op { } } } - -/// Trait for converting network transaction responses to primitive transaction types. -pub trait TryFromTransactionResponse { - /// The error type returned if the conversion fails. - type Error: core::error::Error + Send + Sync + Unpin; - - /// Converts a network transaction response to a primitive transaction type. - /// - /// # Returns - /// - /// Returns `Ok(Self)` on successful conversion, or `Err(Self::Error)` if the conversion fails. - fn from_transaction_response( - transaction_response: N::TransactionResponse, - ) -> Result - where - Self: Sized; -} - -impl TryFromTransactionResponse - for reth_ethereum_primitives::TransactionSigned -{ - type Error = Infallible; - - fn from_transaction_response(transaction_response: Transaction) -> Result { - Ok(transaction_response.into_inner().into()) - } -} - -#[cfg(feature = "op")] -impl TryFromTransactionResponse for op_alloy_consensus::OpTxEnvelope { - type Error = Infallible; - - fn from_transaction_response( - transaction_response: op_alloy_rpc_types::Transaction, - ) -> Result { - Ok(transaction_response.inner.into_inner()) - } -} - -#[cfg(test)] -mod transaction_response_tests { - use super::*; - use alloy_consensus::{transaction::Recovered, EthereumTxEnvelope, Signed, TxLegacy}; - use alloy_network::Ethereum; - use alloy_primitives::{Address, Signature, B256, U256}; - use alloy_rpc_types_eth::Transaction; - - #[test] - fn test_ethereum_transaction_conversion() { - let signed_tx = Signed::new_unchecked( - TxLegacy::default(), - Signature::new(U256::ONE, U256::ONE, false), - B256::ZERO, - ); - let envelope = EthereumTxEnvelope::Legacy(signed_tx); - - let tx_response = Transaction { - inner: Recovered::new_unchecked(envelope, Address::ZERO), - block_hash: None, - block_number: None, - transaction_index: None, - effective_gas_price: None, - }; - - let result = >::from_transaction_response(tx_response); - assert!(result.is_ok()); - } - - #[cfg(feature = "op")] - mod op { - use super::*; - - #[test] - fn test_optimism_transaction_conversion() { - use op_alloy_consensus::OpTxEnvelope; - use op_alloy_network::Optimism; - - let signed_tx = Signed::new_unchecked( - TxLegacy::default(), - Signature::new(U256::ONE, U256::ONE, false), - B256::ZERO, - ); - let envelope = OpTxEnvelope::Legacy(signed_tx); - - let inner_tx = Transaction { - inner: Recovered::new_unchecked(envelope, Address::ZERO), - block_hash: None, - block_number: None, - transaction_index: None, - effective_gas_price: None, - }; - - let tx_response = op_alloy_rpc_types::Transaction { - inner: inner_tx, - deposit_nonce: None, - deposit_receipt_version: None, - }; - - let result = - >::from_transaction_response( - tx_response, - ); - - assert!(result.is_ok()); - } - } -} diff --git a/crates/storage/rpc-provider/Cargo.toml b/crates/storage/rpc-provider/Cargo.toml index 54eb20ac12..eb44dfd784 100644 --- a/crates/storage/rpc-provider/Cargo.toml +++ b/crates/storage/rpc-provider/Cargo.toml @@ -16,6 +16,7 @@ workspace = true reth-storage-api.workspace = true reth-chainspec.workspace = true reth-primitives.workspace = true +reth-ethereum-primitives.workspace = true reth-provider.workspace = true reth-errors.workspace = true reth-execution-types.workspace = true @@ -24,7 +25,6 @@ reth-node-types.workspace = true reth-trie.workspace = true reth-stages-types.workspace = true reth-db-api.workspace = true -reth-rpc-convert.workspace = true # alloy alloy-provider = { workspace = true, features = ["debug-api"] } diff --git a/crates/storage/rpc-provider/src/lib.rs b/crates/storage/rpc-provider/src/lib.rs index 8771e5b215..dc3c406e87 100644 --- a/crates/storage/rpc-provider/src/lib.rs +++ b/crates/storage/rpc-provider/src/lib.rs @@ -51,13 +51,14 @@ use reth_provider::{ TransactionVariant, TransactionsProvider, }; use reth_prune_types::{PruneCheckpoint, PruneSegment}; -use reth_rpc_convert::{TryFromBlockResponse, TryFromReceiptResponse, TryFromTransactionResponse}; +pub mod rpc_response; use reth_stages_types::{StageCheckpoint, StageId}; use reth_storage_api::{ BlockBodyIndicesProvider, BlockReaderIdExt, BlockSource, DBProvider, NodePrimitivesProvider, ReceiptProviderIdExt, StatsReader, }; use reth_trie::{updates::TrieUpdates, AccountProof, HashedPostState, MultiProof, TrieInput}; +pub use rpc_response::{EthRpcConverter, RpcResponseConverter}; use std::{ collections::BTreeMap, future::{Future, IntoFuture}, @@ -100,6 +101,14 @@ impl RpcBlockchainProviderConfig { } } +/// Type-erased RPC response converter stored in [`RpcBlockchainProvider`]. +type DynRpcConverter = dyn RpcResponseConverter< + N, + Block = BlockTy, + Transaction = TxTy, + Receipt = ReceiptTy, +>; + /// An RPC-based blockchain provider that fetches blockchain data via remote RPC calls. /// /// This is the RPC equivalent of @@ -119,6 +128,7 @@ impl RpcBlockchainProviderConfig { pub struct RpcBlockchainProvider where Node: NodeTypes, + N: Network, { /// The underlying Alloy provider provider: P, @@ -132,25 +142,66 @@ where config: RpcBlockchainProviderConfig, /// Cached chain spec chain_spec: Arc, + /// Converts RPC responses to primitive types. + converter: Arc>, } -impl std::fmt::Debug for RpcBlockchainProvider { +impl std::fmt::Debug for RpcBlockchainProvider { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("RpcBlockchainProvider").field("config", &self.config).finish() } } -impl RpcBlockchainProvider { - /// Creates a new `RpcBlockchainProvider` with default configuration +impl RpcBlockchainProvider +where + EthRpcConverter: RpcResponseConverter< + N, + Block = BlockTy, + Transaction = TxTy, + Receipt = ReceiptTy, + >, +{ + /// Creates a new `RpcBlockchainProvider` with the default [`EthRpcConverter`]. pub fn new(provider: P) -> Self where Node::ChainSpec: Default, { - Self::new_with_config(provider, RpcBlockchainProviderConfig::default()) + Self::new_with_converter(provider, EthRpcConverter) + } +} + +impl RpcBlockchainProvider { + /// Creates a new `RpcBlockchainProvider` with default configuration and the given converter. + pub fn new_with_converter( + provider: P, + converter: impl RpcResponseConverter< + N, + Block = BlockTy, + Transaction = TxTy, + Receipt = ReceiptTy, + >, + ) -> Self + where + Node::ChainSpec: Default, + { + Self::new_with_converter_and_config( + provider, + converter, + RpcBlockchainProviderConfig::default(), + ) } - /// Creates a new `RpcBlockchainProvider` with custom configuration - pub fn new_with_config(provider: P, config: RpcBlockchainProviderConfig) -> Self + /// Creates a new `RpcBlockchainProvider` with custom configuration and the given converter. + pub fn new_with_converter_and_config( + provider: P, + converter: impl RpcResponseConverter< + N, + Block = BlockTy, + Transaction = TxTy, + Receipt = ReceiptTy, + >, + config: RpcBlockchainProviderConfig, + ) -> Self where Node::ChainSpec: Default, { @@ -162,19 +213,13 @@ impl RpcBlockchainProvider { canon_state_notification, config, chain_spec: Arc::new(Node::ChainSpec::default()), + converter: Arc::new(converter), } } /// Use a custom chain spec for the provider pub fn with_chain_spec(self, chain_spec: Arc) -> Self { - Self { - provider: self.provider, - node_types: std::marker::PhantomData, - network: std::marker::PhantomData, - canon_state_notification: self.canon_state_notification, - config: self.config, - chain_spec, - } + Self { chain_spec, ..self } } /// Helper function to execute async operations in a blocking context @@ -332,7 +377,6 @@ where P: Provider + Clone + 'static, N: Network, Node: NodeTypes, - BlockTy: TryFromBlockResponse, { type Header = HeaderTy; @@ -347,8 +391,7 @@ where }; // Convert the network block response to primitive block - let block = as TryFromBlockResponse>::from_block_response(block_response) - .map_err(ProviderError::other)?; + let block = self.converter.block(block_response).map_err(ProviderError::other)?; Ok(Some(block.into_header())) } @@ -384,8 +427,7 @@ where let block_hash = block_response.header().hash(); // Convert the network block response to primitive block - let block = as TryFromBlockResponse>::from_block_response(block_response) - .map_err(ProviderError::other)?; + let block = self.converter.block(block_response).map_err(ProviderError::other)?; Ok(Some(SealedHeader::new(block.into_header(), block_hash))) } @@ -422,9 +464,6 @@ where P: Provider + Clone + 'static, N: Network, Node: NodeTypes, - BlockTy: TryFromBlockResponse, - TxTy: TryFromTransactionResponse, - ReceiptTy: TryFromReceiptResponse, { type Block = BlockTy; @@ -447,8 +486,7 @@ where }; // Convert the network block response to primitive block - let block = as TryFromBlockResponse>::from_block_response(block_response) - .map_err(ProviderError::other)?; + let block = self.converter.block(block_response).map_err(ProviderError::other)?; Ok(Some(block)) } @@ -507,9 +545,6 @@ where P: Provider + Clone + 'static, N: Network, Node: NodeTypes, - BlockTy: TryFromBlockResponse, - TxTy: TryFromTransactionResponse, - ReceiptTy: TryFromReceiptResponse, { fn block_by_id(&self, id: BlockId) -> ProviderResult> { match id { @@ -541,7 +576,6 @@ where P: Provider + Clone + 'static, N: Network, Node: NodeTypes, - ReceiptTy: TryFromReceiptResponse, { type Receipt = ReceiptTy; @@ -555,15 +589,10 @@ where })?; let Some(receipt_response) = receipt_response else { - // If the receipt was not found, return None return Ok(None); }; - // Convert the network receipt response to primitive receipt - let receipt = - as TryFromReceiptResponse>::from_receipt_response(receipt_response) - .map_err(ProviderError::other)?; - + let receipt = self.converter.receipt(receipt_response).map_err(ProviderError::other)?; Ok(Some(receipt)) } @@ -579,19 +608,12 @@ where .map_err(ProviderError::other)?; let Some(receipts) = receipts_response else { - // If the receipts were not found, return None return Ok(None); }; - // Convert the network receipts response to primitive receipts let receipts = receipts .into_iter() - .map(|receipt_response| { - as TryFromReceiptResponse>::from_receipt_response( - receipt_response, - ) - .map_err(ProviderError::other) - }) + .map(|r| self.converter.receipt(r).map_err(ProviderError::other)) .collect::, _>>()?; Ok(Some(receipts)) @@ -618,7 +640,6 @@ where P: Provider + Clone + 'static, N: Network, Node: NodeTypes, - ReceiptTy: TryFromReceiptResponse, { } @@ -627,8 +648,6 @@ where P: Provider + Clone + 'static, N: Network, Node: NodeTypes, - BlockTy: TryFromBlockResponse, - TxTy: TryFromTransactionResponse, { type Transaction = TxTy; @@ -653,16 +672,11 @@ where })?; let Some(transaction_response) = transaction_response else { - // If the transaction was not found, return None return Ok(None); }; - // Convert the network transaction response to primitive transaction - let transaction = as TryFromTransactionResponse>::from_transaction_response( - transaction_response, - ) - .map_err(ProviderError::other)?; - + let transaction = + self.converter.transaction(transaction_response).map_err(ProviderError::other)?; Ok(Some(transaction)) } @@ -682,14 +696,10 @@ where })?; let Some(block_response) = block_response else { - // If the block was not found, return None return Ok(None); }; - // Convert the network block response to primitive block - let block = as TryFromBlockResponse>::from_block_response(block_response) - .map_err(ProviderError::other)?; - + let block = self.converter.block(block_response).map_err(ProviderError::other)?; Ok(Some(block.into_body().into_transactions())) } @@ -855,7 +865,7 @@ where impl NodePrimitivesProvider for RpcBlockchainProvider where P: Send + Sync, - N: Send + Sync, + N: Network, Node: NodeTypes, { type Primitives = PrimitivesTy; @@ -876,7 +886,7 @@ where impl ChainSpecProvider for RpcBlockchainProvider where P: Send + Sync, - N: Send + Sync, + N: Network, Node: NodeTypes, Node::ChainSpec: Default, { @@ -1834,7 +1844,7 @@ where impl ChainSpecProvider for RpcBlockchainStateProvider where P: Send + Sync + std::fmt::Debug, - N: Send + Sync, + N: Network, Node: NodeTypes, Node::ChainSpec: Default, { @@ -1888,7 +1898,7 @@ where impl NodePrimitivesProvider for RpcBlockchainStateProvider where P: Send + Sync + std::fmt::Debug, - N: Send + Sync, + N: Network, Node: NodeTypes, { type Primitives = PrimitivesTy; diff --git a/crates/storage/rpc-provider/src/rpc_response.rs b/crates/storage/rpc-provider/src/rpc_response.rs new file mode 100644 index 0000000000..28031ac7f4 --- /dev/null +++ b/crates/storage/rpc-provider/src/rpc_response.rs @@ -0,0 +1,75 @@ +//! Unified converter for RPC network responses to primitive types. + +use alloy_network::Network; +use alloy_rpc_types::eth::{Block, Transaction, TransactionReceipt}; +use core::fmt::Debug; +use reth_ethereum_primitives::{Receipt, TransactionSigned}; + +/// Error type used by [`RpcResponseConverter`]. +#[derive(Debug)] +pub struct RpcResponseConverterError(pub Box); + +impl core::fmt::Display for RpcResponseConverterError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + core::fmt::Display::fmt(&self.0, f) + } +} + +impl core::error::Error for RpcResponseConverterError { + fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { + self.0.source() + } +} + +/// Converts RPC network responses (blocks, transactions, receipts) into primitive types. +pub trait RpcResponseConverter: Send + Sync + Debug + 'static { + /// Primitive block type. + type Block; + /// Primitive transaction type. + type Transaction; + /// Primitive receipt type. + type Receipt; + + /// Converts a network block response to a primitive block. + fn block(&self, response: N::BlockResponse) -> Result; + + /// Converts a network transaction response to a primitive transaction. + fn transaction( + &self, + response: N::TransactionResponse, + ) -> Result; + + /// Converts a network receipt response to a primitive receipt. + fn receipt( + &self, + response: N::ReceiptResponse, + ) -> Result; +} + +/// Default Ethereum converter using upstream `From` impls. +#[derive(Debug, Clone, Copy, Default)] +pub struct EthRpcConverter; + +impl RpcResponseConverter for EthRpcConverter { + type Block = alloy_consensus::Block; + type Transaction = TransactionSigned; + type Receipt = Receipt; + + fn block(&self, response: Block) -> Result { + Ok(response.into()) + } + + fn transaction( + &self, + response: Transaction, + ) -> Result { + Ok(response.into_inner().into()) + } + + fn receipt( + &self, + response: TransactionReceipt, + ) -> Result { + Ok(response.into_inner().into()) + } +}