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