//! 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) -> 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()); } }