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