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())))]
540 pub ipc_socket_permissions: Option<String>,
541
542 #[arg(long = "authrpc.addr", default_value_t = DefaultRpcServerArgs::get_global().auth_addr)]
544 pub auth_addr: IpAddr,
545
546 #[arg(long = "authrpc.port", default_value_t = DefaultRpcServerArgs::get_global().auth_port)]
548 pub auth_port: u16,
549
550 #[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())))]
558 pub auth_jwtsecret: Option<PathBuf>,
559
560 #[arg(
565 long = "authrpc.jwtsecret-hex",
566 value_name = "HEX",
567 global = true,
568 required = false,
569 conflicts_with = "auth_jwtsecret"
570 )]
571 pub auth_jwtsecret_hex: Option<JwtSecret>,
572
573 #[arg(long, default_value_t = DefaultRpcServerArgs::get_global().auth_ipc)]
575 pub auth_ipc: bool,
576
577 #[arg(long = "auth-ipc.path")]
582 pub auth_ipc_path: Option<String>,
583
584 #[arg(long = "disable-auth-server", alias = "disable-engine-api", default_value_t = DefaultRpcServerArgs::get_global().disable_auth_server)]
589 pub disable_auth_server: bool,
590
591 #[arg(
597 long = "rpc.jwtsecret",
598 value_name = "HEX",
599 global = true,
600 required = false,
601 value_parser = DefaultValueParser {
602 inner: |value: &str| JwtSecret::from_hex(value),
603 default: &DefaultRpcServerArgs::get_global().rpc_jwtsecret,
604 },
605 default_value = Resettable::from(DefaultRpcServerArgs::get_global().rpc_jwtsecret.as_ref().map(|_| "configured".into())),
606 hide_default_value = true,
607 )]
608 pub rpc_jwtsecret: Option<JwtSecret>,
609
610 #[arg(long = "rpc.disable-metrics", default_value_t = DefaultRpcServerArgs::get_global().rpc_disable_metrics)]
612 pub rpc_disable_metrics: bool,
613
614 #[arg(long = "rpc.max-request-size", alias = "rpc-max-request-size", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_request_size)]
619 pub rpc_max_request_size: MaxU32,
620
621 #[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)]
623 pub rpc_max_response_size: MaxU32,
624
625 #[arg(long = "rpc.max-subscriptions-per-connection", alias = "rpc-max-subscriptions-per-connection", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_subscriptions_per_connection)]
627 pub rpc_max_subscriptions_per_connection: MaxU32,
628
629 #[arg(long = "rpc.max-connections", alias = "rpc-max-connections", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_connections)]
631 pub rpc_max_connections: MaxU32,
632
633 #[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)]
640 pub rpc_max_tracing_requests: usize,
641
642 #[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)]
648 pub rpc_max_blocking_io_requests: usize,
649
650 #[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)]
652 pub rpc_max_trace_filter_blocks: u64,
653
654 #[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)]
656 pub rpc_max_blocks_per_filter: ZeroAsNoneU64,
657
658 #[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)]
660 pub rpc_max_logs_per_response: ZeroAsNoneU64,
661
662 #[arg(
666 long = "rpc.gascap",
667 alias = "rpc-gascap",
668 value_name = "GAS_CAP",
669 value_parser = MaxOr::new(RangedU64ValueParser::<u64>::new().range(1..)),
670 default_value_t = DefaultRpcServerArgs::get_global().rpc_gas_cap
671 )]
672 pub rpc_gas_cap: u64,
673
674 #[arg(
676 long = "rpc.evm-memory-limit",
677 alias = "rpc-evm-memory-limit",
678 value_name = "MEMORY_LIMIT",
679 value_parser = MaxOr::new(RangedU64ValueParser::<u64>::new().range(1..)),
680 default_value_t = DefaultRpcServerArgs::get_global().rpc_evm_memory_limit
681 )]
682 pub rpc_evm_memory_limit: u64,
683
684 #[arg(
686 long = "rpc.txfeecap",
687 alias = "rpc-txfeecap",
688 value_name = "TX_FEE_CAP",
689 value_parser = parse_ether_value,
690 default_value = format_tx_fee_cap(DefaultRpcServerArgs::get_global().rpc_tx_fee_cap)
691 )]
692 pub rpc_tx_fee_cap: u128,
693
694 #[arg(
696 long = "rpc.max-simulate-blocks",
697 value_name = "BLOCKS_COUNT",
698 default_value_t = DefaultRpcServerArgs::get_global().rpc_max_simulate_blocks
699 )]
700 pub rpc_max_simulate_blocks: u64,
701
702 #[arg(
704 long = "rpc.compute-state-root-for-eth-simulate",
705 env = "RETH_RPC_COMPUTE_STATE_ROOT_FOR_ETH_SIMULATE",
706 default_value_t = DefaultRpcServerArgs::get_global().rpc_compute_state_root_for_eth_simulate
707 )]
708 pub rpc_compute_state_root_for_eth_simulate: bool,
709
710 #[arg(
714 long = "rpc.eth-proof-window",
715 default_value_t = DefaultRpcServerArgs::get_global().rpc_eth_proof_window,
716 value_parser = RangedU64ValueParser::<u64>::new().range(..=constants::MAX_ETH_PROOF_WINDOW)
717 )]
718 pub rpc_eth_proof_window: u64,
719
720 #[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)]
722 pub rpc_proof_permits: usize,
723
724 #[arg(
729 long = "rpc.pending-block",
730 default_value = match DefaultRpcServerArgs::get_global().rpc_pending_block {
731 PendingBlockKind::Empty => "empty",
732 PendingBlockKind::None => "none",
733 PendingBlockKind::Full => "full",
734 },
735 value_name = "KIND",
736 )]
737 pub rpc_pending_block: PendingBlockKind,
738
739 #[arg(
741 long = "rpc.forwarder",
742 alias = "rpc-forwarder",
743 value_name = "FORWARDER",
744 value_parser = DefaultValueParser {
745 inner: Url::parse,
746 default: &DefaultRpcServerArgs::get_global().rpc_forwarder,
747 },
748 default_value = Resettable::from(DefaultRpcServerArgs::get_global().rpc_forwarder.as_ref().map(|_| "configured".into())),
749 hide_default_value = true,
750 )]
751 pub rpc_forwarder: Option<Url>,
752
753 #[arg(
756 long = "builder.disallow",
757 value_name = "PATH",
758 value_parser = DefaultValueParser {
759 inner: reth_cli_util::parsers::read_json_from_file::<AddressSet>,
760 default: &DefaultRpcServerArgs::get_global().builder_disallow,
761 },
762 default_value = Resettable::from(DefaultRpcServerArgs::get_global().builder_disallow.as_ref().map(|_| "configured".into())),
763 hide_default_value = true,
764 )]
765 pub builder_disallow: Option<AddressSet>,
766
767 #[command(flatten)]
769 pub rpc_state_cache: RpcStateCacheArgs,
770
771 #[command(flatten)]
773 pub gas_price_oracle: GasPriceOracleArgs,
774
775 #[arg(
777 long = "rpc.send-raw-transaction-sync-timeout",
778 value_name = "SECONDS",
779 default_value = format_duration_as_secs_or_ms(DefaultRpcServerArgs::get_global().rpc_send_raw_transaction_sync_timeout),
780 value_parser = parse_duration_from_secs_or_ms,
781 )]
782 pub rpc_send_raw_transaction_sync_timeout: Duration,
783
784 #[arg(long = "testing.skip-invalid-transactions", default_value_t = false)]
789 pub testing_skip_invalid_transactions: bool,
790
791 #[arg(long = "testing.gas-limit", value_name = "GAS_LIMIT", hide = true)]
797 pub testing_gas_limit: Option<u64>,
798
799 #[arg(long = "rpc.force-blob-sidecar-upcasting", default_value_t = false)]
805 pub rpc_force_blob_sidecar_upcasting: bool,
806}
807
808impl RpcServerArgs {
809 pub const fn with_http(mut self) -> Self {
811 self.http = true;
812 self
813 }
814
815 pub fn with_http_api(mut self, http_api: RpcModuleSelection) -> Self {
817 self.http_api = Some(http_api);
818 self
819 }
820
821 pub const fn with_ws(mut self) -> Self {
823 self.ws = true;
824 self
825 }
826
827 pub fn with_ws_api(mut self, ws_api: RpcModuleSelection) -> Self {
829 self.ws_api = Some(ws_api);
830 self
831 }
832
833 pub const fn with_auth_ipc(mut self) -> Self {
835 self.auth_ipc = true;
836 self
837 }
838
839 pub fn with_api(self, api: RpcModuleSelection) -> Self {
843 self.with_http_api(api.clone()).with_ws_api(api)
844 }
845
846 pub fn adjust_instance_ports(&mut self, instance: Option<u16>) {
863 if let Some(instance) = instance {
864 debug_assert_ne!(instance, 0, "instance must be non-zero");
865 self.auth_port += instance * 100 - 100;
867 self.http_port -= instance - 1;
869 self.ws_port += instance * 2 - 2;
871 self.ipcpath = format!("{}-{}", self.ipcpath, instance);
873 if self.auth_ipc_path.is_none() {
874 self.auth_ipc_path = Some(format!("{}-{instance}", self.auth_ipc_path()));
875 }
876 }
877 }
878
879 pub const fn with_http_unused_port(mut self) -> Self {
882 self.http_port = 0;
883 self
884 }
885
886 pub const fn with_ws_unused_port(mut self) -> Self {
889 self.ws_port = 0;
890 self
891 }
892
893 pub const fn with_auth_unused_port(mut self) -> Self {
896 self.auth_port = 0;
897 self
898 }
899
900 pub fn with_ipc_random_path(mut self) -> Self {
903 let random_string: String =
904 rand::rng().sample_iter(rand::distr::Alphanumeric).take(8).map(char::from).collect();
905 self.ipcpath = format!("{}-{}", self.ipcpath, random_string);
906 self
907 }
908
909 pub fn with_unused_ports(mut self) -> Self {
912 self = self.with_http_unused_port();
913 self = self.with_ws_unused_port();
914 self = self.with_auth_unused_port();
915 self = self.with_ipc_random_path();
916 self
917 }
918
919 pub fn apply<F>(self, f: F) -> Self
921 where
922 F: FnOnce(Self) -> Self,
923 {
924 f(self)
925 }
926
927 pub const fn with_send_raw_transaction_sync_timeout(mut self, timeout: Duration) -> Self {
929 self.rpc_send_raw_transaction_sync_timeout = timeout;
930 self
931 }
932
933 pub fn is_namespace_enabled(&self, ns: RethRpcModule) -> bool {
935 if self.http &&
936 self.http_api.as_ref().unwrap_or(&RpcModuleSelection::Standard).contains(&ns)
937 {
938 return true;
939 }
940 if self.ws && self.ws_api.as_ref().unwrap_or(&RpcModuleSelection::Standard).contains(&ns) {
941 return true;
942 }
943 !self.ipcdisable && RpcModuleSelection::default_ipc_modules().contains(&ns)
944 }
945
946 pub const fn with_force_blob_sidecar_upcasting(mut self) -> Self {
948 self.rpc_force_blob_sidecar_upcasting = true;
949 self
950 }
951
952 pub fn auth_ipc_path(&self) -> &str {
956 self.auth_ipc_path.as_deref().unwrap_or(&DefaultRpcServerArgs::get_global().auth_ipc_path)
957 }
958}
959
960impl Default for RpcServerArgs {
961 fn default() -> Self {
962 let DefaultRpcServerArgs {
963 http,
964 http_addr,
965 http_port,
966 http_disable_compression,
967 http_compression_algorithms,
968 http_decompression_algorithms,
969 http_api,
970 http_corsdomain,
971 ws,
972 ws_addr,
973 ws_port,
974 ws_allowed_origins,
975 ws_api,
976 ipcdisable,
977 ipcpath,
978 ipc_socket_permissions,
979 auth_addr,
980 auth_port,
981 auth_jwtsecret,
982 auth_ipc,
983 auth_ipc_path: _,
984 disable_auth_server,
985 rpc_jwtsecret,
986 rpc_disable_metrics,
987 rpc_max_request_size,
988 rpc_max_response_size,
989 rpc_max_subscriptions_per_connection,
990 rpc_max_connections,
991 rpc_max_tracing_requests,
992 rpc_max_blocking_io_requests,
993 rpc_max_trace_filter_blocks,
994 rpc_max_blocks_per_filter,
995 rpc_max_logs_per_response,
996 rpc_gas_cap,
997 rpc_evm_memory_limit,
998 rpc_tx_fee_cap,
999 rpc_max_simulate_blocks,
1000 rpc_compute_state_root_for_eth_simulate,
1001 rpc_eth_proof_window,
1002 rpc_proof_permits,
1003 rpc_pending_block,
1004 rpc_forwarder,
1005 builder_disallow,
1006 rpc_state_cache,
1007 gas_price_oracle,
1008 rpc_send_raw_transaction_sync_timeout,
1009 } = DefaultRpcServerArgs::get_global().clone();
1010 Self {
1011 http,
1012 http_addr,
1013 http_port,
1014 http_disable_compression,
1015 http_compression_algorithms,
1016 http_decompression_algorithms,
1017 http_api,
1018 http_corsdomain,
1019 ws,
1020 ws_addr,
1021 ws_port,
1022 ws_allowed_origins,
1023 ws_api,
1024 ipcdisable,
1025 ipcpath,
1026 ipc_socket_permissions,
1027 auth_addr,
1028 auth_port,
1029 auth_jwtsecret,
1030 auth_jwtsecret_hex: None,
1031 auth_ipc,
1032 auth_ipc_path: None,
1033 disable_auth_server,
1034 rpc_jwtsecret,
1035 rpc_disable_metrics,
1036 rpc_max_request_size,
1037 rpc_max_response_size,
1038 rpc_max_subscriptions_per_connection,
1039 rpc_max_connections,
1040 rpc_max_tracing_requests,
1041 rpc_max_blocking_io_requests,
1042 rpc_max_trace_filter_blocks,
1043 rpc_max_blocks_per_filter,
1044 rpc_max_logs_per_response,
1045 rpc_gas_cap,
1046 rpc_evm_memory_limit,
1047 rpc_tx_fee_cap,
1048 rpc_max_simulate_blocks,
1049 rpc_compute_state_root_for_eth_simulate,
1050 rpc_eth_proof_window,
1051 rpc_proof_permits,
1052 rpc_pending_block,
1053 rpc_forwarder,
1054 builder_disallow,
1055 rpc_state_cache,
1056 gas_price_oracle,
1057 rpc_send_raw_transaction_sync_timeout,
1058 testing_skip_invalid_transactions: false,
1059 testing_gas_limit: None,
1060 rpc_force_blob_sidecar_upcasting: false,
1061 }
1062 }
1063}
1064
1065fn format_tx_fee_cap(wei: u128) -> String {
1067 format_ether(wei).trim_end_matches('0').trim_end_matches('.').to_string()
1068}
1069
1070#[derive(Clone, Debug, Default)]
1072#[non_exhaustive]
1073struct RpcModuleSelectionValueParser;
1074
1075impl TypedValueParser for RpcModuleSelectionValueParser {
1076 type Value = RpcModuleSelection;
1077
1078 fn parse_ref(
1079 &self,
1080 _cmd: &Command,
1081 _arg: Option<&Arg>,
1082 value: &OsStr,
1083 ) -> Result<Self::Value, clap::Error> {
1084 let val =
1085 value.to_str().ok_or_else(|| clap::Error::new(clap::error::ErrorKind::InvalidUtf8))?;
1086 Ok(val
1088 .parse::<RpcModuleSelection>()
1089 .expect("RpcModuleSelection parsing cannot fail with Other variant"))
1090 }
1091
1092 fn possible_values(&self) -> Option<Box<dyn Iterator<Item = PossibleValue> + '_>> {
1093 let values = RethRpcModule::standard_variant_names().map(PossibleValue::new);
1095 Some(Box::new(values))
1096 }
1097}
1098
1099#[derive(Clone)]
1101struct DefaultValueParser<P: TypedValueParser>
1102where
1103 P::Value: 'static,
1104{
1105 inner: P,
1106 default: &'static Option<P::Value>,
1107}
1108
1109impl<P: TypedValueParser> TypedValueParser for DefaultValueParser<P>
1110where
1111 P::Value: 'static,
1112{
1113 type Value = P::Value;
1114
1115 fn parse_ref(
1116 &self,
1117 cmd: &Command,
1118 arg: Option<&Arg>,
1119 value: &OsStr,
1120 ) -> Result<Self::Value, clap::Error> {
1121 self.inner.parse_ref(cmd, arg, value)
1122 }
1123
1124 fn parse_ref_(
1125 &self,
1126 cmd: &Command,
1127 arg: Option<&Arg>,
1128 value: &OsStr,
1129 source: ValueSource,
1130 ) -> Result<Self::Value, clap::Error> {
1131 if source == ValueSource::DefaultValue &&
1132 let Some(default) = self.default
1133 {
1134 Ok(default.clone())
1135 } else {
1136 self.inner.parse_ref_(cmd, arg, value, source)
1137 }
1138 }
1139}
1140
1141#[cfg(test)]
1142mod tests {
1143 use super::*;
1144 use clap::{Args, Parser};
1145
1146 #[derive(Parser)]
1148 struct CommandParser<T: Args> {
1149 #[command(flatten)]
1150 args: T,
1151 }
1152
1153 #[test]
1154 fn parse_bal_cache_modes() {
1155 for (flag, cache_computed, prewarm) in [
1156 ("--rpc-cache.cache-computed-bals", true, None),
1157 ("--rpc-cache.prewarm-bals", false, Some(0)),
1158 ("--rpc-cache.prewarm-bals=10", false, Some(10)),
1159 ] {
1160 let args = CommandParser::<RpcServerArgs>::parse_from(["reth", flag]).args;
1161 assert_eq!(args.rpc_state_cache.cache_computed_bals, cache_computed);
1162 assert_eq!(args.rpc_state_cache.prewarm_bals, prewarm);
1163 }
1164 }
1165
1166 #[test]
1167 fn test_rpc_server_args_parser() {
1168 let args =
1169 CommandParser::<RpcServerArgs>::parse_from(["reth", "--http.api", "eth,admin,debug"])
1170 .args;
1171
1172 let apis = args.http_api.unwrap();
1173 let expected = RpcModuleSelection::try_from_selection(["eth", "admin", "debug"]).unwrap();
1174
1175 assert_eq!(apis, expected);
1176 }
1177
1178 #[test]
1179 fn test_rpc_server_eth_call_bundle_args() {
1180 let args =
1181 CommandParser::<RpcServerArgs>::parse_from(["reth", "--http.api", "eth,admin,debug"])
1182 .args;
1183
1184 let apis = args.http_api.unwrap();
1185 let expected = RpcModuleSelection::try_from_selection(["eth", "admin", "debug"]).unwrap();
1186
1187 assert_eq!(apis, expected);
1188 }
1189
1190 #[test]
1191 fn test_rpc_server_args_parser_none() {
1192 let args = CommandParser::<RpcServerArgs>::parse_from(["reth", "--http.api", "none"]).args;
1193 let apis = args.http_api.unwrap();
1194 let expected = RpcModuleSelection::Selection(Default::default());
1195 assert_eq!(apis, expected);
1196 }
1197
1198 #[test]
1199 fn rpc_concurrency_limits_reject_zero() {
1200 for flag in
1201 ["--rpc.max-tracing-requests", "--rpc.max-blocking-io-requests", "--rpc.proof-permits"]
1202 {
1203 let result = CommandParser::<RpcServerArgs>::try_parse_from(["reth", flag, "0"]);
1204 assert!(result.is_err(), "{flag} should reject zero");
1205 }
1206
1207 let args = CommandParser::<RpcServerArgs>::parse_from([
1208 "reth",
1209 "--rpc.max-tracing-requests",
1210 "1",
1211 "--rpc.max-blocking-io-requests",
1212 "2",
1213 "--rpc.proof-permits",
1214 "3",
1215 ])
1216 .args;
1217 assert_eq!(args.rpc_max_tracing_requests, 1);
1218 assert_eq!(args.rpc_max_blocking_io_requests, 2);
1219 assert_eq!(args.rpc_proof_permits, 3);
1220 }
1221
1222 #[test]
1223 fn http_compression_algorithms_are_optional() {
1224 let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
1225 assert!(args.http_compression_algorithms.is_none());
1226
1227 let args = CommandParser::<RpcServerArgs>::parse_from([
1228 "reth",
1229 "--http.compression",
1230 "zstd,gzip,deflate,br",
1231 ])
1232 .args;
1233 assert_eq!(
1234 args.http_compression_algorithms.as_deref().unwrap(),
1235 ["zstd", "gzip", "deflate", "br"]
1236 );
1237
1238 let result =
1239 CommandParser::<RpcServerArgs>::try_parse_from(["reth", "--http.compression", "gizp"]);
1240 assert!(result.is_err());
1241 }
1242
1243 #[test]
1244 fn rpc_server_args_default_sanity_test() {
1245 let default_args = RpcServerArgs::default();
1246 let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
1247
1248 assert_eq!(args, default_args);
1249 }
1250
1251 #[test]
1252 fn http_request_decompression_is_opt_in() {
1253 let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
1254 assert!(args.http_decompression_algorithms.is_none());
1255
1256 let args = CommandParser::<RpcServerArgs>::parse_from([
1257 "reth",
1258 "--http.decompression",
1259 "zstd,gzip,deflate,br",
1260 ])
1261 .args;
1262 assert_eq!(
1263 args.http_decompression_algorithms.as_deref().unwrap(),
1264 ["zstd", "gzip", "deflate", "br"]
1265 );
1266 }
1267
1268 #[test]
1269 fn invalid_http_request_decompression_algorithm_is_rejected() {
1270 let result = CommandParser::<RpcServerArgs>::try_parse_from([
1271 "reth",
1272 "--http.decompression",
1273 "gizp",
1274 ]);
1275 assert!(result.is_err());
1276 }
1277
1278 #[test]
1279 fn test_rpc_disable_metrics_arg() {
1280 let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
1281 assert!(!args.rpc_disable_metrics);
1282
1283 let args =
1284 CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.disable-metrics"]).args;
1285 assert!(args.rpc_disable_metrics);
1286 }
1287
1288 #[test]
1289 fn test_rpc_tx_fee_cap_parse_integer() {
1290 let args = CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.txfeecap", "2"]).args;
1291 let expected = 2_000_000_000_000_000_000u128; assert_eq!(args.rpc_tx_fee_cap, expected);
1293 }
1294
1295 #[test]
1296 fn test_rpc_tx_fee_cap_parse_decimal() {
1297 let args =
1298 CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.txfeecap", "1.5"]).args;
1299 let expected = 1_500_000_000_000_000_000u128; assert_eq!(args.rpc_tx_fee_cap, expected);
1301 }
1302
1303 #[test]
1304 fn test_rpc_tx_fee_cap_parse_zero() {
1305 let args = CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.txfeecap", "0"]).args;
1306 assert_eq!(args.rpc_tx_fee_cap, 0); }
1308
1309 #[test]
1310 fn test_rpc_tx_fee_cap_parse_none() {
1311 let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
1312 let expected = 1_000_000_000_000_000_000u128;
1313 assert_eq!(args.rpc_tx_fee_cap, expected); }
1315
1316 #[test]
1317 fn test_rpc_server_args() {
1318 let args = RpcServerArgs {
1319 http: true,
1320 http_addr: "127.0.0.1".parse().unwrap(),
1321 http_port: 8545,
1322 http_disable_compression: false,
1323 http_compression_algorithms: None,
1324 http_decompression_algorithms: None,
1325 http_api: Some(RpcModuleSelection::try_from_selection(["eth", "admin"]).unwrap()),
1326 http_corsdomain: Some("*".to_string()),
1327 ws: true,
1328 ws_addr: "127.0.0.1".parse().unwrap(),
1329 ws_port: 8546,
1330 ws_allowed_origins: Some("*".to_string()),
1331 ws_api: Some(RpcModuleSelection::try_from_selection(["eth", "admin"]).unwrap()),
1332 ipcdisable: false,
1333 ipcpath: "reth.ipc".to_string(),
1334 ipc_socket_permissions: Some("0o666".to_string()),
1335 auth_addr: "127.0.0.1".parse().unwrap(),
1336 auth_port: 8551,
1337 auth_jwtsecret: Some(std::path::PathBuf::from("/tmp/jwt.hex")),
1338 auth_jwtsecret_hex: None,
1339 auth_ipc: false,
1340 auth_ipc_path: Some("engine.ipc".to_string()),
1341 disable_auth_server: false,
1342 rpc_jwtsecret: Some(
1343 JwtSecret::from_hex(
1344 "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
1345 )
1346 .unwrap(),
1347 ),
1348 rpc_disable_metrics: false,
1349 rpc_max_request_size: 15u32.into(),
1350 rpc_max_response_size: 160u32.into(),
1351 rpc_max_subscriptions_per_connection: 1024u32.into(),
1352 rpc_max_connections: 500u32.into(),
1353 rpc_max_tracing_requests: 16,
1354 rpc_max_blocking_io_requests: 256,
1355 rpc_max_trace_filter_blocks: 4000,
1356 rpc_max_blocks_per_filter: 1000u64.into(),
1357 rpc_max_logs_per_response: 10000u64.into(),
1358 rpc_gas_cap: 50_000_000,
1359 rpc_evm_memory_limit: 256,
1360 rpc_tx_fee_cap: 2_000_000_000_000_000_000u128,
1361 rpc_max_simulate_blocks: 256,
1362 rpc_compute_state_root_for_eth_simulate: false,
1363 rpc_eth_proof_window: 100_000,
1364 rpc_proof_permits: 16,
1365 rpc_pending_block: PendingBlockKind::Full,
1366 rpc_forwarder: Some("http://localhost:8545".parse().unwrap()),
1367 builder_disallow: None,
1368 rpc_state_cache: RpcStateCacheArgs {
1369 max_blocks: 5000,
1370 max_receipts: 2000,
1371 max_bals: 1000,
1372 cache_computed_bals: true,
1373 prewarm_bals: Some(0),
1374 max_concurrent_db_requests: 512,
1375 max_cached_tx_hashes: 100_000,
1376 ..Default::default()
1377 },
1378 gas_price_oracle: GasPriceOracleArgs {
1379 blocks: 20,
1380 ignore_price: 2,
1381 max_price: 500_000_000_000,
1382 percentile: 60,
1383 default_suggested_fee: None,
1384 },
1385 rpc_send_raw_transaction_sync_timeout: std::time::Duration::from_secs(30),
1386 testing_skip_invalid_transactions: true,
1387 testing_gas_limit: None,
1388 rpc_force_blob_sidecar_upcasting: false,
1389 };
1390
1391 let parsed_args = CommandParser::<RpcServerArgs>::parse_from([
1392 "reth",
1393 "--http",
1394 "--http.addr",
1395 "127.0.0.1",
1396 "--http.port",
1397 "8545",
1398 "--http.api",
1399 "eth,admin",
1400 "--http.corsdomain",
1401 "*",
1402 "--ws",
1403 "--ws.addr",
1404 "127.0.0.1",
1405 "--ws.port",
1406 "8546",
1407 "--ws.origins",
1408 "*",
1409 "--ws.api",
1410 "eth,admin",
1411 "--ipcpath",
1412 "reth.ipc",
1413 "--ipc.permissions",
1414 "0o666",
1415 "--authrpc.addr",
1416 "127.0.0.1",
1417 "--authrpc.port",
1418 "8551",
1419 "--authrpc.jwtsecret",
1420 "/tmp/jwt.hex",
1421 "--auth-ipc.path",
1422 "engine.ipc",
1423 "--rpc.jwtsecret",
1424 "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
1425 "--rpc.max-request-size",
1426 "15",
1427 "--rpc.max-response-size",
1428 "160",
1429 "--rpc.max-subscriptions-per-connection",
1430 "1024",
1431 "--rpc.max-connections",
1432 "500",
1433 "--rpc.max-tracing-requests",
1434 "16",
1435 "--rpc.max-blocking-io-requests",
1436 "256",
1437 "--rpc.max-trace-filter-blocks",
1438 "4000",
1439 "--rpc.max-blocks-per-filter",
1440 "1000",
1441 "--rpc.max-logs-per-response",
1442 "10000",
1443 "--rpc.gascap",
1444 "50000000",
1445 "--rpc.evm-memory-limit",
1446 "256",
1447 "--rpc.txfeecap",
1448 "2.0",
1449 "--rpc.max-simulate-blocks",
1450 "256",
1451 "--rpc.eth-proof-window",
1452 "100000",
1453 "--rpc.proof-permits",
1454 "16",
1455 "--rpc.pending-block",
1456 "full",
1457 "--rpc.forwarder",
1458 "http://localhost:8545",
1459 "--rpc-cache.max-blocks",
1460 "5000",
1461 "--rpc-cache.max-receipts",
1462 "2000",
1463 "--rpc-cache.max-bals",
1464 "1000",
1465 "--rpc-cache.cache-computed-bals",
1466 "--rpc-cache.prewarm-bals",
1467 "--rpc-cache.max-concurrent-db-requests",
1468 "512",
1469 "--gpo.blocks",
1470 "20",
1471 "--gpo.ignoreprice",
1472 "2",
1473 "--gpo.maxprice",
1474 "500000000000",
1475 "--gpo.percentile",
1476 "60",
1477 "--rpc.send-raw-transaction-sync-timeout",
1478 "30s",
1479 "--testing.skip-invalid-transactions",
1480 ])
1481 .args;
1482
1483 assert_eq!(parsed_args, args);
1484 }
1485
1486 #[test]
1487 fn parse_auth_jwtsecret_hex() {
1488 let hex = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
1489 let args =
1490 CommandParser::<RpcServerArgs>::parse_from(["reth", "--authrpc.jwtsecret-hex", hex])
1491 .args;
1492
1493 let expected = JwtSecret::from_hex(hex).unwrap();
1494 assert_eq!(args.auth_jwtsecret_hex, Some(expected));
1495 assert_eq!(args.auth_jwtsecret, None);
1496 }
1497
1498 #[test]
1499 fn test_auth_jwtsecret_and_hex_are_mutually_exclusive() {
1500 let result = CommandParser::<RpcServerArgs>::try_parse_from([
1501 "reth",
1502 "--authrpc.jwtsecret",
1503 "/tmp/jwt.hex",
1504 "--authrpc.jwtsecret-hex",
1505 "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
1506 ]);
1507
1508 assert!(result.is_err());
1509 }
1510
1511 #[test]
1512 fn test_adjust_instance_ports_suffixes_ipc_paths() {
1513 let paths: Vec<_> = (1..=3u16)
1514 .map(|instance| {
1515 let mut args = RpcServerArgs::default();
1516 let ipcpath = args.ipcpath.clone();
1517 let auth_ipc_path = args.auth_ipc_path().to_string();
1518 args.adjust_instance_ports(Some(instance));
1519 assert_eq!(args.ipcpath, format!("{ipcpath}-{instance}"));
1520 assert_eq!(args.auth_ipc_path(), format!("{auth_ipc_path}-{instance}"));
1521 args.auth_ipc_path().to_string()
1522 })
1523 .collect();
1524
1525 assert_eq!(
1526 paths,
1527 [
1528 "/tmp/reth_engine_api.ipc-1",
1529 "/tmp/reth_engine_api.ipc-2",
1530 "/tmp/reth_engine_api.ipc-3"
1531 ]
1532 );
1533
1534 let mut args = RpcServerArgs::default();
1536 let auth_ipc_path = args.auth_ipc_path().to_string();
1537 args.adjust_instance_ports(None);
1538 assert_eq!(args.auth_ipc_path(), auth_ipc_path);
1539 }
1540
1541 #[test]
1542 fn test_adjust_instance_ports_preserves_explicit_auth_ipc_path() {
1543 for path in ["engine.ipc", constants::DEFAULT_ENGINE_API_IPC_ENDPOINT] {
1544 let mut args =
1545 CommandParser::<RpcServerArgs>::parse_from(["reth", "--auth-ipc.path", path]).args;
1546 args.adjust_instance_ports(Some(2));
1547 assert_eq!(args.auth_ipc_path(), path);
1548 }
1549 }
1550}