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