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, TxEnv};
83    use database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET};
84    use handler::{MainBuilder, MainContext};
85    use interpreter::{
86        interpreter_types::{Jumps, ReturnData},
87        CallInputs, CreateInputs, Interpreter, InterpreterResult, InterpreterTypes,
88    };
89    use primitives::{Address, Bytes, TxKind};
90    use state::bytecode::{opcode, Bytecode};
91
92    #[derive(Default, Debug)]
93    struct StackInspector {
94        pc: usize,
95        opcode: u8,
96        gas_inspector: GasInspector,
97        gas_remaining_steps: Vec<(usize, u64)>,
98    }
99
100    impl<CTX, INTR: InterpreterTypes> Inspector<CTX, INTR> for StackInspector {
101        fn initialize_interp(&mut self, interp: &mut Interpreter<INTR>, _context: &mut CTX) {
102            self.gas_inspector.initialize_interp(&interp.gas);
103        }
104
105        fn step(&mut self, interp: &mut Interpreter<INTR>, _context: &mut CTX) {
106            self.pc = interp.bytecode.pc();
107            self.opcode = interp.bytecode.opcode();
108            self.gas_inspector.step(&interp.gas);
109        }
110
111        fn step_end(&mut self, interp: &mut Interpreter<INTR>, _context: &mut CTX) {
112            interp.bytecode.pc();
113            interp.bytecode.opcode();
114            self.gas_inspector.step_end(&mut interp.gas);
115            self.gas_remaining_steps
116                .push((self.pc, self.gas_inspector.gas_remaining()));
117        }
118
119        fn call_end(&mut self, _c: &mut CTX, _i: &CallInputs, outcome: &mut CallOutcome) {
120            self.gas_inspector.call_end(outcome)
121        }
122
123        fn create_end(&mut self, _c: &mut CTX, _i: &CreateInputs, outcome: &mut CreateOutcome) {
124            self.gas_inspector.create_end(outcome)
125        }
126    }
127
128    #[test]
129    fn test_gas_inspector() {
130        let contract_data: Bytes = Bytes::from(vec![
131            opcode::PUSH1,
132            0x1,
133            opcode::PUSH1,
134            0xb,
135            opcode::JUMPI,
136            opcode::PUSH1,
137            0x1,
138            opcode::PUSH1,
139            0x1,
140            opcode::PUSH1,
141            0x1,
142            opcode::JUMPDEST,
143            opcode::STOP,
144        ]);
145        let bytecode = Bytecode::new_raw(contract_data);
146
147        let ctx = Context::mainnet().with_db(BenchmarkDB::new_bytecode(bytecode.clone()));
148
149        let mut evm = ctx.build_mainnet_with_inspector(StackInspector::default());
150
151        // Run evm.
152        evm.inspect_one_tx(
153            TxEnv::builder()
154                .caller(BENCH_CALLER)
155                .kind(TxKind::Call(BENCH_TARGET))
156                .gas_limit(21100)
157                .build()
158                .unwrap(),
159        )
160        .unwrap();
161
162        let inspector = &evm.inspector;
163
164        // Starting from 100gas
165        let steps = vec![
166            // push1 -3
167            (0, 97),
168            // push1 -3
169            (2, 94),
170            // jumpi -10
171            (4, 84),
172            // jumpdest 1
173            (11, 83),
174            // stop 0
175            (12, 83),
176        ];
177
178        assert_eq!(inspector.gas_remaining_steps, steps);
179    }
180
181    #[derive(Default, Debug)]
182    struct CallOverrideInspector {
183        call_override: Vec<Option<CallOutcome>>,
184        create_override: Vec<Option<CreateOutcome>>,
185        return_buffer: Vec<Bytes>,
186    }
187
188    impl<CTX, INTR: InterpreterTypes> Inspector<CTX, INTR> for CallOverrideInspector {
189        fn call(&mut self, _context: &mut CTX, _inputs: &mut CallInputs) -> Option<CallOutcome> {
190            self.call_override.pop().unwrap_or_default()
191        }
192
193        fn step(&mut self, interpreter: &mut Interpreter<INTR>, _context: &mut CTX) {
194            let this_buffer = interpreter.return_data.buffer();
195            let Some(buffer) = self.return_buffer.last() else {
196                self.return_buffer.push(this_buffer.clone());
197                return;
198            };
199            if this_buffer != buffer {
200                self.return_buffer.push(this_buffer.clone());
201            }
202        }
203
204        fn create(
205            &mut self,
206            _context: &mut CTX,
207            _inputs: &mut CreateInputs,
208        ) -> Option<CreateOutcome> {
209            self.create_override.pop().unwrap_or_default()
210        }
211    }
212
213    #[test]
214    fn test_call_override_inspector() {
215        use interpreter::{CallOutcome, CreateOutcome, InstructionResult};
216
217        let mut inspector = CallOverrideInspector::default();
218        inspector.call_override.push(Some(CallOutcome::new(
219            InterpreterResult::new(InstructionResult::Return, [0x01].into(), Gas::new(100_000)),
220            0..1,
221        )));
222        inspector.call_override.push(None);
223        inspector.create_override.push(Some(CreateOutcome::new(
224            InterpreterResult::new(InstructionResult::Revert, [0x02].into(), Gas::new(100_000)),
225            Some(Address::ZERO),
226        )));
227
228        let contract_data: Bytes = Bytes::from(vec![
229            opcode::PUSH1,
230            0x01,
231            opcode::PUSH1,
232            0x0,
233            opcode::DUP1,
234            opcode::DUP1,
235            opcode::DUP1,
236            opcode::DUP1,
237            opcode::ADDRESS,
238            opcode::CALL,
239            opcode::PUSH1,
240            0x01,
241            opcode::PUSH1,
242            0x0,
243            opcode::DUP1,
244            opcode::DUP1,
245            opcode::DUP1,
246            opcode::DUP1,
247            opcode::DUP1,
248            opcode::ADDRESS,
249            opcode::CREATE,
250            opcode::STOP,
251        ]);
252
253        let bytecode = Bytecode::new_raw(contract_data);
254
255        let mut evm = Context::mainnet()
256            .with_db(BenchmarkDB::new_bytecode(bytecode.clone()))
257            .build_mainnet_with_inspector(inspector);
258
259        let _ = evm
260            .inspect_one_tx(TxEnv::builder_for_bench().build().unwrap())
261            .unwrap();
262        assert_eq!(evm.inspector.return_buffer.len(), 3);
263        assert_eq!(
264            evm.inspector.return_buffer,
265            [Bytes::new(), Bytes::from([0x01]), Bytes::from([0x02])].to_vec()
266        );
267    }
268}