#[cfg(not(feature = "std"))]
use alloc as std;
use std::{rc::Rc, vec::Vec};
use core::cell::RefCell;
use revm::{
bytecode::OpCode,
context::{ContextTr, JournalTr},
interpreter::{
interpreter_types::Jumps, CallInputs, CallOutcome, CreateInputs, Interpreter,
InterpreterTypes,
},
Inspector,
};
#[derive(Debug, derive_more::Deref)]
pub struct Pointer<T>(#[deref] Rc<RefCell<T>>);
impl<T> Clone for Pointer<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T> From<T> for Pointer<T> {
fn from(value: T) -> Self {
Self(Rc::new(RefCell::new(value)))
}
}
impl<T: Clone> Pointer<T> {
pub fn clone_inner(&self) -> T {
self.0.borrow().clone()
}
}
pub type NodeLocation = Vec<usize>;
pub type ItemLocation = (usize, usize);
#[derive(Debug)]
pub struct TraceLocation {
pub call: NodeLocation,
pub item: ItemLocation,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct TraceNode<ITEM, META> {
pub meta: META,
pub parent: Option<Pointer<Self>>,
pub sections: Vec<Vec<Pointer<ITEM>>>,
pub childrens: Vec<Pointer<Self>>,
}
impl<ITEM, META> TraceNode<ITEM, META> {
pub fn new(meta: META, parent: Option<Pointer<Self>>) -> Pointer<Self> {
let this: Pointer<Self> =
Self { meta, parent: parent.clone(), sections: vec![Vec::new()], childrens: vec![] }
.into();
if let Some(p) = parent {
p.borrow_mut().add_child(this.clone());
}
this
}
pub fn last_item(&self) -> Option<Pointer<ITEM>> {
self.sections.iter().flatten().last().cloned()
}
pub fn add_item(&mut self, item: ITEM) {
self.sections[self.childrens.len()].push(item.into());
}
fn add_child<T: Into<Pointer<Self>>>(&mut self, child: T) {
self.childrens.push(child.into());
self.sections.push(Vec::new());
}
}
impl<ITEM, META> Pointer<TraceNode<ITEM, META>> {
pub fn flattened_items(&self) -> Vec<Pointer<ITEM>> {
let mut items = Vec::new();
for i in 0..self.borrow().childrens.len() {
items.extend(self.borrow().sections[i].iter().cloned());
items.extend(self.borrow().childrens[i].flattened_items());
}
items.extend(self.borrow().sections[self.borrow().childrens.len()].iter().cloned());
items
}
pub fn get_node(&self, location: NodeLocation) -> Option<Self> {
if location.is_empty() {
Some(self.clone())
} else {
self.borrow().childrens[location[0]].get_node(location[1..].to_vec())
}
}
pub fn get_item(&self, location: ItemLocation) -> Option<Pointer<ITEM>> {
self.borrow().sections.get(location.0).and_then(|section| section.get(location.1)).cloned()
}
pub fn get_item_nested(&self, location: TraceLocation) -> Option<Pointer<ITEM>> {
self.get_node(location.call).and_then(|n| n.get_item(location.item))
}
pub fn iterate_with<F>(&self, mut f: F)
where
F: FnMut(NodeLocation, Self, ItemLocation, Pointer<ITEM>),
{
self.iterate_with_inner(&mut f, vec![]);
}
fn iterate_with_inner<F>(&self, f: &mut F, node_location: NodeLocation)
where
F: FnMut(NodeLocation, Self, ItemLocation, Pointer<ITEM>),
{
let sections = &self.borrow().sections;
for (section_idx, section) in sections.iter().enumerate() {
for (item_idx, item) in section.iter().enumerate() {
f(node_location.clone(), self.clone(), (section_idx, item_idx), item.clone());
}
if section_idx < sections.len() - 1 {
let mut child_node_location = node_location.clone();
child_node_location.push(section_idx);
self.borrow().childrens[section_idx].iterate_with_inner(f, child_node_location);
}
}
}
}
#[derive(Clone)]
pub struct OpcodeGasInfo {
pub opcode: OpCode,
pub gas_before: u64,
pub gas_after: u64,
pub depth: u64,
}
impl core::fmt::Debug for OpcodeGasInfo {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"{}[depth={}] gas: {} -> {} (cost: {})",
self.opcode.as_str(),
self.depth,
self.gas_before,
self.gas_after,
self.gas_cost()
)
}
}
impl OpcodeGasInfo {
pub fn gas_cost(&self) -> u64 {
self.gas_before.saturating_sub(self.gas_after)
}
}
#[derive(Debug, Clone)]
pub enum MsgCallMeta {
Call(CallInputs),
Create(CreateInputs),
}
#[derive(Debug, Default)]
pub struct GasInspector {
pub stack: Vec<Pointer<TraceNode<OpcodeGasInfo, MsgCallMeta>>>,
pub trace: Option<Pointer<TraceNode<OpcodeGasInfo, MsgCallMeta>>>,
}
impl GasInspector {
pub fn new() -> Self {
Self::default()
}
pub fn clear(&mut self) {
self.trace = None;
self.stack = vec![];
}
pub fn records(&self) -> Vec<OpcodeGasInfo> {
self.trace
.as_ref()
.map(|t| t.flattened_items().into_iter().map(|p| p.clone_inner()).collect())
.unwrap_or_default()
}
}
impl<CTX: ContextTr, INTR: InterpreterTypes> Inspector<CTX, INTR> for GasInspector {
fn call(&mut self, _context: &mut CTX, inputs: &mut CallInputs) -> Option<CallOutcome> {
let parent = self.stack.last().cloned();
let msg_call = TraceNode::new(MsgCallMeta::Call(inputs.clone()), parent);
self.stack.push(msg_call.clone());
if self.trace.is_none() {
self.trace = Some(msg_call);
}
None
}
fn call_end(&mut self, _context: &mut CTX, _inputs: &CallInputs, _outcome: &mut CallOutcome) {
self.stack.pop();
}
fn create(
&mut self,
_context: &mut CTX,
inputs: &mut CreateInputs,
) -> Option<revm::interpreter::CreateOutcome> {
let parent = self.stack.last().cloned();
let msg_call = TraceNode::new(MsgCallMeta::Create(inputs.clone()), parent);
self.stack.push(msg_call.clone());
if self.trace.is_none() {
self.trace = Some(msg_call);
}
None
}
fn create_end(
&mut self,
_context: &mut CTX,
_inputs: &CreateInputs,
_outcome: &mut revm::interpreter::CreateOutcome,
) {
self.stack.pop();
}
fn step(&mut self, interp: &mut Interpreter<INTR>, context: &mut CTX) {
let gas_before = interp.gas.remaining();
let opcode = interp.bytecode.opcode();
let depth = context.journal().depth();
let step = OpcodeGasInfo {
opcode: OpCode::new(opcode).unwrap(),
gas_before,
gas_after: gas_before, depth: depth as u64,
};
if let Some(c) = self.stack.last_mut() {
c.borrow_mut().add_item(step)
}
}
fn step_end(&mut self, interp: &mut Interpreter<INTR>, _context: &mut CTX) {
let current = self
.stack
.last()
.as_ref()
.and_then(|c| c.borrow_mut().last_item())
.expect("current is not None");
current.borrow_mut().gas_after = interp.gas.remaining();
}
}