feat: re-use BlockExecutionStrategy in payload building (#14609)

This commit is contained in:
Arsenii Kulikov
2025-02-20 19:06:19 +04:00
committed by GitHub
parent 09cf07d523
commit 72210736ad
15 changed files with 352 additions and 492 deletions

4
Cargo.lock generated
View File

@ -6720,8 +6720,6 @@ dependencies = [
"futures-core", "futures-core",
"futures-util", "futures-util",
"metrics", "metrics",
"reth-chainspec",
"reth-evm",
"reth-metrics", "reth-metrics",
"reth-payload-builder", "reth-payload-builder",
"reth-payload-builder-primitives", "reth-payload-builder-primitives",
@ -6731,7 +6729,6 @@ dependencies = [
"reth-provider", "reth-provider",
"reth-revm", "reth-revm",
"reth-tasks", "reth-tasks",
"revm",
"tokio", "tokio",
"tracing", "tracing",
] ]
@ -7776,7 +7773,6 @@ dependencies = [
"alloy-genesis", "alloy-genesis",
"alloy-primitives", "alloy-primitives",
"alloy-sol-types", "alloy-sol-types",
"derive_more 2.0.1",
"reth-chainspec", "reth-chainspec",
"reth-ethereum-forks", "reth-ethereum-forks",
"reth-evm", "reth-evm",

View File

@ -21,8 +21,8 @@ use reth_chainspec::EthereumHardforks;
/// - Definition: [Yellow Paper][yp] (page 15, 11.3) /// - Definition: [Yellow Paper][yp] (page 15, 11.3)
/// ///
/// [yp]: https://ethereum.github.io/yellowpaper/paper.pdf /// [yp]: https://ethereum.github.io/yellowpaper/paper.pdf
pub fn base_block_reward<ChainSpec: EthereumHardforks>( pub fn base_block_reward(
chain_spec: &ChainSpec, chain_spec: impl EthereumHardforks,
block_number: BlockNumber, block_number: BlockNumber,
) -> Option<u128> { ) -> Option<u128> {
if chain_spec.is_paris_active_at_block(block_number).is_some_and(|active| active) { if chain_spec.is_paris_active_at_block(block_number).is_some_and(|active| active) {

View File

@ -29,8 +29,6 @@ alloy-evm.workspace = true
alloy-sol-types.workspace = true alloy-sol-types.workspace = true
alloy-consensus.workspace = true alloy-consensus.workspace = true
derive_more.workspace = true
[dev-dependencies] [dev-dependencies]
reth-testing-utils.workspace = true reth-testing-utils.workspace = true
reth-evm = { workspace = true, features = ["test-utils"] } reth-evm = { workspace = true, features = ["test-utils"] }
@ -58,5 +56,4 @@ std = [
"alloy-evm/std", "alloy-evm/std",
"reth-execution-types/std", "reth-execution-types/std",
"reth-evm/std", "reth-evm/std",
"derive_more/std",
] ]

View File

@ -5,8 +5,9 @@ use crate::{
EthEvmConfig, EthEvmConfig,
}; };
use alloc::{boxed::Box, sync::Arc, vec::Vec}; use alloc::{boxed::Box, sync::Arc, vec::Vec};
use alloy_consensus::{BlockHeader, Transaction}; use alloy_consensus::{Header, Transaction};
use alloy_eips::{eip6110, eip7685::Requests}; use alloy_eips::{eip4895::Withdrawals, eip6110, eip7685::Requests};
use alloy_primitives::{Address, B256};
use reth_chainspec::{ChainSpec, EthereumHardfork, EthereumHardforks, MAINNET}; use reth_chainspec::{ChainSpec, EthereumHardfork, EthereumHardforks, MAINNET};
use reth_evm::{ use reth_evm::{
execute::{ execute::{
@ -75,43 +76,83 @@ where
DB: Database, DB: Database,
{ {
let evm = self.evm_config.evm_for_block(db, block.header()); let evm = self.evm_config.evm_for_block(db, block.header());
EthExecutionStrategy::new(evm, block.sealed_block(), self) EthExecutionStrategy::new(evm, block.sealed_block(), &self.chain_spec, &self.evm_config)
}
}
/// Input for block execution.
#[derive(Debug, Clone, Copy)]
pub struct EthBlockExecutionInput<'a> {
/// Block number.
pub number: u64,
/// Block timestamp.
pub timestamp: u64,
/// Parent block hash.
pub parent_hash: B256,
/// Block gas limit.
pub gas_limit: u64,
/// Parent beacon block root.
pub parent_beacon_block_root: Option<B256>,
/// Block beneficiary.
pub beneficiary: Address,
/// Block ommers
pub ommers: &'a [Header],
/// Block withdrawals.
pub withdrawals: Option<&'a Withdrawals>,
}
impl<'a> From<&'a SealedBlock> for EthBlockExecutionInput<'a> {
fn from(block: &'a SealedBlock) -> Self {
Self {
number: block.header().number,
timestamp: block.header().timestamp,
parent_hash: block.header().parent_hash,
gas_limit: block.header().gas_limit,
parent_beacon_block_root: block.header().parent_beacon_block_root,
beneficiary: block.header().beneficiary,
ommers: &block.body().ommers,
withdrawals: block.body().withdrawals.as_ref(),
}
} }
} }
/// Block execution strategy for Ethereum. /// Block execution strategy for Ethereum.
#[derive(Debug, derive_more::Deref)] #[derive(Debug)]
pub struct EthExecutionStrategy<'a, Evm, EvmConfig> { pub struct EthExecutionStrategy<'a, Evm, EvmConfig> {
/// Reference to the parent factory providing access to [`ChainSpec`]. /// Reference to the [`ChainSpec`].
#[deref] chain_spec: &'a ChainSpec,
factory: &'a EthExecutionStrategyFactory<EvmConfig>, /// How to configure the EVM.
evm_config: EvmConfig,
/// Block being executed. /// Input for block execution.
block: &'a SealedBlock, input: EthBlockExecutionInput<'a>,
/// The EVM used by strategy. /// The EVM used by strategy.
evm: Evm, evm: Evm,
/// Utility to call system smart contracts.
system_caller: SystemCaller<&'a ChainSpec>,
/// Receipts of executed transactions. /// Receipts of executed transactions.
receipts: Vec<Receipt>, receipts: Vec<Receipt>,
/// Total gas used by transactions in this block. /// Total gas used by transactions in this block.
gas_used: u64, gas_used: u64,
/// Utility to call system smart contracts.
system_caller: SystemCaller<&'a ChainSpec>,
} }
impl<'a, Evm, EvmConfig> EthExecutionStrategy<'a, Evm, EvmConfig> { impl<'a, Evm, EvmConfig> EthExecutionStrategy<'a, Evm, EvmConfig> {
/// Creates a new [`EthExecutionStrategy`] /// Creates a new [`EthExecutionStrategy`]
pub fn new( pub fn new(
evm: Evm, evm: Evm,
block: &'a SealedBlock, input: impl Into<EthBlockExecutionInput<'a>>,
factory: &'a EthExecutionStrategyFactory<EvmConfig>, chain_spec: &'a ChainSpec,
evm_config: EvmConfig,
) -> Self { ) -> Self {
Self { Self {
evm, evm,
factory, chain_spec,
block, evm_config,
input: input.into(),
receipts: Vec::new(), receipts: Vec::new(),
gas_used: 0, gas_used: 0,
system_caller: SystemCaller::new(&factory.chain_spec), system_caller: SystemCaller::new(chain_spec),
} }
} }
} }
@ -128,9 +169,12 @@ where
fn apply_pre_execution_changes(&mut self) -> Result<(), Self::Error> { fn apply_pre_execution_changes(&mut self) -> Result<(), Self::Error> {
// Set state clear flag if the block is after the Spurious Dragon hardfork. // Set state clear flag if the block is after the Spurious Dragon hardfork.
let state_clear_flag = let state_clear_flag =
self.chain_spec.is_spurious_dragon_active_at_block(self.block.number()); self.chain_spec.is_spurious_dragon_active_at_block(self.input.number);
self.evm.db_mut().set_state_clear_flag(state_clear_flag); self.evm.db_mut().set_state_clear_flag(state_clear_flag);
self.system_caller.apply_pre_execution_changes(self.block.header(), &mut self.evm)?; self.system_caller
.apply_blockhashes_contract_call(self.input.parent_hash, &mut self.evm)?;
self.system_caller
.apply_beacon_root_contract_call(self.input.parent_beacon_block_root, &mut self.evm)?;
Ok(()) Ok(())
} }
@ -138,10 +182,10 @@ where
fn execute_transaction( fn execute_transaction(
&mut self, &mut self,
tx: Recovered<&TransactionSigned>, tx: Recovered<&TransactionSigned>,
) -> Result<(), Self::Error> { ) -> Result<u64, Self::Error> {
// The sum of the transaction's gas limit, Tg, and the gas utilized in this block prior, // The sum of the transaction's gas limit, Tg, and the gas utilized in this block prior,
// must be no greater than the block's gasLimit. // must be no greater than the block's gasLimit.
let block_available_gas = self.block.gas_limit() - self.gas_used; let block_available_gas = self.input.gas_limit - self.gas_used;
if tx.gas_limit() > block_available_gas { if tx.gas_limit() > block_available_gas {
return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas { return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas {
transaction_gas_limit: tx.gas_limit(), transaction_gas_limit: tx.gas_limit(),
@ -161,8 +205,10 @@ where
let ResultAndState { result, state } = result_and_state; let ResultAndState { result, state } = result_and_state;
self.evm.db_mut().commit(state); self.evm.db_mut().commit(state);
let gas_used = result.gas_used();
// append gas used // append gas used
self.gas_used += result.gas_used(); self.gas_used += gas_used;
// Push transaction changeset and calculate header bloom filter for receipt. // Push transaction changeset and calculate header bloom filter for receipt.
self.receipts.push(Receipt { self.receipts.push(Receipt {
@ -174,16 +220,16 @@ where
logs: result.into_logs(), logs: result.into_logs(),
}); });
Ok(()) Ok(gas_used)
} }
fn apply_post_execution_changes( fn apply_post_execution_changes(
mut self, mut self,
) -> Result<BlockExecutionResult<Receipt>, Self::Error> { ) -> Result<BlockExecutionResult<Receipt>, Self::Error> {
let requests = if self.chain_spec.is_prague_active_at_timestamp(self.block.timestamp) { let requests = if self.chain_spec.is_prague_active_at_timestamp(self.input.timestamp) {
// Collect all EIP-6110 deposits // Collect all EIP-6110 deposits
let deposit_requests = let deposit_requests =
crate::eip6110::parse_deposits_from_receipts(&self.chain_spec, &self.receipts)?; crate::eip6110::parse_deposits_from_receipts(self.chain_spec, &self.receipts)?;
let mut requests = Requests::default(); let mut requests = Requests::default();
@ -197,10 +243,15 @@ where
Requests::default() Requests::default()
}; };
let mut balance_increments = post_block_balance_increments(&self.chain_spec, self.block); let mut balance_increments = post_block_balance_increments(
self.chain_spec,
self.evm.block(),
self.input.ommers,
self.input.withdrawals,
);
// Irregular state change at Ethereum DAO hardfork // Irregular state change at Ethereum DAO hardfork
if self.chain_spec.fork(EthereumHardfork::Dao).transitions_at_block(self.block.number()) { if self.chain_spec.fork(EthereumHardfork::Dao).transitions_at_block(self.input.number) {
// drain balances from hardcoded addresses. // drain balances from hardcoded addresses.
let drained_balance: u128 = self let drained_balance: u128 = self
.evm .evm

View File

@ -10,21 +10,20 @@
#![allow(clippy::useless_let_if_seq)] #![allow(clippy::useless_let_if_seq)]
use alloy_consensus::{BlockHeader, Header, Transaction, Typed2718, EMPTY_OMMER_ROOT_HASH}; use alloy_consensus::{BlockHeader, Header, Transaction, Typed2718, EMPTY_OMMER_ROOT_HASH};
use alloy_eips::{eip4844::DATA_GAS_PER_BLOB, eip6110, eip7685::Requests, merge::BEACON_NONCE}; use alloy_eips::{eip4844::DATA_GAS_PER_BLOB, merge::BEACON_NONCE};
use alloy_primitives::U256; use alloy_primitives::U256;
use reth_basic_payload_builder::{ use reth_basic_payload_builder::{
commit_withdrawals, is_better_payload, BuildArguments, BuildOutcome, PayloadBuilder, is_better_payload, BuildArguments, BuildOutcome, PayloadBuilder, PayloadConfig,
PayloadConfig,
}; };
use reth_chainspec::{ChainSpec, ChainSpecProvider, EthChainSpec, EthereumHardforks}; use reth_chainspec::{ChainSpec, ChainSpecProvider, EthChainSpec, EthereumHardforks};
use reth_errors::RethError; use reth_errors::{BlockExecutionError, BlockValidationError};
use reth_ethereum_primitives::{Block, BlockBody, Receipt, TransactionSigned}; use reth_ethereum_primitives::{Block, BlockBody, TransactionSigned};
use reth_evm::{ use reth_evm::{execute::BlockExecutionStrategy, ConfigureEvm, NextBlockEnvAttributes};
system_calls::SystemCaller, ConfigureEvm, Evm, EvmEnv, EvmError, InvalidTxError, use reth_evm_ethereum::{
NextBlockEnvAttributes, execute::{EthBlockExecutionInput, EthExecutionStrategy},
EthEvmConfig,
}; };
use reth_evm_ethereum::{eip6110::parse_deposits_from_receipts, EthEvmConfig}; use reth_execution_types::{BlockExecutionResult, ExecutionOutcome};
use reth_execution_types::ExecutionOutcome;
use reth_payload_builder::{EthBuiltPayload, EthPayloadBuilderAttributes}; use reth_payload_builder::{EthBuiltPayload, EthPayloadBuilderAttributes};
use reth_payload_builder_primitives::PayloadBuilderError; use reth_payload_builder_primitives::PayloadBuilderError;
use reth_payload_primitives::PayloadBuilderAttributes; use reth_payload_primitives::PayloadBuilderAttributes;
@ -41,10 +40,7 @@ use reth_transaction_pool::{
error::InvalidPoolTransactionError, BestTransactions, BestTransactionsAttributes, error::InvalidPoolTransactionError, BestTransactions, BestTransactionsAttributes,
PoolTransaction, TransactionPool, ValidPoolTransaction, PoolTransaction, TransactionPool, ValidPoolTransaction,
}; };
use revm::{ use revm::context_interface::Block as _;
context_interface::{result::ResultAndState, Block as _},
DatabaseCommit,
};
use std::sync::Arc; use std::sync::Arc;
use tracing::{debug, trace, warn}; use tracing::{debug, trace, warn};
@ -82,27 +78,6 @@ impl<Pool, Client, EvmConfig> EthereumPayloadBuilder<Pool, Client, EvmConfig> {
} }
} }
impl<Pool, Client, EvmConfig> EthereumPayloadBuilder<Pool, Client, EvmConfig>
where
EvmConfig: ConfigureEvm<Header = Header>,
{
/// Returns the configured [`EvmEnv`] for the targeted payload
/// (that has the `parent` as its parent).
fn evm_env(
&self,
config: &PayloadConfig<EthPayloadBuilderAttributes>,
parent: &Header,
) -> Result<EvmEnv<EvmConfig::Spec>, EvmConfig::Error> {
let next_attributes = NextBlockEnvAttributes {
timestamp: config.attributes.timestamp(),
suggested_fee_recipient: config.attributes.suggested_fee_recipient(),
prev_randao: config.attributes.prev_randao(),
gas_limit: self.builder_config.gas_limit(parent.gas_limit),
};
self.evm_config.next_evm_env(parent, next_attributes)
}
}
// Default implementation of [PayloadBuilder] for unit type // Default implementation of [PayloadBuilder] for unit type
impl<Pool, Client, EvmConfig> PayloadBuilder for EthereumPayloadBuilder<Pool, Client, EvmConfig> impl<Pool, Client, EvmConfig> PayloadBuilder for EthereumPayloadBuilder<Pool, Client, EvmConfig>
where where
@ -117,17 +92,12 @@ where
&self, &self,
args: BuildArguments<EthPayloadBuilderAttributes, EthBuiltPayload>, args: BuildArguments<EthPayloadBuilderAttributes, EthBuiltPayload>,
) -> Result<BuildOutcome<EthBuiltPayload>, PayloadBuilderError> { ) -> Result<BuildOutcome<EthBuiltPayload>, PayloadBuilderError> {
let evm_env = self
.evm_env(&args.config, &args.config.parent_header)
.map_err(PayloadBuilderError::other)?;
default_ethereum_payload( default_ethereum_payload(
self.evm_config.clone(), self.evm_config.clone(),
self.client.clone(), self.client.clone(),
self.pool.clone(), self.pool.clone(),
self.builder_config.clone(), self.builder_config.clone(),
args, args,
evm_env,
|attributes| self.pool.best_transactions_with_attributes(attributes), |attributes| self.pool.best_transactions_with_attributes(attributes),
) )
} }
@ -138,17 +108,12 @@ where
) -> Result<EthBuiltPayload, PayloadBuilderError> { ) -> Result<EthBuiltPayload, PayloadBuilderError> {
let args = BuildArguments::new(Default::default(), config, Default::default(), None); let args = BuildArguments::new(Default::default(), config, Default::default(), None);
let evm_env = self
.evm_env(&args.config, &args.config.parent_header)
.map_err(PayloadBuilderError::other)?;
default_ethereum_payload( default_ethereum_payload(
self.evm_config.clone(), self.evm_config.clone(),
self.client.clone(), self.client.clone(),
self.pool.clone(), self.pool.clone(),
self.builder_config.clone(), self.builder_config.clone(),
args, args,
evm_env,
|attributes| self.pool.best_transactions_with_attributes(attributes), |attributes| self.pool.best_transactions_with_attributes(attributes),
)? )?
.into_payload() .into_payload()
@ -168,7 +133,6 @@ pub fn default_ethereum_payload<EvmConfig, Client, Pool, F>(
pool: Pool, pool: Pool,
builder_config: EthereumBuilderConfig, builder_config: EthereumBuilderConfig,
args: BuildArguments<EthPayloadBuilderAttributes, EthBuiltPayload>, args: BuildArguments<EthPayloadBuilderAttributes, EthBuiltPayload>,
evm_env: EvmEnv<EvmConfig::Spec>,
best_txs: F, best_txs: F,
) -> Result<BuildOutcome<EthBuiltPayload>, PayloadBuilderError> ) -> Result<BuildOutcome<EthBuiltPayload>, PayloadBuilderError>
where where
@ -178,13 +142,24 @@ where
F: FnOnce(BestTransactionsAttributes) -> BestTransactionsIter<Pool>, F: FnOnce(BestTransactionsAttributes) -> BestTransactionsIter<Pool>,
{ {
let BuildArguments { mut cached_reads, config, cancel, best_payload } = args; let BuildArguments { mut cached_reads, config, cancel, best_payload } = args;
let PayloadConfig { parent_header, attributes } = config;
let chain_spec = client.chain_spec(); let state_provider = client.state_by_block_hash(parent_header.hash())?;
let state_provider = client.state_by_block_hash(config.parent_header.hash())?;
let state = StateProviderDatabase::new(state_provider); let state = StateProviderDatabase::new(state_provider);
let mut db = let mut db =
State::builder().with_database(cached_reads.as_db_mut(state)).with_bundle_update().build(); State::builder().with_database(cached_reads.as_db_mut(state)).with_bundle_update().build();
let PayloadConfig { parent_header, attributes } = config;
let next_attributes = NextBlockEnvAttributes {
timestamp: attributes.timestamp(),
suggested_fee_recipient: attributes.suggested_fee_recipient(),
prev_randao: attributes.prev_randao(),
gas_limit: builder_config.gas_limit(parent_header.gas_limit),
};
let evm_env = evm_config
.next_evm_env(&parent_header, next_attributes)
.map_err(PayloadBuilderError::other)?;
let chain_spec = client.chain_spec();
debug!(target: "payload_builder", id=%attributes.id, parent_header = ?parent_header.hash(), parent_number = parent_header.number, "building new payload"); debug!(target: "payload_builder", id=%attributes.id, parent_header = ?parent_header.hash(), parent_number = parent_header.number, "building new payload");
let mut cumulative_gas_used = 0; let mut cumulative_gas_used = 0;
@ -202,32 +177,27 @@ where
let block_number = evm_env.block_env.number; let block_number = evm_env.block_env.number;
let beneficiary = evm_env.block_env.beneficiary; let beneficiary = evm_env.block_env.beneficiary;
let mut evm = evm_config.evm_with_env(&mut db, evm_env); let mut strategy = EthExecutionStrategy::new(
let mut system_caller = SystemCaller::new(chain_spec.clone()); evm_config.evm_with_env(&mut db, evm_env),
EthBlockExecutionInput {
number: parent_header.number + 1,
timestamp: attributes.timestamp(),
parent_hash: parent_header.hash(),
gas_limit: next_attributes.gas_limit,
parent_beacon_block_root: attributes.parent_beacon_block_root,
beneficiary,
ommers: &[],
withdrawals: Some(&attributes.withdrawals),
},
&chain_spec,
&evm_config,
);
// apply eip-4788 pre block contract call strategy.apply_pre_execution_changes().map_err(|err| {
system_caller warn!(target: "payload_builder", %err, "failed to apply pre-execution changes");
.apply_beacon_root_contract_call(attributes.parent_beacon_block_root, &mut evm)
.map_err(|err| {
warn!(target: "payload_builder",
parent_hash=%parent_header.hash(),
%err,
"failed to apply beacon root contract call for payload"
);
PayloadBuilderError::Internal(err.into())
})?;
// apply eip-2935 blockhashes update
system_caller.apply_blockhashes_contract_call(
parent_header.hash(),
&mut evm,
)
.map_err(|err| {
warn!(target: "payload_builder", parent_hash=%parent_header.hash(), %err, "failed to update parent header blockhashes for payload");
PayloadBuilderError::Internal(err.into()) PayloadBuilderError::Internal(err.into())
})?; })?;
let mut receipts = Vec::new();
let mut block_blob_count = 0; let mut block_blob_count = 0;
let blob_params = chain_spec.blob_params_at_timestamp(attributes.timestamp); let blob_params = chain_spec.blob_params_at_timestamp(attributes.timestamp);
let max_blob_count = let max_blob_count =
@ -278,37 +248,31 @@ where
} }
} }
// Configure the environment for the tx. let gas_used = match strategy.execute_transaction(tx.as_recovered_ref()) {
let tx_env = evm_config.tx_env(&tx); Ok(gas_used) => gas_used,
Err(BlockExecutionError::Validation(BlockValidationError::InvalidTx {
let ResultAndState { result, state } = match evm.transact(tx_env) { error, ..
Ok(res) => res, })) => {
Err(err) => { if error.is_nonce_too_low() {
if let Some(err) = err.as_invalid_tx_err() { // if the nonce is too low, we can skip this transaction
if err.is_nonce_too_low() { trace!(target: "payload_builder", %error, ?tx, "skipping nonce too low transaction");
// if the nonce is too low, we can skip this transaction } else {
trace!(target: "payload_builder", %err, ?tx, "skipping nonce too low transaction"); // if the transaction is invalid, we can skip it and all of its
} else { // descendants
// if the transaction is invalid, we can skip it and all of its trace!(target: "payload_builder", %error, ?tx, "skipping invalid transaction and its descendants");
// descendants best_txs.mark_invalid(
trace!(target: "payload_builder", %err, ?tx, "skipping invalid transaction and its descendants"); &pool_tx,
best_txs.mark_invalid( InvalidPoolTransactionError::Consensus(
&pool_tx, InvalidTransactionError::TxTypeNotSupported,
InvalidPoolTransactionError::Consensus( ),
InvalidTransactionError::TxTypeNotSupported, );
),
);
}
continue
} }
// this is an error that we should treat as fatal for this attempt continue
return Err(PayloadBuilderError::evm(err))
} }
// this is an error that we should treat as fatal for this attempt
Err(err) => return Err(PayloadBuilderError::evm(err)),
}; };
// commit changes
evm.db_mut().commit(state);
// add to the total blob gas used if the transaction successfully executed // add to the total blob gas used if the transaction successfully executed
if let Some(blob_tx) = tx.as_eip4844() { if let Some(blob_tx) = tx.as_eip4844() {
block_blob_count += blob_tx.blob_versioned_hashes.len() as u64; block_blob_count += blob_tx.blob_versioned_hashes.len() as u64;
@ -319,23 +283,11 @@ where
} }
} }
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(Receipt {
tx_type: tx.tx_type(),
success: result.is_success(),
cumulative_gas_used,
logs: result.into_logs().into_iter().collect(),
});
// update add to total fees // update add to total fees
let miner_fee = let miner_fee =
tx.effective_tip_per_gas(base_fee).expect("fee is always valid; execution succeeded"); tx.effective_tip_per_gas(base_fee).expect("fee is always valid; execution succeeded");
total_fees += U256::from(miner_fee) * U256::from(gas_used); total_fees += U256::from(miner_fee) * U256::from(gas_used);
cumulative_gas_used += gas_used;
// append transaction to the block body // append transaction to the block body
executed_txs.push(tx.into_tx()); executed_txs.push(tx.into_tx());
@ -344,39 +296,20 @@ where
// check if we have a better block // check if we have a better block
if !is_better_payload(best_payload.as_ref(), total_fees) { if !is_better_payload(best_payload.as_ref(), total_fees) {
// Release db // Release db
drop(evm); drop(strategy);
// can skip building the block // can skip building the block
return Ok(BuildOutcome::Aborted { fees: total_fees, cached_reads }) return Ok(BuildOutcome::Aborted { fees: total_fees, cached_reads })
} }
// calculate the requests and the requests root let BlockExecutionResult { receipts, requests, gas_used } = strategy
let requests = if chain_spec.is_prague_active_at_timestamp(attributes.timestamp) { .apply_post_execution_changes()
let deposit_requests = parse_deposits_from_receipts(&chain_spec, receipts.iter()) .map_err(|err| PayloadBuilderError::Internal(err.into()))?;
.map_err(|err| PayloadBuilderError::Internal(RethError::Execution(err.into())))?;
let mut requests = Requests::default(); let requests =
chain_spec.is_prague_active_at_timestamp(attributes.timestamp).then_some(requests);
if !deposit_requests.is_empty() { let withdrawals_root = chain_spec
requests.push_request_with_type(eip6110::DEPOSIT_REQUEST_TYPE, deposit_requests); .is_shanghai_active_at_timestamp(attributes.timestamp)
} .then_some(proofs::calculate_withdrawals_root(&attributes.withdrawals));
requests.extend(
system_caller
.apply_post_execution_changes(&mut evm)
.map_err(|err| PayloadBuilderError::Internal(err.into()))?,
);
Some(requests)
} else {
None
};
// Release db
drop(evm);
let withdrawals_root =
commit_withdrawals(&mut db, &chain_spec, attributes.timestamp, &attributes.withdrawals)?;
// merge all transitions into bundle state, this would apply the withdrawal balance changes // merge all transitions into bundle state, this would apply the withdrawal balance changes
// and 4788 contract call // and 4788 contract call
@ -449,7 +382,7 @@ where
number: parent_header.number + 1, number: parent_header.number + 1,
gas_limit: block_gas_limit, gas_limit: block_gas_limit,
difficulty: U256::ZERO, difficulty: U256::ZERO,
gas_used: cumulative_gas_used, gas_used,
extra_data: builder_config.extra_data, extra_data: builder_config.extra_data,
parent_beacon_block_root: attributes.parent_beacon_block_root, parent_beacon_block_root: attributes.parent_beacon_block_root,
blob_gas_used, blob_gas_used,

View File

@ -143,7 +143,7 @@ impl BlockExecutionError {
matches!(self, Self::Validation(BlockValidationError::StateRoot(_))) matches!(self, Self::Validation(BlockValidationError::StateRoot(_)))
} }
/// Handles an EVM error occured when executing a transaction. /// Handles an EVM error occurred when executing a transaction.
/// ///
/// If an error matches [`EvmError::InvalidTransaction`], it will be wrapped into /// If an error matches [`EvmError::InvalidTransaction`], it will be wrapped into
/// [`BlockValidationError::InvalidTx`], otherwise into [`InternalBlockExecutionError::EVM`]. /// [`BlockValidationError::InvalidTx`], otherwise into [`InternalBlockExecutionError::EVM`].

View File

@ -2,6 +2,7 @@
use crate::{ConfigureEvm, ConfigureEvmEnv}; use crate::{ConfigureEvm, ConfigureEvmEnv};
use alloy_evm::{EvmEnv, EvmFactory}; use alloy_evm::{EvmEnv, EvmFactory};
use reth_primitives_traits::NodePrimitives; use reth_primitives_traits::NodePrimitives;
use revm::inspector::NoOpInspector;
/// This is a type alias to make type bounds simpler when we have a [`NodePrimitives`] and need a /// This is a type alias to make type bounds simpler when we have a [`NodePrimitives`] and need a
/// [`ConfigureEvmEnv`] whose associated types match the [`NodePrimitives`] associated types. /// [`ConfigureEvmEnv`] whose associated types match the [`NodePrimitives`] associated types.
@ -43,3 +44,8 @@ pub type HaltReasonFor<Evm> = <<Evm as ConfigureEvm>::EvmFactory as EvmFactory<
/// Helper to access [`ConfigureEvmEnv::Spec`] for a given [`ConfigureEvmEnv`]. /// Helper to access [`ConfigureEvmEnv::Spec`] for a given [`ConfigureEvmEnv`].
pub type SpecFor<Evm> = <Evm as ConfigureEvmEnv>::Spec; pub type SpecFor<Evm> = <Evm as ConfigureEvmEnv>::Spec;
/// Helper to access [`EvmFactory::Evm`] for a given [`ConfigureEvm`].
pub type EvmFor<Evm, DB, I = NoOpInspector> = <<Evm as ConfigureEvm>::EvmFactory as EvmFactory<
EvmEnv<<Evm as ConfigureEvmEnv>::Spec>,
>>::Evm<DB, I>;

View File

@ -174,10 +174,12 @@ pub trait BlockExecutionStrategy {
fn apply_pre_execution_changes(&mut self) -> Result<(), Self::Error>; fn apply_pre_execution_changes(&mut self) -> Result<(), Self::Error>;
/// Executes a single transaction and applies execution result to internal state. /// Executes a single transaction and applies execution result to internal state.
///
/// Returns the gas used by the transaction.
fn execute_transaction( fn execute_transaction(
&mut self, &mut self,
tx: Recovered<&<Self::Primitives as NodePrimitives>::SignedTx>, tx: Recovered<&<Self::Primitives as NodePrimitives>::SignedTx>,
) -> Result<(), Self::Error>; ) -> Result<u64, Self::Error>;
/// Applies any necessary changes after executing the block's transactions. /// Applies any necessary changes after executing the block's transactions.
fn apply_post_execution_changes( fn apply_post_execution_changes(
@ -449,8 +451,8 @@ mod tests {
fn execute_transaction( fn execute_transaction(
&mut self, &mut self,
_tx: Recovered<&TransactionSigned>, _tx: Recovered<&TransactionSigned>,
) -> Result<(), Self::Error> { ) -> Result<u64, Self::Error> {
Ok(()) Ok(0)
} }
fn apply_post_execution_changes( fn apply_post_execution_changes(

View File

@ -24,10 +24,7 @@ pub use alloy_evm::evm::EvmFactory;
use alloy_primitives::{Address, B256}; use alloy_primitives::{Address, B256};
use core::fmt::Debug; use core::fmt::Debug;
use reth_primitives_traits::{BlockHeader, SignedTransaction}; use reth_primitives_traits::{BlockHeader, SignedTransaction};
use revm::{ use revm::{context::TxEnv, inspector::Inspector};
context::TxEnv,
inspector::{Inspector, NoOpInspector},
};
pub mod batch; pub mod batch;
pub mod either; pub mod either;
@ -76,11 +73,7 @@ pub trait ConfigureEvm: ConfigureEvmEnv {
/// including the spec id and transaction environment. /// including the spec id and transaction environment.
/// ///
/// This will preserve any handler modifications /// This will preserve any handler modifications
fn evm_with_env<DB: Database>( fn evm_with_env<DB: Database>(&self, db: DB, evm_env: EvmEnv<Self::Spec>) -> EvmFor<Self, DB> {
&self,
db: DB,
evm_env: EvmEnv<Self::Spec>,
) -> <Self::EvmFactory as EvmFactory<EvmEnv<Self::Spec>>>::Evm<DB, NoOpInspector> {
self.evm_factory().create_evm(db, evm_env) self.evm_factory().create_evm(db, evm_env)
} }
@ -91,11 +84,7 @@ pub trait ConfigureEvm: ConfigureEvmEnv {
/// # Caution /// # Caution
/// ///
/// This does not initialize the tx environment. /// This does not initialize the tx environment.
fn evm_for_block<DB: Database>( fn evm_for_block<DB: Database>(&self, db: DB, header: &Self::Header) -> EvmFor<Self, DB> {
&self,
db: DB,
header: &Self::Header,
) -> <Self::EvmFactory as EvmFactory<EvmEnv<Self::Spec>>>::Evm<DB, NoOpInspector> {
let evm_env = self.evm_env(header); let evm_env = self.evm_env(header);
self.evm_with_env(db, evm_env) self.evm_with_env(db, evm_env)
} }
@ -111,7 +100,7 @@ pub trait ConfigureEvm: ConfigureEvmEnv {
db: DB, db: DB,
evm_env: EvmEnv<Self::Spec>, evm_env: EvmEnv<Self::Spec>,
inspector: I, inspector: I,
) -> <Self::EvmFactory as EvmFactory<EvmEnv<Self::Spec>>>::Evm<DB, I> ) -> EvmFor<Self, DB, I>
where where
DB: Database, DB: Database,
I: InspectorFor<DB, Self>, I: InspectorFor<DB, Self>,
@ -131,19 +120,11 @@ where
(*self).evm_factory() (*self).evm_factory()
} }
fn evm_for_block<DB: Database>( fn evm_for_block<DB: Database>(&self, db: DB, header: &Self::Header) -> EvmFor<Self, DB> {
&self,
db: DB,
header: &Self::Header,
) -> <Self::EvmFactory as EvmFactory<EvmEnv<Self::Spec>>>::Evm<DB, NoOpInspector> {
(*self).evm_for_block(db, header) (*self).evm_for_block(db, header)
} }
fn evm_with_env<DB: Database>( fn evm_with_env<DB: Database>(&self, db: DB, evm_env: EvmEnv<Self::Spec>) -> EvmFor<Self, DB> {
&self,
db: DB,
evm_env: EvmEnv<Self::Spec>,
) -> <Self::EvmFactory as EvmFactory<EvmEnv<Self::Spec>>>::Evm<DB, NoOpInspector> {
(*self).evm_with_env(db, evm_env) (*self).evm_with_env(db, evm_env)
} }
@ -152,7 +133,7 @@ where
db: DB, db: DB,
evm_env: EvmEnv<Self::Spec>, evm_env: EvmEnv<Self::Spec>,
inspector: I, inspector: I,
) -> <Self::EvmFactory as EvmFactory<EvmEnv<Self::Spec>>>::Evm<DB, I> ) -> EvmFor<Self, DB, I>
where where
DB: Database, DB: Database,
I: InspectorFor<DB, Self>, I: InspectorFor<DB, Self>,

View File

@ -1,50 +1,46 @@
//! State changes that are not related to transactions. //! State changes that are not related to transactions.
use alloy_consensus::BlockHeader; use alloy_consensus::BlockHeader;
use alloy_eips::eip4895::Withdrawal; use alloy_eips::eip4895::{Withdrawal, Withdrawals};
use alloy_primitives::{map::HashMap, Address}; use alloy_primitives::{map::HashMap, Address};
use reth_chainspec::EthereumHardforks; use reth_chainspec::EthereumHardforks;
use reth_consensus_common::calc; use reth_consensus_common::calc;
use reth_primitives::SealedBlock; use revm::context::BlockEnv;
use reth_primitives_traits::BlockBody;
/// Collect all balance changes at the end of the block. /// Collect all balance changes at the end of the block.
/// ///
/// Balance changes might include the block reward, uncle rewards, withdrawals, or irregular /// Balance changes might include the block reward, uncle rewards, withdrawals, or irregular
/// state changes (DAO fork). /// state changes (DAO fork).
#[inline] #[inline]
pub fn post_block_balance_increments<ChainSpec, Block>( pub fn post_block_balance_increments<H>(
chain_spec: &ChainSpec, chain_spec: impl EthereumHardforks,
block: &SealedBlock<Block>, block_env: &BlockEnv,
ommers: &[H],
withdrawals: Option<&Withdrawals>,
) -> HashMap<Address, u128> ) -> HashMap<Address, u128>
where where
ChainSpec: EthereumHardforks, H: BlockHeader,
Block: reth_primitives_traits::Block,
{ {
let mut balance_increments = HashMap::default(); let mut balance_increments = HashMap::default();
// Add block rewards if they are enabled. // Add block rewards if they are enabled.
if let Some(base_block_reward) = calc::base_block_reward(chain_spec, block.header().number()) { if let Some(base_block_reward) = calc::base_block_reward(&chain_spec, block_env.number) {
// Ommer rewards // Ommer rewards
if let Some(ommers) = block.body().ommers() { for ommer in ommers {
for ommer in ommers { *balance_increments.entry(ommer.beneficiary()).or_default() +=
*balance_increments.entry(ommer.beneficiary()).or_default() += calc::ommer_reward(base_block_reward, block_env.number, ommer.number());
calc::ommer_reward(base_block_reward, block.header().number(), ommer.number());
}
} }
// Full block reward // Full block reward
*balance_increments.entry(block.header().beneficiary()).or_default() += calc::block_reward( *balance_increments.entry(block_env.beneficiary).or_default() +=
base_block_reward, calc::block_reward(base_block_reward, ommers.len());
block.body().ommers().map(|s| s.len()).unwrap_or(0),
);
} }
// process withdrawals // process withdrawals
insert_post_block_withdrawals_balance_increments( insert_post_block_withdrawals_balance_increments(
chain_spec, chain_spec,
block.header().timestamp(), block_env.timestamp,
block.body().withdrawals().as_ref().map(|w| w.as_slice()), withdrawals.map(|w| w.as_slice()),
&mut balance_increments, &mut balance_increments,
); );
@ -77,8 +73,8 @@ pub fn post_block_withdrawals_balance_increments<ChainSpec: EthereumHardforks>(
/// ///
/// Zero-valued withdrawals are filtered out. /// Zero-valued withdrawals are filtered out.
#[inline] #[inline]
pub fn insert_post_block_withdrawals_balance_increments<ChainSpec: EthereumHardforks>( pub fn insert_post_block_withdrawals_balance_increments(
chain_spec: &ChainSpec, chain_spec: impl EthereumHardforks,
block_timestamp: u64, block_timestamp: u64,
withdrawals: Option<&[Withdrawal]>, withdrawals: Option<&[Withdrawal]>,
balance_increments: &mut HashMap<Address, u128>, balance_increments: &mut HashMap<Address, u128>,

View File

@ -6,7 +6,7 @@ use crate::{
}; };
use alloc::{boxed::Box, sync::Arc, vec::Vec}; use alloc::{boxed::Box, sync::Arc, vec::Vec};
use alloy_consensus::{ use alloy_consensus::{
transaction::Recovered, BlockHeader, Eip658Value, Receipt, Transaction as _, TxReceipt, transaction::Recovered, BlockHeader, Eip658Value, Header, Receipt, Transaction as _, TxReceipt,
}; };
use op_alloy_consensus::OpDepositReceipt; use op_alloy_consensus::OpDepositReceipt;
use reth_evm::{ use reth_evm::{
@ -22,9 +22,12 @@ use reth_execution_types::BlockExecutionResult;
use reth_optimism_chainspec::OpChainSpec; use reth_optimism_chainspec::OpChainSpec;
use reth_optimism_forks::OpHardforks; use reth_optimism_forks::OpHardforks;
use reth_optimism_primitives::{transaction::signed::OpTransaction, DepositReceipt, OpPrimitives}; use reth_optimism_primitives::{transaction::signed::OpTransaction, DepositReceipt, OpPrimitives};
use reth_primitives_traits::{NodePrimitives, RecoveredBlock, SealedBlock, SignedTransaction}; use reth_primitives_traits::{
Block, NodePrimitives, RecoveredBlock, SealedBlock, SignedTransaction,
};
use revm::{context_interface::result::ResultAndState, DatabaseCommit}; use revm::{context_interface::result::ResultAndState, DatabaseCommit};
use revm_database::State; use revm_database::State;
use revm_primitives::{Address, B256};
use tracing::trace; use tracing::trace;
/// Factory for [`OpExecutionStrategy`]. /// Factory for [`OpExecutionStrategy`].
@ -89,19 +92,59 @@ where
DB: Database, DB: Database,
{ {
let evm = self.evm_config.evm_for_block(db, block.header()); let evm = self.evm_config.evm_for_block(db, block.header());
OpExecutionStrategy::new(evm, block.sealed_block(), self) OpExecutionStrategy::new(
evm,
block.sealed_block(),
&self.chain_spec,
&self.evm_config,
self.receipt_builder.as_ref(),
)
}
}
/// Input for block execution.
#[derive(Debug, Clone, Copy)]
pub struct OpBlockExecutionInput {
/// Block number.
pub number: u64,
/// Block timestamp.
pub timestamp: u64,
/// Parent block hash.
pub parent_hash: B256,
/// Block gas limit.
pub gas_limit: u64,
/// Parent beacon block root.
pub parent_beacon_block_root: Option<B256>,
/// Block beneficiary.
pub beneficiary: Address,
}
impl<'a, B: Block> From<&'a SealedBlock<B>> for OpBlockExecutionInput {
fn from(block: &'a SealedBlock<B>) -> Self {
Self {
number: block.header().number(),
timestamp: block.header().timestamp(),
parent_hash: block.header().parent_hash(),
gas_limit: block.header().gas_limit(),
parent_beacon_block_root: block.header().parent_beacon_block_root(),
beneficiary: block.header().beneficiary(),
}
} }
} }
/// Block execution strategy for Optimism. /// Block execution strategy for Optimism.
#[derive(Debug, derive_more::Deref)] #[derive(Debug)]
pub struct OpExecutionStrategy<'a, Evm, N: NodePrimitives, ChainSpec, EvmConfig: ConfigureEvm> { pub struct OpExecutionStrategy<'a, Evm, N: NodePrimitives, ChainSpec, EvmConfig: ConfigureEvm> {
/// Reference to the parent factory. /// Chainspec.
#[deref] chain_spec: ChainSpec,
factory: &'a OpExecutionStrategyFactory<N, ChainSpec, EvmConfig>, /// How to configure the EVM.
evm_config: EvmConfig,
/// Receipt builder.
receipt_builder:
&'a dyn OpReceiptBuilder<N::SignedTx, HaltReasonFor<EvmConfig>, Receipt = N::Receipt>,
/// Block being executed. /// Input for block execution.
block: &'a SealedBlock<N::Block>, input: OpBlockExecutionInput,
/// The EVM used by strategy. /// The EVM used by strategy.
evm: Evm, evm: Evm,
/// Receipts of executed transactions. /// Receipts of executed transactions.
@ -111,7 +154,7 @@ pub struct OpExecutionStrategy<'a, Evm, N: NodePrimitives, ChainSpec, EvmConfig:
/// Whether Regolith hardfork is active. /// Whether Regolith hardfork is active.
is_regolith: bool, is_regolith: bool,
/// Utility to call system smart contracts. /// Utility to call system smart contracts.
system_caller: SystemCaller<&'a ChainSpec>, system_caller: SystemCaller<ChainSpec>,
} }
impl<'a, Evm, N, ChainSpec, EvmConfig> OpExecutionStrategy<'a, Evm, N, ChainSpec, EvmConfig> impl<'a, Evm, N, ChainSpec, EvmConfig> OpExecutionStrategy<'a, Evm, N, ChainSpec, EvmConfig>
@ -123,17 +166,26 @@ where
/// Creates a new [`OpExecutionStrategy`] /// Creates a new [`OpExecutionStrategy`]
pub fn new( pub fn new(
evm: Evm, evm: Evm,
block: &'a SealedBlock<N::Block>, input: impl Into<OpBlockExecutionInput>,
factory: &'a OpExecutionStrategyFactory<N, ChainSpec, EvmConfig>, chain_spec: ChainSpec,
evm_config: EvmConfig,
receipt_builder: &'a dyn OpReceiptBuilder<
N::SignedTx,
HaltReasonFor<EvmConfig>,
Receipt = N::Receipt,
>,
) -> Self { ) -> Self {
let input = input.into();
Self { Self {
is_regolith: chain_spec.is_regolith_active_at_timestamp(input.timestamp),
evm, evm,
factory, system_caller: SystemCaller::new(chain_spec.clone()),
block, chain_spec,
evm_config,
receipt_builder,
receipts: Vec::new(), receipts: Vec::new(),
gas_used: 0, gas_used: 0,
is_regolith: factory.chain_spec.is_regolith_active_at_timestamp(block.timestamp()), input,
system_caller: SystemCaller::new(&factory.chain_spec),
} }
} }
} }
@ -153,28 +205,26 @@ where
fn apply_pre_execution_changes(&mut self) -> Result<(), Self::Error> { fn apply_pre_execution_changes(&mut self) -> Result<(), Self::Error> {
// Set state clear flag if the block is after the Spurious Dragon hardfork. // Set state clear flag if the block is after the Spurious Dragon hardfork.
let state_clear_flag = let state_clear_flag =
(*self.chain_spec).is_spurious_dragon_active_at_block(self.block.number()); self.chain_spec.is_spurious_dragon_active_at_block(self.input.number);
self.evm.db_mut().set_state_clear_flag(state_clear_flag); self.evm.db_mut().set_state_clear_flag(state_clear_flag);
self.system_caller.apply_beacon_root_contract_call( self.system_caller
self.block.parent_beacon_block_root(), .apply_beacon_root_contract_call(self.input.parent_beacon_block_root, &mut self.evm)?;
&mut self.evm,
)?;
// Ensure that the create2deployer is force-deployed at the canyon transition. Optimism // Ensure that the create2deployer is force-deployed at the canyon transition. Optimism
// blocks will always have at least a single transaction in them (the L1 info transaction), // blocks will always have at least a single transaction in them (the L1 info transaction),
// so we can safely assume that this will always be triggered upon the transition and that // so we can safely assume that this will always be triggered upon the transition and that
// the above check for empty blocks will never be hit on OP chains. // the above check for empty blocks will never be hit on OP chains.
ensure_create2_deployer(self.chain_spec.clone(), self.block.timestamp(), self.evm.db_mut()) ensure_create2_deployer(self.chain_spec.clone(), self.input.timestamp, self.evm.db_mut())
.map_err(|_| OpBlockExecutionError::ForceCreate2DeployerFail)?; .map_err(|_| OpBlockExecutionError::ForceCreate2DeployerFail)?;
Ok(()) Ok(())
} }
fn execute_transaction(&mut self, tx: Recovered<&N::SignedTx>) -> Result<(), Self::Error> { fn execute_transaction(&mut self, tx: Recovered<&N::SignedTx>) -> Result<u64, Self::Error> {
// The sum of the transactions gas limit, Tg, and the gas utilized in this block prior, // The sum of the transactions gas limit, Tg, and the gas utilized in this block prior,
// must be no greater than the blocks gasLimit. // must be no greater than the blocks gasLimit.
let block_available_gas = self.block.gas_limit() - self.gas_used; let block_available_gas = self.input.gas_limit - self.gas_used;
if tx.gas_limit() > block_available_gas && (self.is_regolith || !tx.is_deposit()) { if tx.gas_limit() > block_available_gas && (self.is_regolith || !tx.is_deposit()) {
return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas { return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas {
transaction_gas_limit: tx.gas_limit(), transaction_gas_limit: tx.gas_limit(),
@ -215,8 +265,10 @@ where
let ResultAndState { result, state } = result_and_state; let ResultAndState { result, state } = result_and_state;
self.evm.db_mut().commit(state); self.evm.db_mut().commit(state);
let gas_used = result.gas_used();
// append gas used // append gas used
self.gas_used += result.gas_used(); self.gas_used += gas_used;
self.receipts.push( self.receipts.push(
match self.receipt_builder.build_receipt(ReceiptBuilderCtx { match self.receipt_builder.build_receipt(ReceiptBuilderCtx {
@ -243,22 +295,25 @@ where
// this is only set for post-Canyon deposit // this is only set for post-Canyon deposit
// transactions. // transactions.
deposit_receipt_version: (tx.is_deposit() && deposit_receipt_version: (tx.is_deposit() &&
self.chain_spec self.chain_spec.is_canyon_active_at_timestamp(self.input.timestamp))
.is_canyon_active_at_timestamp(self.block.timestamp()))
.then_some(1), .then_some(1),
}) })
} }
}, },
); );
Ok(()) Ok(gas_used)
} }
fn apply_post_execution_changes( fn apply_post_execution_changes(
mut self, mut self,
) -> Result<BlockExecutionResult<N::Receipt>, Self::Error> { ) -> Result<BlockExecutionResult<N::Receipt>, Self::Error> {
let balance_increments = let balance_increments = post_block_balance_increments::<Header>(
post_block_balance_increments(&self.chain_spec.clone(), self.block); &self.chain_spec.clone(),
self.evm.block(),
&[],
None,
);
// increment balances // increment balances
self.evm self.evm
.db_mut() .db_mut()

View File

@ -7,26 +7,24 @@ use crate::{
OpPayloadPrimitives, OpPayloadPrimitives,
}; };
use alloy_consensus::{ use alloy_consensus::{
constants::EMPTY_WITHDRAWALS, Eip658Value, Header, Transaction, Typed2718, constants::EMPTY_WITHDRAWALS, Header, Transaction, Typed2718, EMPTY_OMMER_ROOT_HASH,
EMPTY_OMMER_ROOT_HASH,
}; };
use alloy_eips::{eip4895::Withdrawals, merge::BEACON_NONCE}; use alloy_eips::{eip4895::Withdrawals, merge::BEACON_NONCE};
use alloy_primitives::{Address, Bytes, B256, U256}; use alloy_primitives::{Address, Bytes, B256, U256};
use alloy_rlp::Encodable; use alloy_rlp::Encodable;
use alloy_rpc_types_debug::ExecutionWitness; use alloy_rpc_types_debug::ExecutionWitness;
use alloy_rpc_types_engine::PayloadId; use alloy_rpc_types_engine::PayloadId;
use op_alloy_consensus::OpDepositReceipt;
use op_alloy_rpc_types_engine::OpPayloadAttributes; use op_alloy_rpc_types_engine::OpPayloadAttributes;
use reth_basic_payload_builder::*; use reth_basic_payload_builder::*;
use reth_chain_state::{ExecutedBlock, ExecutedBlockWithTrieUpdates}; use reth_chain_state::{ExecutedBlock, ExecutedBlockWithTrieUpdates};
use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks}; use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks};
use reth_evm::{ use reth_evm::{
system_calls::SystemCaller, ConfigureEvm, ConfigureEvmFor, Database, Evm, EvmEnv, EvmError, execute::{BlockExecutionError, BlockExecutionStrategy, BlockValidationError},
HaltReasonFor, InvalidTxError, NextBlockEnvAttributes, ConfigureEvm, ConfigureEvmFor, Database, EvmEnv, HaltReasonFor, NextBlockEnvAttributes,
}; };
use reth_execution_types::ExecutionOutcome; use reth_execution_types::ExecutionOutcome;
use reth_optimism_consensus::calculate_receipt_root_no_memo_optimism; use reth_optimism_consensus::calculate_receipt_root_no_memo_optimism;
use reth_optimism_evm::{OpReceiptBuilder, ReceiptBuilderCtx}; use reth_optimism_evm::{OpBlockExecutionInput, OpExecutionStrategy, OpReceiptBuilder};
use reth_optimism_forks::OpHardforks; use reth_optimism_forks::OpHardforks;
use reth_optimism_primitives::transaction::signed::OpTransaction; use reth_optimism_primitives::transaction::signed::OpTransaction;
use reth_optimism_storage::predeploys; use reth_optimism_storage::predeploys;
@ -38,8 +36,8 @@ use reth_primitives::{
}; };
use reth_primitives_traits::{block::Block as _, proofs, RecoveredBlock}; use reth_primitives_traits::{block::Block as _, proofs, RecoveredBlock};
use reth_provider::{ use reth_provider::{
HashedPostStateProvider, ProviderError, StateProofProvider, StateProviderFactory, BlockExecutionResult, HashedPostStateProvider, ProviderError, StateProofProvider,
StateRootProvider, StorageRootProvider, StateProviderFactory, StateRootProvider, StorageRootProvider,
}; };
use reth_revm::{ use reth_revm::{
cancelled::CancelOnDrop, cancelled::CancelOnDrop,
@ -48,14 +46,8 @@ use reth_revm::{
witness::ExecutionWitnessRecord, witness::ExecutionWitnessRecord,
}; };
use reth_transaction_pool::{BestTransactionsAttributes, PoolTransaction, TransactionPool}; use reth_transaction_pool::{BestTransactionsAttributes, PoolTransaction, TransactionPool};
use revm::{ use revm::context_interface::Block;
context_interface::{ use std::sync::Arc;
result::{ExecutionResult, ResultAndState},
Block,
},
Database as _, DatabaseCommit,
};
use std::{fmt::Display, sync::Arc};
use tracing::{debug, trace, warn}; use tracing::{debug, trace, warn};
/// Optimism's payload builder /// Optimism's payload builder
@ -366,21 +358,34 @@ impl<Txs> OpBuilder<'_, Txs> {
let Self { best } = self; let Self { best } = self;
debug!(target: "payload_builder", id=%ctx.payload_id(), parent_header = ?ctx.parent().hash(), parent_number = ctx.parent().number, "building new payload"); debug!(target: "payload_builder", id=%ctx.payload_id(), parent_header = ?ctx.parent().hash(), parent_number = ctx.parent().number, "building new payload");
let mut evm = ctx.evm_config.evm_with_env(&mut *state, ctx.evm_env.clone()); let mut strategy = OpExecutionStrategy::new(
ctx.evm_config.evm_with_env(&mut *state, ctx.evm_env.clone()),
OpBlockExecutionInput {
number: ctx.evm_env.block_env.number,
timestamp: ctx.evm_env.block_env.timestamp,
parent_hash: ctx.parent().hash(),
gas_limit: ctx.evm_env.block_env.gas_limit,
parent_beacon_block_root: ctx.attributes().parent_beacon_block_root(),
beneficiary: ctx.evm_env.block_env.beneficiary,
},
&ctx.chain_spec,
&ctx.evm_config,
ctx.receipt_builder.as_ref(),
);
// 1. apply eip-4788 pre block contract call // 1. apply pre-execution changes
ctx.apply_pre_beacon_root_contract_call(&mut evm)?; strategy.apply_pre_execution_changes().map_err(|err| {
warn!(target: "payload_builder", %err, "failed to apply pre-execution changes");
PayloadBuilderError::Internal(err.into())
})?;
// 2. ensure create2deployer is force deployed // 2. execute sequencer transactions
ctx.ensure_create2_deployer(evm.db_mut())?; let mut info = ctx.execute_sequencer_transactions(&mut strategy)?;
// 3. execute sequencer transactions // 3. if mem pool transactions are requested we execute them
let mut info = ctx.execute_sequencer_transactions(&mut evm)?;
// 4. if mem pool transactions are requested we execute them
if !ctx.attributes().no_tx_pool { if !ctx.attributes().no_tx_pool {
let best_txs = best(ctx.best_transaction_attributes()); let best_txs = best(ctx.best_transaction_attributes());
if ctx.execute_best_transactions(&mut info, &mut evm, best_txs)?.is_some() { if ctx.execute_best_transactions(&mut info, &mut strategy, best_txs)?.is_some() {
return Ok(BuildOutcomeKind::Cancelled) return Ok(BuildOutcomeKind::Cancelled)
} }
@ -391,7 +396,9 @@ impl<Txs> OpBuilder<'_, Txs> {
} }
} }
drop(evm); let BlockExecutionResult { receipts, .. } = strategy
.apply_post_execution_changes()
.map_err(|err| PayloadBuilderError::Internal(err.into()))?;
// merge all transitions into bundle state, this would apply the withdrawal balance changes // merge all transitions into bundle state, this would apply the withdrawal balance changes
// and 4788 contract call // and 4788 contract call
@ -407,7 +414,7 @@ impl<Txs> OpBuilder<'_, Txs> {
None None
}; };
let payload = ExecutedPayload { info, withdrawals_root }; let payload = ExecutedPayload { receipts, info, withdrawals_root };
Ok(BuildOutcomeKind::Better { payload }) Ok(BuildOutcomeKind::Better { payload })
} }
@ -426,19 +433,16 @@ impl<Txs> OpBuilder<'_, Txs> {
DB: Database<Error = ProviderError> + AsRef<P>, DB: Database<Error = ProviderError> + AsRef<P>,
P: StateRootProvider + HashedPostStateProvider + StorageRootProvider, P: StateRootProvider + HashedPostStateProvider + StorageRootProvider,
{ {
let ExecutedPayload { info, withdrawals_root } = match self.execute(&mut state, &ctx)? { let ExecutedPayload { receipts, info, withdrawals_root } =
BuildOutcomeKind::Better { payload } | BuildOutcomeKind::Freeze(payload) => payload, match self.execute(&mut state, &ctx)? {
BuildOutcomeKind::Cancelled => return Ok(BuildOutcomeKind::Cancelled), BuildOutcomeKind::Better { payload } | BuildOutcomeKind::Freeze(payload) => payload,
BuildOutcomeKind::Aborted { fees } => return Ok(BuildOutcomeKind::Aborted { fees }), BuildOutcomeKind::Cancelled => return Ok(BuildOutcomeKind::Cancelled),
}; BuildOutcomeKind::Aborted { fees } => return Ok(BuildOutcomeKind::Aborted { fees }),
};
let block_number = ctx.block_number(); let block_number = ctx.block_number();
let execution_outcome = ExecutionOutcome::new( let execution_outcome =
state.take_bundle(), ExecutionOutcome::new(state.take_bundle(), vec![receipts], block_number, Vec::new());
vec![info.receipts],
block_number,
Vec::new(),
);
let receipts_root = execution_outcome let receipts_root = execution_outcome
.generic_receipts_root_slow(block_number, |receipts| { .generic_receipts_root_slow(block_number, |receipts| {
calculate_receipt_root_no_memo_optimism( calculate_receipt_root_no_memo_optimism(
@ -588,6 +592,8 @@ pub struct ExecutedPayload<N: NodePrimitives> {
pub info: ExecutionInfo<N>, pub info: ExecutionInfo<N>,
/// Withdrawal hash. /// Withdrawal hash.
pub withdrawals_root: Option<B256>, pub withdrawals_root: Option<B256>,
/// The transaction receipts.
pub receipts: Vec<N::Receipt>,
} }
/// This acts as the container for executed transactions and its byproducts (receipts, gas used) /// This acts as the container for executed transactions and its byproducts (receipts, gas used)
@ -597,8 +603,6 @@ pub struct ExecutionInfo<N: NodePrimitives> {
pub executed_transactions: Vec<N::SignedTx>, pub executed_transactions: Vec<N::SignedTx>,
/// The recovered senders for the executed transactions. /// The recovered senders for the executed transactions.
pub executed_senders: Vec<Address>, pub executed_senders: Vec<Address>,
/// The transaction receipts
pub receipts: Vec<N::Receipt>,
/// All gas used so far /// All gas used so far
pub cumulative_gas_used: u64, pub cumulative_gas_used: u64,
/// Estimated DA size /// Estimated DA size
@ -613,7 +617,6 @@ impl<N: NodePrimitives> ExecutionInfo<N> {
Self { Self {
executed_transactions: Vec::with_capacity(capacity), executed_transactions: Vec::with_capacity(capacity),
executed_senders: Vec::with_capacity(capacity), executed_senders: Vec::with_capacity(capacity),
receipts: Vec::with_capacity(capacity),
cumulative_gas_used: 0, cumulative_gas_used: 0,
cumulative_da_bytes_used: 0, cumulative_da_bytes_used: 0,
total_fees: U256::ZERO, total_fees: U256::ZERO,
@ -787,26 +790,6 @@ where
pub fn is_better_payload(&self, total_fees: U256) -> bool { pub fn is_better_payload(&self, total_fees: U256) -> bool {
is_better_payload(self.best_payload.as_ref(), total_fees) is_better_payload(self.best_payload.as_ref(), total_fees)
} }
/// Ensure that the create2deployer is force-deployed at the canyon transition. Optimism
/// blocks will always have at least a single transaction in them (the L1 info transaction),
/// so we can safely assume that this will always be triggered upon the transition and that
/// the above check for empty blocks will never be hit on OP chains.
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, ChainSpec, N> OpPayloadBuilderCtx<EvmConfig, ChainSpec, N> impl<EvmConfig, ChainSpec, N> OpPayloadBuilderCtx<EvmConfig, ChainSpec, N>
@ -815,73 +798,10 @@ where
ChainSpec: EthChainSpec + OpHardforks, ChainSpec: EthChainSpec + OpHardforks,
N: OpPayloadPrimitives, N: OpPayloadPrimitives,
{ {
/// apply eip-4788 pre block contract call
pub fn apply_pre_beacon_root_contract_call(
&self,
evm: &mut impl Evm<DB: DatabaseCommit>,
) -> Result<(), PayloadBuilderError> {
SystemCaller::new(&self.chain_spec)
.apply_beacon_root_contract_call(
self.attributes().payload_attributes.parent_beacon_block_root,
evm,
)
.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(())
}
/// Constructs a receipt for the given transaction.
fn build_receipt(
&self,
info: &ExecutionInfo<N>,
result: ExecutionResult<HaltReasonFor<EvmConfig>>,
deposit_nonce: Option<u64>,
tx: &N::SignedTx,
) -> N::Receipt {
match self.receipt_builder.build_receipt(ReceiptBuilderCtx {
tx,
result,
cumulative_gas_used: info.cumulative_gas_used,
}) {
Ok(receipt) => receipt,
Err(ctx) => {
let receipt = alloy_consensus::Receipt {
// Success flag was added in `EIP-658: Embedding transaction status code
// in receipts`.
status: Eip658Value::Eip658(ctx.result.is_success()),
cumulative_gas_used: ctx.cumulative_gas_used,
logs: ctx.result.into_logs(),
};
self.receipt_builder.build_deposit_receipt(OpDepositReceipt {
inner: receipt,
deposit_nonce,
// The deposit receipt version was introduced in Canyon to indicate an
// update to how receipt hashes should be computed
// when set. The state transition process ensures
// this is only set for post-Canyon deposit
// transactions.
deposit_receipt_version: self.is_canyon_active().then_some(1),
})
}
}
}
/// Executes all sequencer transactions that are included in the payload attributes. /// Executes all sequencer transactions that are included in the payload attributes.
pub fn execute_sequencer_transactions( pub fn execute_sequencer_transactions(
&self, &self,
evm: &mut impl Evm< strategy: &mut impl BlockExecutionStrategy<Primitives = N, Error = BlockExecutionError>,
DB: Database<Error = ProviderError> + DatabaseCommit,
Tx = EvmConfig::TxEnv,
HaltReason = HaltReasonFor<EvmConfig>,
>,
) -> Result<ExecutionInfo<N>, PayloadBuilderError> { ) -> Result<ExecutionInfo<N>, PayloadBuilderError> {
let mut info = ExecutionInfo::with_capacity(self.attributes().transactions.len()); let mut info = ExecutionInfo::with_capacity(self.attributes().transactions.len());
@ -901,51 +821,24 @@ where
PayloadBuilderError::other(OpPayloadBuilderError::TransactionEcRecoverFailed) PayloadBuilderError::other(OpPayloadBuilderError::TransactionEcRecoverFailed)
})?; })?;
// Cache the depositor account prior to the state transition for the deposit nonce. let gas_used = match strategy.execute_transaction(sequencer_tx.as_recovered_ref()) {
// Ok(gas_used) => gas_used,
// Note that this *only* needs to be done post-regolith hardfork, as deposit nonces Err(BlockExecutionError::Validation(BlockValidationError::InvalidTx {
// were not introduced in Bedrock. In addition, regular transactions don't have deposit error,
// nonces, so we don't need to touch the DB for those. ..
let depositor_nonce = (self.is_regolith_active() && sequencer_tx.is_deposit()) })) => {
.then(|| { trace!(target: "payload_builder", %error, ?sequencer_tx, "Error in sequencer transaction, skipping.");
evm.db_mut() continue
.basic(sequencer_tx.signer()) }
.map(|acc| acc.unwrap_or_default().nonce)
})
.transpose()
.map_err(|_| {
PayloadBuilderError::other(OpPayloadBuilderError::AccountLoadFailed(
sequencer_tx.signer(),
))
})?;
let tx_env = self.evm_config.tx_env(&sequencer_tx);
let ResultAndState { result, state: _ } = match evm.transact_commit(tx_env) {
Ok(res) => res,
Err(err) => { Err(err) => {
if err.is_invalid_tx_err() {
trace!(target: "payload_builder", %err, ?sequencer_tx, "Error in sequencer transaction, skipping.");
continue
}
// this is an error that we should treat as fatal for this attempt // this is an error that we should treat as fatal for this attempt
return Err(PayloadBuilderError::EvmExecutionError(Box::new(err))) return Err(PayloadBuilderError::EvmExecutionError(Box::new(err)))
} }
}; };
let gas_used = result.gas_used();
// add gas used by the transaction to cumulative gas used, before creating the receipt // add gas used by the transaction to cumulative gas used, before creating the receipt
info.cumulative_gas_used += gas_used; info.cumulative_gas_used += gas_used;
// Push transaction changeset and calculate header bloom filter for receipt.
info.receipts.push(self.build_receipt(
&info,
result,
depositor_nonce,
sequencer_tx.tx(),
));
// append sender and transaction to the respective lists // append sender and transaction to the respective lists
info.executed_senders.push(sequencer_tx.signer()); info.executed_senders.push(sequencer_tx.signer());
info.executed_transactions.push(sequencer_tx.into_tx()); info.executed_transactions.push(sequencer_tx.into_tx());
@ -960,11 +853,7 @@ where
pub fn execute_best_transactions( pub fn execute_best_transactions(
&self, &self,
info: &mut ExecutionInfo<N>, info: &mut ExecutionInfo<N>,
evm: &mut impl Evm< strategy: &mut impl BlockExecutionStrategy<Primitives = N, Error = BlockExecutionError>,
DB: DatabaseCommit,
Tx = EvmConfig::TxEnv,
HaltReason = HaltReasonFor<EvmConfig>,
>,
mut best_txs: impl PayloadTransactions< mut best_txs: impl PayloadTransactions<
Transaction: PoolTransaction<Consensus = EvmConfig::Transaction>, Transaction: PoolTransaction<Consensus = EvmConfig::Transaction>,
>, >,
@ -995,40 +884,34 @@ where
return Ok(Some(())) return Ok(Some(()))
} }
// Configure the environment for the tx. let gas_used = match strategy.execute_transaction(tx.as_recovered_ref()) {
let tx_env = self.evm_config.tx_env(&tx); Ok(gas_used) => gas_used,
Err(BlockExecutionError::Validation(BlockValidationError::InvalidTx {
let ResultAndState { result, state: _ } = match evm.transact_commit(tx_env) { error,
Ok(res) => res, ..
Err(err) => { })) => {
if let Some(err) = err.as_invalid_tx_err() { if error.is_nonce_too_low() {
if err.is_nonce_too_low() { // if the nonce is too low, we can skip this transaction
// if the nonce is too low, we can skip this transaction trace!(target: "payload_builder", %error, ?tx, "skipping nonce too low transaction");
trace!(target: "payload_builder", %err, ?tx, "skipping nonce too low transaction"); } else {
} else { // if the transaction is invalid, we can skip it and all of its
// if the transaction is invalid, we can skip it and all of its // descendants
// descendants trace!(target: "payload_builder", %error, ?tx, "skipping invalid transaction and its descendants");
trace!(target: "payload_builder", %err, ?tx, "skipping invalid transaction and its descendants"); best_txs.mark_invalid(tx.signer(), tx.nonce());
best_txs.mark_invalid(tx.signer(), tx.nonce());
}
continue
} }
continue
}
Err(err) => {
// this is an error that we should treat as fatal for this attempt // this is an error that we should treat as fatal for this attempt
return Err(PayloadBuilderError::EvmExecutionError(Box::new(err))) return Err(PayloadBuilderError::EvmExecutionError(Box::new(err)))
} }
}; };
let gas_used = result.gas_used();
// add gas used by the transaction to cumulative gas used, before creating the // add gas used by the transaction to cumulative gas used, before creating the
// receipt // receipt
info.cumulative_gas_used += gas_used; info.cumulative_gas_used += gas_used;
info.cumulative_da_bytes_used += tx.length() as u64; info.cumulative_da_bytes_used += tx.length() as u64;
// Push transaction changeset and calculate header bloom filter for receipt.
info.receipts.push(self.build_receipt(info, result, None, &tx));
// update add to total fees // update add to total fees
let miner_fee = tx let miner_fee = tx
.effective_tip_per_gas(base_fee) .effective_tip_per_gas(base_fee)

View File

@ -13,7 +13,6 @@ workspace = true
[dependencies] [dependencies]
# reth # reth
reth-chainspec.workspace = true
reth-primitives.workspace = true reth-primitives.workspace = true
reth-primitives-traits.workspace = true reth-primitives-traits.workspace = true
reth-provider.workspace = true reth-provider.workspace = true
@ -21,12 +20,8 @@ reth-payload-builder.workspace = true
reth-payload-builder-primitives.workspace = true reth-payload-builder-primitives.workspace = true
reth-payload-primitives.workspace = true reth-payload-primitives.workspace = true
reth-tasks.workspace = true reth-tasks.workspace = true
reth-evm.workspace = true
reth-revm.workspace = true reth-revm.workspace = true
# revm
revm.workspace = true
# ethereum # ethereum
alloy-primitives.workspace = true alloy-primitives.workspace = true
alloy-consensus.workspace = true alloy-consensus.workspace = true

View File

@ -9,22 +9,18 @@
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
use crate::metrics::PayloadBuilderMetrics; use crate::metrics::PayloadBuilderMetrics;
use alloy_consensus::constants::EMPTY_WITHDRAWALS; use alloy_eips::merge::SLOT_DURATION;
use alloy_eips::{eip4895::Withdrawals, merge::SLOT_DURATION};
use alloy_primitives::{B256, U256}; use alloy_primitives::{B256, U256};
use futures_core::ready; use futures_core::ready;
use futures_util::FutureExt; use futures_util::FutureExt;
use reth_chainspec::EthereumHardforks;
use reth_evm::state_change::post_block_withdrawals_balance_increments;
use reth_payload_builder::{KeepPayloadJobAlive, PayloadId, PayloadJob, PayloadJobGenerator}; use reth_payload_builder::{KeepPayloadJobAlive, PayloadId, PayloadJob, PayloadJobGenerator};
use reth_payload_builder_primitives::PayloadBuilderError; use reth_payload_builder_primitives::PayloadBuilderError;
use reth_payload_primitives::{BuiltPayload, PayloadBuilderAttributes, PayloadKind}; use reth_payload_primitives::{BuiltPayload, PayloadBuilderAttributes, PayloadKind};
use reth_primitives::{NodePrimitives, SealedHeader}; use reth_primitives::{NodePrimitives, SealedHeader};
use reth_primitives_traits::{proofs, HeaderTy}; use reth_primitives_traits::HeaderTy;
use reth_provider::{BlockReaderIdExt, CanonStateNotification, StateProviderFactory}; use reth_provider::{BlockReaderIdExt, CanonStateNotification, StateProviderFactory};
use reth_revm::{cached::CachedReads, cancelled::CancelOnDrop, db::State}; use reth_revm::{cached::CachedReads, cancelled::CancelOnDrop};
use reth_tasks::TaskSpawner; use reth_tasks::TaskSpawner;
use revm::Database;
use std::{ use std::{
fmt, fmt,
future::Future, future::Future,
@ -881,37 +877,6 @@ impl<Payload> Default for MissingPayloadBehaviour<Payload> {
} }
} }
/// Executes the withdrawals and commits them to the _runtime_ Database and `BundleState`.
///
/// Returns the withdrawals root.
///
/// Returns `None` values pre shanghai
pub fn commit_withdrawals<DB, ChainSpec>(
db: &mut State<DB>,
chain_spec: &ChainSpec,
timestamp: u64,
withdrawals: &Withdrawals,
) -> Result<Option<B256>, DB::Error>
where
DB: Database,
ChainSpec: EthereumHardforks,
{
if !chain_spec.is_shanghai_active_at_timestamp(timestamp) {
return Ok(None)
}
if withdrawals.is_empty() {
return Ok(Some(EMPTY_WITHDRAWALS))
}
let balance_increments =
post_block_withdrawals_balance_increments(chain_spec, timestamp, withdrawals);
db.increment_balances(balance_increments)?;
Ok(Some(proofs::calculate_withdrawals_root(withdrawals)))
}
/// Checks if the new payload is better than the current best. /// Checks if the new payload is better than the current best.
/// ///
/// This compares the total fees of the blocks, higher is better. /// This compares the total fees of the blocks, higher is better.

View File

@ -136,8 +136,8 @@ where
fn execute_transaction( fn execute_transaction(
&mut self, &mut self,
_tx: Recovered<&TransactionSigned>, _tx: Recovered<&TransactionSigned>,
) -> Result<(), Self::Error> { ) -> Result<u64, Self::Error> {
Ok(()) Ok(0)
} }
fn apply_post_execution_changes( fn apply_post_execution_changes(