From ad86979e06c0577fc097577358e460e7f5ec9bdf Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Wed, 9 Oct 2024 11:48:40 +0400 Subject: [PATCH] fix: support EOF opcodes in `cast da` (#9070) * fix: support EOF opcodes in cast da * fix * fix doc * fmt --- Cargo.lock | 1 - crates/cast/Cargo.toml | 1 - crates/cast/bin/main.rs | 2 +- crates/cast/src/lib.rs | 30 +++++++++++++++++++++++++----- crates/evm/core/src/ic.rs | 33 ++++++++++++++++++++++++++++++++- 5 files changed, 58 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d29783c24..be6aebeea 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 46f8eac95..9f0b6758c 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 0b861f90d..63894d980 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 8132f8e4f..69f86268b 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!( "{}", @@ -1959,17 +1960,36 @@ impl SimpleCast { /// # Example /// /// ``` + /// use alloy_primitives::hex; /// use cast::SimpleCast as Cast; /// /// # async fn foo() -> eyre::Result<()> { /// let bytecode = "0x608060405260043610603f57600035"; - /// let opcodes = Cast::disassemble(bytecode)?; + /// let opcodes = Cast::disassemble(&hex::decode(bytecode)?)?; /// println!("{}", opcodes); /// # 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 f7ab3093c..2711f8933 100644 --- a/crates/evm/core/src/ic.rs +++ b/crates/evm/core/src/ic.rs @@ -1,5 +1,9 @@ use alloy_primitives::map::HashMap; -use revm::interpreter::opcode::{PUSH0, PUSH1, PUSH32}; +use revm::interpreter::{ + opcode::{PUSH0, PUSH1, PUSH32}, + OpCode, +}; +use revm_inspectors::opcode::immediate_size; /// Maps from program counter to instruction counter. /// @@ -84,3 +88,30 @@ fn make_map(code: &[u8]) -> HashMap { } 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 +}