Skip to content

Commit 7e91e4a

Browse files
committed
Add replay tracing and fix include for templates
1 parent 791864e commit 7e91e4a

File tree

22 files changed

+1480
-36
lines changed

22 files changed

+1480
-36
lines changed

Cargo.lock

Lines changed: 41 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cargo-stylus/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,6 @@ tokio = { workspace = true, features = ["rt-multi-thread"] }
2121
anstyle = "1.0.10"
2222
clap = { version = "4.5.39", features = ["derive"] }
2323
env_logger = "0.11"
24+
25+
# replay
26+
libloading = "0.8"

cargo-stylus/src/commands/build.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ use crate::{common_args::BuildArgs, error::CargoStylusResult};
77

88
#[derive(Debug, clap::Args)]
99
pub struct Args {
10+
#[arg(long)]
11+
contract: Vec<String>,
12+
1013
#[command(flatten)]
1114
build: BuildArgs,
1215
}
@@ -16,6 +19,14 @@ pub fn exec(args: Args) -> CargoStylusResult {
1619
features: args.build.features,
1720
..Default::default()
1821
};
19-
ops::build_workspace(&config)?;
22+
23+
if args.contract.is_empty() {
24+
ops::build_workspace(&config)?;
25+
} else {
26+
for contract in args.contract {
27+
ops::build_contract(contract, &config)?;
28+
}
29+
}
30+
2031
Ok(())
2132
}

cargo-stylus/src/commands/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ pub async fn exec(cmd: Command) -> CargoStylusResult {
7575
Command::GetInitcode(args) => get_initcode::exec(args),
7676
Command::Init(args) => init::exec(args),
7777
Command::New(args) => new::exec(args),
78-
Command::Replay(args) => replay::exec(args),
78+
Command::Replay(args) => replay::exec(args).await,
7979
Command::Simulate(args) => simulate::exec(args),
8080
Command::Trace(args) => trace::exec(args).await,
8181
Command::Verify(args) => verify::exec(args).await,
Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,81 @@
11
// Copyright 2025, Offchain Labs, Inc.
22
// For licensing, see https://github.com/OffchainLabs/stylus-sdk-rs/blob/main/licenses/COPYRIGHT.md
33

4-
use crate::error::CargoStylusResult;
4+
use std::iter;
5+
6+
use eyre::bail;
7+
use stylus_tools::{
8+
core::{
9+
build::build_shared_library,
10+
debugger::Debugger,
11+
tracing::{hostios, Trace},
12+
},
13+
utils::color::Color,
14+
};
15+
16+
use crate::{
17+
common_args::{ProviderArgs, TraceArgs},
18+
error::CargoStylusResult,
19+
};
520

621
#[derive(Debug, clap::Args)]
7-
pub struct Args {}
22+
pub struct Args {
23+
/// Which specific package to build during replay, if any.
24+
#[arg(long)]
25+
package: Option<String>,
26+
/// Whether this process is the child of another.
27+
///
28+
/// The parent process launches the debugger, with the same arguments plus this `--child` flag.
29+
/// The child process then runs the transaction.
30+
#[arg(long, hide(true))]
31+
child: bool,
32+
33+
#[command(flatten)]
34+
trace: TraceArgs,
35+
#[command(flatten)]
36+
provider: ProviderArgs,
37+
}
838

9-
pub fn exec(_args: Args) -> CargoStylusResult {
39+
pub async fn exec(args: Args) -> CargoStylusResult {
40+
if args.child {
41+
exec_replay_transaction(args).await?;
42+
} else {
43+
launch_debugger()?;
44+
}
1045
Ok(())
1146
}
47+
48+
async fn exec_replay_transaction(args: Args) -> eyre::Result<()> {
49+
let provider = args.provider.build_provider().await?;
50+
let trace = Trace::new(args.trace.tx, args.trace.use_native_tracer, &provider).await?;
51+
let shared_library = build_shared_library(&args.trace.project, args.package, None)?;
52+
53+
// TODO: don't assume the contract is top-level
54+
let Some(args) = trace.tx().input.input() else {
55+
bail!("missing transaction input");
56+
};
57+
let args_len = args.len();
58+
59+
unsafe {
60+
*hostios::FRAME.lock() = Some(trace.into_frame_reader());
61+
62+
type Entrypoint = unsafe extern "C" fn(usize) -> usize;
63+
let lib = libloading::Library::new(shared_library)?;
64+
let main: libloading::Symbol<Entrypoint> = lib.get(b"user_entrypoint")?;
65+
66+
match main(args_len) {
67+
0 => println!("call completed successfully"),
68+
1 => println!("call reverted"),
69+
x => println!("call exited with unknown status code: {}", x.red()),
70+
}
71+
}
72+
Ok(())
73+
}
74+
75+
// TODO: Use "never" type when feature is stabilized rust-lang/rust#35121
76+
fn launch_debugger() -> eyre::Result<()> {
77+
let debugger = Debugger::select()?;
78+
let args = std::env::args().chain(iter::once("--child".to_string()));
79+
let err = debugger.exec(args);
80+
eyre::bail!("failed to exec {}: {}", debugger.program, err);
81+
}

cargo-stylus/src/commands/trace.rs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,24 @@
11
// Copyright 2025, Offchain Labs, Inc.
22
// For licensing, see https://github.com/OffchainLabs/stylus-sdk-rs/blob/main/licenses/COPYRIGHT.md
33

4-
use alloy::primitives::TxHash;
54
use stylus_tools::core::tracing::Trace;
65

7-
use crate::{common_args::ProviderArgs, error::CargoStylusResult};
6+
use crate::{
7+
common_args::{ProviderArgs, TraceArgs},
8+
error::CargoStylusResult,
9+
};
810

911
#[derive(Debug, clap::Args)]
1012
pub struct Args {
11-
/// Tx to replay.
12-
#[arg(short, long)]
13-
tx: TxHash,
14-
/// If set, use the native tracer instead of the JavaScript one. Notice the native tracer might not be available in the node.
15-
#[arg(short, long, default_value_t = false)]
16-
use_native_tracer: bool,
17-
13+
#[command(flatten)]
14+
trace: TraceArgs,
1815
#[command(flatten)]
1916
provider: ProviderArgs,
2017
}
2118

2219
pub async fn exec(args: Args) -> CargoStylusResult {
2320
let provider = args.provider.build_provider().await?;
24-
let trace = Trace::new(args.tx, args.use_native_tracer, &provider)
21+
let trace = Trace::new(args.trace.tx, args.trace.use_native_tracer, &provider)
2522
.await
2623
.map_err(eyre::Error::from)?;
2724
println!("{}", trace.json());

cargo-stylus/src/common_args.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::{fs, path::PathBuf};
55

66
use alloy::{
77
network::EthereumWallet,
8-
primitives::FixedBytes,
8+
primitives::{FixedBytes, TxHash},
99
providers::{Provider, ProviderBuilder, WalletProvider},
1010
signers::{
1111
local::{LocalSigner, PrivateKeySigner},
@@ -67,6 +67,19 @@ pub struct ProviderArgs {
6767
pub endpoint: String,
6868
}
6969

70+
#[derive(Debug, clap::Args)]
71+
pub struct TraceArgs {
72+
/// Tx to replay.
73+
#[arg(short, long)]
74+
pub tx: TxHash,
75+
/// If set, use the native tracer instead of the JavaScript one. Notice the native tracer might not be available in the node.
76+
#[arg(short, long, default_value_t = false)]
77+
pub use_native_tracer: bool,
78+
/// Project path.
79+
#[arg(short, long, default_value = ".")]
80+
pub project: PathBuf,
81+
}
82+
7083
#[derive(Debug, clap::Args)]
7184
pub struct VerificationArgs {
7285
/// If specified, will not run the command in a reproducible docker container.

stylus-tools/Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,13 @@ license.workspace = true
1010
repository.workspace = true
1111
version.workspace = true
1212

13+
include = ["src/templates/**"]
14+
1315
[dependencies]
1416
alloy = { workspace = true, features = ["contract", "getrandom", "rpc-types", "signer-local", "sol-types"] }
1517
eyre.workspace = true
1618
hex.workspace = true
19+
lazy_static.workspace = true
1720
regex.workspace = true
1821
reqwest.workspace = true
1922
testcontainers = { workspace = true, optional = true }
@@ -44,5 +47,10 @@ wasmparser = "0.213.0"
4447
wasmprinter = "0.221.2"
4548
wat = "1.230"
4649

50+
# replay
51+
function_name = "0.3"
52+
parking_lot = "0.12"
53+
sneks = "0.1"
54+
4755
[features]
4856
integration-tests = ["dep:testcontainers"]

stylus-tools/src/core/activation.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::{
1919
precompiles,
2020
utils::{
2121
bump_data_fee,
22-
color::{DebugColor, GREY, LAVENDER},
22+
color::{GREY, LAVENDER},
2323
format_data_fee,
2424
},
2525
};
@@ -81,13 +81,6 @@ pub async fn activate_contract(
8181
.await?
8282
.get_receipt()
8383
.await?;
84-
85-
info!(@grey,
86-
"successfully activated contract 0x{} with tx {}",
87-
hex::encode(address),
88-
hex::encode(receipt.transaction_hash).debug_lavender()
89-
);
90-
9184
Ok(receipt)
9285
}
9386

0 commit comments

Comments
 (0)