Skip to content

Commit

Permalink
refactor: evm instantiation (gakonst#323)
Browse files Browse the repository at this point in the history
* refactor(cli): move compiler opts to intermediate struct

* chore: expose test evm type

* refactor(cli): move evm instantiation params to helper funcs

* chore: remove dangling `run` references

* chore: fmt cargo

* chore: correct evmodin instantiations

* chore: remove more dangling run refs
  • Loading branch information
gakonst authored Dec 28, 2021
1 parent 96fc847 commit f9a0375
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 139 deletions.
3 changes: 2 additions & 1 deletion cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ openssl = ["ethers/openssl"]

sputnik-evm = [
"sputnik",
"evm-adapters/sputnik"
"evm-adapters/sputnik",
"evm-adapters/sputnik-helpers",
]

evmodin-evm = [
Expand Down
23 changes: 9 additions & 14 deletions cli/src/cmd/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ use ethers::{
solc::{
artifacts::{Optimizer, Settings},
remappings::Remapping,
EvmVersion, MinimalCombinedArtifacts, Project, ProjectCompileOutput, ProjectPathsConfig,
SolcConfig,
MinimalCombinedArtifacts, Project, ProjectCompileOutput, ProjectPathsConfig, SolcConfig,
},
types::Address,
};
Expand All @@ -15,7 +14,7 @@ use std::{
str::FromStr,
};

use crate::{cmd::Cmd, utils};
use crate::{cmd::Cmd, opts::forge::CompilerArgs, utils};

#[cfg(feature = "evmodin-evm")]
use evmodin::util::mocked_host::MockedHost;
Expand Down Expand Up @@ -50,14 +49,8 @@ pub struct BuildArgs {
#[structopt(help = "path to where the contract artifacts are stored", long = "out", short)]
pub out_path: Option<PathBuf>,

#[structopt(help = "choose the evm version", long, default_value = "london")]
pub evm_version: EvmVersion,

#[structopt(help = "activate the solidity optimizer", long)]
pub optimize: bool,

#[structopt(help = "optimizer parameter runs", long, default_value = "200")]
pub optimize_runs: u32,
#[structopt(flatten)]
pub compiler: CompilerArgs,

#[structopt(
help = "if set to true, skips auto-detecting solc and uses what is in the user's $PATH ",
Expand Down Expand Up @@ -218,8 +211,10 @@ impl BuildArgs {

let paths = paths_builder.build()?;

let optimizer =
Optimizer { enabled: Some(self.optimize), runs: Some(self.optimize_runs as usize) };
let optimizer = Optimizer {
enabled: Some(self.compiler.optimize),
runs: Some(self.compiler.optimize_runs as usize),
};

// unflatten the libraries
let mut libraries = BTreeMap::default();
Expand All @@ -234,7 +229,7 @@ impl BuildArgs {
// build the project w/ allowed paths = root and all the libs
let solc_settings = Settings {
optimizer,
evm_version: Some(self.evm_version),
evm_version: Some(self.compiler.evm_version),
libraries,
..Default::default()
};
Expand Down
133 changes: 17 additions & 116 deletions cli/src/cmd/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,17 @@
use crate::{
cmd::{
build::{BuildArgs, Env, EvmType},
build::{BuildArgs, EvmType},
Cmd,
},
opts::forge::EvmOpts,
utils,
};
use ansi_term::Colour;
use ethers::{
providers::Provider,
solc::{ArtifactOutput, Project},
types::{Address, U256},
};
use evm_adapters::FAUCET_ACCOUNT;
use ethers::solc::{ArtifactOutput, Project};
use forge::MultiContractRunnerBuilder;
use regex::Regex;
use std::{collections::BTreeMap, convert::TryFrom, sync::Arc};
use std::collections::BTreeMap;
use structopt::StructOpt;

#[derive(Debug, Clone, StructOpt)]
Expand All @@ -25,7 +21,7 @@ pub struct TestArgs {
json: bool,

#[structopt(flatten)]
env: Env,
evm_opts: EvmOpts,

#[structopt(
long = "--match",
Expand All @@ -38,47 +34,6 @@ pub struct TestArgs {
#[structopt(flatten)]
opts: BuildArgs,

#[structopt(
long,
short,
help = "the EVM type you want to use (e.g. sputnik, evmodin)",
default_value = "sputnik"
)]
evm_type: EvmType,

#[structopt(
help = "fetch state over a remote instead of starting from empty state",
long,
short
)]
#[structopt(alias = "rpc-url")]
fork_url: Option<String>,

#[structopt(help = "pins the block number for the state fork", long)]
#[structopt(env = "DAPP_FORK_BLOCK")]
fork_block_number: Option<u64>,

#[structopt(
help = "the initial balance of each deployed test contract",
long,
default_value = "0xffffffffffffffffffffffff"
)]
initial_balance: U256,

#[structopt(
help = "the address which will be executing all tests",
long,
default_value = "0x0000000000000000000000000000000000000000",
env = "DAPP_TEST_ADDRESS"
)]
sender: Address,

#[structopt(help = "enables the FFI cheatcode", long)]
ffi: bool,

#[structopt(help = "verbosity of 'forge test' output (0-3)", long, default_value = "0")]
verbosity: u8,

#[structopt(
help = "if set to true, the process will exit with an exit code = 0, even if the tests fail",
long,
Expand All @@ -91,20 +46,7 @@ impl Cmd for TestArgs {
type Output = TestOutcome;

fn run(self) -> eyre::Result<Self::Output> {
let TestArgs {
opts,
env,
json,
pattern,
evm_type,
fork_url,
fork_block_number,
initial_balance,
sender,
ffi,
verbosity,
allow_failure,
} = self;
let TestArgs { opts, evm_opts, json, pattern, allow_failure } = self;
// Setup the fuzzer
// TODO: Add CLI Options to modify the persistence
let cfg = proptest::test_runner::Config { failure_persistence: None, ..Default::default() };
Expand All @@ -116,72 +58,31 @@ impl Cmd for TestArgs {
// prepare the test builder
let builder = MultiContractRunnerBuilder::default()
.fuzzer(fuzzer)
.initial_balance(initial_balance)
.sender(sender);
.initial_balance(evm_opts.initial_balance)
.sender(evm_opts.sender);

// run the tests depending on the chosen EVM
match evm_type {
match evm_opts.evm_type {
#[cfg(feature = "sputnik-evm")]
EvmType::Sputnik => {
use evm_adapters::sputnik::{
vicinity, Executor, ForkMemoryBackend, PRECOMPILES_MAP,
};
use sputnik::backend::{Backend, MemoryBackend};
let mut cfg = utils::sputnik_cfg(opts.evm_version);

// We disable the contract size limit by default, because Solidity
// test smart contracts are likely to be >24kb
cfg.create_contract_limit = None;

let vicinity = if let Some(ref url) = fork_url {
let provider = Provider::try_from(url.as_str())?;
let rt = tokio::runtime::Runtime::new().expect("could not start tokio rt");
rt.block_on(vicinity(&provider, fork_block_number))?
} else {
env.sputnik_state()
};
let mut backend = MemoryBackend::new(&vicinity, Default::default());
// max out the balance of the faucet
let faucet =
backend.state_mut().entry(*FAUCET_ACCOUNT).or_insert_with(Default::default);
faucet.balance = U256::MAX;

let backend: Box<dyn Backend> = if let Some(ref url) = fork_url {
let provider = Provider::try_from(url.as_str())?;
let init_state = backend.state().clone();
let backend =
ForkMemoryBackend::new(provider, backend, fork_block_number, init_state);
Box::new(backend)
} else {
Box::new(backend)
};
let backend = Arc::new(backend);

let precompiles = PRECOMPILES_MAP.clone();
let evm = Executor::new_with_cheatcodes(
backend,
env.gas_limit,
&cfg,
&precompiles,
ffi,
verbosity > 2,
);

test(builder, project, evm, pattern, json, verbosity, allow_failure)
let mut cfg = utils::sputnik_cfg(opts.compiler.evm_version);
let vicinity = evm_opts.vicinity()?;
let evm = utils::sputnik_helpers::evm(&evm_opts, &mut cfg, &vicinity)?;
test(builder, project, evm, pattern, json, evm_opts.verbosity, allow_failure)
}
#[cfg(feature = "evmodin-evm")]
EvmType::EvmOdin => {
use evm_adapters::evmodin::EvmOdin;
use evmodin::tracing::NoopTracer;

let revision = utils::evmodin_cfg(opts.evm_version);
let revision = utils::evmodin_cfg(opts.compiler.evm_version);

// TODO: Replace this with a proper host. We'll want this to also be
// provided generically when we add the Forking host(s).
let host = env.evmodin_state();
let host = evm_opts.env.evmodin_state();

let evm = EvmOdin::new(host, env.gas_limit, revision, NoopTracer);
test(builder, project, evm, pattern, json, verbosity, allow_failure)
let evm = EvmOdin::new(host, evm_opts.env.gas_limit, revision, NoopTracer);
test(builder, project, evm, pattern, json, evm_opts.verbosity, allow_failure)
}
}
}
Expand Down
86 changes: 79 additions & 7 deletions cli/src/opts/forge.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use structopt::StructOpt;

use ethers::types::Address;
use ethers::{solc::EvmVersion, types::Address};
use std::{path::PathBuf, str::FromStr};

use crate::cmd::{build::BuildArgs, create, snapshot, test};
use crate::cmd::{build::BuildArgs, create::CreateArgs, snapshot, test};

#[derive(Debug, StructOpt)]
pub struct Opts {
Expand Down Expand Up @@ -40,10 +40,7 @@ pub enum Subcommands {
dependencies: Vec<Dependency>,
},

#[structopt(
alias = "r",
about = "prints the automatically inferred remappings for this repository"
)]
#[structopt(about = "prints the automatically inferred remappings for this repository")]
Remappings {
#[structopt(
help = "the project's root path, default being the current working directory",
Expand All @@ -67,7 +64,7 @@ pub enum Subcommands {
},

#[structopt(alias = "c", about = "deploy a compiled contract")]
Create(create::CreateArgs),
Create(CreateArgs),

#[structopt(alias = "i", about = "initializes a new forge sample repository")]
Init {
Expand Down Expand Up @@ -96,6 +93,81 @@ pub enum Subcommands {
Snapshot(snapshot::SnapshotArgs),
}

#[derive(Debug, Clone, StructOpt)]
pub struct CompilerArgs {
#[structopt(help = "choose the evm version", long, default_value = "london")]
pub evm_version: EvmVersion,

#[structopt(help = "activate the solidity optimizer", long)]
pub optimize: bool,

#[structopt(help = "optimizer parameter runs", long, default_value = "200")]
pub optimize_runs: u32,
}

use crate::cmd::build::{Env, EvmType};
use ethers::types::U256;

#[derive(Debug, Clone, StructOpt)]
pub struct EvmOpts {
#[structopt(flatten)]
pub env: Env,

#[structopt(
long,
short,
help = "the EVM type you want to use (e.g. sputnik, evmodin)",
default_value = "sputnik"
)]
pub evm_type: EvmType,

#[structopt(
help = "fetch state over a remote instead of starting from empty state",
long,
short
)]
#[structopt(alias = "rpc-url")]
pub fork_url: Option<String>,

#[structopt(help = "pins the block number for the state fork", long)]
#[structopt(env = "DAPP_FORK_BLOCK")]
pub fork_block_number: Option<u64>,

#[structopt(
help = "the initial balance of each deployed test contract",
long,
default_value = "0xffffffffffffffffffffffff"
)]
pub initial_balance: U256,

#[structopt(
help = "the address which will be executing all tests",
long,
default_value = "0x0000000000000000000000000000000000000000",
env = "DAPP_TEST_ADDRESS"
)]
pub sender: Address,

#[structopt(help = "enables the FFI cheatcode", long)]
pub ffi: bool,

#[structopt(help = "verbosity of EVM output (0-3)", long, default_value = "0")]
pub verbosity: u8,
}

impl EvmOpts {
#[cfg(feature = "sputnik-evm")]
pub fn vicinity(&self) -> eyre::Result<sputnik::backend::MemoryVicinity> {
Ok(if let Some(ref url) = self.fork_url {
let provider = ethers::providers::Provider::try_from(url.as_str())?;
let rt = tokio::runtime::Runtime::new().expect("could not start tokio rt");
rt.block_on(evm_adapters::sputnik::vicinity(&provider, self.fork_block_number))?
} else {
self.env.sputnik_state()
})
}
}

/// Represents the common dapp argument pattern for `<path>:<contractname>` where `<path>:` is
/// optional.
#[derive(Clone, Debug)]
Expand Down
Loading

0 comments on commit f9a0375

Please sign in to comment.