1use crate::{frame::EthFrame, instructions::EthInstructions, EthPrecompiles};
2use context::{BlockEnv, Cfg, CfgEnv, Context, Evm, FrameStack, Journal, TxEnv};
3use context_interface::{Block, Database, JournalTr, Transaction};
4use database_interface::EmptyDB;
5use interpreter::interpreter::EthInterpreter;
6use primitives::hardfork::SpecId;
7
8pub type MainnetEvm<CTX, INSP = ()> =
10 Evm<CTX, INSP, EthInstructions<EthInterpreter, CTX>, EthPrecompiles, EthFrame<EthInterpreter>>;
11
12pub type MainnetContext<DB> = Context<BlockEnv, TxEnv, CfgEnv, DB, Journal<DB>, ()>;
14
15pub trait MainBuilder: Sized {
17 type Context;
19
20 fn build_mainnet(self) -> MainnetEvm<Self::Context>;
22
23 fn build_mainnet_with_inspector<INSP>(self, inspector: INSP)
25 -> MainnetEvm<Self::Context, INSP>;
26}
27
28impl<BLOCK, TX, CFG, DB, JOURNAL, CHAIN> MainBuilder for Context<BLOCK, TX, CFG, DB, JOURNAL, CHAIN>
29where
30 BLOCK: Block,
31 TX: Transaction,
32 CFG: Cfg,
33 DB: Database,
34 JOURNAL: JournalTr<Database = DB>,
35{
36 type Context = Self;
37
38 fn build_mainnet(self) -> MainnetEvm<Self::Context> {
39 let spec = self.cfg.spec().into();
40 Evm {
41 ctx: self,
42 inspector: (),
43 instruction: EthInstructions::new_mainnet_with_spec(spec),
44 precompiles: EthPrecompiles::new(spec),
45 frame_stack: FrameStack::new_prealloc(8),
46 #[cfg(feature = "asyncdb")]
47 async_stack: database_interface::async_db::FiberStack::default(),
48 }
49 }
50
51 fn build_mainnet_with_inspector<INSP>(
52 self,
53 inspector: INSP,
54 ) -> MainnetEvm<Self::Context, INSP> {
55 let spec = self.cfg.spec().into();
56 Evm {
57 ctx: self,
58 inspector,
59 instruction: EthInstructions::new_mainnet_with_spec(spec),
60 precompiles: EthPrecompiles::new(spec),
61 frame_stack: FrameStack::new_prealloc(8),
62 #[cfg(feature = "asyncdb")]
63 async_stack: database_interface::async_db::FiberStack::default(),
64 }
65 }
66}
67
68pub trait MainContext {
70 fn mainnet() -> Self;
72}
73
74impl MainContext for Context<BlockEnv, TxEnv, CfgEnv, EmptyDB, Journal<EmptyDB>, ()> {
75 fn mainnet() -> Self {
76 Context::new(EmptyDB::new(), SpecId::default())
77 }
78}
79
80#[cfg(test)]
81mod test {
82 use crate::{ExecuteEvm, MainBuilder, MainContext};
83 use alloy_signer::{Either, SignerSync};
84 use alloy_signer_local::PrivateKeySigner;
85 use bytecode::{
86 opcode::{PUSH1, SSTORE},
87 Bytecode,
88 };
89 use context::{Context, TxEnv};
90 use context_interface::transaction::Authorization;
91 use database::{BenchmarkDB, EEADDRESS, FFADDRESS};
92 use primitives::{hardfork::SpecId, StorageKey, StorageValue, TxKind, U256};
93
94 #[test]
95 fn eip8024_can_be_enabled_independently_of_amsterdam() {
96 use bytecode::opcode::{DUPN, EXCHANGE, SWAPN};
97 use context::{
98 result::{ExecutionResult, HaltReason},
99 CfgEnv,
100 };
101 use primitives::Bytes;
102 use std::vec;
103
104 for opcode in [DUPN, SWAPN, EXCHANGE] {
105 let (mut code, expected) = if opcode == EXCHANGE {
106 (
108 vec![0x60, 0, 0x60, 1, 0x60, 2, EXCHANGE, 0x8e, 0x50],
109 U256::ZERO,
110 )
111 } else {
112 let mut code = vec![0x60, 1];
113 code.extend([0x5f; 16]);
114 if opcode == SWAPN {
115 code.extend([0x60, 2]);
116 }
117 code.extend([opcode, 0x80]);
118 (code, U256::from(1))
119 };
120 code.extend([0x5f, 0x52, 0x60, 0x20, 0x5f, 0xf3]);
121
122 for (spec, override_flag, enabled) in [
123 (SpecId::OSAKA, None, false),
124 (SpecId::OSAKA, Some(true), true),
125 (SpecId::AMSTERDAM, None, true),
126 (SpecId::AMSTERDAM, Some(false), false),
127 ] {
128 let mut cfg = CfgEnv::new_with_spec(spec);
129 if let Some(enable) = override_flag {
130 cfg = cfg.with_enable_amsterdam_eip8024(enable);
131 }
132 let mut evm = Context::mainnet()
133 .with_cfg(cfg)
134 .with_db(BenchmarkDB::new_bytecode(Bytecode::new_legacy(
135 Bytes::copy_from_slice(&code),
136 )))
137 .build_mainnet();
138 let result = evm
139 .transact(
140 TxEnv::builder()
141 .caller(EEADDRESS)
142 .kind(TxKind::Call(FFADDRESS))
143 .gas_limit(100_000)
144 .build()
145 .unwrap(),
146 )
147 .unwrap()
148 .result;
149 if enabled {
150 assert!(result.is_success(), "{spec:?}, {opcode:x}: {result:?}");
151 assert_eq!(U256::from_be_slice(result.output().unwrap()), expected);
152 } else {
153 assert!(
154 matches!(
155 result,
156 ExecutionResult::Halt {
157 reason: HaltReason::NotActivated,
158 ..
159 }
160 ),
161 "{spec:?}, {opcode:x}: {result:?}"
162 );
163 }
164 }
165 }
166 }
167
168 #[test]
169 fn sanity_eip7702_tx() {
170 let signer = PrivateKeySigner::random();
171 let auth = Authorization {
172 chain_id: U256::ZERO,
173 nonce: 0,
174 address: FFADDRESS,
175 };
176 let signature = signer.sign_hash_sync(&auth.signature_hash()).unwrap();
177 let auth = auth.into_signed(signature);
178
179 let bytecode = Bytecode::new_legacy([PUSH1, 0x01, PUSH1, 0x01, SSTORE].into());
180
181 let ctx = Context::mainnet()
182 .modify_cfg_chained(|cfg| cfg.set_spec_and_mainnet_gas_params(SpecId::PRAGUE))
183 .with_db(BenchmarkDB::new_bytecode(bytecode));
184
185 let mut evm = ctx.build_mainnet();
186
187 let state = evm
188 .transact(
189 TxEnv::builder()
190 .gas_limit(100_000)
191 .authorization_list(vec![Either::Left(auth)])
192 .caller(EEADDRESS)
193 .kind(TxKind::Call(signer.address()))
194 .build()
195 .unwrap(),
196 )
197 .unwrap()
198 .state;
199
200 let auth_acc = state.get(&signer.address()).unwrap();
201 assert_eq!(auth_acc.info.code, Some(Bytecode::new_eip7702(FFADDRESS)));
202 assert_eq!(auth_acc.info.nonce, 1);
203 assert_eq!(
204 auth_acc
205 .storage
206 .get(&StorageKey::from(1))
207 .unwrap()
208 .present_value,
209 StorageValue::from(1)
210 );
211 }
212}