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;
83 use database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET};
84 use handler::{MainBuilder, MainContext};
85 use interpreter::{
86 interpreter_types::{Jumps, LoopControl},
87 CallInputs, CreateInputs, Interpreter, InterpreterTypes,
88 };
89 use primitives::{Bytes, TxKind};
90 use state::bytecode::{opcode, Bytecode};
91
92 #[derive(Default, Debug)]
93 struct StackInspector {
94 pc: usize,
95 gas_inspector: GasInspector,
96 gas_remaining_steps: Vec<(usize, u64)>,
97 }
98
99 impl<CTX, INTR: InterpreterTypes> Inspector<CTX, INTR> for StackInspector {
100 fn initialize_interp(&mut self, interp: &mut Interpreter<INTR>, _context: &mut CTX) {
101 self.gas_inspector.initialize_interp(interp.control.gas());
102 }
103
104 fn step(&mut self, interp: &mut Interpreter<INTR>, _context: &mut CTX) {
105 self.pc = interp.bytecode.pc();
106 self.gas_inspector.step(interp.control.gas());
107 }
108
109 fn step_end(&mut self, interp: &mut Interpreter<INTR>, _context: &mut CTX) {
110 self.gas_inspector.step_end(interp.control.gas_mut());
111 self.gas_remaining_steps
112 .push((self.pc, self.gas_inspector.gas_remaining()));
113 }
114
115 fn call_end(&mut self, _c: &mut CTX, _i: &CallInputs, outcome: &mut CallOutcome) {
116 self.gas_inspector.call_end(outcome)
117 }
118
119 fn create_end(&mut self, _c: &mut CTX, _i: &CreateInputs, outcome: &mut CreateOutcome) {
120 self.gas_inspector.create_end(outcome)
121 }
122 }
123
124 #[test]
125 fn test_gas_inspector() {
126 let contract_data: Bytes = Bytes::from(vec![
127 opcode::PUSH1,
128 0x1,
129 opcode::PUSH1,
130 0xb,
131 opcode::JUMPI,
132 opcode::PUSH1,
133 0x1,
134 opcode::PUSH1,
135 0x1,
136 opcode::PUSH1,
137 0x1,
138 opcode::JUMPDEST,
139 opcode::STOP,
140 ]);
141 let bytecode = Bytecode::new_raw(contract_data);
142
143 let ctx = Context::mainnet()
144 .with_db(BenchmarkDB::new_bytecode(bytecode.clone()))
145 .modify_tx_chained(|tx| {
146 tx.caller = BENCH_CALLER;
147 tx.kind = TxKind::Call(BENCH_TARGET);
148 tx.gas_limit = 21100;
149 });
150
151 let mut evm = ctx.build_mainnet_with_inspector(StackInspector::default());
152
153 evm.inspect_replay().unwrap();
155
156 let inspector = &evm.data.inspector;
157
158 let steps = vec![
160 (0, 97),
162 (2, 94),
164 (4, 84),
166 (11, 83),
168 (12, 83),
170 ];
171
172 assert_eq!(inspector.gas_remaining_steps, steps);
173 }
174}