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