Pending receipt (#10597)

Co-authored-by: Emilia Hane <emiliaha95@gmail.com>
Co-authored-by: Federico Gimenez <fgimenez@users.noreply.github.com>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
malik
2024-08-28 14:22:35 +01:00
committed by GitHub
parent 29b02cc0e1
commit 3be92d5049
4 changed files with 76 additions and 29 deletions

View File

@@ -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<Option<SealedBlockWithSenders>, Self::Error> {
async fn local_pending_block(
&self,
) -> Result<Option<(SealedBlockWithSenders, Vec<Receipt>)>, Self::Error> {
// See: <https://github.com/ethereum-optimism/op-geth/blob/f2e69450c6eec9c35d56af91389a1c47737206ca/miner/worker.go#L367-L375>
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(

View File

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

View File

@@ -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<Output = Result<Option<SealedBlockWithSenders>, Self::Error>> + Send
) -> impl Future<Output = Result<Option<(SealedBlockWithSenders, Vec<Receipt>)>, 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<SealedBlockWithSenders, Self::Error>
fn build_block(
&self,
env: PendingBlockEnv,
) -> Result<(SealedBlockWithSenders, Vec<Receipt>), Self::Error>
where
EthApiError: From<ProviderError>,
{
@@ -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<Option<Receipt>> to Vec<Receipt>
let receipts: Vec<Receipt> = 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))
}
}

View File

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