Skip to content

Commit 293eaef

Browse files
authored
feature: utility inspectors (#98)
* feat: a couple utility inspectors * feat: inspector stack * lint: clippy * feat: stack vs layered * fix: allow instantiating layered * fix: trace syscalls and change fail values * lint: clippy * chore: better docs
1 parent 9a79e24 commit 293eaef

File tree

8 files changed

+845
-7
lines changed

8 files changed

+845
-7
lines changed

src/inspectors/layer.rs

+145
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
use revm::{
2+
interpreter::{
3+
CallInputs, CallOutcome, CreateInputs, CreateOutcome, EOFCreateInputs, Interpreter,
4+
InterpreterTypes,
5+
},
6+
primitives::{Address, Log, U256},
7+
Inspector,
8+
};
9+
10+
/// A layer in a stack of inspectors. Contains its own inspector and an
11+
/// inner inspector. This is used to create a stack of inspectors that can
12+
/// be used to inspect the execution of a contract.
13+
///
14+
/// Use `Layered` when you need to retain type information about the inner
15+
/// inspectors.
16+
///
17+
/// The current inspector will be invoked first, then the inner inspector.
18+
/// For functions that may return values (e.g. [`Inspector::call`]), if the
19+
/// current inspector returns a value, the inner inspector will not be invoked.
20+
#[derive(Clone, Debug)]
21+
pub struct Layered<Outer, Inner> {
22+
outer: Outer,
23+
inner: Inner,
24+
}
25+
26+
impl<Outer, Inner> Layered<Outer, Inner> {
27+
/// Create a new [`Layered`] inspector with the given current and inner
28+
/// inspectors.
29+
pub const fn new(outer: Outer, inner: Inner) -> Self {
30+
Self { outer, inner }
31+
}
32+
33+
/// Wrap this inspector in another, creating a new [`Layered`] inspector.
34+
/// with this as the inner inspector.
35+
pub const fn wrap_in<Other>(self, outer: Other) -> Layered<Other, Self> {
36+
Layered { outer, inner: self }
37+
}
38+
39+
/// Wrap this inspector around another, creating a new [`Layered`] inspector
40+
/// with this as the outer inspector.
41+
pub const fn wrap_around<Other>(self, inner: Other) -> Layered<Self, Other> {
42+
Layered { outer: self, inner }
43+
}
44+
45+
/// Get a reference to the current inspector.
46+
pub const fn outer(&self) -> &Outer {
47+
&self.outer
48+
}
49+
50+
/// Get a mutable reference to the current inspector.
51+
pub fn outer_mut(&mut self) -> &mut Outer {
52+
&mut self.outer
53+
}
54+
55+
/// Get a reference to the inner inspector.
56+
pub const fn inner(&self) -> &Inner {
57+
&self.inner
58+
}
59+
60+
/// Get a mutable reference to the inner inspector.
61+
pub fn inner_mut(&mut self) -> &mut Inner {
62+
&mut self.inner
63+
}
64+
}
65+
66+
impl<Ctx, Int: InterpreterTypes, Outer, Inner> Inspector<Ctx, Int> for Layered<Outer, Inner>
67+
where
68+
Outer: Inspector<Ctx, Int>,
69+
Inner: Inspector<Ctx, Int>,
70+
{
71+
fn initialize_interp(&mut self, interp: &mut Interpreter<Int>, context: &mut Ctx) {
72+
self.outer.initialize_interp(interp, context);
73+
self.inner.initialize_interp(interp, context);
74+
}
75+
76+
fn step(&mut self, interp: &mut Interpreter<Int>, context: &mut Ctx) {
77+
self.outer.step(interp, context);
78+
self.inner.step(interp, context);
79+
}
80+
81+
fn step_end(&mut self, interp: &mut Interpreter<Int>, context: &mut Ctx) {
82+
self.outer.step_end(interp, context);
83+
self.inner.step_end(interp, context);
84+
}
85+
86+
fn log(&mut self, interp: &mut Interpreter<Int>, context: &mut Ctx, log: Log) {
87+
self.outer.log(interp, context, log.clone());
88+
self.inner.log(interp, context, log);
89+
}
90+
91+
fn call(&mut self, context: &mut Ctx, inputs: &mut CallInputs) -> Option<CallOutcome> {
92+
if let Some(outcome) = self.outer.call(context, inputs) {
93+
return Some(outcome);
94+
}
95+
self.inner.call(context, inputs)
96+
}
97+
98+
fn call_end(&mut self, context: &mut Ctx, inputs: &CallInputs, outcome: &mut CallOutcome) {
99+
self.outer.call_end(context, inputs, outcome);
100+
self.inner.call_end(context, inputs, outcome);
101+
}
102+
103+
fn create(&mut self, context: &mut Ctx, inputs: &mut CreateInputs) -> Option<CreateOutcome> {
104+
if let Some(outcome) = self.outer.create(context, inputs) {
105+
return Some(outcome);
106+
}
107+
self.inner.create(context, inputs)
108+
}
109+
110+
fn create_end(
111+
&mut self,
112+
context: &mut Ctx,
113+
inputs: &CreateInputs,
114+
outcome: &mut CreateOutcome,
115+
) {
116+
self.outer.create_end(context, inputs, outcome);
117+
self.inner.create_end(context, inputs, outcome);
118+
}
119+
120+
fn eofcreate(
121+
&mut self,
122+
context: &mut Ctx,
123+
inputs: &mut EOFCreateInputs,
124+
) -> Option<CreateOutcome> {
125+
if let Some(outcome) = self.outer.eofcreate(context, inputs) {
126+
return Some(outcome);
127+
}
128+
self.inner.eofcreate(context, inputs)
129+
}
130+
131+
fn eofcreate_end(
132+
&mut self,
133+
context: &mut Ctx,
134+
inputs: &EOFCreateInputs,
135+
outcome: &mut CreateOutcome,
136+
) {
137+
self.outer.eofcreate_end(context, inputs, outcome);
138+
self.inner.eofcreate_end(context, inputs, outcome);
139+
}
140+
141+
fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
142+
self.outer.selfdestruct(contract, target, value);
143+
self.inner.selfdestruct(contract, target, value);
144+
}
145+
}

src/inspectors/mod.rs

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
mod timeout;
2+
pub use timeout::TimeLimit;
3+
4+
mod set;
5+
pub use set::InspectorSet;
6+
7+
mod spanning;
8+
pub use spanning::SpanningInspector;
9+
10+
mod layer;
11+
pub use layer::Layered;
12+
13+
#[cfg(test)]
14+
mod test {
15+
use super::*;
16+
use crate::{test_utils::TestInspector, NoopBlock, NoopCfg};
17+
use revm::{database::InMemoryDB, inspector::InspectorEvmTr, primitives::B256};
18+
use std::time::Duration;
19+
20+
#[test]
21+
fn test() {
22+
let inspector =
23+
Layered::new(TimeLimit::new(Duration::from_micros(10)), SpanningInspector::at_info())
24+
.wrap_around(TestInspector::default());
25+
26+
let mut trevm = crate::TrevmBuilder::new()
27+
.with_db(InMemoryDB::default())
28+
.with_insp(inspector)
29+
.build_trevm()
30+
.unwrap()
31+
.fill_cfg(&NoopCfg)
32+
.fill_block(&NoopBlock);
33+
34+
trevm.apply_eip4788(B256::repeat_byte(0xaa)).unwrap();
35+
36+
assert!(trevm.inner_mut_unchecked().inspector().outer().outer().has_elapsed());
37+
}
38+
}

src/inspectors/set.rs

+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
use std::collections::VecDeque;
2+
3+
use revm::{
4+
interpreter::{
5+
CallInputs, CallOutcome, CreateInputs, CreateOutcome, EOFCreateInputs, Interpreter,
6+
InterpreterTypes,
7+
},
8+
primitives::{Address, Log, U256},
9+
Inspector,
10+
};
11+
12+
/// A stack of [`Inspector`]s.
13+
///
14+
/// This is a thin wrapper around a [`VecDeque`] of inspectors.
15+
#[derive(Default)]
16+
pub struct InspectorSet<Ctx, Int> {
17+
inspectors: VecDeque<Box<dyn Inspector<Ctx, Int>>>,
18+
}
19+
20+
impl<Ctx, Int> core::ops::Deref for InspectorSet<Ctx, Int> {
21+
type Target = VecDeque<Box<dyn Inspector<Ctx, Int>>>;
22+
23+
fn deref(&self) -> &Self::Target {
24+
&self.inspectors
25+
}
26+
}
27+
28+
impl<Ctx, Int> core::ops::DerefMut for InspectorSet<Ctx, Int> {
29+
fn deref_mut(&mut self) -> &mut Self::Target {
30+
&mut self.inspectors
31+
}
32+
}
33+
34+
impl<Ctx, Int> core::fmt::Debug for InspectorSet<Ctx, Int> {
35+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36+
f.debug_struct("InspectorStack").field("inspectors", &self.inspectors.len()).finish()
37+
}
38+
}
39+
40+
impl<Ctx, Int> InspectorSet<Ctx, Int>
41+
where
42+
Int: InterpreterTypes,
43+
{
44+
/// Instantiate a new empty inspector stack.
45+
pub fn new() -> Self {
46+
Self { inspectors: Default::default() }
47+
}
48+
49+
/// Instantiate a new empty stack with pre-allocated capacity.
50+
pub fn with_capacity(cap: usize) -> Self {
51+
Self { inspectors: VecDeque::with_capacity(cap) }
52+
}
53+
54+
/// Push an inspector to the back of the stack.
55+
pub fn push_back<I: Inspector<Ctx, Int> + 'static>(&mut self, inspector: I) {
56+
self.inspectors.push_back(Box::new(inspector));
57+
}
58+
59+
/// Push an inspector to the front of the stack.
60+
pub fn push_front<I: Inspector<Ctx, Int> + 'static>(&mut self, inspector: I) {
61+
self.inspectors.push_front(Box::new(inspector));
62+
}
63+
64+
/// Pop an inspector from the back of the stack.
65+
pub fn pop_back(&mut self) -> Option<Box<dyn Inspector<Ctx, Int>>> {
66+
self.inspectors.pop_back()
67+
}
68+
69+
/// Pop an inspector from the front of the stack.
70+
pub fn pop_front(&mut self) -> Option<Box<dyn Inspector<Ctx, Int>>> {
71+
self.inspectors.pop_front()
72+
}
73+
}
74+
75+
impl<Ctx, Int> Inspector<Ctx, Int> for InspectorSet<Ctx, Int>
76+
where
77+
Int: InterpreterTypes,
78+
{
79+
fn initialize_interp(&mut self, interp: &mut Interpreter<Int>, context: &mut Ctx) {
80+
self.inspectors.iter_mut().for_each(|i| i.initialize_interp(interp, context));
81+
}
82+
83+
fn step(&mut self, interp: &mut Interpreter<Int>, context: &mut Ctx) {
84+
self.inspectors.iter_mut().for_each(|i| i.step(interp, context));
85+
}
86+
87+
fn step_end(&mut self, interp: &mut Interpreter<Int>, context: &mut Ctx) {
88+
self.inspectors.iter_mut().for_each(|i| i.step_end(interp, context));
89+
}
90+
91+
fn log(&mut self, interp: &mut Interpreter<Int>, context: &mut Ctx, log: Log) {
92+
self.inspectors.iter_mut().for_each(|i| i.log(interp, context, log.clone()));
93+
}
94+
95+
fn call(&mut self, context: &mut Ctx, inputs: &mut CallInputs) -> Option<CallOutcome> {
96+
for inspector in self.inspectors.iter_mut() {
97+
let outcome = inspector.call(context, inputs);
98+
if outcome.is_some() {
99+
return outcome;
100+
}
101+
}
102+
None
103+
}
104+
105+
fn call_end(&mut self, context: &mut Ctx, inputs: &CallInputs, outcome: &mut CallOutcome) {
106+
self.inspectors.iter_mut().for_each(|i| i.call_end(context, inputs, outcome))
107+
}
108+
109+
fn create(&mut self, context: &mut Ctx, inputs: &mut CreateInputs) -> Option<CreateOutcome> {
110+
for inspector in self.inspectors.iter_mut() {
111+
let outcome = inspector.create(context, inputs);
112+
if outcome.is_some() {
113+
return outcome;
114+
}
115+
}
116+
None
117+
}
118+
119+
fn create_end(
120+
&mut self,
121+
context: &mut Ctx,
122+
inputs: &CreateInputs,
123+
outcome: &mut CreateOutcome,
124+
) {
125+
self.inspectors.iter_mut().for_each(|i| i.create_end(context, inputs, outcome))
126+
}
127+
128+
fn eofcreate(
129+
&mut self,
130+
context: &mut Ctx,
131+
inputs: &mut EOFCreateInputs,
132+
) -> Option<CreateOutcome> {
133+
for inspector in self.inspectors.iter_mut() {
134+
let outcome = inspector.eofcreate(context, inputs);
135+
if outcome.is_some() {
136+
return outcome;
137+
}
138+
}
139+
None
140+
}
141+
142+
fn eofcreate_end(
143+
&mut self,
144+
context: &mut Ctx,
145+
inputs: &EOFCreateInputs,
146+
outcome: &mut CreateOutcome,
147+
) {
148+
self.inspectors.iter_mut().for_each(|i| i.eofcreate_end(context, inputs, outcome))
149+
}
150+
151+
fn selfdestruct(&mut self, contract: Address, target: Address, value: U256) {
152+
self.inspectors.iter_mut().for_each(|i| i.selfdestruct(contract, target, value))
153+
}
154+
}

0 commit comments

Comments
 (0)