diff --git a/crates/optimism/rpc/src/eth/pending_block.rs b/crates/optimism/rpc/src/eth/pending_block.rs index 4474c8ef4d93..513e04da95b6 100644 --- a/crates/optimism/rpc/src/eth/pending_block.rs +++ b/crates/optimism/rpc/src/eth/pending_block.rs @@ -4,11 +4,11 @@ use reth_chainspec::ChainSpec; use reth_evm::ConfigureEvm; use reth_node_api::FullNodeComponents; use reth_primitives::{ - revm_primitives::BlockEnv, BlockHashOrNumber, BlockNumber, SealedBlockWithSenders, B256, + revm_primitives::BlockEnv, BlockNumber, Receipt, SealedBlockWithSenders, B256, }; use reth_provider::{ BlockReader, BlockReaderIdExt, ChainSpecProvider, EvmEnvProvider, ExecutionOutcome, - StateProviderFactory, + ReceiptProvider, StateProviderFactory, }; use reth_rpc_eth_api::{ helpers::{LoadPendingBlock, SpawnBlocking}, @@ -50,17 +50,28 @@ where } /// Returns the locally built pending block - async fn local_pending_block(&self) -> Result, Self::Error> { + async fn local_pending_block( + &self, + ) -> Result)>, Self::Error> { // See: let latest = self .provider() .latest_header() .map_err(Self::Error::from_eth_err)? .ok_or_else(|| EthApiError::UnknownBlockNumber)?; - let (_, block_hash) = latest.split(); - self.provider() - .sealed_block_with_senders(BlockHashOrNumber::from(block_hash), Default::default()) - .map_err(Self::Error::from_eth_err) + let block = self + .provider() + .block_with_senders(latest.hash().into(), Default::default()) + .map_err(Self::Error::from_eth_err)? + .ok_or_else(|| EthApiError::UnknownBlockNumber)? + .seal(latest.hash()); + + let receipts = self + .provider() + .receipts_by_block(block.hash().into()) + .map_err(Self::Error::from_eth_err)? + .ok_or_else(|| EthApiError::UnknownBlockNumber)?; + Ok(Some((block, receipts))) } fn receipts_root( diff --git a/crates/rpc/rpc-eth-api/src/helpers/block.rs b/crates/rpc/rpc-eth-api/src/helpers/block.rs index 42745a9a631a..1fc23d1facc3 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/block.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/block.rs @@ -145,10 +145,19 @@ pub trait EthBlocks: LoadBlock { { async move { if block_id.is_pending() { - return Ok(LoadBlock::provider(self) + // First, try to get the pending block from the provider, in case we already + // received the actual pending block from the CL. + if let Some((block, receipts)) = LoadBlock::provider(self) .pending_block_and_receipts() .map_err(Self::Error::from_eth_err)? - .map(|(sb, receipts)| (sb, Arc::new(receipts)))) + { + return Ok(Some((block, Arc::new(receipts)))); + } + + // If no pending block from provider, build the pending block locally. + if let Some((block, receipts)) = self.local_pending_block().await? { + return Ok(Some((block.block, Arc::new(receipts)))); + } } if let Some(block_hash) = LoadBlock::provider(self) @@ -243,8 +252,12 @@ pub trait LoadBlock: LoadPendingBlock + SpawnBlocking { return if maybe_pending.is_some() { Ok(maybe_pending) } else { - self.local_pending_block().await - } + // If no pending block from provider, try to get local pending block + return match self.local_pending_block().await? { + Some((block, _)) => Ok(Some(block)), + None => Ok(None), + }; + }; } let block_hash = match LoadPendingBlock::provider(self) diff --git a/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs b/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs index 876b5b776317..165d27126668 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs @@ -3,6 +3,7 @@ use std::time::{Duration, Instant}; +use crate::{EthApiTypes, FromEthApiError, FromEvmError}; use futures::Future; use reth_chainspec::{ChainSpec, EthereumHardforks}; use reth_evm::{ @@ -23,7 +24,7 @@ use reth_primitives::{ }; use reth_provider::{ BlockReader, BlockReaderIdExt, ChainSpecProvider, EvmEnvProvider, ProviderError, - StateProviderFactory, + ReceiptProvider, StateProviderFactory, }; use reth_revm::{ database::StateProviderDatabase, state_change::post_block_withdrawals_balance_increments, @@ -34,8 +35,6 @@ use revm::{db::states::bundle_state::BundleRetention, DatabaseCommit, State}; use tokio::sync::Mutex; use tracing::debug; -use crate::{EthApiTypes, FromEthApiError, FromEvmError}; - use super::SpawnBlocking; /// Loads a pending block from database. @@ -125,16 +124,26 @@ pub trait LoadPendingBlock: EthApiTypes { /// Returns the locally built pending block fn local_pending_block( &self, - ) -> impl Future, Self::Error>> + Send + ) -> impl Future)>, Self::Error>> + Send where Self: SpawnBlocking, { async move { let pending = self.pending_block_env_and_cfg()?; if pending.origin.is_actual_pending() { - return Ok(pending.origin.into_actual_pending()) + if let Some(block) = pending.origin.clone().into_actual_pending() { + // we have the real pending block, so we should also have its receipts + if let Some(receipts) = self + .provider() + .receipts_by_block(block.hash().into()) + .map_err(Self::Error::from_eth_err)? + { + return Ok(Some((block, receipts))) + } + } } + // we couldn't find the real pending block, so we need to build it ourselves let mut lock = self.pending_block().lock().await; let now = Instant::now(); @@ -146,12 +155,12 @@ pub trait LoadPendingBlock: EthApiTypes { pending.origin.header().hash() == pending_block.block.parent_hash && now <= pending_block.expires_at { - return Ok(Some(pending_block.block.clone())) + return Ok(Some((pending_block.block.clone(), pending_block.receipts.clone()))) } } // no pending block from the CL yet, so we need to build it ourselves via txpool - let pending_block = match self + let (sealed_block, receipts) = match self .spawn_blocking_io(move |this| { // we rebuild the block this.build_block(pending) @@ -166,9 +175,13 @@ pub trait LoadPendingBlock: EthApiTypes { }; let now = Instant::now(); - *lock = Some(PendingBlock::new(pending_block.clone(), now + Duration::from_secs(1))); + *lock = Some(PendingBlock::new( + now + Duration::from_secs(1), + sealed_block.clone(), + receipts.clone(), + )); - Ok(Some(pending_block)) + Ok(Some((sealed_block, receipts))) } } @@ -207,7 +220,10 @@ pub trait LoadPendingBlock: EthApiTypes { /// /// 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) -> Result + fn build_block( + &self, + env: PendingBlockEnv, + ) -> Result<(SealedBlockWithSenders, Vec), Self::Error> where EthApiError: From, { @@ -382,7 +398,7 @@ pub trait LoadPendingBlock: EthApiTypes { let execution_outcome = ExecutionOutcome::new( db.take_bundle(), - vec![receipts].into(), + vec![receipts.clone()].into(), block_number, Vec::new(), ); @@ -438,8 +454,11 @@ pub trait LoadPendingBlock: EthApiTypes { requests_root, }; + // Convert Vec> to Vec + let receipts: Vec = receipts.into_iter().flatten().collect(); + // seal the block let block = Block { header, body: executed_txs, ommers: vec![], withdrawals, requests }; - Ok(SealedBlockWithSenders { block: block.seal_slow(), senders }) + Ok((SealedBlockWithSenders { block: block.seal_slow(), senders }, receipts)) } } diff --git a/crates/rpc/rpc-eth-types/src/pending_block.rs b/crates/rpc/rpc-eth-types/src/pending_block.rs index eaa6bb3d7ffc..8a9503d81e81 100644 --- a/crates/rpc/rpc-eth-types/src/pending_block.rs +++ b/crates/rpc/rpc-eth-types/src/pending_block.rs @@ -5,10 +5,12 @@ use std::time::Instant; use derive_more::Constructor; -use reth_primitives::{BlockId, BlockNumberOrTag, SealedBlockWithSenders, SealedHeader, B256}; +use reth_primitives::{ + BlockId, BlockNumberOrTag, Receipt, SealedBlockWithSenders, SealedHeader, B256, +}; use revm_primitives::{BlockEnv, CfgEnvWithHandlerCfg}; -/// Configured [`BlockEnv`] and [`CfgEnvWithHandlerCfg`] for a pending block +/// Configured [`BlockEnv`] and [`CfgEnvWithHandlerCfg`] for a pending block. #[derive(Debug, Clone, Constructor)] pub struct PendingBlockEnv { /// Configured [`CfgEnvWithHandlerCfg`] for the pending block. @@ -79,11 +81,13 @@ impl PendingBlockEnvOrigin { } } -/// In memory pending block for `pending` tag +/// Locally built pending block for `pending` tag. #[derive(Debug, Constructor)] pub struct PendingBlock { - /// The cached pending block - pub block: SealedBlockWithSenders, - /// Timestamp when the pending block is considered outdated + /// Timestamp when the pending block is considered outdated. pub expires_at: Instant, + /// The locally built pending block. + pub block: SealedBlockWithSenders, + /// The receipts for the pending block + pub receipts: Vec, }