Skip to content

migrate: anvil to revm 21 #10361

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from
Apr 29, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
9f7b1f2
downgrade op-revm to 2.0.0 to resolve dep conflict
yash-atreya Apr 24, 2025
84d11f1
add `as_mut_dyn` to trait `MaybeFullDatabase` as we now require mut d…
yash-atreya Apr 24, 2025
6990086
Revert "add `as_mut_dyn` to trait `MaybeFullDatabase` as we now requi…
yash-atreya Apr 24, 2025
29600d7
fix: Inspector should be generic over CTX not DB
yash-atreya Apr 24, 2025
6a5d57b
fix: pass TxEnv to evm.transact
yash-atreya Apr 24, 2025
e4cc032
fix: inspector inference in TransactionExecutor and build_access_list…
yash-atreya Apr 24, 2025
213593b
workaround: dup LogCollector to use with AnvilEvmContext
yash-atreya Apr 24, 2025
fdb9239
fix tests
yash-atreya Apr 24, 2025
78e2f76
fix traces test
yash-atreya Apr 24, 2025
b7b9ddd
fix: use default kzg settings in blob validation
yash-atreya Apr 24, 2025
7fced07
reintroduce OptimismHardfork
yash-atreya Apr 24, 2025
af4e00a
fix: disable nonce check if nonce is None
yash-atreya Apr 24, 2025
f1cdd41
fix!: load state tests by addressing breaking changes in state files
yash-atreya Apr 24, 2025
673696a
fix: access_list test by using evm.inspect_with_tx
yash-atreya Apr 25, 2025
4d9b1bc
fix: replace evm.transact with evm.inspect_with_tx
yash-atreya Apr 25, 2025
298c535
fix: make impl Inspector for AnvilInspector generic over CTX
yash-atreya Apr 25, 2025
1abfdcf
fix: clone inspector in TransactionExecutor to enable evm.inspect_commit
yash-atreya Apr 25, 2025
0efb119
Merge branch 'zerosnacks/revm-bump-2' into yash/anvil-revm-21
yash-atreya Apr 25, 2025
4730007
fix: remove cloned inspector from TransactionExecutor
yash-atreya Apr 25, 2025
1afa5a2
feat(`anvil`): op support revm 21 (#10407)
yash-atreya Apr 29, 2025
57a0fec
docs EitherEvm
yash-atreya Apr 29, 2025
42ccf70
nit
yash-atreya Apr 29, 2025
f8ef782
refac: return TxEnv and Deposit parts separately
yash-atreya Apr 29, 2025
d02f5b8
nits
yash-atreya Apr 29, 2025
d1c2316
nit
yash-atreya Apr 29, 2025
0b8f251
make anvil result aliases more generic
yash-atreya Apr 29, 2025
7e46fad
nit
yash-atreya Apr 29, 2025
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
2 changes: 1 addition & 1 deletion crates/anvil/src/eth/backend/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
error::InvalidTransactionError,
pool::transactions::PoolTransaction,
},
inject_precompiles,
// inject_precompiles,
mem::inspector::AnvilInspector,
PrecompileFactory,
};
Expand Down
63 changes: 60 additions & 3 deletions crates/anvil/src/eth/backend/mem/inspector.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
//! Anvil specific [`revm::Inspector`] implementation

use crate::foundry_common::ErrorExt;
use alloy_primitives::{Address, Log, U256};
use alloy_sol_types::SolInterface;
use foundry_evm::{
backend::DatabaseError,
call_inspectors,
constants::HARDHAT_CONSOLE_ADDRESS,
decode::decode_console_logs,
inspectors::{LogCollector, TracingInspector},
inspectors::{hh_to_ds, TracingInspector},
traces::{
render_trace_arena_inner, CallTraceDecoder, SparsedTraceArena, TracingInspectorConfig,
},
};
use foundry_evm_core::abi::console;
use revm::{
interpreter::{
interpreter::EthInterpreter, CallInputs, CallOutcome, CreateInputs, CreateOutcome,
EOFCreateInputs, Interpreter,
EOFCreateInputs, Gas, InstructionResult, Interpreter, InterpreterResult,
},
Database, Inspector,
};
Expand Down Expand Up @@ -130,7 +134,8 @@ where

fn log(&mut self, interp: &mut Interpreter, ecx: &mut AnvilEvmContext<'_, D>, log: Log) {
call_inspectors!([&mut self.tracer, &mut self.log_collector], |inspector| {
inspector.log(interp, ecx, log);
// TODO: rm the log.clone
inspector.log(interp, ecx, log.clone());
});
}

Expand Down Expand Up @@ -220,3 +225,55 @@ pub fn print_logs(logs: &[Log]) {
tracing::info!(target: crate::logging::EVM_CONSOLE_LOG_TARGET, "{}", log);
}
}

// DUPLICATION foundry_evm
// Duplicated workaround due to the `FoundryEvmContext` database being hardcoded to `dyn
// DatabaseExt` instead of being generic.
/// An inspector that collects logs during execution.
///
/// The inspector collects logs from the `LOG` opcodes as well as Hardhat-style `console.sol` logs.
#[derive(Clone, Debug, Default)]
pub struct LogCollector {
/// The collected logs. Includes both `LOG` opcodes and Hardhat-style `console.sol` logs.
pub logs: Vec<Log>,
}

impl LogCollector {
#[cold]
fn do_hardhat_log(&mut self, inputs: &CallInputs) -> Option<CallOutcome> {
if let Err(err) = self.hardhat_log(&inputs.input) {
let result = InstructionResult::Revert;
let output = err.abi_encode_revert();
return Some(CallOutcome {
result: InterpreterResult { result, output, gas: Gas::new(inputs.gas_limit) },
memory_offset: inputs.return_memory_offset.clone(),
})
}
None
}

fn hardhat_log(&mut self, data: &[u8]) -> alloy_sol_types::Result<()> {
let decoded = console::hh::ConsoleCalls::abi_decode(data, false)?;
self.logs.push(hh_to_ds(&decoded));
Ok(())
}
}

impl<DB: Database<Error = DatabaseError>> Inspector<AnvilEvmContext<'_, DB>, EthInterpreter>
for LogCollector
{
fn log(&mut self, _interp: &mut Interpreter, _context: &mut AnvilEvmContext<'_, DB>, log: Log) {
self.logs.push(log.clone());
}

fn call(
&mut self,
_context: &mut AnvilEvmContext<'_, DB>,
inputs: &mut CallInputs,
) -> Option<CallOutcome> {
if inputs.target_address == HARDHAT_CONSOLE_ADDRESS {
return self.do_hardhat_log(inputs);
}
None
}
}
6 changes: 4 additions & 2 deletions crates/anvil/src/eth/backend/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@ use crate::{
sign::build_typed_transaction,
util::get_precompiles_for,
},
inject_precompiles,
// inject_precompiles,
mem::{
inspector::AnvilInspector,
storage::{BlockchainStorage, InMemoryBlockStates, MinedBlockOutcome},
},
ForkChoice, NodeConfig, PrecompileFactory,
ForkChoice,
NodeConfig,
PrecompileFactory,
};
use alloy_chains::NamedChain;
use alloy_consensus::{
Expand Down
130 changes: 65 additions & 65 deletions crates/anvil/src/evm.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{fmt::Debug, sync::Arc};
use std::fmt::Debug;

use alloy_primitives::Address;
use revm::precompile::Precompiles;
Expand All @@ -10,75 +10,75 @@ pub trait PrecompileFactory: Send + Sync + Unpin + Debug {
fn precompiles(&self) -> Vec<(Address, Precompiles)>;
}

/// Appends a handler register to `evm` that injects the given `precompiles`.
///
/// This will add an additional handler that extends the default precompiles with the given set of
/// precompiles.
pub fn inject_precompiles<DB: revm::Database, I>(
evm: &mut revm::Evm<'_, I, DB>,
precompiles: Precompiles,
) {
evm.handler.append_handler_register_box(Box::new(move |handler| {
let precompiles = precompiles.clone();
let prev = handler.pre_execution.load_precompiles.clone();
handler.pre_execution.load_precompiles = Arc::new(move || {
let mut cx = prev();
cx.extend(precompiles.iter().cloned().map(|(a, b)| (a, b.into())));
cx
});
}));
}
// /// Appends a handler register to `evm` that injects the given `precompiles`.
// ///
// /// This will add an additional handler that extends the default precompiles with the given set
// of /// precompiles.
// pub fn inject_precompiles<DB: revm::Database, I>(
// evm: &mut revm::Evm<'_, I, DB>,
// precompiles: Precompiles,
// ) {
// evm.handler.append_handler_register_box(Box::new(move |handler| {
// let precompiles = precompiles.clone();
// let prev = handler.pre_execution.load_precompiles.clone();
// handler.pre_execution.load_precompiles = Arc::new(move || {
// let mut cx = prev();
// cx.extend(precompiles.iter().cloned().map(|(a, b)| (a, b.into())));
// cx
// });
// }));
// }

#[cfg(test)]
mod tests {
use crate::{evm::inject_precompiles, PrecompileFactory};
use alloy_primitives::{address, Address, Bytes};
use revm::{
precompile::{PrecompileOutput, PrecompileResult, Precompiles},
primitives::hardfork::SpecId,
};
// #[cfg(test)]
// mod tests {
// use crate::{evm::inject_precompiles, PrecompileFactory};
// use alloy_primitives::{address, Address, Bytes};
// use revm::{
// precompile::{PrecompileOutput, PrecompileResult, Precompiles},
// primitives::hardfork::SpecId,
// };

#[test]
fn build_evm_with_extra_precompiles() {
const PRECOMPILE_ADDR: Address = address!("0x0000000000000000000000000000000000000071");
// #[test]
// fn build_evm_with_extra_precompiles() {
// const PRECOMPILE_ADDR: Address = address!("0x0000000000000000000000000000000000000071");

fn my_precompile(_bytes: &Bytes, _gas_limit: u64) -> PrecompileResult {
Ok(PrecompileOutput { bytes: Bytes::new(), gas_used: 0 })
}
// fn my_precompile(_bytes: &Bytes, _gas_limit: u64) -> PrecompileResult {
// Ok(PrecompileOutput { bytes: Bytes::new(), gas_used: 0 })
// }

#[derive(Debug)]
struct CustomPrecompileFactory;
// #[derive(Debug)]
// struct CustomPrecompileFactory;

impl PrecompileFactory for CustomPrecompileFactory {
fn precompiles(&self) -> Vec<(Address, Precompile)> {
vec![(PRECOMPILE_ADDR, Precompile::Standard(my_precompile))]
}
}
// impl PrecompileFactory for CustomPrecompileFactory {
// fn precompiles(&self) -> Vec<(Address, Precompile)> {
// vec![(PRECOMPILE_ADDR, Precompile::Standard(my_precompile))]
// }
// }

let db = revm::db::EmptyDB::default();
let env = Box::<revm::primitives::Env>::default();
let spec = SpecId::default();
let handler_cfg = revm::primitives::HandlerCfg::new(spec);
let inspector = revm::inspectors::NoOpInspector;
let context = revm::Context::new(revm::EvmContext::new_with_env(db, env), inspector);
let handler = revm::Handler::new(handler_cfg);
let mut evm = revm::Evm::new(context, handler);
assert!(!evm
.handler
.pre_execution()
.load_precompiles()
.addresses()
.any(|&addr| addr == PRECOMPILE_ADDR));
// let db = revm::db::EmptyDB::default();
// let env = Box::<revm::primitives::Env>::default();
// let spec = SpecId::default();
// let handler_cfg = revm::primitives::HandlerCfg::new(spec);
// let inspector = revm::inspectors::NoOpInspector;
// let context = revm::Context::new(revm::EvmContext::new_with_env(db, env), inspector);
// let handler = revm::Handler::new(handler_cfg);
// let mut evm = revm::Evm::new(context, handler);
// assert!(!evm
// .handler
// .pre_execution()
// .load_precompiles()
// .addresses()
// .any(|&addr| addr == PRECOMPILE_ADDR));

inject_precompiles(&mut evm, CustomPrecompileFactory.precompiles());
assert!(evm
.handler
.pre_execution()
.load_precompiles()
.addresses()
.any(|&addr| addr == PRECOMPILE_ADDR));
// inject_precompiles(&mut evm, CustomPrecompileFactory.precompiles());
// assert!(evm
// .handler
// .pre_execution()
// .load_precompiles()
// .addresses()
// .any(|&addr| addr == PRECOMPILE_ADDR));

let result = evm.transact().unwrap();
assert!(result.result.is_success());
}
}
// let result = evm.transact().unwrap();
// assert!(result.result.is_success());
// }
// }
4 changes: 3 additions & 1 deletion crates/anvil/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ pub use hardfork::EthereumHardfork;
pub mod eth;
/// Evm related abstractions
mod evm;
pub use evm::{inject_precompiles, PrecompileFactory};
// pub use evm::{inject_precompiles, PrecompileFactory};
pub use evm::PrecompileFactory;

/// support for polling filters
pub mod filter;
/// commandline output
Expand Down
2 changes: 2 additions & 0 deletions crates/evm/core/src/evm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ use revm::{
Database, Journal,
};

// TODO: FoundryEvmContext should be generic over DB.
// We cannot use the LogCollector inspector which works over FoundryEvmContext in anvil without it.
pub type FoundryEvmContext<'db> = EthEvmContext<&'db mut dyn DatabaseExt>;

pub type FoundryEvm<'db, I, P = FoundryPrecompiles> =
Expand Down
2 changes: 1 addition & 1 deletion crates/evm/evm/src/inspectors/logs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ impl InspectorExt for LogCollector {
}

/// Converts a Hardhat `console.log` call to a DSTest `log(string)` event.
fn hh_to_ds(call: &console::hh::ConsoleCalls) -> Log {
pub fn hh_to_ds(call: &console::hh::ConsoleCalls) -> Log {
// Convert the parameters of the call to their string representation using `ConsoleFmt`.
let msg = call.fmt(Default::default());
new_console_log(&msg)
Expand Down
2 changes: 1 addition & 1 deletion crates/evm/evm/src/inspectors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ mod chisel_state;
pub use chisel_state::ChiselState;

mod logs;
pub use logs::LogCollector;
pub use logs::{hh_to_ds, LogCollector};

mod stack;
pub use stack::{InspectorData, InspectorStack, InspectorStackBuilder};
Loading