Skip to main content

evm_lens_core/
stats.rs

1use revm::bytecode::{Bytecode, opcode::OPCODE_INFO};
2
3#[derive(Debug)]
4pub struct Stats {
5    pub byte_len: usize,
6    pub opcode_count: usize,
7    pub max_stack_depth: usize,
8}
9
10#[derive(Debug)]
11pub enum StatsError {
12    UnknownOpcode(u8),
13}
14
15impl std::fmt::Display for StatsError {
16    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17        match self {
18            StatsError::UnknownOpcode(opcode) => {
19                write!(f, "Unknown opcode: 0x{opcode:02x}")
20            }
21        }
22    }
23}
24
25impl std::error::Error for StatsError {}
26
27pub fn compute_stats(bytecode: &Bytecode) -> Result<Stats, StatsError> {
28    // Count the number of opcodes
29    let opcode_count = compute_opcode_count(bytecode);
30
31    // Get total byte length
32    let byte_len = get_byte_len(bytecode);
33
34    // Track PUSH / POP depth
35    let max_stack_depth = compute_max_stack_depth(bytecode)?;
36
37    Ok(Stats {
38        byte_len,
39        opcode_count,
40        max_stack_depth,
41    })
42}
43
44fn compute_opcode_count(bytecode: &Bytecode) -> usize {
45    let iter = bytecode.iter_opcodes();
46    iter.count()
47}
48
49fn get_byte_len(bytecode: &Bytecode) -> usize {
50    bytecode.bytecode().as_ref().len()
51}
52
53fn compute_max_stack_depth(bytecode: &Bytecode) -> Result<usize, StatsError> {
54    let mut iter = bytecode.iter_opcodes();
55    let mut max_depth: i32 = 0;
56    let mut depth: i32 = 0;
57
58    while let Some(opcode) = iter.peek_opcode() {
59        let opcode_info = OPCODE_INFO[opcode.get() as usize];
60
61        match opcode_info {
62            Some(opcode_info) => {
63                depth += opcode_info.io_diff() as i32;
64            }
65            None => {
66                // If the opcode is not found, it's an invalid opcode
67                return Err(StatsError::UnknownOpcode(opcode.get()));
68            }
69        }
70
71        max_depth = max_depth.max(depth);
72        iter.next();
73    }
74
75    Ok(max_depth as usize)
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81    use revm::primitives::Bytes;
82
83    #[test]
84    fn test_simple_bytecode_stats() {
85        // PUSH1 0xFF, STOP
86        let bytes = hex::decode("60FF00").unwrap();
87        let bytecode = Bytecode::new_raw_checked(Bytes::from(bytes)).unwrap();
88
89        let stats = compute_stats(&bytecode).unwrap();
90        assert_eq!(stats.byte_len, 3);
91        assert_eq!(stats.opcode_count, 2);
92        assert_eq!(stats.max_stack_depth, 1); // PUSH1 adds 1 to stack
93    }
94
95    #[test]
96    fn test_complex_bytecode_stats() {
97        // PUSH1 0x01, PUSH1 0x02, ADD, STOP
98        let bytes = hex::decode("600160020100").unwrap();
99        let bytecode = Bytecode::new_raw_checked(Bytes::from(bytes)).unwrap();
100
101        let stats = compute_stats(&bytecode).unwrap();
102        assert_eq!(stats.byte_len, 6);
103        assert_eq!(stats.opcode_count, 4);
104        assert_eq!(stats.max_stack_depth, 2); // Max depth when both PUSH1s are on stack
105    }
106
107    #[test]
108    fn test_stack_operations() {
109        // PUSH1 0x01, PUSH1 0x02, DUP1, SWAP1, ADD, STOP
110        let bytes = hex::decode("6001600280900100").unwrap();
111        let bytecode = Bytecode::new_raw_checked(Bytes::from(bytes)).unwrap();
112
113        let stats = compute_stats(&bytecode).unwrap();
114        assert_eq!(stats.byte_len, 8);
115        assert_eq!(stats.opcode_count, 6);
116        assert_eq!(stats.max_stack_depth, 3); // DUP1 increases stack depth to 3
117    }
118
119    #[test]
120    fn test_memory_operations() {
121        // PUSH1 0x20, PUSH1 0x00, MSTORE, PUSH1 0x00, MLOAD, STOP
122        let bytes = hex::decode("602060005260005100").unwrap();
123        let bytecode = Bytecode::new_raw_checked(Bytes::from(bytes)).unwrap();
124
125        let stats = compute_stats(&bytecode).unwrap();
126        assert_eq!(stats.byte_len, 9);
127        assert_eq!(stats.opcode_count, 6);
128        assert_eq!(stats.max_stack_depth, 2); // Max when PUSH1 values are on stack
129    }
130
131    #[test]
132    fn test_push_operations() {
133        // PUSH1 0xFF, PUSH2 0xABCD, PUSH32 (32 bytes of data), STOP
134        let mut bytes = vec![0x60, 0xFF]; // PUSH1 0xFF
135        bytes.extend_from_slice(&[0x61, 0xAB, 0xCD]); // PUSH2 0xABCD
136        bytes.push(0x7F); // PUSH32
137        bytes.extend_from_slice(&[0xFF; 32]); // 32 bytes of 0xFF
138        bytes.push(0x00); // STOP
139
140        let bytecode = Bytecode::new_raw_checked(Bytes::from(bytes)).unwrap();
141
142        let stats = compute_stats(&bytecode).unwrap();
143        assert_eq!(stats.byte_len, 39); // 2 + 3 + 1 + 32 + 1 = 39
144        assert_eq!(stats.opcode_count, 4);
145        assert_eq!(stats.max_stack_depth, 3); // All three PUSH operations on stack
146    }
147
148    #[test]
149    fn test_arithmetic_operations() {
150        // PUSH1 0x05, PUSH1 0x03, ADD, PUSH1 0x02, MUL, STOP
151        let bytes = hex::decode("600560030160020200").unwrap();
152        let bytecode = Bytecode::new_raw_checked(Bytes::from(bytes)).unwrap();
153
154        let stats = compute_stats(&bytecode).unwrap();
155        assert_eq!(stats.byte_len, 9);
156        assert_eq!(stats.opcode_count, 6);
157        assert_eq!(stats.max_stack_depth, 2); // Max depth when two values are on stack
158    }
159
160    #[test]
161    fn test_single_opcode() {
162        // Just STOP
163        let bytes = hex::decode("00").unwrap();
164        let bytecode = Bytecode::new_raw_checked(Bytes::from(bytes)).unwrap();
165
166        let stats = compute_stats(&bytecode).unwrap();
167        assert_eq!(stats.byte_len, 1);
168        assert_eq!(stats.opcode_count, 1);
169        assert_eq!(stats.max_stack_depth, 0); // STOP doesn't affect stack
170    }
171
172    #[test]
173    fn test_large_bytecode() {
174        // Create a larger bytecode with many operations
175        let mut bytes = Vec::new();
176
177        // Add 20 PUSH1 operations (reduced from 50 for simpler test)
178        for i in 0..20 {
179            bytes.push(0x60); // PUSH1
180            bytes.push(i as u8); // value
181        }
182        bytes.push(0x00); // STOP
183
184        let bytecode = Bytecode::new_raw_checked(Bytes::from(bytes)).unwrap();
185
186        let stats = compute_stats(&bytecode).unwrap();
187        assert_eq!(stats.byte_len, 41); // 20 * 2 + 1
188        assert_eq!(stats.opcode_count, 21); // 20 PUSH1s + 1 STOP
189        assert_eq!(stats.max_stack_depth, 20); // All PUSH1s accumulate on stack
190    }
191
192    #[test]
193    fn test_compute_opcode_count() {
194        let bytes = hex::decode("60FF61ABCD00").unwrap(); // PUSH1, PUSH2, STOP
195        let bytecode = Bytecode::new_raw_checked(Bytes::from(bytes)).unwrap();
196
197        let count = compute_opcode_count(&bytecode);
198        assert_eq!(count, 3);
199    }
200
201    #[test]
202    fn test_get_byte_len() {
203        let bytes = hex::decode("60FF61ABCD00").unwrap();
204        let bytecode = Bytecode::new_raw_checked(Bytes::from(bytes)).unwrap();
205
206        let len = get_byte_len(&bytecode);
207        assert_eq!(len, 6);
208    }
209
210    #[test]
211    fn test_compute_max_stack_depth() {
212        let bytes = hex::decode("60FF00").unwrap(); // PUSH1 0xFF, STOP
213        let bytecode = Bytecode::new_raw_checked(Bytes::from(bytes)).unwrap();
214
215        let depth = compute_max_stack_depth(&bytecode).unwrap();
216        assert_eq!(depth, 1);
217    }
218
219    #[test]
220    fn test_zero_stack_depth() {
221        let bytes = hex::decode("00").unwrap(); // Just STOP
222        let bytecode = Bytecode::new_raw_checked(Bytes::from(bytes)).unwrap();
223
224        let depth = compute_max_stack_depth(&bytecode).unwrap();
225        assert_eq!(depth, 0);
226    }
227
228    #[test]
229    fn test_error_display() {
230        let error = StatsError::UnknownOpcode(0xFF);
231        assert_eq!(format!("{}", error), "Unknown opcode: 0xff");
232    }
233
234    #[test]
235    fn test_stats_struct_access() {
236        let stats = Stats {
237            byte_len: 10,
238            opcode_count: 5,
239            max_stack_depth: 3,
240        };
241
242        assert_eq!(stats.byte_len, 10);
243        assert_eq!(stats.opcode_count, 5);
244        assert_eq!(stats.max_stack_depth, 3);
245    }
246}