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