Skip to main content

revm_handler/
mainnet_builder.rs

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
8/// Type alias for a mainnet EVM instance with standard Ethereum components.
9pub type MainnetEvm<CTX, INSP = ()> =
10    Evm<CTX, INSP, EthInstructions<EthInterpreter, CTX>, EthPrecompiles, EthFrame<EthInterpreter>>;
11
12/// Type alias for a mainnet context with standard Ethereum environment types.
13pub type MainnetContext<DB> = Context<BlockEnv, TxEnv, CfgEnv, DB, Journal<DB>, ()>;
14
15/// Trait for building mainnet EVM instances from contexts.
16pub trait MainBuilder: Sized {
17    /// The context type that will be used in the EVM.
18    type Context;
19
20    /// Builds a mainnet EVM instance without an inspector.
21    fn build_mainnet(self) -> MainnetEvm<Self::Context>;
22
23    /// Builds a mainnet EVM instance with the provided inspector.
24    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
68/// Trait used to initialize Context with default mainnet types.
69pub trait MainContext {
70    /// Creates a new mainnet context with default configuration.
71    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                // Exchange the bottom two values and pop the unchanged top value.
107                (
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}