From e57df5085801a0a7ceeecfc9812af789f08f02fb Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 9 Oct 2024 06:26:20 +0400 Subject: [PATCH 1/4] fix: support EOF opcodes in cast da --- Cargo.lock | 1 - crates/cast/Cargo.toml | 1 - crates/cast/bin/main.rs | 2 +- crates/cast/src/lib.rs | 27 +++++++++++++++++++---- crates/evm/core/src/ic.rs | 45 ++++++++++++++++++++++++++++++--------- 5 files changed, 59 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d29783c243fc..be6aebeea7de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3644,7 +3644,6 @@ dependencies = [ "comfy-table", "criterion", "dunce", - "evm-disassembler", "evmole", "eyre", "foundry-block-explorers", diff --git a/crates/cast/Cargo.toml b/crates/cast/Cargo.toml index 46f8eac95cd8..9f0b6758c11c 100644 --- a/crates/cast/Cargo.toml +++ b/crates/cast/Cargo.toml @@ -54,7 +54,6 @@ alloy-sol-types.workspace = true alloy-transport.workspace = true chrono.workspace = true -evm-disassembler.workspace = true eyre.workspace = true futures.workspace = true rand.workspace = true diff --git a/crates/cast/bin/main.rs b/crates/cast/bin/main.rs index 0b861f90d02a..63894d980b0a 100644 --- a/crates/cast/bin/main.rs +++ b/crates/cast/bin/main.rs @@ -307,7 +307,7 @@ async fn main_args(args: CastArgs) -> Result<()> { println!("Computed Address: {}", computed.to_checksum(None)); } CastSubcommand::Disassemble { bytecode } => { - println!("{}", SimpleCast::disassemble(&bytecode)?); + println!("{}", SimpleCast::disassemble(&hex::decode(bytecode)?)?); } CastSubcommand::Selectors { bytecode, resolve } => { let functions = SimpleCast::extract_functions(&bytecode)?; diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 8132f8e4f706..b099090d5a7f 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -21,7 +21,6 @@ use alloy_sol_types::sol; use alloy_transport::Transport; use base::{Base, NumberWithBase, ToBase}; use chrono::DateTime; -use evm_disassembler::{disassemble_bytes, disassemble_str, format_operations}; use eyre::{Context, ContextCompat, Result}; use foundry_block_explorers::Client; use foundry_common::{ @@ -37,6 +36,7 @@ use rayon::prelude::*; use revm::primitives::Eof; use std::{ borrow::Cow, + fmt::Write, io, marker::PhantomData, path::PathBuf, @@ -45,6 +45,7 @@ use std::{ time::Duration, }; use tokio::signal::ctrl_c; +use utils::decode_instructions; use foundry_common::abi::encode_function_args_packed; pub use foundry_evm::*; @@ -670,7 +671,7 @@ where if disassemble { let code = self.provider.get_code_at(who).block_id(block.unwrap_or_default()).await?.to_vec(); - Ok(format_operations(disassemble_bytes(code)?)?) + SimpleCast::disassemble(&code) } else { Ok(format!( "{}", @@ -1968,8 +1969,26 @@ impl SimpleCast { /// # Ok(()) /// # } /// ``` - pub fn disassemble(bytecode: &str) -> Result { - format_operations(disassemble_str(bytecode)?) + pub fn disassemble(code: &[u8]) -> Result { + let mut output = String::new(); + + for step in decode_instructions(code) { + write!(output, "{:08x}: ", step.pc)?; + + if let Some(op) = step.op { + write!(output, "{op}")?; + } else { + write!(output, "INVALID")?; + } + + if !step.immediate.is_empty() { + write!(output, " {}", hex::encode_prefixed(step.immediate))?; + } + + writeln!(output)?; + } + + Ok(output) } /// Gets the selector for a given function signature diff --git a/crates/evm/core/src/ic.rs b/crates/evm/core/src/ic.rs index f7ab3093c520..1e3cb85c54e8 100644 --- a/crates/evm/core/src/ic.rs +++ b/crates/evm/core/src/ic.rs @@ -1,5 +1,6 @@ use alloy_primitives::map::HashMap; -use revm::interpreter::opcode::{PUSH0, PUSH1, PUSH32}; +use revm::interpreter::OpCode; +use revm_inspectors::opcode::immediate_size; /// Maps from program counter to instruction counter. /// @@ -64,23 +65,47 @@ fn make_map(code: &[u8]) -> HashMap { let mut map = HashMap::default(); let mut pc = 0; - let mut cumulative_push_size = 0; + let mut ic = 0; + while pc < code.len() { - let ic = pc - cumulative_push_size; if PC_FIRST { map.insert(pc, ic); } else { map.insert(ic, pc); } - if (PUSH1..=PUSH32).contains(&code[pc]) { - // Skip the push bytes. - let push_size = (code[pc] - PUSH0) as usize; - pc += push_size; - cumulative_push_size += push_size; - } + let immediate_size = + OpCode::new(code[pc]).map(|op| immediate_size(op, &code[pc + 1..])).unwrap_or(0); - pc += 1; + pc += 1 + immediate_size as usize; + ic += 1; } map } + +/// Represents a single instruction consisting of the opcode and its immediate data. +pub struct Instruction<'a> { + /// OpCode, if it could be decoded. + pub op: Option, + /// Immediate data following the opcode. + pub immediate: &'a [u8], + /// Program counter of the opcode. + pub pc: usize, +} + +/// Decodes raw opcode bytes into [`Instruction`]s. +pub fn decode_instructions(code: &[u8]) -> Vec> { + let mut pc = 0; + let mut steps = Vec::new(); + + while pc < code.len() { + let op = OpCode::new(code[pc]); + let immediate_size = op.map(|op| immediate_size(op, &code[pc + 1..])).unwrap_or(0) as usize; + + steps.push(Instruction { op, pc, immediate: &code[pc + 1..pc + 1 + immediate_size] }); + + pc += 1 + immediate_size; + } + + steps +} From 6ec9224cb7e5e692b7fe7b62def0de6496afc93b Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 9 Oct 2024 06:44:58 +0400 Subject: [PATCH 2/4] fix --- crates/evm/core/src/ic.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/crates/evm/core/src/ic.rs b/crates/evm/core/src/ic.rs index 1e3cb85c54e8..2711f8933543 100644 --- a/crates/evm/core/src/ic.rs +++ b/crates/evm/core/src/ic.rs @@ -1,5 +1,8 @@ use alloy_primitives::map::HashMap; -use revm::interpreter::OpCode; +use revm::interpreter::{ + opcode::{PUSH0, PUSH1, PUSH32}, + OpCode, +}; use revm_inspectors::opcode::immediate_size; /// Maps from program counter to instruction counter. @@ -65,20 +68,23 @@ fn make_map(code: &[u8]) -> HashMap { let mut map = HashMap::default(); let mut pc = 0; - let mut ic = 0; - + let mut cumulative_push_size = 0; while pc < code.len() { + let ic = pc - cumulative_push_size; if PC_FIRST { map.insert(pc, ic); } else { map.insert(ic, pc); } - let immediate_size = - OpCode::new(code[pc]).map(|op| immediate_size(op, &code[pc + 1..])).unwrap_or(0); + if (PUSH1..=PUSH32).contains(&code[pc]) { + // Skip the push bytes. + let push_size = (code[pc] - PUSH0) as usize; + pc += push_size; + cumulative_push_size += push_size; + } - pc += 1 + immediate_size as usize; - ic += 1; + pc += 1; } map } From a8ac2c829d955c6dfd95c4743da6a58c2ead343d Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 9 Oct 2024 07:05:43 +0400 Subject: [PATCH 3/4] fix doc --- crates/cast/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index b099090d5a7f..7f9c68ffc74f 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -1961,10 +1961,11 @@ impl SimpleCast { /// /// ``` /// use cast::SimpleCast as Cast; + /// use alloy_primitives::hex; /// /// # async fn foo() -> eyre::Result<()> { /// let bytecode = "0x608060405260043610603f57600035"; - /// let opcodes = Cast::disassemble(bytecode)?; + /// let opcodes = Cast::disassemble(&hex::decode(bytecode)?)?; /// println!("{}", opcodes); /// # Ok(()) /// # } From aca1dece1c35f2138c90abb0bdfdd6431f94fe6a Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 9 Oct 2024 07:11:23 +0400 Subject: [PATCH 4/4] fmt --- crates/cast/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cast/src/lib.rs b/crates/cast/src/lib.rs index 7f9c68ffc74f..69f86268b3d0 100644 --- a/crates/cast/src/lib.rs +++ b/crates/cast/src/lib.rs @@ -1960,8 +1960,8 @@ impl SimpleCast { /// # Example /// /// ``` - /// use cast::SimpleCast as Cast; /// use alloy_primitives::hex; + /// use cast::SimpleCast as Cast; /// /// # async fn foo() -> eyre::Result<()> { /// let bytecode = "0x608060405260043610603f57600035";