1use revm_interpreter::CallOutcome;
4
5use crate::{
6 interpreter::{CallInputs, CreateInputs, CreateOutcome},
7 primitives::db::Database,
8 EvmContext, Inspector,
9};
10
11#[allow(dead_code)]
13#[derive(Clone, Copy, Debug, Default)]
14pub struct GasInspector {
15 gas_remaining: u64,
16 last_gas_cost: u64,
17}
18
19impl GasInspector {
20 pub fn gas_remaining(&self) -> u64 {
21 self.gas_remaining
22 }
23
24 pub fn last_gas_cost(&self) -> u64 {
25 self.last_gas_cost
26 }
27}
28
29impl<DB: Database> Inspector<DB> for GasInspector {
30 fn initialize_interp(
31 &mut self,
32 interp: &mut crate::interpreter::Interpreter,
33 _context: &mut EvmContext<DB>,
34 ) {
35 self.gas_remaining = interp.gas.limit();
36 }
37
38 fn step(
39 &mut self,
40 interp: &mut crate::interpreter::Interpreter,
41 _context: &mut EvmContext<DB>,
42 ) {
43 self.gas_remaining = interp.gas.remaining();
44 }
45
46 fn step_end(
47 &mut self,
48 interp: &mut crate::interpreter::Interpreter,
49 _context: &mut EvmContext<DB>,
50 ) {
51 let remaining = interp.gas.remaining();
52 self.last_gas_cost = self.gas_remaining.saturating_sub(remaining);
53 self.gas_remaining = remaining;
54 }
55
56 fn call_end(
57 &mut self,
58 _context: &mut EvmContext<DB>,
59 _inputs: &CallInputs,
60 mut outcome: CallOutcome,
61 ) -> CallOutcome {
62 if outcome.result.result.is_error() {
63 outcome.result.gas.spend_all();
64 self.gas_remaining = 0;
65 }
66 outcome
67 }
68
69 fn create_end(
70 &mut self,
71 _context: &mut EvmContext<DB>,
72 _inputs: &CreateInputs,
73 mut outcome: CreateOutcome,
74 ) -> CreateOutcome {
75 if outcome.result.result.is_error() {
76 outcome.result.gas.spend_all();
77 self.gas_remaining = 0;
78 }
79 outcome
80 }
81}
82
83#[cfg(test)]
84mod tests {
85
86 use revm_interpreter::CallOutcome;
87 use revm_interpreter::CreateOutcome;
88
89 use crate::{
90 inspectors::GasInspector,
91 interpreter::{CallInputs, CreateInputs, Interpreter},
92 primitives::Log,
93 Database, EvmContext, Inspector,
94 };
95
96 #[derive(Default, Debug)]
97 struct StackInspector {
98 pc: usize,
99 gas_inspector: GasInspector,
100 gas_remaining_steps: Vec<(usize, u64)>,
101 }
102
103 impl<DB: Database> Inspector<DB> for StackInspector {
104 fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext<DB>) {
105 self.gas_inspector.initialize_interp(interp, context);
106 }
107
108 fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext<DB>) {
109 self.pc = interp.program_counter();
110 self.gas_inspector.step(interp, context);
111 }
112
113 fn log(&mut self, interp: &mut Interpreter, context: &mut EvmContext<DB>, log: &Log) {
114 self.gas_inspector.log(interp, context, log);
115 }
116
117 fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<DB>) {
118 self.gas_inspector.step_end(interp, context);
119 self.gas_remaining_steps
120 .push((self.pc, self.gas_inspector.gas_remaining()));
121 }
122
123 fn call(
124 &mut self,
125 context: &mut EvmContext<DB>,
126 call: &mut CallInputs,
127 ) -> Option<CallOutcome> {
128 self.gas_inspector.call(context, call)
129 }
130
131 fn call_end(
132 &mut self,
133 context: &mut EvmContext<DB>,
134 inputs: &CallInputs,
135 outcome: CallOutcome,
136 ) -> CallOutcome {
137 self.gas_inspector.call_end(context, inputs, outcome)
138 }
139
140 fn create(
141 &mut self,
142 context: &mut EvmContext<DB>,
143 call: &mut CreateInputs,
144 ) -> Option<CreateOutcome> {
145 self.gas_inspector.create(context, call);
146 None
147 }
148
149 fn create_end(
150 &mut self,
151 context: &mut EvmContext<DB>,
152 inputs: &CreateInputs,
153 outcome: CreateOutcome,
154 ) -> CreateOutcome {
155 self.gas_inspector.create_end(context, inputs, outcome)
156 }
157 }
158
159 #[test]
160 fn test_gas_inspector() {
161 use crate::{
162 db::BenchmarkDB,
163 inspector::inspector_handle_register,
164 interpreter::opcode,
165 primitives::{address, Bytecode, Bytes, TxKind},
166 Evm,
167 };
168
169 let contract_data: Bytes = Bytes::from(vec![
170 opcode::PUSH1,
171 0x1,
172 opcode::PUSH1,
173 0xb,
174 opcode::JUMPI,
175 opcode::PUSH1,
176 0x1,
177 opcode::PUSH1,
178 0x1,
179 opcode::PUSH1,
180 0x1,
181 opcode::JUMPDEST,
182 opcode::STOP,
183 ]);
184 let bytecode = Bytecode::new_raw(contract_data);
185
186 let mut evm: Evm<'_, StackInspector, BenchmarkDB> = Evm::builder()
187 .with_db(BenchmarkDB::new_bytecode(bytecode.clone()))
188 .with_external_context(StackInspector::default())
189 .modify_tx_env(|tx| {
190 tx.clear();
191 tx.caller = address!("1000000000000000000000000000000000000000");
192 tx.transact_to = TxKind::Call(address!("0000000000000000000000000000000000000000"));
193 tx.gas_limit = 21100;
194 })
195 .append_handler_register(inspector_handle_register)
196 .build();
197
198 evm.transact().unwrap();
200
201 let inspector = evm.into_context().external;
202
203 let steps = vec![
205 (0, 97),
207 (2, 94),
209 (4, 84),
211 (11, 83),
213 (12, 83),
215 ];
216
217 assert_eq!(inspector.gas_remaining_steps, steps);
218 }
219}