1use clap::Args;
4use std::{path::PathBuf, str::FromStr};
5
6#[derive(Debug, Args, PartialEq, Eq, Default, Clone)]
8#[command(next_help_heading = "Benchmark")]
9pub struct BenchmarkArgs {
10 #[arg(long, verbatim_doc_comment)]
12 pub from: Option<u64>,
13
14 #[arg(long, verbatim_doc_comment)]
16 pub to: Option<u64>,
17
18 #[arg(long, conflicts_with_all = &["from", "to"], verbatim_doc_comment)]
22 pub advance: Option<u64>,
23
24 #[arg(
32 long = "jwt-secret",
33 alias = "jwtsecret",
34 value_name = "PATH",
35 global = true,
36 required = false
37 )]
38 pub auth_jwtsecret: Option<PathBuf>,
39
40 #[arg(
42 long,
43 value_name = "ENGINE_RPC_URL",
44 verbatim_doc_comment,
45 default_value = "http://localhost:8551"
46 )]
47 pub engine_rpc_url: String,
48
49 #[arg(
51 long,
52 value_name = "LOCAL_RPC_URL",
53 verbatim_doc_comment,
54 default_value = "http://localhost:8545"
55 )]
56 pub local_rpc_url: String,
57
58 #[arg(long, value_name = "WS_RPC_URL", verbatim_doc_comment)]
66 pub ws_rpc_url: Option<String>,
67
68 #[arg(long, short, value_name = "BENCHMARK_OUTPUT", verbatim_doc_comment)]
70 pub output: Option<PathBuf>,
71
72 #[arg(long = "metrics-url", value_name = "URL", verbatim_doc_comment)]
81 pub metrics_url: Option<String>,
82
83 #[arg(
87 long = "rpc-block-fetch-retries",
88 value_name = "RETRIES",
89 default_value = "10",
90 value_parser = parse_rpc_block_fetch_retries,
91 verbatim_doc_comment
92 )]
93 pub rpc_block_fetch_retries: RpcBlockFetchRetries,
94
95 #[arg(long, default_value = "false", verbatim_doc_comment)]
104 pub reth_new_payload: bool,
105
106 #[arg(
113 long = "wait-for-persistence",
114 value_name = "MODE",
115 num_args = 0..=1,
116 default_missing_value = "always",
117 value_parser = clap::value_parser!(WaitForPersistence),
118 requires = "reth_new_payload",
119 verbatim_doc_comment
120 )]
121 pub wait_for_persistence: Option<WaitForPersistence>,
122
123 #[arg(long, default_value = "false", verbatim_doc_comment, requires = "reth_new_payload")]
128 pub no_wait_for_caches: bool,
129
130 #[arg(long, default_value = "false", verbatim_doc_comment)]
132 pub rlp_blocks: bool,
133
134 #[arg(long, verbatim_doc_comment)]
137 pub big_blocks: Option<usize>,
138}
139
140#[derive(Debug, Clone, Copy, PartialEq, Eq)]
142pub enum RpcBlockFetchRetries {
143 Finite(u32),
145 Forever,
147}
148
149impl RpcBlockFetchRetries {
150 pub const fn as_max_retries(self) -> u32 {
152 match self {
153 Self::Finite(n) => n,
154 Self::Forever => u32::MAX,
155 }
156 }
157}
158
159impl Default for RpcBlockFetchRetries {
160 fn default() -> Self {
161 Self::Finite(10)
162 }
163}
164
165impl FromStr for RpcBlockFetchRetries {
166 type Err = String;
167
168 fn from_str(s: &str) -> Result<Self, Self::Err> {
169 let s = s.trim();
170 if s.eq_ignore_ascii_case("forever") ||
171 s.eq_ignore_ascii_case("infinite") ||
172 s.eq_ignore_ascii_case("inf")
173 {
174 return Ok(Self::Forever)
175 }
176
177 let retries = s
178 .parse::<u32>()
179 .map_err(|_| format!("invalid retry value {s:?}, expected a number or 'forever'"))?;
180 Ok(Self::Finite(retries))
181 }
182}
183
184fn parse_rpc_block_fetch_retries(value: &str) -> Result<RpcBlockFetchRetries, String> {
185 value.parse()
186}
187
188#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
190pub enum WaitForPersistence {
191 #[default]
193 Always,
194 Never,
196 EveryN(u64),
198}
199
200impl WaitForPersistence {
201 pub const fn rpc_value(self, block_number: u64) -> Option<bool> {
207 match self {
208 Self::Always => None,
209 Self::Never => Some(false),
210 Self::EveryN(n) => {
211 if block_number.is_multiple_of(n) {
212 Some(true)
213 } else {
214 Some(false)
215 }
216 }
217 }
218 }
219}
220
221impl FromStr for WaitForPersistence {
222 type Err = String;
223
224 fn from_str(s: &str) -> Result<Self, Self::Err> {
225 let s = s.trim();
226 if s.eq_ignore_ascii_case("always") {
227 return Ok(Self::Always)
228 }
229 if s.eq_ignore_ascii_case("never") {
230 return Ok(Self::Never)
231 }
232 let n = s.parse::<u64>().map_err(|_| {
233 format!("invalid value {s:?}, expected 'always', 'never', or a block interval number")
234 })?;
235 if n == 0 {
236 return Err("block interval must be > 0, use 'never' to disable".to_string())
237 }
238 Ok(Self::EveryN(n))
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245 use clap::Parser;
246
247 #[derive(Parser)]
249 struct CommandParser<T: Args> {
250 #[command(flatten)]
251 args: T,
252 }
253
254 #[test]
255 fn test_parse_benchmark_args() {
256 let default_args = BenchmarkArgs {
257 engine_rpc_url: "http://localhost:8551".to_string(),
258 local_rpc_url: "http://localhost:8545".to_string(),
259 ..Default::default()
260 };
261 let args = CommandParser::<BenchmarkArgs>::parse_from(["reth-bench"]).args;
262 assert_eq!(args, default_args);
263 }
264
265 #[test]
266 fn test_parse_rpc_block_fetch_retries_forever() {
267 let args = CommandParser::<BenchmarkArgs>::parse_from([
268 "reth-bench",
269 "--rpc-block-fetch-retries",
270 "forever",
271 ])
272 .args;
273 assert_eq!(args.rpc_block_fetch_retries, RpcBlockFetchRetries::Forever);
274 }
275
276 #[test]
277 fn test_parse_rpc_block_fetch_retries_number() {
278 let args = CommandParser::<BenchmarkArgs>::parse_from([
279 "reth-bench",
280 "--rpc-block-fetch-retries",
281 "7",
282 ])
283 .args;
284 assert_eq!(args.rpc_block_fetch_retries, RpcBlockFetchRetries::Finite(7));
285 }
286
287 #[test]
288 fn test_parse_wait_for_persistence() {
289 let args = CommandParser::<BenchmarkArgs>::parse_from([
290 "reth-bench",
291 "--reth-new-payload",
292 "--wait-for-persistence",
293 "always",
294 ])
295 .args;
296 assert_eq!(args.wait_for_persistence, Some(WaitForPersistence::Always));
297
298 let args = CommandParser::<BenchmarkArgs>::parse_from([
299 "reth-bench",
300 "--reth-new-payload",
301 "--wait-for-persistence",
302 "never",
303 ])
304 .args;
305 assert_eq!(args.wait_for_persistence, Some(WaitForPersistence::Never));
306
307 let args = CommandParser::<BenchmarkArgs>::parse_from([
308 "reth-bench",
309 "--reth-new-payload",
310 "--wait-for-persistence",
311 "10",
312 ])
313 .args;
314 assert_eq!(args.wait_for_persistence, Some(WaitForPersistence::EveryN(10)));
315
316 let args = CommandParser::<BenchmarkArgs>::parse_from([
318 "reth-bench",
319 "--reth-new-payload",
320 "--wait-for-persistence",
321 ])
322 .args;
323 assert_eq!(args.wait_for_persistence, Some(WaitForPersistence::Always));
324
325 let args = CommandParser::<BenchmarkArgs>::parse_from(["reth-bench"]).args;
327 assert_eq!(args.wait_for_persistence, None);
328 }
329}