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

fix: exclude empty artifacts from ContractsByArtifact #7713

Merged
merged 5 commits into from
Apr 18, 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
11 changes: 7 additions & 4 deletions crates/cheatcodes/src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,11 +355,14 @@ fn get_artifact_code(state: &Cheatcodes, path: &str, deployed: bool) -> Result<B
}
}?;

if deployed {
return Ok(artifact.1.deployed_bytecode.clone())
let maybe_bytecode = if deployed {
artifact.1.deployed_bytecode.clone()
} else {
return Ok(artifact.1.bytecode.clone())
}
artifact.1.bytecode.clone()
};

return maybe_bytecode
.ok_or_else(|| fmt_err!("No bytecode for contract. Is it abstract or unlinked?"));
} else {
match (file.map(|f| f.to_string_lossy().to_string()), contract_name) {
(Some(file), Some(contract_name)) => {
Expand Down
25 changes: 20 additions & 5 deletions crates/common/src/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ pub struct ContractData {
/// Contract ABI.
pub abi: JsonAbi,
/// Contract creation code.
pub bytecode: Bytes,
pub bytecode: Option<Bytes>,
/// Contract runtime code.
pub deployed_bytecode: Bytes,
pub deployed_bytecode: Option<Bytes>,
}

type ArtifactWithContractRef<'a> = (&'a ArtifactId, &'a ContractData);
Expand All @@ -45,6 +45,12 @@ impl ContractsByArtifact {
let bytecode = artifact.bytecode.and_then(|b| b.into_bytes())?;
let deployed_bytecode =
artifact.deployed_bytecode.and_then(|b| b.into_bytes())?;

// Exclude artifacts with present but empty bytecode. Such artifacts are usually
// interfaces and abstract contracts.
let bytecode = (bytecode.len() > 0).then_some(bytecode);
let deployed_bytecode =
(deployed_bytecode.len() > 0).then_some(deployed_bytecode);
let abi = artifact.abi?;

Some((id, ContractData { name, abi, bytecode, deployed_bytecode }))
Expand All @@ -55,14 +61,23 @@ impl ContractsByArtifact {

/// Finds a contract which has a similar bytecode as `code`.
pub fn find_by_creation_code(&self, code: &[u8]) -> Option<ArtifactWithContractRef> {
self.iter()
.find(|(_, contract)| bytecode_diff_score(contract.bytecode.as_ref(), code) <= 0.1)
self.iter().find(|(_, contract)| {
if let Some(bytecode) = &contract.bytecode {
bytecode_diff_score(bytecode.as_ref(), code) <= 0.1
} else {
false
}
})
}

/// Finds a contract which has a similar deployed bytecode as `code`.
pub fn find_by_deployed_code(&self, code: &[u8]) -> Option<ArtifactWithContractRef> {
self.iter().find(|(_, contract)| {
bytecode_diff_score(contract.deployed_bytecode.as_ref(), code) <= 0.1
if let Some(deployed_bytecode) = &contract.deployed_bytecode {
bytecode_diff_score(deployed_bytecode.as_ref(), code) <= 0.1
} else {
false
}
})
}

Expand Down
21 changes: 12 additions & 9 deletions crates/evm/traces/src/identifier/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ impl<'a> LocalTraceIdentifier<'a> {
pub fn new(known_contracts: &'a ContractsByArtifact) -> Self {
let mut ordered_ids = known_contracts
.iter()
.map(|(id, contract)| (id, contract.deployed_bytecode.len()))
.filter_map(|(id, contract)| Some((id, contract.deployed_bytecode.as_ref()?)))
.map(|(id, bytecode)| (id, bytecode.len()))
.collect::<Vec<_>>();
ordered_ids.sort_by_key(|(_, len)| *len);
Self { known_contracts, ordered_ids }
Expand All @@ -40,14 +41,16 @@ impl<'a> LocalTraceIdentifier<'a> {

let mut check = |id| {
let contract = self.known_contracts.get(id)?;
let score = bytecode_diff_score(&contract.deployed_bytecode, code);
if score == 0.0 {
trace!(target: "evm::traces", "found exact match");
return Some((id, &contract.abi));
}
if score < min_score {
min_score = score;
min_score_id = Some((id, &contract.abi));
if let Some(deployed_bytecode) = &contract.deployed_bytecode {
let score = bytecode_diff_score(deployed_bytecode, code);
if score == 0.0 {
trace!(target: "evm::traces", "found exact match");
return Some((id, &contract.abi));
}
if score < min_score {
min_score = score;
min_score_id = Some((id, &contract.abi));
}
}
None
};
Expand Down
4 changes: 3 additions & 1 deletion crates/script/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use alloy_primitives::{Address, Bytes};
use alloy_provider::Provider;
use alloy_rpc_types::request::TransactionRequest;
use async_recursion::async_recursion;
use eyre::Result;
use eyre::{OptionExt, Result};
use foundry_cheatcodes::ScriptWallets;
use foundry_cli::utils::{ensure_clean_constructor, needs_setup};
use foundry_common::{
Expand Down Expand Up @@ -63,6 +63,8 @@ impl LinkedState {

let ContractData { abi, bytecode, .. } = build_data.get_target_contract()?;

let bytecode = bytecode.ok_or_eyre("target contract has no bytecode")?;

let (func, calldata) = args.get_method_and_calldata(&abi)?;

ensure_clean_constructor(&abi)?;
Expand Down
8 changes: 3 additions & 5 deletions crates/script/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,11 +362,9 @@ impl ScriptArgs {

// From artifacts
for (artifact, contract) in known_contracts.iter() {
bytecodes.push((
artifact.name.clone(),
&contract.bytecode,
&contract.deployed_bytecode,
));
let Some(bytecode) = &contract.bytecode else { continue };
let Some(deployed_bytecode) = &contract.deployed_bytecode else { continue };
bytecodes.push((artifact.name.clone(), bytecode, deployed_bytecode));
}

// From traces
Expand Down
5 changes: 3 additions & 2 deletions crates/script/src/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ impl TransactionWithMetadata {

let Some(data) = self.transaction.input.input() else { return Ok(()) };
let Some(info) = info else { return Ok(()) };
let Some(bytecode) = info.bytecode.as_ref() else { return Ok(()) };

// `create2` transactions are prefixed by a 32 byte salt.
let creation_code = if is_create2 {
Expand All @@ -143,11 +144,11 @@ impl TransactionWithMetadata {
};

// The constructor args start after bytecode.
let contains_constructor_args = creation_code.len() > info.bytecode.len();
let contains_constructor_args = creation_code.len() > bytecode.len();
if !contains_constructor_args {
return Ok(());
}
let constructor_args = &creation_code[info.bytecode.len()..];
let constructor_args = &creation_code[bytecode.len()..];

let Some(constructor) = info.abi.constructor() else { return Ok(()) };
let values = constructor.abi_decode_input(constructor_args, false).map_err(|e| {
Expand Down
6 changes: 3 additions & 3 deletions crates/script/src/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,11 @@ impl VerifyBundle {
libraries: &[String],
) -> Option<VerifyArgs> {
for (artifact, contract) in self.known_contracts.iter() {
let Some(bytecode) = contract.bytecode.as_ref() else { continue };
// If it's a CREATE2, the tx.data comes with a 32-byte salt in the beginning
// of the transaction
if data.split_at(create2_offset).1.starts_with(&contract.bytecode) {
let constructor_args =
data.split_at(create2_offset + contract.bytecode.len()).1.to_vec();
if data.split_at(create2_offset).1.starts_with(bytecode) {
let constructor_args = data.split_at(create2_offset + bytecode.len()).1.to_vec();

let contract = ContractInfo {
path: Some(
Expand Down
Loading