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(long, value_name = "WS_RPC_URL", verbatim_doc_comment)]
57 pub ws_rpc_url: Option<String>,
58
59 #[arg(long, short, value_name = "BENCHMARK_OUTPUT", verbatim_doc_comment)]
61 pub output: Option<PathBuf>,
62
63 #[arg(long = "metrics-url", value_name = "URL", verbatim_doc_comment)]
72 pub metrics_url: Option<String>,
73
74 #[arg(
78 long = "rpc-block-fetch-retries",
79 value_name = "RETRIES",
80 default_value = "10",
81 value_parser = parse_rpc_block_fetch_retries,
82 verbatim_doc_comment
83 )]
84 pub rpc_block_fetch_retries: RpcBlockFetchRetries,
85
86 #[arg(long, default_value = "false", verbatim_doc_comment)]
95 pub reth_new_payload: bool,
96
97 #[arg(
104 long = "wait-for-persistence",
105 value_name = "MODE",
106 num_args = 0..=1,
107 default_missing_value = "always",
108 value_parser = clap::value_parser!(WaitForPersistence),
109 requires = "reth_new_payload",
110 verbatim_doc_comment
111 )]
112 pub wait_for_persistence: Option<WaitForPersistence>,
113
114 #[arg(long, default_value = "false", verbatim_doc_comment, requires = "reth_new_payload")]
119 pub no_wait_for_caches: bool,
120
121 #[arg(long, default_value = "false", verbatim_doc_comment)]
123 pub rlp_blocks: bool,
124}
125
126#[derive(Debug, Clone, Copy, PartialEq, Eq)]
128pub enum RpcBlockFetchRetries {
129 Finite(u32),
131 Forever,
133}
134
135impl RpcBlockFetchRetries {
136 pub const fn as_max_retries(self) -> u32 {
138 match self {
139 Self::Finite(n) => n,
140 Self::Forever => u32::MAX,
141 }
142 }
143}
144
145impl Default for RpcBlockFetchRetries {
146 fn default() -> Self {
147 Self::Finite(10)
148 }
149}
150
151impl FromStr for RpcBlockFetchRetries {
152 type Err = String;
153
154 fn from_str(s: &str) -> Result<Self, Self::Err> {
155 let s = s.trim();
156 if s.eq_ignore_ascii_case("forever") ||
157 s.eq_ignore_ascii_case("infinite") ||
158 s.eq_ignore_ascii_case("inf")
159 {
160 return Ok(Self::Forever)
161 }
162
163 let retries = s
164 .parse::<u32>()
165 .map_err(|_| format!("invalid retry value {s:?}, expected a number or 'forever'"))?;
166 Ok(Self::Finite(retries))
167 }
168}
169
170fn parse_rpc_block_fetch_retries(value: &str) -> Result<RpcBlockFetchRetries, String> {
171 value.parse()
172}
173
174#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
176pub enum WaitForPersistence {
177 #[default]
179 Always,
180 Never,
182 EveryN(u64),
184}
185
186impl WaitForPersistence {
187 pub const fn rpc_value(self, block_number: u64) -> Option<bool> {
193 match self {
194 Self::Always => None,
195 Self::Never => Some(false),
196 Self::EveryN(n) => {
197 if block_number.is_multiple_of(n) {
198 Some(true)
199 } else {
200 Some(false)
201 }
202 }
203 }
204 }
205}
206
207impl FromStr for WaitForPersistence {
208 type Err = String;
209
210 fn from_str(s: &str) -> Result<Self, Self::Err> {
211 let s = s.trim();
212 if s.eq_ignore_ascii_case("always") {
213 return Ok(Self::Always)
214 }
215 if s.eq_ignore_ascii_case("never") {
216 return Ok(Self::Never)
217 }
218 let n = s.parse::<u64>().map_err(|_| {
219 format!("invalid value {s:?}, expected 'always', 'never', or a block interval number")
220 })?;
221 if n == 0 {
222 return Err("block interval must be > 0, use 'never' to disable".to_string())
223 }
224 Ok(Self::EveryN(n))
225 }
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231 use clap::Parser;
232
233 #[derive(Parser)]
235 struct CommandParser<T: Args> {
236 #[command(flatten)]
237 args: T,
238 }
239
240 #[test]
241 fn test_parse_benchmark_args() {
242 let default_args = BenchmarkArgs {
243 engine_rpc_url: "http://localhost:8551".to_string(),
244 ..Default::default()
245 };
246 let args = CommandParser::<BenchmarkArgs>::parse_from(["reth-bench"]).args;
247 assert_eq!(args, default_args);
248 }
249
250 #[test]
251 fn test_parse_rpc_block_fetch_retries_forever() {
252 let args = CommandParser::<BenchmarkArgs>::parse_from([
253 "reth-bench",
254 "--rpc-block-fetch-retries",
255 "forever",
256 ])
257 .args;
258 assert_eq!(args.rpc_block_fetch_retries, RpcBlockFetchRetries::Forever);
259 }
260
261 #[test]
262 fn test_parse_rpc_block_fetch_retries_number() {
263 let args = CommandParser::<BenchmarkArgs>::parse_from([
264 "reth-bench",
265 "--rpc-block-fetch-retries",
266 "7",
267 ])
268 .args;
269 assert_eq!(args.rpc_block_fetch_retries, RpcBlockFetchRetries::Finite(7));
270 }
271
272 #[test]
273 fn test_parse_wait_for_persistence() {
274 let args = CommandParser::<BenchmarkArgs>::parse_from([
275 "reth-bench",
276 "--reth-new-payload",
277 "--wait-for-persistence",
278 "always",
279 ])
280 .args;
281 assert_eq!(args.wait_for_persistence, Some(WaitForPersistence::Always));
282
283 let args = CommandParser::<BenchmarkArgs>::parse_from([
284 "reth-bench",
285 "--reth-new-payload",
286 "--wait-for-persistence",
287 "never",
288 ])
289 .args;
290 assert_eq!(args.wait_for_persistence, Some(WaitForPersistence::Never));
291
292 let args = CommandParser::<BenchmarkArgs>::parse_from([
293 "reth-bench",
294 "--reth-new-payload",
295 "--wait-for-persistence",
296 "10",
297 ])
298 .args;
299 assert_eq!(args.wait_for_persistence, Some(WaitForPersistence::EveryN(10)));
300
301 let args = CommandParser::<BenchmarkArgs>::parse_from([
303 "reth-bench",
304 "--reth-new-payload",
305 "--wait-for-persistence",
306 ])
307 .args;
308 assert_eq!(args.wait_for_persistence, Some(WaitForPersistence::Always));
309
310 let args = CommandParser::<BenchmarkArgs>::parse_from(["reth-bench"]).args;
312 assert_eq!(args.wait_for_persistence, None);
313 }
314}