revm/inspector/
gas.rs

1//! GasIspector. Helper Inspector to calculate gas for others.
2
3use revm_interpreter::CallOutcome;
4
5use crate::{
6    interpreter::{CallInputs, CreateInputs, CreateOutcome},
7    primitives::db::Database,
8    EvmContext, Inspector,
9};
10
11/// Helper [Inspector] that keeps track of gas.
12#[allow(dead_code)]
13#[derive(Clone, Copy, Debug, Default)]
14pub struct GasInspector {
15    gas_remaining: u64,
16    last_gas_cost: u64,
17}
18
19impl GasInspector {
20    pub fn gas_remaining(&self) -> u64 {
21        self.gas_remaining
22    }
23
24    pub fn last_gas_cost(&self) -> u64 {
25        self.last_gas_cost
26    }
27}
28
29impl<DB: Database> Inspector<DB> for GasInspector {
30    fn initialize_interp(
31        &mut self,
32        interp: &mut crate::interpreter::Interpreter,
33        _context: &mut EvmContext<DB>,
34    ) {
35        self.gas_remaining = interp.gas.limit();
36    }
37
38    fn step(
39        &mut self,
40        interp: &mut crate::interpreter::Interpreter,
41        _context: &mut EvmContext<DB>,
42    ) {
43        self.gas_remaining = interp.gas.remaining();
44    }
45
46    fn step_end(
47        &mut self,
48        interp: &mut crate::interpreter::Interpreter,
49        _context: &mut EvmContext<DB>,
50    ) {
51        let remaining = interp.gas.remaining();
52        self.last_gas_cost = self.gas_remaining.saturating_sub(remaining);
53        self.gas_remaining = remaining;
54    }
55
56    fn call_end(
57        &mut self,
58        _context: &mut EvmContext<DB>,
59        _inputs: &CallInputs,
60        mut outcome: CallOutcome,
61    ) -> CallOutcome {
62        if outcome.result.result.is_error() {
63            outcome.result.gas.spend_all();
64            self.gas_remaining = 0;
65        }
66        outcome
67    }
68
69    fn create_end(
70        &mut self,
71        _context: &mut EvmContext<DB>,
72        _inputs: &CreateInputs,
73        mut outcome: CreateOutcome,
74    ) -> CreateOutcome {
75        if outcome.result.result.is_error() {
76            outcome.result.gas.spend_all();
77            self.gas_remaining = 0;
78        }
79        outcome
80    }
81}
82
83#[cfg(test)]
84mod tests {
85
86    use revm_interpreter::CallOutcome;
87    use revm_interpreter::CreateOutcome;
88
89    use crate::{
90        inspectors::GasInspector,
91        interpreter::{CallInputs, CreateInputs, Interpreter},
92        primitives::Log,
93        Database, EvmContext, Inspector,
94    };
95
96    #[derive(Default, Debug)]
97    struct StackInspector {
98        pc: usize,
99        gas_inspector: GasInspector,
100        gas_remaining_steps: Vec<(usize, u64)>,
101    }
102
103    impl<DB: Database> Inspector<DB> for StackInspector {
104        fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut EvmContext<DB>) {
105            self.gas_inspector.initialize_interp(interp, context);
106        }
107
108        fn step(&mut self, interp: &mut Interpreter, context: &mut EvmContext<DB>) {
109            self.pc = interp.program_counter();
110            self.gas_inspector.step(interp, context);
111        }
112
113        fn log(&mut self, interp: &mut Interpreter, context: &mut EvmContext<DB>, log: &Log) {
114            self.gas_inspector.log(interp, context, log);
115        }
116
117        fn step_end(&mut self, interp: &mut Interpreter, context: &mut EvmContext<DB>) {
118            self.gas_inspector.step_end(interp, context);
119            self.gas_remaining_steps
120                .push((self.pc, self.gas_inspector.gas_remaining()));
121        }
122
123        fn call(
124            &mut self,
125            context: &mut EvmContext<DB>,
126            call: &mut CallInputs,
127        ) -> Option<CallOutcome> {
128            self.gas_inspector.call(context, call)
129        }
130
131        fn call_end(
132            &mut self,
133            context: &mut EvmContext<DB>,
134            inputs: &CallInputs,
135            outcome: CallOutcome,
136        ) -> CallOutcome {
137            self.gas_inspector.call_end(context, inputs, outcome)
138        }
139
140        fn create(
141            &mut self,
142            context: &mut EvmContext<DB>,
143            call: &mut CreateInputs,
144        ) -> Option<CreateOutcome> {
145            self.gas_inspector.create(context, call);
146            None
147        }
148
149        fn create_end(
150            &mut self,
151            context: &mut EvmContext<DB>,
152            inputs: &CreateInputs,
153            outcome: CreateOutcome,
154        ) -> CreateOutcome {
155            self.gas_inspector.create_end(context, inputs, outcome)
156        }
157    }
158
159    #[test]
160    fn test_gas_inspector() {
161        use crate::{
162            db::BenchmarkDB,
163            inspector::inspector_handle_register,
164            interpreter::opcode,
165            primitives::{address, Bytecode, Bytes, TxKind},
166            Evm,
167        };
168
169        let contract_data: Bytes = Bytes::from(vec![
170            opcode::PUSH1,
171            0x1,
172            opcode::PUSH1,
173            0xb,
174            opcode::JUMPI,
175            opcode::PUSH1,
176            0x1,
177            opcode::PUSH1,
178            0x1,
179            opcode::PUSH1,
180            0x1,
181            opcode::JUMPDEST,
182            opcode::STOP,
183        ]);
184        let bytecode = Bytecode::new_raw(contract_data);
185
186        let mut evm: Evm<'_, StackInspector, BenchmarkDB> = Evm::builder()
187            .with_db(BenchmarkDB::new_bytecode(bytecode.clone()))
188            .with_external_context(StackInspector::default())
189            .modify_tx_env(|tx| {
190                tx.clear();
191                tx.caller = address!("1000000000000000000000000000000000000000");
192                tx.transact_to = TxKind::Call(address!("0000000000000000000000000000000000000000"));
193                tx.gas_limit = 21100;
194            })
195            .append_handler_register(inspector_handle_register)
196            .build();
197
198        // run evm.
199        evm.transact().unwrap();
200
201        let inspector = evm.into_context().external;
202
203        // starting from 100gas
204        let steps = vec![
205            // push1 -3
206            (0, 97),
207            // push1 -3
208            (2, 94),
209            // jumpi -10
210            (4, 84),
211            // jumpdest 1
212            (11, 83),
213            // stop 0
214            (12, 83),
215        ];
216
217        assert_eq!(inspector.gas_remaining_steps, steps);
218    }
219}