1use crate::{Inspector, InspectorEvmTr, JournalExt};
2use context::{result::ExecutionResult, ContextTr, JournalEntry, Transaction};
3use handler::{evm::FrameTr, EvmTr, FrameResult, Handler, ItemOrResult};
4use interpreter::{
5 instructions::InstructionTable,
6 interpreter_types::{Jumps, LoopControl},
7 FrameInput, Host, InitialAndFloorGas, InstructionResult, Interpreter, InterpreterAction,
8 InterpreterTypes,
9};
10use state::bytecode::opcode;
11
12pub trait InspectorHandler: Handler
31where
32 Self::Evm:
33 InspectorEvmTr<Inspector: Inspector<<<Self as Handler>::Evm as EvmTr>::Context, Self::IT>>,
34{
35 type IT: InterpreterTypes;
37
38 fn inspect_run(
42 &mut self,
43 evm: &mut Self::Evm,
44 ) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
45 match self.inspect_run_without_catch_error(evm) {
46 Ok(output) => Ok(output),
47 Err(e) => self.catch_error(evm, e),
48 }
49 }
50
51 fn inspect_run_without_catch_error(
55 &mut self,
56 evm: &mut Self::Evm,
57 ) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
58 let init_and_floor_gas = self.validate(evm)?;
59 let eip7702_refund = self.pre_execution(evm)? as i64;
60 let mut frame_result = self.inspect_execution(evm, &init_and_floor_gas)?;
61 self.post_execution(evm, &mut frame_result, init_and_floor_gas, eip7702_refund)?;
62 self.execution_result(evm, frame_result)
63 }
64
65 fn inspect_execution(
69 &mut self,
70 evm: &mut Self::Evm,
71 init_and_floor_gas: &InitialAndFloorGas,
72 ) -> Result<FrameResult, Self::Error> {
73 let gas_limit = evm.ctx().tx().gas_limit() - init_and_floor_gas.initial_gas;
74 let first_frame_input = self.first_frame_input(evm, gas_limit)?;
76
77 let mut frame_result = self.inspect_run_exec_loop(evm, first_frame_input)?;
79
80 self.last_frame_result(evm, &mut frame_result)?;
82 Ok(frame_result)
83 }
84
85 fn inspect_run_exec_loop(
96 &mut self,
97 evm: &mut Self::Evm,
98 first_frame_input: <<Self::Evm as EvmTr>::Frame as FrameTr>::FrameInit,
99 ) -> Result<FrameResult, Self::Error> {
100 let res = evm.inspect_frame_init(first_frame_input)?;
101
102 if let ItemOrResult::Result(frame_result) = res {
103 return Ok(frame_result);
104 }
105
106 loop {
107 let call_or_result = evm.inspect_frame_run()?;
108
109 let result = match call_or_result {
110 ItemOrResult::Item(init) => {
111 match evm.inspect_frame_init(init)? {
112 ItemOrResult::Item(_) => {
113 continue;
114 }
115 ItemOrResult::Result(result) => result,
117 }
118 }
119 ItemOrResult::Result(result) => result,
120 };
121
122 if let Some(result) = evm.frame_return_result(result)? {
123 return Ok(result);
124 }
125 }
126 }
127
128 fn inspect_run_system_call(
134 &mut self,
135 evm: &mut Self::Evm,
136 ) -> Result<ExecutionResult<Self::HaltReason>, Self::Error> {
137 let init_and_floor_gas = InitialAndFloorGas::new(0, 0);
139 match self
141 .inspect_execution(evm, &init_and_floor_gas)
142 .and_then(|exec_result| self.execution_result(evm, exec_result))
143 {
144 out @ Ok(_) => out,
145 Err(e) => self.catch_error(evm, e),
146 }
147 }
148}
149
150pub fn frame_start<CTX, INTR: InterpreterTypes>(
152 context: &mut CTX,
153 inspector: &mut impl Inspector<CTX, INTR>,
154 frame_input: &mut FrameInput,
155) -> Option<FrameResult> {
156 match frame_input {
157 FrameInput::Call(i) => {
158 if let Some(output) = inspector.call(context, i) {
159 return Some(FrameResult::Call(output));
160 }
161 }
162 FrameInput::Create(i) => {
163 if let Some(output) = inspector.create(context, i) {
164 return Some(FrameResult::Create(output));
165 }
166 }
167 FrameInput::Empty => unreachable!(),
168 }
169 None
170}
171
172pub fn frame_end<CTX, INTR: InterpreterTypes>(
174 context: &mut CTX,
175 inspector: &mut impl Inspector<CTX, INTR>,
176 frame_input: &FrameInput,
177 frame_output: &mut FrameResult,
178) {
179 match frame_output {
180 FrameResult::Call(outcome) => {
181 let FrameInput::Call(i) = frame_input else {
182 panic!("FrameInput::Call expected {frame_input:?}");
183 };
184 inspector.call_end(context, i, outcome);
185 }
186 FrameResult::Create(outcome) => {
187 let FrameInput::Create(i) = frame_input else {
188 panic!("FrameInput::Create expected {frame_input:?}");
189 };
190 inspector.create_end(context, i, outcome);
191 }
192 }
193}
194
195pub fn inspect_instructions<CTX, IT>(
201 context: &mut CTX,
202 interpreter: &mut Interpreter<IT>,
203 mut inspector: impl Inspector<CTX, IT>,
204 instructions: &InstructionTable<IT, CTX>,
205) -> InterpreterAction
206where
207 CTX: ContextTr<Journal: JournalExt> + Host,
208 IT: InterpreterTypes,
209{
210 loop {
211 inspector.step(interpreter, context);
212 if interpreter.bytecode.is_end() {
213 break;
214 }
215
216 let opcode = interpreter.bytecode.opcode();
217 interpreter.step(instructions, context);
218
219 if (opcode::LOG0..=opcode::LOG4).contains(&opcode) {
220 inspect_log(interpreter, context, &mut inspector);
221 }
222
223 inspector.step_end(interpreter, context);
224
225 if interpreter.bytecode.is_end() {
226 break;
227 }
228 }
229
230 let next_action = interpreter.take_next_action();
231
232 if let InterpreterAction::Return(result) = &next_action {
234 if result.result == InstructionResult::SelfDestruct {
235 inspect_selfdestruct(context, &mut inspector);
236 }
237 }
238
239 next_action
240}
241
242#[inline(never)]
243#[cold]
244fn inspect_log<CTX, IT>(
245 interpreter: &mut Interpreter<IT>,
246 context: &mut CTX,
247 inspector: &mut impl Inspector<CTX, IT>,
248) where
249 CTX: ContextTr<Journal: JournalExt> + Host,
250 IT: InterpreterTypes,
251{
252 if interpreter
254 .bytecode
255 .action()
256 .as_ref()
257 .is_some_and(|x| x.is_return())
258 {
259 return;
260 }
261
262 let log = context.journal_mut().logs().last().unwrap().clone();
263 inspector.log(interpreter, context, log);
264}
265
266#[inline(never)]
267#[cold]
268fn inspect_selfdestruct<CTX, IT>(context: &mut CTX, inspector: &mut impl Inspector<CTX, IT>)
269where
270 CTX: ContextTr<Journal: JournalExt> + Host,
271 IT: InterpreterTypes,
272{
273 if let Some(
274 JournalEntry::AccountDestroyed {
275 address: contract,
276 target: to,
277 had_balance: balance,
278 ..
279 }
280 | JournalEntry::BalanceTransfer {
281 from: contract,
282 to,
283 balance,
284 ..
285 },
286 ) = context.journal_mut().journal().last()
287 {
288 inspector.selfdestruct(*contract, *to, *balance);
289 }
290}