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