Skip to content

Commit

Permalink
Pending receipt (#10597)
Browse files Browse the repository at this point in the history
Co-authored-by: Emilia Hane <[email protected]>
Co-authored-by: Federico Gimenez <[email protected]>
Co-authored-by: Matthias Seitz <[email protected]>
  • Loading branch information
4 people authored Aug 28, 2024
1 parent 29b02cc commit 3be92d5
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 29 deletions.
25 changes: 18 additions & 7 deletions crates/optimism/rpc/src/eth/pending_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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(
Expand Down
21 changes: 17 additions & 4 deletions crates/rpc/rpc-eth-api/src/helpers/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
43 changes: 31 additions & 12 deletions crates/rpc/rpc-eth-api/src/helpers/pending_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand All @@ -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,
Expand All @@ -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.
Expand Down Expand Up @@ -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();
Expand All @@ -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)
Expand All @@ -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)))
}
}

Expand Down Expand Up @@ -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>,
{
Expand Down Expand Up @@ -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(),
);
Expand Down Expand Up @@ -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))
}
}
16 changes: 10 additions & 6 deletions crates/rpc/rpc-eth-types/src/pending_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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>,
}

0 comments on commit 3be92d5

Please sign in to comment.