diff --git a/crates/cli/src/forge/cmd/test/mod.rs b/crates/cli/src/forge/cmd/test/mod.rs index 113903ca1e12d..56b0911cc16c4 100644 --- a/crates/cli/src/forge/cmd/test/mod.rs +++ b/crates/cli/src/forge/cmd/test/mod.rs @@ -31,9 +31,14 @@ use foundry_config::{ }, get_available_profiles, Config, }; -use foundry_evm::utils::evm_spec; +use foundry_evm::{trace::decoder::default_precompiles, utils::evm_spec}; use regex::Regex; -use std::{collections::BTreeMap, path::PathBuf, sync::mpsc::channel, time::Duration}; +use std::{ + collections::{BTreeMap, HashMap}, + path::PathBuf, + sync::mpsc::channel, + time::Duration, +}; use tracing::trace; use watchexec::config::{InitConfig, RuntimeConfig}; use yansi::Paint; @@ -557,6 +562,13 @@ async fn test( let mut gas_report = GasReport::new(config.gas_reports, config.gas_reports_ignore); let sig_identifier = SignaturesIdentifier::new(Config::foundry_cache_dir(), config.offline)?; + // Set up precompiles + // If empty, we wanna use the default precompiles. + let precompiles: HashMap<_, _> = match config.precompiles.len() { + 0 => default_precompiles(), + // Else, we assume the user has specified the precompiles they want to use. + _ => config.precompiles.into_iter().collect(), + }; let mut total_passed = 0; let mut total_failed = 0; @@ -601,6 +613,7 @@ async fn test( .with_labels(result.labeled_addresses.clone()) .with_events(local_identifier.events().cloned()) .with_verbosity(verbosity) + .with_precompiles(precompiles.clone()) .build(); // Signatures are of no value for gas reports diff --git a/crates/cli/tests/it/config.rs b/crates/cli/tests/it/config.rs index b1bdb03d9ab63..40a4395ac2f83 100644 --- a/crates/cli/tests/it/config.rs +++ b/crates/cli/tests/it/config.rs @@ -114,6 +114,7 @@ forgetest!(can_extract_config_values, |prj: TestProject, mut cmd: TestCommand| { fs_permissions: Default::default(), __non_exhaustive: (), __warnings: vec![], + precompiles: Default::default(), }; prj.write_config(input.clone()); let config = cmd.config(); diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 63003ca3c6396..f49c7858695f5 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -2,7 +2,10 @@ #![deny(missing_docs, unsafe_code, unused_crate_dependencies)] use crate::cache::StorageCachingConfig; -use ethers_core::types::{Address, Chain::Mainnet, H160, H256, U256}; +use ethers_core::{ + abi::Function, + types::{Address, Chain::Mainnet, H160, H256, U256}, +}; pub use ethers_solc::{self, artifacts::OptimizerDetails}; use ethers_solc::{ artifacts::{ @@ -202,6 +205,9 @@ pub struct Config { pub model_checker: Option, /// verbosity to use pub verbosity: u8, + /// Information for decoding custom precompile calls + #[serde(default)] + pub precompiles: Vec<(Address, Function)>, /// url of the rpc server that should be used for any rpc calls pub eth_rpc_url: Option, /// etherscan API key, or alias for an `EtherscanConfig` in `etherscan` table @@ -1775,6 +1781,7 @@ impl Default for Config { eth_rpc_url: None, etherscan_api_key: None, verbosity: 0, + precompiles: vec![], remappings: vec![], auto_detect_remappings: true, libraries: vec![], diff --git a/crates/evm/src/trace/decoder.rs b/crates/evm/src/trace/decoder.rs index f8e8ae651a1c3..42d60fcc35444 100644 --- a/crates/evm/src/trace/decoder.rs +++ b/crates/evm/src/trace/decoder.rs @@ -52,6 +52,13 @@ impl CallTraceDecoderBuilder { self } + /// Add information for decoding custom precompile calls. + pub fn with_precompiles(mut self, precompiles: HashMap) -> Self { + self.decoder.precompiles = + self.decoder.precompiles.into_iter().chain(precompiles).collect(); + self + } + /// Build the decoder. pub fn build(self) -> CallTraceDecoder { self.decoder @@ -110,6 +117,21 @@ macro_rules! precompiles { }}; } +/// Returns the default precompiles used in the Ethereum Network. +pub fn default_precompiles() -> HashMap { + precompiles!( + 0x01: ecrecover(hash: FixedBytes(32), v: Uint(256), r: Uint(256), s: Uint(256)) -> (publicAddress: Address), + 0x02: sha256(data: Bytes) -> (hash: FixedBytes(32)), + 0x03: ripemd(data: Bytes) -> (hash: FixedBytes(32)), + 0x04: identity(data: Bytes) -> (data: Bytes), + 0x05: modexp(Bsize: Uint(256), Esize: Uint(256), Msize: Uint(256), BEM: Bytes) -> (value: Bytes), + 0x06: ecadd(x1: Uint(256), y1: Uint(256), x2: Uint(256), y2: Uint(256)) -> (x: Uint(256), y: Uint(256)), + 0x07: ecmul(x1: Uint(256), y1: Uint(256), s: Uint(256)) -> (x: Uint(256), y: Uint(256)), + 0x08: ecpairing(x1: Uint(256), y1: Uint(256), x2: Uint(256), y2: Uint(256), x3: Uint(256), y3: Uint(256)) -> (success: Uint(256)), + 0x09: blake2f(rounds: Uint(4), h: FixedBytes(64), m: FixedBytes(128), t: FixedBytes(16), f: FixedBytes(1)) -> (h: FixedBytes(64)), + ).into() +} + impl CallTraceDecoder { /// Creates a new call trace decoder. /// @@ -119,17 +141,7 @@ impl CallTraceDecoder { Self { // TODO: These are the Ethereum precompiles. We should add a way to support precompiles // for other networks, too. - precompiles: precompiles!( - 0x01: ecrecover(hash: FixedBytes(32), v: Uint(256), r: Uint(256), s: Uint(256)) -> (publicAddress: Address), - 0x02: sha256(data: Bytes) -> (hash: FixedBytes(32)), - 0x03: ripemd(data: Bytes) -> (hash: FixedBytes(32)), - 0x04: identity(data: Bytes) -> (data: Bytes), - 0x05: modexp(Bsize: Uint(256), Esize: Uint(256), Msize: Uint(256), BEM: Bytes) -> (value: Bytes), - 0x06: ecadd(x1: Uint(256), y1: Uint(256), x2: Uint(256), y2: Uint(256)) -> (x: Uint(256), y: Uint(256)), - 0x07: ecmul(x1: Uint(256), y1: Uint(256), s: Uint(256)) -> (x: Uint(256), y: Uint(256)), - 0x08: ecpairing(x1: Uint(256), y1: Uint(256), x2: Uint(256), y2: Uint(256), x3: Uint(256), y3: Uint(256)) -> (success: Uint(256)), - 0x09: blake2f(rounds: Uint(4), h: FixedBytes(64), m: FixedBytes(128), t: FixedBytes(16), f: FixedBytes(1)) -> (h: FixedBytes(64)), - ).into(), + precompiles: default_precompiles(), contracts: Default::default(), diff --git a/crates/evm/src/trace/mod.rs b/crates/evm/src/trace/mod.rs index 23b5d1fa47a69..843e4bfe43599 100644 --- a/crates/evm/src/trace/mod.rs +++ b/crates/evm/src/trace/mod.rs @@ -24,7 +24,7 @@ use yansi::{Color, Paint}; /// Identifiers figure out what ABIs and labels belong to all the addresses of the trace. pub mod identifier; -mod decoder; +pub mod decoder; mod executor; pub mod node; pub mod utils;