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::{
22 instructions::{EthInstructions, InstructionProvider},
23 EthPrecompiles, PrecompileProvider,
24 },
25 inspector::{inspectors::TracerEip3155, JournalExt},
26 interpreter::{
27 interpreter::EthInterpreter, CallInputs, CallOutcome, InterpreterResult, SStoreResult,
28 SelfDestructResult, StateLoad,
29 },
30 primitives::{
31 hardfork::SpecId, Address, AddressMap, AddressSet, HashSet, Log, StorageKey, StorageValue,
32 B256, U256,
33 },
34 state::{Account, Bytecode, EvmState},
35 Context, Database, DatabaseCommit, InspectEvm, Inspector, Journal, JournalEntry,
36};
37use std::{convert::Infallible, fmt::Debug};
38
39#[derive(Clone, Debug)]
43struct Backend {
44 journaled_state: Journal<InMemoryDB>,
46 method_with_inspector_counter: usize,
48 method_without_inspector_counter: usize,
49}
50
51impl Backend {
52 fn new(spec: SpecId, db: InMemoryDB) -> Self {
53 let mut journaled_state = Journal::new(db);
54 journaled_state.set_spec_id(spec);
55 Self {
56 journaled_state,
57 method_with_inspector_counter: 0,
58 method_without_inspector_counter: 0,
59 }
60 }
61}
62
63impl JournalTr for Backend {
64 type Database = InMemoryDB;
65 type State = EvmState;
66 type JournaledAccount<'a> = JournaledAccount<'a, InMemoryDB, JournalEntry>;
67
68 fn new(database: InMemoryDB) -> Self {
69 Self::new(SpecId::default(), database)
70 }
71
72 fn db(&self) -> &Self::Database {
73 self.journaled_state.db()
74 }
75
76 fn db_mut(&mut self) -> &mut Self::Database {
77 self.journaled_state.db_mut()
78 }
79
80 fn sload(
81 &mut self,
82 address: Address,
83 key: StorageKey,
84 ) -> Result<StateLoad<StorageValue>, <Self::Database as Database>::Error> {
85 self.journaled_state.sload(address, key)
86 }
87
88 fn sstore(
89 &mut self,
90 address: Address,
91 key: StorageKey,
92 value: StorageValue,
93 ) -> Result<StateLoad<SStoreResult>, <Self::Database as Database>::Error> {
94 self.journaled_state.sstore(address, key, value)
95 }
96
97 fn tload(&mut self, address: Address, key: StorageKey) -> StorageValue {
98 self.journaled_state.tload(address, key)
99 }
100
101 fn tstore(&mut self, address: Address, key: StorageKey, value: StorageValue) {
102 self.journaled_state.tstore(address, key, value)
103 }
104
105 fn log(&mut self, log: Log) {
106 self.journaled_state.log(log)
107 }
108
109 fn logs(&self) -> &[Log] {
110 self.journaled_state.logs()
111 }
112
113 fn selfdestruct(
114 &mut self,
115 address: Address,
116 target: Address,
117 skip_cold_load: bool,
118 ) -> Result<StateLoad<SelfDestructResult>, JournalLoadError<Infallible>> {
119 self.journaled_state
120 .selfdestruct(address, target, skip_cold_load)
121 }
122
123 fn warm_access_list(&mut self, access_list: AddressMap<HashSet<StorageKey>>) {
124 self.journaled_state.warm_access_list(access_list);
125 }
126
127 fn warm_coinbase_account(&mut self, address: Address) {
128 self.journaled_state.warm_coinbase_account(address)
129 }
130
131 fn warm_precompiles(&mut self, addresses: AddressSet) {
132 self.journaled_state.warm_precompiles(addresses)
133 }
134
135 fn precompile_addresses(&self) -> &AddressSet {
136 self.journaled_state.precompile_addresses()
137 }
138
139 fn set_spec_id(&mut self, spec_id: SpecId) {
140 self.journaled_state.set_spec_id(spec_id);
141 }
142
143 fn set_eip7708_config(&mut self, disabled: bool, delayed_burn_disabled: bool) {
144 self.journaled_state
145 .set_eip7708_config(disabled, delayed_burn_disabled);
146 }
147
148 fn touch_account(&mut self, address: Address) {
149 self.journaled_state.touch_account(address);
150 }
151
152 fn transfer(
153 &mut self,
154 from: Address,
155 to: Address,
156 balance: U256,
157 ) -> Result<Option<TransferError>, Infallible> {
158 self.journaled_state.transfer(from, to, balance)
159 }
160
161 fn transfer_loaded(
162 &mut self,
163 from: Address,
164 to: Address,
165 balance: U256,
166 ) -> Option<TransferError> {
167 self.journaled_state.transfer_loaded(from, to, balance)
168 }
169
170 fn load_account(&mut self, address: Address) -> Result<StateLoad<&Account>, Infallible> {
171 self.journaled_state.load_account(address)
172 }
173
174 fn load_account_with_code(
175 &mut self,
176 address: Address,
177 ) -> Result<StateLoad<&Account>, Infallible> {
178 self.journaled_state.load_account_with_code(address)
179 }
180
181 fn load_account_delegated(
182 &mut self,
183 address: Address,
184 ) -> Result<StateLoad<AccountLoad>, Infallible> {
185 self.journaled_state.load_account_delegated(address)
186 }
187
188 fn set_code_with_hash(&mut self, address: Address, code: Bytecode, hash: B256) {
189 self.journaled_state.set_code_with_hash(address, code, hash);
190 }
191
192 fn code(
193 &mut self,
194 address: Address,
195 ) -> Result<StateLoad<revm::primitives::Bytes>, <Self::Database as Database>::Error> {
196 self.journaled_state.code(address)
197 }
198
199 fn code_hash(
200 &mut self,
201 address: Address,
202 ) -> Result<StateLoad<B256>, <Self::Database as Database>::Error> {
203 self.journaled_state.code_hash(address)
204 }
205
206 fn clear(&mut self) {
207 self.journaled_state.clear();
208 }
209
210 fn checkpoint(&mut self) -> JournalCheckpoint {
211 self.journaled_state.checkpoint()
212 }
213
214 fn checkpoint_commit(&mut self) {
215 self.journaled_state.checkpoint_commit()
216 }
217
218 fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint) {
219 self.journaled_state.checkpoint_revert(checkpoint)
220 }
221
222 fn create_account_checkpoint(
223 &mut self,
224 caller: Address,
225 address: Address,
226 balance: U256,
227 spec_id: SpecId,
228 ) -> Result<JournalCheckpoint, TransferError> {
229 self.journaled_state
230 .create_account_checkpoint(caller, address, balance, spec_id)
231 }
232
233 #[inline]
235 fn depth(&self) -> usize {
236 self.journaled_state.depth()
237 }
238
239 fn finalize(&mut self) -> Self::State {
240 self.journaled_state.finalize()
241 }
242
243 #[allow(deprecated)]
244 fn caller_accounting_journal_entry(
245 &mut self,
246 address: Address,
247 old_balance: U256,
248 bump_nonce: bool,
249 ) {
250 #[allow(deprecated)]
251 self.journaled_state
252 .caller_accounting_journal_entry(address, old_balance, bump_nonce)
253 }
254
255 fn balance_incr(
256 &mut self,
257 address: Address,
258 balance: U256,
259 ) -> Result<(), <Self::Database as Database>::Error> {
260 self.journaled_state.balance_incr(address, balance)
261 }
262
263 #[allow(deprecated)]
264 fn nonce_bump_journal_entry(&mut self, address: Address) {
265 #[allow(deprecated)]
266 self.journaled_state.nonce_bump_journal_entry(address)
267 }
268
269 fn take_logs(&mut self) -> Vec<Log> {
270 self.journaled_state.take_logs()
271 }
272
273 fn commit_tx(&mut self) {
274 self.journaled_state.commit_tx()
275 }
276
277 fn discard_tx(&mut self) {
278 self.journaled_state.discard_tx()
279 }
280
281 fn sload_skip_cold_load(
282 &mut self,
283 address: Address,
284 key: StorageKey,
285 skip_cold_load: bool,
286 ) -> Result<StateLoad<StorageValue>, JournalLoadError<<Self::Database as Database>::Error>>
287 {
288 self.journaled_state
289 .sload_skip_cold_load(address, key, skip_cold_load)
290 }
291
292 fn sstore_skip_cold_load(
293 &mut self,
294 address: Address,
295 key: StorageKey,
296 value: StorageValue,
297 skip_cold_load: bool,
298 ) -> Result<StateLoad<SStoreResult>, JournalLoadError<<Self::Database as Database>::Error>>
299 {
300 self.journaled_state
301 .sstore_skip_cold_load(address, key, value, skip_cold_load)
302 }
303
304 fn load_account_mut_skip_cold_load(
305 &mut self,
306 address: Address,
307 skip_cold_load: bool,
308 ) -> Result<StateLoad<Self::JournaledAccount<'_>>, Infallible> {
309 self.journaled_state
310 .load_account_mut_skip_cold_load(address, skip_cold_load)
311 }
312
313 fn load_account_info_skip_cold_load(
314 &mut self,
315 address: Address,
316 load_code: bool,
317 skip_cold_load: bool,
318 ) -> Result<AccountInfoLoad<'_>, JournalLoadError<Infallible>> {
319 self.journaled_state
320 .load_account_info_skip_cold_load(address, load_code, skip_cold_load)
321 }
322
323 fn load_account_mut_optional_code(
324 &mut self,
325 address: Address,
326 load_code: bool,
327 ) -> Result<StateLoad<Self::JournaledAccount<'_>>, Infallible> {
328 self.journaled_state
329 .load_account_mut_optional_code(address, load_code)
330 }
331}
332
333impl JournalExt for Backend {
334 fn journal(&self) -> &[JournalEntry] {
335 self.journaled_state.journal()
336 }
337
338 fn evm_state(&self) -> &EvmState {
339 self.journaled_state.evm_state()
340 }
341
342 fn evm_state_mut(&mut self) -> &mut EvmState {
343 self.journaled_state.evm_state_mut()
344 }
345}
346
347trait DatabaseExt: JournalTr {
350 fn method_that_takes_inspector_as_argument<
353 InspectorT,
354 BlockT,
355 TxT,
356 CfgT,
357 InstructionProviderT,
358 PrecompileT,
359 >(
360 &mut self,
361 env: Env<BlockT, TxT, CfgT>,
362 inspector: InspectorT,
363 ) -> anyhow::Result<()>
364 where
365 InspectorT: Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>, EthInterpreter>,
366 BlockT: Block,
367 TxT: Transaction + Clone,
368 CfgT: Cfg,
369 InstructionProviderT: InstructionProvider<
370 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
371 InterpreterTypes = EthInterpreter,
372 > + Default,
373 PrecompileT: PrecompileProvider<
374 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
375 Output = InterpreterResult,
376 > + Default;
377
378 fn method_that_constructs_inspector<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>(
380 &mut self,
381 env: Env<BlockT, TxT, CfgT>,
382 ) -> anyhow::Result<()>
383 where
384 BlockT: Block,
385 TxT: Transaction + Clone,
386 CfgT: Cfg,
387 InstructionProviderT: InstructionProvider<
388 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
389 InterpreterTypes = EthInterpreter,
390 > + Default,
391 PrecompileT: PrecompileProvider<
392 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
393 Output = InterpreterResult,
394 > + Default;
395}
396
397impl DatabaseExt for Backend {
398 fn method_that_takes_inspector_as_argument<
399 InspectorT,
400 BlockT,
401 TxT,
402 CfgT,
403 InstructionProviderT,
404 PrecompileT,
405 >(
406 &mut self,
407 env: Env<BlockT, TxT, CfgT>,
408 inspector: InspectorT,
409 ) -> anyhow::Result<()>
410 where
411 InspectorT: Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>, EthInterpreter>,
412 BlockT: Block,
413 TxT: Transaction + Clone,
414 CfgT: Cfg,
415 InstructionProviderT: InstructionProvider<
416 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
417 InterpreterTypes = EthInterpreter,
418 > + Default,
419 PrecompileT: PrecompileProvider<
420 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
421 Output = InterpreterResult,
422 > + Default,
423 {
424 commit_transaction::<InspectorT, BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>(
425 self, env, inspector,
426 )?;
427 self.method_with_inspector_counter += 1;
428 Ok(())
429 }
430
431 fn method_that_constructs_inspector<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>(
432 &mut self,
433 env: Env<BlockT, TxT, CfgT>,
434 ) -> anyhow::Result<()>
435 where
436 BlockT: Block,
437 TxT: Transaction + Clone,
438 CfgT: Cfg,
439 InstructionProviderT: InstructionProvider<
440 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
441 InterpreterTypes = EthInterpreter,
442 > + Default,
443 PrecompileT: PrecompileProvider<
444 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
445 Output = InterpreterResult,
446 > + Default,
447 {
448 let inspector = TracerEip3155::new(Box::new(std::io::sink()));
449 commit_transaction::<
450 TracerEip3155,
452 BlockT,
453 TxT,
454 CfgT,
455 InstructionProviderT,
456 PrecompileT,
457 >(self, env, inspector)?;
458
459 self.method_without_inspector_counter += 1;
460 Ok(())
461 }
462}
463
464#[derive(Clone, Default)]
467struct Cheatcodes<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT> {
468 call_count: usize,
469 phantom: core::marker::PhantomData<(BlockT, TxT, CfgT, InstructionProviderT, PrecompileT)>,
470}
471
472impl<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>
473 Cheatcodes<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>
474where
475 BlockT: Block + Clone,
476 TxT: Transaction + Clone,
477 CfgT: Cfg + Clone,
478 InstructionProviderT: InstructionProvider<
479 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
480 InterpreterTypes = EthInterpreter,
481 > + Default,
482 PrecompileT: PrecompileProvider<
483 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
484 Output = InterpreterResult,
485 > + Default,
486{
487 fn apply_cheatcode(
488 &mut self,
489 context: &mut Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
490 ) -> anyhow::Result<()> {
491 let block = context.block.clone();
493 let tx = context.tx.clone();
494 let cfg = context.cfg.clone();
495
496 context
498 .journal_mut()
499 .method_that_takes_inspector_as_argument::<_, _, _, _, InstructionProviderT, PrecompileT>(
500 Env {
501 block: block.clone(),
502 tx: tx.clone(),
503 cfg: cfg.clone(),
504 },
505 self,
506 )?;
507
508 context
510 .journal_mut()
511 .method_that_constructs_inspector::<_, _, _, InstructionProviderT, PrecompileT>(
512 Env { block, tx, cfg },
513 )?;
514 Ok(())
515 }
516}
517
518impl<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>
519 Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>>
520 for Cheatcodes<BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>
521where
522 BlockT: Block + Clone,
523 TxT: Transaction + Clone,
524 CfgT: Cfg + Clone,
525 InstructionProviderT: InstructionProvider<
526 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
527 InterpreterTypes = EthInterpreter,
528 > + Default,
529 PrecompileT: PrecompileProvider<
530 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
531 Output = InterpreterResult,
532 > + Default,
533{
534 fn call(
536 &mut self,
537 context: &mut Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
538 _inputs: &mut CallInputs,
539 ) -> Option<CallOutcome> {
540 self.call_count += 1;
541 if self.call_count == 1 {
543 self.apply_cheatcode(context).unwrap();
546 }
547 None
548 }
549}
550
551#[derive(Clone, Debug)]
553struct Env<BlockT, TxT, CfgT> {
554 block: BlockT,
555 tx: TxT,
556 cfg: CfgT,
557}
558
559impl Env<BlockEnv, TxEnv, CfgEnv> {
560 fn mainnet() -> Self {
561 let mut cfg = CfgEnv::default();
563 cfg.disable_nonce_check = true;
564
565 Self {
566 block: BlockEnv::default(),
567 tx: TxEnv::default(),
568 cfg,
569 }
570 }
571}
572
573fn commit_transaction<InspectorT, BlockT, TxT, CfgT, InstructionProviderT, PrecompileT>(
576 backend: &mut Backend,
577 env: Env<BlockT, TxT, CfgT>,
578 inspector: InspectorT,
579) -> Result<(), EVMError<Infallible, InvalidTransaction>>
580where
581 InspectorT: Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>, EthInterpreter>,
582 BlockT: Block,
583 TxT: Transaction + Clone,
584 CfgT: Cfg,
585 InstructionProviderT: InstructionProvider<
586 Context = Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
587 InterpreterTypes = EthInterpreter,
588 > + Default,
589 PrecompileT: PrecompileProvider<
590 Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
591 Output = InterpreterResult,
592 > + Default,
593{
594 let new_backend = backend.clone();
599 let tx = env.tx.clone();
600
601 let context = Context {
602 tx: env.tx,
603 block: env.block,
604 cfg: env.cfg,
605 journaled_state: new_backend,
606 chain: (),
607 local: LocalContext::default(),
608 error: Ok(()),
609 };
610
611 let mut evm = Evm::new_with_inspector(
612 context,
613 inspector,
614 InstructionProviderT::default(),
615 PrecompileT::default(),
616 );
617
618 let state = evm.inspect_tx(tx)?.state;
619
620 backend.journaled_state.database.commit(state);
622 update_state(
623 &mut backend.journaled_state.inner.state,
624 &mut backend.journaled_state.database,
625 )?;
626
627 Ok(())
628}
629
630fn update_state<DB: Database>(state: &mut EvmState, db: &mut DB) -> Result<(), DB::Error> {
633 for (addr, acc) in state.iter_mut() {
634 acc.info = db.basic(*addr)?.unwrap_or_default();
635 for (key, val) in acc.storage.iter_mut() {
636 val.present_value = db.storage(*addr, *key)?;
637 }
638 }
639
640 Ok(())
641}
642
643fn main() -> anyhow::Result<()> {
644 let backend = Backend::new(SpecId::default(), InMemoryDB::default());
645 let mut inspector = Cheatcodes::<
646 BlockEnv,
647 TxEnv,
648 CfgEnv,
649 EthInstructions<EthInterpreter, Context<BlockEnv, TxEnv, CfgEnv, InMemoryDB, Backend>>,
650 EthPrecompiles,
651 >::default();
652 let env = Env::mainnet();
653 let tx = env.tx.clone();
654
655 let context = Context {
656 tx: env.tx,
657 block: env.block,
658 cfg: env.cfg,
659 journaled_state: backend,
660 chain: (),
661 local: LocalContext::default(),
662 error: Ok(()),
663 };
664
665 let mut evm = Evm::new_with_inspector(
666 context,
667 &mut inspector,
668 EthInstructions::default(),
669 EthPrecompiles::default(),
670 );
671 evm.inspect_tx(tx)?;
672
673 assert_eq!(evm.inspector.call_count, 2);
675 assert_eq!(evm.journaled_state.method_with_inspector_counter, 1);
676 assert_eq!(evm.journaled_state.method_without_inspector_counter, 1);
677
678 Ok(())
679}