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: u64,
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    #[inline]
34    pub const fn state_gas_spent(&self) -> u64 {
35        self.state_gas_spent
36    }
37
38    /// Returns the reservoir gas.
39    #[inline]
40    pub const fn reservoir(&self) -> u64 {
41        self.reservoir
42    }
43
44    /// Create a new gas inspector.
45    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    /// Sets remaining gas to gas limit.
55    #[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    /// Sets the remaining gas.
63    #[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    /// calculate last gas cost and remaining gas.
71    #[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    /// Spend all gas if call failed.
81    #[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    /// Spend all gas if create failed.
92    #[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        // Run evm.
175        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        // Starting from 100gas
188        let steps = vec![
189            // push1 -3
190            (0, 97),
191            // push1 -3
192            (2, 94),
193            // jumpi -10
194            (4, 84),
195            // jumpdest 1
196            (11, 83),
197            // stop 0
198            (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}