1#![cfg_attr(not(test), warn(unused_crate_dependencies))]
8
9use revm::{
10 context::{
11 journaled_state::{account::JournaledAccount, AccountInfoLoad, JournalLoadError},
12 result::InvalidTransaction,
13 BlockEnv, Cfg, CfgEnv, ContextTr, Evm, LocalContext, TxEnv,
14 },
15 context_interface::{
16 journaled_state::{AccountLoad, JournalCheckpoint, TransferError},
17 result::EVMError,
18 Block, JournalTr, Transaction,
19 },
20 database::InMemoryDB,
21 handler::{instructions::EthInstructions, EthPrecompiles},
22 inspector::{inspectors::TracerEip3155, JournalExt},
23 interpreter::{
24 interpreter::EthInterpreter, CallInputs, CallOutcome, SStoreResult, SelfDestructResult,
25 StateLoad,
26 },
27 primitives::{
28 hardfork::SpecId, Address, AddressMap, AddressSet, HashSet, Log, StorageKey, StorageValue,
29 B256, U256,
30 },
31 state::{Account, Bytecode, EvmState},
32 Context, Database, DatabaseCommit, InspectEvm, Inspector, Journal, JournalEntry,
33};
34use std::{convert::Infallible, fmt::Debug};
35
36#[derive(Clone, Debug)]
40struct Backend {
41 journaled_state: Journal<InMemoryDB>,
43 method_with_inspector_counter: usize,
45 method_without_inspector_counter: usize,
46}
47
48impl Backend {
49 fn new(spec: SpecId, db: InMemoryDB) -> Self {
50 let mut journaled_state = Journal::new(db);
51 journaled_state.set_spec_id(spec);
52 Self {
53 journaled_state,
54 method_with_inspector_counter: 0,
55 method_without_inspector_counter: 0,
56 }
57 }
58}
59
60impl JournalTr for Backend {
61 type Database = InMemoryDB;
62 type State = EvmState;
63 type JournaledAccount<'a> = JournaledAccount<'a, InMemoryDB, JournalEntry>;
64
65 fn new(database: InMemoryDB) -> Self {
66 Self::new(SpecId::default(), database)
67 }
68
69 fn db_and_state(&self) -> (&Self::Database, &Self::State) {
70 self.journaled_state.db_and_state()
71 }
72
73 fn db_and_state_mut(&mut self) -> (&mut Self::Database, &mut Self::State) {
74 self.journaled_state.db_and_state_mut()
75 }
76
77 fn sload(
78 &mut self,
79 address: Address,
80 key: StorageKey,
81 ) -> Result<StateLoad<StorageValue>, <Self::Database as Database>::Error> {
82 self.journaled_state.sload(address, key)
83 }
84
85 fn sstore(
86 &mut self,
87 address: Address,
88 key: StorageKey,
89 value: StorageValue,
90 ) -> Result<StateLoad<SStoreResult>, <Self::Database as Database>::Error> {
91 self.journaled_state.sstore(address, key, value)
92 }
93
94 fn tload(&mut self, address: Address, key: StorageKey) -> StorageValue {
95 self.journaled_state.tload(address, key)
96 }
97
98 fn tstore(&mut self, address: Address, key: StorageKey, value: StorageValue) {
99 self.journaled_state.tstore(address, key, value)
100 }
101
102 fn log(&mut self, log: Log) {
103 self.journaled_state.log(log)
104 }
105
106 fn logs(&self) -> &[Log] {
107 self.journaled_state.logs()
108 }
109
110 fn selfdestruct(
111 &mut self,
112 address: Address,
113 target: Address,
114 skip_cold_load: bool,
115 ) -> Result<StateLoad<SelfDestructResult>, JournalLoadError<Infallible>> {
116 self.journaled_state
117 .selfdestruct(address, target, skip_cold_load)
118 }
119
120 fn warm_access_list(&mut self, access_list: AddressMap<HashSet<StorageKey>>) {
121 self.journaled_state.warm_access_list(access_list);
122 }
123
124 fn warm_coinbase_account(&mut self, address: Address) {
125 self.journaled_state.warm_coinbase_account(address)
126 }
127
128 fn warm_precompiles(&mut self, addresses: &AddressSet) {
129 self.journaled_state.warm_precompiles(addresses)
130 }
131
132 fn precompile_addresses(&self) -> &AddressSet {
133 self.journaled_state.precompile_addresses()
134 }
135
136 fn set_spec_id(&mut self, spec_id: SpecId) {
137 self.journaled_state.set_spec_id(spec_id);
138 }
139
140 fn set_eip7708_config(&mut self, disabled: bool, delayed_burn_disabled: bool) {
141 self.journaled_state
142 .set_eip7708_config(disabled, delayed_burn_disabled);
143 }
144
145 fn touch_account(&mut self, address: Address) {
146 self.journaled_state.touch_account(address);
147 }
148
149 fn transfer(
150 &mut self,
151 from: Address,
152 to: Address,
153 balance: U256,
154 ) -> Result<Option<TransferError>, Infallible> {
155 self.journaled_state.transfer(from, to, balance)
156 }
157
158 fn transfer_loaded(
159 &mut self,
160 from: Address,
161 to: Address,
162 balance: U256,
163 ) -> Option<TransferError> {
164 self.journaled_state.transfer_loaded(from, to, balance)
165 }
166
167 fn load_account(&mut self, address: Address) -> Result<StateLoad<&Account>, Infallible> {
168 self.journaled_state.load_account(address)
169 }
170
171 fn load_account_with_code(
172 &mut self,
173 address: Address,
174 ) -> Result<StateLoad<&Account>, Infallible> {
175 self.journaled_state.load_account_with_code(address)
176 }
177
178 fn load_account_delegated(
179 &mut self,
180 address: Address,
181 ) -> Result<StateLoad<AccountLoad>, Infallible> {
182 self.journaled_state.load_account_delegated(address)
183 }
184
185 fn set_code_with_hash(&mut self, address: Address, code: Bytecode, hash: B256) {
186 self.journaled_state.set_code_with_hash(address, code, hash);
187 }
188
189 fn code(
190 &mut self,
191 address: Address,
192 ) -> Result<StateLoad<revm::primitives::Bytes>, <Self::Database as Database>::Error> {
193 self.journaled_state.code(address)
194 }
195
196 fn code_hash(
197 &mut self,
198 address: Address,
199 ) -> Result<StateLoad<B256>, <Self::Database as Database>::Error> {
200 self.journaled_state.code_hash(address)
201 }
202
203 fn clear(&mut self) {
204 self.journaled_state.clear();
205 }
206
207 fn checkpoint(&mut self) -> JournalCheckpoint {
208 self.journaled_state.checkpoint()
209 }
210
211 fn checkpoint_commit(&mut self) {
212 self.journaled_state.checkpoint_commit()
213 }
214
215 fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint) {
216 self.journaled_state.checkpoint_revert(checkpoint)
217 }
218
219 fn create_account_checkpoint(
220 &mut self,
221 caller: Address,
222 address: Address,
223 balance: U256,
224 spec_id: SpecId,
225 ) -> Result<JournalCheckpoint, TransferError> {
226 self.journaled_state
227 .create_account_checkpoint(caller, address, balance, spec_id)
228 }
229
230 #[inline]
232 fn depth(&self) -> usize {
233 self.journaled_state.depth()
234 }
235
236 fn finalize(&mut self) -> Self::State {
237 self.journaled_state.finalize()
238 }
239
240 fn caller_accounting_journal_entry(
241 &mut self,
242 address: Address,
243 old_balance: U256,
244 bump_nonce: bool,
245 ) {
246 #[expect(deprecated)]
247 self.journaled_state
248 .caller_accounting_journal_entry(address, old_balance, bump_nonce)
249 }
250
251 fn balance_incr(
252 &mut self,
253 address: Address,
254 balance: U256,
255 ) -> Result<(), <Self::Database as Database>::Error> {
256 self.journaled_state.balance_incr(address, balance)
257 }
258
259 fn nonce_bump_journal_entry(&mut self, address: Address) {
260 #[expect(deprecated)]
261 self.journaled_state.nonce_bump_journal_entry(address)
262 }
263
264 fn take_logs(&mut self) -> Vec<Log> {
265 self.journaled_state.take_logs()
266 }
267
268 fn commit_tx(&mut self) {
269 self.journaled_state.commit_tx()
270 }
271
272 fn discard_tx(&mut self) {
273 self.journaled_state.discard_tx()
274 }
275
276 fn sload_skip_cold_load(
277 &mut self,
278 address: Address,
279 key: StorageKey,
280 skip_cold_load: bool,
281 ) -> Result<StateLoad<StorageValue>, JournalLoadError<<Self::Database as Database>::Error>>
282 {
283 self.journaled_state
284 .sload_skip_cold_load(address, key, skip_cold_load)
285 }
286
287 fn sstore_skip_cold_load(
288 &mut self,
289 address: Address,
290 key: StorageKey,
291 value: StorageValue,
292 skip_cold_load: bool,
293 ) -> Result<StateLoad<SStoreResult>, JournalLoadError<<Self::Database as Database>::Error>>
294 {
295 self.journaled_state
296 .sstore_skip_cold_load(address, key, value, skip_cold_load)
297 }
298
299 fn load_account_mut_skip_cold_load(
300 &mut self,
301 address: Address,
302 skip_cold_load: bool,
303 ) -> Result<StateLoad<Self::JournaledAccount<'_>>, JournalLoadError<Infallible>> {
304 self.journaled_state
305 .load_account_mut_skip_cold_load(address, skip_cold_load)
306 }
307
308 fn load_account_info_skip_cold_load(
309 &mut self,
310 address: Address,
311 load_code: bool,
312 skip_cold_load: bool,
313 ) -> Result<AccountInfoLoad<'_>, JournalLoadError<Infallible>> {
314 self.journaled_state
315 .load_account_info_skip_cold_load(address, load_code, skip_cold_load)
316 }
317
318 fn load_account_mut_optional_code(
319 &mut self,
320 address: Address,
321 load_code: bool,
322 ) -> Result<StateLoad<Self::JournaledAccount<'_>>, Infallible> {
323 self.journaled_state
324 .load_account_mut_optional_code(address, load_code)
325 }
326}
327
328impl JournalExt for Backend {
329 fn journal(&self) -> &[JournalEntry] {
330 self.journaled_state.journal()
331 }
332}
333
334trait DatabaseExt: JournalTr {
337 fn method_that_takes_inspector_as_argument<InspectorT, BlockT, TxT, CfgT>(
340 &mut self,
341 env: Env<BlockT, TxT, CfgT>,
342 inspector: InspectorT,
343 ) -> anyhow::Result<()>
344 where
345 InspectorT: Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>, EthInterpreter>,
346 BlockT: Block,
347 TxT: Transaction + Clone,
348 CfgT: Cfg;
349
350 fn method_that_constructs_inspector<BlockT, TxT, CfgT>(
352 &mut self,
353 env: Env<BlockT, TxT, CfgT>,
354 ) -> anyhow::Result<()>
355 where
356 BlockT: Block,
357 TxT: Transaction + Clone,
358 CfgT: Cfg;
359}
360
361impl DatabaseExt for Backend {
362 fn method_that_takes_inspector_as_argument<InspectorT, BlockT, TxT, CfgT>(
363 &mut self,
364 env: Env<BlockT, TxT, CfgT>,
365 inspector: InspectorT,
366 ) -> anyhow::Result<()>
367 where
368 InspectorT: Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>, EthInterpreter>,
369 BlockT: Block,
370 TxT: Transaction + Clone,
371 CfgT: Cfg,
372 {
373 commit_transaction(self, env, inspector)?;
374 self.method_with_inspector_counter += 1;
375 Ok(())
376 }
377
378 fn method_that_constructs_inspector<BlockT, TxT, CfgT>(
379 &mut self,
380 env: Env<BlockT, TxT, CfgT>,
381 ) -> anyhow::Result<()>
382 where
383 BlockT: Block,
384 TxT: Transaction + Clone,
385 CfgT: Cfg,
386 {
387 let inspector = TracerEip3155::new(Box::new(std::io::sink()));
388 commit_transaction(self, env, inspector)?;
389
390 self.method_without_inspector_counter += 1;
391 Ok(())
392 }
393}
394
395#[derive(Clone, Default)]
398struct Cheatcodes<BlockT, TxT, CfgT> {
399 call_count: usize,
400 phantom: core::marker::PhantomData<(BlockT, TxT, CfgT)>,
401}
402
403impl<BlockT, TxT, CfgT> Cheatcodes<BlockT, TxT, CfgT>
404where
405 BlockT: Block + Clone,
406 TxT: Transaction + Clone,
407 CfgT: Cfg + Clone,
408{
409 fn apply_cheatcode(
410 &mut self,
411 context: &mut Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
412 ) -> anyhow::Result<()> {
413 let block = context.block.clone();
415 let tx = context.tx.clone();
416 let cfg = context.cfg.clone();
417
418 context
420 .journal_mut()
421 .method_that_takes_inspector_as_argument(
422 Env {
423 block: block.clone(),
424 tx: tx.clone(),
425 cfg: cfg.clone(),
426 },
427 self,
428 )?;
429
430 context
432 .journal_mut()
433 .method_that_constructs_inspector(Env { block, tx, cfg })?;
434 Ok(())
435 }
436}
437
438impl<BlockT, TxT, CfgT> Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>>
439 for Cheatcodes<BlockT, TxT, CfgT>
440where
441 BlockT: Block + Clone,
442 TxT: Transaction + Clone,
443 CfgT: Cfg + Clone,
444{
445 fn call(
447 &mut self,
448 context: &mut Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
449 _inputs: &mut CallInputs,
450 ) -> Option<CallOutcome> {
451 self.call_count += 1;
452 if self.call_count == 1 {
454 self.apply_cheatcode(context).unwrap();
457 }
458 None
459 }
460}
461
462#[derive(Clone, Debug)]
464struct Env<BlockT, TxT, CfgT> {
465 block: BlockT,
466 tx: TxT,
467 cfg: CfgT,
468}
469
470impl Env<BlockEnv, TxEnv, CfgEnv> {
471 fn mainnet() -> Self {
472 let mut cfg = CfgEnv::default();
474 cfg.disable_nonce_check = true;
475
476 Self {
477 block: BlockEnv::default(),
478 tx: TxEnv::default(),
479 cfg,
480 }
481 }
482}
483
484fn commit_transaction<InspectorT, BlockT, TxT, CfgT>(
487 backend: &mut Backend,
488 env: Env<BlockT, TxT, CfgT>,
489 inspector: InspectorT,
490) -> Result<(), EVMError<Infallible, InvalidTransaction>>
491where
492 InspectorT: Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>, EthInterpreter>,
493 BlockT: Block,
494 TxT: Transaction + Clone,
495 CfgT: Cfg,
496{
497 let new_backend = backend.clone();
502 let tx = env.tx.clone();
503
504 let context = Context {
505 tx: env.tx,
506 block: env.block,
507 cfg: env.cfg,
508 journaled_state: new_backend,
509 chain: (),
510 local: LocalContext::default(),
511 error: Ok(()),
512 };
513
514 let mut evm = Evm::new_with_inspector(
515 context,
516 inspector,
517 EthInstructions::new_mainnet_with_spec(SpecId::default()),
518 EthPrecompiles::new(SpecId::default()),
519 );
520
521 let state = evm.inspect_tx(tx)?.state;
522
523 backend.journaled_state.database.commit(state);
525 update_state(
526 &mut backend.journaled_state.inner.state,
527 &mut backend.journaled_state.database,
528 )?;
529
530 Ok(())
531}
532
533fn update_state<DB: Database>(state: &mut EvmState, db: &mut DB) -> Result<(), DB::Error> {
536 for (addr, acc) in state.iter_mut() {
537 acc.info = db.basic(*addr)?.unwrap_or_default();
538 for (key, val) in acc.storage.iter_mut() {
539 val.present_value = db.storage(*addr, *key)?;
540 }
541 }
542
543 Ok(())
544}
545
546fn main() -> anyhow::Result<()> {
547 let backend = Backend::new(SpecId::default(), InMemoryDB::default());
548 let mut inspector = Cheatcodes::<BlockEnv, TxEnv, CfgEnv>::default();
549 let env = Env::mainnet();
550 let tx = env.tx.clone();
551
552 let context = Context {
553 tx: env.tx,
554 block: env.block,
555 cfg: env.cfg,
556 journaled_state: backend,
557 chain: (),
558 local: LocalContext::default(),
559 error: Ok(()),
560 };
561
562 let mut evm = Evm::new_with_inspector(
563 context,
564 &mut inspector,
565 EthInstructions::new_mainnet_with_spec(SpecId::default()),
566 EthPrecompiles::new(SpecId::default()),
567 );
568 evm.inspect_tx(tx)?;
569
570 assert_eq!(evm.inspector.call_count, 2);
572 assert_eq!(evm.journaled_state.method_with_inspector_counter, 1);
573 assert_eq!(evm.journaled_state.method_without_inspector_counter, 1);
574
575 Ok(())
576}