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 pub fn gas_remaining(&self) -> u64 {
20 self.gas_remaining
21 }
22
23 pub fn last_gas_cost(&self) -> u64 {
24 self.last_gas_cost
25 }
26
27 pub fn new() -> Self {
28 Self {
29 gas_remaining: 0,
30 last_gas_cost: 0,
31 }
32 }
33
34 #[inline]
35 pub fn initialize_interp(&mut self, gas: &Gas) {
36 self.gas_remaining = gas.limit();
37 }
38
39 #[inline]
40 pub fn step(&mut self, gas: &Gas) {
41 self.gas_remaining = gas.remaining();
42 }
43
44 #[inline]
45 pub fn step_end(&mut self, gas: &mut Gas) {
46 let remaining = gas.remaining();
47 self.last_gas_cost = self.gas_remaining.saturating_sub(remaining);
48 self.gas_remaining = remaining;
49 }
50
51 #[inline]
52 pub fn call_end(&mut self, outcome: &mut CallOutcome) {
53 if outcome.result.result.is_error() {
54 outcome.result.gas.spend_all();
55 self.gas_remaining = 0;
56 }
57 }
58
59 #[inline]
60 pub fn create_end(&mut self, outcome: &mut CreateOutcome) {
61 if outcome.result.result.is_error() {
62 outcome.result.gas.spend_all();
63 self.gas_remaining = 0;
64 }
65 }
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71 use crate::{InspectEvm, Inspector};
72 use database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET};
73 use revm::{
74 bytecode::{opcode, Bytecode},
75 interpreter::{
76 interpreter_types::{Jumps, LoopControl},
77 CallInputs, CreateInputs, Interpreter, InterpreterTypes,
78 },
79 primitives::{Bytes, TxKind},
80 Context, MainBuilder, MainContext,
81 };
82
83 #[derive(Default, Debug)]
84 struct StackInspector {
85 pc: usize,
86 gas_inspector: GasInspector,
87 gas_remaining_steps: Vec<(usize, u64)>,
88 }
89
90 impl<CTX, INTR: InterpreterTypes> Inspector<CTX, INTR> for StackInspector {
91 fn initialize_interp(&mut self, interp: &mut Interpreter<INTR>, _context: &mut CTX) {
92 self.gas_inspector.initialize_interp(interp.control.gas());
93 }
94
95 fn step(&mut self, interp: &mut Interpreter<INTR>, _context: &mut CTX) {
96 self.pc = interp.bytecode.pc();
97 self.gas_inspector.step(interp.control.gas());
98 }
99
100 fn step_end(&mut self, interp: &mut Interpreter<INTR>, _context: &mut CTX) {
101 self.gas_inspector.step_end(interp.control.gas());
102 self.gas_remaining_steps
103 .push((self.pc, self.gas_inspector.gas_remaining()));
104 }
105
106 fn call_end(&mut self, _c: &mut CTX, _i: &CallInputs, outcome: &mut CallOutcome) {
107 self.gas_inspector.call_end(outcome)
108 }
109
110 fn create_end(&mut self, _c: &mut CTX, _i: &CreateInputs, outcome: &mut CreateOutcome) {
111 self.gas_inspector.create_end(outcome)
112 }
113 }
114
115 #[test]
116 fn test_gas_inspector() {
117 let contract_data: Bytes = Bytes::from(vec![
118 opcode::PUSH1,
119 0x1,
120 opcode::PUSH1,
121 0xb,
122 opcode::JUMPI,
123 opcode::PUSH1,
124 0x1,
125 opcode::PUSH1,
126 0x1,
127 opcode::PUSH1,
128 0x1,
129 opcode::JUMPDEST,
130 opcode::STOP,
131 ]);
132 let bytecode = Bytecode::new_raw(contract_data);
133
134 let ctx = Context::mainnet()
135 .with_db(BenchmarkDB::new_bytecode(bytecode.clone()))
136 .modify_tx_chained(|tx| {
137 tx.caller = BENCH_CALLER;
138 tx.kind = TxKind::Call(BENCH_TARGET);
139 tx.gas_limit = 21100;
140 });
141
142 let mut evm = ctx.build_mainnet_with_inspector(StackInspector::default());
143
144 evm.inspect_previous().unwrap();
146
147 let inspector = &evm.data.inspector;
148
149 let steps = vec![
151 (0, 97),
153 (2, 94),
155 (4, 84),
157 (11, 83),
159 (12, 83),
161 ];
162
163 assert_eq!(inspector.gas_remaining_steps, steps);
164 }
165}