1use crate::{OpEvmConfig, OpRethReceiptBuilder};
4use alloc::sync::Arc;
5use reth_evm::execute::BasicBlockExecutorProvider;
6use reth_optimism_chainspec::OpChainSpec;
7
8#[derive(Debug)]
10pub struct OpExecutorProvider;
11
12impl OpExecutorProvider {
13 pub fn optimism(chain_spec: Arc<OpChainSpec>) -> BasicBlockExecutorProvider<OpEvmConfig> {
15 BasicBlockExecutorProvider::new(OpEvmConfig::new(
16 chain_spec,
17 OpRethReceiptBuilder::default(),
18 ))
19 }
20}
21
22#[cfg(test)]
23mod tests {
24 use super::*;
25 use crate::OpChainSpec;
26 use alloy_consensus::{Block, BlockBody, Header, TxEip1559};
27 use alloy_primitives::{
28 b256, Address, PrimitiveSignature as Signature, StorageKey, StorageValue, U256,
29 };
30 use op_alloy_consensus::{OpTypedTransaction, TxDeposit};
31 use op_revm::constants::L1_BLOCK_CONTRACT;
32 use reth_chainspec::MIN_TRANSACTION_GAS;
33 use reth_evm::execute::{BasicBlockExecutorProvider, BlockExecutorProvider, Executor};
34 use reth_optimism_chainspec::OpChainSpecBuilder;
35 use reth_optimism_primitives::{OpReceipt, OpTransactionSigned};
36 use reth_primitives_traits::{Account, RecoveredBlock};
37 use reth_revm::{database::StateProviderDatabase, test_utils::StateProviderTest};
38 use std::{collections::HashMap, str::FromStr};
39
40 fn create_op_state_provider() -> StateProviderTest {
41 let mut db = StateProviderTest::default();
42
43 let l1_block_contract_account =
44 Account { balance: U256::ZERO, bytecode_hash: None, nonce: 1 };
45
46 let mut l1_block_storage = HashMap::default();
47 l1_block_storage.insert(StorageKey::with_last_byte(1), StorageValue::from(1000000000));
49 l1_block_storage.insert(StorageKey::with_last_byte(5), StorageValue::from(188));
51 l1_block_storage.insert(StorageKey::with_last_byte(6), StorageValue::from(684000));
53 l1_block_storage.insert(
55 StorageKey::with_last_byte(3),
56 StorageValue::from_str(
57 "0x0000000000000000000000000000000000001db0000d27300000000000000005",
58 )
59 .unwrap(),
60 );
61
62 db.insert_account(L1_BLOCK_CONTRACT, l1_block_contract_account, None, l1_block_storage);
63
64 db
65 }
66
67 fn executor_provider(chain_spec: Arc<OpChainSpec>) -> BasicBlockExecutorProvider<OpEvmConfig> {
68 BasicBlockExecutorProvider::new(OpEvmConfig::new(
69 chain_spec,
70 OpRethReceiptBuilder::default(),
71 ))
72 }
73
74 #[test]
75 fn op_deposit_fields_pre_canyon() {
76 let header = Header {
77 timestamp: 1,
78 number: 1,
79 gas_limit: 1_000_000,
80 gas_used: 42_000,
81 receipts_root: b256!(
82 "0x83465d1e7d01578c0d609be33570f91242f013e9e295b0879905346abbd63731"
83 ),
84 ..Default::default()
85 };
86
87 let mut db = create_op_state_provider();
88
89 let addr = Address::ZERO;
90 let account = Account { balance: U256::MAX, ..Account::default() };
91 db.insert_account(addr, account, None, HashMap::default());
92
93 let chain_spec = Arc::new(OpChainSpecBuilder::base_mainnet().regolith_activated().build());
94
95 let tx = OpTransactionSigned::new_unhashed(
96 OpTypedTransaction::Eip1559(TxEip1559 {
97 chain_id: chain_spec.chain.id(),
98 nonce: 0,
99 gas_limit: MIN_TRANSACTION_GAS,
100 to: addr.into(),
101 ..Default::default()
102 }),
103 Signature::test_signature(),
104 );
105
106 let tx_deposit = OpTransactionSigned::new_unhashed(
107 OpTypedTransaction::Deposit(op_alloy_consensus::TxDeposit {
108 from: addr,
109 to: addr.into(),
110 gas_limit: MIN_TRANSACTION_GAS,
111 ..Default::default()
112 }),
113 Signature::test_signature(),
114 );
115
116 let provider = executor_provider(chain_spec);
117 let mut executor = provider.executor(StateProviderDatabase::new(&db));
118
119 executor.with_state_mut(|state| {
121 state.load_cache_account(L1_BLOCK_CONTRACT).unwrap();
122 });
123
124 let output = executor
126 .execute(&RecoveredBlock::new_unhashed(
127 Block {
128 header,
129 body: BlockBody { transactions: vec![tx, tx_deposit], ..Default::default() },
130 },
131 vec![addr, addr],
132 ))
133 .unwrap();
134
135 let receipts = &output.receipts;
136 let tx_receipt = &receipts[0];
137 let deposit_receipt = &receipts[1];
138
139 assert!(!matches!(tx_receipt, OpReceipt::Deposit(_)));
140 let OpReceipt::Deposit(deposit_receipt) = deposit_receipt else {
142 panic!("expected deposit")
143 };
144 assert!(deposit_receipt.deposit_nonce.is_some());
145 assert!(deposit_receipt.deposit_receipt_version.is_none());
147 }
148
149 #[test]
150 fn op_deposit_fields_post_canyon() {
151 let header = Header {
153 timestamp: 2,
154 number: 1,
155 gas_limit: 1_000_000,
156 gas_used: 42_000,
157 receipts_root: b256!(
158 "0xfffc85c4004fd03c7bfbe5491fae98a7473126c099ac11e8286fd0013f15f908"
159 ),
160 ..Default::default()
161 };
162
163 let mut db = create_op_state_provider();
164 let addr = Address::ZERO;
165 let account = Account { balance: U256::MAX, ..Account::default() };
166
167 db.insert_account(addr, account, None, HashMap::default());
168
169 let chain_spec = Arc::new(OpChainSpecBuilder::base_mainnet().canyon_activated().build());
170
171 let tx = OpTransactionSigned::new_unhashed(
172 OpTypedTransaction::Eip1559(TxEip1559 {
173 chain_id: chain_spec.chain.id(),
174 nonce: 0,
175 gas_limit: MIN_TRANSACTION_GAS,
176 to: addr.into(),
177 ..Default::default()
178 }),
179 Signature::test_signature(),
180 );
181
182 let tx_deposit = OpTransactionSigned::new_unhashed(
183 OpTypedTransaction::Deposit(op_alloy_consensus::TxDeposit {
184 from: addr,
185 to: addr.into(),
186 gas_limit: MIN_TRANSACTION_GAS,
187 ..Default::default()
188 }),
189 TxDeposit::signature(),
190 );
191
192 let provider = executor_provider(chain_spec);
193 let mut executor = provider.executor(StateProviderDatabase::new(&db));
194
195 executor.with_state_mut(|state| {
197 state.load_cache_account(L1_BLOCK_CONTRACT).unwrap();
198 });
199
200 let output = executor
202 .execute(&RecoveredBlock::new_unhashed(
203 Block {
204 header,
205 body: BlockBody { transactions: vec![tx, tx_deposit], ..Default::default() },
206 },
207 vec![addr, addr],
208 ))
209 .expect("Executing a block while canyon is active should not fail");
210
211 let receipts = &output.receipts;
212 let tx_receipt = &receipts[0];
213 let deposit_receipt = &receipts[1];
214
215 assert!(!matches!(tx_receipt, OpReceipt::Deposit(_)));
217 let OpReceipt::Deposit(deposit_receipt) = deposit_receipt else {
218 panic!("expected deposit")
219 };
220 assert_eq!(deposit_receipt.deposit_receipt_version, Some(1));
221
222 assert!(deposit_receipt.deposit_nonce.is_some());
224 }
225}