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

Merged
merged 40 commits into from
Mar 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
dba8079
chore: bump revm dec 2024
rakita Dec 20, 2024
832037e
fmt
rakita Dec 20, 2024
26eed85
save compile err msg
rakita Dec 20, 2024
8fb15a6
progress
rakita Dec 20, 2024
c755f5b
simplify impl for Inspector, and add util for evm exec
rakita Dec 23, 2024
394c402
temp comment out tests
rakita Dec 23, 2024
a2b2a93
use generic ctx
rakita Dec 23, 2024
fb1872f
and with access list
rakita Dec 23, 2024
8c93e30
cleanup requirement
rakita Dec 23, 2024
125daa2
run tests
rakita Dec 24, 2024
8e46f98
fix all tests
rakita Dec 25, 2024
509a16c
enable is_precompile
rakita Dec 25, 2024
8e703f3
js tracer integrated
rakita Dec 26, 2024
26703d2
precompile addresses
rakita Dec 26, 2024
5a062c3
fix docs
rakita Dec 26, 2024
6175c99
Merge remote-tracking branch 'origin/main'
rakita Jan 24, 2025
12e5d70
bump to revm main, 27.jan 2025
rakita Jan 27, 2025
9703646
cleanup, rm printlns
rakita Jan 27, 2025
2b1a434
no_std
rakita Jan 27, 2025
366f74a
Merge remote-tracking branch 'origin/main'
rakita Feb 10, 2025
046edcb
bump newest revm feb 2025
rakita Feb 10, 2025
abd2011
doc
rakita Feb 10, 2025
665f5ac
chore: bump revm
rakita Feb 11, 2025
1a9082b
bump revm
rakita Feb 11, 2025
98e0971
bump revm
rakita Feb 11, 2025
687aa7b
bump revm
rakita Feb 11, 2025
bdd7925
bump revm main
rakita Feb 11, 2025
0d543bc
Update Cargo.toml
mattsse Feb 12, 2025
7d150ab
Update Cargo.toml
mattsse Feb 12, 2025
0e9da64
bump revm
klkvr Feb 13, 2025
74b6aa4
bump revm
klkvr Feb 17, 2025
8900c2b
bump revm
klkvr Feb 18, 2025
237f5b5
bump revm
klkvr Mar 5, 2025
72d0f27
bump revm
klkvr Mar 5, 2025
ff73346
bump revm
klkvr Mar 5, 2025
9219073
Merge branch 'main' into rakita/bump_revm_dec_2024
mattsse Mar 7, 2025
3c6cca4
20.0.0-alpha.3
klkvr Mar 10, 2025
2a365e3
9e39df5
klkvr Mar 10, 2025
667180b
20.0.0-alpha.4
klkvr Mar 11, 2025
959b76c
20.0.0-alpha.5
klkvr Mar 12, 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
15 changes: 9 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ alloy-rpc-types-eth = "0.12"
alloy-rpc-types-trace = "0.12"
alloy-sol-types = "0.8"
alloy-primitives = { version = "0.8", features = ["map"] }
revm = { version = "19.0.0", default-features = false, features = ["std"] }
revm = "20.0.0-alpha.5"

anstyle = { version = "1.0", optional = true }
colorchoice = "1.0"
Expand All @@ -53,10 +53,13 @@ snapbox = { version = "0.6", features = ["term-svg"] }

[features]
default = ["std"]
std = ["alloy-primitives/std", "anstyle/std", "serde/std", "serde_json/std", "revm/std", "thiserror/std"]
std = [
"alloy-primitives/std",
"anstyle/std",
"serde/std",
"serde_json/std",
"revm/std",
"thiserror/std",
]
serde = ["dep:serde", "revm/serde"]
js-tracer = ["dep:boa_engine", "dep:boa_gc"]

[patch.crates-io]
#alloy-rpc-types-eth = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
#alloy-rpc-types-trace = { git = "https://github.com/alloy-rs/alloy", rev = "cfb13aa" }
49 changes: 28 additions & 21 deletions src/access_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,15 @@ use alloy_primitives::{
};
use alloy_rpc_types_eth::{AccessList, AccessListItem};
use revm::{
interpreter::{opcode, Interpreter},
Database, EvmContext, Inspector,
bytecode::opcode,
context::JournalTr,
context_interface::{ContextTr, Transaction},
inspector::JournalExt,
interpreter::{
interpreter_types::{InputsTr, Jumps},
Interpreter,
},
Inspector,
};

/// An [Inspector] that collects touched accounts and storage slots.
Expand Down Expand Up @@ -65,67 +72,67 @@ impl AccessListInspector {
/// top-level call.
///
/// Those include caller, callee and precompiles.
fn collect_excluded_addresses<DB: Database>(&mut self, context: &EvmContext<DB>) {
let from = context.env.tx.caller;
let to = if let TxKind::Call(to) = context.env.tx.transact_to {
fn collect_excluded_addresses<CTX: ContextTr<Journal: JournalExt>>(&mut self, context: &CTX) {
let from = context.tx().caller();
let to = if let TxKind::Call(to) = context.tx().kind() {
to
} else {
// We need to exclude the created address if this is a CREATE frame.
//
// This assumes that caller has already been loaded but nonce was not increased yet.
let nonce = context.journaled_state.account(from).info.nonce;
let nonce = context.journal_ref().evm_state().get(&from).unwrap().info.nonce;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, can we drop the _ref() suffix here? should just be journal and journal_mut

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In most cases a mutable journal is needed, there was few cases were ref is needed so this fn was added

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, then I think it should remain like that for legacy reasons

from.create(nonce)
};
let precompiles = context.precompiles.addresses().copied();
let precompiles = context.journal_ref().precompile_addresses().clone();
self.excluded = [from, to].into_iter().chain(precompiles).collect();
}
}

impl<DB> Inspector<DB> for AccessListInspector
impl<CTX> Inspector<CTX> for AccessListInspector
where
DB: Database,
CTX: ContextTr<Journal: JournalExt>,
{
fn call(
&mut self,
context: &mut EvmContext<DB>,
context: &mut CTX,
_inputs: &mut revm::interpreter::CallInputs,
) -> Option<revm::interpreter::CallOutcome> {
// At the top-level frame, fill the excluded addresses
if context.journaled_state.depth() == 0 {
if context.journal().depth() == 0 {
self.collect_excluded_addresses(context)
}
None
}

fn create(
&mut self,
context: &mut EvmContext<DB>,
context: &mut CTX,
_inputs: &mut revm::interpreter::CreateInputs,
) -> Option<revm::interpreter::CreateOutcome> {
// At the top-level frame, fill the excluded addresses
if context.journaled_state.depth() == 0 {
if context.journal().depth() == 0 {
self.collect_excluded_addresses(context)
}
None
}

fn eofcreate(
&mut self,
context: &mut EvmContext<DB>,
context: &mut CTX,
_inputs: &mut revm::interpreter::EOFCreateInputs,
) -> Option<revm::interpreter::CreateOutcome> {
// At the top-level frame, fill the excluded addresses
if context.journaled_state.depth() == 0 {
if context.journal().depth() == 0 {
self.collect_excluded_addresses(context)
}
None
}

fn step(&mut self, interp: &mut Interpreter, _context: &mut EvmContext<DB>) {
match interp.current_opcode() {
fn step(&mut self, interp: &mut Interpreter, _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 @@ -137,15 +144,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
26 changes: 12 additions & 14 deletions src/edge_cov.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ use alloc::{vec, vec::Vec};
use alloy_primitives::{map::DefaultHashBuilder, Address, U256};
use core::hash::{BuildHasher, Hash, Hasher};
use revm::{
bytecode::opcode::{self},
interpreter::{
opcode::{self},
interpreter_types::{InputsTr, Jumps},
Interpreter,
},
Database, EvmContext, Inspector,
Inspector,
};

// This is the maximum number of edges that can be tracked. There is a tradeoff between performance
Expand Down Expand Up @@ -63,26 +64,23 @@ impl Default for EdgeCovInspector {
}
}

impl<DB> Inspector<DB> for EdgeCovInspector
where
DB: Database,
{
fn step(&mut self, interp: &mut Interpreter, _context: &mut EvmContext<DB>) {
let address = interp.contract.target_address; // TODO track context for delegatecall?
let current_pc = interp.program_counter();
impl<CTX> Inspector<CTX> for EdgeCovInspector {
fn step(&mut self, interp: &mut Interpreter, _context: &mut CTX) {
let address = interp.input.target_address(); // TODO track context for delegatecall?
let current_pc = interp.bytecode.pc();

match interp.current_opcode() {
match interp.bytecode.opcode() {
opcode::JUMP => {
// unconditional jump
if let Ok(jump_dest) = interp.stack().peek(0) {
if let Ok(jump_dest) = interp.stack.peek(0) {
self.store_hit(address, current_pc, jump_dest);
}
}
opcode::JUMPI => {
if let Ok(stack_value) = interp.stack().peek(0) {
let jump_dest = if stack_value != U256::from(0) {
if let Ok(stack_value) = interp.stack.peek(0) {
let jump_dest = if !stack_value.is_zero() {
// branch taken
interp.stack().peek(1)
interp.stack.peek(1)
} else {
// fall through
Ok(U256::from(current_pc + 1))
Expand Down
96 changes: 55 additions & 41 deletions src/opcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ use alloc::string::ToString;
use alloy_primitives::map::HashMap;
use alloy_rpc_types_trace::opcode::OpcodeGas;
use revm::{
bytecode::opcode::{self, OpCode},
interpreter::{
opcode::{self, OpCode},
interpreter_types::{Immediates, Jumps, LoopControl},
Interpreter,
},
Database, EvmContext, Inspector,
Inspector,
};

/// An Inspector that counts opcodes and measures gas usage per opcode.
Expand Down Expand Up @@ -58,80 +59,81 @@ 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> for OpcodeGasInspector {
fn step(&mut self, interp: &mut Interpreter, _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, _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::CacheDB,
database_interface::EmptyDB,
interpreter::{interpreter::ExtBytecode, InputsImpl, SharedMemory},
primitives::{hardfork::SpecId, Bytes},
Context, MainContext,
};
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::new(
Rc::new(RefCell::new(SharedMemory::new())),
ExtBytecode::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::mainnet().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 @@ -142,9 +144,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::new(
Rc::new(RefCell::new(SharedMemory::new())),
ExtBytecode::new(bytecode),
InputsImpl::default(),
false,
false,
SpecId::LATEST,
u64::MAX,
);
let db = CacheDB::new(EmptyDB::default());

let mut context = Context::mainnet().with_db(db);
for _ in opcodes.iter() {
opcode_counter.step(&mut interpreter, &mut context);
}
}
}
1 change: 1 addition & 0 deletions src/tracing/arena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,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
Loading
Loading