Skip to content

Commit

Permalink
feat(chisel): add --no-vm option, enabled by default for old Solc ver…
Browse files Browse the repository at this point in the history
…sions (#6854)

* feat(chisel): add --no-vm option, enabled by default for old Solc versions

* fix

* feat: allow >=0.6.2 <0.8.4

* chore: clippy
  • Loading branch information
DaniPopes authored Jan 19, 2024
1 parent 77e977f commit 6b2633c
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 58 deletions.
7 changes: 5 additions & 2 deletions crates/cheatcodes/spec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,17 @@ mod tests {
}

fn sol_iface() -> String {
let cheats = Cheatcodes::new().to_string().trim().replace('\n', "\n ");
let mut cheats = Cheatcodes::new();
cheats.errors = Default::default(); // Skip errors to allow <0.8.4.
let cheats = cheats.to_string().trim().replace('\n', "\n ");
format!(
"\
// Automatically generated from `foundry-cheatcodes` Vm definitions. Do not modify manually.
// This interface is just for internal testing purposes. Use `forge-std` instead.
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity ^0.8.4;
pragma solidity >=0.6.2 <0.9.0;
pragma experimental ABIEncoderV2;
interface Vm {{
{cheats}
Expand Down
13 changes: 1 addition & 12 deletions crates/chisel/benches/session_source.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
use chisel::session_source::{SessionSource, SessionSourceConfig};
use criterion::{criterion_group, Criterion};
use foundry_compilers::Solc;
use foundry_config::Config;
use foundry_evm::opts::EvmOpts;
use once_cell::sync::Lazy;
use std::hint::black_box;
use tokio::runtime::Runtime;
Expand Down Expand Up @@ -66,16 +64,7 @@ fn inspect(c: &mut Criterion) {

/// Helper function for getting an empty [SessionSource] with default configuration
fn get_empty_session_source() -> SessionSource {
SessionSource::new(
SOLC.clone(),
SessionSourceConfig {
foundry_config: Config::default(),
evm_opts: EvmOpts::default(),
backend: None,
traces: false,
calldata: None,
},
)
SessionSource::new(SOLC.clone(), SessionSourceConfig::default())
}

fn rt() -> Runtime {
Expand Down
9 changes: 9 additions & 0 deletions crates/chisel/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ pub struct ChiselParser {
#[clap(long, help_heading = "REPL options")]
pub prelude: Option<PathBuf>,

/// Disable the default `Vm` import.
#[clap(long, help_heading = "REPL options", long_help = format!(
"Disable the default `Vm` import.\n\n\
The import is disabled by default if the Solc version is less than {}.",
chisel::session_source::MIN_VM_VERSION
))]
pub no_vm: bool,

#[clap(flatten)]
pub opts: CoreBuildArgs,

Expand Down Expand Up @@ -107,6 +115,7 @@ async fn main() -> eyre::Result<()> {
// Enable traces if any level of verbosity was passed
traces: config.verbosity > 0,
foundry_config: config,
no_vm: args.no_vm,
evm_opts,
backend: None,
calldata: None,
Expand Down
93 changes: 59 additions & 34 deletions crates/chisel/src/session_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ use solang_parser::pt;
use std::{collections::HashMap, fs, path::PathBuf};
use yansi::Paint;

/// The minimum Solidity version of the `Vm` interface.
pub const MIN_VM_VERSION: Version = Version::new(0, 6, 2);

/// Solidity source for the `Vm` interface in [forge-std](https://github.com/foundry-rs/forge-std)
static VM_SOURCE: &str = include_str!("../../../testdata/cheats/Vm.sol");

Expand Down Expand Up @@ -69,6 +72,8 @@ pub struct SessionSourceConfig {
pub foundry_config: Config,
/// EVM Options
pub evm_opts: EvmOpts,
/// Disable the default `Vm` import.
pub no_vm: bool,
#[serde(skip)]
/// In-memory REVM db for the session's runner.
pub backend: Option<Backend>,
Expand Down Expand Up @@ -184,9 +189,13 @@ impl SessionSource {
///
/// A new instance of [SessionSource]
#[track_caller]
pub fn new(solc: Solc, config: SessionSourceConfig) -> Self {
#[cfg(debug_assertions)]
let _ = solc.version().unwrap();
pub fn new(solc: Solc, mut config: SessionSourceConfig) -> Self {
if let Ok(v) = solc.version_short() {
if v < MIN_VM_VERSION && !config.no_vm {
tracing::info!(version=%v, minimum=%MIN_VM_VERSION, "Disabling VM injection");
config.no_vm = true;
}
}

Self {
file_name: PathBuf::from("ReplContract.sol".to_string()),
Expand Down Expand Up @@ -315,14 +324,15 @@ impl SessionSource {
sources.insert(self.file_name.clone(), Source::new(self.to_repl_source()));

// Include Vm.sol if forge-std remapping is not available
if !self
.config
.foundry_config
.get_all_remappings()
.into_iter()
.any(|r| r.name.starts_with("forge-std"))
if !self.config.no_vm &&
!self
.config
.foundry_config
.get_all_remappings()
.into_iter()
.any(|r| r.name.starts_with("forge-std"))
{
sources.insert(PathBuf::from("forge-std/Vm.sol"), Source::new(VM_SOURCE.to_owned()));
sources.insert(PathBuf::from("forge-std/Vm.sol"), Source::new(VM_SOURCE));
}

// we only care about the solidity source, so we can safely unwrap
Expand Down Expand Up @@ -446,24 +456,27 @@ impl SessionSource {
/// The [SessionSource] represented as a Forge Script contract.
pub fn to_script_source(&self) -> String {
let Version { major, minor, patch, .. } = self.solc.version().unwrap();
let Self { contract_name, global_code, top_level_code, run_code, config, .. } = self;

let script_import =
if !config.no_vm { "import {Script} from \"forge-std/Script.sol\";\n" } else { "" };

format!(
r#"
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^{major}.{minor}.{patch};
import {{Script}} from "forge-std/Script.sol";
{}
{script_import}
{global_code}
contract {} is Script {{
{}
contract {contract_name} is Script {{
{top_level_code}
/// @notice Script entry point
function run() public {{
{}
{run_code}
}}
}}
"#,
self.global_code, self.contract_name, self.top_level_code, self.run_code,
}}"#,
)
}

Expand All @@ -474,25 +487,34 @@ contract {} is Script {{
/// The [SessionSource] represented as a REPL contract.
pub fn to_repl_source(&self) -> String {
let Version { major, minor, patch, .. } = self.solc.version().unwrap();
let Self { contract_name, global_code, top_level_code, run_code, config, .. } = self;

let (vm_import, vm_constant) = if !config.no_vm {
(
"import {Vm} from \"forge-std/Vm.sol\";\n",
"Vm internal constant vm = Vm(address(uint160(uint256(keccak256(\"hevm cheat code\")))));\n"
)
} else {
("", "")
};

format!(
r#"
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^{major}.{minor}.{patch};
import {{Vm}} from "forge-std/Vm.sol";
{}
{vm_import}
{global_code}
contract {} {{
Vm internal constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code")))));
{}
contract {contract_name} {{
{vm_constant}
{top_level_code}
/// @notice REPL contract entry point
function run() public {{
{}
{run_code}
}}
}}
"#,
self.global_code, self.contract_name, self.top_level_code, self.run_code,
}}"#,
)
}

Expand Down Expand Up @@ -646,14 +668,17 @@ pub fn parse_fragment(
) -> Option<ParseTreeFragment> {
let mut base = SessionSource::new(solc, config);

if base.clone().with_run_code(buffer).parse().is_ok() {
return Some(ParseTreeFragment::Function)
match base.clone().with_run_code(buffer).parse() {
Ok(_) => return Some(ParseTreeFragment::Function),
Err(e) => tracing::debug!(?e),
}
if base.clone().with_top_level_code(buffer).parse().is_ok() {
return Some(ParseTreeFragment::Contract)
match base.clone().with_top_level_code(buffer).parse() {
Ok(_) => return Some(ParseTreeFragment::Contract),
Err(e) => tracing::debug!(?e),
}
if base.with_global_code(buffer).parse().is_ok() {
return Some(ParseTreeFragment::Source)
match base.with_global_code(buffer).parse() {
Ok(_) => return Some(ParseTreeFragment::Source),
Err(e) => tracing::debug!(?e),
}

None
Expand Down
6 changes: 1 addition & 5 deletions crates/chisel/tests/cache.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use chisel::session::ChiselSession;
use foundry_compilers::EvmVersion;
use foundry_config::Config;
use foundry_evm::opts::EvmOpts;
use serial_test::serial;
use std::path::Path;

Expand Down Expand Up @@ -43,10 +42,7 @@ fn test_write_session() {
// Create a new session
let mut env = ChiselSession::new(chisel::session_source::SessionSourceConfig {
foundry_config,
evm_opts: EvmOpts::default(),
backend: None,
traces: false,
calldata: None,
..Default::default()
})
.unwrap_or_else(|e| panic!("Failed to create ChiselSession!, {}", e));

Expand Down
7 changes: 4 additions & 3 deletions crates/evm/core/src/fork/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,13 +174,14 @@ where
entry.get_mut().push(listener);
}
Entry::Vacant(entry) => {
trace!(target: "backendhandler", "preparing storage request, address={:?}, idx={}", address, idx);
trace!(target: "backendhandler", %address, %idx, "preparing storage request");
entry.insert(vec![listener]);
let provider = self.provider.clone();
let block_id = self.block_id;
let fut = Box::pin(async move {
let storage = provider.get_storage_at(address, idx, block_id).await;
(storage.wrap_err("could not fetch slot {idx} from {address}"), address, idx)
let storage =
provider.get_storage_at(address, idx, block_id).await.map_err(Into::into);
(storage, address, idx)
});
self.pending_requests.push(ProviderRequest::Storage(fut));
}
Expand Down
4 changes: 2 additions & 2 deletions testdata/cheats/Vm.sol

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 6b2633c

Please sign in to comment.