Skip to content
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

chore: bump revm dec 2024 #246

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
11 changes: 10 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@ alloy-rpc-types-eth = "0.8"
alloy-rpc-types-trace = "0.8"
alloy-sol-types = "0.8"
alloy-primitives = { version = "0.8", features = ["map"] }
revm = { version = "18.0.0", default-features = false, features = ["std"] }
revm = { git = "https://github.com/bluealloy/revm.git", rev = "3c0c034f", default-features = false, features = [
"std",
] }
revm-inspector = { git = "https://github.com/bluealloy/revm.git", rev = "3c0c034f", default-features = false, features = [
"std",
] }

anstyle = "1.0"
colorchoice = "1.0"
Expand All @@ -50,7 +55,11 @@ boa_gc = { version = "0.19", optional = true }

[dev-dependencies]
snapbox = { version = "0.6", features = ["term-svg"] }
revm-database = { git = "https://github.com/bluealloy/revm.git", rev = "3c0c034f", default-features = false, features = [
"std",
] }

[features]
default = ["js-tracer"]
serde = ["dep:serde", "revm/serde"]
js-tracer = ["dep:boa_engine", "dep:boa_gc"]
26 changes: 14 additions & 12 deletions src/access_list.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
use alloy_primitives::{Address, B256};
use alloy_rpc_types_eth::{AccessList, AccessListItem};
use revm::{
interpreter::{opcode, Interpreter},
Database, EvmContext, Inspector,
bytecode::opcode,
interpreter::{
interpreter::EthInterpreter,
interpreter_types::{InputsTrait, Jumps},
Interpreter,
},
};
use revm_inspector::Inspector;
use std::collections::{BTreeSet, HashMap, HashSet};

/// An [Inspector] that collects touched accounts and storage slots.
Expand Down Expand Up @@ -58,15 +63,12 @@ impl AccessListInspector {
}
}

impl<DB> Inspector<DB> for AccessListInspector
where
DB: Database,
{
fn step(&mut self, interp: &mut Interpreter, _context: &mut EvmContext<DB>) {
match interp.current_opcode() {
impl<CTX> Inspector<CTX, EthInterpreter> for AccessListInspector {
fn step(&mut self, interp: &mut Interpreter<EthInterpreter>, _context: &mut CTX) {
match interp.bytecode.opcode() {
opcode::SLOAD | opcode::SSTORE => {
if let Ok(slot) = interp.stack().peek(0) {
let cur_contract = interp.contract.target_address;
if let Ok(slot) = interp.stack.peek(0) {
let cur_contract = interp.input.target_address();
self.access_list
.entry(cur_contract)
.or_default()
Expand All @@ -78,15 +80,15 @@ where
| opcode::EXTCODESIZE
| opcode::BALANCE
| opcode::SELFDESTRUCT => {
if let Ok(slot) = interp.stack().peek(0) {
if let Ok(slot) = interp.stack.peek(0) {
let addr = Address::from_word(B256::from(slot.to_be_bytes()));
if !self.excluded.contains(&addr) {
self.access_list.entry(addr).or_default();
}
}
}
opcode::DELEGATECALL | opcode::CALL | opcode::STATICCALL | opcode::CALLCODE => {
if let Ok(slot) = interp.stack().peek(1) {
if let Ok(slot) = interp.stack.peek(1) {
let addr = Address::from_word(B256::from(slot.to_be_bytes()));
if !self.excluded.contains(&addr) {
self.access_list.entry(addr).or_default();
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! revm [Inspector](revm::Inspector) implementations, such as call tracers
//! revm [Inspector](revm_inspector::Inspector) implementations, such as call tracers
//!
//! ## Feature Flags
//!
Expand Down
98 changes: 57 additions & 41 deletions src/opcode.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use alloy_rpc_types_trace::opcode::OpcodeGas;
use revm::{
bytecode::opcode::{self, OpCode},
interpreter::{
opcode::{self, OpCode},
interpreter::EthInterpreter,
interpreter_types::{Immediates, Jumps, LoopControl},
Interpreter,
},
Database, EvmContext, Inspector,
};
use revm_inspector::Inspector;
use std::collections::HashMap;

/// An Inspector that counts opcodes and measures gas usage per opcode.
Expand Down Expand Up @@ -57,80 +59,82 @@ impl OpcodeGasInspector {
}
}

impl<DB> Inspector<DB> for OpcodeGasInspector
where
DB: Database,
{
fn step(&mut self, interp: &mut Interpreter, _context: &mut EvmContext<DB>) {
let opcode_value = interp.current_opcode();
impl<CTX> Inspector<CTX, EthInterpreter> for OpcodeGasInspector {
fn step(&mut self, interp: &mut Interpreter<EthInterpreter>, _context: &mut CTX) {
let opcode_value = interp.bytecode.opcode();
if let Some(opcode) = OpCode::new(opcode_value) {
// keep track of opcode counts
*self.opcode_counts.entry(opcode).or_default() += 1;

// keep track of the last opcode executed
self.last_opcode_gas_remaining = Some((opcode, interp.gas().remaining()));
self.last_opcode_gas_remaining = Some((opcode, interp.control.gas().remaining()));
}
}

fn step_end(&mut self, interp: &mut Interpreter, _context: &mut EvmContext<DB>) {
fn step_end(&mut self, interp: &mut Interpreter<EthInterpreter>, _context: &mut CTX) {
// update gas usage for the last opcode
if let Some((opcode, gas_remaining)) = self.last_opcode_gas_remaining.take() {
let gas_cost = gas_remaining.saturating_sub(interp.gas().remaining());
let gas_cost = gas_remaining.saturating_sub(interp.control.gas().remaining());
*self.opcode_gas.entry(opcode).or_default() += gas_cost;
}
}
}

/// Accepts [OpCode] and a slice of bytecode immediately after it and returns the size of immediate
/// Accepts Bytecode that implements [Immediates] and returns the size of immediate
/// value.
///
/// Primarily needed to handle a special case of RJUMPV opcode.
pub fn immediate_size(op: OpCode, bytes_after: &[u8]) -> u8 {
match op.get() {
opcode::RJUMPV => {
if bytes_after.is_empty() {
return 0;
}
1 + (bytes_after[0] + 1) * 2
}
_ => op.info().immediate_size(),
pub fn immediate_size(bytecode: &impl Immediates) -> u8 {
let opcode = bytecode.read_u8();
if opcode == opcode::RJUMPV {
let vtable_size = bytecode.read_slice(2)[2];
return 1 + (vtable_size + 1) * 2;
}
let Some(opcode) = OpCode::new(opcode) else { return 0 };
opcode.info().immediate_size()
}

#[cfg(test)]
mod tests {
use super::*;
use revm::{
db::{CacheDB, EmptyDB},
interpreter::{opcode, Contract},
bytecode::Bytecode,
database_interface::EmptyDB,
interpreter::{InputsImpl, SharedMemory},
primitives::Bytes,
specification::hardfork::SpecId,
Context,
};
use revm_database::CacheDB;
use std::{cell::RefCell, rc::Rc};

#[test]
fn test_opcode_counter_inspector() {
let mut opcode_counter = OpcodeGasInspector::new();
let contract = Contract::default();
let mut interpreter = Interpreter::new(contract, 10000, false);
let db = CacheDB::new(EmptyDB::default());

let opcodes = [
OpCode::new(opcode::ADD).unwrap(),
OpCode::new(opcode::ADD).unwrap(),
OpCode::new(opcode::ADD).unwrap(),
OpCode::new(opcode::BYTE).unwrap(),
];
let opcodes = [opcode::ADD, opcode::ADD, opcode::ADD, opcode::BYTE];

let bytecode = Bytecode::new_raw(Bytes::from(opcodes));
let mut interpreter = Interpreter::<EthInterpreter>::new(
Rc::new(RefCell::new(SharedMemory::new())),
bytecode,
InputsImpl::default(),
false,
false,
SpecId::LATEST,
u64::MAX,
);
let db = CacheDB::new(EmptyDB::default());

for &opcode in &opcodes {
interpreter.instruction_pointer = &opcode.get();
opcode_counter.step(&mut interpreter, &mut EvmContext::new(db.clone()));
let mut context = Context::default().with_db(db);
for _ in &opcodes {
opcode_counter.step(&mut interpreter, &mut context);
}
}

#[test]
fn test_with_variety_of_opcodes() {
let mut opcode_counter = OpcodeGasInspector::new();
let contract = Contract::default();
let mut interpreter = Interpreter::new(contract, 2024, false);
let db = CacheDB::new(EmptyDB::default());

let opcodes = [
opcode::PUSH1,
Expand All @@ -141,9 +145,21 @@ mod tests {
opcode::STOP,
];

for opcode in opcodes.iter() {
interpreter.instruction_pointer = opcode;
opcode_counter.step(&mut interpreter, &mut EvmContext::new(db.clone()));
let bytecode = Bytecode::new_raw(Bytes::from(opcodes));
let mut interpreter = Interpreter::<EthInterpreter>::new(
Rc::new(RefCell::new(SharedMemory::new())),
bytecode,
InputsImpl::default(),
false,
false,
SpecId::LATEST,
u64::MAX,
);
let db = CacheDB::new(EmptyDB::default());

let mut context = Context::default().with_db(db);
for _ in opcodes.iter() {
opcode_counter.step(&mut interpreter, &mut context);
}
}
}
2 changes: 2 additions & 0 deletions src/tracing/arena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ impl CallTraceArena {
kind: PushTraceKind,
new_trace: CallTrace,
) -> usize {
println!("entry: {:?} kind {kind:?}, new_trace: {new_trace:?}", entry);
loop {
match new_trace.depth {
// The entry node, just update it
Expand Down Expand Up @@ -91,6 +92,7 @@ impl CallTraceArena {
}

/// How to push a trace into the arena
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum PushTraceKind {
/// This will _only_ push the trace into the arena.
PushOnly,
Expand Down
11 changes: 6 additions & 5 deletions src/tracing/builder/geth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ use alloy_rpc_types_trace::geth::{
GethDefaultTracingOptions, PreStateConfig, PreStateFrame, PreStateMode, StructLog,
};
use revm::{
db::DatabaseRef,
primitives::{EvmState, ResultAndState},
context_interface::result::{HaltReasonTrait, ResultAndState},
state::EvmState,
DatabaseRef,
};
use std::{
borrow::Cow,
Expand Down Expand Up @@ -96,7 +97,7 @@ impl<'a> GethTraceBuilder<'a> {
/// Generate a geth-style trace e.g. for `debug_traceTransaction`
///
/// This expects the gas used and return value for the
/// [ExecutionResult](revm::primitives::ExecutionResult) of the executed transaction.
/// [ExecutionResult](revm::context_interface::result::ExecutionResult) of the executed transaction.
pub fn geth_traces(
&self,
receipt_gas_used: u64,
Expand Down Expand Up @@ -128,7 +129,7 @@ impl<'a> GethTraceBuilder<'a> {
/// This decodes all call frames from the recorded traces.
///
/// This expects the gas used and return value for the
/// [ExecutionResult](revm::primitives::ExecutionResult) of the executed transaction.
/// [ExecutionResult](revm::context_interface::result::ExecutionResult) of the executed transaction.
pub fn geth_call_traces(&self, opts: CallConfig, gas_used: u64) -> CallFrame {
if self.nodes.is_empty() {
return Default::default();
Expand Down Expand Up @@ -216,7 +217,7 @@ impl<'a> GethTraceBuilder<'a> {
/// * `db` - The database to fetch state pre-transaction execution.
pub fn geth_prestate_traces<DB: DatabaseRef>(
&self,
ResultAndState { state, .. }: &ResultAndState,
ResultAndState { state, .. }: &ResultAndState<impl HaltReasonTrait>,
prestate_config: &PreStateConfig,
db: DB,
) -> Result<PreStateFrame, DB::Error> {
Expand Down
13 changes: 8 additions & 5 deletions src/tracing/builder/parity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ use alloy_primitives::{map::HashSet, Address, U256, U64};
use alloy_rpc_types_eth::TransactionInfo;
use alloy_rpc_types_trace::parity::*;
use revm::{
db::DatabaseRef,
primitives::{Account, ExecutionResult, ResultAndState, SpecId, KECCAK_EMPTY},
context_interface::result::{ExecutionResult, HaltReasonTrait, ResultAndState},
primitives::KECCAK_EMPTY,
specification::hardfork::SpecId,
state::Account,
DatabaseRef,
};
use std::{collections::VecDeque, iter::Peekable};

Expand Down Expand Up @@ -148,7 +151,7 @@ impl ParityTraceBuilder {
/// using the [DatabaseRef].
pub fn into_trace_results(
self,
res: &ExecutionResult,
res: &ExecutionResult<impl HaltReasonTrait>,
trace_types: &HashSet<TraceType>,
) -> TraceResults {
let output = res.output().cloned().unwrap_or_default();
Expand All @@ -169,7 +172,7 @@ impl ParityTraceBuilder {
/// with the [TracingInspector](crate::tracing::TracingInspector).
pub fn into_trace_results_with_state<DB: DatabaseRef>(
self,
res: &ResultAndState,
res: &ResultAndState<impl HaltReasonTrait>,
trace_types: &HashSet<TraceType>,
db: DB,
) -> Result<TraceResults, DB::Error> {
Expand Down Expand Up @@ -479,7 +482,7 @@ where
/// in the [ExecutionResult] state map and compares the balance and nonce against what's in the
/// `db`, which should point to the beginning of the transaction.
///
/// It's expected that `DB` is a revm [Database](revm::db::Database) which at this point already
/// It's expected that `DB` is a revm [Database](revm::database_interface::Database) which at this point already
/// contains all the accounts that are in the state map and never has to fetch them from disk.
pub fn populate_state_diff<'a, DB, I>(
state_diff: &mut StateDiff,
Expand Down
2 changes: 1 addition & 1 deletion src/tracing/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use alloy_rpc_types_trace::{
geth::{CallConfig, FlatCallConfig, GethDefaultTracingOptions, PreStateConfig},
parity::TraceType,
};
use revm::interpreter::OpCode;
use revm::bytecode::opcode::OpCode;

/// 256 bits each marking whether an opcode should be included into steps trace or not.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
Expand Down
17 changes: 4 additions & 13 deletions src/tracing/fourbyte.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,8 @@
use alloy_primitives::{hex, Selector};
use alloy_rpc_types_trace::geth::FourByteFrame;
use revm::{
interpreter::{CallInputs, CallOutcome},
Database, EvmContext, Inspector,
};
use revm::interpreter::{interpreter::EthInterpreter, CallInputs, CallOutcome};
use revm_inspector::Inspector;
use std::collections::HashMap;

/// Fourbyte tracing inspector that records all function selectors and their calldata sizes.
Expand All @@ -43,15 +41,8 @@ impl FourByteInspector {
}
}

impl<DB> Inspector<DB> for FourByteInspector
where
DB: Database,
{
fn call(
&mut self,
_context: &mut EvmContext<DB>,
inputs: &mut CallInputs,
) -> Option<CallOutcome> {
impl<CTX> Inspector<CTX, EthInterpreter> for FourByteInspector {
fn call(&mut self, _context: &mut CTX, inputs: &mut CallInputs) -> Option<CallOutcome> {
if inputs.input.len() >= 4 {
let selector =
Selector::try_from(&inputs.input[..4]).expect("input is at least 4 bytes");
Expand Down
Loading
Loading