Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions src/inspectors/layer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
use revm::{
interpreter::{
CallInputs, CallOutcome, CreateInputs, CreateOutcome, EOFCreateInputs, Interpreter,
InterpreterTypes,
},
primitives::{Address, Log, U256},
Inspector,
};

/// A layer in a stack of inspectors. Contains its own inspector and an
/// inner inspector. This is used to create a stack of inspectors that can
/// be used to inspect the execution of a contract.
///
/// Use `Layered` when you need to retain type information about the inner
/// inspectors.
///
/// The current inspector will be invoked first, then the inner inspector.
/// For functions that may return values (e.g. [`Inspector::call`]), if the
/// current inspector returns a value, the inner inspector will not be invoked.
#[derive(Clone, Debug)]
pub struct Layered<Outer, Inner> {
outer: Outer,
inner: Inner,
}

impl<Outer, Inner> Layered<Outer, Inner> {
/// Create a new [`Layered`] inspector with the given current and inner
/// inspectors.
pub const fn new(outer: Outer, inner: Inner) -> Self {
Self { outer, inner }
}

/// Wrap this inspector in another, creating a new [`Layered`] inspector.
/// with this as the inner inspector.
pub const fn wrap_in<Other>(self, outer: Other) -> Layered<Other, Self> {
Layered { outer, inner: self }
}

/// Wrap this inspector around another, creating a new [`Layered`] inspector
/// with this as the outer inspector.
pub const fn wrap_around<Other>(self, inner: Other) -> Layered<Self, Other> {
Layered { outer: self, inner }
}

/// Get a reference to the current inspector.
pub const fn outer(&self) -> &Outer {
&self.outer
}

/// Get a mutable reference to the current inspector.
pub fn outer_mut(&mut self) -> &mut Outer {
&mut self.outer
}

/// Get a reference to the inner inspector.
pub const fn inner(&self) -> &Inner {
&self.inner
}

/// Get a mutable reference to the inner inspector.
pub fn inner_mut(&mut self) -> &mut Inner {
&mut self.inner
}
}

impl<Ctx, Int: InterpreterTypes, Outer, Inner> Inspector<Ctx, Int> for Layered<Outer, Inner>
where
Outer: Inspector<Ctx, Int>,
Inner: Inspector<Ctx, Int>,
{
fn initialize_interp(&mut self, interp: &mut Interpreter<Int>, context: &mut Ctx) {
self.outer.initialize_interp(interp, context);
self.inner.initialize_interp(interp, context);
}

fn step(&mut self, interp: &mut Interpreter<Int>, context: &mut Ctx) {
self.outer.step(interp, context);
self.inner.step(interp, context);
}

fn step_end(&mut self, interp: &mut Interpreter<Int>, context: &mut Ctx) {
self.outer.step_end(interp, context);
self.inner.step_end(interp, context);
}

fn log(&mut self, interp: &mut Interpreter<Int>, context: &mut Ctx, log: Log) {
self.outer.log(interp, context, log.clone());
self.inner.log(interp, context, log);
}

fn call(&mut self, context: &mut Ctx, inputs: &mut CallInputs) -> Option<CallOutcome> {
if let Some(outcome) = self.outer.call(context, inputs) {
return Some(outcome);
}
self.inner.call(context, inputs)
}

fn call_end(&mut self, context: &mut Ctx, inputs: &CallInputs, outcome: &mut CallOutcome) {
self.outer.call_end(context, inputs, outcome);
self.inner.call_end(context, inputs, outcome);
}

fn create(&mut self, context: &mut Ctx, inputs: &mut CreateInputs) -> Option<CreateOutcome> {
if let Some(outcome) = self.outer.create(context, inputs) {
return Some(outcome);
}
self.inner.create(context, inputs)
}

fn create_end(
&mut self,
context: &mut Ctx,
inputs: &CreateInputs,
outcome: &mut CreateOutcome,
) {
self.outer.create_end(context, inputs, outcome);
self.inner.create_end(context, inputs, outcome);
}

fn eofcreate(
&mut self,
context: &mut Ctx,
inputs: &mut EOFCreateInputs,
) -> Option<CreateOutcome> {
if let Some(outcome) = self.outer.eofcreate(context, inputs) {
return Some(outcome);
}
self.inner.eofcreate(context, inputs)
}

fn eofcreate_end(
&mut self,
context: &mut Ctx,
inputs: &EOFCreateInputs,
outcome: &mut CreateOutcome,
) {
self.outer.eofcreate_end(context, inputs, outcome);
self.inner.eofcreate_end(context, inputs, outcome);
}

fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
self.outer.selfdestruct(contract, target, value);
self.inner.selfdestruct(contract, target, value);
}
}
38 changes: 38 additions & 0 deletions src/inspectors/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
mod timeout;
pub use timeout::TimeLimit;

mod set;
pub use set::InspectorSet;

mod spanning;
pub use spanning::SpanningInspector;

mod layer;
pub use layer::Layered;

#[cfg(test)]
mod test {
use super::*;
use crate::{test_utils::TestInspector, NoopBlock, NoopCfg};
use revm::{database::InMemoryDB, inspector::InspectorEvmTr, primitives::B256};
use std::time::Duration;

#[test]
fn test() {
let inspector =
Layered::new(TimeLimit::new(Duration::from_micros(10)), SpanningInspector::at_info())
.wrap_around(TestInspector::default());

let mut trevm = crate::TrevmBuilder::new()
.with_db(InMemoryDB::default())
.with_insp(inspector)
.build_trevm()
.unwrap()
.fill_cfg(&NoopCfg)
.fill_block(&NoopBlock);

trevm.apply_eip4788(B256::repeat_byte(0xaa)).unwrap();

assert!(trevm.inner_mut_unchecked().inspector().outer().outer().has_elapsed());
}
}
154 changes: 154 additions & 0 deletions src/inspectors/set.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
use std::collections::VecDeque;

use revm::{
interpreter::{
CallInputs, CallOutcome, CreateInputs, CreateOutcome, EOFCreateInputs, Interpreter,
InterpreterTypes,
},
primitives::{Address, Log, U256},
Inspector,
};

/// A stack of [`Inspector`]s.
///
/// This is a thin wrapper around a [`VecDeque`] of inspectors.
#[derive(Default)]
pub struct InspectorSet<Ctx, Int> {
inspectors: VecDeque<Box<dyn Inspector<Ctx, Int>>>,
}

impl<Ctx, Int> core::ops::Deref for InspectorSet<Ctx, Int> {
type Target = VecDeque<Box<dyn Inspector<Ctx, Int>>>;

fn deref(&self) -> &Self::Target {
&self.inspectors
}
}

impl<Ctx, Int> core::ops::DerefMut for InspectorSet<Ctx, Int> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inspectors
}
}

impl<Ctx, Int> core::fmt::Debug for InspectorSet<Ctx, Int> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("InspectorStack").field("inspectors", &self.inspectors.len()).finish()
}
}

impl<Ctx, Int> InspectorSet<Ctx, Int>
where
Int: InterpreterTypes,
{
/// Instantiate a new empty inspector stack.
pub fn new() -> Self {
Self { inspectors: Default::default() }
}

/// Instantiate a new empty stack with pre-allocated capacity.
pub fn with_capacity(cap: usize) -> Self {
Self { inspectors: VecDeque::with_capacity(cap) }
}

/// Push an inspector to the back of the stack.
pub fn push_back<I: Inspector<Ctx, Int> + 'static>(&mut self, inspector: I) {
self.inspectors.push_back(Box::new(inspector));
}

/// Push an inspector to the front of the stack.
pub fn push_front<I: Inspector<Ctx, Int> + 'static>(&mut self, inspector: I) {
self.inspectors.push_front(Box::new(inspector));
}

/// Pop an inspector from the back of the stack.
pub fn pop_back(&mut self) -> Option<Box<dyn Inspector<Ctx, Int>>> {
self.inspectors.pop_back()
}

/// Pop an inspector from the front of the stack.
pub fn pop_front(&mut self) -> Option<Box<dyn Inspector<Ctx, Int>>> {
self.inspectors.pop_front()
}
}

impl<Ctx, Int> Inspector<Ctx, Int> for InspectorSet<Ctx, Int>
where
Int: InterpreterTypes,
{
fn initialize_interp(&mut self, interp: &mut Interpreter<Int>, context: &mut Ctx) {
self.inspectors.iter_mut().for_each(|i| i.initialize_interp(interp, context));
}

fn step(&mut self, interp: &mut Interpreter<Int>, context: &mut Ctx) {
self.inspectors.iter_mut().for_each(|i| i.step(interp, context));
}

fn step_end(&mut self, interp: &mut Interpreter<Int>, context: &mut Ctx) {
self.inspectors.iter_mut().for_each(|i| i.step_end(interp, context));
}

fn log(&mut self, interp: &mut Interpreter<Int>, context: &mut Ctx, log: Log) {
self.inspectors.iter_mut().for_each(|i| i.log(interp, context, log.clone()));
}

fn call(&mut self, context: &mut Ctx, inputs: &mut CallInputs) -> Option<CallOutcome> {
for inspector in self.inspectors.iter_mut() {
let outcome = inspector.call(context, inputs);
if outcome.is_some() {
return outcome;
}
}
None
}

fn call_end(&mut self, context: &mut Ctx, inputs: &CallInputs, outcome: &mut CallOutcome) {
self.inspectors.iter_mut().for_each(|i| i.call_end(context, inputs, outcome))
}

fn create(&mut self, context: &mut Ctx, inputs: &mut CreateInputs) -> Option<CreateOutcome> {
for inspector in self.inspectors.iter_mut() {
let outcome = inspector.create(context, inputs);
if outcome.is_some() {
return outcome;
}
}
None
}

fn create_end(
&mut self,
context: &mut Ctx,
inputs: &CreateInputs,
outcome: &mut CreateOutcome,
) {
self.inspectors.iter_mut().for_each(|i| i.create_end(context, inputs, outcome))
}

fn eofcreate(
&mut self,
context: &mut Ctx,
inputs: &mut EOFCreateInputs,
) -> Option<CreateOutcome> {
for inspector in self.inspectors.iter_mut() {
let outcome = inspector.eofcreate(context, inputs);
if outcome.is_some() {
return outcome;
}
}
None
}

fn eofcreate_end(
&mut self,
context: &mut Ctx,
inputs: &EOFCreateInputs,
outcome: &mut CreateOutcome,
) {
self.inspectors.iter_mut().for_each(|i| i.eofcreate_end(context, inputs, outcome))
}

fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
self.inspectors.iter_mut().for_each(|i| i.selfdestruct(contract, target, value))
}
}
Loading
Loading