Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 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
42 changes: 22 additions & 20 deletions Cargo.lock

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

46 changes: 12 additions & 34 deletions crates/cli/src/utils/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ use ethers::{
abi::Abi,
core::types::Chain,
solc::{
artifacts::{CompactBytecode, CompactDeployedBytecode, ContractBytecodeSome},
artifacts::{CompactBytecode, CompactDeployedBytecode},
cache::{CacheEntry, SolFilesCache},
info::ContractInfo,
utils::read_json_file,
Artifact, ArtifactId, ProjectCompileOutput,
Artifact, ProjectCompileOutput,
},
};
use eyre::{Result, WrapErr};
Expand All @@ -20,9 +20,9 @@ use foundry_evm::{
CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, Traces,
},
};
use std::{collections::BTreeMap, fmt::Write, path::PathBuf, str::FromStr};
use std::{fmt::Write, path::PathBuf, str::FromStr};
use tracing::trace;
use ui::{TUIExitReason, Tui, Ui};
use ui::DebuggerArgs;
use yansi::Paint;

/// Given a `Project`'s output, removes the matching ABI, Bytecode and
Expand Down Expand Up @@ -391,8 +391,14 @@ pub async fn handle_traces(
}

if debug {
let (sources, bytecode) = etherscan_identifier.get_compiled_contracts().await?;
run_debugger(result, decoder, bytecode, sources)?;
let (sources, _compiled_contracts) = etherscan_identifier.get_compiled_contracts().await?;
let debugger = DebuggerArgs {
debug: vec![result.debug],
decoder: &decoder,
sources,
breakpoints: Default::default(),
};
debugger.run()?;
} else {
print_traces(&mut result, &decoder, verbose).await?;
}
Expand Down Expand Up @@ -429,31 +435,3 @@ pub async fn print_traces(
println!("Gas used: {}", result.gas_used);
Ok(())
}

pub fn run_debugger(
result: TraceResult,
decoder: CallTraceDecoder,
known_contracts: BTreeMap<ArtifactId, ContractBytecodeSome>,
sources: BTreeMap<ArtifactId, String>,
) -> Result<()> {
let calls: Vec<DebugArena> = vec![result.debug];
let flattened = calls.last().expect("we should have collected debug info").flatten(0);
let tui = Tui::new(
flattened,
0,
decoder.contracts,
known_contracts.into_iter().map(|(id, artifact)| (id.name, artifact)).collect(),
sources
.into_iter()
.map(|(id, source)| {
let mut sources = BTreeMap::new();
sources.insert(0, source);
(id.name, sources)
})
.collect(),
Default::default(),
)?;
match tui.start().expect("Failed to start tui") {
TUIExitReason::CharExit => Ok(()),
}
}
19 changes: 9 additions & 10 deletions crates/common/src/compile.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! Support for compiling [ethers::solc::Project]
use crate::{glob::GlobMatcher, term, TestFunctionExt};
use crate::{compact_to_contract, glob::GlobMatcher, term, TestFunctionExt};
use comfy_table::{presets::ASCII_MARKDOWN, *};
use ethers_etherscan::contract::Metadata;
use ethers_solc::{
Expand Down Expand Up @@ -401,7 +401,7 @@ pub fn compile_target_with_filter(
/// Creates and compiles a project from an Etherscan source.
pub async fn compile_from_source(
metadata: &Metadata,
) -> Result<(ArtifactId, ContractBytecodeSome)> {
) -> Result<(ArtifactId, u32, ContractBytecodeSome)> {
Comment thread
franfrandev marked this conversation as resolved.
Comment thread
franfrandev marked this conversation as resolved.
let root = tempfile::tempdir()?;
let root_path = root.path();
let project = etherscan_project(metadata, root_path)?;
Expand All @@ -412,19 +412,18 @@ pub async fn compile_from_source(
eyre::bail!(project_output.to_string())
}

let (artifact_id, contract) = project_output
.into_contract_bytecodes()
let (artifact_id, file_id, contract) = project_output
.into_artifacts()
.find(|(artifact_id, _)| artifact_id.name == metadata.contract_name)
.map(|(aid, art)| {
(aid, art.source_file().expect("no source file").id, art.into_contract_bytecode())
})
.expect("there should be a contract with bytecode");
let bytecode = ContractBytecodeSome {
abi: contract.abi.unwrap(),
bytecode: contract.bytecode.unwrap().into(),
deployed_bytecode: contract.deployed_bytecode.unwrap().into(),
};
let bytecode = compact_to_contract(contract);

root.close()?;

Ok((artifact_id, bytecode))
Ok((artifact_id, file_id, bytecode))
}

/// Creates a [Project] from an Etherscan source.
Expand Down
14 changes: 13 additions & 1 deletion crates/common/src/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ use ethers_core::{
types::{Address, H256},
utils::hex,
};
use ethers_solc::{artifacts::ContractBytecodeSome, ArtifactId, ProjectPathsConfig};
use ethers_solc::{
artifacts::{CompactContractBytecode, ContractBytecodeSome},
ArtifactId, ProjectPathsConfig,
};
use once_cell::sync::Lazy;
use regex::Regex;
use std::{
Expand Down Expand Up @@ -265,3 +268,12 @@ mod tests {
let _decoded = abi::decode(&params, args).unwrap();
}
}

/// Helper function to convert CompactContractBytecode ~> ContractBytecodeSome
pub fn compact_to_contract(contract: CompactContractBytecode) -> ContractBytecodeSome {
ContractBytecodeSome {
abi: contract.abi.unwrap(),
bytecode: contract.bytecode.unwrap().into(),
deployed_bytecode: contract.deployed_bytecode.unwrap().into(),
}
Comment thread
franfrandev marked this conversation as resolved.
Outdated
}
6 changes: 3 additions & 3 deletions crates/evm/src/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
use std::fmt::Display;

/// An arena of [DebugNode]s
#[derive(Default, Debug, Clone)]
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct DebugArena {
/// The arena of nodes
pub arena: Vec<DebugNode>,
Expand Down Expand Up @@ -78,7 +78,7 @@ impl DebugArena {
}

/// A node in the arena
#[derive(Default, Debug, Clone)]
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub struct DebugNode {
/// Parent node index in the arena
pub parent: Option<usize>,
Expand Down Expand Up @@ -109,7 +109,7 @@ impl DebugNode {
/// It holds the current program counter (where in the program you are),
/// the stack and memory (prior to the opcodes execution), any bytes to be
/// pushed onto the stack, and the instruction counter for use with sourcemap.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DebugStep {
/// Stack *prior* to running the associated opcode
pub stack: Vec<U256>,
Expand Down
14 changes: 9 additions & 5 deletions crates/evm/src/fuzz/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,7 @@ impl<'a> FuzzedExecutor<'a> {
let coverage: RefCell<Option<HitMaps>> = RefCell::default();

// Stores fuzz state for use with [fuzz_calldata_from_state]
let state: EvmFuzzState = if let Some(fork_db) = self.executor.backend.active_fork_db() {
build_initial_state(fork_db, &self.config.dictionary)
} else {
build_initial_state(self.executor.backend.mem_db(), &self.config.dictionary)
};
let state = self.build_fuzz_state();

let mut weights = vec![];
let dictionary_weight = self.config.dictionary.dictionary_weight.min(100);
Expand Down Expand Up @@ -248,6 +244,14 @@ impl<'a> FuzzedExecutor<'a> {
}))
}
}

pub fn build_fuzz_state(&self) -> EvmFuzzState {
Comment thread
franfrandev marked this conversation as resolved.
if let Some(fork_db) = self.executor.backend.active_fork_db() {
build_initial_state(fork_db, &self.config.dictionary)
} else {
build_initial_state(self.executor.backend.mem_db(), &self.config.dictionary)
}
}
}

#[derive(Clone, Debug, Serialize, Deserialize)]
Expand Down
19 changes: 12 additions & 7 deletions crates/evm/src/trace/identifier/etherscan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use futures::{
};
use std::{
borrow::Cow,
collections::BTreeMap,
collections::{BTreeMap, HashMap},
pin::Pin,
sync::{
atomic::{AtomicBool, Ordering},
Expand Down Expand Up @@ -60,10 +60,14 @@ impl EtherscanIdentifier {
/// Etherscan and compiles them locally, for usage in the debugger.
pub async fn get_compiled_contracts(
&self,
) -> eyre::Result<(BTreeMap<ArtifactId, String>, BTreeMap<ArtifactId, ContractBytecodeSome>)>
{
) -> eyre::Result<(
// TODO should use `ContractSources` but has circular import.
Comment thread
franfrandev marked this conversation as resolved.
Outdated
// Maybe move it lower
HashMap<String, HashMap<u32, (String, ContractBytecodeSome)>>,
BTreeMap<ArtifactId, ContractBytecodeSome>,
)> {
let mut compiled_contracts = BTreeMap::new();
let mut sources = BTreeMap::new();
let mut sources = HashMap::new();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shuffles ordering—is ordering an issue here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need ordering, so there should be better performances with a HashMap
Can leave as-is if not appriopriate for this PR


// TODO: Add caching so we dont double-fetch contracts.
let contracts_iter = self
Expand All @@ -90,9 +94,10 @@ impl EtherscanIdentifier {
// construct the map
for (results, (_, metadata)) in artifacts.into_iter().zip(contracts_iter) {
// get the inner type
let (artifact_id, bytecode) = results?;
compiled_contracts.insert(artifact_id.clone(), bytecode);
sources.insert(artifact_id, metadata.source_code());
let (artifact_id, file_id, bytecode) = results?;
compiled_contracts.insert(artifact_id.clone(), bytecode.clone());
let inner_map = sources.entry(artifact_id.clone().name).or_insert_with(HashMap::new);
inner_map.insert(file_id, (metadata.source_code(), bytecode));
}

Ok((sources, compiled_contracts))
Expand Down
7 changes: 3 additions & 4 deletions crates/forge/bin/cmd/debug.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use super::{build::BuildArgs, retry::RETRY_VERIFY_ON_CREATE, script::ScriptArgs};
use clap::{Parser, ValueHint};
use eyre::Result;
use foundry_cli::opts::CoreBuildArgs;
use foundry_common::evm::{Breakpoints, EvmArgs};
use foundry_common::evm::EvmArgs;
use std::path::PathBuf;

// Loads project's figment and merges the build cli arguments into it
Expand Down Expand Up @@ -41,7 +40,7 @@ pub struct DebugArgs {
}

impl DebugArgs {
pub async fn debug(self, breakpoints: Breakpoints) -> Result<()> {
pub async fn run(self) -> eyre::Result<()> {
let script = ScriptArgs {
path: self.path.to_str().expect("Invalid path string.").to_string(),
args: self.args,
Expand All @@ -54,6 +53,6 @@ impl DebugArgs {
retry: RETRY_VERIFY_ON_CREATE,
..Default::default()
};
script.run_script(breakpoints).await
script.run_script().await
}
}
Loading