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

feat(traces): show state changes in cast run and forge test on -vvvvv #9013

Open
wants to merge 29 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
9bf8320
Add options for state changes output and json output in cast run command
cassc Oct 3, 2024
dceddb0
Merge remote-tracking branch 'foundry-rs/master' into add-options-to-…
cassc Oct 3, 2024
5546575
fix test
cassc Oct 3, 2024
28ef6a9
add back serde_json in Cargo.lock
cassc Oct 3, 2024
99a6914
format using nightly
cassc Oct 4, 2024
c6ca2b6
rename parameter
cassc Oct 4, 2024
68f5887
update revm-inspectors
cassc Oct 7, 2024
7bad232
Merge remote-tracking branch 'foundry-rs/master' into add-options-to-…
cassc Oct 7, 2024
4b6af4f
Merge remote-tracking branch 'foundry-rs/master' into add-options-to-…
cassc Oct 18, 2024
16da772
Merge remote-tracking branch 'foundry-rs/master' into add-options-to-…
cassc Oct 23, 2024
8b7807c
supress clippy warning and merge master
cassc Oct 23, 2024
5d66616
Merge remote-tracking branch 'foundry-rs/master' into add-options-to-…
cassc Oct 29, 2024
a0143fe
add serde_json
cassc Oct 29, 2024
e041926
disable some stdout print when --json option is used
cassc Nov 4, 2024
b52f8c8
remove unnecessary check
cassc Nov 5, 2024
73b0323
replace with sh_println
cassc Nov 5, 2024
4511b2a
Merge branch 'master' into add-options-to-output-storage-change-and-j…
cassc Nov 7, 2024
e9b1e3e
Merge remote-tracking branch 'foundry-rs/master' into add-options-to-…
cassc Nov 11, 2024
5420f26
Merge remote-tracking branch 'refs/remotes/origin/add-options-to-outp…
cassc Nov 11, 2024
d45b1b8
Merge branch 'master' into add-options-to-output-storage-change-and-j…
cassc Nov 13, 2024
0f983d9
Merge remote-tracking branch 'foundry-rs/master' into add-options-to-…
cassc Nov 15, 2024
9aa2894
Merge remote-tracking branch 'origin/add-options-to-output-storage-ch…
cassc Nov 15, 2024
cb48139
Merge branch 'master' into add-options-to-output-storage-change-and-j…
cassc Nov 18, 2024
f45e4cc
replace with shell::is_json
cassc Nov 19, 2024
af9ee80
Merge branch 'master' into add-options-to-output-storage-change-and-j…
cassc Nov 19, 2024
0e11203
merge master
cassc Nov 25, 2024
44fbe73
Merge branch 'master' into cast-storage-re
grandizzy Nov 27, 2024
ca94656
Show storage for verbosity > 1, add test
grandizzy Nov 27, 2024
85f9571
Change verbosity to > 4 for both cast and forge test, add test, fix ci
grandizzy Nov 27, 2024
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
1 change: 1 addition & 0 deletions Cargo.lock

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

14 changes: 11 additions & 3 deletions crates/cast/bin/cmd/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,15 @@ impl CallArgs {
env.cfg.disable_block_gas_limit = true;
env.block.gas_limit = U256::MAX;

let mut executor =
TracingExecutor::new(env, fork, evm_version, debug, decode_internal, alphanet);
let mut executor = TracingExecutor::new(
env,
fork,
evm_version,
debug,
decode_internal,
false,
alphanet,
);

let value = tx.value.unwrap_or_default();
let input = tx.inner.input.into_input().unwrap_or_default();
Expand All @@ -200,7 +207,8 @@ impl CallArgs {
),
};

handle_traces(trace, &config, chain, labels, debug, decode_internal, false).await?;
handle_traces(trace, &config, chain, labels, debug, decode_internal, false, false)
.await?;

return Ok(());
}
Expand Down
16 changes: 13 additions & 3 deletions crates/cast/bin/cmd/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ pub struct RunArgs {
#[arg(long, short)]
verbose: bool,

/// Prints the trace in JSON format.
#[arg(long, short)]
json: bool,
cassc marked this conversation as resolved.
Show resolved Hide resolved

/// Prints the state changes, only works with the `json` flag.
#[arg(long)]
with_state_changes: bool,
cassc marked this conversation as resolved.
Show resolved Hide resolved
zerosnacks marked this conversation as resolved.
Show resolved Hide resolved

/// Label addresses in the trace.
///
/// Example: 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045:vitalik.eth
Expand Down Expand Up @@ -163,6 +171,7 @@ impl RunArgs {
evm_version,
self.debug,
self.decode_internal,
self.with_state_changes,
alphanet,
);
let mut env =
Expand All @@ -177,15 +186,15 @@ impl RunArgs {
pb.set_position(0);

let BlockTransactions::Full(ref txs) = block.transactions else {
return Err(eyre::eyre!("Could not get block txs"))
return Err(eyre::eyre!("Could not get block txs"));
};

for (index, tx) in txs.iter().enumerate() {
// System transactions such as on L2s don't contain any pricing info so
// we skip them otherwise this would cause
// reverts
if is_known_system_sender(tx.from) ||
tx.transaction_type == Some(SYSTEM_TRANSACTION_TYPE)
if is_known_system_sender(tx.from)
|| tx.transaction_type == Some(SYSTEM_TRANSACTION_TYPE)
{
pb.set_position((index + 1) as u64);
continue;
Expand Down Expand Up @@ -250,6 +259,7 @@ impl RunArgs {
self.debug,
self.decode_internal,
self.verbose,
self.json,
)
.await?;

Expand Down
59 changes: 34 additions & 25 deletions crates/cli/src/utils/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ use foundry_evm::{
debug::DebugTraceIdentifier,
decode_trace_arena,
identifier::{EtherscanIdentifier, SignaturesIdentifier},
render_trace_arena_with_bytecodes, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind,
Traces,
render_trace_arena_inner, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, Traces,
},
};
use std::{
Expand Down Expand Up @@ -164,22 +163,22 @@ pub fn has_different_gas_calc(chain_id: u64) -> bool {
if let Some(chain) = Chain::from(chain_id).named() {
return matches!(
chain,
NamedChain::Acala |
NamedChain::AcalaMandalaTestnet |
NamedChain::AcalaTestnet |
NamedChain::Arbitrum |
NamedChain::ArbitrumGoerli |
NamedChain::ArbitrumSepolia |
NamedChain::ArbitrumTestnet |
NamedChain::Karura |
NamedChain::KaruraTestnet |
NamedChain::Mantle |
NamedChain::MantleSepolia |
NamedChain::MantleTestnet |
NamedChain::Moonbase |
NamedChain::Moonbeam |
NamedChain::MoonbeamDev |
NamedChain::Moonriver
NamedChain::Acala
| NamedChain::AcalaMandalaTestnet
| NamedChain::AcalaTestnet
| NamedChain::Arbitrum
| NamedChain::ArbitrumGoerli
| NamedChain::ArbitrumSepolia
| NamedChain::ArbitrumTestnet
| NamedChain::Karura
| NamedChain::KaruraTestnet
| NamedChain::Mantle
| NamedChain::MantleSepolia
| NamedChain::MantleTestnet
| NamedChain::Moonbase
| NamedChain::Moonbeam
| NamedChain::MoonbeamDev
| NamedChain::Moonriver
);
}
false
Expand All @@ -190,10 +189,10 @@ pub fn has_batch_support(chain_id: u64) -> bool {
if let Some(chain) = Chain::from(chain_id).named() {
return !matches!(
chain,
NamedChain::Arbitrum |
NamedChain::ArbitrumTestnet |
NamedChain::ArbitrumGoerli |
NamedChain::ArbitrumSepolia
NamedChain::Arbitrum
| NamedChain::ArbitrumTestnet
| NamedChain::ArbitrumGoerli
| NamedChain::ArbitrumSepolia
);
}
true
Expand Down Expand Up @@ -363,6 +362,7 @@ pub async fn handle_traces(
debug: bool,
decode_internal: bool,
verbose: bool,
json: bool,
) -> Result<()> {
let labels = labels.iter().filter_map(|label_str| {
let mut iter = label_str.split(':');
Expand Down Expand Up @@ -412,7 +412,7 @@ pub async fn handle_traces(
.build();
debugger.try_run()?;
} else {
print_traces(&mut result, &decoder, verbose).await?;
print_traces(&mut result, &decoder, verbose, json).await?;
}

Ok(())
Expand All @@ -422,14 +422,23 @@ pub async fn print_traces(
result: &mut TraceResult,
decoder: &CallTraceDecoder,
verbose: bool,
json: bool,
) -> Result<()> {
let traces = result.traces.as_mut().expect("No traces found");

println!("Traces:");
if !json {
println!("Traces:");
}

for (_, arena) in traces {
decode_trace_arena(arena, decoder).await?;
println!("{}", render_trace_arena_with_bytecodes(arena, verbose));
println!("{}", render_trace_arena_inner(arena, verbose, json));
}

if json {
return Ok(());
}

println!();

if result.success {
Expand Down
9 changes: 6 additions & 3 deletions crates/evm/evm/src/executors/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,18 @@ impl TracingExecutor {
version: Option<EvmVersion>,
debug: bool,
decode_internal: bool,
with_state_changes: bool,
alphanet: bool,
) -> Self {
let db = Backend::spawn(fork);
let trace_mode =
TraceMode::Call.with_debug(debug).with_decode_internal(if decode_internal {
let trace_mode = TraceMode::Call
.with_debug(debug)
.with_decode_internal(if decode_internal {
InternalTraceMode::Full
} else {
InternalTraceMode::None
});
})
.with_state_changes(with_state_changes);
Self {
// configures a bare version of the evm executor: no cheatcode inspector is enabled,
// tracing will be enabled only for the targeted transaction
Expand Down
1 change: 1 addition & 0 deletions crates/evm/traces/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ eyre.workspace = true
futures.workspace = true
itertools.workspace = true
serde.workspace = true
serde_json.workspace = true
tokio = { workspace = true, features = ["time", "macros"] }
tracing.workspace = true
rustc-hash.workspace = true
Expand Down
26 changes: 22 additions & 4 deletions crates/evm/traces/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,14 +180,18 @@ pub async fn decode_trace_arena(

/// Render a collection of call traces to a string.
pub fn render_trace_arena(arena: &SparsedTraceArena) -> String {
render_trace_arena_with_bytecodes(arena, false)
render_trace_arena_inner(arena, false, false)
}

/// Render a collection of call traces to a string optionally including contract creation bytecodes.
pub fn render_trace_arena_with_bytecodes(
/// Render a collection of call traces to a string optionally including contract creation bytecodes and in JSON format.
pub fn render_trace_arena_inner(
arena: &SparsedTraceArena,
with_bytecodes: bool,
as_json: bool,
) -> String {
if as_json {
return serde_json::to_string(&arena.resolve_arena()).expect("Failed to write traces");
}
let mut w = TraceWriter::new(Vec::<u8>::new()).write_bytecodes(with_bytecodes);
cassc marked this conversation as resolved.
Show resolved Hide resolved
w.write_arena(&arena.resolve_arena()).expect("Failed to write traces");
String::from_utf8(w.into_writer()).expect("trace writer wrote invalid UTF-8")
Expand Down Expand Up @@ -286,6 +290,8 @@ pub enum TraceMode {
///
/// Used by debugger.
Debug,
/// Debug trace with storage changes.
RecordStateDiff,
}

impl TraceMode {
Expand All @@ -305,6 +311,10 @@ impl TraceMode {
matches!(self, Self::Jump)
}

pub const fn record_state_diff(self) -> bool {
matches!(self, Self::RecordStateDiff)
}

pub const fn is_debug(self) -> bool {
matches!(self, Self::Debug)
}
Expand All @@ -321,6 +331,14 @@ impl TraceMode {
std::cmp::max(self, mode.into())
}

pub fn with_state_changes(self, yes: bool) -> Self {
if yes {
std::cmp::max(self, Self::RecordStateDiff)
} else {
self
}
}

pub fn with_verbosity(self, verbosiy: u8) -> Self {
if verbosiy >= 3 {
std::cmp::max(self, Self::Call)
Expand All @@ -342,7 +360,7 @@ impl TraceMode {
StackSnapshotType::None
},
record_logs: true,
record_state_diff: false,
record_state_diff: self.record_state_diff(),
record_returndata_snapshots: self.is_debug(),
record_opcodes_filter: (self.is_jump() || self.is_jump_simple())
.then(|| OpcodeFilter::new().enabled(OpCode::JUMP).enabled(OpCode::JUMPDEST)),
Expand Down
11 changes: 6 additions & 5 deletions crates/verify/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,15 @@ pub fn build_using_cache(

// Check if Solidity version matches
if let Ok(version) = Version::parse(&version) {
if !(artifact.version.major == version.major &&
artifact.version.minor == version.minor &&
artifact.version.patch == version.patch)
if !(artifact.version.major == version.major
&& artifact.version.minor == version.minor
&& artifact.version.patch == version.patch)
{
continue;
}
}

return Ok(artifact.artifact)
return Ok(artifact.artifact);
}
}
}
Expand Down Expand Up @@ -189,7 +189,7 @@ fn is_partial_match(
// 1. Check length of constructor args
if constructor_args.is_empty() || is_runtime {
// Assume metadata is at the end of the bytecode
return try_extract_and_compare_bytecode(local_bytecode, bytecode)
return try_extract_and_compare_bytecode(local_bytecode, bytecode);
cassc marked this conversation as resolved.
Show resolved Hide resolved
}

// If not runtime, extract constructor args from the end of the bytecode
Expand Down Expand Up @@ -340,6 +340,7 @@ pub async fn get_tracing_executor(
Some(fork_config.evm_version),
false,
false,
false,
is_alphanet,
);

Expand Down
Loading