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