Skip to content
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

chore: error instead of panic on create fork failure #6852

Merged
merged 1 commit into from
Jan 19, 2024
Merged
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
192 changes: 93 additions & 99 deletions crates/forge/bin/cmd/script/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use forge::{
backend::Backend,
executors::ExecutorBuilder,
inspectors::{cheatcodes::BroadcastableTransactions, CheatsConfig},
traces::{CallTraceDecoder, Traces},
traces::CallTraceDecoder,
};
use foundry_cli::utils::{ensure_clean_constructor, needs_setup};
use foundry_common::{
Expand All @@ -24,9 +24,6 @@ use futures::future::join_all;
use parking_lot::RwLock;
use std::{collections::VecDeque, sync::Arc};

/// Helper alias type for the processed result of a runner onchain simulation.
type RunnerResult = (Option<TransactionWithMetadata>, Traces);

impl ScriptArgs {
/// Locally deploys and executes the contract method that will collect all broadcastable
/// transactions.
Expand All @@ -51,7 +48,7 @@ impl ScriptArgs {

ensure_clean_constructor(&abi)?;

let mut runner = self.prepare_runner(script_config, sender, SimulationStage::Local).await;
let mut runner = self.prepare_runner(script_config, sender, SimulationStage::Local).await?;
let (address, mut result) = runner.setup(
predeploy_libraries,
bytecode,
Expand Down Expand Up @@ -105,7 +102,7 @@ impl ScriptArgs {

let runners = Arc::new(
self.build_runners(script_config)
.await
.await?
.into_iter()
.map(|(rpc, runner)| (rpc, Arc::new(RwLock::new(runner))))
.collect::<HashMap<_, _>>(),
Expand Down Expand Up @@ -139,87 +136,81 @@ impl ScriptArgs {
let mut final_txs = VecDeque::new();

// Executes all transactions from the different forks concurrently.
let futs =
transactions
.into_iter()
.map(|transaction| async {
let mut runner = runners
.get(transaction.rpc.as_ref().expect("to have been filled already."))
.expect("to have been built.")
.write();

if let TypedTransaction::Legacy(mut tx) = transaction.transaction {
let result = runner
.simulate(
tx.from.expect(
"Transaction doesn't have a `from` address at execution time",
).to_alloy(),
tx.to.clone(),
tx.data.clone().map(|b| b.to_alloy()),
tx.value.map(|v| v.to_alloy()),
)
.wrap_err("Internal EVM error during simulation")?;

if !result.success || result.traces.is_empty() {
return Ok((None, result.traces));
}

let created_contracts = result
.traces
.iter()
.flat_map(|(_, traces)| {
traces.nodes().iter().filter_map(|node| {
if node.trace.kind.is_any_create() {
return Some(AdditionalContract {
opcode: node.trace.kind,
address: node.trace.address,
init_code: node.trace.data.clone(),
});
}
None
})
})
.collect();

// Simulate mining the transaction if the user passes `--slow`.
if self.slow {
runner.executor.env.block.number += U256::from(1);
}

let is_fixed_gas_limit = tx.gas.is_some();
// If tx.gas is already set that means it was specified in script
if !is_fixed_gas_limit {
// We inflate the gas used by the user specified percentage
tx.gas = Some(
U256::from(result.gas_used * self.gas_estimate_multiplier / 100)
.to_ethers(),
);
} else {
println!("Gas limit was set in script to {:}", tx.gas.unwrap());
}

let tx = TransactionWithMetadata::new(
tx.into(),
transaction.rpc,
&result,
&address_to_abi,
decoder,
created_contracts,
is_fixed_gas_limit,
)?;

Ok((Some(tx), result.traces))
} else {
unreachable!()
let futs = transactions
.into_iter()
.map(|transaction| async {
let rpc = transaction.rpc.as_ref().expect("missing broadcastable tx rpc url");
let mut runner = runners.get(rpc).expect("invalid rpc url").write();

let TypedTransaction::Legacy(mut tx) = transaction.transaction else {
unreachable!()
};
let result = runner
.simulate(
tx.from
.expect("transaction doesn't have a `from` address at execution time")
.to_alloy(),
tx.to.clone(),
tx.data.clone().map(|b| b.to_alloy()),
tx.value.map(|v| v.to_alloy()),
)
.wrap_err("Internal EVM error during simulation")?;

if !result.success || result.traces.is_empty() {
return Ok((None, result.traces));
}

let created_contracts = result
.traces
.iter()
.flat_map(|(_, traces)| {
traces.nodes().iter().filter_map(|node| {
if node.trace.kind.is_any_create() {
return Some(AdditionalContract {
opcode: node.trace.kind,
address: node.trace.address,
init_code: node.trace.data.clone(),
});
}
None
})
})
.collect();

// Simulate mining the transaction if the user passes `--slow`.
if self.slow {
runner.executor.env.block.number += U256::from(1);
}

let is_fixed_gas_limit = tx.gas.is_some();
match tx.gas {
// If tx.gas is already set that means it was specified in script
Some(gas) => {
println!("Gas limit was set in script to {gas}");
}
})
.collect::<Vec<_>>();
// We inflate the gas used by the user specified percentage
None => {
let gas = U256::from(result.gas_used * self.gas_estimate_multiplier / 100);
tx.gas = Some(gas.to_ethers());
}
}

let tx = TransactionWithMetadata::new(
tx.into(),
transaction.rpc,
&result,
&address_to_abi,
decoder,
created_contracts,
is_fixed_gas_limit,
)?;

eyre::Ok((Some(tx), result.traces))
})
.collect::<Vec<_>>();

let mut abort = false;
for res in join_all(futs).await {
// type hint
let res: Result<RunnerResult> = res;

let (tx, traces) = res?;

// Transaction will be `None`, if execution didn't pass.
Expand Down Expand Up @@ -251,7 +242,10 @@ impl ScriptArgs {
}

/// Build the multiple runners from different forks.
async fn build_runners(&self, script_config: &ScriptConfig) -> HashMap<RpcUrl, ScriptRunner> {
async fn build_runners(
&self,
script_config: &ScriptConfig,
) -> Result<HashMap<RpcUrl, ScriptRunner>> {
let sender = script_config.evm_opts.sender;

if !shell::verbosity().is_silent() {
Expand All @@ -266,11 +260,10 @@ impl ScriptArgs {
.map(|rpc| async {
let mut script_config = script_config.clone();
script_config.evm_opts.fork_url = Some(rpc.clone());

(
rpc.clone(),
self.prepare_runner(&mut script_config, sender, SimulationStage::OnChain).await,
)
let runner = self
.prepare_runner(&mut script_config, sender, SimulationStage::OnChain)
.await?;
Ok((rpc.clone(), runner))
})
.collect::<Vec<_>>();

Expand All @@ -283,22 +276,19 @@ impl ScriptArgs {
script_config: &mut ScriptConfig,
sender: Address,
stage: SimulationStage,
) -> ScriptRunner {
) -> Result<ScriptRunner> {
trace!("preparing script runner");
let env =
script_config.evm_opts.evm_env().await.expect("Could not instantiate fork environment");
let env = script_config.evm_opts.evm_env().await?;

// The db backend that serves all the data.
let db = match &script_config.evm_opts.fork_url {
Some(url) => match script_config.backends.get(url) {
Some(db) => db.clone(),
None => {
let backend = Backend::spawn(
script_config.evm_opts.get_fork(&script_config.config, env.clone()),
)
.await;
script_config.backends.insert(url.clone(), backend);
script_config.backends.get(url).unwrap().clone()
let fork = script_config.evm_opts.get_fork(&script_config.config, env.clone());
let backend = Backend::spawn(fork).await;
script_config.backends.insert(url.clone(), backend.clone());
backend
}
},
None => {
Expand All @@ -324,6 +314,10 @@ impl ScriptArgs {
});
}

ScriptRunner::new(builder.build(env, db), script_config.evm_opts.initial_balance, sender)
Ok(ScriptRunner::new(
builder.build(env, db),
script_config.evm_opts.initial_balance,
sender,
))
}
}
9 changes: 4 additions & 5 deletions crates/forge/bin/cmd/verify/etherscan/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@ impl VerificationProvider for EtherscanVerificationProvider {

warn!("Failed verify submission: {:?}", resp);
eprintln!(
"Encountered an error verifying this contract:\nResponse: `{}`\nDetails: `{}`",
resp.message, resp.result
);
"Encountered an error verifying this contract:\nResponse: `{}`\nDetails: `{}`",
resp.message, resp.result
);
std::process::exit(1);
}

Expand All @@ -117,8 +117,7 @@ impl VerificationProvider for EtherscanVerificationProvider {

if let Some(resp) = resp {
println!(
"Submitted contract for verification:\n\tResponse: `{}`\n\tGUID: `{}`\n\tURL:
{}",
"Submitted contract for verification:\n\tResponse: `{}`\n\tGUID: `{}`\n\tURL: {}",
resp.message,
resp.result,
etherscan.address_url(args.address)
Expand Down