Skip to content

Commit

Permalink
refactor: rewrite revert and precompile decoding (#6222)
Browse files Browse the repository at this point in the history
* wip

* rm ugly ass brute force decoding

* improve decoding

* decode precompiles

* chore: rewrite hardhat console patcher

* fixes

* chore: clippy

* further

* addtest
  • Loading branch information
DaniPopes authored Nov 7, 2023
1 parent 6958dc1 commit 63e043e
Show file tree
Hide file tree
Showing 42 changed files with 652 additions and 683 deletions.
138 changes: 75 additions & 63 deletions Cargo.lock

Large diffs are not rendered by default.

69 changes: 17 additions & 52 deletions crates/anvil/src/eth/backend/mem/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,18 @@ use ethers::{
DefaultFrame, Filter, FilteredParams, GethDebugTracingOptions, GethTrace, Log, OtherFields,
Trace, Transaction, TransactionReceipt, H160,
},
utils::{hex, keccak256, rlp},
utils::{keccak256, rlp},
};
use flate2::{read::GzDecoder, write::GzEncoder, Compression};
use foundry_common::fmt::format_token;
use foundry_evm::{
backend::{DatabaseError, DatabaseResult},
constants::DEFAULT_CREATE2_DEPLOYER_RUNTIME_CODE,
decode::{decode_custom_error_args, decode_revert},
decode::decode_revert,
inspectors::AccessListTracer,
revm::{
self,
db::CacheDB,
interpreter::{return_ok, InstructionResult},
interpreter::InstructionResult,
primitives::{
Account, BlockEnv, CreateScheme, EVMError, Env, ExecutionResult, InvalidHeader, Output,
SpecId, TransactTo, TxEnv, KECCAK_EMPTY,
Expand Down Expand Up @@ -285,7 +284,7 @@ impl Backend {
fork_genesis_infos.clear();

for res in genesis_accounts {
let (address, mut info) = res??;
let (address, mut info) = res.map_err(DatabaseError::display)??;
info.balance = self.genesis.balance;
db.insert_account(address, info.clone());

Expand Down Expand Up @@ -910,54 +909,20 @@ impl Backend {
// insert all transactions
for (info, receipt) in transactions.into_iter().zip(receipts) {
// log some tx info
{
node_info!(" Transaction: {:?}", info.transaction_hash);
if let Some(ref contract) = info.contract_address {
node_info!(" Contract created: {:?}", contract);
}
node_info!(" Gas used: {}", receipt.gas_used());
match info.exit {
return_ok!() => (),
InstructionResult::OutOfFund => {
node_info!(" Error: reverted due to running out of funds");
}
InstructionResult::CallTooDeep => {
node_info!(" Error: reverted with call too deep");
}
InstructionResult::Revert => {
if let Some(ref r) = info.out {
if let Ok(reason) = decode_revert(r.as_ref(), None, None) {
node_info!(" Error: reverted with '{}'", reason);
} else {
match decode_custom_error_args(r, 5) {
// assuming max 5 args
Some(token) => {
node_info!(
" Error: reverted with custom error: {:?}",
format_token(&token)
);
}
None => {
node_info!(
" Error: reverted with custom error: {}",
hex::encode(r)
);
}
}
}
} else {
node_info!(" Error: reverted without a reason");
}
}
InstructionResult::OutOfGas => {
node_info!(" Error: ran out of gas");
}
reason => {
node_info!(" Error: failed due to {:?}", reason);
}
}
node_info!("");
node_info!(" Transaction: {:?}", info.transaction_hash);
if let Some(contract) = &info.contract_address {
node_info!(" Contract created: {contract:?}");
}
node_info!(" Gas used: {}", receipt.gas_used());
if !info.exit.is_ok() {
let r = decode_revert(
info.out.as_deref().unwrap_or_default(),
None,
Some(info.exit),
);
node_info!(" Error: reverted with: {r}");
}
node_info!("");

let mined_tx = MinedTransaction {
info,
Expand Down
1 change: 0 additions & 1 deletion crates/cast/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ alloy-dyn-abi.workspace = true
foundry-compilers = { workspace = true, default-features = false }
foundry-block-explorers = { workspace = true }


chrono.workspace = true
evm-disassembler = "0.3"
eyre.workspace = true
Expand Down
2 changes: 1 addition & 1 deletion crates/config/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ pub fn get_available_profiles(toml_path: impl AsRef<Path>) -> eyre::Result<Vec<S
let doc = read_toml(toml_path)?;

if let Some(Item::Table(profiles)) = doc.as_table().get(Config::PROFILE_SECTION) {
for (profile, _) in profiles.iter() {
for (profile, _) in profiles {
let p = profile.to_string();
if !result.contains(&p) {
result.push(p);
Expand Down
2 changes: 1 addition & 1 deletion crates/evm/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ repository.workspace = true

[dependencies]
foundry-abi.workspace = true
foundry-cheatcodes-defs.workspace = true
foundry-common.workspace = true
foundry-compilers.workspace = true
foundry-config.workspace = true
Expand Down Expand Up @@ -53,5 +54,4 @@ once_cell = "1"

# Misc
url = "2"
auto_impl = "1"
itertools.workspace = true
32 changes: 16 additions & 16 deletions crates/evm/core/src/abi/mod.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
//! Several ABI-related utilities for executors.

use once_cell::sync::Lazy;
use std::collections::HashMap;

pub use foundry_abi::{
console::{self, ConsoleEvents, CONSOLE_ABI},
hardhat_console::{self, HardhatConsoleCalls, HARDHATCONSOLE_ABI as HARDHAT_CONSOLE_ABI},
console::{ConsoleEvents, CONSOLE_ABI},
hardhat_console::{HardhatConsoleCalls, HARDHATCONSOLE_ABI as HARDHAT_CONSOLE_ABI},
hevm::HEVM_ABI,
};
use once_cell::sync::Lazy;
use std::collections::HashMap;

/// If the input starts with a known `hardhat/console.log` `uint` selector, then this will replace
/// it with the selector `abigen!` bindings expect.
pub fn patch_hardhat_console_selector(input: &mut Vec<u8>) {
if input.len() < 4 {
return
}
let selector = unsafe { &mut *(input.get_unchecked_mut(..4) as *mut [u8] as *mut [u8; 4]) };
if let Some(abigen_selector) = HARDHAT_CONSOLE_SELECTOR_PATCHES.get(selector) {
*selector = *abigen_selector;
pub fn patch_hardhat_console_selector(input: &mut [u8]) {
if let Some(selector) = input.get_mut(..4) {
let selector: &mut [u8; 4] = selector.try_into().unwrap();
if let Some(generated_selector) = HARDHAT_CONSOLE_SELECTOR_PATCHES.get(selector) {
*selector = *generated_selector;
}
}
}

Expand All @@ -27,7 +27,7 @@ pub fn patch_hardhat_console_selector(input: &mut Vec<u8>) {
/// bindings which `abigen!` creates. `hardhat/console.log` logs its events in functions that accept
/// `uint` manually as `abi.encodeWithSignature("log(int)", p0)`, but `abigen!` uses `uint256` for
/// its call bindings (`HardhatConsoleCalls`) as generated by solc.
pub static HARDHAT_CONSOLE_SELECTOR_PATCHES: Lazy<HashMap<[u8; 4], [u8; 4]>> = Lazy::new(|| {
static HARDHAT_CONSOLE_SELECTOR_PATCHES: Lazy<HashMap<[u8; 4], [u8; 4]>> = Lazy::new(|| {
HashMap::from([
// log(bool,uint256,uint256,address)
([241, 97, 178, 33], [0, 221, 135, 185]),
Expand Down Expand Up @@ -545,11 +545,11 @@ mod tests {
use super::*;

#[test]
fn hardhat_console_path_works() {
for (hh, abigen) in HARDHAT_CONSOLE_SELECTOR_PATCHES.iter() {
let mut hh = (*hh).to_vec();
fn hardhat_console_patch() {
for (hh, generated) in HARDHAT_CONSOLE_SELECTOR_PATCHES.iter() {
let mut hh = *hh;
patch_hardhat_console_selector(&mut hh);
assert_eq!((*abigen).to_vec(), hh);
assert_eq!(hh, *generated);
}
}
}
1 change: 0 additions & 1 deletion crates/evm/core/src/backend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ const GLOBAL_FAILURE_SLOT: B256 =
b256!("6661696c65640000000000000000000000000000000000000000000000000000");

/// An extension trait that allows us to easily extend the `revm::Inspector` capabilities
#[auto_impl::auto_impl(&mut, Box)]
pub trait DatabaseExt: Database<Error = DatabaseError> {
/// Creates a new snapshot at the current point of execution.
///
Expand Down
11 changes: 6 additions & 5 deletions crates/evm/core/src/debug.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{abi::HEVM_ABI, utils::CallKind};
use crate::utils::CallKind;
use alloy_primitives::{Address, U256};
use revm::interpreter::{Memory, OpCode};
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -178,11 +178,12 @@ impl Display for Instruction {
Instruction::Cheatcode(cheat) => write!(
f,
"VM_{}",
&*HEVM_ABI
.functions()
.find(|func| func.short_signature() == *cheat)
foundry_cheatcodes_defs::Vm::CHEATCODES
.iter()
.map(|c| &c.func)
.find(|c| c.selector_bytes == *cheat)
.expect("unknown cheatcode found in debugger")
.name
.id
.to_uppercase()
),
}
Expand Down
Loading

0 comments on commit 63e043e

Please sign in to comment.