diff --git a/Cargo.lock b/Cargo.lock index e658b9e9859..872ef282647 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -109,9 +109,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "alloy-chains" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03fd095a9d70f4b1c5c102c84a4c782867a5c6416dbf6dcd42a63e7c7a89d3c8" +checksum = "24ceb48af11349cd7fbd12aa739800be3c4b3965f640b7ae26666907f3bdf091" dependencies = [ "alloy-rlp", "arbitrary", @@ -140,7 +140,7 @@ dependencies = [ [[package]] name = "alloy-consensus" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#7578618d61213ea832c40c7e613f1d644ce08f27" +source = "git+https://github.com/alloy-rs/alloy#b000e16f06161c15b42a4c59f27cb3feb23b3c14" dependencies = [ "alloy-eips 0.1.0 (git+https://github.com/alloy-rs/alloy)", "alloy-primitives", @@ -165,7 +165,7 @@ dependencies = [ "itoa", "serde", "serde_json", - "winnow 0.6.10", + "winnow 0.6.13", ] [[package]] @@ -189,7 +189,7 @@ dependencies = [ [[package]] name = "alloy-eips" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#7578618d61213ea832c40c7e613f1d644ce08f27" +source = "git+https://github.com/alloy-rs/alloy#b000e16f06161c15b42a4c59f27cb3feb23b3c14" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -214,7 +214,7 @@ dependencies = [ [[package]] name = "alloy-genesis" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#7578618d61213ea832c40c7e613f1d644ce08f27" +source = "git+https://github.com/alloy-rs/alloy#b000e16f06161c15b42a4c59f27cb3feb23b3c14" dependencies = [ "alloy-primitives", "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy)", @@ -417,7 +417,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#7578618d61213ea832c40c7e613f1d644ce08f27" +source = "git+https://github.com/alloy-rs/alloy#b000e16f06161c15b42a4c59f27cb3feb23b3c14" dependencies = [ "alloy-rpc-types-eth 0.1.0 (git+https://github.com/alloy-rs/alloy)", "alloy-serde 0.1.0 (git+https://github.com/alloy-rs/alloy)", @@ -489,7 +489,7 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#7578618d61213ea832c40c7e613f1d644ce08f27" +source = "git+https://github.com/alloy-rs/alloy#b000e16f06161c15b42a4c59f27cb3feb23b3c14" dependencies = [ "alloy-consensus 0.1.0 (git+https://github.com/alloy-rs/alloy)", "alloy-eips 0.1.0 (git+https://github.com/alloy-rs/alloy)", @@ -529,7 +529,7 @@ dependencies = [ [[package]] name = "alloy-serde" version = "0.1.0" -source = "git+https://github.com/alloy-rs/alloy#7578618d61213ea832c40c7e613f1d644ce08f27" +source = "git+https://github.com/alloy-rs/alloy#b000e16f06161c15b42a4c59f27cb3feb23b3c14" dependencies = [ "alloy-primitives", "serde", @@ -622,7 +622,7 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baa2fbd22d353d8685bd9fee11ba2d8b5c3b1d11e56adb3265fcf1f32bfdf404" dependencies = [ - "winnow 0.6.10", + "winnow 0.6.13", ] [[package]] @@ -679,7 +679,7 @@ dependencies = [ "alloy-transport", "bytes", "futures", - "interprocess 2.1.1", + "interprocess 2.2.0", "pin-project", "serde_json", "tokio", @@ -776,9 +776,9 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ "windows-sys 0.52.0", ] @@ -1120,9 +1120,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.72" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", @@ -1669,9 +1669,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695" dependencies = [ "jobserver", "libc", @@ -1807,9 +1807,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "coins-bip32" @@ -3645,9 +3645,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f3935c160d00ac752e09787e6e6bfc26494c2183cc922f1bc678a60d4733bc2" +checksum = "d0e7a4dd27b9476dc40cb050d3632d3bba3a70ddbff012285f7f8559a1e7e545" [[package]] name = "httpdate" @@ -3726,7 +3726,7 @@ dependencies = [ "hyper", "hyper-util", "log", - "rustls 0.23.9", + "rustls 0.23.10", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -4108,9 +4108,9 @@ dependencies = [ [[package]] name = "interprocess" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13f2533e1f1a70bec71ea7a85d1c0a4dab141c314035ce76e51a19a2f48be708" +checksum = "67bafc2f5dbdad79a6d925649758d5472647b416028099f0b829d1b67fdd47d3" dependencies = [ "doctest-file", "futures-core", @@ -4283,7 +4283,7 @@ dependencies = [ "http 1.1.0", "jsonrpsee-core", "pin-project", - "rustls 0.23.9", + "rustls 0.23.10", "rustls-pki-types", "rustls-platform-verifier", "soketto", @@ -4338,7 +4338,7 @@ dependencies = [ "hyper-util", "jsonrpsee-core", "jsonrpsee-types", - "rustls 0.23.9", + "rustls 0.23.10", "rustls-platform-verifier", "serde", "serde_json", @@ -4603,9 +4603,9 @@ dependencies = [ [[package]] name = "libp2p-identity" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "999ec70441b2fb35355076726a6bc466c932e9bdc66f6a11c6c0aa17c7ab9be0" +checksum = "55cca1eb2bc1fd29f099f3daaab7effd01e1a54b7c577d0ed082521034d912e8" dependencies = [ "asn1_der", "bs58", @@ -5331,9 +5331,9 @@ dependencies = [ [[package]] name = "object" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" +checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434" dependencies = [ "memchr", ] @@ -7651,6 +7651,7 @@ version = "1.0.0-rc.1" dependencies = [ "futures", "jsonrpsee", + "parking_lot 0.12.3", "reth-evm", "reth-evm-optimism", "reth-network-api", @@ -8570,9 +8571,9 @@ dependencies = [ [[package]] name = "roaring" -version = "0.10.4" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b26f4c25a604fcb3a1bcd96dd6ba37c93840de95de8198d94c0d571a74a804d1" +checksum = "7699249cc2c7d71939f30868f47e9d7add0bdc030d90ee10bfd16887ff8bb1c8" dependencies = [ "bytemuck", "byteorder", @@ -8717,9 +8718,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.9" +version = "0.23.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a218f0f6d05669de4eabfb24f31ce802035c952429d037507b4a4a39f0e60c5b" +checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402" dependencies = [ "log", "once_cell", @@ -8770,7 +8771,7 @@ dependencies = [ "jni", "log", "once_cell", - "rustls 0.23.9", + "rustls 0.23.10", "rustls-native-certs", "rustls-platform-verifier-android", "rustls-webpki", @@ -9438,9 +9439,9 @@ dependencies = [ [[package]] name = "strum_macros" -version = "0.26.3" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7993a8e3a9e88a00351486baae9522c91b123a088f76469e5bd5cc17198ea87" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -9480,9 +9481,9 @@ dependencies = [ [[package]] name = "symbolic-common" -version = "12.8.0" +version = "12.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cccfffbc6bb3bb2d3a26cd2077f4d055f6808d266f9d4d158797a4c60510dfe" +checksum = "71297dc3e250f7dbdf8adb99e235da783d690f5819fdeb4cce39d9cfb0aca9f1" dependencies = [ "debugid", "memmap2 0.9.4", @@ -9492,9 +9493,9 @@ dependencies = [ [[package]] name = "symbolic-demangle" -version = "12.8.0" +version = "12.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a99812da4020a67e76c4eb41f08c87364c14170495ff780f30dd519c221a68" +checksum = "424fa2c9bf2c862891b9cfd354a752751a6730fd838a4691e7f6c2c7957b9daf" dependencies = [ "cpp_demangle", "rustc-demangle", @@ -9877,7 +9878,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.9", + "rustls 0.23.10", "rustls-pki-types", "tokio", ] @@ -9902,7 +9903,7 @@ checksum = "becd34a233e7e31a3dbf7c7241b38320f57393dcae8e7324b0167d21b8e320b0" dependencies = [ "futures-util", "log", - "rustls 0.23.9", + "rustls 0.23.10", "rustls-pki-types", "tokio", "tokio-rustls 0.26.0", @@ -9967,7 +9968,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.10", + "winnow 0.6.13", ] [[package]] @@ -10243,7 +10244,7 @@ dependencies = [ "httparse", "log", "rand 0.8.5", - "rustls 0.23.9", + "rustls 0.23.10", "rustls-pki-types", "sha1", "thiserror", @@ -10339,9 +10340,9 @@ dependencies = [ [[package]] name = "unicode-width" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "universal-hash" @@ -10409,9 +10410,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" @@ -10597,9 +10598,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +checksum = "3c452ad30530b54a4d8e71952716a212b08efd0f3562baa66c29a618b07da7c3" dependencies = [ "rustls-pki-types", ] @@ -10718,9 +10719,9 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "749f0da9cc72d82e600d8d2e44cadd0b9eedb9038f71a1c58556ac1c5791813b" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" dependencies = [ "windows-targets 0.52.5", ] @@ -10875,9 +10876,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.10" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f217b6745021054125ef5741032a021a9c65f82bee2a8017cca928f1e3179991" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] diff --git a/crates/optimism/rpc/Cargo.toml b/crates/optimism/rpc/Cargo.toml index d05c1a50a96..389be51e0f3 100644 --- a/crates/optimism/rpc/Cargo.toml +++ b/crates/optimism/rpc/Cargo.toml @@ -28,9 +28,13 @@ reth-tasks.workspace = true # async futures.workspace = true tokio = { workspace = true, features = ["sync"] } +parking_lot.workspace = true # rpc jsonrpsee.workspace = true # misc -thiserror.workspace = true \ No newline at end of file +thiserror.workspace = true + +[features] +optimism = [] \ No newline at end of file diff --git a/crates/optimism/rpc/src/block.rs b/crates/optimism/rpc/src/block.rs index 53b23233d83..014d3f123d3 100644 --- a/crates/optimism/rpc/src/block.rs +++ b/crates/optimism/rpc/src/block.rs @@ -1,25 +1,14 @@ //! Loads and formats OP block RPC response. -use reth_evm::ConfigureEvm; use reth_primitives::TransactionMeta; -use reth_provider::{ - BlockReaderIdExt, ChainSpecProvider, EvmEnvProvider, HeaderProvider, StateProviderFactory, -}; +use reth_provider::{BlockReaderIdExt, ChainSpecProvider, HeaderProvider}; use reth_rpc::eth::{ - api::{ - EthBlocks, LoadBlock, LoadPendingBlock, LoadReceipt, PendingBlock, ReceiptBuilder, - SpawnBlocking, - }, - cache::EthStateCache, + api::{EthBlocks, LoadBlock, LoadReceipt, ReceiptBuilder}, error::EthResult, }; use reth_rpc_types::{AnyTransactionReceipt, BlockId}; -use reth_transaction_pool::TransactionPool; -use tokio::sync::Mutex; - -use crate::receipt::op_receipt_fields; -use super::OptimismApi; +use crate::{receipt::op_receipt_fields, OptimismApi}; impl EthBlocks for OptimismApi @@ -78,50 +67,3 @@ where Ok(None) } } - -impl LoadBlock - for OptimismApi -where - Self: LoadPendingBlock + SpawnBlocking, - Provider: BlockReaderIdExt, -{ - #[inline] - fn provider(&self) -> impl BlockReaderIdExt { - self.inner.provider() - } - - #[inline] - fn cache(&self) -> &EthStateCache { - self.inner.cache() - } -} - -impl LoadPendingBlock - for OptimismApi -where - Provider: BlockReaderIdExt + EvmEnvProvider + ChainSpecProvider + StateProviderFactory, - Pool: TransactionPool, - EvmConfig: ConfigureEvm, -{ - #[inline] - fn provider( - &self, - ) -> impl BlockReaderIdExt + EvmEnvProvider + ChainSpecProvider + StateProviderFactory { - self.inner.provider() - } - - #[inline] - fn pool(&self) -> impl TransactionPool { - self.inner.pool() - } - - #[inline] - fn pending_block(&self) -> &Mutex> { - self.inner.pending_block() - } - - #[inline] - fn evm_config(&self) -> &impl ConfigureEvm { - self.inner.evm_config() - } -} diff --git a/crates/optimism/rpc/src/lib.rs b/crates/optimism/rpc/src/lib.rs index c968f7b2f0b..985b0131637 100644 --- a/crates/optimism/rpc/src/lib.rs +++ b/crates/optimism/rpc/src/lib.rs @@ -6,14 +6,20 @@ issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" )] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +// The `optimism` feature must be enabled to use this crate. +#![cfg(feature = "optimism")] use std::sync::Arc; -use reth_rpc::eth::api::{EthApiInner, SpawnBlocking}; -use reth_tasks::{pool::BlockingTaskPool, TaskSpawner}; +use reth_rpc::{ + call_impl, eth::api::EthApiInner, eth_call_impl, eth_fees_impl, eth_state_impl, + eth_transactions_impl, load_block_impl, load_fee_impl, load_state_impl, load_transaction_impl, + spawn_blocking_impl, trace_impl, +}; pub mod block; pub mod error; +pub mod pending_block; pub mod receipt; pub mod transaction; @@ -29,16 +35,16 @@ pub struct OptimismApi { inner: Arc>, } -impl SpawnBlocking - for OptimismApi -where - Self: Clone + Send + Sync + 'static, -{ - fn io_task_spawner(&self) -> impl TaskSpawner { - self.inner.task_spawner() - } - - fn tracing_task_pool(&self) -> &BlockingTaskPool { - self.inner.blocking_task_pool() - } -} +eth_call_impl!(OptimismApi); +eth_fees_impl!(OptimismApi); +eth_state_impl!(OptimismApi); +eth_transactions_impl!(OptimismApi); + +load_block_impl!(OptimismApi); +load_fee_impl!(OptimismApi); +load_state_impl!(OptimismApi); +load_transaction_impl!(OptimismApi); + +call_impl!(OptimismApi); +spawn_blocking_impl!(OptimismApi); +trace_impl!(OptimismApi); diff --git a/crates/optimism/rpc/src/pending_block.rs b/crates/optimism/rpc/src/pending_block.rs new file mode 100644 index 00000000000..54b0d74c018 --- /dev/null +++ b/crates/optimism/rpc/src/pending_block.rs @@ -0,0 +1,82 @@ +//! Loads OP pending block for a RPC response. + +use reth_evm::ConfigureEvm; +use reth_primitives::{ + revm_primitives::{BlockEnv, ExecutionResult}, + BlockNumber, Receipt, TransactionSignedEcRecovered, B256, +}; +use reth_provider::{ + BlockReaderIdExt, ChainSpecProvider, EvmEnvProvider, ExecutionOutcome, StateProviderFactory, +}; +use reth_rpc::eth::{ + api::{LoadPendingBlock, SpawnBlocking}, + PendingBlock, +}; +use reth_transaction_pool::TransactionPool; + +use crate::OptimismApi; + +impl LoadPendingBlock + for OptimismApi +where + Self: SpawnBlocking, + Provider: BlockReaderIdExt + EvmEnvProvider + ChainSpecProvider + StateProviderFactory, + Pool: TransactionPool, + EvmConfig: ConfigureEvm, +{ + #[inline] + fn provider( + &self, + ) -> impl reth_provider::BlockReaderIdExt + + reth_provider::EvmEnvProvider + + reth_provider::ChainSpecProvider + + reth_provider::StateProviderFactory { + self.inner.provider() + } + + #[inline] + fn pool(&self) -> impl reth_transaction_pool::TransactionPool { + self.inner.pool() + } + + #[inline] + fn pending_block(&self) -> &tokio::sync::Mutex> { + self.inner.pending_block() + } + + #[inline] + fn evm_config(&self) -> &impl reth_evm::ConfigureEvm { + self.inner.evm_config() + } + + fn assemble_receipt( + &self, + tx: &TransactionSignedEcRecovered, + result: ExecutionResult, + cumulative_gas_used: u64, + ) -> Receipt { + Receipt { + tx_type: tx.tx_type(), + success: result.is_success(), + cumulative_gas_used, + logs: result.into_logs().into_iter().map(Into::into).collect(), + deposit_nonce: None, + deposit_receipt_version: None, + } + } + + fn receipts_root( + &self, + _block_env: &BlockEnv, + execution_outcome: &ExecutionOutcome, + block_number: BlockNumber, + ) -> B256 { + execution_outcome + .optimism_receipts_root_slow( + block_number, + self.provider().chain_spec().as_ref(), + _block_env.timestamp.to::(), + ) + .expect("Block is present") + } +} diff --git a/crates/rpc/rpc/src/eth/api/block.rs b/crates/rpc/rpc/src/eth/api/block.rs index 776379b1445..4c16a7c8c2e 100644 --- a/crates/rpc/rpc/src/eth/api/block.rs +++ b/crates/rpc/rpc/src/eth/api/block.rs @@ -1,40 +1,47 @@ //! Contains RPC handler implementations specific to blocks. -use reth_provider::{BlockReaderIdExt, HeaderProvider}; +use crate::EthApi; -use crate::{ - eth::{ - api::{EthBlocks, LoadBlock}, - cache::EthStateCache, - }, - EthApi, -}; - -use super::{LoadPendingBlock, SpawnBlocking}; - -impl EthBlocks for EthApi -where - Self: LoadBlock, - Provider: HeaderProvider, -{ - #[inline] - fn provider(&self) -> impl HeaderProvider { - self.inner.provider() - } +/// Implements [`EthBlocks`](crate::eth::api::EthBlocks) for a type, that has similar +/// data layout to [`EthApi`]. +#[macro_export] +macro_rules! eth_blocks_impl { + ($network_api:ty) => { + impl $crate::eth::api::EthBlocks for $network_api + where + Self: $crate::eth::api::LoadBlock, + Provider: reth_provider::HeaderProvider, + { + #[inline] + fn provider(&self) -> impl reth_provider::HeaderProvider { + self.inner.provider() + } + } + }; } -impl LoadBlock for EthApi -where - Self: LoadPendingBlock + SpawnBlocking, - Provider: BlockReaderIdExt, -{ - #[inline] - fn provider(&self) -> impl BlockReaderIdExt { - self.inner.provider() - } +/// Implements [`LoadBlock`](crate::eth::api::LoadBlock) for a type, that has similar +/// data layout to [`EthApi`]. +#[macro_export] +macro_rules! load_block_impl { + ($network_api:ty) => { + impl $crate::eth::api::LoadBlock for $network_api + where + Self: $crate::eth::api::LoadPendingBlock + $crate::eth::api::SpawnBlocking, + Provider: reth_provider::BlockReaderIdExt, + { + #[inline] + fn provider(&self) -> impl reth_provider::BlockReaderIdExt { + self.inner.provider() + } - #[inline] - fn cache(&self) -> &EthStateCache { - self.inner.cache() - } + #[inline] + fn cache(&self) -> &$crate::eth::cache::EthStateCache { + self.inner.cache() + } + } + }; } + +eth_blocks_impl!(EthApi); +load_block_impl!(EthApi); diff --git a/crates/rpc/rpc/src/eth/api/call.rs b/crates/rpc/rpc/src/eth/api/call.rs index e3e0cb74e94..db68343b531 100644 --- a/crates/rpc/rpc/src/eth/api/call.rs +++ b/crates/rpc/rpc/src/eth/api/call.rs @@ -1,29 +1,41 @@ //! Contains RPC handler implementations specific to endpoints that call/execute within evm. -use reth_evm::ConfigureEvm; +use crate::EthApi; -use crate::eth::{ - api::{Call, EthCall, LoadPendingBlock, LoadState, SpawnBlocking}, - EthApi, -}; - -impl EthCall for EthApi where - Self: Call + LoadPendingBlock -{ +/// Implements [`EthCall`](crate::eth::api::EthCall) for a type, that has similar +/// data layout to [`EthApi`]. +#[macro_export] +macro_rules! eth_call_impl { + ($network_api:ty) => { + impl $crate::eth::api::EthCall for $network_api where + Self: $crate::eth::api::Call + $crate::eth::api::LoadPendingBlock + { + } + }; } -impl Call for EthApi -where - Self: LoadState + SpawnBlocking, - EvmConfig: ConfigureEvm, -{ - #[inline] - fn call_gas_limit(&self) -> u64 { - self.inner.gas_cap() - } +/// Implements [`Call`](crate::eth::api::Call) for a type, that has similar +/// data layout to [`EthApi`]. +#[macro_export] +macro_rules! call_impl { + ($network_api:ty) => { + impl $crate::eth::api::Call for $network_api + where + Self: $crate::eth::api::LoadState + $crate::eth::api::SpawnBlocking, + EvmConfig: reth_evm::ConfigureEvm, + { + #[inline] + fn call_gas_limit(&self) -> u64 { + self.inner.gas_cap() + } - #[inline] - fn evm_config(&self) -> &impl ConfigureEvm { - self.inner.evm_config() - } + #[inline] + fn evm_config(&self) -> &impl reth_evm::ConfigureEvm { + self.inner.evm_config() + } + } + }; } + +eth_call_impl!(EthApi); +call_impl!(EthApi); diff --git a/crates/rpc/rpc/src/eth/api/fees.rs b/crates/rpc/rpc/src/eth/api/fees.rs index 88c1b39d63e..0392bbc60e1 100644 --- a/crates/rpc/rpc/src/eth/api/fees.rs +++ b/crates/rpc/rpc/src/eth/api/fees.rs @@ -1,44 +1,60 @@ //! Contains RPC handler implementations for fee history. -use reth_provider::{BlockIdReader, BlockReaderIdExt, ChainSpecProvider, HeaderProvider}; +use crate::EthApi; -use crate::{ - eth::{ - api::{EthFees, LoadBlock, LoadFee}, - cache::EthStateCache, - gas_oracle::GasPriceOracle, - FeeHistoryCache, - }, - EthApi, -}; - -impl EthFees for EthApi where - Self: LoadFee -{ +/// Implements [`EthFees`](crate::eth::api::EthFees) for a type, that has similar +/// data layout to [`EthApi`]. +#[macro_export] +macro_rules! eth_fees_impl { + ($network_api:ty) => { + impl $crate::eth::api::EthFees for $network_api where + Self: $crate::eth::api::LoadFee + { + } + }; } -impl LoadFee for EthApi -where - Self: LoadBlock, - Provider: BlockReaderIdExt + HeaderProvider + ChainSpecProvider, -{ - #[inline] - fn provider(&self) -> impl BlockIdReader + HeaderProvider + ChainSpecProvider { - self.inner.provider() - } +/// Implements [`LoadFee`](crate::eth::api::LoadFee) for a type, that has similar +/// data layout to [`EthApi`]. +#[macro_export] +macro_rules! load_fee_impl { + ($network_api:ty) => { + impl $crate::eth::api::LoadFee for $network_api + where + Self: $crate::eth::api::LoadBlock, + Provider: reth_provider::BlockReaderIdExt + + reth_provider::HeaderProvider + + reth_provider::ChainSpecProvider, + { + #[inline] + fn provider( + &self, + ) -> impl reth_provider::BlockIdReader + + reth_provider::HeaderProvider + + reth_provider::ChainSpecProvider { + self.inner.provider() + } - #[inline] - fn cache(&self) -> &EthStateCache { - self.inner.cache() - } + #[inline] + fn cache(&self) -> &$crate::eth::cache::EthStateCache { + self.inner.cache() + } - #[inline] - fn gas_oracle(&self) -> &GasPriceOracle { - self.inner.gas_oracle() - } + #[inline] + fn gas_oracle( + &self, + ) -> &$crate::eth::gas_oracle::GasPriceOracle + { + self.inner.gas_oracle() + } - #[inline] - fn fee_history_cache(&self) -> &FeeHistoryCache { - self.inner.fee_history_cache() - } + #[inline] + fn fee_history_cache(&self) -> &$crate::eth::FeeHistoryCache { + self.inner.fee_history_cache() + } + } + }; } + +eth_fees_impl!(EthApi); +load_fee_impl!(EthApi); diff --git a/crates/rpc/rpc/src/eth/api/mod.rs b/crates/rpc/rpc/src/eth/api/mod.rs index 985b0069541..d9627b2dce8 100644 --- a/crates/rpc/rpc/src/eth/api/mod.rs +++ b/crates/rpc/rpc/src/eth/api/mod.rs @@ -269,20 +269,29 @@ where } } -impl SpawnBlocking - for EthApi -where - Self: Clone + Send + Sync + 'static, -{ - fn io_task_spawner(&self) -> impl TaskSpawner { - self.inner.task_spawner() - } - - fn tracing_task_pool(&self) -> &BlockingTaskPool { - self.inner.blocking_task_pool() - } +/// Implements [`SpawnBlocking`] for a type, that has similar data layout to [`EthApi`]. +#[macro_export] +macro_rules! spawn_blocking_impl { + ($network_api:ty) => { + impl $crate::eth::api::SpawnBlocking for $network_api + where + Self: Clone + Send + Sync + 'static, + { + #[inline] + fn io_task_spawner(&self) -> impl reth_tasks::TaskSpawner { + self.inner.task_spawner() + } + + #[inline] + fn tracing_task_pool(&self) -> &reth_tasks::pool::BlockingTaskPool { + self.inner.blocking_task_pool() + } + } + }; } +spawn_blocking_impl!(EthApi); + /// The default gas limit for `eth_call` and adjacent calls. /// /// This is different from the default to regular 30M block gas limit diff --git a/crates/rpc/rpc/src/eth/api/pending_block.rs b/crates/rpc/rpc/src/eth/api/pending_block.rs index 4fb6871d327..fab17ef7718 100644 --- a/crates/rpc/rpc/src/eth/api/pending_block.rs +++ b/crates/rpc/rpc/src/eth/api/pending_block.rs @@ -4,71 +4,64 @@ use std::time::Instant; use derive_more::Constructor; use reth_errors::ProviderError; -use reth_evm::ConfigureEvm; use reth_primitives::{ - constants::{eip4844::MAX_DATA_GAS_PER_BLOCK, BEACON_NONCE, EMPTY_ROOT_HASH}, - proofs, - revm::env::tx_env_with_recovered, - revm_primitives::{ - BlockEnv, CfgEnvWithHandlerCfg, EVMError, Env, InvalidTransaction, ResultAndState, SpecId, - }, - Block, BlockId, BlockNumberOrTag, ChainSpec, Header, IntoRecoveredTransaction, Receipt, - Requests, SealedBlockWithSenders, SealedHeader, B256, EMPTY_OMMER_ROOT_HASH, U256, + revm_primitives::{BlockEnv, CfgEnvWithHandlerCfg}, + BlockId, BlockNumberOrTag, ChainSpec, SealedBlockWithSenders, SealedHeader, B256, }; -use reth_provider::{ - BlockReaderIdExt, ChainSpecProvider, EvmEnvProvider, ExecutionOutcome, StateProviderFactory, -}; -use reth_revm::{ - database::StateProviderDatabase, - state_change::{ - apply_beacon_root_contract_call, apply_blockhashes_update, - post_block_withdrawals_balance_increments, - }, -}; -use reth_transaction_pool::{BestTransactionsAttributes, TransactionPool}; -use revm::{db::states::bundle_state::BundleRetention, Database, DatabaseCommit, State}; +use reth_revm::state_change::{apply_beacon_root_contract_call, apply_blockhashes_update}; +use revm::{Database, DatabaseCommit}; use revm_primitives::EnvWithHandlerCfg; -use tokio::sync::Mutex; use crate::{ - eth::{ - api::{LoadPendingBlock, SpawnBlocking}, - error::{EthApiError, EthResult}, - }, + eth::error::{EthApiError, EthResult}, EthApi, }; -impl LoadPendingBlock - for EthApi -where - Self: SpawnBlocking, - Provider: BlockReaderIdExt + EvmEnvProvider + ChainSpecProvider + StateProviderFactory, - Pool: TransactionPool, - EvmConfig: ConfigureEvm, -{ - #[inline] - fn provider( - &self, - ) -> impl BlockReaderIdExt + EvmEnvProvider + ChainSpecProvider + StateProviderFactory { - self.inner.provider() - } +/// Implements [`LoadPendingBlock`](crate::eth::api::LoadPendingBlock) for a type, that has similar +/// data layout to [`EthApi`]. +#[macro_export] +macro_rules! load_pending_block_impl { + ($network_api:ty) => { + impl $crate::eth::api::LoadPendingBlock for $network_api + where + Self: $crate::eth::api::SpawnBlocking, + Provider: reth_provider::BlockReaderIdExt + + reth_provider::EvmEnvProvider + + reth_provider::ChainSpecProvider + + reth_provider::StateProviderFactory, + Pool: reth_transaction_pool::TransactionPool, + EvmConfig: reth_evm::ConfigureEvm, + { + #[inline] + fn provider( + &self, + ) -> impl reth_provider::BlockReaderIdExt + + reth_provider::EvmEnvProvider + + reth_provider::ChainSpecProvider + + reth_provider::StateProviderFactory { + self.inner.provider() + } - #[inline] - fn pool(&self) -> impl TransactionPool { - self.inner.pool() - } + #[inline] + fn pool(&self) -> impl reth_transaction_pool::TransactionPool { + self.inner.pool() + } - #[inline] - fn pending_block(&self) -> &Mutex> { - self.inner.pending_block() - } + #[inline] + fn pending_block(&self) -> &tokio::sync::Mutex> { + self.inner.pending_block() + } - #[inline] - fn evm_config(&self) -> &impl ConfigureEvm { - self.inner.evm_config() - } + #[inline] + fn evm_config(&self) -> &impl reth_evm::ConfigureEvm { + self.inner.evm_config() + } + } + }; } +load_pending_block_impl!(EthApi); + /// Configured [`BlockEnv`] and [`CfgEnvWithHandlerCfg`] for a pending block #[derive(Debug, Clone, Constructor)] pub struct PendingBlockEnv { @@ -80,263 +73,6 @@ pub struct PendingBlockEnv { pub origin: PendingBlockEnvOrigin, } -impl PendingBlockEnv { - /// Builds a pending block using the given client and pool. - /// - /// If the origin is the actual pending block, the block is built with withdrawals. - /// - /// After Cancun, if the origin is the actual pending block, the block includes the EIP-4788 pre - /// block contract call using the parent beacon block root received from the CL. - pub fn build_block( - self, - client: &Client, - pool: &Pool, - ) -> EthResult - where - Client: StateProviderFactory + ChainSpecProvider, - Pool: TransactionPool, - { - let Self { cfg, block_env, origin } = self; - - let parent_hash = origin.build_target_hash(); - let state_provider = client.history_by_block_hash(parent_hash)?; - let state = StateProviderDatabase::new(state_provider); - let mut db = State::builder().with_database(state).with_bundle_update().build(); - - let mut cumulative_gas_used = 0; - let mut sum_blob_gas_used = 0; - let block_gas_limit: u64 = block_env.gas_limit.to::(); - let base_fee = block_env.basefee.to::(); - let block_number = block_env.number.to::(); - - let mut executed_txs = Vec::new(); - let mut senders = Vec::new(); - let mut best_txs = pool.best_transactions_with_attributes(BestTransactionsAttributes::new( - base_fee, - block_env.get_blob_gasprice().map(|gasprice| gasprice as u64), - )); - - let (withdrawals, withdrawals_root) = match origin { - PendingBlockEnvOrigin::ActualPending(ref block) => { - (block.withdrawals.clone(), block.withdrawals_root) - } - PendingBlockEnvOrigin::DerivedFromLatest(_) => (None, None), - }; - - let chain_spec = client.chain_spec(); - - let parent_beacon_block_root = if origin.is_actual_pending() { - // apply eip-4788 pre block contract call if we got the block from the CL with the real - // parent beacon block root - pre_block_beacon_root_contract_call( - &mut db, - chain_spec.as_ref(), - block_number, - &cfg, - &block_env, - origin.header().parent_beacon_block_root, - )?; - origin.header().parent_beacon_block_root - } else { - None - }; - pre_block_blockhashes_update( - &mut db, - chain_spec.as_ref(), - &block_env, - block_number, - parent_hash, - )?; - - let mut receipts = Vec::new(); - - while let Some(pool_tx) = best_txs.next() { - // ensure we still have capacity for this transaction - if cumulative_gas_used + pool_tx.gas_limit() > block_gas_limit { - // we can't fit this transaction into the block, so we need to mark it as invalid - // which also removes all dependent transaction from the iterator before we can - // continue - best_txs.mark_invalid(&pool_tx); - continue - } - - if pool_tx.origin.is_private() { - // we don't want to leak any state changes made by private transactions, so we mark - // them as invalid here which removes all dependent transactions from the iterator - // before we can continue - best_txs.mark_invalid(&pool_tx); - continue - } - - // convert tx to a signed transaction - let tx = pool_tx.to_recovered_transaction(); - - // There's only limited amount of blob space available per block, so we need to check if - // the EIP-4844 can still fit in the block - if let Some(blob_tx) = tx.transaction.as_eip4844() { - let tx_blob_gas = blob_tx.blob_gas(); - if sum_blob_gas_used + tx_blob_gas > MAX_DATA_GAS_PER_BLOCK { - // we can't fit this _blob_ transaction into the block, so we mark it as - // invalid, which removes its dependent transactions from - // the iterator. This is similar to the gas limit condition - // for regular transactions above. - best_txs.mark_invalid(&pool_tx); - continue - } - } - - // Configure the environment for the block. - let env = - Env::boxed(cfg.cfg_env.clone(), block_env.clone(), tx_env_with_recovered(&tx)); - - let mut evm = revm::Evm::builder().with_env(env).with_db(&mut db).build(); - - let ResultAndState { result, state } = match evm.transact() { - Ok(res) => res, - Err(err) => { - match err { - EVMError::Transaction(err) => { - if matches!(err, InvalidTransaction::NonceTooLow { .. }) { - // if the nonce is too low, we can skip this transaction - } else { - // if the transaction is invalid, we can skip it and all of its - // descendants - best_txs.mark_invalid(&pool_tx); - } - continue - } - err => { - // this is an error that we should treat as fatal for this attempt - return Err(err.into()) - } - } - } - }; - // drop evm to release db reference. - drop(evm); - // commit changes - db.commit(state); - - // add to the total blob gas used if the transaction successfully executed - if let Some(blob_tx) = tx.transaction.as_eip4844() { - let tx_blob_gas = blob_tx.blob_gas(); - sum_blob_gas_used += tx_blob_gas; - - // if we've reached the max data gas per block, we can skip blob txs entirely - if sum_blob_gas_used == MAX_DATA_GAS_PER_BLOCK { - best_txs.skip_blobs(); - } - } - - let gas_used = result.gas_used(); - - // add gas used by the transaction to cumulative gas used, before creating the receipt - cumulative_gas_used += gas_used; - - // Push transaction changeset and calculate header bloom filter for receipt. - receipts.push(Some(Receipt { - tx_type: tx.tx_type(), - success: result.is_success(), - cumulative_gas_used, - logs: result.into_logs().into_iter().map(Into::into).collect(), - #[cfg(feature = "optimism")] - deposit_nonce: None, - #[cfg(feature = "optimism")] - deposit_receipt_version: None, - })); - - // append transaction to the list of executed transactions - let (tx, sender) = tx.to_components(); - executed_txs.push(tx); - senders.push(sender); - } - - // executes the withdrawals and commits them to the Database and BundleState. - let balance_increments = post_block_withdrawals_balance_increments( - &chain_spec, - block_env.timestamp.try_into().unwrap_or(u64::MAX), - &withdrawals.clone().unwrap_or_default(), - ); - - // increment account balances for withdrawals - db.increment_balances(balance_increments)?; - - // merge all transitions into bundle state. - db.merge_transitions(BundleRetention::PlainState); - - let execution_outcome = ExecutionOutcome::new( - db.take_bundle(), - vec![receipts].into(), - block_number, - Vec::new(), - ); - - #[cfg(feature = "optimism")] - let receipts_root = execution_outcome - .optimism_receipts_root_slow( - block_number, - chain_spec.as_ref(), - block_env.timestamp.to::(), - ) - .expect("Block is present"); - - #[cfg(not(feature = "optimism"))] - let receipts_root = - execution_outcome.receipts_root_slow(block_number).expect("Block is present"); - - let logs_bloom = - execution_outcome.block_logs_bloom(block_number).expect("Block is present"); - - // calculate the state root - let state_provider = &db.database; - let state_root = state_provider.state_root(execution_outcome.state())?; - - // create the block header - let transactions_root = proofs::calculate_transaction_root(&executed_txs); - - // check if cancun is activated to set eip4844 header fields correctly - let blob_gas_used = - if cfg.handler_cfg.spec_id >= SpecId::CANCUN { Some(sum_blob_gas_used) } else { None }; - - // note(onbjerg): the rpc spec has not been changed to include requests, so for now we just - // set these to empty - let (requests, requests_root) = - if chain_spec.is_prague_active_at_timestamp(block_env.timestamp.to::()) { - (Some(Requests::default()), Some(EMPTY_ROOT_HASH)) - } else { - (None, None) - }; - - let header = Header { - parent_hash, - ommers_hash: EMPTY_OMMER_ROOT_HASH, - beneficiary: block_env.coinbase, - state_root, - transactions_root, - receipts_root, - withdrawals_root, - logs_bloom, - timestamp: block_env.timestamp.to::(), - mix_hash: block_env.prevrandao.unwrap_or_default(), - nonce: BEACON_NONCE, - base_fee_per_gas: Some(base_fee), - number: block_number, - gas_limit: block_gas_limit, - difficulty: U256::ZERO, - gas_used: cumulative_gas_used, - blob_gas_used, - excess_blob_gas: block_env.get_blob_excess_gas(), - extra_data: Default::default(), - parent_beacon_block_root, - requests_root, - }; - - // seal the block - let block = Block { header, body: executed_txs, ommers: vec![], withdrawals, requests }; - Ok(SealedBlockWithSenders { block: block.seal_slow(), senders }) - } -} - /// Apply the [EIP-4788](https://eips.ethereum.org/EIPS/eip-4788) pre block contract call. /// /// This constructs a new [Evm](revm::Evm) with the given DB, and environment @@ -344,7 +80,7 @@ impl PendingBlockEnv { /// /// This uses [`apply_beacon_root_contract_call`] to ultimately apply the beacon root contract state /// change. -fn pre_block_beacon_root_contract_call( +pub fn pre_block_beacon_root_contract_call( db: &mut DB, chain_spec: &ChainSpec, block_number: u64, @@ -382,7 +118,7 @@ where /// [`CfgEnvWithHandlerCfg`] and [`BlockEnv`]. /// /// This uses [`apply_blockhashes_update`]. -fn pre_block_blockhashes_update + DatabaseCommit>( +pub fn pre_block_blockhashes_update + DatabaseCommit>( db: &mut DB, chain_spec: &ChainSpec, initialized_block_env: &BlockEnv, @@ -446,7 +182,7 @@ impl PendingBlockEnvOrigin { /// For the [`PendingBlockEnvOrigin::ActualPending`] this is the parent hash of the block. /// For the [`PendingBlockEnvOrigin::DerivedFromLatest`] this is the hash of the _latest_ /// header. - fn build_target_hash(&self) -> B256 { + pub fn build_target_hash(&self) -> B256 { match self { Self::ActualPending(block) => block.parent_hash, Self::DerivedFromLatest(header) => header.hash(), diff --git a/crates/rpc/rpc/src/eth/api/state.rs b/crates/rpc/rpc/src/eth/api/state.rs index 34c63d92854..49df9989490 100644 --- a/crates/rpc/rpc/src/eth/api/state.rs +++ b/crates/rpc/rpc/src/eth/api/state.rs @@ -1,46 +1,56 @@ //! Contains RPC handler implementations specific to state. -use crate::{ - eth::{ - api::{EthState, LoadState, SpawnBlocking}, - cache::EthStateCache, - }, - EthApi, -}; -use reth_provider::StateProviderFactory; -use reth_transaction_pool::TransactionPool; +use crate::EthApi; -impl EthState for EthApi where - Self: LoadState + SpawnBlocking -{ +/// Implements [`EthState`](crate::eth::api::EthState) for a type, that has similar +/// data layout to [`EthApi`]. +#[macro_export] +macro_rules! eth_state_impl { + ($network_api:ty) => { + impl $crate::eth::api::EthState for $network_api where + Self: $crate::eth::api::LoadState + $crate::eth::api::SpawnBlocking + { + } + }; } -impl LoadState for EthApi -where - Provider: StateProviderFactory, - Pool: TransactionPool, -{ - #[inline] - fn provider(&self) -> impl StateProviderFactory { - &self.inner.provider - } +/// Implements [`LoadState`](crate::eth::api::LoadState) for a type, that has similar +/// data layout to [`EthApi`]. +#[macro_export] +macro_rules! load_state_impl { + ($network_api:ty) => { + impl $crate::eth::api::LoadState for $network_api + where + Provider: reth_provider::StateProviderFactory, + Pool: reth_transaction_pool::TransactionPool, + { + #[inline] + fn provider(&self) -> impl reth_provider::StateProviderFactory { + self.inner.provider() + } - #[inline] - fn cache(&self) -> &EthStateCache { - self.inner.cache() - } + #[inline] + fn cache(&self) -> &$crate::eth::cache::EthStateCache { + self.inner.cache() + } - #[inline] - fn pool(&self) -> impl TransactionPool { - self.inner.pool() - } + #[inline] + fn pool(&self) -> impl reth_transaction_pool::TransactionPool { + self.inner.pool() + } + } + }; } +eth_state_impl!(EthApi); +load_state_impl!(EthApi); + #[cfg(test)] mod tests { use super::*; use crate::eth::{ - cache::EthStateCache, gas_oracle::GasPriceOracle, FeeHistoryCache, FeeHistoryCacheConfig, + api::EthState, cache::EthStateCache, gas_oracle::GasPriceOracle, FeeHistoryCache, + FeeHistoryCacheConfig, }; use reth_evm_ethereum::EthEvmConfig; use reth_primitives::{ diff --git a/crates/rpc/rpc/src/eth/api/trace.rs b/crates/rpc/rpc/src/eth/api/trace.rs index 9f336223eea..9d19d85d5e3 100644 --- a/crates/rpc/rpc/src/eth/api/trace.rs +++ b/crates/rpc/rpc/src/eth/api/trace.rs @@ -1,19 +1,23 @@ //! Contains RPC handler implementations specific to tracing. -use reth_evm::ConfigureEvm; +use crate::EthApi; -use crate::{ - eth::api::{LoadState, Trace}, - EthApi, -}; - -impl Trace for EthApi -where - Self: LoadState, - EvmConfig: ConfigureEvm, -{ - #[inline] - fn evm_config(&self) -> &impl ConfigureEvm { - self.inner.evm_config() - } +/// Implements [`Trace`](crate::eth::api::Trace) for a type, that has similar +/// data layout to [`EthApi`]. +#[macro_export] +macro_rules! trace_impl { + ($network_api:ty) => { + impl $crate::eth::api::Trace for $network_api + where + Self: $crate::eth::api::LoadState, + EvmConfig: reth_evm::ConfigureEvm, + { + #[inline] + fn evm_config(&self) -> &impl reth_evm::ConfigureEvm { + self.inner.evm_config() + } + } + }; } + +trace_impl!(EthApi); diff --git a/crates/rpc/rpc/src/eth/api/traits/pending_block.rs b/crates/rpc/rpc/src/eth/api/traits/pending_block.rs index 8ab068e1a9d..aa9fe28fb85 100644 --- a/crates/rpc/rpc/src/eth/api/traits/pending_block.rs +++ b/crates/rpc/rpc/src/eth/api/traits/pending_block.rs @@ -5,18 +5,36 @@ use std::time::{Duration, Instant}; use futures::Future; use reth_evm::ConfigureEvm; -use reth_primitives::{SealedBlockWithSenders, SealedHeader}; +use reth_primitives::{ + constants::{eip4844::MAX_DATA_GAS_PER_BLOCK, BEACON_NONCE, EMPTY_ROOT_HASH}, + proofs::calculate_transaction_root, + revm::env::tx_env_with_recovered, + revm_primitives::{ + BlockEnv, CfgEnv, CfgEnvWithHandlerCfg, EVMError, Env, ExecutionResult, InvalidTransaction, + ResultAndState, SpecId, + }, + Block, BlockNumber, Header, IntoRecoveredTransaction, Receipt, Requests, + SealedBlockWithSenders, SealedHeader, TransactionSignedEcRecovered, B256, + EMPTY_OMMER_ROOT_HASH, U256, +}; use reth_provider::{ - BlockReader, BlockReaderIdExt, ChainSpecProvider, EvmEnvProvider, StateProviderFactory, + BlockReader, BlockReaderIdExt, ChainSpecProvider, EvmEnvProvider, ExecutionOutcome, + StateProviderFactory, }; -use reth_transaction_pool::TransactionPool; -use revm_primitives::{BlockEnv, CfgEnv, CfgEnvWithHandlerCfg, SpecId}; +use reth_revm::{ + database::StateProviderDatabase, state_change::post_block_withdrawals_balance_increments, +}; +use reth_transaction_pool::{BestTransactionsAttributes, TransactionPool}; +use revm::{db::states::bundle_state::BundleRetention, DatabaseCommit, State}; use tokio::sync::Mutex; use tracing::debug; use crate::eth::{ api::{ - pending_block::{PendingBlock, PendingBlockEnv, PendingBlockEnvOrigin}, + pending_block::{ + pre_block_beacon_root_contract_call, pre_block_blockhashes_update, PendingBlock, + PendingBlockEnv, PendingBlockEnvOrigin, + }, SpawnBlocking, }, error::{EthApiError, EthResult}, @@ -131,7 +149,7 @@ pub trait LoadPendingBlock { let pending_block = match self .spawn_blocking_io(move |this| { // we rebuild the block - pending.build_block(&this.provider(), &this.pool()) + this.build_block(pending) }) .await { @@ -148,4 +166,263 @@ pub trait LoadPendingBlock { Ok(Some(pending_block)) } } + + /// Assembles a [`Receipt`] for a transaction, based on its [`ExecutionResult`]. + fn assemble_receipt( + &self, + tx: &TransactionSignedEcRecovered, + result: ExecutionResult, + cumulative_gas_used: u64, + ) -> Receipt { + Receipt { + tx_type: tx.tx_type(), + success: result.is_success(), + cumulative_gas_used, + logs: result.into_logs().into_iter().map(Into::into).collect(), + #[cfg(feature = "optimism")] + deposit_nonce: None, + #[cfg(feature = "optimism")] + deposit_receipt_version: None, + } + } + + /// Calculates receipts root in block building. + /// + /// Panics if block is not in the [`ExecutionOutcome`]'s block range. + fn receipts_root( + &self, + _block_env: &BlockEnv, + execution_outcome: &ExecutionOutcome, + block_number: BlockNumber, + ) -> B256 { + execution_outcome.receipts_root_slow(block_number).expect("Block is present") + } + + /// Builds a pending block using the configured provider and pool. + /// + /// If the origin is the actual pending block, the block is built with withdrawals. + /// + /// After Cancun, if the origin is the actual pending block, the block includes the EIP-4788 pre + /// block contract call using the parent beacon block root received from the CL. + fn build_block(&self, env: PendingBlockEnv) -> EthResult { + let PendingBlockEnv { cfg, block_env, origin } = env; + + let parent_hash = origin.build_target_hash(); + let state_provider = self.provider().history_by_block_hash(parent_hash)?; + let state = StateProviderDatabase::new(state_provider); + let mut db = State::builder().with_database(state).with_bundle_update().build(); + + let mut cumulative_gas_used = 0; + let mut sum_blob_gas_used = 0; + let block_gas_limit: u64 = block_env.gas_limit.to::(); + let base_fee = block_env.basefee.to::(); + let block_number = block_env.number.to::(); + + let mut executed_txs = Vec::new(); + let mut senders = Vec::new(); + let mut best_txs = + self.pool().best_transactions_with_attributes(BestTransactionsAttributes::new( + base_fee, + block_env.get_blob_gasprice().map(|gasprice| gasprice as u64), + )); + + let (withdrawals, withdrawals_root) = match origin { + PendingBlockEnvOrigin::ActualPending(ref block) => { + (block.withdrawals.clone(), block.withdrawals_root) + } + PendingBlockEnvOrigin::DerivedFromLatest(_) => (None, None), + }; + + let chain_spec = self.provider().chain_spec(); + + let parent_beacon_block_root = if origin.is_actual_pending() { + // apply eip-4788 pre block contract call if we got the block from the CL with the real + // parent beacon block root + pre_block_beacon_root_contract_call( + &mut db, + chain_spec.as_ref(), + block_number, + &cfg, + &block_env, + origin.header().parent_beacon_block_root, + )?; + origin.header().parent_beacon_block_root + } else { + None + }; + pre_block_blockhashes_update( + &mut db, + chain_spec.as_ref(), + &block_env, + block_number, + parent_hash, + )?; + + let mut receipts = Vec::new(); + + while let Some(pool_tx) = best_txs.next() { + // ensure we still have capacity for this transaction + if cumulative_gas_used + pool_tx.gas_limit() > block_gas_limit { + // we can't fit this transaction into the block, so we need to mark it as invalid + // which also removes all dependent transaction from the iterator before we can + // continue + best_txs.mark_invalid(&pool_tx); + continue + } + + if pool_tx.origin.is_private() { + // we don't want to leak any state changes made by private transactions, so we mark + // them as invalid here which removes all dependent transactions from the iterator + // before we can continue + best_txs.mark_invalid(&pool_tx); + continue + } + + // convert tx to a signed transaction + let tx = pool_tx.to_recovered_transaction(); + + // There's only limited amount of blob space available per block, so we need to check if + // the EIP-4844 can still fit in the block + if let Some(blob_tx) = tx.transaction.as_eip4844() { + let tx_blob_gas = blob_tx.blob_gas(); + if sum_blob_gas_used + tx_blob_gas > MAX_DATA_GAS_PER_BLOCK { + // we can't fit this _blob_ transaction into the block, so we mark it as + // invalid, which removes its dependent transactions from + // the iterator. This is similar to the gas limit condition + // for regular transactions above. + best_txs.mark_invalid(&pool_tx); + continue + } + } + + // Configure the environment for the block. + let env = + Env::boxed(cfg.cfg_env.clone(), block_env.clone(), tx_env_with_recovered(&tx)); + + let mut evm = revm::Evm::builder().with_env(env).with_db(&mut db).build(); + + let ResultAndState { result, state } = match evm.transact() { + Ok(res) => res, + Err(err) => { + match err { + EVMError::Transaction(err) => { + if matches!(err, InvalidTransaction::NonceTooLow { .. }) { + // if the nonce is too low, we can skip this transaction + } else { + // if the transaction is invalid, we can skip it and all of its + // descendants + best_txs.mark_invalid(&pool_tx); + } + continue + } + err => { + // this is an error that we should treat as fatal for this attempt + return Err(err.into()) + } + } + } + }; + // drop evm to release db reference. + drop(evm); + // commit changes + db.commit(state); + + // add to the total blob gas used if the transaction successfully executed + if let Some(blob_tx) = tx.transaction.as_eip4844() { + let tx_blob_gas = blob_tx.blob_gas(); + sum_blob_gas_used += tx_blob_gas; + + // if we've reached the max data gas per block, we can skip blob txs entirely + if sum_blob_gas_used == MAX_DATA_GAS_PER_BLOCK { + best_txs.skip_blobs(); + } + } + + let gas_used = result.gas_used(); + + // add gas used by the transaction to cumulative gas used, before creating the receipt + cumulative_gas_used += gas_used; + + // Push transaction changeset and calculate header bloom filter for receipt. + receipts.push(Some(self.assemble_receipt(&tx, result, cumulative_gas_used))); + + // append transaction to the list of executed transactions + let (tx, sender) = tx.to_components(); + executed_txs.push(tx); + senders.push(sender); + } + + // executes the withdrawals and commits them to the Database and BundleState. + let balance_increments = post_block_withdrawals_balance_increments( + &chain_spec, + block_env.timestamp.try_into().unwrap_or(u64::MAX), + &withdrawals.clone().unwrap_or_default(), + ); + + // increment account balances for withdrawals + db.increment_balances(balance_increments)?; + + // merge all transitions into bundle state. + db.merge_transitions(BundleRetention::PlainState); + + let execution_outcome = ExecutionOutcome::new( + db.take_bundle(), + vec![receipts].into(), + block_number, + Vec::new(), + ); + + let receipts_root = self.receipts_root(&block_env, &execution_outcome, block_number); + + let logs_bloom = + execution_outcome.block_logs_bloom(block_number).expect("Block is present"); + + // calculate the state root + let state_provider = &db.database; + let state_root = state_provider.state_root(execution_outcome.state())?; + + // create the block header + let transactions_root = calculate_transaction_root(&executed_txs); + + // check if cancun is activated to set eip4844 header fields correctly + let blob_gas_used = + if cfg.handler_cfg.spec_id >= SpecId::CANCUN { Some(sum_blob_gas_used) } else { None }; + + // note(onbjerg): the rpc spec has not been changed to include requests, so for now we just + // set these to empty + let (requests, requests_root) = + if chain_spec.is_prague_active_at_timestamp(block_env.timestamp.to::()) { + (Some(Requests::default()), Some(EMPTY_ROOT_HASH)) + } else { + (None, None) + }; + + let header = Header { + parent_hash, + ommers_hash: EMPTY_OMMER_ROOT_HASH, + beneficiary: block_env.coinbase, + state_root, + transactions_root, + receipts_root, + withdrawals_root, + logs_bloom, + timestamp: block_env.timestamp.to::(), + mix_hash: block_env.prevrandao.unwrap_or_default(), + nonce: BEACON_NONCE, + base_fee_per_gas: Some(base_fee), + number: block_number, + gas_limit: block_gas_limit, + difficulty: U256::ZERO, + gas_used: cumulative_gas_used, + blob_gas_used, + excess_blob_gas: block_env.get_blob_excess_gas(), + extra_data: Default::default(), + parent_beacon_block_root, + requests_root, + }; + + // seal the block + let block = Block { header, body: executed_txs, ommers: vec![], withdrawals, requests }; + Ok(SealedBlockWithSenders { block: block.seal_slow(), senders }) + } } diff --git a/crates/rpc/rpc/src/eth/api/transactions.rs b/crates/rpc/rpc/src/eth/api/transactions.rs index f848eb7a489..a07911494a0 100644 --- a/crates/rpc/rpc/src/eth/api/transactions.rs +++ b/crates/rpc/rpc/src/eth/api/transactions.rs @@ -1,71 +1,76 @@ //! Contains RPC handler implementations specific to transactions -use std::sync::Arc; - use reth_primitives::{TransactionSignedEcRecovered, B256}; -use reth_provider::{BlockReaderIdExt, TransactionsProvider}; use reth_rpc_types::{Transaction, TransactionInfo}; use reth_rpc_types_compat::transaction::from_recovered_with_block_context; -use reth_transaction_pool::TransactionPool; - -use crate::{ - eth::{ - api::{EthTransactions, LoadTransaction, RawTransactionForwarder, SpawnBlocking}, - cache::EthStateCache, - revm_utils::FillableTransaction, - signer::EthSigner, - }, - EthApi, -}; - -impl EthTransactions - for EthApi -where - Self: LoadTransaction, - Pool: TransactionPool + 'static, - Provider: BlockReaderIdExt, -{ - #[inline] - fn provider(&self) -> impl BlockReaderIdExt { - self.inner.provider() - } - #[inline] - fn raw_tx_forwarder(&self) -> Option> { - self.inner.raw_tx_forwarder() - } +use crate::{eth::revm_utils::FillableTransaction, EthApi}; + +/// Implements [`EthTransactions`](crate::eth::api::EthTransactions) for a type, that has similar +/// data layout to [`EthApi`]. +#[macro_export] +macro_rules! eth_transactions_impl { + ($network_api:ty) => { + impl $crate::eth::api::EthTransactions for $network_api + where + Self: $crate::eth::api::LoadTransaction, + Pool: reth_transaction_pool::TransactionPool + 'static, + Provider: reth_provider::BlockReaderIdExt, + { + #[inline] + fn provider(&self) -> impl reth_provider::BlockReaderIdExt { + self.inner.provider() + } - #[inline] - fn signers(&self) -> &parking_lot::RwLock>> { - self.inner.signers() - } + #[inline] + fn raw_tx_forwarder( + &self, + ) -> Option> { + self.inner.raw_tx_forwarder() + } + + #[inline] + fn signers(&self) -> &parking_lot::RwLock>> { + self.inner.signers() + } + } + }; } -impl LoadTransaction - for EthApi -where - Self: SpawnBlocking, - Provider: TransactionsProvider, - Pool: TransactionPool, -{ - type Pool = Pool; - - #[inline] - fn provider(&self) -> impl TransactionsProvider { - self.inner.provider() - } +/// Implements [`LoadTransaction`](crate::eth::api::LoadTransaction) for a type, that has similar +/// data layout to [`EthApi`]. +#[macro_export] +macro_rules! load_transaction_impl { + ($network_api:ty) => { + impl $crate::eth::api::LoadTransaction for $network_api + where + Self: $crate::eth::api::SpawnBlocking, + Provider: reth_provider::TransactionsProvider, + Pool: reth_transaction_pool::TransactionPool, + { + type Pool = Pool; + + #[inline] + fn provider(&self) -> impl reth_provider::TransactionsProvider { + self.inner.provider() + } - #[inline] - fn cache(&self) -> &EthStateCache { - self.inner.cache() - } + #[inline] + fn cache(&self) -> &$crate::eth::cache::EthStateCache { + self.inner.cache() + } - #[inline] - fn pool(&self) -> &Self::Pool { - self.inner.pool() - } + #[inline] + fn pool(&self) -> &Self::Pool { + self.inner.pool() + } + } + }; } +eth_transactions_impl!(EthApi); +load_transaction_impl!(EthApi); + /// Represents from where a transaction was fetched. #[derive(Debug, Clone, Eq, PartialEq)] pub enum TransactionSource { @@ -159,14 +164,15 @@ impl From for Transaction { mod tests { use super::*; use crate::eth::{ - cache::EthStateCache, gas_oracle::GasPriceOracle, FeeHistoryCache, FeeHistoryCacheConfig, + api::EthTransactions, cache::EthStateCache, gas_oracle::GasPriceOracle, FeeHistoryCache, + FeeHistoryCacheConfig, }; use reth_evm_ethereum::EthEvmConfig; use reth_network_api::noop::NoopNetwork; use reth_primitives::{constants::ETHEREUM_BLOCK_GAS_LIMIT, hex_literal::hex, Bytes}; use reth_provider::test_utils::NoopProvider; use reth_tasks::pool::BlockingTaskPool; - use reth_transaction_pool::test_utils::testing_pool; + use reth_transaction_pool::{test_utils::testing_pool, TransactionPool}; #[tokio::test] async fn send_raw_transaction() { diff --git a/crates/rpc/rpc/src/eth/mod.rs b/crates/rpc/rpc/src/eth/mod.rs index d596ee2a389..79a8028a258 100644 --- a/crates/rpc/rpc/src/eth/mod.rs +++ b/crates/rpc/rpc/src/eth/mod.rs @@ -15,10 +15,11 @@ pub(crate) mod utils; pub use api::{ fee_history::{fee_history_cache_new_blocks_task, FeeHistoryCache, FeeHistoryCacheConfig}, - EthApi, EthApiSpec, TransactionSource, RPC_DEFAULT_GAS_CAP, + EthApi, EthApiSpec, PendingBlock, TransactionSource, RPC_DEFAULT_GAS_CAP, }; pub use bundle::EthBundle; pub use filter::{EthFilter, EthFilterConfig}; pub use id_provider::EthSubscriptionIdProvider; pub use pubsub::EthPubSub; +pub use signer::EthSigner;