use revm::bytecode::{Bytecode, opcode::OPCODE_INFO};
#[derive(Debug)]
pub struct Stats {
pub byte_len: usize,
pub opcode_count: usize,
pub max_stack_depth: usize,
}
#[derive(Debug)]
pub enum StatsError {
UnknownOpcode(u8),
}
impl std::fmt::Display for StatsError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
StatsError::UnknownOpcode(opcode) => {
write!(f, "Unknown opcode: 0x{opcode:02x}")
}
}
}
}
impl std::error::Error for StatsError {}
pub fn compute_stats(bytecode: &Bytecode) -> Result<Stats, StatsError> {
let opcode_count = compute_opcode_count(bytecode);
let byte_len = get_byte_len(bytecode);
let max_stack_depth = compute_max_stack_depth(bytecode)?;
Ok(Stats {
byte_len,
opcode_count,
max_stack_depth,
})
}
fn compute_opcode_count(bytecode: &Bytecode) -> usize {
let iter = bytecode.iter_opcodes();
iter.count()
}
fn get_byte_len(bytecode: &Bytecode) -> usize {
bytecode.bytecode().as_ref().len()
}
fn compute_max_stack_depth(bytecode: &Bytecode) -> Result<usize, StatsError> {
let mut iter = bytecode.iter_opcodes();
let mut max_depth: i32 = 0;
let mut depth: i32 = 0;
while let Some(opcode) = iter.peek_opcode() {
let opcode_info = OPCODE_INFO[opcode.get() as usize];
match opcode_info {
Some(opcode_info) => {
depth += opcode_info.io_diff() as i32;
}
None => {
return Err(StatsError::UnknownOpcode(opcode.get()));
}
}
max_depth = max_depth.max(depth);
iter.next();
}
Ok(max_depth as usize)
}
#[cfg(test)]
mod tests {
use super::*;
use revm::primitives::Bytes;
#[test]
fn test_simple_bytecode_stats() {
let bytes = hex::decode("60FF00").unwrap();
let bytecode = Bytecode::new_raw_checked(Bytes::from(bytes)).unwrap();
let stats = compute_stats(&bytecode).unwrap();
assert_eq!(stats.byte_len, 3);
assert_eq!(stats.opcode_count, 2);
assert_eq!(stats.max_stack_depth, 1); }
#[test]
fn test_complex_bytecode_stats() {
let bytes = hex::decode("600160020100").unwrap();
let bytecode = Bytecode::new_raw_checked(Bytes::from(bytes)).unwrap();
let stats = compute_stats(&bytecode).unwrap();
assert_eq!(stats.byte_len, 6);
assert_eq!(stats.opcode_count, 4);
assert_eq!(stats.max_stack_depth, 2); }
#[test]
fn test_stack_operations() {
let bytes = hex::decode("6001600280900100").unwrap();
let bytecode = Bytecode::new_raw_checked(Bytes::from(bytes)).unwrap();
let stats = compute_stats(&bytecode).unwrap();
assert_eq!(stats.byte_len, 8);
assert_eq!(stats.opcode_count, 6);
assert_eq!(stats.max_stack_depth, 3); }
#[test]
fn test_memory_operations() {
let bytes = hex::decode("602060005260005100").unwrap();
let bytecode = Bytecode::new_raw_checked(Bytes::from(bytes)).unwrap();
let stats = compute_stats(&bytecode).unwrap();
assert_eq!(stats.byte_len, 9);
assert_eq!(stats.opcode_count, 6);
assert_eq!(stats.max_stack_depth, 2); }
#[test]
fn test_push_operations() {
let mut bytes = vec![0x60, 0xFF]; bytes.extend_from_slice(&[0x61, 0xAB, 0xCD]); bytes.push(0x7F); bytes.extend_from_slice(&[0xFF; 32]); bytes.push(0x00);
let bytecode = Bytecode::new_raw_checked(Bytes::from(bytes)).unwrap();
let stats = compute_stats(&bytecode).unwrap();
assert_eq!(stats.byte_len, 39); assert_eq!(stats.opcode_count, 4);
assert_eq!(stats.max_stack_depth, 3); }
#[test]
fn test_arithmetic_operations() {
let bytes = hex::decode("600560030160020200").unwrap();
let bytecode = Bytecode::new_raw_checked(Bytes::from(bytes)).unwrap();
let stats = compute_stats(&bytecode).unwrap();
assert_eq!(stats.byte_len, 9);
assert_eq!(stats.opcode_count, 6);
assert_eq!(stats.max_stack_depth, 2); }
#[test]
fn test_single_opcode() {
let bytes = hex::decode("00").unwrap();
let bytecode = Bytecode::new_raw_checked(Bytes::from(bytes)).unwrap();
let stats = compute_stats(&bytecode).unwrap();
assert_eq!(stats.byte_len, 1);
assert_eq!(stats.opcode_count, 1);
assert_eq!(stats.max_stack_depth, 0); }
#[test]
fn test_large_bytecode() {
let mut bytes = Vec::new();
for i in 0..20 {
bytes.push(0x60); bytes.push(i as u8); }
bytes.push(0x00);
let bytecode = Bytecode::new_raw_checked(Bytes::from(bytes)).unwrap();
let stats = compute_stats(&bytecode).unwrap();
assert_eq!(stats.byte_len, 41); assert_eq!(stats.opcode_count, 21); assert_eq!(stats.max_stack_depth, 20); }
#[test]
fn test_compute_opcode_count() {
let bytes = hex::decode("60FF61ABCD00").unwrap(); let bytecode = Bytecode::new_raw_checked(Bytes::from(bytes)).unwrap();
let count = compute_opcode_count(&bytecode);
assert_eq!(count, 3);
}
#[test]
fn test_get_byte_len() {
let bytes = hex::decode("60FF61ABCD00").unwrap();
let bytecode = Bytecode::new_raw_checked(Bytes::from(bytes)).unwrap();
let len = get_byte_len(&bytecode);
assert_eq!(len, 6);
}
#[test]
fn test_compute_max_stack_depth() {
let bytes = hex::decode("60FF00").unwrap(); let bytecode = Bytecode::new_raw_checked(Bytes::from(bytes)).unwrap();
let depth = compute_max_stack_depth(&bytecode).unwrap();
assert_eq!(depth, 1);
}
#[test]
fn test_zero_stack_depth() {
let bytes = hex::decode("00").unwrap(); let bytecode = Bytecode::new_raw_checked(Bytes::from(bytes)).unwrap();
let depth = compute_max_stack_depth(&bytecode).unwrap();
assert_eq!(depth, 0);
}
#[test]
fn test_error_display() {
let error = StatsError::UnknownOpcode(0xFF);
assert_eq!(format!("{}", error), "Unknown opcode: 0xff");
}
#[test]
fn test_stats_struct_access() {
let stats = Stats {
byte_len: 10,
opcode_count: 5,
max_stack_depth: 3,
};
assert_eq!(stats.byte_len, 10);
assert_eq!(stats.opcode_count, 5);
assert_eq!(stats.max_stack_depth, 3);
}
}