revm_inspector/
count_inspector.rs

1//! CountInspector - Inspector that counts all opcodes that were called.
2use crate::inspector::Inspector;
3use interpreter::{interpreter_types::Jumps, InterpreterTypes};
4use primitives::HashMap;
5
6/// Inspector that counts all opcodes that were called during execution.
7#[derive(Clone, Debug, Default)]
8pub struct CountInspector {
9    /// Map from opcode value to count of times it was executed.
10    opcode_counts: HashMap<u8, u64>,
11    /// Count of initialize_interp calls.
12    initialize_interp_count: u64,
13    /// Count of step calls.
14    step_count: u64,
15    /// Count of step_end calls.
16    step_end_count: u64,
17    /// Count of log calls.
18    log_count: u64,
19    /// Count of call calls.
20    call_count: u64,
21    /// Count of call_end calls.
22    call_end_count: u64,
23    /// Count of create calls.
24    create_count: u64,
25    /// Count of create_end calls.
26    create_end_count: u64,
27    /// Count of selfdestruct calls.
28    selfdestruct_count: u64,
29}
30
31impl CountInspector {
32    /// Create a new CountInspector.
33    pub fn new() -> Self {
34        Self {
35            opcode_counts: HashMap::default(),
36            initialize_interp_count: 0,
37            step_count: 0,
38            step_end_count: 0,
39            log_count: 0,
40            call_count: 0,
41            call_end_count: 0,
42            create_count: 0,
43            create_end_count: 0,
44            selfdestruct_count: 0,
45        }
46    }
47
48    /// Get the count for a specific opcode.
49    pub fn get_count(&self, opcode: u8) -> u64 {
50        self.opcode_counts.get(&opcode).copied().unwrap_or(0)
51    }
52
53    /// Get a reference to all opcode counts.
54    pub fn opcode_counts(&self) -> &HashMap<u8, u64> {
55        &self.opcode_counts
56    }
57
58    /// Get the total number of opcodes executed.
59    pub fn total_opcodes(&self) -> u64 {
60        self.opcode_counts.values().sum()
61    }
62
63    /// Get the number of unique opcodes executed.
64    pub fn unique_opcodes(&self) -> usize {
65        self.opcode_counts.len()
66    }
67
68    /// Clear all counts.
69    pub fn clear(&mut self) {
70        self.opcode_counts.clear();
71        self.initialize_interp_count = 0;
72        self.step_count = 0;
73        self.step_end_count = 0;
74        self.log_count = 0;
75        self.call_count = 0;
76        self.call_end_count = 0;
77        self.create_count = 0;
78        self.create_end_count = 0;
79        self.selfdestruct_count = 0;
80    }
81
82    /// Get the count of initialize_interp calls.
83    pub fn initialize_interp_count(&self) -> u64 {
84        self.initialize_interp_count
85    }
86
87    /// Get the count of step calls.
88    pub fn step_count(&self) -> u64 {
89        self.step_count
90    }
91
92    /// Get the count of step_end calls.
93    pub fn step_end_count(&self) -> u64 {
94        self.step_end_count
95    }
96
97    /// Get the count of log calls.
98    pub fn log_count(&self) -> u64 {
99        self.log_count
100    }
101
102    /// Get the count of call calls.
103    pub fn call_count(&self) -> u64 {
104        self.call_count
105    }
106
107    /// Get the count of call_end calls.
108    pub fn call_end_count(&self) -> u64 {
109        self.call_end_count
110    }
111
112    /// Get the count of create calls.
113    pub fn create_count(&self) -> u64 {
114        self.create_count
115    }
116
117    /// Get the count of create_end calls.
118    pub fn create_end_count(&self) -> u64 {
119        self.create_end_count
120    }
121
122    /// Get the count of selfdestruct calls.
123    pub fn selfdestruct_count(&self) -> u64 {
124        self.selfdestruct_count
125    }
126}
127
128impl<CTX, INTR: InterpreterTypes> Inspector<CTX, INTR> for CountInspector {
129    fn initialize_interp(
130        &mut self,
131        _interp: &mut interpreter::Interpreter<INTR>,
132        _context: &mut CTX,
133    ) {
134        self.initialize_interp_count += 1;
135    }
136
137    fn step(&mut self, interp: &mut interpreter::Interpreter<INTR>, _context: &mut CTX) {
138        self.step_count += 1;
139        let opcode = interp.bytecode.opcode();
140        *self.opcode_counts.entry(opcode).or_insert(0) += 1;
141    }
142
143    fn step_end(&mut self, _interp: &mut interpreter::Interpreter<INTR>, _context: &mut CTX) {
144        self.step_end_count += 1;
145    }
146
147    fn log(
148        &mut self,
149        _interp: &mut interpreter::Interpreter<INTR>,
150        _context: &mut CTX,
151        _log: primitives::Log,
152    ) {
153        self.log_count += 1;
154    }
155
156    fn call(
157        &mut self,
158        _context: &mut CTX,
159        _inputs: &mut interpreter::CallInputs,
160    ) -> Option<interpreter::CallOutcome> {
161        self.call_count += 1;
162        None
163    }
164
165    fn call_end(
166        &mut self,
167        _context: &mut CTX,
168        _inputs: &interpreter::CallInputs,
169        _outcome: &mut interpreter::CallOutcome,
170    ) {
171        self.call_end_count += 1;
172    }
173
174    fn create(
175        &mut self,
176        _context: &mut CTX,
177        _inputs: &mut interpreter::CreateInputs,
178    ) -> Option<interpreter::CreateOutcome> {
179        self.create_count += 1;
180        None
181    }
182
183    fn create_end(
184        &mut self,
185        _context: &mut CTX,
186        _inputs: &interpreter::CreateInputs,
187        _outcome: &mut interpreter::CreateOutcome,
188    ) {
189        self.create_end_count += 1;
190    }
191
192    fn selfdestruct(
193        &mut self,
194        _contract: primitives::Address,
195        _target: primitives::Address,
196        _value: primitives::U256,
197    ) {
198        self.selfdestruct_count += 1;
199    }
200}
201
202#[cfg(test)]
203mod tests {
204    use super::*;
205    use crate::InspectEvm;
206    use context::Context;
207    use database::BenchmarkDB;
208    use handler::{MainBuilder, MainContext};
209    use primitives::{Bytes, TxKind};
210    use state::bytecode::{opcode, Bytecode};
211
212    #[test]
213    fn test_count_inspector() {
214        // Create simple bytecode that just adds two numbers and stops
215        let contract_data: Bytes = Bytes::from(vec![
216            opcode::PUSH1,
217            0x10, // 0: PUSH1 16
218            opcode::PUSH1,
219            0x20,         // 2: PUSH1 32
220            opcode::ADD,  // 4: ADD
221            opcode::DUP1, // 5: DUP1 (duplicate the result)
222            opcode::PUSH1,
223            0x00,           // 6: PUSH1 0
224            opcode::MSTORE, // 8: MSTORE (store result in memory)
225            opcode::STOP,   // 9: STOP
226        ]);
227        let bytecode = Bytecode::new_raw(contract_data);
228
229        let ctx = Context::mainnet().with_db(BenchmarkDB::new_bytecode(bytecode.clone()));
230        let mut count_inspector = CountInspector::new();
231
232        let mut evm = ctx.build_mainnet_with_inspector(&mut count_inspector);
233
234        // Execute the contract
235        evm.inspect_one_tx(
236            context::TxEnv::builder()
237                .kind(TxKind::Call(database::BENCH_TARGET))
238                .gas_limit(30000)
239                .build()
240                .unwrap(),
241        )
242        .unwrap();
243
244        // Check opcode counts
245        assert_eq!(count_inspector.get_count(opcode::PUSH1), 3);
246        assert_eq!(count_inspector.get_count(opcode::ADD), 1);
247        assert_eq!(count_inspector.get_count(opcode::DUP1), 1);
248        assert_eq!(count_inspector.get_count(opcode::MSTORE), 1);
249        assert_eq!(count_inspector.get_count(opcode::STOP), 1);
250
251        // Check totals
252        assert_eq!(count_inspector.total_opcodes(), 7);
253        assert_eq!(count_inspector.unique_opcodes(), 5);
254
255        // Check inspector function counts
256        assert_eq!(count_inspector.initialize_interp_count(), 1);
257        assert_eq!(count_inspector.step_count(), 7); // Each opcode triggers a step
258        assert_eq!(count_inspector.step_end_count(), 7); // Each opcode triggers a step_end
259        assert_eq!(count_inspector.log_count(), 0); // No LOG opcodes
260        assert_eq!(count_inspector.call_count(), 1); // The transaction itself is a call
261        assert_eq!(count_inspector.call_end_count(), 1);
262        assert_eq!(count_inspector.create_count(), 0); // No CREATE opcodes
263        assert_eq!(count_inspector.create_end_count(), 0);
264        assert_eq!(count_inspector.selfdestruct_count(), 0); // No SELFDESTRUCT opcodes
265    }
266
267    #[test]
268    fn test_count_inspector_clear() {
269        let mut inspector = CountInspector::new();
270
271        // Add some counts manually for testing
272        *inspector.opcode_counts.entry(opcode::PUSH1).or_insert(0) += 5;
273        *inspector.opcode_counts.entry(opcode::ADD).or_insert(0) += 3;
274        inspector.initialize_interp_count = 2;
275        inspector.step_count = 10;
276        inspector.step_end_count = 10;
277        inspector.log_count = 1;
278        inspector.call_count = 3;
279        inspector.call_end_count = 3;
280        inspector.create_count = 1;
281        inspector.create_end_count = 1;
282        inspector.selfdestruct_count = 1;
283
284        assert_eq!(inspector.total_opcodes(), 8);
285        assert_eq!(inspector.unique_opcodes(), 2);
286        assert_eq!(inspector.initialize_interp_count(), 2);
287        assert_eq!(inspector.step_count(), 10);
288
289        // Clear and verify
290        inspector.clear();
291        assert_eq!(inspector.total_opcodes(), 0);
292        assert_eq!(inspector.unique_opcodes(), 0);
293        assert!(inspector.opcode_counts().is_empty());
294        assert_eq!(inspector.initialize_interp_count(), 0);
295        assert_eq!(inspector.step_count(), 0);
296        assert_eq!(inspector.step_end_count(), 0);
297        assert_eq!(inspector.log_count(), 0);
298        assert_eq!(inspector.call_count(), 0);
299        assert_eq!(inspector.call_end_count(), 0);
300        assert_eq!(inspector.create_count(), 0);
301        assert_eq!(inspector.create_end_count(), 0);
302        assert_eq!(inspector.selfdestruct_count(), 0);
303    }
304
305    #[test]
306    fn test_count_inspector_with_logs() {
307        // Create bytecode that emits a log
308        let contract_data: Bytes = Bytes::from(vec![
309            opcode::PUSH1,
310            0x20, // 0: PUSH1 32 (length)
311            opcode::PUSH1,
312            0x00,         // 2: PUSH1 0 (offset)
313            opcode::LOG0, // 4: LOG0 - emit log with no topics
314            opcode::STOP, // 5: STOP
315        ]);
316        let bytecode = Bytecode::new_raw(contract_data);
317
318        let ctx = Context::mainnet().with_db(BenchmarkDB::new_bytecode(bytecode.clone()));
319        let mut count_inspector = CountInspector::new();
320
321        let mut evm = ctx.build_mainnet_with_inspector(&mut count_inspector);
322
323        // Execute the contract
324        evm.inspect_one_tx(
325            context::TxEnv::builder()
326                .kind(TxKind::Call(database::BENCH_TARGET))
327                .gas_limit(30000)
328                .build()
329                .unwrap(),
330        )
331        .unwrap();
332
333        // Check that log was counted
334        assert_eq!(count_inspector.log_count(), 1);
335        assert_eq!(count_inspector.step_count(), 4); // 2 PUSH1 + LOG0 + STOP
336    }
337}