Skip to content

Commit 030ebbf

Browse files
committed
Simple REVM test runner (#788)
* refactor: nuke `evm-adapters` * refactor: simple revm test runner Current features: - Can run unit tests - Works with both revert-type tests and DSTest-type tests - Collects logs, albeit not for reverting tests - Integrated with config and CLI flags Disabled features: - Gas reports - Tracing - Cheatcodes - Fuzzing - Log decoding - Forking mode - Hardhat-style `console.log`, since those require us to decode calls to a specific address (HH does not emit logs) - The debugger In addition to this, I've disabled some tests that could never pass under the current circumstances, but that should be adjusted and re-enabled when their respective features are implemented (such as fuzz tests) * refactor: adjust CLI to new runner API * feat: log collector inspector * feat: hardhat logs * chore: lint * refactor: extract hh log converter to helper fn * refactor: return single test result if setup fails * build: use upstream revm chore: renuke `evm-adapters`
1 parent 8cee1c8 commit 030ebbf

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+1351
-10902
lines changed

Cargo.lock

Lines changed: 87 additions & 412 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
[workspace]
22
members = [
3-
"evm-adapters",
43
"utils",
54
"cast",
65
"forge",
@@ -18,11 +17,6 @@ codegen-units = 1
1817
panic = "abort"
1918
debug = true
2019

21-
## Patch sputnik with more recent primitive types
22-
# https://github.com/rust-blockchain/evm/pulls
23-
[patch."https://github.com/rust-blockchain/evm"]
24-
evm = { git = "https://github.com/gakonst/evm", branch = "bump-primitive-types" }
25-
2620
## Patch ethers-rs with a local checkout then run `cargo update -p ethers`
2721
#[patch."https://github.com/gakonst/ethers-rs"]
2822
#ethers = { path = "../ethers-rs" }

cli/Cargo.toml

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ foundry-utils = { path = "../utils" }
2626
forge = { path = "../forge" }
2727
foundry-config = { path = "../config" }
2828
cast = { path = "../cast" }
29-
evm-adapters = { path = "../evm-adapters" }
30-
ui = { path = "../ui" }
29+
# TODO: Re-enable when ported
30+
#ui = { path = "../ui" }
3131
dunce = "1.0.2"
3232
# ethers = "0.5"
3333
ethers = { git = "https://github.com/gakonst/ethers-rs", default-features = false }
@@ -46,9 +46,6 @@ rayon = "1.5.1"
4646
serde = "1.0.133"
4747
futures = "0.3.17"
4848

49-
## EVM Implementations
50-
# evm = { version = "0.30.1" }
51-
sputnik = { package = "evm", git = "https://github.com/rust-blockchain/evm", default-features = false, features = ["std"], optional = true }
5249
proptest = "1.0.0"
5350
glob = "0.3.0"
5451
semver = "1.0.5"
@@ -68,17 +65,11 @@ pretty_assertions = "1.0.0"
6865
toml = "0.5"
6966

7067
[features]
71-
default = ["sputnik-evm", "rustls"]
68+
default = ["rustls"]
7269
solc-asm = ["ethers/solc-sha2-asm"]
7370
rustls = ["ethers/rustls"]
7471
openssl = ["ethers/openssl"]
7572

76-
sputnik-evm = [
77-
"sputnik",
78-
"evm-adapters/sputnik",
79-
"evm-adapters/sputnik-helpers",
80-
]
81-
8273
integration-tests = []
8374

8475
[[bin]]

cli/src/cmd/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ pub mod init;
4747
pub mod inspect;
4848
pub mod install;
4949
pub mod remappings;
50-
pub mod run;
50+
// TODO: Re-enable when ported
51+
//pub mod run;
5152
pub mod snapshot;
5253
pub mod test;
5354
pub mod tree;

cli/src/cmd/test.rs

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ use crate::{
77
};
88
use ansi_term::Colour;
99
use clap::{AppSettings, Parser};
10-
use ethers::solc::{ArtifactOutput, ProjectCompileOutput};
11-
use evm_adapters::{
12-
call_tracing::ExecutionInfo, evm_opts::EvmOpts, gas_report::GasReport, sputnik::helpers::vm,
10+
use ethers::{
11+
abi::RawLog,
12+
contract::EthLogDecode,
13+
solc::{ArtifactOutput, ProjectCompileOutput},
1314
};
14-
use forge::{MultiContractRunnerBuilder, TestFilter, TestResult};
15+
use forge::{executor::opts::EvmOpts, MultiContractRunnerBuilder, TestFilter, TestResult};
1516
use foundry_config::{figment::Figment, Config};
1617
use regex::Regex;
1718
use std::{collections::BTreeMap, str::FromStr, sync::mpsc::channel, thread};
@@ -187,13 +188,11 @@ impl Cmd for TestArgs {
187188
let output = super::compile(&project, false, false)?;
188189

189190
// prepare the test builder
190-
let mut evm_cfg = crate::utils::sputnik_cfg(&config.evm_version);
191-
evm_cfg.create_contract_limit = None;
192-
191+
let mut evm_spec = crate::utils::evm_spec(&config.evm_version);
193192
let builder = MultiContractRunnerBuilder::default()
194193
.fuzzer(fuzzer)
195194
.initial_balance(evm_opts.initial_balance)
196-
.evm_cfg(evm_cfg)
195+
.evm_spec(evm_spec)
197196
.sender(evm_opts.sender);
198197

199198
test(
@@ -347,11 +346,12 @@ fn test<A: ArtifactOutput + 'static>(
347346
println!("{}", res);
348347
Ok(TestOutcome::new(results, allow_failure))
349348
} else {
350-
// Dapptools-style printing of test results
351-
let mut gas_report = GasReport::new(gas_reports.1);
349+
// TODO: Re-enable when ported
350+
//let mut gas_report = GasReport::new(gas_reports.1);
352351
let (tx, rx) = channel::<(String, BTreeMap<String, TestResult>)>();
353352
let known_contracts = runner.known_contracts.clone();
354-
let execution_info = runner.execution_info.clone();
353+
// TODO: Re-enable when ported
354+
//let execution_info = runner.execution_info.clone();
355355

356356
let handle = thread::spawn(move || {
357357
while let Ok((contract_name, tests)) = rx.recv() {
@@ -366,13 +366,19 @@ fn test<A: ArtifactOutput + 'static>(
366366
// output does not look like 1 big block.
367367
let mut add_newline = false;
368368
if verbosity > 1 && !result.logs.is_empty() {
369-
add_newline = true;
370-
println!("Logs:");
371-
for log in &result.logs {
372-
println!(" {}", log);
369+
// We only decode logs from Hardhat and DS-style console events
370+
let console_logs: Vec<String> =
371+
result.logs.iter().filter_map(decode_console_log).collect();
372+
373+
if !console_logs.is_empty() {
374+
println!("Logs:");
375+
for log in console_logs {
376+
println!(" {}", log);
377+
}
373378
}
374379
}
375-
if verbosity > 2 {
380+
// TODO: Re-enable this when traces are ported
381+
/*if verbosity > 2 {
376382
if let (Some(traces), Some(identified_contracts)) =
377383
(&result.traces, &result.identified_contracts)
378384
{
@@ -427,7 +433,7 @@ fn test<A: ArtifactOutput + 'static>(
427433
}
428434
}
429435
}
430-
}
436+
}*/
431437
if add_newline {
432438
println!();
433439
}
@@ -439,7 +445,8 @@ fn test<A: ArtifactOutput + 'static>(
439445

440446
handle.join().unwrap();
441447

442-
if gas_reporting {
448+
// TODO: Re-enable when ported
449+
/*if gas_reporting {
443450
for tests in results.values() {
444451
for result in tests.values() {
445452
if let (Some(traces), Some(identified_contracts)) =
@@ -451,7 +458,45 @@ fn test<A: ArtifactOutput + 'static>(
451458
}
452459
gas_report.finalize();
453460
println!("{}", gas_report);
454-
}
461+
}*/
455462
Ok(TestOutcome::new(results, allow_failure))
456463
}
457464
}
465+
466+
fn decode_console_log(log: &RawLog) -> Option<String> {
467+
use forge::abi::ConsoleEvents::{self, *};
468+
469+
let decoded = match ConsoleEvents::decode_log(log).ok()? {
470+
LogsFilter(inner) => format!("{}", inner.0),
471+
LogBytesFilter(inner) => format!("{}", inner.0),
472+
LogNamedAddressFilter(inner) => format!("{}: {:?}", inner.key, inner.val),
473+
LogNamedBytes32Filter(inner) => {
474+
format!("{}: 0x{}", inner.key, hex::encode(inner.val))
475+
}
476+
LogNamedDecimalIntFilter(inner) => {
477+
let (sign, val) = inner.val.into_sign_and_abs();
478+
format!(
479+
"{}: {}{}",
480+
inner.key,
481+
sign,
482+
ethers::utils::format_units(val, inner.decimals.as_u32()).unwrap()
483+
)
484+
}
485+
LogNamedDecimalUintFilter(inner) => {
486+
format!(
487+
"{}: {}",
488+
inner.key,
489+
ethers::utils::format_units(inner.val, inner.decimals.as_u32()).unwrap()
490+
)
491+
}
492+
LogNamedIntFilter(inner) => format!("{}: {:?}", inner.key, inner.val),
493+
LogNamedUintFilter(inner) => format!("{}: {:?}", inner.key, inner.val),
494+
LogNamedBytesFilter(inner) => {
495+
format!("{}: 0x{}", inner.key, hex::encode(inner.val))
496+
}
497+
LogNamedStringFilter(inner) => format!("{}: {}", inner.key, inner.val),
498+
499+
e => e.to_string(),
500+
};
501+
Some(decoded)
502+
}

cli/src/forge.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,10 @@ fn main() -> eyre::Result<()> {
3636
cmd.run()?;
3737
}
3838
}
39-
Subcommands::Run(cmd) => {
40-
cmd.run()?;
41-
}
39+
// TODO: Re-enable when ported
40+
//Subcommands::Run(cmd) => {
41+
// cmd.run()?;
42+
//}
4243
Subcommands::VerifyContract(args) => {
4344
utils::block_on(cmd::verify::run_verify(&args))?;
4445
}

cli/src/opts/evm.rs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
//! cli arguments for configuring the evm settings
22
use clap::Parser;
33
use ethers::types::{Address, U256};
4-
use evm_adapters::evm_opts::EvmType;
54
use foundry_config::{
65
figment::{
76
self,
@@ -27,7 +26,7 @@ use serde::Serialize;
2726
//
2827
// ```ignore
2928
// use foundry_config::Config;
30-
// use evm_adapter::EvmOpts;
29+
// use forge::executor::opts::EvmOpts;
3130
// # fn t(args: EvmArgs) {
3231
// let figment = Config::figment_with_root(".").merge(args);
3332
// let opts = figment.extract::<EvmOpts>().unwrap()
@@ -40,14 +39,6 @@ pub struct EvmArgs {
4039
#[serde(flatten)]
4140
pub env: EnvArgs,
4241

43-
#[clap(
44-
long,
45-
short,
46-
help = "the EVM type you want to use (e.g. sputnik)",
47-
default_value = "sputnik"
48-
)]
49-
pub evm_type: EvmType,
50-
5142
#[clap(help = "fetch state over a remote instead of starting from empty state", long, short)]
5243
#[clap(alias = "rpc-url")]
5344
#[serde(rename = "eth_rpc_url", skip_serializing_if = "Option::is_none")]
@@ -153,5 +144,4 @@ pub struct EnvArgs {
153144
#[clap(help = "the block.gaslimit value during EVM execution", long)]
154145
#[serde(skip_serializing_if = "Option::is_none")]
155146
pub block_gas_limit: Option<u64>,
156-
// TODO: Add configuration option for base fee.
157147
}

cli/src/opts/forge.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use crate::cmd::{
1313
inspect,
1414
install::InstallArgs,
1515
remappings::RemappingArgs,
16-
run::RunArgs,
1716
snapshot, test, tree,
1817
verify::{VerifyArgs, VerifyCheckArgs},
1918
};
@@ -47,10 +46,10 @@ pub enum Subcommands {
4746
#[clap(alias = "b")]
4847
Build(BuildArgs),
4948

50-
#[clap(about = "Run a single smart contract as a script")]
51-
#[clap(alias = "r")]
52-
Run(RunArgs),
53-
49+
// TODO: Re-enable when ported
50+
//#[clap(about = "Run a single smart contract as a script")]
51+
//#[clap(alias = "r")]
52+
//Run(RunArgs),
5453
#[clap(alias = "u", about = "Fetches all upstream lib changes")]
5554
Update {
5655
#[clap(

cli/src/utils.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
use std::{future::Future, path::Path, str::FromStr, time::Duration};
22

33
use ethers::{solc::EvmVersion, types::U256};
4-
#[cfg(feature = "sputnik-evm")]
5-
use sputnik::Config;
64

5+
use forge::executor::SpecId;
76
// reexport all `foundry_config::utils`
87
#[doc(hidden)]
98
pub use foundry_config::utils::*;
@@ -58,12 +57,11 @@ pub fn subscriber() {
5857
.init();
5958
}
6059

61-
#[cfg(feature = "sputnik-evm")]
62-
pub fn sputnik_cfg(evm: &EvmVersion) -> Config {
60+
pub fn evm_spec(evm: &EvmVersion) -> SpecId {
6361
match evm {
64-
EvmVersion::Istanbul => Config::istanbul(),
65-
EvmVersion::Berlin => Config::berlin(),
66-
EvmVersion::London => Config::london(),
62+
EvmVersion::Istanbul => SpecId::ISTANBUL,
63+
EvmVersion::Berlin => SpecId::BERLIN,
64+
EvmVersion::London => SpecId::LONDON,
6765
_ => panic!("Unsupported EVM version"),
6866
}
6967
}

cli/tests/cmd.rs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Contains various tests for checking forge's commands
22
use ansi_term::Colour;
33
use ethers::solc::{artifacts::Metadata, ConfigurableContractArtifact};
4-
use evm_adapters::evm_opts::{EvmOpts, EvmType};
4+
use forge::executor::opts::EvmOpts;
55
use foundry_cli_test_utils::{
66
ethers_solc::{remappings::Remapping, PathStyle},
77
forgetest, forgetest_ignore, forgetest_init, pretty_eq,
@@ -11,11 +11,7 @@ use foundry_config::{
1111
parse_with_profile, BasicConfig, Config, OptimizerDetails, SolidityErrorCode,
1212
};
1313
use pretty_assertions::assert_eq;
14-
use std::{
15-
env::{self},
16-
fs,
17-
str::FromStr,
18-
};
14+
use std::{env, fs, str::FromStr};
1915

2016
// import forge utils as mod
2117
#[allow(unused)]
@@ -245,9 +241,7 @@ forgetest_init!(can_get_evm_opts, |prj: TestProject, mut cmd: TestCommand| {
245241
assert!(config.ffi);
246242

247243
cmd.set_env("FOUNDRY_ETH_RPC_URL", url);
248-
let figment = Config::figment_with_root(prj.root())
249-
.merge(("evm_type", EvmType::Sputnik))
250-
.merge(("debug", false));
244+
let figment = Config::figment_with_root(prj.root()).merge(("debug", false));
251245
let evm_opts: EvmOpts = figment.extract().unwrap();
252246
assert_eq!(evm_opts.fork_url, Some(url.to_string()));
253247
});

0 commit comments

Comments
 (0)