Skip to main content

revm_inspector/
gas.rs

1//! GasIspector. Helper Inspector to calculate gas for others.
2use interpreter::{CallOutcome, CreateOutcome, Gas};
3
4/// Helper that keeps track of gas.
5#[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    /// Returns the remaining gas.
21    #[inline]
22    pub const fn gas_remaining(&self) -> u64 {
23        self.gas_remaining
24    }
25
26    /// Returns the last gas cost.
27    #[inline]
28    pub const fn last_gas_cost(&self) -> u64 {
29        self.last_gas_cost
30    }
31
32    /// Returns the state gas spent.
33    ///
34    /// Can be negative within a call frame (EIP-8037 issue #2): a child that
35    /// restores a slot set by its parent via 0→x→0 goes negative until the
36    /// frame returns and the parent's charge is reconciled.
37    #[inline]
38    pub const fn state_gas_spent(&self) -> i64 {
39        self.state_gas_spent
40    }
41
42    /// Returns the reservoir gas.
43    #[inline]
44    pub const fn reservoir(&self) -> u64 {
45        self.reservoir
46    }
47
48    /// Create a new gas inspector.
49    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    /// Sets remaining gas to gas limit.
59    #[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    /// Sets the remaining gas.
67    #[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    /// calculate last gas cost and remaining gas.
75    #[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    /// Spend all gas if call failed.
85    #[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    /// Spend all gas if create failed.
96    #[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        // Run evm.
179        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        // Starting from 100gas
192        let steps = vec![
193            // push1 -3
194            (0, 97),
195            // push1 -3
196            (2, 94),
197            // jumpi -10
198            (4, 84),
199            // jumpdest 1
200            (11, 83),
201            // stop 0
202            (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}