diff --git a/Cargo.lock b/Cargo.lock index 410c2ae71c..6edd9e6059 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2313,6 +2313,15 @@ dependencies = [ "web-time", ] +[[package]] +name = "inspector_with_lifetime" +version = "0.0.0" +dependencies = [ + "alloy-primitives", + "anyhow", + "revm", +] + [[package]] name = "ipnet" version = "2.11.0" @@ -3539,6 +3548,7 @@ name = "revm-inspector" version = "1.0.0-alpha.5" dependencies = [ "auto_impl", + "derive-where", "revm-context", "revm-database", "revm-database-interface", diff --git a/Cargo.toml b/Cargo.toml index 2c64f201b5..da736c1c0e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ members = [ "examples/database_components", "examples/uniswap_get_reserves", "examples/uniswap_v2_usdc_swap", - "examples/erc20_gas", + "examples/erc20_gas", "examples/inspector_with_lifetime", #"examples/custom_opcodes", ] resolver = "2" diff --git a/crates/inspector/Cargo.toml b/crates/inspector/Cargo.toml index b461888638..f00f315af3 100644 --- a/crates/inspector/Cargo.toml +++ b/crates/inspector/Cargo.toml @@ -32,6 +32,7 @@ state.workspace = true interpreter.workspace = true auto_impl.workspace = true +derive-where.workspace = true # Optional serde = { workspace = true, features = ["derive", "rc"], optional = true } diff --git a/crates/inspector/src/eip3155.rs b/crates/inspector/src/eip3155.rs index 61a713e58f..7b83997dd4 100644 --- a/crates/inspector/src/eip3155.rs +++ b/crates/inspector/src/eip3155.rs @@ -1,6 +1,7 @@ use crate::inspectors::GasInspector; use crate::Inspector; use context::{Cfg, ContextTr, JournalTr, Transaction}; +use core::marker::PhantomData; use interpreter::{ interpreter_types::{Jumps, LoopControl, MemoryTr, RuntimeFlag, StackTr, SubRoutineStack}, CallInputs, CallOutcome, CreateInputs, CreateOutcome, Interpreter, InterpreterResult, @@ -12,7 +13,7 @@ use state::bytecode::opcode::OpCode; use std::io::Write; /// [EIP-3155](https://eips.ethereum.org/EIPS/eip-3155) tracer [Inspector]. -pub struct TracerEip3155 { +pub struct TracerEip3155 { output: Box, gas_inspector: GasInspector, /// Print summary of the execution. @@ -28,6 +29,7 @@ pub struct TracerEip3155 { skip: bool, include_memory: bool, memory: Option, + phantom: PhantomData, } // # Output @@ -107,7 +109,7 @@ struct Summary { fork: Option, } -impl TracerEip3155 { +impl TracerEip3155 { /// Creates a new EIP-3155 tracer with the given output writer, by first wrapping it in a /// [`BufWriter`](std::io::BufWriter). pub fn buffered(output: impl Write + 'static) -> Self { @@ -131,6 +133,7 @@ impl TracerEip3155 { refunded: 0, mem_size: 0, skip: false, + phantom: PhantomData, } } @@ -208,11 +211,13 @@ impl CloneStack for Stack { } } -impl Inspector for TracerEip3155 +impl Inspector for TracerEip3155 where CTX: ContextTr, INTR: InterpreterTypes, { + type Context<'context> = CTX; + fn initialize_interp(&mut self, interp: &mut Interpreter, _: &mut CTX) { self.gas_inspector.initialize_interp(interp.control.gas()); } diff --git a/crates/inspector/src/gas.rs b/crates/inspector/src/gas.rs index 27793e26d3..a0df107b3d 100644 --- a/crates/inspector/src/gas.rs +++ b/crates/inspector/src/gas.rs @@ -67,10 +67,13 @@ impl GasInspector { #[cfg(test)] mod tests { + use core::marker::PhantomData; + use super::*; use crate::{InspectEvm, Inspector}; use context::Context; use database::{BenchmarkDB, BENCH_CALLER, BENCH_TARGET}; + use derive_where::derive_where; use handler::{MainBuilder, MainContext}; use interpreter::{ interpreter_types::{Jumps, LoopControl}, @@ -79,14 +82,17 @@ mod tests { use primitives::{Bytes, TxKind}; use state::bytecode::{opcode, Bytecode}; - #[derive(Default, Debug)] - struct StackInspector { + #[derive_where(Default, Debug)] + struct StackInspector { pc: usize, gas_inspector: GasInspector, gas_remaining_steps: Vec<(usize, u64)>, + phantom: PhantomData, } - impl Inspector for StackInspector { + impl Inspector for StackInspector { + type Context<'context> = CTX; + fn initialize_interp(&mut self, interp: &mut Interpreter, _context: &mut CTX) { self.gas_inspector.initialize_interp(interp.control.gas()); } diff --git a/crates/inspector/src/inspector.rs b/crates/inspector/src/inspector.rs index bfd3326aa2..341a5a0f90 100644 --- a/crates/inspector/src/inspector.rs +++ b/crates/inspector/src/inspector.rs @@ -15,13 +15,19 @@ use std::{vec, vec::Vec}; /// EVM hooks into execution. #[auto_impl(&mut, Box)] -pub trait Inspector { +pub trait Inspector { + type Context<'context>; + /// Called before the interpreter is initialized. /// /// If `interp.instruction_result` is set to anything other than [InstructionResult::Continue] then the execution of the interpreter /// is skipped. #[inline] - fn initialize_interp(&mut self, interp: &mut Interpreter, context: &mut CTX) { + fn initialize_interp( + &mut self, + interp: &mut Interpreter, + context: &mut Self::Context<'_>, + ) { let _ = interp; let _ = context; } @@ -35,7 +41,7 @@ pub trait Inspector { /// /// To get the current opcode, use `interp.current_opcode()`. #[inline] - fn step(&mut self, interp: &mut Interpreter, context: &mut CTX) { + fn step(&mut self, interp: &mut Interpreter, context: &mut Self::Context<'_>) { let _ = interp; let _ = context; } @@ -45,14 +51,14 @@ pub trait Inspector { /// Setting `interp.instruction_result` to anything other than [InstructionResult::Continue] alters the execution /// of the interpreter. #[inline] - fn step_end(&mut self, interp: &mut Interpreter, context: &mut CTX) { + fn step_end(&mut self, interp: &mut Interpreter, context: &mut Self::Context<'_>) { let _ = interp; let _ = context; } /// Called when a log is emitted. #[inline] - fn log(&mut self, interp: &mut Interpreter, context: &mut CTX, log: Log) { + fn log(&mut self, interp: &mut Interpreter, context: &mut Self::Context<'_>, log: Log) { let _ = interp; let _ = context; let _ = log; @@ -62,7 +68,11 @@ pub trait Inspector { /// /// InstructionResulting anything other than [InstructionResult::Continue] overrides the result of the call. #[inline] - fn call(&mut self, context: &mut CTX, inputs: &mut CallInputs) -> Option { + fn call( + &mut self, + context: &mut Self::Context<'_>, + inputs: &mut CallInputs, + ) -> Option { let _ = context; let _ = inputs; None @@ -74,7 +84,12 @@ pub trait Inspector { /// /// This allows the inspector to modify the given `result` before returning it. #[inline] - fn call_end(&mut self, context: &mut CTX, inputs: &CallInputs, outcome: &mut CallOutcome) { + fn call_end( + &mut self, + context: &mut Self::Context<'_>, + inputs: &CallInputs, + outcome: &mut CallOutcome, + ) { let _ = context; let _ = inputs; let _ = outcome; @@ -86,7 +101,11 @@ pub trait Inspector { /// /// If this returns `None` then the creation proceeds as normal. #[inline] - fn create(&mut self, context: &mut CTX, inputs: &mut CreateInputs) -> Option { + fn create( + &mut self, + context: &mut Self::Context<'_>, + inputs: &mut CreateInputs, + ) -> Option { let _ = context; let _ = inputs; None @@ -99,7 +118,7 @@ pub trait Inspector { #[inline] fn create_end( &mut self, - context: &mut CTX, + context: &mut Self::Context<'_>, inputs: &CreateInputs, outcome: &mut CreateOutcome, ) { @@ -113,7 +132,7 @@ pub trait Inspector { /// This can happen from create TX or from EOFCREATE opcode. fn eofcreate( &mut self, - context: &mut CTX, + context: &mut Self::Context<'_>, inputs: &mut EOFCreateInputs, ) -> Option { let _ = context; @@ -124,7 +143,7 @@ pub trait Inspector { /// Called when eof creating has ended. fn eofcreate_end( &mut self, - context: &mut CTX, + context: &mut Self::Context<'_>, inputs: &EOFCreateInputs, outcome: &mut CreateOutcome, ) { @@ -177,8 +196,12 @@ impl JournalExt for Journal { pub trait InspectorHandler: Handler where - Self::Evm: - InspectorEvmTr::Evm as EvmTr>::Context, Self::IT>>, + Self::Evm: InspectorEvmTr< + Inspector: for<'context> Inspector< + Self::IT, + Context<'context> = <::Evm as EvmTr>::Context, + >, + >, Self::Frame: InspectorFrame, { type IT: InterpreterTypes; @@ -307,9 +330,9 @@ where } } -fn frame_start( - context: &mut CTX, - inspector: &mut impl Inspector, +fn frame_start<'context, CTX, INTR: InterpreterTypes>( + context: &'context mut CTX, + inspector: &mut impl Inspector = CTX>, frame_input: &mut FrameInput, ) -> Option { match frame_input { @@ -332,9 +355,9 @@ fn frame_start( None } -fn frame_end( - context: &mut CTX, - inspector: &mut impl Inspector, +fn frame_end<'context, CTX, INTR: InterpreterTypes>( + context: &'context mut CTX, + inspector: &mut impl Inspector = CTX>, frame_input: &FrameInput, frame_output: &mut FrameResult, ) { @@ -360,10 +383,10 @@ fn frame_end( } } -pub fn inspect_instructions( - context: &mut CTX, +pub fn inspect_instructions<'context, CTX, IT>( + context: &'context mut CTX, interpreter: &mut Interpreter, - mut inspector: impl Inspector, + mut inspector: impl Inspector = CTX>, instructions: &InstructionTable, ) -> InterpreterAction where diff --git a/crates/inspector/src/mainnet_inspect.rs b/crates/inspector/src/mainnet_inspect.rs index 0b866389fc..fadd6e3a5b 100644 --- a/crates/inspector/src/mainnet_inspect.rs +++ b/crates/inspector/src/mainnet_inspect.rs @@ -15,7 +15,10 @@ impl InspectorHandler for MainnetHandler where EVM: InspectorEvmTr< Context: ContextTr>, - Inspector: Inspector<<::Evm as EvmTr>::Context, EthInterpreter>, + Inspector: for<'context> Inspector< + EthInterpreter, + Context<'context> = <::Evm as EvmTr>::Context, + >, >, ERROR: EvmTrError, FRAME: Frame @@ -28,7 +31,7 @@ impl InspectEvm for Evm, PRECOMPILES> where CTX: ContextSetters + ContextTr + JournalExt>, - INSP: Inspector, + INSP: for<'context> Inspector = CTX>, PRECOMPILES: PrecompileProvider, { type Inspector = INSP; @@ -51,7 +54,7 @@ impl InspectCommitEvm where CTX: ContextSetters + ContextTr + JournalExt, Db: DatabaseCommit>, - INSP: Inspector, + INSP: for<'context> Inspector = CTX>, PRECOMPILES: PrecompileProvider, { fn inspect_commit_previous(&mut self) -> Self::CommitOutput { diff --git a/crates/inspector/src/noop.rs b/crates/inspector/src/noop.rs index 776dbe99f6..1b0b931162 100644 --- a/crates/inspector/src/noop.rs +++ b/crates/inspector/src/noop.rs @@ -1,8 +1,15 @@ +use core::marker::PhantomData; + use crate::inspector::Inspector; +use derive_where::derive_where; use interpreter::InterpreterTypes; /// Dummy [Inspector], helpful as standalone replacement. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct NoOpInspector {} +#[derive_where(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct NoOpInspector { + phantom: PhantomData, +} -impl Inspector for NoOpInspector {} +impl Inspector for NoOpInspector { + type Context<'context> = CTX; +} diff --git a/crates/inspector/src/traits.rs b/crates/inspector/src/traits.rs index d3cd6afc6c..81e666c185 100644 --- a/crates/inspector/src/traits.rs +++ b/crates/inspector/src/traits.rs @@ -33,7 +33,7 @@ where Context = CTX, InterpreterTypes: InterpreterTypes, >, - INSP: Inspector, + INSP: for<'context> Inspector = CTX>, { type Inspector = INSP; diff --git a/crates/optimism/src/api/default_ctx.rs b/crates/optimism/src/api/default_ctx.rs index 17fa5de765..9ff8bf8af6 100644 --- a/crates/optimism/src/api/default_ctx.rs +++ b/crates/optimism/src/api/default_ctx.rs @@ -37,7 +37,7 @@ mod test { fn default_run_op() { let ctx = Context::op(); // convert to optimism context - let mut evm = ctx.build_op_with_inspector(NoOpInspector {}); + let mut evm = ctx.build_op_with_inspector(NoOpInspector::default()); // execute let _ = evm.replay(); // inspect diff --git a/crates/optimism/src/api/exec.rs b/crates/optimism/src/api/exec.rs index 4471d1f256..93d34c416f 100644 --- a/crates/optimism/src/api/exec.rs +++ b/crates/optimism/src/api/exec.rs @@ -84,7 +84,7 @@ impl InspectEvm for OpEvm, PRECOMPILE> where CTX: OpContextTr + ContextSetters, - INSP: Inspector, + INSP: for<'context> Inspector = CTX>, PRECOMPILE: PrecompileProvider, { type Inspector = INSP; @@ -103,7 +103,7 @@ impl InspectCommitEvm for OpEvm, PRECOMPILE> where CTX: OpContextTr + ContextSetters, - INSP: Inspector, + INSP: for<'context> Inspector = CTX>, PRECOMPILE: PrecompileProvider, { fn inspect_commit_previous(&mut self) -> Self::CommitOutput { diff --git a/crates/optimism/src/evm.rs b/crates/optimism/src/evm.rs index dc61340cac..88995b3308 100644 --- a/crates/optimism/src/evm.rs +++ b/crates/optimism/src/evm.rs @@ -34,7 +34,7 @@ where Context = CTX, InterpreterTypes: InterpreterTypes, >, - INSP: Inspector, + INSP: for<'context> Inspector = CTX>, { type Inspector = INSP; diff --git a/crates/optimism/src/handler.rs b/crates/optimism/src/handler.rs index beddf5de54..7d846aef1d 100644 --- a/crates/optimism/src/handler.rs +++ b/crates/optimism/src/handler.rs @@ -452,7 +452,10 @@ impl InspectorHandler for OpHandler where EVM: InspectorEvmTr< Context: OpContextTr, - Inspector: Inspector<<::Evm as EvmTr>::Context, EthInterpreter>, + Inspector: for<'context> Inspector< + EthInterpreter, + Context<'context> = <::Evm as EvmTr>::Context, + >, >, ERROR: EvmTrError + From + FromStringError + IsTxError, // TODO `FrameResult` should be a generic trait. diff --git a/examples/inspector_with_lifetime/Cargo.toml b/examples/inspector_with_lifetime/Cargo.toml new file mode 100644 index 0000000000..941d534010 --- /dev/null +++ b/examples/inspector_with_lifetime/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "inspector_with_lifetime" +version = "0.0.0" +publish = false +authors.workspace = true +edition.workspace = true +keywords.workspace = true +license.workspace = true +repository.workspace = true +readme.workspace = true + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] + +[lints.rust] +unreachable_pub = "warn" +unused_must_use = "deny" +rust_2018_idioms = "deny" + +[lints.rustdoc] +all = "warn" + +[dependencies] +# revms +revm = { workspace = true, features = ["std"] } + +# alloy +alloy-primitives = { workspace = true, features = ["rand"] } + +# misc +anyhow = "1.0.89" + diff --git a/examples/inspector_with_lifetime/src/main.rs b/examples/inspector_with_lifetime/src/main.rs new file mode 100644 index 0000000000..5fa1c6adef --- /dev/null +++ b/examples/inspector_with_lifetime/src/main.rs @@ -0,0 +1,83 @@ +use std::convert::Infallible; + +use revm::{ + context::{ + result::{EVMError, ExecutionResult}, + BlockEnv, CfgEnv, TxEnv, + }, + database::State, + inspector::NoOpInspector, + primitives::B256, + Database, DatabaseCommit, ExecuteCommitEvm, Inspector, Journal, JournalEntry, MainBuilder as _, +}; + +pub type ContextRef<'db> = revm::Context< + BlockEnv, + TxEnv, + CfgEnv, + &'db mut dyn Database, + Journal<&'db mut dyn Database>, +>; + +pub trait DBSuperTrait: Database + DatabaseCommit {} + +impl + DatabaseCommit> DBSuperTrait for T {} + +fn mine_block( + cfg: &CfgEnv, + db: &mut dyn DBSuperTrait, + transactions: Vec, + inspector: &mut InspectorT, +) -> Result, EVMError> +where + InspectorT: for<'context> Inspector = ContextRef<'context>>, +{ + let block = BlockEnv { + prevrandao: Some(B256::random()), + ..BlockEnv::default() + }; + + let mut results = Vec::new(); + for tx in transactions { + let result = run_transaction(&block, cfg, db, tx, inspector)?; + + results.push(result); + } + + Ok(results) +} + +fn run_transaction( + block: &BlockEnv, + cfg: &CfgEnv, + db: &mut dyn DBSuperTrait, + tx: TxEnv, + inspector: &mut InspectorT, +) -> Result> +where + InspectorT: for<'context> Inspector = ContextRef<'context>>, +{ + let context = revm::Context { + block: block.clone(), + tx, + cfg: cfg.clone(), + journaled_state: Journal::<_, JournalEntry>::new(cfg.spec, db), + chain: (), + error: Ok(()), + }; + + let mut evm = context.build_mainnet_with_inspector(inspector); + evm.replay_commit() +} + +fn main() -> anyhow::Result<()> { + let cfg = CfgEnv::default(); + let mut db = State::builder().build(); + let transactions = vec![]; + + let mut inspector = NoOpInspector::default(); + let results = mine_block(&cfg, &mut db, transactions, &mut inspector)?; + println!("results: {results:?}"); + + Ok(()) +}