1use crate::merkle_trie::compute_test_roots;
4use revm_context::{Context, block::BlockEnv, cfg::CfgEnv, tx::TxEnv};
5use revm_context_interface::result::{EVMError, ExecutionResult, HaltReason, InvalidTransaction};
6use revm_database::{self as database, bal::EvmDatabaseError};
7use revm_database_interface::{DatabaseCommit, EmptyDB};
8use revm_handler::{ExecuteCommitEvm, Handler, MainBuilder, MainContext};
9use revm_inspector::{InspectCommitEvm, inspectors::TracerEip3155};
10use revm_primitives::{B256, Bytes};
11use revmc::{revm_evm::JitEvm, runtime::JitBackend};
12use std::{convert::Infallible, io::stderr};
13
14type ExecResult =
15 Result<ExecutionResult<HaltReason>, EVMError<EvmDatabaseError<Infallible>, InvalidTransaction>>;
16
17#[derive(Debug)]
19pub struct ExecutionSnapshot {
20 pub status: ExecStatus,
21 pub output: Option<Bytes>,
22 pub gas_used: u64,
23 pub state_root: B256,
24 pub logs_root: B256,
25 pub post_state_dump: String,
26}
27
28#[derive(Debug)]
30pub enum ExecStatus {
31 Success(String),
32 Revert,
33 Halt(String),
34 Error(String),
35}
36
37impl std::fmt::Display for ExecStatus {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 match self {
40 Self::Success(r) => write!(f, "Success({r})"),
41 Self::Revert => write!(f, "Revert"),
42 Self::Halt(r) => write!(f, "Halt({r})"),
43 Self::Error(e) => write!(f, "Error({e})"),
44 }
45 }
46}
47
48fn snapshot_from_result(
49 exec_result: &ExecResult,
50 db: &database::State<EmptyDB>,
51) -> ExecutionSnapshot {
52 let validation = compute_test_roots(exec_result, db);
53
54 let (status, output, gas_used) = match exec_result {
55 Ok(result) => {
56 let status = match result {
57 ExecutionResult::Success { reason, .. } => {
58 ExecStatus::Success(format!("{reason:?}"))
59 }
60 ExecutionResult::Revert { .. } => ExecStatus::Revert,
61 ExecutionResult::Halt { reason, .. } => ExecStatus::Halt(format!("{reason:?}")),
62 };
63 (status, result.output().cloned(), result.tx_gas_used())
64 }
65 Err(e) => (ExecStatus::Error(e.to_string()), None, 0),
66 };
67
68 ExecutionSnapshot {
69 status,
70 output,
71 gas_used,
72 state_root: validation.state_root,
73 logs_root: validation.logs_root,
74 post_state_dump: db.cache.pretty_print(),
75 }
76}
77
78pub fn run_interpreter(
80 cfg: &CfgEnv,
81 block: &BlockEnv,
82 tx: &TxEnv,
83 cache_state: &database::CacheState,
84) -> ExecutionSnapshot {
85 let prestate = cache_state.clone();
86 let mut state =
87 database::State::builder().with_cached_prestate(prestate).with_bundle_update().build();
88
89 let mut evm = Context::mainnet()
90 .with_block(block)
91 .with_tx(tx)
92 .with_cfg(cfg.clone())
93 .with_db(&mut state)
94 .build_mainnet();
95 let exec_result = evm.transact_commit(tx);
96 let db = evm.ctx.journaled_state.database;
97
98 snapshot_from_result(&exec_result, db)
99}
100
101pub fn run_jit(
103 backend: &JitBackend,
104 cfg: &CfgEnv,
105 block: &BlockEnv,
106 tx: &TxEnv,
107 cache_state: &database::CacheState,
108) -> ExecutionSnapshot {
109 let prestate = cache_state.clone();
110 let state =
111 database::State::builder().with_cached_prestate(prestate).with_bundle_update().build();
112
113 let evm_context = Context::mainnet()
114 .with_block(block.clone())
115 .with_tx(tx.clone())
116 .with_cfg(cfg.clone())
117 .with_db(state);
118 let inner = evm_context.build_mainnet();
119 let mut evm = JitEvm::new(inner, backend.clone());
120 let mut handler = revm_handler::MainnetHandler::default();
121 let exec_result = handler.run(&mut evm);
122 if exec_result.is_ok() {
123 let s = evm.ctx.journaled_state.finalize();
124 DatabaseCommit::commit(&mut evm.ctx.journaled_state.database, s);
125 }
126
127 snapshot_from_result(&exec_result, &evm.ctx.journaled_state.database)
128}
129
130pub fn trace_interpreter(
132 cfg: &CfgEnv,
133 block: &BlockEnv,
134 tx: &TxEnv,
135 cache_state: &database::CacheState,
136) {
137 let prestate = cache_state.clone();
138 let mut state =
139 database::State::builder().with_cached_prestate(prestate).with_bundle_update().build();
140
141 let mut evm = Context::mainnet()
142 .with_db(&mut state)
143 .with_block(block)
144 .with_tx(tx)
145 .with_cfg(cfg.clone())
146 .build_mainnet_with_inspector(TracerEip3155::buffered(stderr()).without_summary());
147 let exec_result = evm.inspect_tx_commit(tx);
148
149 eprintln!("\nExecution result: {exec_result:#?}");
150 eprintln!("\nState after:\n{}", evm.ctx.journaled_state.database.cache.pretty_print());
151}
152
153pub fn compare(interp: &ExecutionSnapshot, jit: &ExecutionSnapshot) -> Vec<Mismatch> {
155 let mut mismatches = Vec::new();
156
157 let interp_status = format!("{}", interp.status);
158 let jit_status = format!("{}", jit.status);
159 if interp_status != jit_status {
160 mismatches.push(Mismatch { field: "status", interpreter: interp_status, jit: jit_status });
161 }
162
163 if interp.output != jit.output {
164 mismatches.push(Mismatch {
165 field: "output",
166 interpreter: format!("{:?}", interp.output),
167 jit: format!("{:?}", jit.output),
168 });
169 }
170
171 if interp.gas_used != jit.gas_used {
172 mismatches.push(Mismatch {
173 field: "gas_used",
174 interpreter: interp.gas_used.to_string(),
175 jit: jit.gas_used.to_string(),
176 });
177 }
178
179 if interp.state_root != jit.state_root {
180 mismatches.push(Mismatch {
181 field: "state_root",
182 interpreter: format!("{}", interp.state_root),
183 jit: format!("{}", jit.state_root),
184 });
185 }
186
187 if interp.logs_root != jit.logs_root {
188 mismatches.push(Mismatch {
189 field: "logs_root",
190 interpreter: format!("{}", interp.logs_root),
191 jit: format!("{}", jit.logs_root),
192 });
193 }
194
195 mismatches
196}
197
198pub struct Mismatch {
200 pub field: &'static str,
201 pub interpreter: String,
202 pub jit: String,
203}
204
205impl std::fmt::Display for Mismatch {
206 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
207 write!(f, " {}: interpreter={}, jit={}", self.field, self.interpreter, self.jit)
208 }
209}