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