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, Log};
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(&mut self, _context: &mut CTX, _log: Log) {
137        self.log_count += 1;
138    }
139
140    fn call(
141        &mut self,
142        _context: &mut CTX,
143        _inputs: &mut interpreter::CallInputs,
144    ) -> Option<interpreter::CallOutcome> {
145        self.call_count += 1;
146        None
147    }
148
149    fn call_end(
150        &mut self,
151        _context: &mut CTX,
152        _inputs: &interpreter::CallInputs,
153        _outcome: &mut interpreter::CallOutcome,
154    ) {
155        self.call_end_count += 1;
156    }
157
158    fn create(
159        &mut self,
160        _context: &mut CTX,
161        _inputs: &mut interpreter::CreateInputs,
162    ) -> Option<interpreter::CreateOutcome> {
163        self.create_count += 1;
164        None
165    }
166
167    fn create_end(
168        &mut self,
169        _context: &mut CTX,
170        _inputs: &interpreter::CreateInputs,
171        _outcome: &mut interpreter::CreateOutcome,
172    ) {
173        self.create_end_count += 1;
174    }
175
176    fn selfdestruct(
177        &mut self,
178        _contract: primitives::Address,
179        _target: primitives::Address,
180        _value: primitives::U256,
181    ) {
182        self.selfdestruct_count += 1;
183    }
184}
185
186#[cfg(test)]
187mod tests {
188    use super::*;
189    use crate::InspectEvm;
190    use context::Context;
191    use database::BenchmarkDB;
192    use handler::{MainBuilder, MainContext};
193    use primitives::{Bytes, TxKind};
194    use state::bytecode::{opcode, Bytecode};
195
196    #[test]
197    fn test_count_inspector() {
198        // Create simple bytecode that just adds two numbers and stops
199        let contract_data: Bytes = Bytes::from(vec![
200            opcode::PUSH1,
201            0x10, // 0: PUSH1 16
202            opcode::PUSH1,
203            0x20,         // 2: PUSH1 32
204            opcode::ADD,  // 4: ADD
205            opcode::DUP1, // 5: DUP1 (duplicate the result)
206            opcode::PUSH1,
207            0x00,           // 6: PUSH1 0
208            opcode::MSTORE, // 8: MSTORE (store result in memory)
209            opcode::STOP,   // 9: STOP
210        ]);
211        let bytecode = Bytecode::new_raw(contract_data);
212
213        let ctx = Context::mainnet().with_db(BenchmarkDB::new_bytecode(bytecode.clone()));
214        let mut count_inspector = CountInspector::new();
215
216        let mut evm = ctx.build_mainnet_with_inspector(&mut count_inspector);
217
218        // Execute the contract
219        evm.inspect_one_tx(
220            context::TxEnv::builder()
221                .kind(TxKind::Call(database::BENCH_TARGET))
222                .gas_limit(30000)
223                .build()
224                .unwrap(),
225        )
226        .unwrap();
227
228        // Check opcode counts
229        assert_eq!(count_inspector.get_count(opcode::PUSH1), 3);
230        assert_eq!(count_inspector.get_count(opcode::ADD), 1);
231        assert_eq!(count_inspector.get_count(opcode::DUP1), 1);
232        assert_eq!(count_inspector.get_count(opcode::MSTORE), 1);
233        assert_eq!(count_inspector.get_count(opcode::STOP), 1);
234
235        // Check totals
236        assert_eq!(count_inspector.total_opcodes(), 7);
237        assert_eq!(count_inspector.unique_opcodes(), 5);
238
239        // Check inspector function counts
240        assert_eq!(count_inspector.initialize_interp_count(), 1);
241        assert_eq!(count_inspector.step_count(), 7); // Each opcode triggers a step
242        assert_eq!(count_inspector.step_end_count(), 7); // Each opcode triggers a step_end
243        assert_eq!(count_inspector.log_count(), 0); // No LOG opcodes
244        assert_eq!(count_inspector.call_count(), 1); // The transaction itself is a call
245        assert_eq!(count_inspector.call_end_count(), 1);
246        assert_eq!(count_inspector.create_count(), 0); // No CREATE opcodes
247        assert_eq!(count_inspector.create_end_count(), 0);
248        assert_eq!(count_inspector.selfdestruct_count(), 0); // No SELFDESTRUCT opcodes
249    }
250
251    #[test]
252    fn test_count_inspector_clear() {
253        let mut inspector = CountInspector::new();
254
255        // Add some counts manually for testing
256        *inspector.opcode_counts.entry(opcode::PUSH1).or_insert(0) += 5;
257        *inspector.opcode_counts.entry(opcode::ADD).or_insert(0) += 3;
258        inspector.initialize_interp_count = 2;
259        inspector.step_count = 10;
260        inspector.step_end_count = 10;
261        inspector.log_count = 1;
262        inspector.call_count = 3;
263        inspector.call_end_count = 3;
264        inspector.create_count = 1;
265        inspector.create_end_count = 1;
266        inspector.selfdestruct_count = 1;
267
268        assert_eq!(inspector.total_opcodes(), 8);
269        assert_eq!(inspector.unique_opcodes(), 2);
270        assert_eq!(inspector.initialize_interp_count(), 2);
271        assert_eq!(inspector.step_count(), 10);
272
273        // Clear and verify
274        inspector.clear();
275        assert_eq!(inspector.total_opcodes(), 0);
276        assert_eq!(inspector.unique_opcodes(), 0);
277        assert!(inspector.opcode_counts().is_empty());
278        assert_eq!(inspector.initialize_interp_count(), 0);
279        assert_eq!(inspector.step_count(), 0);
280        assert_eq!(inspector.step_end_count(), 0);
281        assert_eq!(inspector.log_count(), 0);
282        assert_eq!(inspector.call_count(), 0);
283        assert_eq!(inspector.call_end_count(), 0);
284        assert_eq!(inspector.create_count(), 0);
285        assert_eq!(inspector.create_end_count(), 0);
286        assert_eq!(inspector.selfdestruct_count(), 0);
287    }
288
289    #[test]
290    fn test_count_inspector_with_logs() {
291        // Create bytecode that emits a log
292        let contract_data: Bytes = Bytes::from(vec![
293            opcode::PUSH1,
294            0x20, // 0: PUSH1 32 (length)
295            opcode::PUSH1,
296            0x00,         // 2: PUSH1 0 (offset)
297            opcode::LOG0, // 4: LOG0 - emit log with no topics
298            opcode::STOP, // 5: STOP
299        ]);
300        let bytecode = Bytecode::new_raw(contract_data);
301
302        let ctx = Context::mainnet().with_db(BenchmarkDB::new_bytecode(bytecode.clone()));
303        let mut count_inspector = CountInspector::new();
304
305        let mut evm = ctx.build_mainnet_with_inspector(&mut count_inspector);
306
307        // Execute the contract
308        evm.inspect_one_tx(
309            context::TxEnv::builder()
310                .kind(TxKind::Call(database::BENCH_TARGET))
311                .gas_limit(30000)
312                .build()
313                .unwrap(),
314        )
315        .unwrap();
316
317        // Check that log was counted
318        assert_eq!(count_inspector.log_count(), 1);
319        assert_eq!(count_inspector.step_count(), 4); // 2 PUSH1 + LOG0 + STOP
320    }
321}