use crate::inspector::Inspector;
use interpreter::{interpreter_types::Jumps, InterpreterTypes};
use primitives::Log;
#[derive(Clone, Debug)]
pub struct CountInspector {
opcode_counts: [u64; 256],
initialize_interp_count: u64,
step_count: u64,
step_end_count: u64,
log_count: u64,
call_count: u64,
call_end_count: u64,
create_count: u64,
create_end_count: u64,
selfdestruct_count: u64,
}
impl Default for CountInspector {
fn default() -> Self {
Self {
opcode_counts: [0; 256],
initialize_interp_count: 0,
step_count: 0,
step_end_count: 0,
log_count: 0,
call_count: 0,
call_end_count: 0,
create_count: 0,
create_end_count: 0,
selfdestruct_count: 0,
}
}
}
impl CountInspector {
pub fn new() -> Self {
Self::default()
}
pub fn get_count(&self, opcode: u8) -> u64 {
self.opcode_counts[opcode as usize]
}
pub fn opcode_counts(&self) -> &[u64; 256] {
&self.opcode_counts
}
pub fn total_opcodes(&self) -> u64 {
self.opcode_counts.iter().copied().sum()
}
pub fn unique_opcodes(&self) -> usize {
self.opcode_counts
.iter()
.filter(|&&count| count > 0)
.count()
}
pub fn clear(&mut self) {
self.opcode_counts = [0; 256];
self.initialize_interp_count = 0;
self.step_count = 0;
self.step_end_count = 0;
self.log_count = 0;
self.call_count = 0;
self.call_end_count = 0;
self.create_count = 0;
self.create_end_count = 0;
self.selfdestruct_count = 0;
}
pub fn initialize_interp_count(&self) -> u64 {
self.initialize_interp_count
}
pub fn step_count(&self) -> u64 {
self.step_count
}
pub fn step_end_count(&self) -> u64 {
self.step_end_count
}
pub fn log_count(&self) -> u64 {
self.log_count
}
pub fn call_count(&self) -> u64 {
self.call_count
}
pub fn call_end_count(&self) -> u64 {
self.call_end_count
}
pub fn create_count(&self) -> u64 {
self.create_count
}
pub fn create_end_count(&self) -> u64 {
self.create_end_count
}
pub fn selfdestruct_count(&self) -> u64 {
self.selfdestruct_count
}
}
impl<CTX, INTR: InterpreterTypes> Inspector<CTX, INTR> for CountInspector {
fn initialize_interp(
&mut self,
_interp: &mut interpreter::Interpreter<INTR>,
_context: &mut CTX,
) {
self.initialize_interp_count += 1;
}
fn step(&mut self, interp: &mut interpreter::Interpreter<INTR>, _context: &mut CTX) {
self.step_count += 1;
let opcode = interp.bytecode.opcode();
self.opcode_counts[opcode as usize] += 1;
}
fn step_end(&mut self, _interp: &mut interpreter::Interpreter<INTR>, _context: &mut CTX) {
self.step_end_count += 1;
}
fn log(&mut self, _context: &mut CTX, _log: Log) {
self.log_count += 1;
}
fn call(
&mut self,
_context: &mut CTX,
_inputs: &mut interpreter::CallInputs,
) -> Option<interpreter::CallOutcome> {
self.call_count += 1;
None
}
fn call_end(
&mut self,
_context: &mut CTX,
_inputs: &interpreter::CallInputs,
_outcome: &mut interpreter::CallOutcome,
) {
self.call_end_count += 1;
}
fn create(
&mut self,
_context: &mut CTX,
_inputs: &mut interpreter::CreateInputs,
) -> Option<interpreter::CreateOutcome> {
self.create_count += 1;
None
}
fn create_end(
&mut self,
_context: &mut CTX,
_inputs: &interpreter::CreateInputs,
_outcome: &mut interpreter::CreateOutcome,
) {
self.create_end_count += 1;
}
fn selfdestruct(
&mut self,
_contract: primitives::Address,
_target: primitives::Address,
_value: primitives::U256,
) {
self.selfdestruct_count += 1;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::InspectEvm;
use context::Context;
use database::BenchmarkDB;
use handler::{MainBuilder, MainContext};
use primitives::{Bytes, TxKind};
use state::bytecode::{opcode, Bytecode};
#[test]
fn test_count_inspector() {
let contract_data: Bytes = Bytes::from(vec![
opcode::PUSH1,
0x10, opcode::PUSH1,
0x20, opcode::ADD, opcode::DUP1, opcode::PUSH1,
0x00, opcode::MSTORE, opcode::STOP, ]);
let bytecode = Bytecode::new_raw(contract_data);
let ctx = Context::mainnet().with_db(BenchmarkDB::new_bytecode(bytecode.clone()));
let mut count_inspector = CountInspector::new();
let mut evm = ctx.build_mainnet_with_inspector(&mut count_inspector);
evm.inspect_one_tx(
context::TxEnv::builder()
.kind(TxKind::Call(database::BENCH_TARGET))
.gas_limit(30000)
.build()
.unwrap(),
)
.unwrap();
assert_eq!(count_inspector.get_count(opcode::PUSH1), 3);
assert_eq!(count_inspector.get_count(opcode::ADD), 1);
assert_eq!(count_inspector.get_count(opcode::DUP1), 1);
assert_eq!(count_inspector.get_count(opcode::MSTORE), 1);
assert_eq!(count_inspector.get_count(opcode::STOP), 1);
assert_eq!(count_inspector.total_opcodes(), 7);
assert_eq!(count_inspector.unique_opcodes(), 5);
assert_eq!(count_inspector.initialize_interp_count(), 1);
assert_eq!(count_inspector.step_count(), 7); assert_eq!(count_inspector.step_end_count(), 7); assert_eq!(count_inspector.log_count(), 0); assert_eq!(count_inspector.call_count(), 1); assert_eq!(count_inspector.call_end_count(), 1);
assert_eq!(count_inspector.create_count(), 0); assert_eq!(count_inspector.create_end_count(), 0);
assert_eq!(count_inspector.selfdestruct_count(), 0); }
#[test]
fn test_count_inspector_clear() {
let mut inspector = CountInspector::new();
inspector.opcode_counts[opcode::PUSH1 as usize] += 5;
inspector.opcode_counts[opcode::ADD as usize] += 3;
inspector.initialize_interp_count = 2;
inspector.step_count = 10;
inspector.step_end_count = 10;
inspector.log_count = 1;
inspector.call_count = 3;
inspector.call_end_count = 3;
inspector.create_count = 1;
inspector.create_end_count = 1;
inspector.selfdestruct_count = 1;
assert_eq!(inspector.total_opcodes(), 8);
assert_eq!(inspector.unique_opcodes(), 2);
assert_eq!(inspector.initialize_interp_count(), 2);
assert_eq!(inspector.step_count(), 10);
inspector.clear();
assert_eq!(inspector.total_opcodes(), 0);
assert_eq!(inspector.unique_opcodes(), 0);
assert_eq!(inspector.initialize_interp_count(), 0);
assert_eq!(inspector.step_count(), 0);
assert_eq!(inspector.step_end_count(), 0);
assert_eq!(inspector.log_count(), 0);
assert_eq!(inspector.call_count(), 0);
assert_eq!(inspector.call_end_count(), 0);
assert_eq!(inspector.create_count(), 0);
assert_eq!(inspector.create_end_count(), 0);
assert_eq!(inspector.selfdestruct_count(), 0);
assert!(inspector.opcode_counts().iter().all(|&count| count == 0));
}
#[test]
fn test_count_inspector_with_logs() {
let contract_data: Bytes = Bytes::from(vec![
opcode::PUSH1,
0x20, opcode::PUSH1,
0x00, opcode::LOG0, opcode::STOP, ]);
let bytecode = Bytecode::new_raw(contract_data);
let ctx = Context::mainnet().with_db(BenchmarkDB::new_bytecode(bytecode.clone()));
let mut count_inspector = CountInspector::new();
let mut evm = ctx.build_mainnet_with_inspector(&mut count_inspector);
evm.inspect_one_tx(
context::TxEnv::builder()
.kind(TxKind::Call(database::BENCH_TARGET))
.gas_limit(30000)
.build()
.unwrap(),
)
.unwrap();
assert_eq!(count_inspector.log_count(), 1);
assert_eq!(count_inspector.step_count(), 4); }
}