mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-04-30 03:01:58 -04:00
feat: pending block support in BlockExecutionStrategyFactory (#14730)
This commit is contained in:
@@ -303,7 +303,7 @@ where
|
||||
.with_bundle_update()
|
||||
.build();
|
||||
|
||||
let mut strategy = evm_config.create_strategy(&mut state, &reorg_target);
|
||||
let mut strategy = evm_config.strategy_for_block(&mut state, &reorg_target);
|
||||
|
||||
strategy.apply_pre_execution_changes()?;
|
||||
|
||||
|
||||
@@ -15,13 +15,10 @@ workspace = true
|
||||
reth-execution-types.workspace = true
|
||||
reth-chainspec.workspace = true
|
||||
reth-ethereum-forks.workspace = true
|
||||
reth-revm.workspace = true
|
||||
revm.workspace = true
|
||||
reth-evm.workspace = true
|
||||
reth-primitives.workspace = true
|
||||
|
||||
# Ethereum
|
||||
reth-primitives-traits.workspace = true
|
||||
|
||||
# Alloy
|
||||
alloy-primitives.workspace = true
|
||||
alloy-eips.workspace = true
|
||||
@@ -32,8 +29,8 @@ alloy-consensus.workspace = true
|
||||
[dev-dependencies]
|
||||
reth-testing-utils.workspace = true
|
||||
reth-evm = { workspace = true, features = ["test-utils"] }
|
||||
reth-revm = { workspace = true, features = ["test-utils"] }
|
||||
reth-primitives = { workspace = true, features = ["secp256k1"] }
|
||||
reth-primitives-traits.workspace = true
|
||||
reth-execution-types.workspace = true
|
||||
secp256k1.workspace = true
|
||||
serde_json.workspace = true
|
||||
@@ -43,7 +40,6 @@ alloy-genesis.workspace = true
|
||||
default = ["std"]
|
||||
std = [
|
||||
"reth-primitives/std",
|
||||
"reth-revm/std",
|
||||
"alloy-consensus/std",
|
||||
"alloy-eips/std",
|
||||
"alloy-genesis/std",
|
||||
@@ -51,9 +47,11 @@ std = [
|
||||
"secp256k1/std",
|
||||
"reth-ethereum-forks/std",
|
||||
"serde_json/std",
|
||||
"reth-primitives-traits/std",
|
||||
"reth-chainspec/std",
|
||||
"alloy-evm/std",
|
||||
"reth-execution-types/std",
|
||||
"reth-evm/std",
|
||||
"reth-primitives-traits/std",
|
||||
"alloy-sol-types/std",
|
||||
"revm/std",
|
||||
]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use alloy_consensus::Header;
|
||||
use reth_chainspec::{ChainSpec, EthereumHardforks};
|
||||
use reth_ethereum_forks::EthereumHardfork;
|
||||
use reth_revm::specification::hardfork::SpecId;
|
||||
use revm::specification::hardfork::SpecId;
|
||||
|
||||
/// Map the latest active hardfork at the given header to a revm [`SpecId`].
|
||||
pub fn revm_spec(chain_spec: &ChainSpec, header: &Header) -> SpecId {
|
||||
|
||||
@@ -8,7 +8,7 @@ use alloc::{boxed::Box, sync::Arc, vec::Vec};
|
||||
use alloy_consensus::{Header, Transaction};
|
||||
use alloy_eips::{eip4895::Withdrawals, eip6110, eip7685::Requests};
|
||||
use alloy_evm::FromRecoveredTx;
|
||||
use alloy_primitives::{Address, B256};
|
||||
use alloy_primitives::B256;
|
||||
use reth_chainspec::{ChainSpec, EthereumHardfork, EthereumHardforks};
|
||||
use reth_evm::{
|
||||
execute::{
|
||||
@@ -17,14 +17,16 @@ use reth_evm::{
|
||||
},
|
||||
state_change::post_block_balance_increments,
|
||||
system_calls::{OnStateHook, StateChangePostBlockSource, StateChangeSource, SystemCaller},
|
||||
ConfigureEvm, Database, Evm, EvmEnv, EvmFactory, TransactionEnv,
|
||||
Database, Evm, EvmEnv, EvmFactory, EvmFor, InspectorFor, NextBlockEnvAttributes,
|
||||
TransactionEnv,
|
||||
};
|
||||
use reth_execution_types::BlockExecutionResult;
|
||||
use reth_primitives::{EthPrimitives, Receipt, Recovered, SealedBlock, TransactionSigned};
|
||||
use reth_primitives_traits::NodePrimitives;
|
||||
use reth_revm::{
|
||||
context_interface::result::ResultAndState, db::State, specification::hardfork::SpecId,
|
||||
DatabaseCommit,
|
||||
use reth_primitives::{
|
||||
EthPrimitives, Receipt, Recovered, SealedBlock, SealedHeader, TransactionSigned,
|
||||
};
|
||||
use revm::{
|
||||
context::result::ExecutionResult, context_interface::result::ResultAndState, database::State,
|
||||
specification::hardfork::SpecId, DatabaseCommit,
|
||||
};
|
||||
|
||||
impl<EvmF> BlockExecutionStrategyFactory for EthEvmConfig<EvmF>
|
||||
@@ -33,57 +35,60 @@ where
|
||||
+ Send
|
||||
+ Sync
|
||||
+ Unpin
|
||||
+ Clone,
|
||||
+ Clone
|
||||
+ 'static,
|
||||
{
|
||||
type Primitives = EthPrimitives;
|
||||
type ExecutionCtx<'a> = EthBlockExecutionCtx<'a>;
|
||||
type Strategy<'a, DB: Database + 'a, I: InspectorFor<&'a mut State<DB>, Self> + 'a> =
|
||||
EthExecutionStrategy<'a, EvmFor<Self, &'a mut State<DB>, I>>;
|
||||
|
||||
fn create_strategy<'a, DB>(
|
||||
&'a self,
|
||||
db: &'a mut State<DB>,
|
||||
block: &'a SealedBlock<<Self::Primitives as NodePrimitives>::Block>,
|
||||
) -> impl BlockExecutionStrategy<Primitives = Self::Primitives, Error = BlockExecutionError> + 'a
|
||||
where
|
||||
DB: Database,
|
||||
{
|
||||
let evm = self.evm_for_block(db, block.header());
|
||||
EthExecutionStrategy::new(evm, block, &self.chain_spec)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,
|
||||
fn context_for_block<'a>(&self, block: &'a SealedBlock) -> Self::ExecutionCtx<'a> {
|
||||
EthBlockExecutionCtx {
|
||||
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(),
|
||||
}
|
||||
}
|
||||
|
||||
fn context_for_next_block<'a>(
|
||||
&self,
|
||||
parent: &SealedHeader,
|
||||
attributes: NextBlockEnvAttributes<'a>,
|
||||
) -> Self::ExecutionCtx<'a> {
|
||||
EthBlockExecutionCtx {
|
||||
parent_hash: parent.hash(),
|
||||
parent_beacon_block_root: attributes.parent_beacon_block_root,
|
||||
ommers: &[],
|
||||
withdrawals: attributes.withdrawals,
|
||||
}
|
||||
}
|
||||
|
||||
fn create_strategy<'a, DB, I>(
|
||||
&'a self,
|
||||
evm: EvmFor<Self, &'a mut State<DB>, I>,
|
||||
ctx: Self::ExecutionCtx<'a>,
|
||||
) -> Self::Strategy<'a, DB, I>
|
||||
where
|
||||
DB: Database,
|
||||
I: InspectorFor<&'a mut State<DB>, Self> + 'a,
|
||||
{
|
||||
EthExecutionStrategy::new(evm, ctx, &self.chain_spec)
|
||||
}
|
||||
}
|
||||
|
||||
/// Context for Ethereum block execution.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct EthBlockExecutionCtx<'a> {
|
||||
/// Parent block hash.
|
||||
pub parent_hash: B256,
|
||||
/// Parent beacon block root.
|
||||
pub parent_beacon_block_root: Option<B256>,
|
||||
/// Block ommers
|
||||
pub ommers: &'a [Header],
|
||||
/// Block withdrawals.
|
||||
pub withdrawals: Option<&'a Withdrawals>,
|
||||
}
|
||||
|
||||
/// Block execution strategy for Ethereum.
|
||||
@@ -92,8 +97,8 @@ pub struct EthExecutionStrategy<'a, Evm> {
|
||||
/// Reference to the [`ChainSpec`].
|
||||
chain_spec: &'a ChainSpec,
|
||||
|
||||
/// Input for block execution.
|
||||
input: EthBlockExecutionInput<'a>,
|
||||
/// Context for block execution.
|
||||
ctx: EthBlockExecutionCtx<'a>,
|
||||
/// The EVM used by strategy.
|
||||
evm: Evm,
|
||||
/// Utility to call system smart contracts.
|
||||
@@ -107,15 +112,11 @@ pub struct EthExecutionStrategy<'a, Evm> {
|
||||
|
||||
impl<'a, Evm> EthExecutionStrategy<'a, Evm> {
|
||||
/// Creates a new [`EthExecutionStrategy`]
|
||||
pub fn new(
|
||||
evm: Evm,
|
||||
input: impl Into<EthBlockExecutionInput<'a>>,
|
||||
chain_spec: &'a ChainSpec,
|
||||
) -> Self {
|
||||
pub fn new(evm: Evm, ctx: EthBlockExecutionCtx<'a>, chain_spec: &'a ChainSpec) -> Self {
|
||||
Self {
|
||||
evm,
|
||||
chain_spec,
|
||||
input: input.into(),
|
||||
ctx,
|
||||
receipts: Vec::new(),
|
||||
gas_used: 0,
|
||||
system_caller: SystemCaller::new(chain_spec),
|
||||
@@ -130,27 +131,28 @@ where
|
||||
{
|
||||
type Error = BlockExecutionError;
|
||||
type Primitives = EthPrimitives;
|
||||
type Evm = E;
|
||||
|
||||
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.input.number);
|
||||
self.chain_spec.is_spurious_dragon_active_at_block(self.evm.block().number);
|
||||
self.evm.db_mut().set_state_clear_flag(state_clear_flag);
|
||||
self.system_caller.apply_blockhashes_contract_call(self.ctx.parent_hash, &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)?;
|
||||
.apply_beacon_root_contract_call(self.ctx.parent_beacon_block_root, &mut self.evm)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn execute_transaction(
|
||||
fn execute_transaction_with_result_closure(
|
||||
&mut self,
|
||||
tx: Recovered<&TransactionSigned>,
|
||||
f: impl FnOnce(&ExecutionResult<<Self::Evm as Evm>::HaltReason>),
|
||||
) -> 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.input.gas_limit - self.gas_used;
|
||||
let block_available_gas = self.evm.block().gas_limit - self.gas_used;
|
||||
if tx.gas_limit() > block_available_gas {
|
||||
return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas {
|
||||
transaction_gas_limit: tx.gas_limit(),
|
||||
@@ -169,6 +171,8 @@ where
|
||||
let ResultAndState { result, state } = result_and_state;
|
||||
self.evm.db_mut().commit(state);
|
||||
|
||||
f(&result);
|
||||
|
||||
let gas_used = result.gas_used();
|
||||
|
||||
// append gas used
|
||||
@@ -190,7 +194,8 @@ where
|
||||
fn apply_post_execution_changes(
|
||||
mut self,
|
||||
) -> Result<BlockExecutionResult<Receipt>, Self::Error> {
|
||||
let requests = if self.chain_spec.is_prague_active_at_timestamp(self.input.timestamp) {
|
||||
let requests = if self.chain_spec.is_prague_active_at_timestamp(self.evm.block().timestamp)
|
||||
{
|
||||
// Collect all EIP-6110 deposits
|
||||
let deposit_requests =
|
||||
crate::eip6110::parse_deposits_from_receipts(self.chain_spec, &self.receipts)?;
|
||||
@@ -210,12 +215,13 @@ where
|
||||
let mut balance_increments = post_block_balance_increments(
|
||||
self.chain_spec,
|
||||
self.evm.block(),
|
||||
self.input.ommers,
|
||||
self.input.withdrawals,
|
||||
self.ctx.ommers,
|
||||
self.ctx.withdrawals,
|
||||
);
|
||||
|
||||
// Irregular state change at Ethereum DAO hardfork
|
||||
if self.chain_spec.fork(EthereumHardfork::Dao).transitions_at_block(self.input.number) {
|
||||
if self.chain_spec.fork(EthereumHardfork::Dao).transitions_at_block(self.evm.block().number)
|
||||
{
|
||||
// drain balances from hardcoded addresses.
|
||||
let drained_balance: u128 = self
|
||||
.evm
|
||||
@@ -247,6 +253,10 @@ where
|
||||
fn with_state_hook(&mut self, hook: Option<Box<dyn OnStateHook>>) {
|
||||
self.system_caller.with_state_hook(hook);
|
||||
}
|
||||
|
||||
fn evm_mut(&mut self) -> &mut Self::Evm {
|
||||
&mut self.evm
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper type with backwards compatible methods to obtain Ethereum executor
|
||||
@@ -281,53 +291,46 @@ mod tests {
|
||||
use reth_chainspec::{ChainSpecBuilder, ForkCondition, MAINNET};
|
||||
use reth_evm::execute::{BasicBlockExecutorProvider, BlockExecutorProvider, Executor};
|
||||
use reth_execution_types::BlockExecutionResult;
|
||||
use reth_primitives::{Account, Block, BlockBody, RecoveredBlock, Transaction};
|
||||
use reth_primitives::{Block, BlockBody, RecoveredBlock, Transaction};
|
||||
use reth_primitives_traits::{crypto::secp256k1::public_key_to_address, Block as _};
|
||||
use reth_revm::{
|
||||
database::StateProviderDatabase,
|
||||
db::TransitionState,
|
||||
use reth_testing_utils::generators::{self, sign_tx_with_key_pair};
|
||||
use revm::{
|
||||
database::{CacheDB, EmptyDB, TransitionState},
|
||||
primitives::{address, BLOCKHASH_SERVE_WINDOW},
|
||||
state::EvmState,
|
||||
test_utils::StateProviderTest,
|
||||
state::{AccountInfo, Bytecode, EvmState},
|
||||
Database,
|
||||
};
|
||||
use reth_testing_utils::generators::{self, sign_tx_with_key_pair};
|
||||
use secp256k1::{Keypair, Secp256k1};
|
||||
use std::{collections::HashMap, sync::mpsc};
|
||||
use std::sync::mpsc;
|
||||
|
||||
fn create_state_provider_with_beacon_root_contract() -> StateProviderTest {
|
||||
let mut db = StateProviderTest::default();
|
||||
fn create_database_with_beacon_root_contract() -> CacheDB<EmptyDB> {
|
||||
let mut db = CacheDB::new(Default::default());
|
||||
|
||||
let beacon_root_contract_account = Account {
|
||||
let beacon_root_contract_account = AccountInfo {
|
||||
balance: U256::ZERO,
|
||||
bytecode_hash: Some(keccak256(BEACON_ROOTS_CODE.clone())),
|
||||
code_hash: keccak256(BEACON_ROOTS_CODE.clone()),
|
||||
nonce: 1,
|
||||
code: Some(Bytecode::new_raw(BEACON_ROOTS_CODE.clone())),
|
||||
};
|
||||
|
||||
db.insert_account(
|
||||
BEACON_ROOTS_ADDRESS,
|
||||
beacon_root_contract_account,
|
||||
Some(BEACON_ROOTS_CODE.clone()),
|
||||
HashMap::default(),
|
||||
);
|
||||
db.insert_account_info(BEACON_ROOTS_ADDRESS, beacon_root_contract_account);
|
||||
|
||||
db
|
||||
}
|
||||
|
||||
fn create_state_provider_with_withdrawal_requests_contract() -> StateProviderTest {
|
||||
let mut db = StateProviderTest::default();
|
||||
fn create_database_with_withdrawal_requests_contract() -> CacheDB<EmptyDB> {
|
||||
let mut db = CacheDB::new(Default::default());
|
||||
|
||||
let withdrawal_requests_contract_account = Account {
|
||||
let withdrawal_requests_contract_account = AccountInfo {
|
||||
nonce: 1,
|
||||
balance: U256::ZERO,
|
||||
bytecode_hash: Some(keccak256(WITHDRAWAL_REQUEST_PREDEPLOY_CODE.clone())),
|
||||
code_hash: keccak256(WITHDRAWAL_REQUEST_PREDEPLOY_CODE.clone()),
|
||||
code: Some(Bytecode::new_raw(WITHDRAWAL_REQUEST_PREDEPLOY_CODE.clone())),
|
||||
};
|
||||
|
||||
db.insert_account(
|
||||
db.insert_account_info(
|
||||
WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS,
|
||||
withdrawal_requests_contract_account,
|
||||
Some(WITHDRAWAL_REQUEST_PREDEPLOY_CODE.clone()),
|
||||
HashMap::default(),
|
||||
);
|
||||
|
||||
db
|
||||
@@ -342,7 +345,7 @@ mod tests {
|
||||
let mut header =
|
||||
Header { timestamp: 1, number: 1, excess_blob_gas: Some(0), ..Header::default() };
|
||||
|
||||
let db = create_state_provider_with_beacon_root_contract();
|
||||
let db = create_database_with_beacon_root_contract();
|
||||
|
||||
let chain_spec = Arc::new(
|
||||
ChainSpecBuilder::from(&*MAINNET)
|
||||
@@ -353,7 +356,7 @@ mod tests {
|
||||
|
||||
let provider = executor_provider(chain_spec);
|
||||
|
||||
let mut executor = provider.executor(StateProviderDatabase::new(&db));
|
||||
let mut executor = provider.executor(db);
|
||||
|
||||
// attempt to execute a block without parent beacon block root, expect err
|
||||
let err = executor
|
||||
@@ -423,7 +426,7 @@ mod tests {
|
||||
..Header::default()
|
||||
};
|
||||
|
||||
let db = StateProviderTest::default();
|
||||
let db = CacheDB::new(EmptyDB::default());
|
||||
|
||||
// DON'T deploy the contract at genesis
|
||||
let chain_spec = Arc::new(
|
||||
@@ -437,7 +440,7 @@ mod tests {
|
||||
|
||||
// attempt to execute an empty block with parent beacon block root, this should not fail
|
||||
provider
|
||||
.executor(StateProviderDatabase::new(&db))
|
||||
.executor(db)
|
||||
.execute_one(&RecoveredBlock::new_unhashed(
|
||||
Block {
|
||||
header,
|
||||
@@ -455,10 +458,10 @@ mod tests {
|
||||
// This test ensures that we do not increment the nonce of an empty SYSTEM_ADDRESS account
|
||||
// // during the pre-block call
|
||||
|
||||
let mut db = create_state_provider_with_beacon_root_contract();
|
||||
let mut db = create_database_with_beacon_root_contract();
|
||||
|
||||
// insert an empty SYSTEM_ADDRESS
|
||||
db.insert_account(SYSTEM_ADDRESS, Account::default(), None, HashMap::default());
|
||||
db.insert_account_info(SYSTEM_ADDRESS, Default::default());
|
||||
|
||||
let chain_spec = Arc::new(
|
||||
ChainSpecBuilder::from(&*MAINNET)
|
||||
@@ -478,7 +481,7 @@ mod tests {
|
||||
..Header::default()
|
||||
};
|
||||
|
||||
let mut executor = provider.executor(StateProviderDatabase::new(&db));
|
||||
let mut executor = provider.executor(db);
|
||||
|
||||
// attempt to execute an empty block with parent beacon block root, this should not fail
|
||||
executor
|
||||
@@ -501,7 +504,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn eip_4788_genesis_call() {
|
||||
let db = create_state_provider_with_beacon_root_contract();
|
||||
let db = create_database_with_beacon_root_contract();
|
||||
|
||||
// activate cancun at genesis
|
||||
let chain_spec = Arc::new(
|
||||
@@ -513,7 +516,7 @@ mod tests {
|
||||
|
||||
let mut header = chain_spec.genesis_header().clone();
|
||||
let provider = executor_provider(chain_spec);
|
||||
let mut executor = provider.executor(StateProviderDatabase::new(&db));
|
||||
let mut executor = provider.executor(db);
|
||||
|
||||
// attempt to execute the genesis block with non-zero parent beacon block root, expect err
|
||||
header.parent_beacon_block_root = Some(B256::with_last_byte(0x69));
|
||||
@@ -565,7 +568,7 @@ mod tests {
|
||||
..Header::default()
|
||||
};
|
||||
|
||||
let db = create_state_provider_with_beacon_root_contract();
|
||||
let db = create_database_with_beacon_root_contract();
|
||||
|
||||
let chain_spec = Arc::new(
|
||||
ChainSpecBuilder::from(&*MAINNET)
|
||||
@@ -577,7 +580,7 @@ mod tests {
|
||||
let provider = executor_provider(chain_spec);
|
||||
|
||||
// execute header
|
||||
let mut executor = provider.executor(StateProviderDatabase::new(&db));
|
||||
let mut executor = provider.executor(db);
|
||||
|
||||
// Now execute a block with the fixed header, ensure that it does not fail
|
||||
executor
|
||||
@@ -611,30 +614,26 @@ mod tests {
|
||||
}
|
||||
|
||||
/// Create a state provider with blockhashes and the EIP-2935 system contract.
|
||||
fn create_state_provider_with_block_hashes(latest_block: u64) -> StateProviderTest {
|
||||
let mut db = StateProviderTest::default();
|
||||
fn create_database_with_block_hashes(latest_block: u64) -> CacheDB<EmptyDB> {
|
||||
let mut db = CacheDB::new(Default::default());
|
||||
for block_number in 0..=latest_block {
|
||||
db.insert_block_hash(block_number, keccak256(block_number.to_string()));
|
||||
db.block_hashes.insert(U256::from(block_number), keccak256(block_number.to_string()));
|
||||
}
|
||||
|
||||
let blockhashes_contract_account = Account {
|
||||
let blockhashes_contract_account = AccountInfo {
|
||||
balance: U256::ZERO,
|
||||
bytecode_hash: Some(keccak256(HISTORY_STORAGE_CODE.clone())),
|
||||
code_hash: keccak256(HISTORY_STORAGE_CODE.clone()),
|
||||
code: Some(Bytecode::new_raw(HISTORY_STORAGE_CODE.clone())),
|
||||
nonce: 1,
|
||||
};
|
||||
|
||||
db.insert_account(
|
||||
HISTORY_STORAGE_ADDRESS,
|
||||
blockhashes_contract_account,
|
||||
Some(HISTORY_STORAGE_CODE.clone()),
|
||||
HashMap::default(),
|
||||
);
|
||||
db.insert_account_info(HISTORY_STORAGE_ADDRESS, blockhashes_contract_account);
|
||||
|
||||
db
|
||||
}
|
||||
#[test]
|
||||
fn eip_2935_pre_fork() {
|
||||
let db = create_state_provider_with_block_hashes(1);
|
||||
let db = create_database_with_block_hashes(1);
|
||||
|
||||
let chain_spec = Arc::new(
|
||||
ChainSpecBuilder::from(&*MAINNET)
|
||||
@@ -644,7 +643,7 @@ mod tests {
|
||||
);
|
||||
|
||||
let provider = executor_provider(chain_spec);
|
||||
let mut executor = provider.executor(StateProviderDatabase::new(&db));
|
||||
let mut executor = provider.executor(db);
|
||||
|
||||
// construct the header for block one
|
||||
let header = Header { timestamp: 1, number: 1, ..Header::default() };
|
||||
@@ -673,7 +672,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn eip_2935_fork_activation_genesis() {
|
||||
let db = create_state_provider_with_block_hashes(0);
|
||||
let db = create_database_with_block_hashes(0);
|
||||
|
||||
let chain_spec = Arc::new(
|
||||
ChainSpecBuilder::from(&*MAINNET)
|
||||
@@ -685,7 +684,7 @@ mod tests {
|
||||
|
||||
let header = chain_spec.genesis_header().clone();
|
||||
let provider = executor_provider(chain_spec);
|
||||
let mut executor = provider.executor(StateProviderDatabase::new(&db));
|
||||
let mut executor = provider.executor(db);
|
||||
|
||||
// attempt to execute genesis block, this should not fail
|
||||
executor
|
||||
@@ -712,7 +711,7 @@ mod tests {
|
||||
#[test]
|
||||
fn eip_2935_fork_activation_within_window_bounds() {
|
||||
let fork_activation_block = (BLOCKHASH_SERVE_WINDOW - 10) as u64;
|
||||
let db = create_state_provider_with_block_hashes(fork_activation_block);
|
||||
let db = create_database_with_block_hashes(fork_activation_block);
|
||||
|
||||
let chain_spec = Arc::new(
|
||||
ChainSpecBuilder::from(&*MAINNET)
|
||||
@@ -732,7 +731,7 @@ mod tests {
|
||||
..Header::default()
|
||||
};
|
||||
let provider = executor_provider(chain_spec);
|
||||
let mut executor = provider.executor(StateProviderDatabase::new(&db));
|
||||
let mut executor = provider.executor(db);
|
||||
|
||||
// attempt to execute the fork activation block, this should not fail
|
||||
executor
|
||||
@@ -765,7 +764,7 @@ mod tests {
|
||||
#[test]
|
||||
fn eip_2935_fork_activation_outside_window_bounds() {
|
||||
let fork_activation_block = (BLOCKHASH_SERVE_WINDOW + 256) as u64;
|
||||
let db = create_state_provider_with_block_hashes(fork_activation_block);
|
||||
let db = create_database_with_block_hashes(fork_activation_block);
|
||||
|
||||
let chain_spec = Arc::new(
|
||||
ChainSpecBuilder::from(&*MAINNET)
|
||||
@@ -776,7 +775,7 @@ mod tests {
|
||||
);
|
||||
|
||||
let provider = executor_provider(chain_spec);
|
||||
let mut executor = provider.executor(StateProviderDatabase::new(&db));
|
||||
let mut executor = provider.executor(db);
|
||||
|
||||
let header = Header {
|
||||
parent_hash: B256::random(),
|
||||
@@ -805,7 +804,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn eip_2935_state_transition_inside_fork() {
|
||||
let db = create_state_provider_with_block_hashes(2);
|
||||
let db = create_database_with_block_hashes(2);
|
||||
|
||||
let chain_spec = Arc::new(
|
||||
ChainSpecBuilder::from(&*MAINNET)
|
||||
@@ -819,7 +818,7 @@ mod tests {
|
||||
let header_hash = header.hash_slow();
|
||||
|
||||
let provider = executor_provider(chain_spec);
|
||||
let mut executor = provider.executor(StateProviderDatabase::new(&db));
|
||||
let mut executor = provider.executor(db);
|
||||
|
||||
// attempt to execute the genesis block, this should not fail
|
||||
executor
|
||||
@@ -927,17 +926,15 @@ mod tests {
|
||||
.build(),
|
||||
);
|
||||
|
||||
let mut db = create_state_provider_with_withdrawal_requests_contract();
|
||||
let mut db = create_database_with_withdrawal_requests_contract();
|
||||
|
||||
let secp = Secp256k1::new();
|
||||
let sender_key_pair = Keypair::new(&secp, &mut generators::rng());
|
||||
let sender_address = public_key_to_address(sender_key_pair.public_key());
|
||||
|
||||
db.insert_account(
|
||||
db.insert_account_info(
|
||||
sender_address,
|
||||
Account { nonce: 1, balance: U256::from(ETH_TO_WEI), bytecode_hash: None },
|
||||
None,
|
||||
HashMap::default(),
|
||||
AccountInfo { nonce: 1, balance: U256::from(ETH_TO_WEI), ..Default::default() },
|
||||
);
|
||||
|
||||
// https://github.com/lightclient/sys-asm/blob/9282bdb9fd64e024e27f60f507486ffb2183cba2/test/Withdrawal.t.sol.in#L36
|
||||
@@ -969,7 +966,7 @@ mod tests {
|
||||
|
||||
let provider = executor_provider(chain_spec);
|
||||
|
||||
let mut executor = provider.executor(StateProviderDatabase::new(&db));
|
||||
let mut executor = provider.executor(db);
|
||||
|
||||
let BlockExecutionResult { receipts, requests, .. } = executor
|
||||
.execute_one(
|
||||
@@ -998,7 +995,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Create a state provider with the withdrawal requests contract pre-deployed
|
||||
let mut db = create_state_provider_with_withdrawal_requests_contract();
|
||||
let mut db = create_database_with_withdrawal_requests_contract();
|
||||
|
||||
// Initialize Secp256k1 for key pair generation
|
||||
let secp = Secp256k1::new();
|
||||
@@ -1008,11 +1005,9 @@ mod tests {
|
||||
let sender_address = public_key_to_address(sender_key_pair.public_key());
|
||||
|
||||
// Insert the sender account into the state with a nonce of 1 and a balance of 1 ETH in Wei
|
||||
db.insert_account(
|
||||
db.insert_account_info(
|
||||
sender_address,
|
||||
Account { nonce: 1, balance: U256::from(ETH_TO_WEI), bytecode_hash: None },
|
||||
None,
|
||||
HashMap::default(),
|
||||
AccountInfo { nonce: 1, balance: U256::from(ETH_TO_WEI), ..Default::default() },
|
||||
);
|
||||
|
||||
// Define the validator public key and withdrawal amount as fixed bytes
|
||||
@@ -1045,7 +1040,7 @@ mod tests {
|
||||
);
|
||||
|
||||
// Create an executor from the state provider
|
||||
let mut executor = executor_provider(chain_spec).executor(StateProviderDatabase::new(&db));
|
||||
let mut executor = executor_provider(chain_spec).executor(db);
|
||||
|
||||
// Execute the block and capture the result
|
||||
let exec_result = executor.execute_one(
|
||||
@@ -1079,13 +1074,11 @@ mod tests {
|
||||
|
||||
let withdrawal_recipient = address!("1000000000000000000000000000000000000000");
|
||||
|
||||
let mut db = StateProviderTest::default();
|
||||
let mut db = CacheDB::new(EmptyDB::default());
|
||||
let initial_balance = 100;
|
||||
db.insert_account(
|
||||
db.insert_account_info(
|
||||
withdrawal_recipient,
|
||||
Account { balance: U256::from(initial_balance), nonce: 1, bytecode_hash: None },
|
||||
None,
|
||||
HashMap::default(),
|
||||
AccountInfo { balance: U256::from(initial_balance), nonce: 1, ..Default::default() },
|
||||
);
|
||||
|
||||
let withdrawal =
|
||||
@@ -1112,7 +1105,7 @@ mod tests {
|
||||
);
|
||||
|
||||
let provider = executor_provider(chain_spec);
|
||||
let executor = provider.executor(StateProviderDatabase::new(&db));
|
||||
let executor = provider.executor(db);
|
||||
|
||||
let (tx, rx) = mpsc::channel();
|
||||
let tx_clone = tx.clone();
|
||||
|
||||
@@ -28,7 +28,7 @@ use reth_evm::{
|
||||
ConfigureEvm, ConfigureEvmEnv, EvmEnv, EvmFactory, NextBlockEnvAttributes, TransactionEnv,
|
||||
};
|
||||
use reth_primitives::TransactionSigned;
|
||||
use reth_revm::{
|
||||
use revm::{
|
||||
context::{BlockEnv, CfgEnv},
|
||||
context_interface::block::BlobExcessGasAndPrice,
|
||||
specification::hardfork::SpecId,
|
||||
@@ -123,7 +123,7 @@ where
|
||||
fn next_evm_env(
|
||||
&self,
|
||||
parent: &Self::Header,
|
||||
attributes: NextBlockEnvAttributes,
|
||||
attributes: NextBlockEnvAttributes<'_>,
|
||||
) -> Result<EvmEnv, Self::Error> {
|
||||
// ensure we're not missing any timestamp based hardforks
|
||||
let spec_id = revm_spec_by_timestamp_and_block_number(
|
||||
@@ -204,10 +204,10 @@ mod tests {
|
||||
use alloy_genesis::Genesis;
|
||||
use reth_chainspec::{Chain, ChainSpec};
|
||||
use reth_evm::{execute::ProviderError, EvmEnv};
|
||||
use reth_revm::{
|
||||
use revm::{
|
||||
context::{BlockEnv, CfgEnv},
|
||||
database::CacheDB,
|
||||
database_interface::EmptyDBTyped,
|
||||
db::CacheDB,
|
||||
inspector::NoOpInspector,
|
||||
};
|
||||
|
||||
|
||||
@@ -17,12 +17,12 @@ use reth_basic_payload_builder::{
|
||||
};
|
||||
use reth_chainspec::{ChainSpec, ChainSpecProvider, EthChainSpec, EthereumHardforks};
|
||||
use reth_errors::{BlockExecutionError, BlockValidationError};
|
||||
use reth_ethereum_primitives::{Block, BlockBody, TransactionSigned};
|
||||
use reth_evm::{execute::BlockExecutionStrategy, ConfigureEvm, NextBlockEnvAttributes};
|
||||
use reth_evm_ethereum::{
|
||||
execute::{EthBlockExecutionInput, EthExecutionStrategy},
|
||||
EthEvmConfig,
|
||||
use reth_ethereum_primitives::{Block, BlockBody, EthPrimitives, TransactionSigned};
|
||||
use reth_evm::{
|
||||
execute::{BlockExecutionStrategy, BlockExecutionStrategyFactory},
|
||||
Evm, NextBlockEnvAttributes,
|
||||
};
|
||||
use reth_evm_ethereum::EthEvmConfig;
|
||||
use reth_execution_types::{BlockExecutionResult, ExecutionOutcome};
|
||||
use reth_payload_builder::{EthBuiltPayload, EthPayloadBuilderAttributes};
|
||||
use reth_payload_builder_primitives::PayloadBuilderError;
|
||||
@@ -81,7 +81,7 @@ impl<Pool, Client, EvmConfig> EthereumPayloadBuilder<Pool, Client, EvmConfig> {
|
||||
// Default implementation of [PayloadBuilder] for unit type
|
||||
impl<Pool, Client, EvmConfig> PayloadBuilder for EthereumPayloadBuilder<Pool, Client, EvmConfig>
|
||||
where
|
||||
EvmConfig: ConfigureEvm<Header = Header, Transaction = TransactionSigned>,
|
||||
EvmConfig: BlockExecutionStrategyFactory<Primitives = EthPrimitives>,
|
||||
Client: StateProviderFactory + ChainSpecProvider<ChainSpec = ChainSpec> + Clone,
|
||||
Pool: TransactionPool<Transaction: PoolTransaction<Consensus = TransactionSigned>>,
|
||||
{
|
||||
@@ -136,7 +136,7 @@ pub fn default_ethereum_payload<EvmConfig, Client, Pool, F>(
|
||||
best_txs: F,
|
||||
) -> Result<BuildOutcome<EthBuiltPayload>, PayloadBuilderError>
|
||||
where
|
||||
EvmConfig: ConfigureEvm<Header = Header, Transaction = TransactionSigned>,
|
||||
EvmConfig: BlockExecutionStrategyFactory<Primitives = EthPrimitives>,
|
||||
Client: StateProviderFactory + ChainSpecProvider<ChainSpec = ChainSpec>,
|
||||
Pool: TransactionPool<Transaction: PoolTransaction<Consensus = TransactionSigned>>,
|
||||
F: FnOnce(BestTransactionsAttributes) -> BestTransactionsIter<Pool>,
|
||||
@@ -154,43 +154,31 @@ where
|
||||
suggested_fee_recipient: attributes.suggested_fee_recipient(),
|
||||
prev_randao: attributes.prev_randao(),
|
||||
gas_limit: builder_config.gas_limit(parent_header.gas_limit),
|
||||
parent_beacon_block_root: attributes.parent_beacon_block_root(),
|
||||
withdrawals: Some(attributes.withdrawals()),
|
||||
};
|
||||
let evm_env = evm_config
|
||||
.next_evm_env(&parent_header, next_attributes)
|
||||
|
||||
let mut strategy = evm_config
|
||||
.strategy_for_next_block(&mut db, &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");
|
||||
let mut cumulative_gas_used = 0;
|
||||
let block_gas_limit: u64 = evm_env.block_env.gas_limit;
|
||||
let base_fee = evm_env.block_env.basefee;
|
||||
let block_gas_limit: u64 = strategy.evm_mut().block().gas_limit;
|
||||
let base_fee = strategy.evm_mut().block().basefee;
|
||||
|
||||
let mut executed_txs = Vec::new();
|
||||
|
||||
let mut best_txs = best_txs(BestTransactionsAttributes::new(
|
||||
base_fee,
|
||||
evm_env.block_env.blob_gasprice().map(|gasprice| gasprice as u64),
|
||||
strategy.evm_mut().block().blob_gasprice().map(|gasprice| gasprice as u64),
|
||||
));
|
||||
let mut total_fees = U256::ZERO;
|
||||
|
||||
let block_number = evm_env.block_env.number;
|
||||
let beneficiary = evm_env.block_env.beneficiary;
|
||||
|
||||
let mut strategy = EthExecutionStrategy::new(
|
||||
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,
|
||||
);
|
||||
let block_number = strategy.evm_mut().block().number;
|
||||
let beneficiary = strategy.evm_mut().block().beneficiary;
|
||||
|
||||
strategy.apply_pre_execution_changes().map_err(|err| {
|
||||
warn!(target: "payload_builder", %err, "failed to apply pre-execution changes");
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
//! Traits for execution.
|
||||
|
||||
use alloy_consensus::BlockHeader;
|
||||
use alloy_evm::Evm;
|
||||
// Re-export execution types
|
||||
use crate::{system_calls::OnStateHook, ConfigureEvmFor, Database};
|
||||
use crate::{
|
||||
system_calls::OnStateHook, ConfigureEvmFor, Database, EvmFor, InspectorFor,
|
||||
NextBlockEnvAttributes,
|
||||
};
|
||||
use alloc::{boxed::Box, vec::Vec};
|
||||
use alloy_primitives::{
|
||||
map::{DefaultHashBuilder, HashMap},
|
||||
@@ -13,9 +17,15 @@ pub use reth_execution_errors::{
|
||||
};
|
||||
use reth_execution_types::BlockExecutionResult;
|
||||
pub use reth_execution_types::{BlockExecutionOutput, ExecutionOutcome};
|
||||
use reth_primitives::{NodePrimitives, Receipt, Recovered, RecoveredBlock, SealedBlock};
|
||||
use reth_primitives::{
|
||||
NodePrimitives, Receipt, Recovered, RecoveredBlock, SealedBlock, SealedHeader,
|
||||
};
|
||||
pub use reth_storage_errors::provider::ProviderError;
|
||||
use revm::state::{Account, AccountStatus, EvmState};
|
||||
use revm::{
|
||||
context::result::ExecutionResult,
|
||||
inspector::NoOpInspector,
|
||||
state::{Account, AccountStatus, EvmState},
|
||||
};
|
||||
use revm_database::{states::bundle_state::BundleRetention, State};
|
||||
|
||||
/// A type that knows how to execute a block. It is assumed to operate on a
|
||||
@@ -163,10 +173,23 @@ pub struct ExecuteOutput<R = Receipt> {
|
||||
}
|
||||
|
||||
/// Defines the strategy for executing a single block.
|
||||
///
|
||||
/// The current abstraction assumes that block execution consists of the following steps:
|
||||
/// 1. Apply pre-execution changes. Those might include system calls, irregular state transitions
|
||||
/// (DAO fork), etc.
|
||||
/// 2. Apply block transactions to the state.
|
||||
/// 3. Apply post-execution changes and finalize the state. This might include other system calls,
|
||||
/// block rewards, etc.
|
||||
///
|
||||
/// The output of [`BlockExecutionStrategy::apply_post_execution_changes`] is a
|
||||
/// [`BlockExecutionResult`] which contains all relevant information about the block execution.
|
||||
pub trait BlockExecutionStrategy {
|
||||
/// Primitive types used by the strategy.
|
||||
type Primitives: NodePrimitives;
|
||||
|
||||
/// EVM used by the strategy.
|
||||
type Evm: Evm;
|
||||
|
||||
/// The error type returned by this strategy's methods.
|
||||
type Error: core::error::Error;
|
||||
|
||||
@@ -179,6 +202,16 @@ pub trait BlockExecutionStrategy {
|
||||
fn execute_transaction(
|
||||
&mut self,
|
||||
tx: Recovered<&<Self::Primitives as NodePrimitives>::SignedTx>,
|
||||
) -> Result<u64, Self::Error> {
|
||||
self.execute_transaction_with_result_closure(tx, |_| ())
|
||||
}
|
||||
|
||||
/// Executes a single transaction and applies execution result to internal state. Invokes the
|
||||
/// given closure with an internal [`ExecutionResult`] produced by the EVM.
|
||||
fn execute_transaction_with_result_closure(
|
||||
&mut self,
|
||||
tx: Recovered<&<Self::Primitives as NodePrimitives>::SignedTx>,
|
||||
f: impl FnOnce(&ExecutionResult<<Self::Evm as Evm>::HaltReason>),
|
||||
) -> Result<u64, Self::Error>;
|
||||
|
||||
/// Applies any necessary changes after executing the block's transactions.
|
||||
@@ -188,21 +221,89 @@ pub trait BlockExecutionStrategy {
|
||||
|
||||
/// Sets a hook to be called after each state change during execution.
|
||||
fn with_state_hook(&mut self, hook: Option<Box<dyn OnStateHook>>);
|
||||
|
||||
/// Exposes mutable reference to EVM.
|
||||
fn evm_mut(&mut self) -> &mut Self::Evm;
|
||||
}
|
||||
|
||||
/// A strategy factory that can create block execution strategies.
|
||||
pub trait BlockExecutionStrategyFactory: ConfigureEvmFor<Self::Primitives> {
|
||||
/// A factory that can create block execution strategies.
|
||||
///
|
||||
/// This trait extends [`crate::ConfigureEvm`] and provides a way to construct a
|
||||
/// [`BlockExecutionStrategy`]. Strategy is expected to derive most of the context for block
|
||||
/// execution from the EVM (which includes [`revm::context::BlockEnv`]), and any additional context
|
||||
/// should be contained in configured [`ExecutionCtx`].
|
||||
///
|
||||
/// Strategy is required to provide a way to obtain [`ExecutionCtx`] from either a complete
|
||||
/// [`SealedBlock`] (in case of execution of an externally obtained block), or from a parent header
|
||||
/// along with [`NextBlockEnvAttributes`] (in the case of block building).
|
||||
///
|
||||
/// For more context on the strategy design, see the documentation for [`BlockExecutionStrategy`].
|
||||
///
|
||||
/// [`ExecutionCtx`]: BlockExecutionStrategyFactory::ExecutionCtx
|
||||
pub trait BlockExecutionStrategyFactory: ConfigureEvmFor<Self::Primitives> + 'static {
|
||||
/// Primitive types used by the strategy.
|
||||
type Primitives: NodePrimitives;
|
||||
|
||||
/// Creates a strategy using the given database.
|
||||
fn create_strategy<'a, DB>(
|
||||
/// Strategy this factory produces.
|
||||
type Strategy<'a, DB: Database + 'a, I: InspectorFor<&'a mut State<DB>, Self> + 'a>: BlockExecutionStrategy<
|
||||
Primitives = Self::Primitives,
|
||||
Error = BlockExecutionError,
|
||||
Evm = EvmFor<Self, &'a mut State<DB>, I>,
|
||||
>;
|
||||
|
||||
/// Context required for block execution.
|
||||
///
|
||||
/// This is similar to [`alloy_evm::EvmEnv`], but only contains context unrelated to EVM and
|
||||
/// required for execution of an entire block.
|
||||
type ExecutionCtx<'a>;
|
||||
|
||||
/// Returns the configured [`BlockExecutionStrategyFactory::ExecutionCtx`] for a given block.
|
||||
fn context_for_block<'a>(
|
||||
&self,
|
||||
block: &'a SealedBlock<<Self::Primitives as NodePrimitives>::Block>,
|
||||
) -> Self::ExecutionCtx<'a>;
|
||||
|
||||
/// Returns the configured [`BlockExecutionStrategyFactory::ExecutionCtx`] for `parent + 1`
|
||||
/// block.
|
||||
fn context_for_next_block<'a>(
|
||||
&self,
|
||||
parent: &SealedHeader<<Self::Primitives as NodePrimitives>::BlockHeader>,
|
||||
attributes: NextBlockEnvAttributes<'a>,
|
||||
) -> Self::ExecutionCtx<'a>;
|
||||
|
||||
/// Creates a strategy with given EVM and execution context.
|
||||
fn create_strategy<'a, DB, I>(
|
||||
&'a self,
|
||||
evm: EvmFor<Self, &'a mut State<DB>, I>,
|
||||
ctx: Self::ExecutionCtx<'a>,
|
||||
) -> Self::Strategy<'a, DB, I>
|
||||
where
|
||||
DB: Database,
|
||||
I: InspectorFor<&'a mut State<DB>, Self> + 'a;
|
||||
|
||||
/// Creates a strategy for execution of a given block.
|
||||
fn strategy_for_block<'a, DB: Database>(
|
||||
&'a self,
|
||||
db: &'a mut State<DB>,
|
||||
block: &'a SealedBlock<<Self::Primitives as NodePrimitives>::Block>,
|
||||
) -> impl BlockExecutionStrategy<Primitives = Self::Primitives, Error = BlockExecutionError> + 'a
|
||||
where
|
||||
DB: Database;
|
||||
) -> Self::Strategy<'a, DB, NoOpInspector> {
|
||||
let evm = self.evm_for_block(db, block.header());
|
||||
let ctx = self.context_for_block(block);
|
||||
self.create_strategy(evm, ctx)
|
||||
}
|
||||
|
||||
/// Creates a strategy for execution of a next block.
|
||||
fn strategy_for_next_block<'a, DB: Database>(
|
||||
&'a self,
|
||||
db: &'a mut State<DB>,
|
||||
parent: &'a SealedHeader<<Self::Primitives as NodePrimitives>::BlockHeader>,
|
||||
attributes: NextBlockEnvAttributes<'a>,
|
||||
) -> Result<Self::Strategy<'a, DB, NoOpInspector>, Self::Error> {
|
||||
let evm_env = self.next_evm_env(parent, attributes)?;
|
||||
let evm = self.evm_with_env(db, evm_env);
|
||||
let ctx = self.context_for_next_block(parent, attributes);
|
||||
Ok(self.create_strategy(evm, ctx))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> Clone for BasicBlockExecutorProvider<F>
|
||||
@@ -275,7 +376,7 @@ where
|
||||
block: &RecoveredBlock<<Self::Primitives as NodePrimitives>::Block>,
|
||||
) -> Result<BlockExecutionResult<<Self::Primitives as NodePrimitives>::Receipt>, Self::Error>
|
||||
{
|
||||
let mut strategy = self.strategy_factory.create_strategy(&mut self.db, block);
|
||||
let mut strategy = self.strategy_factory.strategy_for_block(&mut self.db, block);
|
||||
|
||||
strategy.apply_pre_execution_changes()?;
|
||||
for tx in block.transactions_recovered() {
|
||||
@@ -296,7 +397,7 @@ where
|
||||
where
|
||||
H: OnStateHook + 'static,
|
||||
{
|
||||
let mut strategy = self.strategy_factory.create_strategy(&mut self.db, block);
|
||||
let mut strategy = self.strategy_factory.strategy_for_block(&mut self.db, block);
|
||||
strategy.with_state_hook(Some(Box::new(state_hook)));
|
||||
|
||||
strategy.apply_pre_execution_changes()?;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use alloy_eips::eip2930::AccessList;
|
||||
use alloy_eips::{eip2930::AccessList, eip4895::Withdrawals};
|
||||
pub use alloy_evm::evm::EvmFactory;
|
||||
use alloy_evm::{FromRecoveredTx, IntoTxEnv};
|
||||
use alloy_primitives::{Address, B256};
|
||||
@@ -177,7 +177,7 @@ pub trait ConfigureEvmEnv: Send + Sync + Unpin + Clone {
|
||||
fn next_evm_env(
|
||||
&self,
|
||||
parent: &Self::Header,
|
||||
attributes: NextBlockEnvAttributes,
|
||||
attributes: NextBlockEnvAttributes<'_>,
|
||||
) -> Result<EvmEnv<Self::Spec>, Self::Error>;
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ pub trait ConfigureEvmEnv: Send + Sync + Unpin + Clone {
|
||||
/// [`ConfigureEvmEnv::next_evm_env`] and contains fields that can't be derived from the
|
||||
/// parent header alone (attributes that are determined by the CL.)
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct NextBlockEnvAttributes {
|
||||
pub struct NextBlockEnvAttributes<'a> {
|
||||
/// The timestamp of the next block.
|
||||
pub timestamp: u64,
|
||||
/// The suggested fee recipient for the next block.
|
||||
@@ -195,6 +195,10 @@ pub struct NextBlockEnvAttributes {
|
||||
pub prev_randao: B256,
|
||||
/// Block gas limit.
|
||||
pub gas_limit: u64,
|
||||
/// The parent beacon block root.
|
||||
pub parent_beacon_block_root: Option<B256>,
|
||||
/// Withdrawals
|
||||
pub withdrawals: Option<&'a Withdrawals>,
|
||||
}
|
||||
|
||||
/// Abstraction over transaction environment.
|
||||
|
||||
@@ -6,7 +6,7 @@ use reth_basic_payload_builder::PayloadBuilder;
|
||||
use reth_consensus::{ConsensusError, FullConsensus};
|
||||
use reth_db_api::{database_metrics::DatabaseMetrics, Database};
|
||||
use reth_engine_primitives::{BeaconConsensusEngineEvent, BeaconConsensusEngineHandle};
|
||||
use reth_evm::{execute::BlockExecutorProvider, ConfigureEvmFor};
|
||||
use reth_evm::execute::{BlockExecutionStrategyFactory, BlockExecutorProvider};
|
||||
use reth_network_api::FullNetwork;
|
||||
use reth_node_core::node_config::NodeConfig;
|
||||
use reth_node_types::{NodeTypes, NodeTypesWithDBAdapter, NodeTypesWithEngine, TxTy};
|
||||
@@ -68,7 +68,7 @@ pub trait FullNodeComponents: FullNodeTypes + Clone + 'static {
|
||||
type Pool: TransactionPool<Transaction: PoolTransaction<Consensus = TxTy<Self::Types>>> + Unpin;
|
||||
|
||||
/// The node's EVM configuration, defining settings for the Ethereum Virtual Machine.
|
||||
type Evm: ConfigureEvmFor<<Self::Types as NodeTypes>::Primitives>;
|
||||
type Evm: BlockExecutionStrategyFactory<Primitives = <Self::Types as NodeTypes>::Primitives>;
|
||||
|
||||
/// The type that knows how to execute blocks.
|
||||
type Executor: BlockExecutorProvider<Primitives = <Self::Types as NodeTypes>::Primitives>;
|
||||
|
||||
@@ -18,67 +18,67 @@ use reth_evm::{
|
||||
},
|
||||
state_change::post_block_balance_increments,
|
||||
system_calls::{OnStateHook, StateChangePostBlockSource, StateChangeSource, SystemCaller},
|
||||
ConfigureEvm, Database, Evm,
|
||||
Database, Evm, EvmFor, InspectorFor, NextBlockEnvAttributes,
|
||||
};
|
||||
use reth_execution_types::BlockExecutionResult;
|
||||
use reth_optimism_chainspec::OpChainSpec;
|
||||
use reth_optimism_forks::OpHardforks;
|
||||
use reth_optimism_primitives::{transaction::signed::OpTransaction, DepositReceipt};
|
||||
use reth_primitives_traits::{Block, NodePrimitives, SealedBlock, SignedTransaction};
|
||||
use reth_primitives_traits::{NodePrimitives, SealedBlock, SealedHeader, SignedTransaction};
|
||||
use revm::{context::TxEnv, context_interface::result::ResultAndState, DatabaseCommit};
|
||||
use revm_database::State;
|
||||
use revm_primitives::{Address, B256};
|
||||
use revm_primitives::B256;
|
||||
use tracing::trace;
|
||||
|
||||
impl<ChainSpec, N> BlockExecutionStrategyFactory for OpEvmConfig<ChainSpec, N>
|
||||
where
|
||||
ChainSpec: EthChainSpec + OpHardforks,
|
||||
ChainSpec: EthChainSpec + OpHardforks + 'static,
|
||||
N: NodePrimitives<SignedTx: OpTransaction, Receipt: DepositReceipt>,
|
||||
revm_optimism::OpTransaction<TxEnv>: FromRecoveredTx<N::SignedTx>,
|
||||
{
|
||||
type Primitives = N;
|
||||
type Strategy<'a, DB: Database + 'a, I: InspectorFor<&'a mut State<DB>, Self> + 'a> =
|
||||
OpExecutionStrategy<'a, EvmFor<Self, &'a mut State<DB>, I>, N, &'a ChainSpec>;
|
||||
type ExecutionCtx<'a> = OpBlockExecutionCtx;
|
||||
|
||||
fn create_strategy<'a, DB>(
|
||||
&'a self,
|
||||
db: &'a mut State<DB>,
|
||||
block: &'a SealedBlock<<Self::Primitives as NodePrimitives>::Block>,
|
||||
) -> impl BlockExecutionStrategy<Primitives = Self::Primitives, Error = BlockExecutionError> + 'a
|
||||
where
|
||||
DB: Database,
|
||||
{
|
||||
let evm = self.evm_for_block(db, block.header());
|
||||
OpExecutionStrategy::new(evm, block, &self.chain_spec, 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(),
|
||||
fn context_for_block<'a>(&self, block: &'a SealedBlock<N::Block>) -> Self::ExecutionCtx<'a> {
|
||||
OpBlockExecutionCtx {
|
||||
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(),
|
||||
}
|
||||
}
|
||||
|
||||
fn context_for_next_block<'a>(
|
||||
&self,
|
||||
parent: &SealedHeader<N::BlockHeader>,
|
||||
attributes: NextBlockEnvAttributes<'a>,
|
||||
) -> Self::ExecutionCtx<'a> {
|
||||
OpBlockExecutionCtx {
|
||||
parent_hash: parent.hash(),
|
||||
parent_beacon_block_root: attributes.parent_beacon_block_root,
|
||||
}
|
||||
}
|
||||
|
||||
fn create_strategy<'a, DB, I>(
|
||||
&'a self,
|
||||
evm: EvmFor<Self, &'a mut State<DB>, I>,
|
||||
ctx: Self::ExecutionCtx<'a>,
|
||||
) -> Self::Strategy<'a, DB, I>
|
||||
where
|
||||
DB: Database,
|
||||
I: reth_evm::InspectorFor<&'a mut State<DB>, Self> + 'a,
|
||||
{
|
||||
OpExecutionStrategy::new(evm, ctx, self.chain_spec.as_ref(), self.receipt_builder.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
/// Context for OP block execution.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct OpBlockExecutionCtx {
|
||||
/// Parent block hash.
|
||||
pub parent_hash: B256,
|
||||
/// Parent beacon block root.
|
||||
pub parent_beacon_block_root: Option<B256>,
|
||||
}
|
||||
|
||||
/// Block execution strategy for Optimism.
|
||||
@@ -89,8 +89,8 @@ pub struct OpExecutionStrategy<'a, E: Evm, N: NodePrimitives, ChainSpec> {
|
||||
/// Receipt builder.
|
||||
receipt_builder: &'a dyn OpReceiptBuilder<N::SignedTx, E::HaltReason, Receipt = N::Receipt>,
|
||||
|
||||
/// Input for block execution.
|
||||
input: OpBlockExecutionInput,
|
||||
/// Context for block execution.
|
||||
ctx: OpBlockExecutionCtx,
|
||||
/// The EVM used by strategy.
|
||||
evm: E,
|
||||
/// Receipts of executed transactions.
|
||||
@@ -112,20 +112,19 @@ where
|
||||
/// Creates a new [`OpExecutionStrategy`]
|
||||
pub fn new(
|
||||
evm: E,
|
||||
input: impl Into<OpBlockExecutionInput>,
|
||||
ctx: OpBlockExecutionCtx,
|
||||
chain_spec: ChainSpec,
|
||||
receipt_builder: &'a dyn OpReceiptBuilder<N::SignedTx, E::HaltReason, Receipt = N::Receipt>,
|
||||
) -> Self {
|
||||
let input = input.into();
|
||||
Self {
|
||||
is_regolith: chain_spec.is_regolith_active_at_timestamp(input.timestamp),
|
||||
is_regolith: chain_spec.is_regolith_active_at_timestamp(evm.block().timestamp),
|
||||
evm,
|
||||
system_caller: SystemCaller::new(chain_spec.clone()),
|
||||
chain_spec,
|
||||
receipt_builder,
|
||||
receipts: Vec::new(),
|
||||
gas_used: 0,
|
||||
input,
|
||||
ctx,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,30 +138,39 @@ where
|
||||
{
|
||||
type Primitives = N;
|
||||
type Error = BlockExecutionError;
|
||||
type Evm = E;
|
||||
|
||||
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.input.number);
|
||||
self.chain_spec.is_spurious_dragon_active_at_block(self.evm.block().number);
|
||||
self.evm.db_mut().set_state_clear_flag(state_clear_flag);
|
||||
|
||||
self.system_caller
|
||||
.apply_beacon_root_contract_call(self.input.parent_beacon_block_root, &mut self.evm)?;
|
||||
.apply_beacon_root_contract_call(self.ctx.parent_beacon_block_root, &mut self.evm)?;
|
||||
|
||||
// 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.
|
||||
ensure_create2_deployer(self.chain_spec.clone(), self.input.timestamp, self.evm.db_mut())
|
||||
.map_err(|_| OpBlockExecutionError::ForceCreate2DeployerFail)?;
|
||||
ensure_create2_deployer(
|
||||
self.chain_spec.clone(),
|
||||
self.evm.block().timestamp,
|
||||
self.evm.db_mut(),
|
||||
)
|
||||
.map_err(|_| OpBlockExecutionError::ForceCreate2DeployerFail)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn execute_transaction(&mut self, tx: Recovered<&N::SignedTx>) -> Result<u64, Self::Error> {
|
||||
fn execute_transaction_with_result_closure(
|
||||
&mut self,
|
||||
tx: Recovered<&<Self::Primitives as NodePrimitives>::SignedTx>,
|
||||
f: impl FnOnce(&revm::context::result::ExecutionResult<<Self::Evm as Evm>::HaltReason>),
|
||||
) -> 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.input.gas_limit - self.gas_used;
|
||||
let block_available_gas = self.evm.block().gas_limit - self.gas_used;
|
||||
if tx.gas_limit() > block_available_gas && (self.is_regolith || !tx.is_deposit()) {
|
||||
return Err(BlockValidationError::TransactionGasLimitMoreThanAvailableBlockGas {
|
||||
transaction_gas_limit: tx.gas_limit(),
|
||||
@@ -202,6 +210,8 @@ where
|
||||
let ResultAndState { result, state } = result_and_state;
|
||||
self.evm.db_mut().commit(state);
|
||||
|
||||
f(&result);
|
||||
|
||||
let gas_used = result.gas_used();
|
||||
|
||||
// append gas used
|
||||
@@ -232,7 +242,8 @@ where
|
||||
// this is only set for post-Canyon deposit
|
||||
// transactions.
|
||||
deposit_receipt_version: (tx.is_deposit() &&
|
||||
self.chain_spec.is_canyon_active_at_timestamp(self.input.timestamp))
|
||||
self.chain_spec
|
||||
.is_canyon_active_at_timestamp(self.evm.block().timestamp))
|
||||
.then_some(1),
|
||||
})
|
||||
}
|
||||
@@ -270,6 +281,10 @@ where
|
||||
fn with_state_hook(&mut self, hook: Option<Box<dyn OnStateHook>>) {
|
||||
self.system_caller.with_state_hook(hook);
|
||||
}
|
||||
|
||||
fn evm_mut(&mut self) -> &mut Self::Evm {
|
||||
&mut self.evm
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper type with backwards compatible methods to obtain executor providers.
|
||||
|
||||
@@ -132,7 +132,7 @@ where
|
||||
fn next_evm_env(
|
||||
&self,
|
||||
parent: &Self::Header,
|
||||
attributes: NextBlockEnvAttributes,
|
||||
attributes: NextBlockEnvAttributes<'_>,
|
||||
) -> Result<EvmEnv<Self::Spec>, Self::Error> {
|
||||
// ensure we're not missing any timestamp based hardforks
|
||||
let spec_id = revm_spec_by_timestamp_after_bedrock(&self.chain_spec, attributes.timestamp);
|
||||
|
||||
@@ -19,12 +19,15 @@ use reth_basic_payload_builder::*;
|
||||
use reth_chain_state::{ExecutedBlock, ExecutedBlockWithTrieUpdates};
|
||||
use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks};
|
||||
use reth_evm::{
|
||||
execute::{BlockExecutionError, BlockExecutionStrategy, BlockValidationError},
|
||||
ConfigureEvm, ConfigureEvmFor, Database, EvmEnv, HaltReasonFor, NextBlockEnvAttributes,
|
||||
execute::{
|
||||
BlockExecutionError, BlockExecutionStrategy, BlockExecutionStrategyFactory,
|
||||
BlockValidationError,
|
||||
},
|
||||
ConfigureEvm, ConfigureEvmFor, Database, Evm, HaltReasonFor, NextBlockEnvAttributes,
|
||||
};
|
||||
use reth_execution_types::ExecutionOutcome;
|
||||
use reth_optimism_consensus::calculate_receipt_root_no_memo_optimism;
|
||||
use reth_optimism_evm::{OpBlockExecutionInput, OpExecutionStrategy, OpReceiptBuilder};
|
||||
use reth_optimism_evm::OpReceiptBuilder;
|
||||
use reth_optimism_forks::OpHardforks;
|
||||
use reth_optimism_primitives::transaction::signed::OpTransaction;
|
||||
use reth_optimism_storage::predeploys;
|
||||
@@ -44,7 +47,7 @@ use reth_revm::{
|
||||
witness::ExecutionWitnessRecord,
|
||||
};
|
||||
use reth_transaction_pool::{BestTransactionsAttributes, PoolTransaction, TransactionPool};
|
||||
use revm::context_interface::Block;
|
||||
use revm::context::{Block, BlockEnv};
|
||||
use std::sync::Arc;
|
||||
use tracing::{debug, trace, warn};
|
||||
|
||||
@@ -160,7 +163,7 @@ where
|
||||
Pool: TransactionPool<Transaction: PoolTransaction<Consensus = N::SignedTx>>,
|
||||
Client: StateProviderFactory + ChainSpecProvider<ChainSpec: EthChainSpec + OpHardforks>,
|
||||
N: OpPayloadPrimitives,
|
||||
EvmConfig: ConfigureEvmFor<N>,
|
||||
EvmConfig: BlockExecutionStrategyFactory<Primitives = N>,
|
||||
{
|
||||
/// Constructs an Optimism payload from the transactions sent via the
|
||||
/// Payload attributes by the sequencer. If the `no_tx_pool` argument is passed in
|
||||
@@ -178,10 +181,6 @@ where
|
||||
where
|
||||
Txs: PayloadTransactions<Transaction: PoolTransaction<Consensus = N::SignedTx>>,
|
||||
{
|
||||
let evm_env = self
|
||||
.evm_env(&args.config.attributes, &args.config.parent_header)
|
||||
.map_err(PayloadBuilderError::other)?;
|
||||
|
||||
let BuildArguments { mut cached_reads, config, cancel, best_payload } = args;
|
||||
|
||||
let ctx = OpPayloadBuilderCtx {
|
||||
@@ -189,7 +188,6 @@ where
|
||||
da_config: self.config.da_config.clone(),
|
||||
chain_spec: self.client.chain_spec(),
|
||||
config,
|
||||
evm_env,
|
||||
cancel,
|
||||
best_payload,
|
||||
receipt_builder: self.receipt_builder.clone(),
|
||||
@@ -214,22 +212,6 @@ where
|
||||
.map(|out| out.with_cached_reads(cached_reads))
|
||||
}
|
||||
|
||||
/// Returns the configured [`EvmEnv`] for the targeted payload
|
||||
/// (that has the `parent` as its parent).
|
||||
pub fn evm_env(
|
||||
&self,
|
||||
attributes: &OpPayloadBuilderAttributes<N::SignedTx>,
|
||||
parent: &Header,
|
||||
) -> Result<EvmEnv<EvmConfig::Spec>, 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_evm_env(parent, next_attributes)
|
||||
}
|
||||
|
||||
/// Computes the witness for the payload.
|
||||
pub fn payload_witness(
|
||||
&self,
|
||||
@@ -239,15 +221,12 @@ where
|
||||
let attributes = OpPayloadBuilderAttributes::try_new(parent.hash(), attributes, 3)
|
||||
.map_err(PayloadBuilderError::other)?;
|
||||
|
||||
let evm_env = self.evm_env(&attributes, &parent).map_err(PayloadBuilderError::other)?;
|
||||
|
||||
let config = PayloadConfig { parent_header: Arc::new(parent), attributes };
|
||||
let ctx: OpPayloadBuilderCtx<EvmConfig, Client::ChainSpec, N> = OpPayloadBuilderCtx {
|
||||
evm_config: self.evm_config.clone(),
|
||||
da_config: self.config.da_config.clone(),
|
||||
chain_spec: self.client.chain_spec(),
|
||||
config,
|
||||
evm_env,
|
||||
cancel: Default::default(),
|
||||
best_payload: Default::default(),
|
||||
receipt_builder: self.receipt_builder.clone(),
|
||||
@@ -269,7 +248,7 @@ where
|
||||
Client: StateProviderFactory + ChainSpecProvider<ChainSpec: EthChainSpec + OpHardforks> + Clone,
|
||||
N: OpPayloadPrimitives,
|
||||
Pool: TransactionPool<Transaction: PoolTransaction<Consensus = N::SignedTx>>,
|
||||
EvmConfig: ConfigureEvmFor<N>,
|
||||
EvmConfig: BlockExecutionStrategyFactory<Primitives = N>,
|
||||
Txs: OpPayloadTransactions<Pool::Transaction>,
|
||||
{
|
||||
type Attributes = OpPayloadBuilderAttributes<N::SignedTx>;
|
||||
@@ -348,7 +327,7 @@ impl<Txs> OpBuilder<'_, Txs> {
|
||||
where
|
||||
N: OpPayloadPrimitives,
|
||||
Txs: PayloadTransactions<Transaction: PoolTransaction<Consensus = N::SignedTx>>,
|
||||
EvmConfig: ConfigureEvmFor<N>,
|
||||
EvmConfig: BlockExecutionStrategyFactory<Primitives = N>,
|
||||
ChainSpec: EthChainSpec + OpHardforks,
|
||||
DB: Database<Error = ProviderError> + AsRef<P>,
|
||||
P: StorageRootProvider,
|
||||
@@ -356,19 +335,23 @@ impl<Txs> OpBuilder<'_, Txs> {
|
||||
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");
|
||||
|
||||
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.receipt_builder.as_ref(),
|
||||
);
|
||||
let mut strategy = ctx
|
||||
.evm_config
|
||||
.strategy_for_next_block(
|
||||
&mut *state,
|
||||
ctx.parent(),
|
||||
NextBlockEnvAttributes {
|
||||
timestamp: ctx.attributes().timestamp(),
|
||||
suggested_fee_recipient: ctx.attributes().suggested_fee_recipient(),
|
||||
prev_randao: ctx.attributes().prev_randao(),
|
||||
gas_limit: ctx.attributes().gas_limit.unwrap_or(ctx.parent().gas_limit),
|
||||
parent_beacon_block_root: ctx.attributes().parent_beacon_block_root(),
|
||||
withdrawals: None,
|
||||
},
|
||||
)
|
||||
.map_err(PayloadBuilderError::other)?;
|
||||
|
||||
let block_env = strategy.evm_mut().block().clone();
|
||||
|
||||
// 1. apply pre-execution changes
|
||||
strategy.apply_pre_execution_changes().map_err(|err| {
|
||||
@@ -381,7 +364,7 @@ impl<Txs> OpBuilder<'_, Txs> {
|
||||
|
||||
// 3. if mem pool transactions are requested we execute them
|
||||
if !ctx.attributes().no_tx_pool {
|
||||
let best_txs = best(ctx.best_transaction_attributes());
|
||||
let best_txs = best(ctx.best_transaction_attributes(strategy.evm_mut().block()));
|
||||
if ctx.execute_best_transactions(&mut info, &mut strategy, best_txs)?.is_some() {
|
||||
return Ok(BuildOutcomeKind::Cancelled)
|
||||
}
|
||||
@@ -411,7 +394,7 @@ impl<Txs> OpBuilder<'_, Txs> {
|
||||
None
|
||||
};
|
||||
|
||||
let payload = ExecutedPayload { receipts, info, withdrawals_root };
|
||||
let payload = ExecutedPayload { receipts, info, withdrawals_root, block_env };
|
||||
|
||||
Ok(BuildOutcomeKind::Better { payload })
|
||||
}
|
||||
@@ -423,21 +406,21 @@ impl<Txs> OpBuilder<'_, Txs> {
|
||||
ctx: OpPayloadBuilderCtx<EvmConfig, ChainSpec, N>,
|
||||
) -> Result<BuildOutcomeKind<OpBuiltPayload<N>>, PayloadBuilderError>
|
||||
where
|
||||
EvmConfig: ConfigureEvmFor<N>,
|
||||
EvmConfig: BlockExecutionStrategyFactory<Primitives = N>,
|
||||
ChainSpec: EthChainSpec + OpHardforks,
|
||||
N: OpPayloadPrimitives,
|
||||
Txs: PayloadTransactions<Transaction: PoolTransaction<Consensus = N::SignedTx>>,
|
||||
DB: Database<Error = ProviderError> + AsRef<P>,
|
||||
P: StateRootProvider + HashedPostStateProvider + StorageRootProvider,
|
||||
{
|
||||
let ExecutedPayload { receipts, info, withdrawals_root } =
|
||||
let ExecutedPayload { receipts, info, withdrawals_root, block_env } =
|
||||
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 block_number = block_env.number;
|
||||
let execution_outcome =
|
||||
ExecutionOutcome::new(state.take_bundle(), vec![receipts], block_number, Vec::new());
|
||||
let receipts_root = execution_outcome
|
||||
@@ -477,7 +460,7 @@ impl<Txs> OpBuilder<'_, Txs> {
|
||||
let header = Header {
|
||||
parent_hash: ctx.parent().hash(),
|
||||
ommers_hash: EMPTY_OMMER_ROOT_HASH,
|
||||
beneficiary: ctx.evm_env.block_env.beneficiary,
|
||||
beneficiary: block_env.beneficiary,
|
||||
state_root,
|
||||
transactions_root,
|
||||
receipts_root,
|
||||
@@ -486,9 +469,9 @@ impl<Txs> OpBuilder<'_, Txs> {
|
||||
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()),
|
||||
base_fee_per_gas: Some(block_env.basefee),
|
||||
number: ctx.parent().number + 1,
|
||||
gas_limit: ctx.block_gas_limit(),
|
||||
gas_limit: block_env.gas_limit,
|
||||
difficulty: U256::ZERO,
|
||||
gas_used: info.cumulative_gas_used,
|
||||
extra_data,
|
||||
@@ -546,7 +529,7 @@ impl<Txs> OpBuilder<'_, Txs> {
|
||||
ctx: &OpPayloadBuilderCtx<EvmConfig, ChainSpec, N>,
|
||||
) -> Result<ExecutionWitness, PayloadBuilderError>
|
||||
where
|
||||
EvmConfig: ConfigureEvmFor<N>,
|
||||
EvmConfig: BlockExecutionStrategyFactory<Primitives = N>,
|
||||
ChainSpec: EthChainSpec + OpHardforks,
|
||||
N: OpPayloadPrimitives,
|
||||
Txs: PayloadTransactions<Transaction: PoolTransaction<Consensus = N::SignedTx>>,
|
||||
@@ -591,6 +574,8 @@ pub struct ExecutedPayload<N: NodePrimitives> {
|
||||
pub withdrawals_root: Option<B256>,
|
||||
/// The transaction receipts.
|
||||
pub receipts: Vec<N::Receipt>,
|
||||
/// The block env used during execution.
|
||||
pub block_env: BlockEnv,
|
||||
}
|
||||
|
||||
/// This acts as the container for executed transactions and its byproducts (receipts, gas used)
|
||||
@@ -658,8 +643,6 @@ pub struct OpPayloadBuilderCtx<EvmConfig: ConfigureEvm, ChainSpec, N: NodePrimit
|
||||
pub chain_spec: Arc<ChainSpec>,
|
||||
/// How to build the payload.
|
||||
pub config: PayloadConfig<OpPayloadBuilderAttributes<N::SignedTx>>,
|
||||
/// Evm Settings
|
||||
pub evm_env: EvmEnv<EvmConfig::Spec>,
|
||||
/// Marker to check whether the job has been cancelled.
|
||||
pub cancel: CancelOnDrop,
|
||||
/// The currently best payload.
|
||||
@@ -692,26 +675,6 @@ where
|
||||
.then(|| &self.attributes().payload_attributes.withdrawals)
|
||||
}
|
||||
|
||||
/// Returns the block gas limit to target.
|
||||
pub fn block_gas_limit(&self) -> u64 {
|
||||
self.attributes().gas_limit.unwrap_or(self.evm_env.block_env.gas_limit)
|
||||
}
|
||||
|
||||
/// Returns the block number for the block.
|
||||
pub fn block_number(&self) -> u64 {
|
||||
self.evm_env.block_env.number
|
||||
}
|
||||
|
||||
/// Returns the current base fee
|
||||
pub fn base_fee(&self) -> u64 {
|
||||
self.evm_env.block_env.basefee
|
||||
}
|
||||
|
||||
/// Returns the current blob gas price.
|
||||
pub fn get_blob_gasprice(&self) -> Option<u64> {
|
||||
self.evm_env.block_env.blob_gasprice().map(|gasprice| gasprice as u64)
|
||||
}
|
||||
|
||||
/// Returns the blob fields for the header.
|
||||
///
|
||||
/// This will always return `Some(0)` after ecotone.
|
||||
@@ -744,8 +707,11 @@ where
|
||||
}
|
||||
|
||||
/// Returns the current fee settings for transactions from the mempool
|
||||
pub fn best_transaction_attributes(&self) -> BestTransactionsAttributes {
|
||||
BestTransactionsAttributes::new(self.base_fee(), self.get_blob_gasprice())
|
||||
pub fn best_transaction_attributes(&self, block_env: &BlockEnv) -> BestTransactionsAttributes {
|
||||
BestTransactionsAttributes::new(
|
||||
block_env.basefee,
|
||||
block_env.blob_gasprice().map(|p| p as u64),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the unique id for this payload job.
|
||||
@@ -855,10 +821,10 @@ where
|
||||
Transaction: PoolTransaction<Consensus = EvmConfig::Transaction>,
|
||||
>,
|
||||
) -> Result<Option<()>, PayloadBuilderError> {
|
||||
let block_gas_limit = self.block_gas_limit();
|
||||
let block_gas_limit = strategy.evm_mut().block().gas_limit;
|
||||
let block_da_limit = self.da_config.max_da_block_size();
|
||||
let tx_da_limit = self.da_config.max_da_tx_size();
|
||||
let base_fee = self.base_fee();
|
||||
let base_fee = strategy.evm_mut().block().basefee;
|
||||
|
||||
while let Some(tx) = best_txs.next(()) {
|
||||
let tx = tx.into_consensus();
|
||||
|
||||
@@ -3,20 +3,20 @@
|
||||
use crate::OpEthApi;
|
||||
use alloy_consensus::{
|
||||
constants::EMPTY_WITHDRAWALS, proofs::calculate_transaction_root, transaction::Recovered,
|
||||
Eip658Value, Header, Transaction as _, TxReceipt, EMPTY_OMMER_ROOT_HASH,
|
||||
Header, Transaction as _, TxReceipt, EMPTY_OMMER_ROOT_HASH,
|
||||
};
|
||||
use alloy_eips::{eip7685::EMPTY_REQUESTS_HASH, merge::BEACON_NONCE, BlockNumberOrTag};
|
||||
use alloy_primitives::{B256, U256};
|
||||
use op_alloy_consensus::{OpDepositReceipt, OpTxType};
|
||||
use reth_chainspec::{EthChainSpec, EthereumHardforks};
|
||||
use reth_evm::{ConfigureEvm, HaltReasonFor};
|
||||
use reth_evm::execute::BlockExecutionStrategyFactory;
|
||||
use reth_node_api::NodePrimitives;
|
||||
use reth_optimism_consensus::calculate_receipt_root_no_memo_optimism;
|
||||
use reth_optimism_forks::OpHardforks;
|
||||
use reth_optimism_primitives::{OpBlock, OpReceipt, OpTransactionSigned};
|
||||
use reth_primitives::{logs_bloom, BlockBody, RecoveredBlock};
|
||||
use reth_primitives::{logs_bloom, BlockBody, RecoveredBlock, SealedHeader};
|
||||
use reth_provider::{
|
||||
BlockReader, BlockReaderIdExt, ChainSpecProvider, ProviderBlock, ProviderHeader,
|
||||
ProviderReceipt, ProviderTx, ReceiptProvider, StateProviderFactory,
|
||||
BlockExecutionResult, BlockReader, BlockReaderIdExt, ChainSpecProvider, ProviderBlock,
|
||||
ProviderHeader, ProviderReceipt, ProviderTx, ReceiptProvider, StateProviderFactory,
|
||||
};
|
||||
use reth_rpc_eth_api::{
|
||||
helpers::{LoadPendingBlock, SpawnBlocking},
|
||||
@@ -25,10 +25,7 @@ use reth_rpc_eth_api::{
|
||||
};
|
||||
use reth_rpc_eth_types::{EthApiError, PendingBlock};
|
||||
use reth_transaction_pool::{PoolTransaction, TransactionPool};
|
||||
use revm::{
|
||||
context::BlockEnv,
|
||||
context_interface::{result::ExecutionResult, Block},
|
||||
};
|
||||
use revm::{context::BlockEnv, context_interface::Block};
|
||||
|
||||
impl<N> LoadPendingBlock for OpEthApi<N>
|
||||
where
|
||||
@@ -48,9 +45,12 @@ where
|
||||
> + ChainSpecProvider<ChainSpec: EthChainSpec + OpHardforks>
|
||||
+ StateProviderFactory,
|
||||
Pool: TransactionPool<Transaction: PoolTransaction<Consensus = ProviderTx<N::Provider>>>,
|
||||
Evm: ConfigureEvm<
|
||||
Header = ProviderHeader<Self::Provider>,
|
||||
Transaction = ProviderTx<Self::Provider>,
|
||||
Evm: BlockExecutionStrategyFactory<
|
||||
Primitives: NodePrimitives<
|
||||
SignedTx = ProviderTx<Self::Provider>,
|
||||
BlockHeader = ProviderHeader<Self::Provider>,
|
||||
Receipt = ProviderReceipt<Self::Provider>,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
{
|
||||
@@ -98,25 +98,25 @@ where
|
||||
fn assemble_block(
|
||||
&self,
|
||||
block_env: &BlockEnv,
|
||||
parent_hash: B256,
|
||||
result: &BlockExecutionResult<ProviderReceipt<Self::Provider>>,
|
||||
parent: &SealedHeader<ProviderHeader<Self::Provider>>,
|
||||
state_root: B256,
|
||||
transactions: Vec<Recovered<ProviderTx<Self::Provider>>>,
|
||||
receipts: &[ProviderReceipt<Self::Provider>],
|
||||
) -> reth_provider::ProviderBlock<Self::Provider> {
|
||||
let chain_spec = self.provider().chain_spec();
|
||||
let timestamp = block_env.timestamp;
|
||||
|
||||
let transactions_root = calculate_transaction_root(&transactions);
|
||||
let receipts_root =
|
||||
calculate_receipt_root_no_memo_optimism(receipts, &chain_spec, timestamp);
|
||||
calculate_receipt_root_no_memo_optimism(&result.receipts, &chain_spec, timestamp);
|
||||
|
||||
let logs_bloom = logs_bloom(receipts.iter().flat_map(|r| r.logs()));
|
||||
let logs_bloom = logs_bloom(result.receipts.iter().flat_map(|r| r.logs()));
|
||||
let is_cancun = chain_spec.is_cancun_active_at_timestamp(timestamp);
|
||||
let is_prague = chain_spec.is_prague_active_at_timestamp(timestamp);
|
||||
let is_shanghai = chain_spec.is_shanghai_active_at_timestamp(timestamp);
|
||||
|
||||
let header = Header {
|
||||
parent_hash,
|
||||
parent_hash: parent.hash(),
|
||||
ommers_hash: EMPTY_OMMER_ROOT_HASH,
|
||||
beneficiary: block_env.beneficiary,
|
||||
state_root,
|
||||
@@ -131,7 +131,7 @@ where
|
||||
number: block_env.number,
|
||||
gas_limit: block_env.gas_limit,
|
||||
difficulty: U256::ZERO,
|
||||
gas_used: receipts.last().map(|r| r.cumulative_gas_used()).unwrap_or_default(),
|
||||
gas_used: result.gas_used,
|
||||
blob_gas_used: is_cancun.then(|| {
|
||||
transactions.iter().map(|tx| tx.blob_gas_used().unwrap_or_default()).sum::<u64>()
|
||||
}),
|
||||
@@ -151,29 +151,4 @@ where
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn assemble_receipt(
|
||||
&self,
|
||||
tx: &ProviderTx<Self::Provider>,
|
||||
result: ExecutionResult<HaltReasonFor<N::Evm>>,
|
||||
cumulative_gas_used: u64,
|
||||
) -> reth_provider::ProviderReceipt<Self::Provider> {
|
||||
let receipt = alloy_consensus::Receipt {
|
||||
status: Eip658Value::Eip658(result.is_success()),
|
||||
cumulative_gas_used,
|
||||
logs: result.into_logs().into_iter().collect(),
|
||||
};
|
||||
|
||||
match tx.tx_type() {
|
||||
OpTxType::Legacy => OpReceipt::Legacy(receipt),
|
||||
OpTxType::Eip2930 => OpReceipt::Eip2930(receipt),
|
||||
OpTxType::Eip1559 => OpReceipt::Eip1559(receipt),
|
||||
OpTxType::Eip7702 => OpReceipt::Eip7702(receipt),
|
||||
OpTxType::Deposit => OpReceipt::Deposit(OpDepositReceipt {
|
||||
inner: receipt,
|
||||
deposit_nonce: None,
|
||||
deposit_receipt_version: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use alloy_rpc_types_debug::ExecutionWitness;
|
||||
use jsonrpsee_core::{async_trait, RpcResult};
|
||||
use op_alloy_rpc_types_engine::OpPayloadAttributes;
|
||||
use reth_chainspec::ChainSpecProvider;
|
||||
use reth_evm::{ConfigureEvm, ConfigureEvmFor};
|
||||
use reth_evm::{execute::BlockExecutionStrategyFactory, ConfigureEvm};
|
||||
use reth_node_api::NodePrimitives;
|
||||
use reth_optimism_chainspec::OpChainSpec;
|
||||
use reth_optimism_payload_builder::{OpPayloadBuilder, OpPayloadPrimitives};
|
||||
@@ -69,7 +69,7 @@ where
|
||||
+ ChainSpecProvider<ChainSpec = OpChainSpec>
|
||||
+ Clone
|
||||
+ 'static,
|
||||
EvmConfig: ConfigureEvmFor<Provider::Primitives> + 'static,
|
||||
EvmConfig: BlockExecutionStrategyFactory<Primitives = Provider::Primitives> + 'static,
|
||||
{
|
||||
async fn execute_payload(
|
||||
&self,
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::{
|
||||
helpers::estimate::EstimateCall, FromEvmError, FullEthApiTypes, RpcBlock, RpcNodeCore,
|
||||
};
|
||||
use alloy_consensus::BlockHeader;
|
||||
use alloy_eips::{eip1559::calc_next_block_base_fee, eip2930::AccessListResult};
|
||||
use alloy_eips::eip2930::AccessListResult;
|
||||
use alloy_primitives::{Bytes, B256, U256};
|
||||
use alloy_rpc_types_eth::{
|
||||
simulate::{SimBlock, SimulatePayload, SimulatedBlock},
|
||||
@@ -15,20 +15,23 @@ use alloy_rpc_types_eth::{
|
||||
BlockId, Bundle, EthCallResponse, StateContext, TransactionInfo,
|
||||
};
|
||||
use futures::Future;
|
||||
use reth_chainspec::EthChainSpec;
|
||||
use reth_errors::ProviderError;
|
||||
use reth_errors::{ProviderError, RethError};
|
||||
use reth_evm::{
|
||||
ConfigureEvm, ConfigureEvmEnv, Evm, EvmEnv, HaltReasonFor, InspectorFor, SpecFor,
|
||||
TransactionEnv,
|
||||
execute::BlockExecutionStrategyFactory, ConfigureEvm, ConfigureEvmEnv, Evm, EvmEnv,
|
||||
HaltReasonFor, InspectorFor, SpecFor, TransactionEnv,
|
||||
};
|
||||
use reth_node_api::BlockBody;
|
||||
use reth_primitives::Recovered;
|
||||
use reth_primitives::{Recovered, SealedHeader};
|
||||
use reth_primitives_traits::SignedTransaction;
|
||||
use reth_provider::{BlockIdReader, ChainSpecProvider, ProviderHeader};
|
||||
use reth_revm::{database::StateProviderDatabase, db::CacheDB, DatabaseRef};
|
||||
use reth_provider::{BlockIdReader, ProviderHeader};
|
||||
use reth_revm::{
|
||||
database::StateProviderDatabase,
|
||||
db::{CacheDB, State},
|
||||
DatabaseRef,
|
||||
};
|
||||
use reth_rpc_eth_types::{
|
||||
cache::db::{StateCacheDbRefMutWrapper, StateProviderTraitObjWrapper},
|
||||
error::{api::FromEvmHalt, ensure_success},
|
||||
error::{api::FromEvmHalt, ensure_success, FromEthApiError},
|
||||
revm_utils::{apply_block_overrides, apply_state_overrides, caller_gas_allowance},
|
||||
simulate::{self, EthSimulateError},
|
||||
EthApiError, RevertError, RpcInvalidTransactionError, StateCacheDb,
|
||||
@@ -74,6 +77,8 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA
|
||||
return Err(EthApiError::InvalidParams("too many blocks.".to_string()).into())
|
||||
}
|
||||
|
||||
let block = block.unwrap_or_default();
|
||||
|
||||
let SimulatePayload {
|
||||
block_state_calls,
|
||||
trace_transfers,
|
||||
@@ -85,50 +90,32 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA
|
||||
return Err(EthApiError::InvalidParams(String::from("calls are empty.")).into())
|
||||
}
|
||||
|
||||
// Build cfg and block env, we'll reuse those.
|
||||
let (mut evm_env, block) = self.evm_env_at(block.unwrap_or_default()).await?;
|
||||
|
||||
// Gas cap for entire operation
|
||||
let total_gas_limit = self.call_gas_limit();
|
||||
|
||||
let base_block =
|
||||
self.block_with_senders(block).await?.ok_or(EthApiError::HeaderNotFound(block))?;
|
||||
let mut parent_hash = base_block.hash();
|
||||
|
||||
// Only enforce base fee if validation is enabled
|
||||
evm_env.cfg_env.disable_base_fee = !validation;
|
||||
// Always disable EIP-3607
|
||||
evm_env.cfg_env.disable_eip3607 = true;
|
||||
let mut parent = base_block.sealed_header().clone();
|
||||
|
||||
let this = self.clone();
|
||||
self.spawn_with_state_at_block(block, move |state| {
|
||||
let mut db = CacheDB::new(StateProviderDatabase::new(state));
|
||||
let mut db =
|
||||
State::builder().with_database(StateProviderDatabase::new(state)).build();
|
||||
let mut gas_used = 0;
|
||||
let mut blocks: Vec<SimulatedBlock<RpcBlock<Self::NetworkTypes>>> =
|
||||
Vec::with_capacity(block_state_calls.len());
|
||||
let mut block_state_calls = block_state_calls.into_iter().peekable();
|
||||
let chain_spec = RpcNodeCore::provider(&this).chain_spec();
|
||||
while let Some(block) = block_state_calls.next() {
|
||||
// Increase number and timestamp for every new block
|
||||
evm_env.block_env.number += 1;
|
||||
evm_env.block_env.timestamp += 1;
|
||||
for block in block_state_calls {
|
||||
let mut evm_env = this
|
||||
.evm_config()
|
||||
.next_evm_env(&parent, this.next_env_attributes(&parent)?)
|
||||
.map_err(RethError::other)
|
||||
.map_err(Self::Error::from_eth_err)?;
|
||||
|
||||
if validation {
|
||||
let base_fee_params =
|
||||
chain_spec.base_fee_params_at_timestamp(evm_env.block_env.timestamp);
|
||||
let base_fee = if let Some(latest) = blocks.last() {
|
||||
let header = &latest.inner.header;
|
||||
calc_next_block_base_fee(
|
||||
header.gas_used(),
|
||||
header.gas_limit(),
|
||||
header.base_fee_per_gas().unwrap_or_default(),
|
||||
base_fee_params,
|
||||
)
|
||||
} else {
|
||||
base_block.next_block_base_fee(base_fee_params).unwrap_or_default()
|
||||
};
|
||||
evm_env.block_env.basefee = base_fee;
|
||||
} else {
|
||||
// Always disable EIP-3607
|
||||
evm_env.cfg_env.disable_eip3607 = true;
|
||||
|
||||
if !validation {
|
||||
evm_env.cfg_env.disable_base_fee = !validation;
|
||||
evm_env.block_env.basefee = 0;
|
||||
}
|
||||
|
||||
@@ -141,6 +128,9 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA
|
||||
apply_state_overrides(state_overrides, &mut db)?;
|
||||
}
|
||||
|
||||
let block_env = evm_env.block_env.clone();
|
||||
let chain_id = evm_env.cfg_env.chain_id;
|
||||
|
||||
if (total_gas_limit - gas_used) < evm_env.block_env.gas_limit {
|
||||
return Err(
|
||||
EthApiError::Other(Box::new(EthSimulateError::GasLimitReached)).into()
|
||||
@@ -152,7 +142,7 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA
|
||||
let txs_without_gas_limit =
|
||||
calls.iter().filter(|tx| tx.gas.is_none()).count();
|
||||
|
||||
if total_specified_gas > evm_env.block_env.gas_limit {
|
||||
if total_specified_gas > block_env.gas_limit {
|
||||
return Err(EthApiError::Other(Box::new(
|
||||
EthSimulateError::BlockGasLimitExceeded,
|
||||
))
|
||||
@@ -160,78 +150,68 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA
|
||||
}
|
||||
|
||||
if txs_without_gas_limit > 0 {
|
||||
(evm_env.block_env.gas_limit - total_specified_gas) /
|
||||
(block_env.gas_limit - total_specified_gas) /
|
||||
txs_without_gas_limit as u64
|
||||
} else {
|
||||
0
|
||||
}
|
||||
};
|
||||
|
||||
let mut calls = calls.into_iter().peekable();
|
||||
let mut transactions = Vec::with_capacity(calls.len());
|
||||
let mut senders = Vec::with_capacity(calls.len());
|
||||
let mut results = Vec::with_capacity(calls.len());
|
||||
|
||||
while let Some(call) = calls.next() {
|
||||
// Resolve transaction, populate missing fields and enforce calls
|
||||
// correctness.
|
||||
let tx = simulate::resolve_transaction(
|
||||
call,
|
||||
let ctx = this
|
||||
.evm_config()
|
||||
.context_for_next_block(&parent, this.next_env_attributes(&parent)?);
|
||||
let (transactions, result, results) = if trace_transfers {
|
||||
// prepare inspector to capture transfer inside the evm so they are recorded
|
||||
// and included in logs
|
||||
let inspector = TransferInspector::new(false).with_logs(true);
|
||||
let evm = this
|
||||
.evm_config()
|
||||
.evm_with_env_and_inspector(&mut db, evm_env, inspector);
|
||||
let strategy = this.evm_config().create_strategy(evm, ctx);
|
||||
simulate::execute_transactions(
|
||||
strategy,
|
||||
calls,
|
||||
validation,
|
||||
default_gas_limit,
|
||||
evm_env.cfg_env.chain_id,
|
||||
&mut db,
|
||||
chain_id,
|
||||
this.tx_resp_builder(),
|
||||
)?;
|
||||
)?
|
||||
} else {
|
||||
let evm = this.evm_config().evm_with_env(&mut db, evm_env);
|
||||
let strategy = this.evm_config().create_strategy(evm, ctx);
|
||||
simulate::execute_transactions(
|
||||
strategy,
|
||||
calls,
|
||||
validation,
|
||||
default_gas_limit,
|
||||
chain_id,
|
||||
this.tx_resp_builder(),
|
||||
)?
|
||||
};
|
||||
|
||||
let tx_env = this.evm_config().tx_env(&tx);
|
||||
let senders = transactions.iter().map(|tx| tx.signer()).collect();
|
||||
|
||||
let (res, (_, tx_env)) = {
|
||||
if trace_transfers {
|
||||
this.transact_with_inspector(
|
||||
&mut db,
|
||||
evm_env.clone(),
|
||||
tx_env,
|
||||
TransferInspector::new(false)
|
||||
// capture transfer inside the evm so they are recorded and
|
||||
// included in the result
|
||||
.with_logs(true),
|
||||
)?
|
||||
} else {
|
||||
this.transact(&mut db, evm_env.clone(), tx_env.clone())?
|
||||
}
|
||||
};
|
||||
|
||||
if calls.peek().is_some() || block_state_calls.peek().is_some() {
|
||||
// need to apply the state changes of this call before executing the
|
||||
// next call
|
||||
db.commit(res.state);
|
||||
}
|
||||
|
||||
transactions.push(tx);
|
||||
senders.push(tx_env.caller());
|
||||
results.push(res.result);
|
||||
}
|
||||
|
||||
let (block, _) = this.assemble_block_and_receipts(
|
||||
&evm_env.block_env,
|
||||
parent_hash,
|
||||
let block = this.assemble_block(
|
||||
&block_env,
|
||||
&result,
|
||||
&parent,
|
||||
// state root calculation is skipped for performance reasons
|
||||
B256::ZERO,
|
||||
transactions,
|
||||
results.clone(),
|
||||
);
|
||||
|
||||
let block: SimulatedBlock<RpcBlock<Self::NetworkTypes>> =
|
||||
simulate::build_simulated_block(
|
||||
senders,
|
||||
results,
|
||||
return_full_transactions,
|
||||
this.tx_resp_builder(),
|
||||
block,
|
||||
)?;
|
||||
let block = simulate::build_simulated_block(
|
||||
senders,
|
||||
results,
|
||||
return_full_transactions,
|
||||
this.tx_resp_builder(),
|
||||
block,
|
||||
)?;
|
||||
|
||||
parent_hash = block.inner.header.hash;
|
||||
parent = SealedHeader::new(
|
||||
block.inner.header.inner.clone(),
|
||||
block.inner.header.hash,
|
||||
);
|
||||
gas_used += block.inner.header.gas_used();
|
||||
|
||||
blocks.push(block);
|
||||
|
||||
@@ -9,17 +9,18 @@ use alloy_primitives::B256;
|
||||
use alloy_rpc_types_eth::BlockNumberOrTag;
|
||||
use futures::Future;
|
||||
use reth_chainspec::{EthChainSpec, EthereumHardforks};
|
||||
use reth_errors::RethError;
|
||||
use reth_errors::{BlockExecutionError, BlockValidationError, RethError};
|
||||
use reth_evm::{
|
||||
state_change::post_block_withdrawals_balance_increments, system_calls::SystemCaller,
|
||||
ConfigureEvm, ConfigureEvmEnv, Evm, EvmEnv, EvmError, HaltReasonFor, InvalidTxError,
|
||||
NextBlockEnvAttributes,
|
||||
execute::{BlockExecutionStrategy, BlockExecutionStrategyFactory},
|
||||
ConfigureEvmEnv, Evm, NextBlockEnvAttributes,
|
||||
};
|
||||
use reth_primitives::{InvalidTransactionError, RecoveredBlock};
|
||||
use reth_node_api::NodePrimitives;
|
||||
use reth_primitives::{InvalidTransactionError, RecoveredBlock, SealedHeader};
|
||||
use reth_primitives_traits::Receipt;
|
||||
use reth_provider::{
|
||||
BlockReader, BlockReaderIdExt, ChainSpecProvider, ProviderBlock, ProviderError, ProviderHeader,
|
||||
ProviderReceipt, ProviderTx, ReceiptProvider, StateProviderFactory,
|
||||
BlockExecutionResult, BlockReader, BlockReaderIdExt, ChainSpecProvider, ProviderBlock,
|
||||
ProviderError, ProviderHeader, ProviderReceipt, ProviderTx, ReceiptProvider,
|
||||
StateProviderFactory,
|
||||
};
|
||||
use reth_revm::{
|
||||
database::StateProviderDatabase,
|
||||
@@ -30,13 +31,7 @@ use reth_transaction_pool::{
|
||||
error::InvalidPoolTransactionError, BestTransactionsAttributes, PoolTransaction,
|
||||
TransactionPool,
|
||||
};
|
||||
use revm::{
|
||||
context::BlockEnv,
|
||||
context_interface::{
|
||||
result::{ExecutionResult, ResultAndState},
|
||||
Block,
|
||||
},
|
||||
};
|
||||
use revm::{context::BlockEnv, context_interface::Block};
|
||||
use std::time::{Duration, Instant};
|
||||
use tokio::sync::Mutex;
|
||||
use tracing::debug;
|
||||
@@ -55,9 +50,12 @@ pub trait LoadPendingBlock:
|
||||
+ ChainSpecProvider<ChainSpec: EthChainSpec + EthereumHardforks>
|
||||
+ StateProviderFactory,
|
||||
Pool: TransactionPool<Transaction: PoolTransaction<Consensus = ProviderTx<Self::Provider>>>,
|
||||
Evm: ConfigureEvm<
|
||||
Header = ProviderHeader<Self::Provider>,
|
||||
Transaction = ProviderTx<Self::Provider>,
|
||||
Evm: BlockExecutionStrategyFactory<
|
||||
Primitives: NodePrimitives<
|
||||
BlockHeader = ProviderHeader<Self::Provider>,
|
||||
SignedTx = ProviderTx<Self::Provider>,
|
||||
Receipt = ProviderReceipt<Self::Provider>,
|
||||
>,
|
||||
>,
|
||||
>
|
||||
{
|
||||
@@ -69,7 +67,7 @@ pub trait LoadPendingBlock:
|
||||
&self,
|
||||
) -> &Mutex<Option<PendingBlock<ProviderBlock<Self::Provider>, ProviderReceipt<Self::Provider>>>>;
|
||||
|
||||
/// Configures the [`EvmEnv`] for the pending block
|
||||
/// Configures the [`PendingBlockEnv`] for the pending block
|
||||
///
|
||||
/// If no pending block is available, this will derive it from the `latest` block
|
||||
#[expect(clippy::type_complexity)]
|
||||
@@ -113,19 +111,26 @@ pub trait LoadPendingBlock:
|
||||
|
||||
let evm_env = self
|
||||
.evm_config()
|
||||
.next_evm_env(
|
||||
&latest,
|
||||
NextBlockEnvAttributes {
|
||||
timestamp: latest.timestamp().saturating_add(12),
|
||||
suggested_fee_recipient: latest.beneficiary(),
|
||||
prev_randao: B256::random(),
|
||||
gas_limit: latest.gas_limit(),
|
||||
},
|
||||
)
|
||||
.next_evm_env(&latest, self.next_env_attributes(&latest)?)
|
||||
.map_err(RethError::other)
|
||||
.map_err(Self::Error::from_eth_err)?;
|
||||
|
||||
Ok(PendingBlockEnv::new(evm_env, PendingBlockEnvOrigin::DerivedFromLatest(latest.hash())))
|
||||
Ok(PendingBlockEnv::new(evm_env, PendingBlockEnvOrigin::DerivedFromLatest(latest)))
|
||||
}
|
||||
|
||||
/// Returns [`NextBlockEnvAttributes`] for building a local pending block.
|
||||
fn next_env_attributes(
|
||||
&self,
|
||||
parent: &SealedHeader<ProviderHeader<Self::Provider>>,
|
||||
) -> Result<NextBlockEnvAttributes<'_>, Self::Error> {
|
||||
Ok(NextBlockEnvAttributes {
|
||||
timestamp: parent.timestamp().saturating_add(12),
|
||||
suggested_fee_recipient: parent.beneficiary(),
|
||||
prev_randao: B256::random(),
|
||||
gas_limit: parent.gas_limit(),
|
||||
parent_beacon_block_root: parent.parent_beacon_block_root(),
|
||||
withdrawals: None,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the locally built pending block
|
||||
@@ -146,11 +151,11 @@ pub trait LoadPendingBlock:
|
||||
{
|
||||
async move {
|
||||
let pending = self.pending_block_env_and_cfg()?;
|
||||
let parent_hash = match pending.origin {
|
||||
let parent = match pending.origin {
|
||||
PendingBlockEnvOrigin::ActualPending(block, receipts) => {
|
||||
return Ok(Some((block, receipts)));
|
||||
}
|
||||
PendingBlockEnvOrigin::DerivedFromLatest(parent_hash) => parent_hash,
|
||||
PendingBlockEnvOrigin::DerivedFromLatest(parent) => parent,
|
||||
};
|
||||
|
||||
// we couldn't find the real pending block, so we need to build it ourselves
|
||||
@@ -162,7 +167,7 @@ pub trait LoadPendingBlock:
|
||||
if let Some(pending_block) = lock.as_ref() {
|
||||
// this is guaranteed to be the `latest` header
|
||||
if pending.evm_env.block_env.number == pending_block.block.number() &&
|
||||
parent_hash == pending_block.block.parent_hash() &&
|
||||
parent.hash() == pending_block.block.parent_hash() &&
|
||||
now <= pending_block.expires_at
|
||||
{
|
||||
return Ok(Some((pending_block.block.clone(), pending_block.receipts.clone())));
|
||||
@@ -173,7 +178,7 @@ pub trait LoadPendingBlock:
|
||||
let (sealed_block, receipts) = match self
|
||||
.spawn_blocking_io(move |this| {
|
||||
// we rebuild the block
|
||||
this.build_block(pending.evm_env, parent_hash)
|
||||
this.build_block(&parent)
|
||||
})
|
||||
.await
|
||||
{
|
||||
@@ -195,47 +200,16 @@ pub trait LoadPendingBlock:
|
||||
}
|
||||
}
|
||||
|
||||
/// Assembles a receipt for a transaction, based on its [`ExecutionResult`].
|
||||
fn assemble_receipt(
|
||||
&self,
|
||||
tx: &ProviderTx<Self::Provider>,
|
||||
result: ExecutionResult<HaltReasonFor<Self::Evm>>,
|
||||
cumulative_gas_used: u64,
|
||||
) -> ProviderReceipt<Self::Provider>;
|
||||
|
||||
/// Assembles a pending block.
|
||||
fn assemble_block(
|
||||
&self,
|
||||
block_env: &BlockEnv,
|
||||
parent_hash: B256,
|
||||
result: &BlockExecutionResult<ProviderReceipt<Self::Provider>>,
|
||||
parent: &SealedHeader<ProviderHeader<Self::Provider>>,
|
||||
state_root: B256,
|
||||
transactions: Vec<Recovered<ProviderTx<Self::Provider>>>,
|
||||
receipts: &[ProviderReceipt<Self::Provider>],
|
||||
) -> ProviderBlock<Self::Provider>;
|
||||
|
||||
/// Helper to invoke both [`Self::assemble_block`] and [`Self::assemble_receipt`].
|
||||
fn assemble_block_and_receipts(
|
||||
&self,
|
||||
block_env: &BlockEnv,
|
||||
parent_hash: B256,
|
||||
state_root: B256,
|
||||
transactions: Vec<Recovered<ProviderTx<Self::Provider>>>,
|
||||
results: Vec<ExecutionResult<HaltReasonFor<Self::Evm>>>,
|
||||
) -> (ProviderBlock<Self::Provider>, Vec<ProviderReceipt<Self::Provider>>) {
|
||||
let mut cumulative_gas_used = 0;
|
||||
let mut receipts = Vec::with_capacity(results.len());
|
||||
|
||||
for (tx, outcome) in transactions.iter().zip(results) {
|
||||
cumulative_gas_used += outcome.gas_used();
|
||||
receipts.push(self.assemble_receipt(tx, outcome, cumulative_gas_used));
|
||||
}
|
||||
|
||||
let block =
|
||||
self.assemble_block(block_env, parent_hash, state_root, transactions, &receipts);
|
||||
|
||||
(block, receipts)
|
||||
}
|
||||
|
||||
/// Builds a pending block using the configured provider and pool.
|
||||
///
|
||||
/// If the origin is the actual pending block, the block is built with withdrawals.
|
||||
@@ -245,8 +219,7 @@ pub trait LoadPendingBlock:
|
||||
#[expect(clippy::type_complexity)]
|
||||
fn build_block(
|
||||
&self,
|
||||
evm_env: EvmEnv<<Self::Evm as ConfigureEvmEnv>::Spec>,
|
||||
parent_hash: B256,
|
||||
parent: &SealedHeader<ProviderHeader<Self::Provider>>,
|
||||
) -> Result<
|
||||
(RecoveredBlock<ProviderBlock<Self::Provider>>, Vec<ProviderReceipt<Self::Provider>>),
|
||||
Self::Error,
|
||||
@@ -256,34 +229,32 @@ pub trait LoadPendingBlock:
|
||||
{
|
||||
let state_provider = self
|
||||
.provider()
|
||||
.history_by_block_hash(parent_hash)
|
||||
.history_by_block_hash(parent.hash())
|
||||
.map_err(Self::Error::from_eth_err)?;
|
||||
let state = StateProviderDatabase::new(state_provider);
|
||||
let mut db = State::builder().with_database(state).with_bundle_update().build();
|
||||
|
||||
let mut strategy = self
|
||||
.evm_config()
|
||||
.strategy_for_next_block(&mut db, parent, self.next_env_attributes(parent)?)
|
||||
.map_err(RethError::other)
|
||||
.map_err(Self::Error::from_eth_err)?;
|
||||
|
||||
strategy.apply_pre_execution_changes().map_err(Self::Error::from_eth_err)?;
|
||||
|
||||
let block_env = strategy.evm_mut().block().clone();
|
||||
|
||||
let mut cumulative_gas_used = 0;
|
||||
let mut sum_blob_gas_used = 0;
|
||||
let block_gas_limit: u64 = evm_env.block_env.gas_limit;
|
||||
let base_fee = evm_env.block_env.basefee;
|
||||
let block_gas_limit: u64 = block_env.gas_limit;
|
||||
|
||||
let mut executed_txs = Vec::new();
|
||||
let mut best_txs =
|
||||
self.pool().best_transactions_with_attributes(BestTransactionsAttributes::new(
|
||||
base_fee,
|
||||
evm_env.block_env.blob_gasprice().map(|gasprice| gasprice as u64),
|
||||
block_env.basefee,
|
||||
block_env.blob_gasprice().map(|gasprice| gasprice as u64),
|
||||
));
|
||||
|
||||
let chain_spec = self.provider().chain_spec();
|
||||
|
||||
let mut system_caller = SystemCaller::new(chain_spec.clone());
|
||||
let mut evm = self.evm_config().evm_with_env(&mut db, evm_env.clone());
|
||||
|
||||
system_caller
|
||||
.apply_blockhashes_contract_call(parent_hash, &mut evm)
|
||||
.map_err(|err| EthApiError::Internal(err.into()))?;
|
||||
|
||||
let mut results = Vec::new();
|
||||
|
||||
while let Some(pool_tx) = best_txs.next() {
|
||||
// ensure we still have capacity for this transaction
|
||||
if cumulative_gas_used + pool_tx.gas_limit() > block_gas_limit {
|
||||
@@ -335,29 +306,28 @@ pub trait LoadPendingBlock:
|
||||
}
|
||||
}
|
||||
|
||||
let tx_env = self.evm_config().tx_env(&tx);
|
||||
|
||||
let ResultAndState { result, state: _ } = match evm.transact_commit(tx_env) {
|
||||
Ok(res) => res,
|
||||
Err(err) => {
|
||||
if let Some(err) = err.as_invalid_tx_err() {
|
||||
if err.is_nonce_too_low() {
|
||||
// if the nonce is too low, we can skip this transaction
|
||||
} else {
|
||||
// if the transaction is invalid, we can skip it and all of its
|
||||
// descendants
|
||||
best_txs.mark_invalid(
|
||||
&pool_tx,
|
||||
InvalidPoolTransactionError::Consensus(
|
||||
InvalidTransactionError::TxTypeNotSupported,
|
||||
),
|
||||
);
|
||||
}
|
||||
continue
|
||||
let gas_used = match strategy.execute_transaction(tx.as_recovered_ref()) {
|
||||
Ok(gas_used) => gas_used,
|
||||
Err(BlockExecutionError::Validation(BlockValidationError::InvalidTx {
|
||||
error,
|
||||
..
|
||||
})) => {
|
||||
if error.is_nonce_too_low() {
|
||||
// if the nonce is too low, we can skip this transaction
|
||||
} else {
|
||||
// if the transaction is invalid, we can skip it and all of its
|
||||
// descendants
|
||||
best_txs.mark_invalid(
|
||||
&pool_tx,
|
||||
InvalidPoolTransactionError::Consensus(
|
||||
InvalidTransactionError::TxTypeNotSupported,
|
||||
),
|
||||
);
|
||||
}
|
||||
// this is an error that we should treat as fatal for this attempt
|
||||
return Err(Self::Error::from_evm_err(err));
|
||||
continue
|
||||
}
|
||||
// this is an error that we should treat as fatal for this attempt
|
||||
Err(err) => return Err(Self::Error::from_eth_err(err)),
|
||||
};
|
||||
|
||||
// add to the total blob gas used if the transaction successfully executed
|
||||
@@ -370,28 +340,14 @@ pub trait LoadPendingBlock:
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// append transaction to the list of executed transactions
|
||||
executed_txs.push(tx);
|
||||
results.push(result);
|
||||
}
|
||||
|
||||
// executes the withdrawals and commits them to the Database and BundleState.
|
||||
let balance_increments = post_block_withdrawals_balance_increments(
|
||||
chain_spec.as_ref(),
|
||||
evm_env.block_env.timestamp,
|
||||
&[],
|
||||
);
|
||||
|
||||
// release db
|
||||
drop(evm);
|
||||
|
||||
// increment account balances for withdrawals
|
||||
db.increment_balances(balance_increments).map_err(Self::Error::from_eth_err)?;
|
||||
let result = strategy.apply_post_execution_changes().map_err(Self::Error::from_eth_err)?;
|
||||
|
||||
// merge all transitions into bundle state.
|
||||
db.merge_transitions(BundleRetention::PlainState);
|
||||
@@ -404,14 +360,8 @@ pub trait LoadPendingBlock:
|
||||
|
||||
let senders = executed_txs.iter().map(|tx| tx.signer()).collect();
|
||||
|
||||
let (block, receipts) = self.assemble_block_and_receipts(
|
||||
&evm_env.block_env,
|
||||
parent_hash,
|
||||
state_root,
|
||||
executed_txs,
|
||||
results,
|
||||
);
|
||||
let block = self.assemble_block(&block_env, &result, parent, state_root, executed_txs);
|
||||
|
||||
Ok((RecoveredBlock::new_unhashed(block, senders), receipts))
|
||||
Ok((RecoveredBlock::new_unhashed(block, senders), result.receipts))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use alloy_rpc_types_eth::{error::EthRpcErrorCode, request::TransactionInputError
|
||||
use alloy_sol_types::{ContractError, RevertReason};
|
||||
pub use api::{AsEthApiError, FromEthApiError, FromEvmError, IntoEthApiError};
|
||||
use core::time::Duration;
|
||||
use reth_errors::RethError;
|
||||
use reth_errors::{BlockExecutionError, RethError};
|
||||
use reth_primitives_traits::transaction::signed::RecoveryError;
|
||||
use reth_rpc_server_types::result::{
|
||||
block_id_to_str, internal_rpc_err, invalid_params_rpc_err, rpc_err, rpc_error_with_code,
|
||||
@@ -255,6 +255,12 @@ impl From<RethError> for EthApiError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BlockExecutionError> for EthApiError {
|
||||
fn from(error: BlockExecutionError) -> Self {
|
||||
Self::Internal(error.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<reth_errors::ProviderError> for EthApiError {
|
||||
fn from(error: reth_errors::ProviderError) -> Self {
|
||||
use reth_errors::ProviderError;
|
||||
|
||||
@@ -9,7 +9,7 @@ use alloy_eips::{BlockId, BlockNumberOrTag};
|
||||
use alloy_primitives::B256;
|
||||
use derive_more::Constructor;
|
||||
use reth_evm::EvmEnv;
|
||||
use reth_primitives::{Receipt, RecoveredBlock};
|
||||
use reth_primitives::{Receipt, RecoveredBlock, SealedHeader};
|
||||
use reth_primitives_traits::Block;
|
||||
|
||||
/// Configured [`EvmEnv`] for a pending block.
|
||||
@@ -32,7 +32,7 @@ pub enum PendingBlockEnvOrigin<B: Block = reth_primitives::Block, R = Receipt> {
|
||||
/// - the timestamp
|
||||
/// - the block number
|
||||
/// - fees
|
||||
DerivedFromLatest(B256),
|
||||
DerivedFromLatest(SealedHeader<B::Header>),
|
||||
}
|
||||
|
||||
impl<B: Block, R> PendingBlockEnvOrigin<B, R> {
|
||||
@@ -56,7 +56,7 @@ impl<B: Block, R> PendingBlockEnvOrigin<B, R> {
|
||||
pub fn state_block_id(&self) -> BlockId {
|
||||
match self {
|
||||
Self::ActualPending(_, _) => BlockNumberOrTag::Pending.into(),
|
||||
Self::DerivedFromLatest(hash) => BlockId::Hash((*hash).into()),
|
||||
Self::DerivedFromLatest(latest) => BlockId::Hash(latest.hash().into()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ impl<B: Block, R> PendingBlockEnvOrigin<B, R> {
|
||||
pub fn build_target_hash(&self) -> B256 {
|
||||
match self {
|
||||
Self::ActualPending(block, _) => block.header().parent_hash(),
|
||||
Self::DerivedFromLatest(hash) => *hash,
|
||||
Self::DerivedFromLatest(latest) => latest.hash(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ use alloy_rpc_types_eth::{
|
||||
Block, BlockTransactionsKind, Header,
|
||||
};
|
||||
use jsonrpsee_types::ErrorObject;
|
||||
use reth_primitives::{Recovered, RecoveredBlock};
|
||||
use reth_evm::{execute::BlockExecutionStrategy, Evm};
|
||||
use reth_execution_types::BlockExecutionResult;
|
||||
use reth_primitives::{NodePrimitives, Recovered, RecoveredBlock};
|
||||
use reth_primitives_traits::{block::BlockTx, BlockBody as _, SignedTransaction};
|
||||
use reth_rpc_server_types::result::rpc_err;
|
||||
use reth_rpc_types_compat::{block::from_block, TransactionCompat};
|
||||
@@ -48,6 +50,60 @@ impl ToRpcError for EthSimulateError {
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts all [`TransactionRequest`]s into [`Recovered`] transactions and applies them to the
|
||||
/// given [`BlockExecutionStrategy`].
|
||||
///
|
||||
/// Returns all executed transactions and the result of the execution.
|
||||
#[expect(clippy::type_complexity)]
|
||||
pub fn execute_transactions<N, S, T>(
|
||||
mut strategy: S,
|
||||
calls: Vec<TransactionRequest>,
|
||||
validation: bool,
|
||||
default_gas_limit: u64,
|
||||
chain_id: u64,
|
||||
tx_resp_builder: &T,
|
||||
) -> Result<
|
||||
(
|
||||
Vec<Recovered<N::SignedTx>>,
|
||||
BlockExecutionResult<N::Receipt>,
|
||||
Vec<ExecutionResult<<S::Evm as Evm>::HaltReason>>,
|
||||
),
|
||||
EthApiError,
|
||||
>
|
||||
where
|
||||
N: NodePrimitives,
|
||||
S: BlockExecutionStrategy<Primitives = N>,
|
||||
EthApiError: From<S::Error> + From<<<S::Evm as Evm>::DB as Database>::Error>,
|
||||
S::Evm: Evm<DB: Database<Error: Into<EthApiError>>>,
|
||||
T: TransactionCompat<N::SignedTx>,
|
||||
{
|
||||
strategy.apply_pre_execution_changes()?;
|
||||
|
||||
let mut transactions = Vec::with_capacity(calls.len());
|
||||
let mut results = Vec::with_capacity(calls.len());
|
||||
for call in calls {
|
||||
// Resolve transaction, populate missing fields and enforce calls
|
||||
// correctness.
|
||||
let tx = resolve_transaction(
|
||||
call,
|
||||
validation,
|
||||
default_gas_limit,
|
||||
chain_id,
|
||||
strategy.evm_mut().db_mut(),
|
||||
tx_resp_builder,
|
||||
)?;
|
||||
|
||||
strategy.execute_transaction_with_result_closure(tx.as_recovered_ref(), |result| {
|
||||
results.push(result.clone())
|
||||
})?;
|
||||
transactions.push(tx);
|
||||
}
|
||||
|
||||
let result = strategy.apply_post_execution_changes()?;
|
||||
|
||||
Ok((transactions, result, results))
|
||||
}
|
||||
|
||||
/// Goes over the list of [`TransactionRequest`]s and populates missing fields trying to resolve
|
||||
/// them into primitive transactions.
|
||||
///
|
||||
|
||||
@@ -7,12 +7,13 @@ use alloy_consensus::{
|
||||
use alloy_eips::{eip7685::EMPTY_REQUESTS_HASH, merge::BEACON_NONCE};
|
||||
use alloy_primitives::U256;
|
||||
use reth_chainspec::{EthChainSpec, EthereumHardforks};
|
||||
use reth_evm::{ConfigureEvm, HaltReasonFor};
|
||||
use reth_primitives::{logs_bloom, BlockBody, Receipt};
|
||||
use reth_evm::execute::BlockExecutionStrategyFactory;
|
||||
use reth_node_api::NodePrimitives;
|
||||
use reth_primitives::{logs_bloom, BlockBody, Receipt, SealedHeader};
|
||||
use reth_primitives_traits::proofs::calculate_transaction_root;
|
||||
use reth_provider::{
|
||||
BlockReader, BlockReaderIdExt, ChainSpecProvider, ProviderBlock, ProviderReceipt, ProviderTx,
|
||||
StateProviderFactory,
|
||||
BlockExecutionResult, BlockReader, BlockReaderIdExt, ChainSpecProvider, ProviderBlock,
|
||||
ProviderHeader, ProviderReceipt, ProviderTx, StateProviderFactory,
|
||||
};
|
||||
use reth_rpc_eth_api::{
|
||||
helpers::{LoadPendingBlock, SpawnBlocking},
|
||||
@@ -21,10 +22,7 @@ use reth_rpc_eth_api::{
|
||||
};
|
||||
use reth_rpc_eth_types::PendingBlock;
|
||||
use reth_transaction_pool::{PoolTransaction, TransactionPool};
|
||||
use revm::{
|
||||
context::BlockEnv,
|
||||
context_interface::{result::ExecutionResult, Block},
|
||||
};
|
||||
use revm::{context::BlockEnv, context_interface::Block};
|
||||
use revm_primitives::B256;
|
||||
|
||||
use crate::EthApi;
|
||||
@@ -46,7 +44,13 @@ where
|
||||
Pool: TransactionPool<
|
||||
Transaction: PoolTransaction<Consensus = ProviderTx<Self::Provider>>,
|
||||
>,
|
||||
Evm: ConfigureEvm<Header = Header, Transaction = ProviderTx<Self::Provider>>,
|
||||
Evm: BlockExecutionStrategyFactory<
|
||||
Primitives: NodePrimitives<
|
||||
BlockHeader = Header,
|
||||
SignedTx = ProviderTx<Self::Provider>,
|
||||
Receipt = ProviderReceipt<Self::Provider>,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
Provider: BlockReader<Block = reth_primitives::Block, Receipt = reth_primitives::Receipt>,
|
||||
{
|
||||
@@ -62,17 +66,17 @@ where
|
||||
fn assemble_block(
|
||||
&self,
|
||||
block_env: &BlockEnv,
|
||||
parent_hash: revm_primitives::B256,
|
||||
result: &BlockExecutionResult<ProviderReceipt<Self::Provider>>,
|
||||
parent: &SealedHeader<ProviderHeader<Self::Provider>>,
|
||||
state_root: revm_primitives::B256,
|
||||
transactions: Vec<Recovered<ProviderTx<Self::Provider>>>,
|
||||
receipts: &[ProviderReceipt<Self::Provider>],
|
||||
) -> reth_provider::ProviderBlock<Self::Provider> {
|
||||
let chain_spec = self.provider().chain_spec();
|
||||
|
||||
let transactions_root = calculate_transaction_root(&transactions);
|
||||
let receipts_root = Receipt::calculate_receipt_root_no_memo(receipts);
|
||||
let receipts_root = Receipt::calculate_receipt_root_no_memo(&result.receipts);
|
||||
|
||||
let logs_bloom = logs_bloom(receipts.iter().flat_map(|r| &r.logs));
|
||||
let logs_bloom = logs_bloom(result.receipts.iter().flat_map(|r| &r.logs));
|
||||
|
||||
let timestamp = block_env.timestamp;
|
||||
let is_shanghai = chain_spec.is_shanghai_active_at_timestamp(timestamp);
|
||||
@@ -80,7 +84,7 @@ where
|
||||
let is_prague = chain_spec.is_prague_active_at_timestamp(timestamp);
|
||||
|
||||
let header = Header {
|
||||
parent_hash,
|
||||
parent_hash: parent.hash(),
|
||||
ommers_hash: EMPTY_OMMER_ROOT_HASH,
|
||||
beneficiary: block_env.beneficiary,
|
||||
state_root,
|
||||
@@ -95,7 +99,7 @@ where
|
||||
number: block_env.number,
|
||||
gas_limit: block_env.gas_limit,
|
||||
difficulty: U256::ZERO,
|
||||
gas_used: receipts.last().map(|r| r.cumulative_gas_used).unwrap_or_default(),
|
||||
gas_used: result.gas_used,
|
||||
blob_gas_used: is_cancun.then(|| {
|
||||
transactions.iter().map(|tx| tx.blob_gas_used().unwrap_or_default()).sum::<u64>()
|
||||
}),
|
||||
@@ -115,18 +119,4 @@ where
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn assemble_receipt(
|
||||
&self,
|
||||
tx: &ProviderTx<Self::Provider>,
|
||||
result: ExecutionResult<HaltReasonFor<Self::Evm>>,
|
||||
cumulative_gas_used: u64,
|
||||
) -> reth_provider::ProviderReceipt<Self::Provider> {
|
||||
Receipt {
|
||||
tx_type: tx.tx_type(),
|
||||
success: result.is_success(),
|
||||
cumulative_gas_used,
|
||||
logs: result.into_logs().into_iter().collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user