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