1use crate::args::{
4 types::{MaxU32, ZeroAsNoneU64},
5 GasPriceOracleArgs, RpcStateCacheArgs,
6};
7use alloy_primitives::{map::AddressSet, utils::format_ether};
8use alloy_rpc_types_engine::JwtSecret;
9use clap::{
10 builder::{PossibleValue, RangedU64ValueParser, Resettable, TypedValueParser},
11 parser::ValueSource,
12 Arg, Args, Command,
13};
14use rand::Rng;
15use reth_cli_util::{
16 parse_duration_from_secs_or_ms, parse_ether_value, parsers::format_duration_as_secs_or_ms,
17};
18use reth_rpc_eth_types::builder::config::PendingBlockKind;
19use reth_rpc_server_types::{constants, RethRpcModule, RpcModuleSelection};
20use std::{
21 ffi::OsStr,
22 net::{IpAddr, Ipv4Addr},
23 path::PathBuf,
24 sync::OnceLock,
25 time::Duration,
26};
27use url::Url;
28
29use super::types::MaxOr;
30
31static RPC_SERVER_DEFAULTS: OnceLock<DefaultRpcServerArgs> = OnceLock::new();
33
34pub(crate) const RPC_DEFAULT_MAX_SUBS_PER_CONN: u32 = 1024;
36
37pub(crate) const RPC_DEFAULT_MAX_REQUEST_SIZE_MB: u32 = 15;
39
40pub(crate) const RPC_DEFAULT_MAX_RESPONSE_SIZE_MB: u32 = 160;
44
45pub(crate) const RPC_DEFAULT_MAX_CONNECTIONS: u32 = 500;
50
51#[derive(Debug, Clone)]
55pub struct DefaultRpcServerArgs {
56 http: bool,
57 http_addr: IpAddr,
58 http_port: u16,
59 http_disable_compression: bool,
60 http_compression_algorithms: Option<Vec<String>>,
61 http_decompression_algorithms: Option<Vec<String>>,
62 http_api: Option<RpcModuleSelection>,
63 http_corsdomain: Option<String>,
64 ws: bool,
65 ws_addr: IpAddr,
66 ws_port: u16,
67 ws_allowed_origins: Option<String>,
68 ws_api: Option<RpcModuleSelection>,
69 ipcdisable: bool,
70 ipcpath: String,
71 ipc_socket_permissions: Option<String>,
72 auth_addr: IpAddr,
73 auth_port: u16,
74 auth_jwtsecret: Option<PathBuf>,
75 auth_ipc: bool,
76 auth_ipc_path: String,
77 disable_auth_server: bool,
78 rpc_jwtsecret: Option<JwtSecret>,
79 rpc_disable_metrics: bool,
80 rpc_max_request_size: MaxU32,
81 rpc_max_response_size: MaxU32,
82 rpc_max_subscriptions_per_connection: MaxU32,
83 rpc_max_connections: MaxU32,
84 rpc_max_tracing_requests: usize,
85 rpc_max_blocking_io_requests: usize,
86 rpc_max_trace_filter_blocks: u64,
87 rpc_max_blocks_per_filter: ZeroAsNoneU64,
88 rpc_max_logs_per_response: ZeroAsNoneU64,
89 rpc_gas_cap: u64,
90 rpc_evm_memory_limit: u64,
91 rpc_tx_fee_cap: u128,
92 rpc_max_simulate_blocks: u64,
93 rpc_compute_state_root_for_eth_simulate: bool,
94 rpc_eth_proof_window: u64,
95 rpc_proof_permits: usize,
96 rpc_pending_block: PendingBlockKind,
97 rpc_forwarder: Option<Url>,
98 builder_disallow: Option<AddressSet>,
99 pub(super) rpc_state_cache: RpcStateCacheArgs,
100 pub(super) gas_price_oracle: GasPriceOracleArgs,
101 rpc_send_raw_transaction_sync_timeout: Duration,
102}
103
104impl DefaultRpcServerArgs {
105 pub fn try_init(self) -> Result<(), Self> {
107 RPC_SERVER_DEFAULTS.set(self)
108 }
109
110 pub fn get_global() -> &'static Self {
112 RPC_SERVER_DEFAULTS.get_or_init(Self::default)
113 }
114
115 pub const fn with_http(mut self, v: bool) -> Self {
117 self.http = v;
118 self
119 }
120
121 pub const fn with_http_addr(mut self, v: IpAddr) -> Self {
123 self.http_addr = v;
124 self
125 }
126
127 pub const fn with_http_port(mut self, v: u16) -> Self {
129 self.http_port = v;
130 self
131 }
132
133 pub const fn with_http_disable_compression(mut self, v: bool) -> Self {
135 self.http_disable_compression = v;
136 self
137 }
138
139 pub fn with_http_compression_algorithms(mut self, v: Option<Vec<String>>) -> Self {
141 self.http_compression_algorithms = v;
142 self
143 }
144
145 pub fn with_http_decompression_algorithms(mut self, v: Option<Vec<String>>) -> Self {
147 self.http_decompression_algorithms = v;
148 self
149 }
150
151 pub fn with_http_api(mut self, v: Option<RpcModuleSelection>) -> Self {
153 self.http_api = v;
154 self
155 }
156
157 pub fn with_http_corsdomain(mut self, v: Option<String>) -> Self {
159 self.http_corsdomain = v;
160 self
161 }
162
163 pub const fn with_ws(mut self, v: bool) -> Self {
165 self.ws = v;
166 self
167 }
168
169 pub const fn with_ws_addr(mut self, v: IpAddr) -> Self {
171 self.ws_addr = v;
172 self
173 }
174
175 pub const fn with_ws_port(mut self, v: u16) -> Self {
177 self.ws_port = v;
178 self
179 }
180
181 pub fn with_ws_allowed_origins(mut self, v: Option<String>) -> Self {
183 self.ws_allowed_origins = v;
184 self
185 }
186
187 pub fn with_ws_api(mut self, v: Option<RpcModuleSelection>) -> Self {
189 self.ws_api = v;
190 self
191 }
192
193 pub const fn with_ipcdisable(mut self, v: bool) -> Self {
195 self.ipcdisable = v;
196 self
197 }
198
199 pub fn with_ipcpath(mut self, v: String) -> Self {
201 self.ipcpath = v;
202 self
203 }
204
205 pub fn with_ipc_socket_permissions(mut self, v: Option<String>) -> Self {
207 self.ipc_socket_permissions = v;
208 self
209 }
210
211 pub const fn with_auth_addr(mut self, v: IpAddr) -> Self {
213 self.auth_addr = v;
214 self
215 }
216
217 pub const fn with_auth_port(mut self, v: u16) -> Self {
219 self.auth_port = v;
220 self
221 }
222
223 pub fn with_auth_jwtsecret(mut self, v: Option<PathBuf>) -> Self {
225 self.auth_jwtsecret = v;
226 self
227 }
228
229 pub const fn with_auth_ipc(mut self, v: bool) -> Self {
231 self.auth_ipc = v;
232 self
233 }
234
235 pub fn with_auth_ipc_path(mut self, v: String) -> Self {
237 self.auth_ipc_path = v;
238 self
239 }
240
241 pub const fn with_disable_auth_server(mut self, v: bool) -> Self {
243 self.disable_auth_server = v;
244 self
245 }
246
247 pub const fn with_rpc_jwtsecret(mut self, v: Option<JwtSecret>) -> Self {
249 self.rpc_jwtsecret = v;
250 self
251 }
252
253 pub const fn with_rpc_disable_metrics(mut self, v: bool) -> Self {
255 self.rpc_disable_metrics = v;
256 self
257 }
258
259 pub const fn with_rpc_max_request_size(mut self, v: MaxU32) -> Self {
261 self.rpc_max_request_size = v;
262 self
263 }
264
265 pub const fn with_rpc_max_response_size(mut self, v: MaxU32) -> Self {
267 self.rpc_max_response_size = v;
268 self
269 }
270
271 pub const fn with_rpc_max_subscriptions_per_connection(mut self, v: MaxU32) -> Self {
273 self.rpc_max_subscriptions_per_connection = v;
274 self
275 }
276
277 pub const fn with_rpc_max_connections(mut self, v: MaxU32) -> Self {
279 self.rpc_max_connections = v;
280 self
281 }
282
283 pub const fn with_rpc_max_tracing_requests(mut self, v: usize) -> Self {
285 self.rpc_max_tracing_requests = v;
286 self
287 }
288
289 pub const fn with_rpc_max_blocking_io_requests(mut self, v: usize) -> Self {
291 self.rpc_max_blocking_io_requests = v;
292 self
293 }
294
295 pub const fn with_rpc_max_trace_filter_blocks(mut self, v: u64) -> Self {
297 self.rpc_max_trace_filter_blocks = v;
298 self
299 }
300
301 pub const fn with_rpc_max_blocks_per_filter(mut self, v: ZeroAsNoneU64) -> Self {
303 self.rpc_max_blocks_per_filter = v;
304 self
305 }
306
307 pub const fn with_rpc_max_logs_per_response(mut self, v: ZeroAsNoneU64) -> Self {
309 self.rpc_max_logs_per_response = v;
310 self
311 }
312
313 pub const fn with_rpc_gas_cap(mut self, v: u64) -> Self {
315 self.rpc_gas_cap = v;
316 self
317 }
318
319 pub const fn with_rpc_evm_memory_limit(mut self, v: u64) -> Self {
321 self.rpc_evm_memory_limit = v;
322 self
323 }
324
325 pub const fn with_rpc_tx_fee_cap(mut self, v: u128) -> Self {
327 self.rpc_tx_fee_cap = v;
328 self
329 }
330
331 pub const fn with_rpc_max_simulate_blocks(mut self, v: u64) -> Self {
333 self.rpc_max_simulate_blocks = v;
334 self
335 }
336
337 pub const fn with_rpc_compute_state_root_for_eth_simulate(mut self, v: bool) -> Self {
339 self.rpc_compute_state_root_for_eth_simulate = v;
340 self
341 }
342
343 pub const fn with_rpc_eth_proof_window(mut self, v: u64) -> Self {
345 self.rpc_eth_proof_window = v;
346 self
347 }
348
349 pub const fn with_rpc_proof_permits(mut self, v: usize) -> Self {
351 self.rpc_proof_permits = v;
352 self
353 }
354
355 pub const fn with_rpc_pending_block(mut self, v: PendingBlockKind) -> Self {
357 self.rpc_pending_block = v;
358 self
359 }
360
361 pub fn with_rpc_forwarder(mut self, v: Option<Url>) -> Self {
363 self.rpc_forwarder = v;
364 self
365 }
366
367 pub fn with_builder_disallow(mut self, v: Option<AddressSet>) -> Self {
369 self.builder_disallow = v;
370 self
371 }
372
373 pub const fn with_rpc_state_cache(mut self, v: RpcStateCacheArgs) -> Self {
375 self.rpc_state_cache = v;
376 self
377 }
378
379 pub const fn with_gas_price_oracle(mut self, v: GasPriceOracleArgs) -> Self {
381 self.gas_price_oracle = v;
382 self
383 }
384
385 pub const fn with_rpc_send_raw_transaction_sync_timeout(mut self, v: Duration) -> Self {
387 self.rpc_send_raw_transaction_sync_timeout = v;
388 self
389 }
390}
391
392impl Default for DefaultRpcServerArgs {
393 fn default() -> Self {
394 Self {
395 http: false,
396 http_addr: Ipv4Addr::LOCALHOST.into(),
397 http_port: constants::DEFAULT_HTTP_RPC_PORT,
398 http_disable_compression: false,
399 http_compression_algorithms: None,
400 http_decompression_algorithms: None,
401 http_api: None,
402 http_corsdomain: None,
403 ws: false,
404 ws_addr: Ipv4Addr::LOCALHOST.into(),
405 ws_port: constants::DEFAULT_WS_RPC_PORT,
406 ws_allowed_origins: None,
407 ws_api: None,
408 ipcdisable: false,
409 ipcpath: constants::DEFAULT_IPC_ENDPOINT.to_string(),
410 ipc_socket_permissions: None,
411 auth_addr: Ipv4Addr::LOCALHOST.into(),
412 auth_port: constants::DEFAULT_AUTH_PORT,
413 auth_jwtsecret: None,
414 auth_ipc: false,
415 auth_ipc_path: constants::DEFAULT_ENGINE_API_IPC_ENDPOINT.to_string(),
416 disable_auth_server: false,
417 rpc_jwtsecret: None,
418 rpc_disable_metrics: false,
419 rpc_max_request_size: RPC_DEFAULT_MAX_REQUEST_SIZE_MB.into(),
420 rpc_max_response_size: RPC_DEFAULT_MAX_RESPONSE_SIZE_MB.into(),
421 rpc_max_subscriptions_per_connection: RPC_DEFAULT_MAX_SUBS_PER_CONN.into(),
422 rpc_max_connections: RPC_DEFAULT_MAX_CONNECTIONS.into(),
423 rpc_max_tracing_requests: constants::default_max_tracing_requests(),
424 rpc_max_blocking_io_requests: constants::DEFAULT_MAX_BLOCKING_IO_REQUEST,
425 rpc_max_trace_filter_blocks: constants::DEFAULT_MAX_TRACE_FILTER_BLOCKS,
426 rpc_max_blocks_per_filter: constants::DEFAULT_MAX_BLOCKS_PER_FILTER.into(),
427 rpc_max_logs_per_response: (constants::DEFAULT_MAX_LOGS_PER_RESPONSE as u64).into(),
428 rpc_gas_cap: constants::gas_oracle::RPC_DEFAULT_GAS_CAP,
429 rpc_evm_memory_limit: (1 << 32) - 1,
430 rpc_tx_fee_cap: constants::DEFAULT_TX_FEE_CAP_WEI,
431 rpc_max_simulate_blocks: constants::DEFAULT_MAX_SIMULATE_BLOCKS,
432 rpc_compute_state_root_for_eth_simulate: false,
433 rpc_eth_proof_window: constants::DEFAULT_ETH_PROOF_WINDOW,
434 rpc_proof_permits: constants::DEFAULT_PROOF_PERMITS,
435 rpc_pending_block: PendingBlockKind::Full,
436 rpc_forwarder: None,
437 builder_disallow: None,
438 rpc_state_cache: RpcStateCacheArgs::default(),
439 gas_price_oracle: GasPriceOracleArgs::default(),
440 rpc_send_raw_transaction_sync_timeout:
441 constants::RPC_DEFAULT_SEND_RAW_TX_SYNC_TIMEOUT_SECS,
442 }
443 }
444}
445
446#[derive(Debug, Clone, Args, PartialEq, Eq)]
448#[command(next_help_heading = "RPC")]
449pub struct RpcServerArgs {
450 #[arg(long, default_value_if("dev", "true", "true"), default_value_t = DefaultRpcServerArgs::get_global().http)]
452 pub http: bool,
453
454 #[arg(long = "http.addr", default_value_t = DefaultRpcServerArgs::get_global().http_addr)]
456 pub http_addr: IpAddr,
457
458 #[arg(long = "http.port", default_value_t = DefaultRpcServerArgs::get_global().http_port)]
460 pub http_port: u16,
461
462 #[arg(long = "http.disable-compression", default_value_t = DefaultRpcServerArgs::get_global().http_disable_compression)]
466 pub http_disable_compression: bool,
467
468 #[arg(
476 long = "http.compression",
477 value_name = "ALGOS",
478 value_delimiter = ',',
479 value_parser = ["zstd", "gzip", "deflate", "br"],
480 default_value = Resettable::from(DefaultRpcServerArgs::get_global().http_compression_algorithms.as_ref().map(|v| v.join(",").into()))
481 )]
482 pub http_compression_algorithms: Option<Vec<String>>,
483
484 #[arg(
488 long = "http.decompression",
489 value_name = "ALGOS",
490 value_delimiter = ',',
491 value_parser = ["zstd", "gzip", "deflate", "br"],
492 default_value = Resettable::from(DefaultRpcServerArgs::get_global().http_decompression_algorithms.as_ref().map(|v| v.join(",").into()))
493 )]
494 pub http_decompression_algorithms: Option<Vec<String>>,
495
496 #[arg(long = "http.api", value_parser = RpcModuleSelectionValueParser::default(), default_value = Resettable::from(DefaultRpcServerArgs::get_global().http_api.as_ref().map(|v| v.to_string().into())))]
498 pub http_api: Option<RpcModuleSelection>,
499
500 #[arg(long = "http.corsdomain", default_value = Resettable::from(DefaultRpcServerArgs::get_global().http_corsdomain.as_ref().map(|v| v.to_string().into())))]
502 pub http_corsdomain: Option<String>,
503
504 #[arg(long, default_value_t = DefaultRpcServerArgs::get_global().ws)]
506 pub ws: bool,
507
508 #[arg(long = "ws.addr", default_value_t = DefaultRpcServerArgs::get_global().ws_addr)]
510 pub ws_addr: IpAddr,
511
512 #[arg(long = "ws.port", default_value_t = DefaultRpcServerArgs::get_global().ws_port)]
514 pub ws_port: u16,
515
516 #[arg(id = "ws.origins", long = "ws.origins", alias = "ws.corsdomain", default_value = Resettable::from(DefaultRpcServerArgs::get_global().ws_allowed_origins.as_ref().map(|v| v.to_string().into())))]
518 pub ws_allowed_origins: Option<String>,
519
520 #[arg(long = "ws.api", value_parser = RpcModuleSelectionValueParser::default(), default_value = Resettable::from(DefaultRpcServerArgs::get_global().ws_api.as_ref().map(|v| v.to_string().into())))]
522 pub ws_api: Option<RpcModuleSelection>,
523
524 #[arg(long, default_value_t = DefaultRpcServerArgs::get_global().ipcdisable)]
526 pub ipcdisable: bool,
527
528 #[arg(long, default_value_t = DefaultRpcServerArgs::get_global().ipcpath.clone())]
530 pub ipcpath: String,
531
532 #[arg(long = "ipc.permissions", default_value = Resettable::from(DefaultRpcServerArgs::get_global().ipc_socket_permissions.as_ref().map(|v| v.to_string().into())))]
536 pub ipc_socket_permissions: Option<String>,
537
538 #[arg(long = "authrpc.addr", default_value_t = DefaultRpcServerArgs::get_global().auth_addr)]
540 pub auth_addr: IpAddr,
541
542 #[arg(long = "authrpc.port", default_value_t = DefaultRpcServerArgs::get_global().auth_port)]
544 pub auth_port: u16,
545
546 #[arg(long = "authrpc.jwtsecret", value_name = "PATH", global = true, required = false, conflicts_with = "auth_jwtsecret_hex", default_value = Resettable::from(DefaultRpcServerArgs::get_global().auth_jwtsecret.as_ref().map(|v| v.to_string_lossy().into())))]
554 pub auth_jwtsecret: Option<PathBuf>,
555
556 #[arg(
561 long = "authrpc.jwtsecret-hex",
562 value_name = "HEX",
563 global = true,
564 required = false,
565 conflicts_with = "auth_jwtsecret"
566 )]
567 pub auth_jwtsecret_hex: Option<JwtSecret>,
568
569 #[arg(long, default_value_t = DefaultRpcServerArgs::get_global().auth_ipc)]
571 pub auth_ipc: bool,
572
573 #[arg(long = "auth-ipc.path", default_value_t = DefaultRpcServerArgs::get_global().auth_ipc_path.clone())]
575 pub auth_ipc_path: String,
576
577 #[arg(long = "disable-auth-server", alias = "disable-engine-api", default_value_t = DefaultRpcServerArgs::get_global().disable_auth_server)]
582 pub disable_auth_server: bool,
583
584 #[arg(
590 long = "rpc.jwtsecret",
591 value_name = "HEX",
592 global = true,
593 required = false,
594 value_parser = DefaultValueParser {
595 inner: |value: &str| JwtSecret::from_hex(value),
596 default: &DefaultRpcServerArgs::get_global().rpc_jwtsecret,
597 },
598 default_value = Resettable::from(DefaultRpcServerArgs::get_global().rpc_jwtsecret.as_ref().map(|_| "configured".into())),
599 hide_default_value = true,
600 )]
601 pub rpc_jwtsecret: Option<JwtSecret>,
602
603 #[arg(long = "rpc.disable-metrics", default_value_t = DefaultRpcServerArgs::get_global().rpc_disable_metrics)]
605 pub rpc_disable_metrics: bool,
606
607 #[arg(long = "rpc.max-request-size", alias = "rpc-max-request-size", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_request_size)]
612 pub rpc_max_request_size: MaxU32,
613
614 #[arg(long = "rpc.max-response-size", alias = "rpc-max-response-size", visible_alias = "rpc.returndata.limit", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_response_size)]
616 pub rpc_max_response_size: MaxU32,
617
618 #[arg(long = "rpc.max-subscriptions-per-connection", alias = "rpc-max-subscriptions-per-connection", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_subscriptions_per_connection)]
620 pub rpc_max_subscriptions_per_connection: MaxU32,
621
622 #[arg(long = "rpc.max-connections", alias = "rpc-max-connections", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_connections)]
624 pub rpc_max_connections: MaxU32,
625
626 #[arg(long = "rpc.max-tracing-requests", alias = "rpc-max-tracing-requests", value_name = "COUNT", value_parser = RangedU64ValueParser::<usize>::new().range(1..), default_value_t = DefaultRpcServerArgs::get_global().rpc_max_tracing_requests)]
633 pub rpc_max_tracing_requests: usize,
634
635 #[arg(long = "rpc.max-blocking-io-requests", alias = "rpc-max-blocking-io-requests", value_name = "COUNT", value_parser = RangedU64ValueParser::<usize>::new().range(1..), default_value_t = DefaultRpcServerArgs::get_global().rpc_max_blocking_io_requests)]
641 pub rpc_max_blocking_io_requests: usize,
642
643 #[arg(long = "rpc.max-trace-filter-blocks", alias = "rpc-max-trace-filter-blocks", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_trace_filter_blocks)]
645 pub rpc_max_trace_filter_blocks: u64,
646
647 #[arg(long = "rpc.max-blocks-per-filter", alias = "rpc-max-blocks-per-filter", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_blocks_per_filter)]
649 pub rpc_max_blocks_per_filter: ZeroAsNoneU64,
650
651 #[arg(long = "rpc.max-logs-per-response", alias = "rpc-max-logs-per-response", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_logs_per_response)]
653 pub rpc_max_logs_per_response: ZeroAsNoneU64,
654
655 #[arg(
657 long = "rpc.gascap",
658 alias = "rpc-gascap",
659 value_name = "GAS_CAP",
660 value_parser = MaxOr::new(RangedU64ValueParser::<u64>::new().range(1..)),
661 default_value_t = DefaultRpcServerArgs::get_global().rpc_gas_cap
662 )]
663 pub rpc_gas_cap: u64,
664
665 #[arg(
667 long = "rpc.evm-memory-limit",
668 alias = "rpc-evm-memory-limit",
669 value_name = "MEMORY_LIMIT",
670 value_parser = MaxOr::new(RangedU64ValueParser::<u64>::new().range(1..)),
671 default_value_t = DefaultRpcServerArgs::get_global().rpc_evm_memory_limit
672 )]
673 pub rpc_evm_memory_limit: u64,
674
675 #[arg(
677 long = "rpc.txfeecap",
678 alias = "rpc-txfeecap",
679 value_name = "TX_FEE_CAP",
680 value_parser = parse_ether_value,
681 default_value = format_tx_fee_cap(DefaultRpcServerArgs::get_global().rpc_tx_fee_cap)
682 )]
683 pub rpc_tx_fee_cap: u128,
684
685 #[arg(
687 long = "rpc.max-simulate-blocks",
688 value_name = "BLOCKS_COUNT",
689 default_value_t = DefaultRpcServerArgs::get_global().rpc_max_simulate_blocks
690 )]
691 pub rpc_max_simulate_blocks: u64,
692
693 #[arg(
695 long = "rpc.compute-state-root-for-eth-simulate",
696 env = "RETH_RPC_COMPUTE_STATE_ROOT_FOR_ETH_SIMULATE",
697 default_value_t = DefaultRpcServerArgs::get_global().rpc_compute_state_root_for_eth_simulate
698 )]
699 pub rpc_compute_state_root_for_eth_simulate: bool,
700
701 #[arg(
705 long = "rpc.eth-proof-window",
706 default_value_t = DefaultRpcServerArgs::get_global().rpc_eth_proof_window,
707 value_parser = RangedU64ValueParser::<u64>::new().range(..=constants::MAX_ETH_PROOF_WINDOW)
708 )]
709 pub rpc_eth_proof_window: u64,
710
711 #[arg(long = "rpc.proof-permits", alias = "rpc-proof-permits", value_name = "COUNT", value_parser = RangedU64ValueParser::<usize>::new().range(1..), default_value_t = DefaultRpcServerArgs::get_global().rpc_proof_permits)]
713 pub rpc_proof_permits: usize,
714
715 #[arg(
720 long = "rpc.pending-block",
721 default_value = match DefaultRpcServerArgs::get_global().rpc_pending_block {
722 PendingBlockKind::Empty => "empty",
723 PendingBlockKind::None => "none",
724 PendingBlockKind::Full => "full",
725 },
726 value_name = "KIND",
727 )]
728 pub rpc_pending_block: PendingBlockKind,
729
730 #[arg(
732 long = "rpc.forwarder",
733 alias = "rpc-forwarder",
734 value_name = "FORWARDER",
735 value_parser = DefaultValueParser {
736 inner: Url::parse,
737 default: &DefaultRpcServerArgs::get_global().rpc_forwarder,
738 },
739 default_value = Resettable::from(DefaultRpcServerArgs::get_global().rpc_forwarder.as_ref().map(|_| "configured".into())),
740 hide_default_value = true,
741 )]
742 pub rpc_forwarder: Option<Url>,
743
744 #[arg(
747 long = "builder.disallow",
748 value_name = "PATH",
749 value_parser = DefaultValueParser {
750 inner: reth_cli_util::parsers::read_json_from_file::<AddressSet>,
751 default: &DefaultRpcServerArgs::get_global().builder_disallow,
752 },
753 default_value = Resettable::from(DefaultRpcServerArgs::get_global().builder_disallow.as_ref().map(|_| "configured".into())),
754 hide_default_value = true,
755 )]
756 pub builder_disallow: Option<AddressSet>,
757
758 #[command(flatten)]
760 pub rpc_state_cache: RpcStateCacheArgs,
761
762 #[command(flatten)]
764 pub gas_price_oracle: GasPriceOracleArgs,
765
766 #[arg(
768 long = "rpc.send-raw-transaction-sync-timeout",
769 value_name = "SECONDS",
770 default_value = format_duration_as_secs_or_ms(DefaultRpcServerArgs::get_global().rpc_send_raw_transaction_sync_timeout),
771 value_parser = parse_duration_from_secs_or_ms,
772 )]
773 pub rpc_send_raw_transaction_sync_timeout: Duration,
774
775 #[arg(long = "testing.skip-invalid-transactions", default_value_t = false)]
780 pub testing_skip_invalid_transactions: bool,
781
782 #[arg(long = "testing.gas-limit", value_name = "GAS_LIMIT", hide = true)]
788 pub testing_gas_limit: Option<u64>,
789
790 #[arg(long = "rpc.force-blob-sidecar-upcasting", default_value_t = false)]
796 pub rpc_force_blob_sidecar_upcasting: bool,
797}
798
799impl RpcServerArgs {
800 pub const fn with_http(mut self) -> Self {
802 self.http = true;
803 self
804 }
805
806 pub fn with_http_api(mut self, http_api: RpcModuleSelection) -> Self {
808 self.http_api = Some(http_api);
809 self
810 }
811
812 pub const fn with_ws(mut self) -> Self {
814 self.ws = true;
815 self
816 }
817
818 pub fn with_ws_api(mut self, ws_api: RpcModuleSelection) -> Self {
820 self.ws_api = Some(ws_api);
821 self
822 }
823
824 pub const fn with_auth_ipc(mut self) -> Self {
826 self.auth_ipc = true;
827 self
828 }
829
830 pub fn with_api(self, api: RpcModuleSelection) -> Self {
834 self.with_http_api(api.clone()).with_ws_api(api)
835 }
836
837 pub fn adjust_instance_ports(&mut self, instance: Option<u16>) {
852 if let Some(instance) = instance {
853 debug_assert_ne!(instance, 0, "instance must be non-zero");
854 self.auth_port += instance * 100 - 100;
856 self.http_port -= instance - 1;
858 self.ws_port += instance * 2 - 2;
860 self.ipcpath = format!("{}-{}", self.ipcpath, instance);
862 }
863 }
864
865 pub const fn with_http_unused_port(mut self) -> Self {
868 self.http_port = 0;
869 self
870 }
871
872 pub const fn with_ws_unused_port(mut self) -> Self {
875 self.ws_port = 0;
876 self
877 }
878
879 pub const fn with_auth_unused_port(mut self) -> Self {
882 self.auth_port = 0;
883 self
884 }
885
886 pub fn with_ipc_random_path(mut self) -> Self {
889 let random_string: String =
890 rand::rng().sample_iter(rand::distr::Alphanumeric).take(8).map(char::from).collect();
891 self.ipcpath = format!("{}-{}", self.ipcpath, random_string);
892 self
893 }
894
895 pub fn with_unused_ports(mut self) -> Self {
898 self = self.with_http_unused_port();
899 self = self.with_ws_unused_port();
900 self = self.with_auth_unused_port();
901 self = self.with_ipc_random_path();
902 self
903 }
904
905 pub fn apply<F>(self, f: F) -> Self
907 where
908 F: FnOnce(Self) -> Self,
909 {
910 f(self)
911 }
912
913 pub const fn with_send_raw_transaction_sync_timeout(mut self, timeout: Duration) -> Self {
915 self.rpc_send_raw_transaction_sync_timeout = timeout;
916 self
917 }
918
919 pub fn is_namespace_enabled(&self, ns: RethRpcModule) -> bool {
921 if self.http && self.http_api.as_ref().is_some_and(|api| api.contains(&ns)) {
922 return true;
923 }
924 if self.ws && self.ws_api.as_ref().is_some_and(|api| api.contains(&ns)) {
925 return true;
926 }
927 !self.ipcdisable
929 }
930
931 pub const fn with_force_blob_sidecar_upcasting(mut self) -> Self {
933 self.rpc_force_blob_sidecar_upcasting = true;
934 self
935 }
936}
937
938impl Default for RpcServerArgs {
939 fn default() -> Self {
940 let DefaultRpcServerArgs {
941 http,
942 http_addr,
943 http_port,
944 http_disable_compression,
945 http_compression_algorithms,
946 http_decompression_algorithms,
947 http_api,
948 http_corsdomain,
949 ws,
950 ws_addr,
951 ws_port,
952 ws_allowed_origins,
953 ws_api,
954 ipcdisable,
955 ipcpath,
956 ipc_socket_permissions,
957 auth_addr,
958 auth_port,
959 auth_jwtsecret,
960 auth_ipc,
961 auth_ipc_path,
962 disable_auth_server,
963 rpc_jwtsecret,
964 rpc_disable_metrics,
965 rpc_max_request_size,
966 rpc_max_response_size,
967 rpc_max_subscriptions_per_connection,
968 rpc_max_connections,
969 rpc_max_tracing_requests,
970 rpc_max_blocking_io_requests,
971 rpc_max_trace_filter_blocks,
972 rpc_max_blocks_per_filter,
973 rpc_max_logs_per_response,
974 rpc_gas_cap,
975 rpc_evm_memory_limit,
976 rpc_tx_fee_cap,
977 rpc_max_simulate_blocks,
978 rpc_compute_state_root_for_eth_simulate,
979 rpc_eth_proof_window,
980 rpc_proof_permits,
981 rpc_pending_block,
982 rpc_forwarder,
983 builder_disallow,
984 rpc_state_cache,
985 gas_price_oracle,
986 rpc_send_raw_transaction_sync_timeout,
987 } = DefaultRpcServerArgs::get_global().clone();
988 Self {
989 http,
990 http_addr,
991 http_port,
992 http_disable_compression,
993 http_compression_algorithms,
994 http_decompression_algorithms,
995 http_api,
996 http_corsdomain,
997 ws,
998 ws_addr,
999 ws_port,
1000 ws_allowed_origins,
1001 ws_api,
1002 ipcdisable,
1003 ipcpath,
1004 ipc_socket_permissions,
1005 auth_addr,
1006 auth_port,
1007 auth_jwtsecret,
1008 auth_jwtsecret_hex: None,
1009 auth_ipc,
1010 auth_ipc_path,
1011 disable_auth_server,
1012 rpc_jwtsecret,
1013 rpc_disable_metrics,
1014 rpc_max_request_size,
1015 rpc_max_response_size,
1016 rpc_max_subscriptions_per_connection,
1017 rpc_max_connections,
1018 rpc_max_tracing_requests,
1019 rpc_max_blocking_io_requests,
1020 rpc_max_trace_filter_blocks,
1021 rpc_max_blocks_per_filter,
1022 rpc_max_logs_per_response,
1023 rpc_gas_cap,
1024 rpc_evm_memory_limit,
1025 rpc_tx_fee_cap,
1026 rpc_max_simulate_blocks,
1027 rpc_compute_state_root_for_eth_simulate,
1028 rpc_eth_proof_window,
1029 rpc_proof_permits,
1030 rpc_pending_block,
1031 rpc_forwarder,
1032 builder_disallow,
1033 rpc_state_cache,
1034 gas_price_oracle,
1035 rpc_send_raw_transaction_sync_timeout,
1036 testing_skip_invalid_transactions: false,
1037 testing_gas_limit: None,
1038 rpc_force_blob_sidecar_upcasting: false,
1039 }
1040 }
1041}
1042
1043fn format_tx_fee_cap(wei: u128) -> String {
1045 format_ether(wei).trim_end_matches('0').trim_end_matches('.').to_string()
1046}
1047
1048#[derive(Clone, Debug, Default)]
1050#[non_exhaustive]
1051struct RpcModuleSelectionValueParser;
1052
1053impl TypedValueParser for RpcModuleSelectionValueParser {
1054 type Value = RpcModuleSelection;
1055
1056 fn parse_ref(
1057 &self,
1058 _cmd: &Command,
1059 _arg: Option<&Arg>,
1060 value: &OsStr,
1061 ) -> Result<Self::Value, clap::Error> {
1062 let val =
1063 value.to_str().ok_or_else(|| clap::Error::new(clap::error::ErrorKind::InvalidUtf8))?;
1064 Ok(val
1066 .parse::<RpcModuleSelection>()
1067 .expect("RpcModuleSelection parsing cannot fail with Other variant"))
1068 }
1069
1070 fn possible_values(&self) -> Option<Box<dyn Iterator<Item = PossibleValue> + '_>> {
1071 let values = RethRpcModule::standard_variant_names().map(PossibleValue::new);
1073 Some(Box::new(values))
1074 }
1075}
1076
1077#[derive(Clone)]
1079struct DefaultValueParser<P: TypedValueParser>
1080where
1081 P::Value: 'static,
1082{
1083 inner: P,
1084 default: &'static Option<P::Value>,
1085}
1086
1087impl<P: TypedValueParser> TypedValueParser for DefaultValueParser<P>
1088where
1089 P::Value: 'static,
1090{
1091 type Value = P::Value;
1092
1093 fn parse_ref(
1094 &self,
1095 cmd: &Command,
1096 arg: Option<&Arg>,
1097 value: &OsStr,
1098 ) -> Result<Self::Value, clap::Error> {
1099 self.inner.parse_ref(cmd, arg, value)
1100 }
1101
1102 fn parse_ref_(
1103 &self,
1104 cmd: &Command,
1105 arg: Option<&Arg>,
1106 value: &OsStr,
1107 source: ValueSource,
1108 ) -> Result<Self::Value, clap::Error> {
1109 if source == ValueSource::DefaultValue &&
1110 let Some(default) = self.default
1111 {
1112 Ok(default.clone())
1113 } else {
1114 self.inner.parse_ref_(cmd, arg, value, source)
1115 }
1116 }
1117}
1118
1119#[cfg(test)]
1120mod tests {
1121 use super::*;
1122 use clap::{Args, Parser};
1123
1124 #[derive(Parser)]
1126 struct CommandParser<T: Args> {
1127 #[command(flatten)]
1128 args: T,
1129 }
1130
1131 #[test]
1132 fn parse_bal_cache_modes() {
1133 for (flag, cache_computed, prewarm) in [
1134 ("--rpc-cache.cache-computed-bals", true, None),
1135 ("--rpc-cache.prewarm-bals", false, Some(0)),
1136 ("--rpc-cache.prewarm-bals=10", false, Some(10)),
1137 ] {
1138 let args = CommandParser::<RpcServerArgs>::parse_from(["reth", flag]).args;
1139 assert_eq!(args.rpc_state_cache.cache_computed_bals, cache_computed);
1140 assert_eq!(args.rpc_state_cache.prewarm_bals, prewarm);
1141 }
1142 }
1143
1144 #[test]
1145 fn test_rpc_server_args_parser() {
1146 let args =
1147 CommandParser::<RpcServerArgs>::parse_from(["reth", "--http.api", "eth,admin,debug"])
1148 .args;
1149
1150 let apis = args.http_api.unwrap();
1151 let expected = RpcModuleSelection::try_from_selection(["eth", "admin", "debug"]).unwrap();
1152
1153 assert_eq!(apis, expected);
1154 }
1155
1156 #[test]
1157 fn test_rpc_server_eth_call_bundle_args() {
1158 let args =
1159 CommandParser::<RpcServerArgs>::parse_from(["reth", "--http.api", "eth,admin,debug"])
1160 .args;
1161
1162 let apis = args.http_api.unwrap();
1163 let expected = RpcModuleSelection::try_from_selection(["eth", "admin", "debug"]).unwrap();
1164
1165 assert_eq!(apis, expected);
1166 }
1167
1168 #[test]
1169 fn test_rpc_server_args_parser_none() {
1170 let args = CommandParser::<RpcServerArgs>::parse_from(["reth", "--http.api", "none"]).args;
1171 let apis = args.http_api.unwrap();
1172 let expected = RpcModuleSelection::Selection(Default::default());
1173 assert_eq!(apis, expected);
1174 }
1175
1176 #[test]
1177 fn rpc_concurrency_limits_reject_zero() {
1178 for flag in
1179 ["--rpc.max-tracing-requests", "--rpc.max-blocking-io-requests", "--rpc.proof-permits"]
1180 {
1181 let result = CommandParser::<RpcServerArgs>::try_parse_from(["reth", flag, "0"]);
1182 assert!(result.is_err(), "{flag} should reject zero");
1183 }
1184
1185 let args = CommandParser::<RpcServerArgs>::parse_from([
1186 "reth",
1187 "--rpc.max-tracing-requests",
1188 "1",
1189 "--rpc.max-blocking-io-requests",
1190 "2",
1191 "--rpc.proof-permits",
1192 "3",
1193 ])
1194 .args;
1195 assert_eq!(args.rpc_max_tracing_requests, 1);
1196 assert_eq!(args.rpc_max_blocking_io_requests, 2);
1197 assert_eq!(args.rpc_proof_permits, 3);
1198 }
1199
1200 #[test]
1201 fn http_compression_algorithms_are_optional() {
1202 let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
1203 assert!(args.http_compression_algorithms.is_none());
1204
1205 let args = CommandParser::<RpcServerArgs>::parse_from([
1206 "reth",
1207 "--http.compression",
1208 "zstd,gzip,deflate,br",
1209 ])
1210 .args;
1211 assert_eq!(
1212 args.http_compression_algorithms.as_deref().unwrap(),
1213 ["zstd", "gzip", "deflate", "br"]
1214 );
1215
1216 let result =
1217 CommandParser::<RpcServerArgs>::try_parse_from(["reth", "--http.compression", "gizp"]);
1218 assert!(result.is_err());
1219 }
1220
1221 #[test]
1222 fn rpc_server_args_default_sanity_test() {
1223 let default_args = RpcServerArgs::default();
1224 let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
1225
1226 assert_eq!(args, default_args);
1227 }
1228
1229 #[test]
1230 fn http_request_decompression_is_opt_in() {
1231 let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
1232 assert!(args.http_decompression_algorithms.is_none());
1233
1234 let args = CommandParser::<RpcServerArgs>::parse_from([
1235 "reth",
1236 "--http.decompression",
1237 "zstd,gzip,deflate,br",
1238 ])
1239 .args;
1240 assert_eq!(
1241 args.http_decompression_algorithms.as_deref().unwrap(),
1242 ["zstd", "gzip", "deflate", "br"]
1243 );
1244 }
1245
1246 #[test]
1247 fn invalid_http_request_decompression_algorithm_is_rejected() {
1248 let result = CommandParser::<RpcServerArgs>::try_parse_from([
1249 "reth",
1250 "--http.decompression",
1251 "gizp",
1252 ]);
1253 assert!(result.is_err());
1254 }
1255
1256 #[test]
1257 fn test_rpc_disable_metrics_arg() {
1258 let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
1259 assert!(!args.rpc_disable_metrics);
1260
1261 let args =
1262 CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.disable-metrics"]).args;
1263 assert!(args.rpc_disable_metrics);
1264 }
1265
1266 #[test]
1267 fn test_rpc_tx_fee_cap_parse_integer() {
1268 let args = CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.txfeecap", "2"]).args;
1269 let expected = 2_000_000_000_000_000_000u128; assert_eq!(args.rpc_tx_fee_cap, expected);
1271 }
1272
1273 #[test]
1274 fn test_rpc_tx_fee_cap_parse_decimal() {
1275 let args =
1276 CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.txfeecap", "1.5"]).args;
1277 let expected = 1_500_000_000_000_000_000u128; assert_eq!(args.rpc_tx_fee_cap, expected);
1279 }
1280
1281 #[test]
1282 fn test_rpc_tx_fee_cap_parse_zero() {
1283 let args = CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.txfeecap", "0"]).args;
1284 assert_eq!(args.rpc_tx_fee_cap, 0); }
1286
1287 #[test]
1288 fn test_rpc_tx_fee_cap_parse_none() {
1289 let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
1290 let expected = 1_000_000_000_000_000_000u128;
1291 assert_eq!(args.rpc_tx_fee_cap, expected); }
1293
1294 #[test]
1295 fn test_rpc_server_args() {
1296 let args = RpcServerArgs {
1297 http: true,
1298 http_addr: "127.0.0.1".parse().unwrap(),
1299 http_port: 8545,
1300 http_disable_compression: false,
1301 http_compression_algorithms: None,
1302 http_decompression_algorithms: None,
1303 http_api: Some(RpcModuleSelection::try_from_selection(["eth", "admin"]).unwrap()),
1304 http_corsdomain: Some("*".to_string()),
1305 ws: true,
1306 ws_addr: "127.0.0.1".parse().unwrap(),
1307 ws_port: 8546,
1308 ws_allowed_origins: Some("*".to_string()),
1309 ws_api: Some(RpcModuleSelection::try_from_selection(["eth", "admin"]).unwrap()),
1310 ipcdisable: false,
1311 ipcpath: "reth.ipc".to_string(),
1312 ipc_socket_permissions: Some("0o666".to_string()),
1313 auth_addr: "127.0.0.1".parse().unwrap(),
1314 auth_port: 8551,
1315 auth_jwtsecret: Some(std::path::PathBuf::from("/tmp/jwt.hex")),
1316 auth_jwtsecret_hex: None,
1317 auth_ipc: false,
1318 auth_ipc_path: "engine.ipc".to_string(),
1319 disable_auth_server: false,
1320 rpc_jwtsecret: Some(
1321 JwtSecret::from_hex(
1322 "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
1323 )
1324 .unwrap(),
1325 ),
1326 rpc_disable_metrics: false,
1327 rpc_max_request_size: 15u32.into(),
1328 rpc_max_response_size: 160u32.into(),
1329 rpc_max_subscriptions_per_connection: 1024u32.into(),
1330 rpc_max_connections: 500u32.into(),
1331 rpc_max_tracing_requests: 16,
1332 rpc_max_blocking_io_requests: 256,
1333 rpc_max_trace_filter_blocks: 4000,
1334 rpc_max_blocks_per_filter: 1000u64.into(),
1335 rpc_max_logs_per_response: 10000u64.into(),
1336 rpc_gas_cap: 50_000_000,
1337 rpc_evm_memory_limit: 256,
1338 rpc_tx_fee_cap: 2_000_000_000_000_000_000u128,
1339 rpc_max_simulate_blocks: 256,
1340 rpc_compute_state_root_for_eth_simulate: false,
1341 rpc_eth_proof_window: 100_000,
1342 rpc_proof_permits: 16,
1343 rpc_pending_block: PendingBlockKind::Full,
1344 rpc_forwarder: Some("http://localhost:8545".parse().unwrap()),
1345 builder_disallow: None,
1346 rpc_state_cache: RpcStateCacheArgs {
1347 max_blocks: 5000,
1348 max_receipts: 2000,
1349 max_headers: 1000,
1350 max_bals: 1000,
1351 cache_computed_bals: true,
1352 prewarm_bals: Some(0),
1353 max_concurrent_db_requests: 512,
1354 max_cached_tx_hashes: 100_000,
1355 ..Default::default()
1356 },
1357 gas_price_oracle: GasPriceOracleArgs {
1358 blocks: 20,
1359 ignore_price: 2,
1360 max_price: 500_000_000_000,
1361 percentile: 60,
1362 default_suggested_fee: None,
1363 },
1364 rpc_send_raw_transaction_sync_timeout: std::time::Duration::from_secs(30),
1365 testing_skip_invalid_transactions: true,
1366 testing_gas_limit: None,
1367 rpc_force_blob_sidecar_upcasting: false,
1368 };
1369
1370 let parsed_args = CommandParser::<RpcServerArgs>::parse_from([
1371 "reth",
1372 "--http",
1373 "--http.addr",
1374 "127.0.0.1",
1375 "--http.port",
1376 "8545",
1377 "--http.api",
1378 "eth,admin",
1379 "--http.corsdomain",
1380 "*",
1381 "--ws",
1382 "--ws.addr",
1383 "127.0.0.1",
1384 "--ws.port",
1385 "8546",
1386 "--ws.origins",
1387 "*",
1388 "--ws.api",
1389 "eth,admin",
1390 "--ipcpath",
1391 "reth.ipc",
1392 "--ipc.permissions",
1393 "0o666",
1394 "--authrpc.addr",
1395 "127.0.0.1",
1396 "--authrpc.port",
1397 "8551",
1398 "--authrpc.jwtsecret",
1399 "/tmp/jwt.hex",
1400 "--auth-ipc.path",
1401 "engine.ipc",
1402 "--rpc.jwtsecret",
1403 "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
1404 "--rpc.max-request-size",
1405 "15",
1406 "--rpc.max-response-size",
1407 "160",
1408 "--rpc.max-subscriptions-per-connection",
1409 "1024",
1410 "--rpc.max-connections",
1411 "500",
1412 "--rpc.max-tracing-requests",
1413 "16",
1414 "--rpc.max-blocking-io-requests",
1415 "256",
1416 "--rpc.max-trace-filter-blocks",
1417 "4000",
1418 "--rpc.max-blocks-per-filter",
1419 "1000",
1420 "--rpc.max-logs-per-response",
1421 "10000",
1422 "--rpc.gascap",
1423 "50000000",
1424 "--rpc.evm-memory-limit",
1425 "256",
1426 "--rpc.txfeecap",
1427 "2.0",
1428 "--rpc.max-simulate-blocks",
1429 "256",
1430 "--rpc.eth-proof-window",
1431 "100000",
1432 "--rpc.proof-permits",
1433 "16",
1434 "--rpc.pending-block",
1435 "full",
1436 "--rpc.forwarder",
1437 "http://localhost:8545",
1438 "--rpc-cache.max-blocks",
1439 "5000",
1440 "--rpc-cache.max-receipts",
1441 "2000",
1442 "--rpc-cache.max-headers",
1443 "1000",
1444 "--rpc-cache.max-bals",
1445 "1000",
1446 "--rpc-cache.cache-computed-bals",
1447 "--rpc-cache.prewarm-bals",
1448 "--rpc-cache.max-concurrent-db-requests",
1449 "512",
1450 "--gpo.blocks",
1451 "20",
1452 "--gpo.ignoreprice",
1453 "2",
1454 "--gpo.maxprice",
1455 "500000000000",
1456 "--gpo.percentile",
1457 "60",
1458 "--rpc.send-raw-transaction-sync-timeout",
1459 "30s",
1460 "--testing.skip-invalid-transactions",
1461 ])
1462 .args;
1463
1464 assert_eq!(parsed_args, args);
1465 }
1466
1467 #[test]
1468 fn parse_auth_jwtsecret_hex() {
1469 let hex = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
1470 let args =
1471 CommandParser::<RpcServerArgs>::parse_from(["reth", "--authrpc.jwtsecret-hex", hex])
1472 .args;
1473
1474 let expected = JwtSecret::from_hex(hex).unwrap();
1475 assert_eq!(args.auth_jwtsecret_hex, Some(expected));
1476 assert_eq!(args.auth_jwtsecret, None);
1477 }
1478
1479 #[test]
1480 fn test_auth_jwtsecret_and_hex_are_mutually_exclusive() {
1481 let result = CommandParser::<RpcServerArgs>::try_parse_from([
1482 "reth",
1483 "--authrpc.jwtsecret",
1484 "/tmp/jwt.hex",
1485 "--authrpc.jwtsecret-hex",
1486 "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
1487 ]);
1488
1489 assert!(result.is_err());
1490 }
1491}