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(&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 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 #[allow(deprecated)]
241 fn caller_accounting_journal_entry(
242 &mut self,
243 address: Address,
244 old_balance: U256,
245 bump_nonce: bool,
246 ) {
247 #[allow(deprecated)]
248 self.journaled_state
249 .caller_accounting_journal_entry(address, old_balance, bump_nonce)
250 }
251
252 fn balance_incr(
253 &mut self,
254 address: Address,
255 balance: U256,
256 ) -> Result<(), <Self::Database as Database>::Error> {
257 self.journaled_state.balance_incr(address, balance)
258 }
259
260 #[allow(deprecated)]
261 fn nonce_bump_journal_entry(&mut self, address: Address) {
262 #[allow(deprecated)]
263 self.journaled_state.nonce_bump_journal_entry(address)
264 }
265
266 fn take_logs(&mut self) -> Vec<Log> {
267 self.journaled_state.take_logs()
268 }
269
270 fn commit_tx(&mut self) {
271 self.journaled_state.commit_tx()
272 }
273
274 fn discard_tx(&mut self) {
275 self.journaled_state.discard_tx()
276 }
277
278 fn sload_skip_cold_load(
279 &mut self,
280 address: Address,
281 key: StorageKey,
282 skip_cold_load: bool,
283 ) -> Result<StateLoad<StorageValue>, JournalLoadError<<Self::Database as Database>::Error>>
284 {
285 self.journaled_state
286 .sload_skip_cold_load(address, key, skip_cold_load)
287 }
288
289 fn sstore_skip_cold_load(
290 &mut self,
291 address: Address,
292 key: StorageKey,
293 value: StorageValue,
294 skip_cold_load: bool,
295 ) -> Result<StateLoad<SStoreResult>, JournalLoadError<<Self::Database as Database>::Error>>
296 {
297 self.journaled_state
298 .sstore_skip_cold_load(address, key, value, skip_cold_load)
299 }
300
301 fn load_account_mut_skip_cold_load(
302 &mut self,
303 address: Address,
304 skip_cold_load: bool,
305 ) -> Result<StateLoad<Self::JournaledAccount<'_>>, Infallible> {
306 self.journaled_state
307 .load_account_mut_skip_cold_load(address, skip_cold_load)
308 }
309
310 fn load_account_info_skip_cold_load(
311 &mut self,
312 address: Address,
313 load_code: bool,
314 skip_cold_load: bool,
315 ) -> Result<AccountInfoLoad<'_>, JournalLoadError<Infallible>> {
316 self.journaled_state
317 .load_account_info_skip_cold_load(address, load_code, skip_cold_load)
318 }
319
320 fn load_account_mut_optional_code(
321 &mut self,
322 address: Address,
323 load_code: bool,
324 ) -> Result<StateLoad<Self::JournaledAccount<'_>>, Infallible> {
325 self.journaled_state
326 .load_account_mut_optional_code(address, load_code)
327 }
328}
329
330impl JournalExt for Backend {
331 fn journal(&self) -> &[JournalEntry] {
332 self.journaled_state.journal()
333 }
334
335 fn evm_state(&self) -> &EvmState {
336 self.journaled_state.evm_state()
337 }
338
339 fn evm_state_mut(&mut self) -> &mut EvmState {
340 self.journaled_state.evm_state_mut()
341 }
342}
343
344trait DatabaseExt: JournalTr {
347 fn method_that_takes_inspector_as_argument<InspectorT, BlockT, TxT, CfgT>(
350 &mut self,
351 env: Env<BlockT, TxT, CfgT>,
352 inspector: InspectorT,
353 ) -> anyhow::Result<()>
354 where
355 InspectorT: Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>, EthInterpreter>,
356 BlockT: Block,
357 TxT: Transaction + Clone,
358 CfgT: Cfg;
359
360 fn method_that_constructs_inspector<BlockT, TxT, CfgT>(
362 &mut self,
363 env: Env<BlockT, TxT, CfgT>,
364 ) -> anyhow::Result<()>
365 where
366 BlockT: Block,
367 TxT: Transaction + Clone,
368 CfgT: Cfg;
369}
370
371impl DatabaseExt for Backend {
372 fn method_that_takes_inspector_as_argument<InspectorT, BlockT, TxT, CfgT>(
373 &mut self,
374 env: Env<BlockT, TxT, CfgT>,
375 inspector: InspectorT,
376 ) -> anyhow::Result<()>
377 where
378 InspectorT: Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>, EthInterpreter>,
379 BlockT: Block,
380 TxT: Transaction + Clone,
381 CfgT: Cfg,
382 {
383 commit_transaction(self, env, inspector)?;
384 self.method_with_inspector_counter += 1;
385 Ok(())
386 }
387
388 fn method_that_constructs_inspector<BlockT, TxT, CfgT>(
389 &mut self,
390 env: Env<BlockT, TxT, CfgT>,
391 ) -> anyhow::Result<()>
392 where
393 BlockT: Block,
394 TxT: Transaction + Clone,
395 CfgT: Cfg,
396 {
397 let inspector = TracerEip3155::new(Box::new(std::io::sink()));
398 commit_transaction(self, env, inspector)?;
399
400 self.method_without_inspector_counter += 1;
401 Ok(())
402 }
403}
404
405#[derive(Clone, Default)]
408struct Cheatcodes<BlockT, TxT, CfgT> {
409 call_count: usize,
410 phantom: core::marker::PhantomData<(BlockT, TxT, CfgT)>,
411}
412
413impl<BlockT, TxT, CfgT> Cheatcodes<BlockT, TxT, CfgT>
414where
415 BlockT: Block + Clone,
416 TxT: Transaction + Clone,
417 CfgT: Cfg + Clone,
418{
419 fn apply_cheatcode(
420 &mut self,
421 context: &mut Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
422 ) -> anyhow::Result<()> {
423 let block = context.block.clone();
425 let tx = context.tx.clone();
426 let cfg = context.cfg.clone();
427
428 context
430 .journal_mut()
431 .method_that_takes_inspector_as_argument(
432 Env {
433 block: block.clone(),
434 tx: tx.clone(),
435 cfg: cfg.clone(),
436 },
437 self,
438 )?;
439
440 context
442 .journal_mut()
443 .method_that_constructs_inspector(Env { block, tx, cfg })?;
444 Ok(())
445 }
446}
447
448impl<BlockT, TxT, CfgT> Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>>
449 for Cheatcodes<BlockT, TxT, CfgT>
450where
451 BlockT: Block + Clone,
452 TxT: Transaction + Clone,
453 CfgT: Cfg + Clone,
454{
455 fn call(
457 &mut self,
458 context: &mut Context<BlockT, TxT, CfgT, InMemoryDB, Backend>,
459 _inputs: &mut CallInputs,
460 ) -> Option<CallOutcome> {
461 self.call_count += 1;
462 if self.call_count == 1 {
464 self.apply_cheatcode(context).unwrap();
467 }
468 None
469 }
470}
471
472#[derive(Clone, Debug)]
474struct Env<BlockT, TxT, CfgT> {
475 block: BlockT,
476 tx: TxT,
477 cfg: CfgT,
478}
479
480impl Env<BlockEnv, TxEnv, CfgEnv> {
481 fn mainnet() -> Self {
482 let mut cfg = CfgEnv::default();
484 cfg.disable_nonce_check = true;
485
486 Self {
487 block: BlockEnv::default(),
488 tx: TxEnv::default(),
489 cfg,
490 }
491 }
492}
493
494fn commit_transaction<InspectorT, BlockT, TxT, CfgT>(
497 backend: &mut Backend,
498 env: Env<BlockT, TxT, CfgT>,
499 inspector: InspectorT,
500) -> Result<(), EVMError<Infallible, InvalidTransaction>>
501where
502 InspectorT: Inspector<Context<BlockT, TxT, CfgT, InMemoryDB, Backend>, EthInterpreter>,
503 BlockT: Block,
504 TxT: Transaction + Clone,
505 CfgT: Cfg,
506{
507 let new_backend = backend.clone();
512 let tx = env.tx.clone();
513
514 let context = Context {
515 tx: env.tx,
516 block: env.block,
517 cfg: env.cfg,
518 journaled_state: new_backend,
519 chain: (),
520 local: LocalContext::default(),
521 error: Ok(()),
522 };
523
524 let mut evm = Evm::new_with_inspector(
525 context,
526 inspector,
527 EthInstructions::new_mainnet_with_spec(SpecId::default()),
528 EthPrecompiles::new(SpecId::default()),
529 );
530
531 let state = evm.inspect_tx(tx)?.state;
532
533 backend.journaled_state.database.commit(state);
535 update_state(
536 &mut backend.journaled_state.inner.state,
537 &mut backend.journaled_state.database,
538 )?;
539
540 Ok(())
541}
542
543fn update_state<DB: Database>(state: &mut EvmState, db: &mut DB) -> Result<(), DB::Error> {
546 for (addr, acc) in state.iter_mut() {
547 acc.info = db.basic(*addr)?.unwrap_or_default();
548 for (key, val) in acc.storage.iter_mut() {
549 val.present_value = db.storage(*addr, *key)?;
550 }
551 }
552
553 Ok(())
554}
555
556fn main() -> anyhow::Result<()> {
557 let backend = Backend::new(SpecId::default(), InMemoryDB::default());
558 let mut inspector = Cheatcodes::<BlockEnv, TxEnv, CfgEnv>::default();
559 let env = Env::mainnet();
560 let tx = env.tx.clone();
561
562 let context = Context {
563 tx: env.tx,
564 block: env.block,
565 cfg: env.cfg,
566 journaled_state: backend,
567 chain: (),
568 local: LocalContext::default(),
569 error: Ok(()),
570 };
571
572 let mut evm = Evm::new_with_inspector(
573 context,
574 &mut inspector,
575 EthInstructions::new_mainnet_with_spec(SpecId::default()),
576 EthPrecompiles::new(SpecId::default()),
577 );
578 evm.inspect_tx(tx)?;
579
580 assert_eq!(evm.inspector.call_count, 2);
582 assert_eq!(evm.journaled_state.method_with_inspector_counter, 1);
583 assert_eq!(evm.journaled_state.method_without_inspector_counter, 1);
584
585 Ok(())
586}