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#[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    pub fn gas_remaining(&self) -> u64 {
20        self.gas_remaining
21    }
22
23    pub fn last_gas_cost(&self) -> u64 {
24        self.last_gas_cost
25    }
26
27    pub fn new() -> Self {
28        Self {
29            gas_remaining: 0,
30            last_gas_cost: 0,
31        }
32    }
33
34    #[inline]
35    pub fn initialize_interp(&mut self, gas: &Gas) {
36        self.gas_remaining = gas.limit();
37    }
38
39    #[inline]
40    pub fn step(&mut self, gas: &Gas) {
41        self.gas_remaining = gas.remaining();
42    }
43
44    #[inline]
45    pub fn step_end(&mut self, gas: &mut Gas) {
46        let remaining = gas.remaining();
47        self.last_gas_cost = self.gas_remaining.saturating_sub(remaining);
48        self.gas_remaining = remaining;
49    }
50
51    #[inline]
52    pub fn call_end(&mut self, outcome: &mut CallOutcome) {
53        if outcome.result.result.is_error() {
54            outcome.result.gas.spend_all();
55            self.gas_remaining = 0;
56        }
57    }
58
59    #[inline]
60    pub fn create_end(&mut self, outcome: &mut CreateOutcome) {
61        if outcome.result.result.is_error() {
62            outcome.result.gas.spend_all();
63            self.gas_remaining = 0;
64        }
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71    use crate::{InspectEvm, Inspector};
72    use database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET};
73    use revm::{
74        bytecode::{opcode, Bytecode},
75        interpreter::{
76            interpreter_types::{Jumps, LoopControl},
77            CallInputs, CreateInputs, Interpreter, InterpreterTypes,
78        },
79        primitives::{Bytes, TxKind},
80        Context, MainBuilder, MainContext,
81    };
82
83    #[derive(Default, Debug)]
84    struct StackInspector {
85        pc: usize,
86        gas_inspector: GasInspector,
87        gas_remaining_steps: Vec<(usize, u64)>,
88    }
89
90    impl<CTX, INTR: InterpreterTypes> Inspector<CTX, INTR> for StackInspector {
91        fn initialize_interp(&mut self, interp: &mut Interpreter<INTR>, _context: &mut CTX) {
92            self.gas_inspector.initialize_interp(interp.control.gas());
93        }
94
95        fn step(&mut self, interp: &mut Interpreter<INTR>, _context: &mut CTX) {
96            self.pc = interp.bytecode.pc();
97            self.gas_inspector.step(interp.control.gas());
98        }
99
100        fn step_end(&mut self, interp: &mut Interpreter<INTR>, _context: &mut CTX) {
101            self.gas_inspector.step_end(interp.control.gas());
102            self.gas_remaining_steps
103                .push((self.pc, self.gas_inspector.gas_remaining()));
104        }
105
106        fn call_end(&mut self, _c: &mut CTX, _i: &CallInputs, outcome: &mut CallOutcome) {
107            self.gas_inspector.call_end(outcome)
108        }
109
110        fn create_end(&mut self, _c: &mut CTX, _i: &CreateInputs, outcome: &mut CreateOutcome) {
111            self.gas_inspector.create_end(outcome)
112        }
113    }
114
115    #[test]
116    fn test_gas_inspector() {
117        let contract_data: Bytes = Bytes::from(vec![
118            opcode::PUSH1,
119            0x1,
120            opcode::PUSH1,
121            0xb,
122            opcode::JUMPI,
123            opcode::PUSH1,
124            0x1,
125            opcode::PUSH1,
126            0x1,
127            opcode::PUSH1,
128            0x1,
129            opcode::JUMPDEST,
130            opcode::STOP,
131        ]);
132        let bytecode = Bytecode::new_raw(contract_data);
133
134        let ctx = Context::mainnet()
135            .with_db(BenchmarkDB::new_bytecode(bytecode.clone()))
136            .modify_tx_chained(|tx| {
137                tx.caller = BENCH_CALLER;
138                tx.kind = TxKind::Call(BENCH_TARGET);
139                tx.gas_limit = 21100;
140            });
141
142        let mut evm = ctx.build_mainnet_with_inspector(StackInspector::default());
143
144        // Run evm.
145        evm.inspect_previous().unwrap();
146
147        let inspector = &evm.data.inspector;
148
149        // Starting from 100gas
150        let steps = vec![
151            // push1 -3
152            (0, 97),
153            // push1 -3
154            (2, 94),
155            // jumpi -10
156            (4, 84),
157            // jumpdest 1
158            (11, 83),
159            // stop 0
160            (12, 83),
161        ];
162
163        assert_eq!(inspector.gas_remaining_steps, steps);
164    }
165}