Files
reth/crates/optimism/evm/src/execute.rs
2025-06-10 10:42:12 +00:00

199 lines
7.2 KiB
Rust

//! Optimism block execution strategy.
/// Helper type with backwards compatible methods to obtain executor providers.
pub type OpExecutorProvider = crate::OpEvmConfig;
#[cfg(test)]
mod tests {
use crate::{OpEvmConfig, OpRethReceiptBuilder};
use alloc::sync::Arc;
use alloy_consensus::{Block, BlockBody, Header, SignableTransaction, TxEip1559};
use alloy_primitives::{b256, Address, Signature, StorageKey, StorageValue, U256};
use op_alloy_consensus::TxDeposit;
use op_revm::constants::L1_BLOCK_CONTRACT;
use reth_chainspec::MIN_TRANSACTION_GAS;
use reth_evm::execute::{BasicBlockExecutor, Executor};
use reth_optimism_chainspec::{OpChainSpec, OpChainSpecBuilder};
use reth_optimism_primitives::{OpReceipt, OpTransactionSigned};
use reth_primitives_traits::{Account, RecoveredBlock};
use reth_revm::{database::StateProviderDatabase, test_utils::StateProviderTest};
use std::{collections::HashMap, str::FromStr};
fn create_op_state_provider() -> StateProviderTest {
let mut db = StateProviderTest::default();
let l1_block_contract_account =
Account { balance: U256::ZERO, bytecode_hash: None, nonce: 1 };
let mut l1_block_storage = HashMap::default();
// base fee
l1_block_storage.insert(StorageKey::with_last_byte(1), StorageValue::from(1000000000));
// l1 fee overhead
l1_block_storage.insert(StorageKey::with_last_byte(5), StorageValue::from(188));
// l1 fee scalar
l1_block_storage.insert(StorageKey::with_last_byte(6), StorageValue::from(684000));
// l1 free scalars post ecotone
l1_block_storage.insert(
StorageKey::with_last_byte(3),
StorageValue::from_str(
"0x0000000000000000000000000000000000001db0000d27300000000000000005",
)
.unwrap(),
);
db.insert_account(L1_BLOCK_CONTRACT, l1_block_contract_account, None, l1_block_storage);
db
}
fn evm_config(chain_spec: Arc<OpChainSpec>) -> OpEvmConfig {
OpEvmConfig::new(chain_spec, OpRethReceiptBuilder::default())
}
#[test]
fn op_deposit_fields_pre_canyon() {
let header = Header {
timestamp: 1,
number: 1,
gas_limit: 1_000_000,
gas_used: 42_000,
receipts_root: b256!(
"0x83465d1e7d01578c0d609be33570f91242f013e9e295b0879905346abbd63731"
),
..Default::default()
};
let mut db = create_op_state_provider();
let addr = Address::ZERO;
let account = Account { balance: U256::MAX, ..Account::default() };
db.insert_account(addr, account, None, HashMap::default());
let chain_spec = Arc::new(OpChainSpecBuilder::base_mainnet().regolith_activated().build());
let tx: OpTransactionSigned = TxEip1559 {
chain_id: chain_spec.chain.id(),
nonce: 0,
gas_limit: MIN_TRANSACTION_GAS,
to: addr.into(),
..Default::default()
}
.into_signed(Signature::test_signature())
.into();
let tx_deposit: OpTransactionSigned = TxDeposit {
from: addr,
to: addr.into(),
gas_limit: MIN_TRANSACTION_GAS,
..Default::default()
}
.into();
let provider = evm_config(chain_spec);
let mut executor = BasicBlockExecutor::new(provider, StateProviderDatabase::new(&db));
// make sure the L1 block contract state is preloaded.
executor.with_state_mut(|state| {
state.load_cache_account(L1_BLOCK_CONTRACT).unwrap();
});
// Attempt to execute a block with one deposit and one non-deposit transaction
let output = executor
.execute(&RecoveredBlock::new_unhashed(
Block {
header,
body: BlockBody { transactions: vec![tx, tx_deposit], ..Default::default() },
},
vec![addr, addr],
))
.unwrap();
let receipts = &output.receipts;
let tx_receipt = &receipts[0];
let deposit_receipt = &receipts[1];
assert!(!matches!(tx_receipt, OpReceipt::Deposit(_)));
// deposit_nonce is present only in deposit transactions
let OpReceipt::Deposit(deposit_receipt) = deposit_receipt else {
panic!("expected deposit")
};
assert!(deposit_receipt.deposit_nonce.is_some());
// deposit_receipt_version is not present in pre canyon transactions
assert!(deposit_receipt.deposit_receipt_version.is_none());
}
#[test]
fn op_deposit_fields_post_canyon() {
// ensure_create2_deployer will fail if timestamp is set to less than 2
let header = Header {
timestamp: 2,
number: 1,
gas_limit: 1_000_000,
gas_used: 42_000,
receipts_root: b256!(
"0xfffc85c4004fd03c7bfbe5491fae98a7473126c099ac11e8286fd0013f15f908"
),
..Default::default()
};
let mut db = create_op_state_provider();
let addr = Address::ZERO;
let account = Account { balance: U256::MAX, ..Account::default() };
db.insert_account(addr, account, None, HashMap::default());
let chain_spec = Arc::new(OpChainSpecBuilder::base_mainnet().canyon_activated().build());
let tx: OpTransactionSigned = TxEip1559 {
chain_id: chain_spec.chain.id(),
nonce: 0,
gas_limit: MIN_TRANSACTION_GAS,
to: addr.into(),
..Default::default()
}
.into_signed(Signature::test_signature())
.into();
let tx_deposit: OpTransactionSigned = TxDeposit {
from: addr,
to: addr.into(),
gas_limit: MIN_TRANSACTION_GAS,
..Default::default()
}
.into();
let provider = evm_config(chain_spec);
let mut executor = BasicBlockExecutor::new(provider, StateProviderDatabase::new(&db));
// make sure the L1 block contract state is preloaded.
executor.with_state_mut(|state| {
state.load_cache_account(L1_BLOCK_CONTRACT).unwrap();
});
// attempt to execute an empty block with parent beacon block root, this should not fail
let output = executor
.execute(&RecoveredBlock::new_unhashed(
Block {
header,
body: BlockBody { transactions: vec![tx, tx_deposit], ..Default::default() },
},
vec![addr, addr],
))
.expect("Executing a block while canyon is active should not fail");
let receipts = &output.receipts;
let tx_receipt = &receipts[0];
let deposit_receipt = &receipts[1];
// deposit_receipt_version is set to 1 for post canyon deposit transactions
assert!(!matches!(tx_receipt, OpReceipt::Deposit(_)));
let OpReceipt::Deposit(deposit_receipt) = deposit_receipt else {
panic!("expected deposit")
};
assert_eq!(deposit_receipt.deposit_receipt_version, Some(1));
// deposit_nonce is present only in deposit transactions
assert!(deposit_receipt.deposit_nonce.is_some());
}
}