1use crate::args::{
4 types::{MaxU32, ZeroAsNoneU64},
5 GasPriceOracleArgs, RpcStateCacheArgs,
6};
7use alloy_primitives::Address;
8use alloy_rpc_types_engine::JwtSecret;
9use clap::{
10 builder::{PossibleValue, RangedU64ValueParser, Resettable, TypedValueParser},
11 Arg, Args, Command,
12};
13use rand::Rng;
14use reth_cli_util::{parse_duration_from_secs_or_ms, parse_ether_value};
15use reth_rpc_eth_types::builder::config::PendingBlockKind;
16use reth_rpc_server_types::{constants, RethRpcModule, RpcModuleSelection};
17use std::{
18 collections::HashSet,
19 ffi::OsStr,
20 net::{IpAddr, Ipv4Addr},
21 path::PathBuf,
22 sync::OnceLock,
23 time::Duration,
24};
25use url::Url;
26
27use super::types::MaxOr;
28
29static RPC_SERVER_DEFAULTS: OnceLock<DefaultRpcServerArgs> = OnceLock::new();
31
32pub(crate) const RPC_DEFAULT_MAX_SUBS_PER_CONN: u32 = 1024;
34
35pub(crate) const RPC_DEFAULT_MAX_REQUEST_SIZE_MB: u32 = 15;
37
38pub(crate) const RPC_DEFAULT_MAX_RESPONSE_SIZE_MB: u32 = 160;
42
43pub(crate) const RPC_DEFAULT_MAX_CONNECTIONS: u32 = 500;
48
49#[derive(Debug, Clone)]
53pub struct DefaultRpcServerArgs {
54 http: bool,
55 http_addr: IpAddr,
56 http_port: u16,
57 http_disable_compression: bool,
58 http_api: Option<RpcModuleSelection>,
59 http_corsdomain: Option<String>,
60 ws: bool,
61 ws_addr: IpAddr,
62 ws_port: u16,
63 ws_allowed_origins: Option<String>,
64 ws_api: Option<RpcModuleSelection>,
65 ipcdisable: bool,
66 ipcpath: String,
67 ipc_socket_permissions: Option<String>,
68 auth_addr: IpAddr,
69 auth_port: u16,
70 auth_jwtsecret: Option<PathBuf>,
71 auth_ipc: bool,
72 auth_ipc_path: String,
73 disable_auth_server: bool,
74 rpc_jwtsecret: Option<JwtSecret>,
75 rpc_max_request_size: MaxU32,
76 rpc_max_response_size: MaxU32,
77 rpc_max_subscriptions_per_connection: MaxU32,
78 rpc_max_connections: MaxU32,
79 rpc_max_tracing_requests: usize,
80 rpc_max_blocking_io_requests: usize,
81 rpc_max_trace_filter_blocks: u64,
82 rpc_max_blocks_per_filter: ZeroAsNoneU64,
83 rpc_max_logs_per_response: ZeroAsNoneU64,
84 rpc_gas_cap: u64,
85 rpc_evm_memory_limit: u64,
86 rpc_tx_fee_cap: u128,
87 rpc_max_simulate_blocks: u64,
88 rpc_eth_proof_window: u64,
89 rpc_proof_permits: usize,
90 rpc_pending_block: PendingBlockKind,
91 rpc_forwarder: Option<Url>,
92 builder_disallow: Option<HashSet<Address>>,
93 rpc_state_cache: RpcStateCacheArgs,
94 gas_price_oracle: GasPriceOracleArgs,
95 rpc_send_raw_transaction_sync_timeout: Duration,
96}
97
98impl DefaultRpcServerArgs {
99 pub fn try_init(self) -> Result<(), Self> {
101 RPC_SERVER_DEFAULTS.set(self)
102 }
103
104 pub fn get_global() -> &'static Self {
106 RPC_SERVER_DEFAULTS.get_or_init(Self::default)
107 }
108
109 pub const fn with_http(mut self, v: bool) -> Self {
111 self.http = v;
112 self
113 }
114
115 pub const fn with_http_addr(mut self, v: IpAddr) -> Self {
117 self.http_addr = v;
118 self
119 }
120
121 pub const fn with_http_port(mut self, v: u16) -> Self {
123 self.http_port = v;
124 self
125 }
126
127 pub const fn with_http_disable_compression(mut self, v: bool) -> Self {
129 self.http_disable_compression = v;
130 self
131 }
132
133 pub fn with_http_api(mut self, v: Option<RpcModuleSelection>) -> Self {
135 self.http_api = v;
136 self
137 }
138
139 pub fn with_http_corsdomain(mut self, v: Option<String>) -> Self {
141 self.http_corsdomain = v;
142 self
143 }
144
145 pub const fn with_ws(mut self, v: bool) -> Self {
147 self.ws = v;
148 self
149 }
150
151 pub const fn with_ws_addr(mut self, v: IpAddr) -> Self {
153 self.ws_addr = v;
154 self
155 }
156
157 pub const fn with_ws_port(mut self, v: u16) -> Self {
159 self.ws_port = v;
160 self
161 }
162
163 pub fn with_ws_allowed_origins(mut self, v: Option<String>) -> Self {
165 self.ws_allowed_origins = v;
166 self
167 }
168
169 pub fn with_ws_api(mut self, v: Option<RpcModuleSelection>) -> Self {
171 self.ws_api = v;
172 self
173 }
174
175 pub const fn with_ipcdisable(mut self, v: bool) -> Self {
177 self.ipcdisable = v;
178 self
179 }
180
181 pub fn with_ipcpath(mut self, v: String) -> Self {
183 self.ipcpath = v;
184 self
185 }
186
187 pub fn with_ipc_socket_permissions(mut self, v: Option<String>) -> Self {
189 self.ipc_socket_permissions = v;
190 self
191 }
192
193 pub const fn with_auth_addr(mut self, v: IpAddr) -> Self {
195 self.auth_addr = v;
196 self
197 }
198
199 pub const fn with_auth_port(mut self, v: u16) -> Self {
201 self.auth_port = v;
202 self
203 }
204
205 pub fn with_auth_jwtsecret(mut self, v: Option<PathBuf>) -> Self {
207 self.auth_jwtsecret = v;
208 self
209 }
210
211 pub const fn with_auth_ipc(mut self, v: bool) -> Self {
213 self.auth_ipc = v;
214 self
215 }
216
217 pub fn with_auth_ipc_path(mut self, v: String) -> Self {
219 self.auth_ipc_path = v;
220 self
221 }
222
223 pub const fn with_disable_auth_server(mut self, v: bool) -> Self {
225 self.disable_auth_server = v;
226 self
227 }
228
229 pub const fn with_rpc_jwtsecret(mut self, v: Option<JwtSecret>) -> Self {
231 self.rpc_jwtsecret = v;
232 self
233 }
234
235 pub const fn with_rpc_max_request_size(mut self, v: MaxU32) -> Self {
237 self.rpc_max_request_size = v;
238 self
239 }
240
241 pub const fn with_rpc_max_response_size(mut self, v: MaxU32) -> Self {
243 self.rpc_max_response_size = v;
244 self
245 }
246
247 pub const fn with_rpc_max_subscriptions_per_connection(mut self, v: MaxU32) -> Self {
249 self.rpc_max_subscriptions_per_connection = v;
250 self
251 }
252
253 pub const fn with_rpc_max_connections(mut self, v: MaxU32) -> Self {
255 self.rpc_max_connections = v;
256 self
257 }
258
259 pub const fn with_rpc_max_tracing_requests(mut self, v: usize) -> Self {
261 self.rpc_max_tracing_requests = v;
262 self
263 }
264
265 pub const fn with_rpc_max_blocking_io_requests(mut self, v: usize) -> Self {
267 self.rpc_max_blocking_io_requests = v;
268 self
269 }
270
271 pub const fn with_rpc_max_trace_filter_blocks(mut self, v: u64) -> Self {
273 self.rpc_max_trace_filter_blocks = v;
274 self
275 }
276
277 pub const fn with_rpc_max_blocks_per_filter(mut self, v: ZeroAsNoneU64) -> Self {
279 self.rpc_max_blocks_per_filter = v;
280 self
281 }
282
283 pub const fn with_rpc_max_logs_per_response(mut self, v: ZeroAsNoneU64) -> Self {
285 self.rpc_max_logs_per_response = v;
286 self
287 }
288
289 pub const fn with_rpc_gas_cap(mut self, v: u64) -> Self {
291 self.rpc_gas_cap = v;
292 self
293 }
294
295 pub const fn with_rpc_evm_memory_limit(mut self, v: u64) -> Self {
297 self.rpc_evm_memory_limit = v;
298 self
299 }
300
301 pub const fn with_rpc_tx_fee_cap(mut self, v: u128) -> Self {
303 self.rpc_tx_fee_cap = v;
304 self
305 }
306
307 pub const fn with_rpc_max_simulate_blocks(mut self, v: u64) -> Self {
309 self.rpc_max_simulate_blocks = v;
310 self
311 }
312
313 pub const fn with_rpc_eth_proof_window(mut self, v: u64) -> Self {
315 self.rpc_eth_proof_window = v;
316 self
317 }
318
319 pub const fn with_rpc_proof_permits(mut self, v: usize) -> Self {
321 self.rpc_proof_permits = v;
322 self
323 }
324
325 pub const fn with_rpc_pending_block(mut self, v: PendingBlockKind) -> Self {
327 self.rpc_pending_block = v;
328 self
329 }
330
331 pub fn with_rpc_forwarder(mut self, v: Option<Url>) -> Self {
333 self.rpc_forwarder = v;
334 self
335 }
336
337 pub fn with_builder_disallow(mut self, v: Option<HashSet<Address>>) -> Self {
339 self.builder_disallow = v;
340 self
341 }
342
343 pub const fn with_rpc_state_cache(mut self, v: RpcStateCacheArgs) -> Self {
345 self.rpc_state_cache = v;
346 self
347 }
348
349 pub const fn with_gas_price_oracle(mut self, v: GasPriceOracleArgs) -> Self {
351 self.gas_price_oracle = v;
352 self
353 }
354
355 pub const fn with_rpc_send_raw_transaction_sync_timeout(mut self, v: Duration) -> Self {
357 self.rpc_send_raw_transaction_sync_timeout = v;
358 self
359 }
360}
361
362impl Default for DefaultRpcServerArgs {
363 fn default() -> Self {
364 Self {
365 http: false,
366 http_addr: Ipv4Addr::LOCALHOST.into(),
367 http_port: constants::DEFAULT_HTTP_RPC_PORT,
368 http_disable_compression: false,
369 http_api: None,
370 http_corsdomain: None,
371 ws: false,
372 ws_addr: Ipv4Addr::LOCALHOST.into(),
373 ws_port: constants::DEFAULT_WS_RPC_PORT,
374 ws_allowed_origins: None,
375 ws_api: None,
376 ipcdisable: false,
377 ipcpath: constants::DEFAULT_IPC_ENDPOINT.to_string(),
378 ipc_socket_permissions: None,
379 auth_addr: Ipv4Addr::LOCALHOST.into(),
380 auth_port: constants::DEFAULT_AUTH_PORT,
381 auth_jwtsecret: None,
382 auth_ipc: false,
383 auth_ipc_path: constants::DEFAULT_ENGINE_API_IPC_ENDPOINT.to_string(),
384 disable_auth_server: false,
385 rpc_jwtsecret: None,
386 rpc_max_request_size: RPC_DEFAULT_MAX_REQUEST_SIZE_MB.into(),
387 rpc_max_response_size: RPC_DEFAULT_MAX_RESPONSE_SIZE_MB.into(),
388 rpc_max_subscriptions_per_connection: RPC_DEFAULT_MAX_SUBS_PER_CONN.into(),
389 rpc_max_connections: RPC_DEFAULT_MAX_CONNECTIONS.into(),
390 rpc_max_tracing_requests: constants::default_max_tracing_requests(),
391 rpc_max_blocking_io_requests: constants::DEFAULT_MAX_BLOCKING_IO_REQUEST,
392 rpc_max_trace_filter_blocks: constants::DEFAULT_MAX_TRACE_FILTER_BLOCKS,
393 rpc_max_blocks_per_filter: constants::DEFAULT_MAX_BLOCKS_PER_FILTER.into(),
394 rpc_max_logs_per_response: (constants::DEFAULT_MAX_LOGS_PER_RESPONSE as u64).into(),
395 rpc_gas_cap: constants::gas_oracle::RPC_DEFAULT_GAS_CAP,
396 rpc_evm_memory_limit: (1 << 32) - 1,
397 rpc_tx_fee_cap: constants::DEFAULT_TX_FEE_CAP_WEI,
398 rpc_max_simulate_blocks: constants::DEFAULT_MAX_SIMULATE_BLOCKS,
399 rpc_eth_proof_window: constants::DEFAULT_ETH_PROOF_WINDOW,
400 rpc_proof_permits: constants::DEFAULT_PROOF_PERMITS,
401 rpc_pending_block: PendingBlockKind::Full,
402 rpc_forwarder: None,
403 builder_disallow: None,
404 rpc_state_cache: RpcStateCacheArgs::default(),
405 gas_price_oracle: GasPriceOracleArgs::default(),
406 rpc_send_raw_transaction_sync_timeout:
407 constants::RPC_DEFAULT_SEND_RAW_TX_SYNC_TIMEOUT_SECS,
408 }
409 }
410}
411
412#[derive(Debug, Clone, Args, PartialEq, Eq)]
414#[command(next_help_heading = "RPC")]
415pub struct RpcServerArgs {
416 #[arg(long, default_value_if("dev", "true", "true"), default_value_t = DefaultRpcServerArgs::get_global().http)]
418 pub http: bool,
419
420 #[arg(long = "http.addr", default_value_t = DefaultRpcServerArgs::get_global().http_addr)]
422 pub http_addr: IpAddr,
423
424 #[arg(long = "http.port", default_value_t = DefaultRpcServerArgs::get_global().http_port)]
426 pub http_port: u16,
427
428 #[arg(long = "http.disable-compression", default_value_t = DefaultRpcServerArgs::get_global().http_disable_compression)]
430 pub http_disable_compression: bool,
431
432 #[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())))]
434 pub http_api: Option<RpcModuleSelection>,
435
436 #[arg(long = "http.corsdomain", default_value = Resettable::from(DefaultRpcServerArgs::get_global().http_corsdomain.as_ref().map(|v| v.to_string().into())))]
438 pub http_corsdomain: Option<String>,
439
440 #[arg(long, default_value_t = DefaultRpcServerArgs::get_global().ws)]
442 pub ws: bool,
443
444 #[arg(long = "ws.addr", default_value_t = DefaultRpcServerArgs::get_global().ws_addr)]
446 pub ws_addr: IpAddr,
447
448 #[arg(long = "ws.port", default_value_t = DefaultRpcServerArgs::get_global().ws_port)]
450 pub ws_port: u16,
451
452 #[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())))]
454 pub ws_allowed_origins: Option<String>,
455
456 #[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())))]
458 pub ws_api: Option<RpcModuleSelection>,
459
460 #[arg(long, default_value_t = DefaultRpcServerArgs::get_global().ipcdisable)]
462 pub ipcdisable: bool,
463
464 #[arg(long, default_value_t = DefaultRpcServerArgs::get_global().ipcpath.clone())]
466 pub ipcpath: String,
467
468 #[arg(long = "ipc.permissions", default_value = Resettable::from(DefaultRpcServerArgs::get_global().ipc_socket_permissions.as_ref().map(|v| v.to_string().into())))]
472 pub ipc_socket_permissions: Option<String>,
473
474 #[arg(long = "authrpc.addr", default_value_t = DefaultRpcServerArgs::get_global().auth_addr)]
476 pub auth_addr: IpAddr,
477
478 #[arg(long = "authrpc.port", default_value_t = DefaultRpcServerArgs::get_global().auth_port)]
480 pub auth_port: u16,
481
482 #[arg(long = "authrpc.jwtsecret", value_name = "PATH", global = true, required = false, default_value = Resettable::from(DefaultRpcServerArgs::get_global().auth_jwtsecret.as_ref().map(|v| v.to_string_lossy().into())))]
489 pub auth_jwtsecret: Option<PathBuf>,
490
491 #[arg(long, default_value_t = DefaultRpcServerArgs::get_global().auth_ipc)]
493 pub auth_ipc: bool,
494
495 #[arg(long = "auth-ipc.path", default_value_t = DefaultRpcServerArgs::get_global().auth_ipc_path.clone())]
497 pub auth_ipc_path: String,
498
499 #[arg(long = "disable-auth-server", alias = "disable-engine-api", default_value_t = DefaultRpcServerArgs::get_global().disable_auth_server)]
504 pub disable_auth_server: bool,
505
506 #[arg(long = "rpc.jwtsecret", value_name = "HEX", global = true, required = false, default_value = Resettable::from(DefaultRpcServerArgs::get_global().rpc_jwtsecret.as_ref().map(|v| format!("{:?}", v).into())))]
512 pub rpc_jwtsecret: Option<JwtSecret>,
513
514 #[arg(long = "rpc.max-request-size", alias = "rpc-max-request-size", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_request_size)]
516 pub rpc_max_request_size: MaxU32,
517
518 #[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)]
520 pub rpc_max_response_size: MaxU32,
521
522 #[arg(long = "rpc.max-subscriptions-per-connection", alias = "rpc-max-subscriptions-per-connection", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_subscriptions_per_connection)]
524 pub rpc_max_subscriptions_per_connection: MaxU32,
525
526 #[arg(long = "rpc.max-connections", alias = "rpc-max-connections", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_connections)]
528 pub rpc_max_connections: MaxU32,
529
530 #[arg(long = "rpc.max-tracing-requests", alias = "rpc-max-tracing-requests", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_tracing_requests)]
537 pub rpc_max_tracing_requests: usize,
538
539 #[arg(long = "rpc.max-blocking-io-requests", alias = "rpc-max-blocking-io-requests", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_blocking_io_requests)]
545 pub rpc_max_blocking_io_requests: usize,
546
547 #[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)]
549 pub rpc_max_trace_filter_blocks: u64,
550
551 #[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)]
553 pub rpc_max_blocks_per_filter: ZeroAsNoneU64,
554
555 #[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)]
557 pub rpc_max_logs_per_response: ZeroAsNoneU64,
558
559 #[arg(
561 long = "rpc.gascap",
562 alias = "rpc-gascap",
563 value_name = "GAS_CAP",
564 value_parser = MaxOr::new(RangedU64ValueParser::<u64>::new().range(1..)),
565 default_value_t = DefaultRpcServerArgs::get_global().rpc_gas_cap
566 )]
567 pub rpc_gas_cap: u64,
568
569 #[arg(
571 long = "rpc.evm-memory-limit",
572 alias = "rpc-evm-memory-limit",
573 value_name = "MEMORY_LIMIT",
574 value_parser = MaxOr::new(RangedU64ValueParser::<u64>::new().range(1..)),
575 default_value_t = DefaultRpcServerArgs::get_global().rpc_evm_memory_limit
576 )]
577 pub rpc_evm_memory_limit: u64,
578
579 #[arg(
581 long = "rpc.txfeecap",
582 alias = "rpc-txfeecap",
583 value_name = "TX_FEE_CAP",
584 value_parser = parse_ether_value,
585 default_value = "1.0"
586 )]
587 pub rpc_tx_fee_cap: u128,
588
589 #[arg(
591 long = "rpc.max-simulate-blocks",
592 value_name = "BLOCKS_COUNT",
593 default_value_t = DefaultRpcServerArgs::get_global().rpc_max_simulate_blocks
594 )]
595 pub rpc_max_simulate_blocks: u64,
596
597 #[arg(
601 long = "rpc.eth-proof-window",
602 default_value_t = DefaultRpcServerArgs::get_global().rpc_eth_proof_window,
603 value_parser = RangedU64ValueParser::<u64>::new().range(..=constants::MAX_ETH_PROOF_WINDOW)
604 )]
605 pub rpc_eth_proof_window: u64,
606
607 #[arg(long = "rpc.proof-permits", alias = "rpc-proof-permits", value_name = "COUNT", default_value_t = constants::DEFAULT_PROOF_PERMITS)]
609 pub rpc_proof_permits: usize,
610
611 #[arg(long = "rpc.pending-block", default_value = "full", value_name = "KIND")]
616 pub rpc_pending_block: PendingBlockKind,
617
618 #[arg(long = "rpc.forwarder", alias = "rpc-forwarder", value_name = "FORWARDER")]
620 pub rpc_forwarder: Option<Url>,
621
622 #[arg(long = "builder.disallow", value_name = "PATH", value_parser = reth_cli_util::parsers::read_json_from_file::<HashSet<Address>>, default_value = Resettable::from(DefaultRpcServerArgs::get_global().builder_disallow.as_ref().map(|v| format!("{:?}", v).into())))]
625 pub builder_disallow: Option<HashSet<Address>>,
626
627 #[command(flatten)]
629 pub rpc_state_cache: RpcStateCacheArgs,
630
631 #[command(flatten)]
633 pub gas_price_oracle: GasPriceOracleArgs,
634
635 #[arg(
637 long = "rpc.send-raw-transaction-sync-timeout",
638 value_name = "SECONDS",
639 default_value = "30s",
640 value_parser = parse_duration_from_secs_or_ms,
641 )]
642 pub rpc_send_raw_transaction_sync_timeout: Duration,
643}
644
645impl RpcServerArgs {
646 pub const fn with_http(mut self) -> Self {
648 self.http = true;
649 self
650 }
651
652 pub fn with_http_api(mut self, http_api: RpcModuleSelection) -> Self {
654 self.http_api = Some(http_api);
655 self
656 }
657
658 pub const fn with_ws(mut self) -> Self {
660 self.ws = true;
661 self
662 }
663
664 pub fn with_ws_api(mut self, ws_api: RpcModuleSelection) -> Self {
666 self.ws_api = Some(ws_api);
667 self
668 }
669
670 pub const fn with_auth_ipc(mut self) -> Self {
672 self.auth_ipc = true;
673 self
674 }
675
676 pub fn with_api(self, api: RpcModuleSelection) -> Self {
680 self.with_http_api(api.clone()).with_ws_api(api)
681 }
682
683 pub fn adjust_instance_ports(&mut self, instance: Option<u16>) {
698 if let Some(instance) = instance {
699 debug_assert_ne!(instance, 0, "instance must be non-zero");
700 self.auth_port += instance * 100 - 100;
702 self.http_port -= instance - 1;
704 self.ws_port += instance * 2 - 2;
706 self.ipcpath = format!("{}-{}", self.ipcpath, instance);
708 }
709 }
710
711 pub const fn with_http_unused_port(mut self) -> Self {
714 self.http_port = 0;
715 self
716 }
717
718 pub const fn with_ws_unused_port(mut self) -> Self {
721 self.ws_port = 0;
722 self
723 }
724
725 pub const fn with_auth_unused_port(mut self) -> Self {
728 self.auth_port = 0;
729 self
730 }
731
732 pub fn with_ipc_random_path(mut self) -> Self {
735 let random_string: String =
736 rand::rng().sample_iter(rand::distr::Alphanumeric).take(8).map(char::from).collect();
737 self.ipcpath = format!("{}-{}", self.ipcpath, random_string);
738 self
739 }
740
741 pub fn with_unused_ports(mut self) -> Self {
744 self = self.with_http_unused_port();
745 self = self.with_ws_unused_port();
746 self = self.with_auth_unused_port();
747 self = self.with_ipc_random_path();
748 self
749 }
750
751 pub fn apply<F>(self, f: F) -> Self
753 where
754 F: FnOnce(Self) -> Self,
755 {
756 f(self)
757 }
758
759 pub const fn with_send_raw_transaction_sync_timeout(mut self, timeout: Duration) -> Self {
761 self.rpc_send_raw_transaction_sync_timeout = timeout;
762 self
763 }
764}
765
766impl Default for RpcServerArgs {
767 fn default() -> Self {
768 let DefaultRpcServerArgs {
769 http,
770 http_addr,
771 http_port,
772 http_disable_compression,
773 http_api,
774 http_corsdomain,
775 ws,
776 ws_addr,
777 ws_port,
778 ws_allowed_origins,
779 ws_api,
780 ipcdisable,
781 ipcpath,
782 ipc_socket_permissions,
783 auth_addr,
784 auth_port,
785 auth_jwtsecret,
786 auth_ipc,
787 auth_ipc_path,
788 disable_auth_server,
789 rpc_jwtsecret,
790 rpc_max_request_size,
791 rpc_max_response_size,
792 rpc_max_subscriptions_per_connection,
793 rpc_max_connections,
794 rpc_max_tracing_requests,
795 rpc_max_blocking_io_requests,
796 rpc_max_trace_filter_blocks,
797 rpc_max_blocks_per_filter,
798 rpc_max_logs_per_response,
799 rpc_gas_cap,
800 rpc_evm_memory_limit,
801 rpc_tx_fee_cap,
802 rpc_max_simulate_blocks,
803 rpc_eth_proof_window,
804 rpc_proof_permits,
805 rpc_pending_block,
806 rpc_forwarder,
807 builder_disallow,
808 rpc_state_cache,
809 gas_price_oracle,
810 rpc_send_raw_transaction_sync_timeout,
811 } = DefaultRpcServerArgs::get_global().clone();
812 Self {
813 http,
814 http_addr,
815 http_port,
816 http_disable_compression,
817 http_api,
818 http_corsdomain,
819 ws,
820 ws_addr,
821 ws_port,
822 ws_allowed_origins,
823 ws_api,
824 ipcdisable,
825 ipcpath,
826 ipc_socket_permissions,
827 auth_addr,
828 auth_port,
829 auth_jwtsecret,
830 auth_ipc,
831 auth_ipc_path,
832 disable_auth_server,
833 rpc_jwtsecret,
834 rpc_max_request_size,
835 rpc_max_response_size,
836 rpc_max_subscriptions_per_connection,
837 rpc_max_connections,
838 rpc_max_tracing_requests,
839 rpc_max_blocking_io_requests,
840 rpc_max_trace_filter_blocks,
841 rpc_max_blocks_per_filter,
842 rpc_max_logs_per_response,
843 rpc_gas_cap,
844 rpc_evm_memory_limit,
845 rpc_tx_fee_cap,
846 rpc_max_simulate_blocks,
847 rpc_eth_proof_window,
848 rpc_proof_permits,
849 rpc_pending_block,
850 rpc_forwarder,
851 builder_disallow,
852 rpc_state_cache,
853 gas_price_oracle,
854 rpc_send_raw_transaction_sync_timeout,
855 }
856 }
857}
858
859#[derive(Clone, Debug, Default)]
861#[non_exhaustive]
862struct RpcModuleSelectionValueParser;
863
864impl TypedValueParser for RpcModuleSelectionValueParser {
865 type Value = RpcModuleSelection;
866
867 fn parse_ref(
868 &self,
869 _cmd: &Command,
870 _arg: Option<&Arg>,
871 value: &OsStr,
872 ) -> Result<Self::Value, clap::Error> {
873 let val =
874 value.to_str().ok_or_else(|| clap::Error::new(clap::error::ErrorKind::InvalidUtf8))?;
875 Ok(val
877 .parse::<RpcModuleSelection>()
878 .expect("RpcModuleSelection parsing cannot fail with Other variant"))
879 }
880
881 fn possible_values(&self) -> Option<Box<dyn Iterator<Item = PossibleValue> + '_>> {
882 let values = RethRpcModule::standard_variant_names().map(PossibleValue::new);
884 Some(Box::new(values))
885 }
886}
887
888#[cfg(test)]
889mod tests {
890 use super::*;
891 use clap::{Args, Parser};
892
893 #[derive(Parser)]
895 struct CommandParser<T: Args> {
896 #[command(flatten)]
897 args: T,
898 }
899
900 #[test]
901 fn test_rpc_server_args_parser() {
902 let args =
903 CommandParser::<RpcServerArgs>::parse_from(["reth", "--http.api", "eth,admin,debug"])
904 .args;
905
906 let apis = args.http_api.unwrap();
907 let expected = RpcModuleSelection::try_from_selection(["eth", "admin", "debug"]).unwrap();
908
909 assert_eq!(apis, expected);
910 }
911
912 #[test]
913 fn test_rpc_server_eth_call_bundle_args() {
914 let args =
915 CommandParser::<RpcServerArgs>::parse_from(["reth", "--http.api", "eth,admin,debug"])
916 .args;
917
918 let apis = args.http_api.unwrap();
919 let expected = RpcModuleSelection::try_from_selection(["eth", "admin", "debug"]).unwrap();
920
921 assert_eq!(apis, expected);
922 }
923
924 #[test]
925 fn test_rpc_server_args_parser_none() {
926 let args = CommandParser::<RpcServerArgs>::parse_from(["reth", "--http.api", "none"]).args;
927 let apis = args.http_api.unwrap();
928 let expected = RpcModuleSelection::Selection(Default::default());
929 assert_eq!(apis, expected);
930 }
931
932 #[test]
933 fn rpc_server_args_default_sanity_test() {
934 let default_args = RpcServerArgs::default();
935 let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
936
937 assert_eq!(args, default_args);
938 }
939
940 #[test]
941 fn test_rpc_tx_fee_cap_parse_integer() {
942 let args = CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.txfeecap", "2"]).args;
943 let expected = 2_000_000_000_000_000_000u128; assert_eq!(args.rpc_tx_fee_cap, expected);
945 }
946
947 #[test]
948 fn test_rpc_tx_fee_cap_parse_decimal() {
949 let args =
950 CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.txfeecap", "1.5"]).args;
951 let expected = 1_500_000_000_000_000_000u128; assert_eq!(args.rpc_tx_fee_cap, expected);
953 }
954
955 #[test]
956 fn test_rpc_tx_fee_cap_parse_zero() {
957 let args = CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.txfeecap", "0"]).args;
958 assert_eq!(args.rpc_tx_fee_cap, 0); }
960
961 #[test]
962 fn test_rpc_tx_fee_cap_parse_none() {
963 let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
964 let expected = 1_000_000_000_000_000_000u128;
965 assert_eq!(args.rpc_tx_fee_cap, expected); }
967
968 #[test]
969 fn test_rpc_server_args() {
970 let args = RpcServerArgs {
971 http: true,
972 http_addr: "127.0.0.1".parse().unwrap(),
973 http_port: 8545,
974 http_disable_compression: false,
975 http_api: Some(RpcModuleSelection::try_from_selection(["eth", "admin"]).unwrap()),
976 http_corsdomain: Some("*".to_string()),
977 ws: true,
978 ws_addr: "127.0.0.1".parse().unwrap(),
979 ws_port: 8546,
980 ws_allowed_origins: Some("*".to_string()),
981 ws_api: Some(RpcModuleSelection::try_from_selection(["eth", "admin"]).unwrap()),
982 ipcdisable: false,
983 ipcpath: "reth.ipc".to_string(),
984 ipc_socket_permissions: Some("0o666".to_string()),
985 auth_addr: "127.0.0.1".parse().unwrap(),
986 auth_port: 8551,
987 auth_jwtsecret: Some(std::path::PathBuf::from("/tmp/jwt.hex")),
988 auth_ipc: false,
989 auth_ipc_path: "engine.ipc".to_string(),
990 disable_auth_server: false,
991 rpc_jwtsecret: Some(
992 JwtSecret::from_hex(
993 "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
994 )
995 .unwrap(),
996 ),
997 rpc_max_request_size: 15u32.into(),
998 rpc_max_response_size: 160u32.into(),
999 rpc_max_subscriptions_per_connection: 1024u32.into(),
1000 rpc_max_connections: 500u32.into(),
1001 rpc_max_tracing_requests: 16,
1002 rpc_max_blocking_io_requests: 256,
1003 rpc_max_trace_filter_blocks: 4000,
1004 rpc_max_blocks_per_filter: 1000u64.into(),
1005 rpc_max_logs_per_response: 10000u64.into(),
1006 rpc_gas_cap: 50_000_000,
1007 rpc_evm_memory_limit: 256,
1008 rpc_tx_fee_cap: 2_000_000_000_000_000_000u128,
1009 rpc_max_simulate_blocks: 256,
1010 rpc_eth_proof_window: 100_000,
1011 rpc_proof_permits: 16,
1012 rpc_pending_block: PendingBlockKind::Full,
1013 rpc_forwarder: Some("http://localhost:8545".parse().unwrap()),
1014 builder_disallow: None,
1015 rpc_state_cache: RpcStateCacheArgs {
1016 max_blocks: 5000,
1017 max_receipts: 2000,
1018 max_headers: 1000,
1019 max_concurrent_db_requests: 512,
1020 },
1021 gas_price_oracle: GasPriceOracleArgs {
1022 blocks: 20,
1023 ignore_price: 2,
1024 max_price: 500_000_000_000,
1025 percentile: 60,
1026 default_suggested_fee: None,
1027 },
1028 rpc_send_raw_transaction_sync_timeout: std::time::Duration::from_secs(30),
1029 };
1030
1031 let parsed_args = CommandParser::<RpcServerArgs>::parse_from([
1032 "reth",
1033 "--http",
1034 "--http.addr",
1035 "127.0.0.1",
1036 "--http.port",
1037 "8545",
1038 "--http.api",
1039 "eth,admin",
1040 "--http.corsdomain",
1041 "*",
1042 "--ws",
1043 "--ws.addr",
1044 "127.0.0.1",
1045 "--ws.port",
1046 "8546",
1047 "--ws.origins",
1048 "*",
1049 "--ws.api",
1050 "eth,admin",
1051 "--ipcpath",
1052 "reth.ipc",
1053 "--ipc.permissions",
1054 "0o666",
1055 "--authrpc.addr",
1056 "127.0.0.1",
1057 "--authrpc.port",
1058 "8551",
1059 "--authrpc.jwtsecret",
1060 "/tmp/jwt.hex",
1061 "--auth-ipc.path",
1062 "engine.ipc",
1063 "--rpc.jwtsecret",
1064 "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
1065 "--rpc.max-request-size",
1066 "15",
1067 "--rpc.max-response-size",
1068 "160",
1069 "--rpc.max-subscriptions-per-connection",
1070 "1024",
1071 "--rpc.max-connections",
1072 "500",
1073 "--rpc.max-tracing-requests",
1074 "16",
1075 "--rpc.max-blocking-io-requests",
1076 "256",
1077 "--rpc.max-trace-filter-blocks",
1078 "4000",
1079 "--rpc.max-blocks-per-filter",
1080 "1000",
1081 "--rpc.max-logs-per-response",
1082 "10000",
1083 "--rpc.gascap",
1084 "50000000",
1085 "--rpc.evm-memory-limit",
1086 "256",
1087 "--rpc.txfeecap",
1088 "2.0",
1089 "--rpc.max-simulate-blocks",
1090 "256",
1091 "--rpc.eth-proof-window",
1092 "100000",
1093 "--rpc.proof-permits",
1094 "16",
1095 "--rpc.pending-block",
1096 "full",
1097 "--rpc.forwarder",
1098 "http://localhost:8545",
1099 "--rpc-cache.max-blocks",
1100 "5000",
1101 "--rpc-cache.max-receipts",
1102 "2000",
1103 "--rpc-cache.max-headers",
1104 "1000",
1105 "--rpc-cache.max-concurrent-db-requests",
1106 "512",
1107 "--gpo.blocks",
1108 "20",
1109 "--gpo.ignoreprice",
1110 "2",
1111 "--gpo.maxprice",
1112 "500000000000",
1113 "--gpo.percentile",
1114 "60",
1115 "--rpc.send-raw-transaction-sync-timeout",
1116 "30s",
1117 ])
1118 .args;
1119
1120 assert_eq!(parsed_args, args);
1121 }
1122}