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

View File

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

View File

@ -5,8 +5,9 @@ use crate::{
EthEvmConfig,
};
use alloc::{boxed::Box, sync::Arc, vec::Vec};
use alloy_consensus::{BlockHeader, Transaction};
use alloy_eips::{eip6110, eip7685::Requests};
use alloy_consensus::{Header, Transaction};
use alloy_eips::{eip4895::Withdrawals, eip6110, eip7685::Requests};
use alloy_primitives::{Address, B256};
use reth_chainspec::{ChainSpec, EthereumHardfork, EthereumHardforks, MAINNET};
use reth_evm::{
execute::{
@ -75,43 +76,83 @@ where
DB: Database,
{
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.
#[derive(Debug, derive_more::Deref)]
#[derive(Debug)]
pub struct EthExecutionStrategy<'a, Evm, EvmConfig> {
/// Reference to the parent factory providing access to [`ChainSpec`].
#[deref]
factory: &'a EthExecutionStrategyFactory<EvmConfig>,
/// Reference to the [`ChainSpec`].
chain_spec: &'a ChainSpec,
/// How to configure the EVM.
evm_config: EvmConfig,
/// Block being executed.
block: &'a SealedBlock,
/// Input for block execution.
input: EthBlockExecutionInput<'a>,
/// The EVM used by strategy.
evm: Evm,
/// Utility to call system smart contracts.
system_caller: SystemCaller<&'a ChainSpec>,
/// Receipts of executed transactions.
receipts: Vec<Receipt>,
/// Total gas used by transactions in this block.
gas_used: u64,
/// Utility to call system smart contracts.
system_caller: SystemCaller<&'a ChainSpec>,
}
impl<'a, Evm, EvmConfig> EthExecutionStrategy<'a, Evm, EvmConfig> {
/// Creates a new [`EthExecutionStrategy`]
pub fn new(
evm: Evm,
block: &'a SealedBlock,
factory: &'a EthExecutionStrategyFactory<EvmConfig>,
input: impl Into<EthBlockExecutionInput<'a>>,
chain_spec: &'a ChainSpec,
evm_config: EvmConfig,
) -> Self {
Self {
evm,
factory,
block,
chain_spec,
evm_config,
input: input.into(),
receipts: Vec::new(),
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> {
// Set state clear flag if the block is after the Spurious Dragon hardfork.
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.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(())
}
@ -138,10 +182,10 @@ where
fn execute_transaction(
&mut self,
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,
// 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 {
return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas {
transaction_gas_limit: tx.gas_limit(),
@ -161,8 +205,10 @@ where
let ResultAndState { result, state } = result_and_state;
self.evm.db_mut().commit(state);
let gas_used = result.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.
self.receipts.push(Receipt {
@ -174,16 +220,16 @@ where
logs: result.into_logs(),
});
Ok(())
Ok(gas_used)
}
fn apply_post_execution_changes(
mut self,
) -> 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
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();
@ -197,10 +243,15 @@ where
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
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.
let drained_balance: u128 = self
.evm