Skip to main content

reth_bench/bench/
mod.rs

1//! `reth benchmark` command. Collection of various benchmarking routines.
2
3use clap::{Parser, Subcommand};
4use reth_cli_runner::CliContext;
5use reth_node_core::args::LogArgs;
6use reth_tracing::FileWorkerGuard;
7
8mod context;
9mod gas_limit_ramp;
10mod generate_big_block;
11pub(crate) mod helpers;
12pub use generate_big_block::{
13    RawTransaction, RpcTransactionSource, TransactionCollector, TransactionSource,
14};
15pub(crate) mod metrics_scraper;
16mod new_payload_fcu;
17mod new_payload_only;
18mod output;
19mod persistence_waiter;
20mod replay_payloads;
21mod send_invalid_payload;
22mod send_payload;
23
24/// `reth bench` command
25#[derive(Debug, Parser)]
26pub struct BenchmarkCommand {
27    #[command(subcommand)]
28    command: Subcommands,
29
30    #[command(flatten)]
31    logs: LogArgs,
32}
33
34/// `reth benchmark` subcommands
35#[derive(Subcommand, Debug)]
36pub enum Subcommands {
37    /// Benchmark which calls `newPayload`, then `forkchoiceUpdated`.
38    NewPayloadFcu(new_payload_fcu::Command),
39
40    /// Benchmark which builds empty blocks with a ramped gas limit.
41    GasLimitRamp(gas_limit_ramp::Command),
42
43    /// Benchmark which only calls subsequent `newPayload` calls.
44    NewPayloadOnly(new_payload_only::Command),
45
46    /// Command for generating and sending an `engine_newPayload` request constructed from an RPC
47    /// block.
48    ///
49    /// This command takes a JSON block input (either from a file or stdin) and generates
50    /// an execution payload that can be used with the `engine_newPayloadV*` API.
51    ///
52    /// One powerful use case is pairing this command with the `cast block` command, for example:
53    ///
54    /// `cast block latest --full --json | reth-bench send-payload --rpc-url localhost:5000
55    /// --jwt-secret $(cat ~/.local/share/reth/mainnet/jwt.hex)`
56    SendPayload(send_payload::Command),
57
58    /// Generate a large block by packing transactions from existing blocks.
59    ///
60    /// This command fetches transactions from real blocks and packs them into a single
61    /// block using the `testing_buildBlockV1` RPC endpoint.
62    ///
63    /// Example:
64    ///
65    /// `reth-bench generate-big-block --rpc-url http://localhost:8545 --engine-rpc-url
66    /// http://localhost:8551 --jwt-secret ~/.local/share/reth/mainnet/jwt.hex --target-gas
67    /// 30000000`
68    GenerateBigBlock(generate_big_block::Command),
69
70    /// Replay pre-generated payloads from a directory.
71    ///
72    /// This command reads payload files from a previous `generate-big-block` run and replays
73    /// them in sequence using `newPayload` followed by `forkchoiceUpdated`.
74    ///
75    /// Example:
76    ///
77    /// `reth-bench replay-payloads --payload-dir ./payloads --engine-rpc-url
78    /// http://localhost:8551 --jwt-secret ~/.local/share/reth/mainnet/jwt.hex`
79    ReplayPayloads(replay_payloads::Command),
80
81    /// Generate and send an invalid `engine_newPayload` request for testing.
82    ///
83    /// Takes a valid block and modifies fields to make it invalid, allowing you to test
84    /// Engine API rejection behavior. Block hash is recalculated after modifications
85    /// unless `--invalid-block-hash` or `--skip-hash-recalc` is used.
86    ///
87    /// Example:
88    ///
89    /// `cast block latest --full --json | reth-bench send-invalid-payload --rpc-url localhost:5000
90    /// --jwt-secret $(cat ~/.local/share/reth/mainnet/jwt.hex) --invalid-state-root`
91    SendInvalidPayload(Box<send_invalid_payload::Command>),
92}
93
94impl BenchmarkCommand {
95    /// Execute `benchmark` command
96    pub async fn execute(self, ctx: CliContext) -> eyre::Result<()> {
97        // Initialize tracing
98        let _guard = self.init_tracing()?;
99
100        match self.command {
101            Subcommands::NewPayloadFcu(command) => command.execute(ctx).await,
102            Subcommands::GasLimitRamp(command) => command.execute(ctx).await,
103            Subcommands::NewPayloadOnly(command) => command.execute(ctx).await,
104            Subcommands::SendPayload(command) => command.execute(ctx).await,
105            Subcommands::GenerateBigBlock(command) => command.execute(ctx).await,
106            Subcommands::ReplayPayloads(command) => command.execute(ctx).await,
107            Subcommands::SendInvalidPayload(command) => (*command).execute(ctx).await,
108        }
109    }
110
111    /// Initializes tracing with the configured options.
112    ///
113    /// If file logging is enabled, this function returns a guard that must be kept alive to ensure
114    /// that all logs are flushed to disk.
115    ///
116    /// Always enables log target display (`RUST_LOG_TARGET=1`) so that the `reth-bench` target
117    /// is visible in output, making it easy to distinguish reth-bench logs from reth logs when
118    /// both are streamed to the same console or file.
119    pub fn init_tracing(&self) -> eyre::Result<Option<FileWorkerGuard>> {
120        // Always show the log target so "reth-bench" is visible in the output.
121        if std::env::var_os("RUST_LOG_TARGET").is_none() {
122            // SAFETY: This is called early during single-threaded initialization, before any
123            // threads are spawned and before the tracing subscriber is set up.
124            unsafe { std::env::set_var("RUST_LOG_TARGET", "1") };
125        }
126
127        let guard = self.logs.init_tracing()?;
128        Ok(guard)
129    }
130}