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

Merged
Show file tree
Hide file tree
Changes from 26 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
357 changes: 168 additions & 189 deletions Cargo.lock

Large diffs are not rendered by default.

17 changes: 15 additions & 2 deletions crates/cast/bin/cmd/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ pub struct CallArgs {
#[arg(long, requires = "trace")]
debug: bool,

/// Prints the state changes
#[arg(long)]
with_state_changes: bool,

Copy link
Member

@zerosnacks zerosnacks Nov 27, 2024

Choose a reason for hiding this comment

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

Instead of introducing a new arg I would prefer we use the global verbosity (-vvv) flag here

It is accessible through shell::verbosity()

#[arg(long, requires = "trace")]
decode_internal: bool,

Expand Down Expand Up @@ -128,6 +132,7 @@ impl CallArgs {
trace,
evm_version,
debug,
with_state_changes,
decode_internal,
labels,
data,
Expand Down Expand Up @@ -182,8 +187,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 @@ -208,6 +220,7 @@ impl CallArgs {
with_local_artifacts,
debug,
decode_internal,
with_state_changes,
)
.await?;

Expand Down
12 changes: 10 additions & 2 deletions crates/cast/bin/cmd/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use foundry_cli::{
opts::{EtherscanOpts, RpcOpts},
utils::{handle_traces, init_progress, TraceResult},
};
use foundry_common::{is_known_system_sender, SYSTEM_TRANSACTION_TYPE};
use foundry_common::{is_known_system_sender, shell, SYSTEM_TRANSACTION_TYPE};
use foundry_compilers::artifacts::EvmVersion;
use foundry_config::{
figment::{
Expand Down Expand Up @@ -50,6 +50,10 @@ pub struct RunArgs {
#[arg(long)]
quick: bool,

/// Prints the state changes
#[arg(long)]
with_state_changes: bool,

/// Label addresses in the trace.
///
/// Example: 0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045:vitalik.eth
Expand Down Expand Up @@ -169,14 +173,17 @@ impl RunArgs {
evm_version,
self.debug,
self.decode_internal,
self.with_state_changes,
alphanet,
);
let mut env =
EnvWithHandlerCfg::new_with_spec_id(Box::new(env.clone()), executor.spec_id());

// Set the state to the moment right before the transaction
if !self.quick {
sh_println!("Executing previous transactions from the block.")?;
if !shell::is_json() {
sh_println!("Executing previous transactions from the block.")?;
}

if let Some(block) = block {
let pb = init_progress(block.transactions.len() as u64, "tx");
Expand Down Expand Up @@ -258,6 +265,7 @@ impl RunArgs {
self.with_local_artifacts,
self.debug,
self.decode_internal,
self.with_state_changes,
)
.await?;

Expand Down
23 changes: 16 additions & 7 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::{ContractSources, DebugTraceIdentifier},
decode_trace_arena,
identifier::{CachedSignatures, SignaturesIdentifier, TraceIdentifiers},
render_trace_arena_with_bytecodes, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind,
Traces,
render_trace_arena_inner, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, Traces,
},
};
use std::{
Expand Down Expand Up @@ -378,6 +377,7 @@ impl TryFrom<Result<RawCallResult>> for TraceResult {
}

/// labels the traces, conditionally prints them or opens the debugger
#[allow(clippy::too_many_arguments)]
pub async fn handle_traces(
mut result: TraceResult,
config: &Config,
Expand All @@ -386,6 +386,7 @@ pub async fn handle_traces(
with_local_artifacts: bool,
debug: bool,
decode_internal: bool,
with_state_changes: bool,
) -> Result<()> {
let (known_contracts, mut sources) = if with_local_artifacts {
let _ = sh_println!("Compiling project to generate artifacts");
Expand Down Expand Up @@ -450,7 +451,7 @@ pub async fn handle_traces(
decoder.debug_identifier = Some(DebugTraceIdentifier::new(sources));
}

print_traces(&mut result, &decoder, shell::verbosity() > 0).await?;
print_traces(&mut result, &decoder, shell::verbosity() > 0, with_state_changes).await?;

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

sh_println!("Traces:")?;
if !shell::is_json() {
sh_println!("Traces:")?;
}

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

if shell::is_json() {
return Ok(());
}
sh_println!()?;

sh_println!()?;
if result.success {
sh_println!("{}", "Transaction successfully executed.".green())?;
} else {
sh_err!("Transaction failed.")?;
}

sh_println!("Gas used: {}", result.gas_used)?;

Ok(())
}

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
tempfile.workspace = true
Expand Down
37 changes: 31 additions & 6 deletions crates/evm/traces/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ extern crate foundry_common;
#[macro_use]
extern crate tracing;

use foundry_common::contracts::{ContractsByAddress, ContractsByArtifact};
use foundry_common::{
contracts::{ContractsByAddress, ContractsByArtifact},
shell,
};
use revm::interpreter::OpCode;
use revm_inspectors::tracing::{
types::{DecodedTraceStep, TraceMemberOrder},
Expand Down Expand Up @@ -183,15 +186,23 @@ 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,
with_storage_changes: bool,
) -> String {
let mut w = TraceWriter::new(Vec::<u8>::new()).write_bytecodes(with_bytecodes);
if shell::is_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)
.with_storage_changes(with_storage_changes);
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 @@ -289,6 +300,8 @@ pub enum TraceMode {
///
/// Used by debugger.
Debug,
/// Debug trace with storage changes.
RecordStateDiff,
}

impl TraceMode {
Expand All @@ -308,6 +321,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 @@ -324,6 +341,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 {
Copy link
Member

Choose a reason for hiding this comment

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

one suggestion for separate improvement @zerosnacks
let's introduce some enum for these numbers

Copy link
Member

Choose a reason for hiding this comment

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

added a ticket for it here: #9426

std::cmp::max(self, Self::Call)
Expand All @@ -345,7 +370,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
1 change: 1 addition & 0 deletions crates/verify/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@ pub async fn get_tracing_executor(
Some(fork_config.evm_version),
false,
false,
false,
is_alphanet,
);

Expand Down