Skip to content

feat: enable decoding non-Ethereum precompiles when tracing #4821

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

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 15 additions & 2 deletions crates/cli/src/forge/cmd/test/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions crates/cli/tests/it/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
9 changes: 8 additions & 1 deletion crates/config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -202,6 +205,9 @@ pub struct Config {
pub model_checker: Option<ModelCheckerSettings>,
/// 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<String>,
/// etherscan API key, or alias for an `EtherscanConfig` in `etherscan` table
Expand Down Expand Up @@ -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![],
Expand Down
34 changes: 23 additions & 11 deletions crates/evm/src/trace/decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ impl CallTraceDecoderBuilder {
self
}

/// Add information for decoding custom precompile calls.
pub fn with_precompiles(mut self, precompiles: HashMap<Address, Function>) -> Self {
self.decoder.precompiles =
self.decoder.precompiles.into_iter().chain(precompiles).collect();
self
}

/// Build the decoder.
pub fn build(self) -> CallTraceDecoder {
self.decoder
Expand Down Expand Up @@ -110,6 +117,21 @@ macro_rules! precompiles {
}};
}

/// Returns the default precompiles used in the Ethereum Network.
pub fn default_precompiles() -> HashMap<Address, Function> {
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.
///
Expand All @@ -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(),

Expand Down
2 changes: 1 addition & 1 deletion crates/evm/src/trace/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down