1use interpreter::{CallOutcome, CreateOutcome, Gas};
3
4#[allow(dead_code)]
6#[derive(Clone, Copy, Debug)]
7pub struct GasInspector {
8 gas_remaining: u64,
9 last_gas_cost: u64,
10}
11
12impl Default for GasInspector {
13 fn default() -> Self {
14 Self::new()
15 }
16}
17
18impl GasInspector {
19 #[inline]
21 pub fn gas_remaining(&self) -> u64 {
22 self.gas_remaining
23 }
24
25 #[inline]
27 pub fn last_gas_cost(&self) -> u64 {
28 self.last_gas_cost
29 }
30
31 pub fn new() -> Self {
33 Self {
34 gas_remaining: 0,
35 last_gas_cost: 0,
36 }
37 }
38
39 #[inline]
41 pub fn initialize_interp(&mut self, gas: &Gas) {
42 self.gas_remaining = gas.limit();
43 }
44
45 #[inline]
47 pub fn step(&mut self, gas: &Gas) {
48 self.gas_remaining = gas.remaining();
49 }
50
51 #[inline]
53 pub fn step_end(&mut self, gas: &mut Gas) {
54 let remaining = gas.remaining();
55 self.last_gas_cost = self.gas_remaining.saturating_sub(remaining);
56 self.gas_remaining = remaining;
57 }
58
59 #[inline]
61 pub fn call_end(&mut self, outcome: &mut CallOutcome) {
62 if outcome.result.result.is_error() {
63 outcome.result.gas.spend_all();
64 self.gas_remaining = 0;
65 }
66 }
67
68 #[inline]
70 pub fn create_end(&mut self, outcome: &mut CreateOutcome) {
71 if outcome.result.result.is_error() {
72 outcome.result.gas.spend_all();
73 self.gas_remaining = 0;
74 }
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81 use crate::{InspectEvm, Inspector};
82 use context::{Context, TxEnv};
83 use database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET};
84 use handler::{MainBuilder, MainContext};
85 use interpreter::{
86 interpreter_types::{Jumps, ReturnData},
87 CallInputs, CreateInputs, Interpreter, InterpreterResult, InterpreterTypes,
88 };
89 use primitives::{Address, Bytes, TxKind};
90 use state::bytecode::{opcode, Bytecode};
91
92 #[derive(Default, Debug)]
93 struct StackInspector {
94 pc: usize,
95 opcode: u8,
96 gas_inspector: GasInspector,
97 gas_remaining_steps: Vec<(usize, u64)>,
98 }
99
100 impl<CTX, INTR: InterpreterTypes> Inspector<CTX, INTR> for StackInspector {
101 fn initialize_interp(&mut self, interp: &mut Interpreter<INTR>, _context: &mut CTX) {
102 self.gas_inspector.initialize_interp(&interp.gas);
103 }
104
105 fn step(&mut self, interp: &mut Interpreter<INTR>, _context: &mut CTX) {
106 self.pc = interp.bytecode.pc();
107 self.opcode = interp.bytecode.opcode();
108 self.gas_inspector.step(&interp.gas);
109 }
110
111 fn step_end(&mut self, interp: &mut Interpreter<INTR>, _context: &mut CTX) {
112 interp.bytecode.pc();
113 interp.bytecode.opcode();
114 self.gas_inspector.step_end(&mut interp.gas);
115 self.gas_remaining_steps
116 .push((self.pc, self.gas_inspector.gas_remaining()));
117 }
118
119 fn call_end(&mut self, _c: &mut CTX, _i: &CallInputs, outcome: &mut CallOutcome) {
120 self.gas_inspector.call_end(outcome)
121 }
122
123 fn create_end(&mut self, _c: &mut CTX, _i: &CreateInputs, outcome: &mut CreateOutcome) {
124 self.gas_inspector.create_end(outcome)
125 }
126 }
127
128 #[test]
129 fn test_gas_inspector() {
130 let contract_data: Bytes = Bytes::from(vec![
131 opcode::PUSH1,
132 0x1,
133 opcode::PUSH1,
134 0xb,
135 opcode::JUMPI,
136 opcode::PUSH1,
137 0x1,
138 opcode::PUSH1,
139 0x1,
140 opcode::PUSH1,
141 0x1,
142 opcode::JUMPDEST,
143 opcode::STOP,
144 ]);
145 let bytecode = Bytecode::new_raw(contract_data);
146
147 let ctx = Context::mainnet().with_db(BenchmarkDB::new_bytecode(bytecode.clone()));
148
149 let mut evm = ctx.build_mainnet_with_inspector(StackInspector::default());
150
151 evm.inspect_one_tx(
153 TxEnv::builder()
154 .caller(BENCH_CALLER)
155 .kind(TxKind::Call(BENCH_TARGET))
156 .gas_limit(21100)
157 .build()
158 .unwrap(),
159 )
160 .unwrap();
161
162 let inspector = &evm.inspector;
163
164 let steps = vec![
166 (0, 97),
168 (2, 94),
170 (4, 84),
172 (11, 83),
174 (12, 83),
176 ];
177
178 assert_eq!(inspector.gas_remaining_steps, steps);
179 }
180
181 #[derive(Default, Debug)]
182 struct CallOverrideInspector {
183 call_override: Vec<Option<CallOutcome>>,
184 create_override: Vec<Option<CreateOutcome>>,
185 return_buffer: Vec<Bytes>,
186 }
187
188 impl<CTX, INTR: InterpreterTypes> Inspector<CTX, INTR> for CallOverrideInspector {
189 fn call(&mut self, _context: &mut CTX, _inputs: &mut CallInputs) -> Option<CallOutcome> {
190 self.call_override.pop().unwrap_or_default()
191 }
192
193 fn step(&mut self, interpreter: &mut Interpreter<INTR>, _context: &mut CTX) {
194 let this_buffer = interpreter.return_data.buffer();
195 let Some(buffer) = self.return_buffer.last() else {
196 self.return_buffer.push(this_buffer.clone());
197 return;
198 };
199 if this_buffer != buffer {
200 self.return_buffer.push(this_buffer.clone());
201 }
202 }
203
204 fn create(
205 &mut self,
206 _context: &mut CTX,
207 _inputs: &mut CreateInputs,
208 ) -> Option<CreateOutcome> {
209 self.create_override.pop().unwrap_or_default()
210 }
211 }
212
213 #[test]
214 fn test_call_override_inspector() {
215 use interpreter::{CallOutcome, CreateOutcome, InstructionResult};
216
217 let mut inspector = CallOverrideInspector::default();
218 inspector.call_override.push(Some(CallOutcome::new(
219 InterpreterResult::new(InstructionResult::Return, [0x01].into(), Gas::new(100_000)),
220 0..1,
221 )));
222 inspector.call_override.push(None);
223 inspector.create_override.push(Some(CreateOutcome::new(
224 InterpreterResult::new(InstructionResult::Revert, [0x02].into(), Gas::new(100_000)),
225 Some(Address::ZERO),
226 )));
227
228 let contract_data: Bytes = Bytes::from(vec![
229 opcode::PUSH1,
230 0x01,
231 opcode::PUSH1,
232 0x0,
233 opcode::DUP1,
234 opcode::DUP1,
235 opcode::DUP1,
236 opcode::DUP1,
237 opcode::ADDRESS,
238 opcode::CALL,
239 opcode::PUSH1,
240 0x01,
241 opcode::PUSH1,
242 0x0,
243 opcode::DUP1,
244 opcode::DUP1,
245 opcode::DUP1,
246 opcode::DUP1,
247 opcode::DUP1,
248 opcode::ADDRESS,
249 opcode::CREATE,
250 opcode::STOP,
251 ]);
252
253 let bytecode = Bytecode::new_raw(contract_data);
254
255 let mut evm = Context::mainnet()
256 .with_db(BenchmarkDB::new_bytecode(bytecode.clone()))
257 .build_mainnet_with_inspector(inspector);
258
259 let _ = evm
260 .inspect_one_tx(TxEnv::builder_for_bench().build().unwrap())
261 .unwrap();
262 assert_eq!(evm.inspector.return_buffer.len(), 3);
263 assert_eq!(
264 evm.inspector.return_buffer,
265 [Bytes::new(), Bytes::from([0x01]), Bytes::from([0x02])].to_vec()
266 );
267 }
268}