use crate::{
config::OpBuilderConfig,
error::OpPayloadBuilderError,
payload::{OpBuiltPayload, OpPayloadBuilderAttributes},
};
use alloy_consensus::{Header, Transaction, EMPTY_OMMER_ROOT_HASH};
use alloy_eips::{eip4895::Withdrawals, merge::BEACON_NONCE};
use alloy_primitives::{Address, Bytes, B256, U256};
use alloy_rpc_types_debug::ExecutionWitness;
use alloy_rpc_types_engine::PayloadId;
use op_alloy_consensus::DepositTransaction;
use op_alloy_rpc_types_engine::OpPayloadAttributes;
use reth_basic_payload_builder::*;
use reth_chain_state::ExecutedBlock;
use reth_chainspec::{ChainSpecProvider, EthereumHardforks};
use reth_evm::{env::EvmEnv, system_calls::SystemCaller, ConfigureEvm, NextBlockEnvAttributes};
use reth_execution_types::ExecutionOutcome;
use reth_optimism_chainspec::OpChainSpec;
use reth_optimism_consensus::calculate_receipt_root_no_memo_optimism;
use reth_optimism_forks::OpHardforks;
use reth_payload_builder_primitives::PayloadBuilderError;
use reth_payload_primitives::PayloadBuilderAttributes;
use reth_payload_util::PayloadTransactions;
use reth_primitives::{
proofs, transaction::SignedTransactionIntoRecoveredExt, Block, BlockBody, BlockExt, Receipt,
SealedHeader, TransactionSigned, TxType,
};
use reth_provider::{
HashedPostStateProvider, ProviderError, StateProofProvider, StateProviderFactory,
StateRootProvider,
};
use reth_revm::{database::StateProviderDatabase, witness::ExecutionWitnessRecord};
use reth_transaction_pool::{
noop::NoopTransactionPool, pool::BestPayloadTransactions, BestTransactionsAttributes,
PoolTransaction, TransactionPool,
};
use revm::{
db::{states::bundle_state::BundleRetention, State},
primitives::{
BlockEnv, CfgEnvWithHandlerCfg, EVMError, EnvWithHandlerCfg, InvalidTransaction,
ResultAndState, TxEnv,
},
Database, DatabaseCommit,
};
use std::{fmt::Display, sync::Arc};
use tracing::{debug, trace, warn};
#[derive(Debug, Clone)]
pub struct OpPayloadBuilder<EvmConfig, Txs = ()> {
pub compute_pending_block: bool,
pub evm_config: EvmConfig,
pub config: OpBuilderConfig,
pub best_transactions: Txs,
}
impl<EvmConfig> OpPayloadBuilder<EvmConfig> {
pub fn new(evm_config: EvmConfig) -> Self {
Self::with_builder_config(evm_config, Default::default())
}
pub const fn with_builder_config(evm_config: EvmConfig, config: OpBuilderConfig) -> Self {
Self { compute_pending_block: true, evm_config, config, best_transactions: () }
}
}
impl<EvmConfig, Txs> OpPayloadBuilder<EvmConfig, Txs> {
pub const fn set_compute_pending_block(mut self, compute_pending_block: bool) -> Self {
self.compute_pending_block = compute_pending_block;
self
}
pub fn with_transactions<T: OpPayloadTransactions>(
self,
best_transactions: T,
) -> OpPayloadBuilder<EvmConfig, T> {
let Self { compute_pending_block, evm_config, config, .. } = self;
OpPayloadBuilder { compute_pending_block, evm_config, best_transactions, config }
}
pub const fn compute_pending_block(self) -> Self {
self.set_compute_pending_block(true)
}
pub const fn is_compute_pending_block(&self) -> bool {
self.compute_pending_block
}
}
impl<EvmConfig, Txs> OpPayloadBuilder<EvmConfig, Txs>
where
EvmConfig: ConfigureEvm<Header = Header, Transaction = TransactionSigned>,
Txs: OpPayloadTransactions,
{
fn build_payload<Client, Pool>(
&self,
args: BuildArguments<Pool, Client, OpPayloadBuilderAttributes, OpBuiltPayload>,
) -> Result<BuildOutcome<OpBuiltPayload>, PayloadBuilderError>
where
Client: StateProviderFactory + ChainSpecProvider<ChainSpec = OpChainSpec>,
Pool: TransactionPool<Transaction: PoolTransaction<Consensus = TransactionSigned>>,
{
let evm_env = self
.cfg_and_block_env(&args.config.attributes, &args.config.parent_header)
.map_err(PayloadBuilderError::other)?;
let EvmEnv { cfg_env_with_handler_cfg, block_env } = evm_env;
let BuildArguments { client, pool, mut cached_reads, config, cancel, best_payload } = args;
let ctx = OpPayloadBuilderCtx {
evm_config: self.evm_config.clone(),
chain_spec: client.chain_spec(),
config,
initialized_cfg: cfg_env_with_handler_cfg,
initialized_block_env: block_env,
cancel,
best_payload,
};
let builder = OpBuilder { pool, best: self.best_transactions.clone() };
let state_provider = client.state_by_block_hash(ctx.parent().hash())?;
let state = StateProviderDatabase::new(state_provider);
if ctx.attributes().no_tx_pool {
let db = State::builder().with_database(state).with_bundle_update().build();
builder.build(db, ctx)
} else {
let db = State::builder()
.with_database(cached_reads.as_db_mut(state))
.with_bundle_update()
.build();
builder.build(db, ctx)
}
.map(|out| out.with_cached_reads(cached_reads))
}
}
impl<EvmConfig, Txs> OpPayloadBuilder<EvmConfig, Txs>
where
EvmConfig: ConfigureEvm<Header = Header, Transaction = TransactionSigned>,
{
pub fn cfg_and_block_env(
&self,
attributes: &OpPayloadBuilderAttributes,
parent: &Header,
) -> Result<EvmEnv, EvmConfig::Error> {
let next_attributes = NextBlockEnvAttributes {
timestamp: attributes.timestamp(),
suggested_fee_recipient: attributes.suggested_fee_recipient(),
prev_randao: attributes.prev_randao(),
gas_limit: attributes.gas_limit.unwrap_or(parent.gas_limit),
};
self.evm_config.next_cfg_and_block_env(parent, next_attributes)
}
pub fn payload_witness<Client>(
&self,
client: &Client,
parent: SealedHeader,
attributes: OpPayloadAttributes,
) -> Result<ExecutionWitness, PayloadBuilderError>
where
Client: StateProviderFactory + ChainSpecProvider<ChainSpec = OpChainSpec>,
{
let attributes = OpPayloadBuilderAttributes::try_new(parent.hash(), attributes, 3)
.map_err(PayloadBuilderError::other)?;
let evm_env =
self.cfg_and_block_env(&attributes, &parent).map_err(PayloadBuilderError::other)?;
let EvmEnv { cfg_env_with_handler_cfg, block_env } = evm_env;
let config = PayloadConfig { parent_header: Arc::new(parent), attributes };
let ctx = OpPayloadBuilderCtx {
evm_config: self.evm_config.clone(),
chain_spec: client.chain_spec(),
config,
initialized_cfg: cfg_env_with_handler_cfg,
initialized_block_env: block_env,
cancel: Default::default(),
best_payload: Default::default(),
};
let state_provider = client.state_by_block_hash(ctx.parent().hash())?;
let state = StateProviderDatabase::new(state_provider);
let mut state = State::builder().with_database(state).with_bundle_update().build();
let builder = OpBuilder { pool: NoopTransactionPool::default(), best: () };
builder.witness(&mut state, &ctx)
}
}
impl<Pool, Client, EvmConfig, Txs> PayloadBuilder<Pool, Client> for OpPayloadBuilder<EvmConfig, Txs>
where
Client: StateProviderFactory + ChainSpecProvider<ChainSpec = OpChainSpec>,
Pool: TransactionPool<Transaction: PoolTransaction<Consensus = TransactionSigned>>,
EvmConfig: ConfigureEvm<Header = Header, Transaction = TransactionSigned>,
Txs: OpPayloadTransactions,
{
type Attributes = OpPayloadBuilderAttributes;
type BuiltPayload = OpBuiltPayload;
fn try_build(
&self,
args: BuildArguments<Pool, Client, OpPayloadBuilderAttributes, OpBuiltPayload>,
) -> Result<BuildOutcome<OpBuiltPayload>, PayloadBuilderError> {
self.build_payload(args)
}
fn on_missing_payload(
&self,
_args: BuildArguments<Pool, Client, OpPayloadBuilderAttributes, OpBuiltPayload>,
) -> MissingPayloadBehaviour<Self::BuiltPayload> {
MissingPayloadBehaviour::AwaitInProgress
}
fn build_empty_payload(
&self,
client: &Client,
config: PayloadConfig<Self::Attributes>,
) -> Result<OpBuiltPayload, PayloadBuilderError> {
let args = BuildArguments {
client,
config,
pool: NoopTransactionPool::default(),
cached_reads: Default::default(),
cancel: Default::default(),
best_payload: None,
};
self.build_payload(args)?.into_payload().ok_or_else(|| PayloadBuilderError::MissingPayload)
}
}
#[derive(Debug)]
pub struct OpBuilder<Pool, Txs> {
pool: Pool,
best: Txs,
}
impl<Pool, Txs> OpBuilder<Pool, Txs>
where
Pool: TransactionPool<Transaction: PoolTransaction<Consensus = TransactionSigned>>,
Txs: OpPayloadTransactions,
{
pub fn execute<EvmConfig, DB>(
self,
state: &mut State<DB>,
ctx: &OpPayloadBuilderCtx<EvmConfig>,
) -> Result<BuildOutcomeKind<ExecutedPayload>, PayloadBuilderError>
where
EvmConfig: ConfigureEvm<Header = Header, Transaction = TransactionSigned>,
DB: Database<Error = ProviderError>,
{
let Self { pool, best } = self;
debug!(target: "payload_builder", id=%ctx.payload_id(), parent_header = ?ctx.parent().hash(), parent_number = ctx.parent().number, "building new payload");
ctx.apply_pre_beacon_root_contract_call(state)?;
ctx.ensure_create2_deployer(state)?;
let mut info = ctx.execute_sequencer_transactions(state)?;
if !ctx.attributes().no_tx_pool {
let best_txs = best.best_transactions(pool, ctx.best_transaction_attributes());
if ctx.execute_best_transactions::<_, Pool>(&mut info, state, best_txs)?.is_some() {
return Ok(BuildOutcomeKind::Cancelled)
}
if !ctx.is_better_payload(info.total_fees) {
return Ok(BuildOutcomeKind::Aborted { fees: info.total_fees })
}
}
let withdrawals_root = ctx.commit_withdrawals(state)?;
state.merge_transitions(BundleRetention::Reverts);
Ok(BuildOutcomeKind::Better { payload: ExecutedPayload { info, withdrawals_root } })
}
pub fn build<EvmConfig, DB, P>(
self,
mut state: State<DB>,
ctx: OpPayloadBuilderCtx<EvmConfig>,
) -> Result<BuildOutcomeKind<OpBuiltPayload>, PayloadBuilderError>
where
EvmConfig: ConfigureEvm<Header = Header, Transaction = TransactionSigned>,
DB: Database<Error = ProviderError> + AsRef<P>,
P: StateRootProvider + HashedPostStateProvider,
{
let ExecutedPayload { info, withdrawals_root } = match self.execute(&mut state, &ctx)? {
BuildOutcomeKind::Better { payload } | BuildOutcomeKind::Freeze(payload) => payload,
BuildOutcomeKind::Cancelled => return Ok(BuildOutcomeKind::Cancelled),
BuildOutcomeKind::Aborted { fees } => return Ok(BuildOutcomeKind::Aborted { fees }),
};
let block_number = ctx.block_number();
let execution_outcome = ExecutionOutcome::new(
state.take_bundle(),
vec![info.receipts].into(),
block_number,
Vec::new(),
);
let receipts_root = execution_outcome
.generic_receipts_root_slow(block_number, |receipts| {
calculate_receipt_root_no_memo_optimism(
receipts,
&ctx.chain_spec,
ctx.attributes().timestamp(),
)
})
.expect("Number is in range");
let logs_bloom =
execution_outcome.block_logs_bloom(block_number).expect("Number is in range");
let state_provider = state.database.as_ref();
let hashed_state = state_provider.hashed_post_state(execution_outcome.state());
let (state_root, trie_output) = {
state_provider.state_root_with_updates(hashed_state.clone()).inspect_err(|err| {
warn!(target: "payload_builder",
parent_header=%ctx.parent().hash(),
%err,
"failed to calculate state root for payload"
);
})?
};
let transactions_root = proofs::calculate_transaction_root(&info.executed_transactions);
let (excess_blob_gas, blob_gas_used) = ctx.blob_fields();
let extra_data = ctx.extra_data()?;
let header = Header {
parent_hash: ctx.parent().hash(),
ommers_hash: EMPTY_OMMER_ROOT_HASH,
beneficiary: ctx.initialized_block_env.coinbase,
state_root,
transactions_root,
receipts_root,
withdrawals_root,
logs_bloom,
timestamp: ctx.attributes().payload_attributes.timestamp,
mix_hash: ctx.attributes().payload_attributes.prev_randao,
nonce: BEACON_NONCE.into(),
base_fee_per_gas: Some(ctx.base_fee()),
number: ctx.parent().number + 1,
gas_limit: ctx.block_gas_limit(),
difficulty: U256::ZERO,
gas_used: info.cumulative_gas_used,
extra_data,
parent_beacon_block_root: ctx.attributes().payload_attributes.parent_beacon_block_root,
blob_gas_used,
excess_blob_gas,
requests_hash: None,
target_blobs_per_block: None,
};
let block = Block {
header,
body: BlockBody {
transactions: info.executed_transactions,
ommers: vec![],
withdrawals: ctx.withdrawals().cloned(),
},
};
let sealed_block = Arc::new(block.seal_slow());
debug!(target: "payload_builder", id=%ctx.attributes().payload_id(), sealed_block_header = ?sealed_block.header, "sealed built block");
let executed = ExecutedBlock {
block: sealed_block.clone(),
senders: Arc::new(info.executed_senders),
execution_output: Arc::new(execution_outcome),
hashed_state: Arc::new(hashed_state),
trie: Arc::new(trie_output),
};
let no_tx_pool = ctx.attributes().no_tx_pool;
let payload = OpBuiltPayload::new(
ctx.payload_id(),
sealed_block,
info.total_fees,
ctx.chain_spec.clone(),
ctx.config.attributes,
Some(executed),
);
if no_tx_pool {
Ok(BuildOutcomeKind::Freeze(payload))
} else {
Ok(BuildOutcomeKind::Better { payload })
}
}
pub fn witness<EvmConfig, DB, P>(
self,
state: &mut State<DB>,
ctx: &OpPayloadBuilderCtx<EvmConfig>,
) -> Result<ExecutionWitness, PayloadBuilderError>
where
EvmConfig: ConfigureEvm<Header = Header, Transaction = TransactionSigned>,
DB: Database<Error = ProviderError> + AsRef<P>,
P: StateProofProvider,
{
let _ = self.execute(state, ctx)?;
let ExecutionWitnessRecord { hashed_state, codes, keys } =
ExecutionWitnessRecord::from_executed_state(state);
let state = state.database.as_ref().witness(Default::default(), hashed_state)?;
Ok(ExecutionWitness { state: state.into_iter().collect(), codes, keys })
}
}
pub trait OpPayloadTransactions: Clone + Send + Sync + Unpin + 'static {
fn best_transactions<
Pool: TransactionPool<Transaction: PoolTransaction<Consensus = TransactionSigned>>,
>(
&self,
pool: Pool,
attr: BestTransactionsAttributes,
) -> impl PayloadTransactions<Transaction = TransactionSigned>;
}
impl OpPayloadTransactions for () {
fn best_transactions<
Pool: TransactionPool<Transaction: PoolTransaction<Consensus = TransactionSigned>>,
>(
&self,
pool: Pool,
attr: BestTransactionsAttributes,
) -> impl PayloadTransactions<Transaction = TransactionSigned> {
BestPayloadTransactions::new(pool.best_transactions_with_attributes(attr))
}
}
#[derive(Debug)]
pub struct ExecutedPayload {
pub info: ExecutionInfo,
pub withdrawals_root: Option<B256>,
}
#[derive(Default, Debug)]
pub struct ExecutionInfo {
pub executed_transactions: Vec<TransactionSigned>,
pub executed_senders: Vec<Address>,
pub receipts: Vec<Option<Receipt>>,
pub cumulative_gas_used: u64,
pub total_fees: U256,
}
impl ExecutionInfo {
pub fn with_capacity(capacity: usize) -> Self {
Self {
executed_transactions: Vec::with_capacity(capacity),
executed_senders: Vec::with_capacity(capacity),
receipts: Vec::with_capacity(capacity),
cumulative_gas_used: 0,
total_fees: U256::ZERO,
}
}
}
#[derive(Debug)]
pub struct OpPayloadBuilderCtx<EvmConfig> {
pub evm_config: EvmConfig,
pub chain_spec: Arc<OpChainSpec>,
pub config: PayloadConfig<OpPayloadBuilderAttributes>,
pub initialized_cfg: CfgEnvWithHandlerCfg,
pub initialized_block_env: BlockEnv,
pub cancel: Cancelled,
pub best_payload: Option<OpBuiltPayload>,
}
impl<EvmConfig> OpPayloadBuilderCtx<EvmConfig> {
pub fn parent(&self) -> &SealedHeader {
&self.config.parent_header
}
pub const fn attributes(&self) -> &OpPayloadBuilderAttributes {
&self.config.attributes
}
pub fn withdrawals(&self) -> Option<&Withdrawals> {
self.chain_spec
.is_shanghai_active_at_timestamp(self.attributes().timestamp())
.then(|| &self.attributes().payload_attributes.withdrawals)
}
pub fn block_gas_limit(&self) -> u64 {
self.attributes()
.gas_limit
.unwrap_or_else(|| self.initialized_block_env.gas_limit.saturating_to())
}
pub fn block_number(&self) -> u64 {
self.initialized_block_env.number.to()
}
pub fn base_fee(&self) -> u64 {
self.initialized_block_env.basefee.to()
}
pub fn get_blob_gasprice(&self) -> Option<u64> {
self.initialized_block_env.get_blob_gasprice().map(|gasprice| gasprice as u64)
}
pub fn blob_fields(&self) -> (Option<u64>, Option<u64>) {
if self.is_ecotone_active() {
(Some(0), Some(0))
} else {
(None, None)
}
}
pub fn extra_data(&self) -> Result<Bytes, PayloadBuilderError> {
if self.is_holocene_active() {
self.attributes()
.get_holocene_extra_data(
self.chain_spec.base_fee_params_at_timestamp(
self.attributes().payload_attributes.timestamp,
),
)
.map_err(PayloadBuilderError::other)
} else {
Ok(Default::default())
}
}
pub fn best_transaction_attributes(&self) -> BestTransactionsAttributes {
BestTransactionsAttributes::new(self.base_fee(), self.get_blob_gasprice())
}
pub fn payload_id(&self) -> PayloadId {
self.attributes().payload_id()
}
pub fn is_regolith_active(&self) -> bool {
self.chain_spec.is_regolith_active_at_timestamp(self.attributes().timestamp())
}
pub fn is_ecotone_active(&self) -> bool {
self.chain_spec.is_ecotone_active_at_timestamp(self.attributes().timestamp())
}
pub fn is_canyon_active(&self) -> bool {
self.chain_spec.is_canyon_active_at_timestamp(self.attributes().timestamp())
}
pub fn is_holocene_active(&self) -> bool {
self.chain_spec.is_holocene_active_at_timestamp(self.attributes().timestamp())
}
pub fn is_better_payload(&self, total_fees: U256) -> bool {
is_better_payload(self.best_payload.as_ref(), total_fees)
}
pub fn commit_withdrawals<DB>(&self, db: &mut State<DB>) -> Result<Option<B256>, ProviderError>
where
DB: Database<Error = ProviderError>,
{
commit_withdrawals(
db,
&self.chain_spec,
self.attributes().payload_attributes.timestamp,
&self.attributes().payload_attributes.withdrawals,
)
}
pub fn ensure_create2_deployer<DB>(&self, db: &mut State<DB>) -> Result<(), PayloadBuilderError>
where
DB: Database,
DB::Error: Display,
{
reth_optimism_evm::ensure_create2_deployer(
self.chain_spec.clone(),
self.attributes().payload_attributes.timestamp,
db,
)
.map_err(|err| {
warn!(target: "payload_builder", %err, "missing create2 deployer, skipping block.");
PayloadBuilderError::other(OpPayloadBuilderError::ForceCreate2DeployerFail)
})
}
}
impl<EvmConfig> OpPayloadBuilderCtx<EvmConfig>
where
EvmConfig: ConfigureEvm<Header = Header, Transaction = TransactionSigned>,
{
pub fn apply_pre_beacon_root_contract_call<DB>(
&self,
db: &mut DB,
) -> Result<(), PayloadBuilderError>
where
DB: Database + DatabaseCommit,
DB::Error: Display,
{
SystemCaller::new(self.evm_config.clone(), self.chain_spec.clone())
.pre_block_beacon_root_contract_call(
db,
&self.initialized_cfg,
&self.initialized_block_env,
self.attributes().payload_attributes.parent_beacon_block_root,
)
.map_err(|err| {
warn!(target: "payload_builder",
parent_header=%self.parent().hash(),
%err,
"failed to apply beacon root contract call for payload"
);
PayloadBuilderError::Internal(err.into())
})?;
Ok(())
}
pub fn execute_sequencer_transactions<DB>(
&self,
db: &mut State<DB>,
) -> Result<ExecutionInfo, PayloadBuilderError>
where
DB: Database<Error = ProviderError>,
{
let mut info = ExecutionInfo::with_capacity(self.attributes().transactions.len());
let env = EnvWithHandlerCfg::new_with_cfg_env(
self.initialized_cfg.clone(),
self.initialized_block_env.clone(),
TxEnv::default(),
);
let mut evm = self.evm_config.evm_with_env(&mut *db, env);
for sequencer_tx in &self.attributes().transactions {
if sequencer_tx.value().is_eip4844() {
return Err(PayloadBuilderError::other(
OpPayloadBuilderError::BlobTransactionRejected,
))
}
let sequencer_tx =
sequencer_tx.value().clone().try_into_ecrecovered().map_err(|_| {
PayloadBuilderError::other(OpPayloadBuilderError::TransactionEcRecoverFailed)
})?;
let depositor = (self.is_regolith_active() && sequencer_tx.is_deposit())
.then(|| {
evm.db_mut()
.load_cache_account(sequencer_tx.signer())
.map(|acc| acc.account_info().unwrap_or_default())
})
.transpose()
.map_err(|_| {
PayloadBuilderError::other(OpPayloadBuilderError::AccountLoadFailed(
sequencer_tx.signer(),
))
})?;
*evm.tx_mut() = self.evm_config.tx_env(sequencer_tx.as_signed(), sequencer_tx.signer());
let ResultAndState { result, state } = match evm.transact() {
Ok(res) => res,
Err(err) => {
match err {
EVMError::Transaction(err) => {
trace!(target: "payload_builder", %err, ?sequencer_tx, "Error in sequencer transaction, skipping.");
continue
}
err => {
return Err(PayloadBuilderError::EvmExecutionError(err))
}
}
}
};
evm.db_mut().commit(state);
let gas_used = result.gas_used();
info.cumulative_gas_used += gas_used;
info.receipts.push(Some(Receipt {
tx_type: sequencer_tx.tx_type(),
success: result.is_success(),
cumulative_gas_used: info.cumulative_gas_used,
logs: result.into_logs().into_iter().map(Into::into).collect(),
deposit_nonce: depositor.map(|account| account.nonce),
deposit_receipt_version: self.is_canyon_active().then_some(1),
}));
info.executed_senders.push(sequencer_tx.signer());
info.executed_transactions.push(sequencer_tx.into_signed());
}
Ok(info)
}
pub fn execute_best_transactions<DB, Pool>(
&self,
info: &mut ExecutionInfo,
db: &mut State<DB>,
mut best_txs: impl PayloadTransactions<Transaction = TransactionSigned>,
) -> Result<Option<()>, PayloadBuilderError>
where
DB: Database<Error = ProviderError>,
{
let block_gas_limit = self.block_gas_limit();
let base_fee = self.base_fee();
let env = EnvWithHandlerCfg::new_with_cfg_env(
self.initialized_cfg.clone(),
self.initialized_block_env.clone(),
TxEnv::default(),
);
let mut evm = self.evm_config.evm_with_env(&mut *db, env);
while let Some(tx) = best_txs.next(()) {
if info.cumulative_gas_used + tx.gas_limit() > block_gas_limit {
best_txs.mark_invalid(tx.signer(), tx.nonce());
continue
}
if tx.is_eip4844() || tx.tx_type() == TxType::Deposit as u8 {
best_txs.mark_invalid(tx.signer(), tx.nonce());
continue
}
if self.cancel.is_cancelled() {
return Ok(Some(()))
}
*evm.tx_mut() = self.evm_config.tx_env(tx.as_signed(), tx.signer());
let ResultAndState { result, state } = match evm.transact() {
Ok(res) => res,
Err(err) => {
match err {
EVMError::Transaction(err) => {
if matches!(err, InvalidTransaction::NonceTooLow { .. }) {
trace!(target: "payload_builder", %err, ?tx, "skipping nonce too low transaction");
} else {
trace!(target: "payload_builder", %err, ?tx, "skipping invalid transaction and its descendants");
best_txs.mark_invalid(tx.signer(), tx.nonce());
}
continue
}
err => {
return Err(PayloadBuilderError::EvmExecutionError(err))
}
}
}
};
evm.db_mut().commit(state);
let gas_used = result.gas_used();
info.cumulative_gas_used += gas_used;
info.receipts.push(Some(Receipt {
tx_type: tx.tx_type(),
success: result.is_success(),
cumulative_gas_used: info.cumulative_gas_used,
logs: result.into_logs().into_iter().map(Into::into).collect(),
deposit_nonce: None,
deposit_receipt_version: None,
}));
let miner_fee = tx
.effective_tip_per_gas(base_fee)
.expect("fee is always valid; execution succeeded");
info.total_fees += U256::from(miner_fee) * U256::from(gas_used);
info.executed_senders.push(tx.signer());
info.executed_transactions.push(tx.into_signed());
}
Ok(None)
}
}