1use crate::args::{
4 types::{MaxU32, ZeroAsNoneU64},
5 GasPriceOracleArgs, RpcStateCacheArgs,
6};
7use alloy_primitives::map::AddressSet;
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 ffi::OsStr,
19 net::{IpAddr, Ipv4Addr},
20 path::PathBuf,
21 sync::OnceLock,
22 time::Duration,
23};
24use url::Url;
25
26use super::types::MaxOr;
27
28static RPC_SERVER_DEFAULTS: OnceLock<DefaultRpcServerArgs> = OnceLock::new();
30
31pub(crate) const RPC_DEFAULT_MAX_SUBS_PER_CONN: u32 = 1024;
33
34pub(crate) const RPC_DEFAULT_MAX_REQUEST_SIZE_MB: u32 = 15;
36
37pub(crate) const RPC_DEFAULT_MAX_RESPONSE_SIZE_MB: u32 = 160;
41
42pub(crate) const RPC_DEFAULT_MAX_CONNECTIONS: u32 = 500;
47
48#[derive(Debug, Clone)]
52pub struct DefaultRpcServerArgs {
53 http: bool,
54 http_addr: IpAddr,
55 http_port: u16,
56 http_disable_compression: bool,
57 http_compression_algorithms: Option<Vec<String>>,
58 http_decompression_algorithms: Option<Vec<String>>,
59 http_api: Option<RpcModuleSelection>,
60 http_corsdomain: Option<String>,
61 ws: bool,
62 ws_addr: IpAddr,
63 ws_port: u16,
64 ws_allowed_origins: Option<String>,
65 ws_api: Option<RpcModuleSelection>,
66 ipcdisable: bool,
67 ipcpath: String,
68 ipc_socket_permissions: Option<String>,
69 auth_addr: IpAddr,
70 auth_port: u16,
71 auth_jwtsecret: Option<PathBuf>,
72 auth_ipc: bool,
73 auth_ipc_path: String,
74 disable_auth_server: bool,
75 rpc_jwtsecret: Option<JwtSecret>,
76 rpc_disable_metrics: bool,
77 rpc_max_request_size: MaxU32,
78 rpc_max_response_size: MaxU32,
79 rpc_max_subscriptions_per_connection: MaxU32,
80 rpc_max_connections: MaxU32,
81 rpc_max_tracing_requests: usize,
82 rpc_max_blocking_io_requests: usize,
83 rpc_max_trace_filter_blocks: u64,
84 rpc_max_blocks_per_filter: ZeroAsNoneU64,
85 rpc_max_logs_per_response: ZeroAsNoneU64,
86 rpc_gas_cap: u64,
87 rpc_evm_memory_limit: u64,
88 rpc_tx_fee_cap: u128,
89 rpc_max_simulate_blocks: u64,
90 rpc_compute_state_root_for_eth_simulate: bool,
91 rpc_eth_proof_window: u64,
92 rpc_proof_permits: usize,
93 rpc_pending_block: PendingBlockKind,
94 rpc_forwarder: Option<Url>,
95 builder_disallow: Option<AddressSet>,
96 rpc_state_cache: RpcStateCacheArgs,
97 gas_price_oracle: GasPriceOracleArgs,
98 rpc_send_raw_transaction_sync_timeout: Duration,
99}
100
101impl DefaultRpcServerArgs {
102 pub fn try_init(self) -> Result<(), Self> {
104 RPC_SERVER_DEFAULTS.set(self)
105 }
106
107 pub fn get_global() -> &'static Self {
109 RPC_SERVER_DEFAULTS.get_or_init(Self::default)
110 }
111
112 pub const fn with_http(mut self, v: bool) -> Self {
114 self.http = v;
115 self
116 }
117
118 pub const fn with_http_addr(mut self, v: IpAddr) -> Self {
120 self.http_addr = v;
121 self
122 }
123
124 pub const fn with_http_port(mut self, v: u16) -> Self {
126 self.http_port = v;
127 self
128 }
129
130 pub const fn with_http_disable_compression(mut self, v: bool) -> Self {
132 self.http_disable_compression = v;
133 self
134 }
135
136 pub fn with_http_compression_algorithms(mut self, v: Option<Vec<String>>) -> Self {
138 self.http_compression_algorithms = v;
139 self
140 }
141
142 pub fn with_http_decompression_algorithms(mut self, v: Option<Vec<String>>) -> Self {
144 self.http_decompression_algorithms = v;
145 self
146 }
147
148 pub fn with_http_api(mut self, v: Option<RpcModuleSelection>) -> Self {
150 self.http_api = v;
151 self
152 }
153
154 pub fn with_http_corsdomain(mut self, v: Option<String>) -> Self {
156 self.http_corsdomain = v;
157 self
158 }
159
160 pub const fn with_ws(mut self, v: bool) -> Self {
162 self.ws = v;
163 self
164 }
165
166 pub const fn with_ws_addr(mut self, v: IpAddr) -> Self {
168 self.ws_addr = v;
169 self
170 }
171
172 pub const fn with_ws_port(mut self, v: u16) -> Self {
174 self.ws_port = v;
175 self
176 }
177
178 pub fn with_ws_allowed_origins(mut self, v: Option<String>) -> Self {
180 self.ws_allowed_origins = v;
181 self
182 }
183
184 pub fn with_ws_api(mut self, v: Option<RpcModuleSelection>) -> Self {
186 self.ws_api = v;
187 self
188 }
189
190 pub const fn with_ipcdisable(mut self, v: bool) -> Self {
192 self.ipcdisable = v;
193 self
194 }
195
196 pub fn with_ipcpath(mut self, v: String) -> Self {
198 self.ipcpath = v;
199 self
200 }
201
202 pub fn with_ipc_socket_permissions(mut self, v: Option<String>) -> Self {
204 self.ipc_socket_permissions = v;
205 self
206 }
207
208 pub const fn with_auth_addr(mut self, v: IpAddr) -> Self {
210 self.auth_addr = v;
211 self
212 }
213
214 pub const fn with_auth_port(mut self, v: u16) -> Self {
216 self.auth_port = v;
217 self
218 }
219
220 pub fn with_auth_jwtsecret(mut self, v: Option<PathBuf>) -> Self {
222 self.auth_jwtsecret = v;
223 self
224 }
225
226 pub const fn with_auth_ipc(mut self, v: bool) -> Self {
228 self.auth_ipc = v;
229 self
230 }
231
232 pub fn with_auth_ipc_path(mut self, v: String) -> Self {
234 self.auth_ipc_path = v;
235 self
236 }
237
238 pub const fn with_disable_auth_server(mut self, v: bool) -> Self {
240 self.disable_auth_server = v;
241 self
242 }
243
244 pub const fn with_rpc_jwtsecret(mut self, v: Option<JwtSecret>) -> Self {
246 self.rpc_jwtsecret = v;
247 self
248 }
249
250 pub const fn with_rpc_disable_metrics(mut self, v: bool) -> Self {
252 self.rpc_disable_metrics = v;
253 self
254 }
255
256 pub const fn with_rpc_max_request_size(mut self, v: MaxU32) -> Self {
258 self.rpc_max_request_size = v;
259 self
260 }
261
262 pub const fn with_rpc_max_response_size(mut self, v: MaxU32) -> Self {
264 self.rpc_max_response_size = v;
265 self
266 }
267
268 pub const fn with_rpc_max_subscriptions_per_connection(mut self, v: MaxU32) -> Self {
270 self.rpc_max_subscriptions_per_connection = v;
271 self
272 }
273
274 pub const fn with_rpc_max_connections(mut self, v: MaxU32) -> Self {
276 self.rpc_max_connections = v;
277 self
278 }
279
280 pub const fn with_rpc_max_tracing_requests(mut self, v: usize) -> Self {
282 self.rpc_max_tracing_requests = v;
283 self
284 }
285
286 pub const fn with_rpc_max_blocking_io_requests(mut self, v: usize) -> Self {
288 self.rpc_max_blocking_io_requests = v;
289 self
290 }
291
292 pub const fn with_rpc_max_trace_filter_blocks(mut self, v: u64) -> Self {
294 self.rpc_max_trace_filter_blocks = v;
295 self
296 }
297
298 pub const fn with_rpc_max_blocks_per_filter(mut self, v: ZeroAsNoneU64) -> Self {
300 self.rpc_max_blocks_per_filter = v;
301 self
302 }
303
304 pub const fn with_rpc_max_logs_per_response(mut self, v: ZeroAsNoneU64) -> Self {
306 self.rpc_max_logs_per_response = v;
307 self
308 }
309
310 pub const fn with_rpc_gas_cap(mut self, v: u64) -> Self {
312 self.rpc_gas_cap = v;
313 self
314 }
315
316 pub const fn with_rpc_evm_memory_limit(mut self, v: u64) -> Self {
318 self.rpc_evm_memory_limit = v;
319 self
320 }
321
322 pub const fn with_rpc_tx_fee_cap(mut self, v: u128) -> Self {
324 self.rpc_tx_fee_cap = v;
325 self
326 }
327
328 pub const fn with_rpc_max_simulate_blocks(mut self, v: u64) -> Self {
330 self.rpc_max_simulate_blocks = v;
331 self
332 }
333
334 pub const fn with_rpc_compute_state_root_for_eth_simulate(mut self, v: bool) -> Self {
336 self.rpc_compute_state_root_for_eth_simulate = v;
337 self
338 }
339
340 pub const fn with_rpc_eth_proof_window(mut self, v: u64) -> Self {
342 self.rpc_eth_proof_window = v;
343 self
344 }
345
346 pub const fn with_rpc_proof_permits(mut self, v: usize) -> Self {
348 self.rpc_proof_permits = v;
349 self
350 }
351
352 pub const fn with_rpc_pending_block(mut self, v: PendingBlockKind) -> Self {
354 self.rpc_pending_block = v;
355 self
356 }
357
358 pub fn with_rpc_forwarder(mut self, v: Option<Url>) -> Self {
360 self.rpc_forwarder = v;
361 self
362 }
363
364 pub fn with_builder_disallow(mut self, v: Option<AddressSet>) -> Self {
366 self.builder_disallow = v;
367 self
368 }
369
370 pub const fn with_rpc_state_cache(mut self, v: RpcStateCacheArgs) -> Self {
372 self.rpc_state_cache = v;
373 self
374 }
375
376 pub const fn with_gas_price_oracle(mut self, v: GasPriceOracleArgs) -> Self {
378 self.gas_price_oracle = v;
379 self
380 }
381
382 pub const fn with_rpc_send_raw_transaction_sync_timeout(mut self, v: Duration) -> Self {
384 self.rpc_send_raw_transaction_sync_timeout = v;
385 self
386 }
387}
388
389impl Default for DefaultRpcServerArgs {
390 fn default() -> Self {
391 Self {
392 http: false,
393 http_addr: Ipv4Addr::LOCALHOST.into(),
394 http_port: constants::DEFAULT_HTTP_RPC_PORT,
395 http_disable_compression: false,
396 http_compression_algorithms: None,
397 http_decompression_algorithms: None,
398 http_api: None,
399 http_corsdomain: None,
400 ws: false,
401 ws_addr: Ipv4Addr::LOCALHOST.into(),
402 ws_port: constants::DEFAULT_WS_RPC_PORT,
403 ws_allowed_origins: None,
404 ws_api: None,
405 ipcdisable: false,
406 ipcpath: constants::DEFAULT_IPC_ENDPOINT.to_string(),
407 ipc_socket_permissions: None,
408 auth_addr: Ipv4Addr::LOCALHOST.into(),
409 auth_port: constants::DEFAULT_AUTH_PORT,
410 auth_jwtsecret: None,
411 auth_ipc: false,
412 auth_ipc_path: constants::DEFAULT_ENGINE_API_IPC_ENDPOINT.to_string(),
413 disable_auth_server: false,
414 rpc_jwtsecret: None,
415 rpc_disable_metrics: false,
416 rpc_max_request_size: RPC_DEFAULT_MAX_REQUEST_SIZE_MB.into(),
417 rpc_max_response_size: RPC_DEFAULT_MAX_RESPONSE_SIZE_MB.into(),
418 rpc_max_subscriptions_per_connection: RPC_DEFAULT_MAX_SUBS_PER_CONN.into(),
419 rpc_max_connections: RPC_DEFAULT_MAX_CONNECTIONS.into(),
420 rpc_max_tracing_requests: constants::default_max_tracing_requests(),
421 rpc_max_blocking_io_requests: constants::DEFAULT_MAX_BLOCKING_IO_REQUEST,
422 rpc_max_trace_filter_blocks: constants::DEFAULT_MAX_TRACE_FILTER_BLOCKS,
423 rpc_max_blocks_per_filter: constants::DEFAULT_MAX_BLOCKS_PER_FILTER.into(),
424 rpc_max_logs_per_response: (constants::DEFAULT_MAX_LOGS_PER_RESPONSE as u64).into(),
425 rpc_gas_cap: constants::gas_oracle::RPC_DEFAULT_GAS_CAP,
426 rpc_evm_memory_limit: (1 << 32) - 1,
427 rpc_tx_fee_cap: constants::DEFAULT_TX_FEE_CAP_WEI,
428 rpc_max_simulate_blocks: constants::DEFAULT_MAX_SIMULATE_BLOCKS,
429 rpc_compute_state_root_for_eth_simulate: false,
430 rpc_eth_proof_window: constants::DEFAULT_ETH_PROOF_WINDOW,
431 rpc_proof_permits: constants::DEFAULT_PROOF_PERMITS,
432 rpc_pending_block: PendingBlockKind::Full,
433 rpc_forwarder: None,
434 builder_disallow: None,
435 rpc_state_cache: RpcStateCacheArgs::default(),
436 gas_price_oracle: GasPriceOracleArgs::default(),
437 rpc_send_raw_transaction_sync_timeout:
438 constants::RPC_DEFAULT_SEND_RAW_TX_SYNC_TIMEOUT_SECS,
439 }
440 }
441}
442
443#[derive(Debug, Clone, Args, PartialEq, Eq)]
445#[command(next_help_heading = "RPC")]
446pub struct RpcServerArgs {
447 #[arg(long, default_value_if("dev", "true", "true"), default_value_t = DefaultRpcServerArgs::get_global().http)]
449 pub http: bool,
450
451 #[arg(long = "http.addr", default_value_t = DefaultRpcServerArgs::get_global().http_addr)]
453 pub http_addr: IpAddr,
454
455 #[arg(long = "http.port", default_value_t = DefaultRpcServerArgs::get_global().http_port)]
457 pub http_port: u16,
458
459 #[arg(long = "http.disable-compression", default_value_t = DefaultRpcServerArgs::get_global().http_disable_compression)]
461 pub http_disable_compression: bool,
462
463 #[arg(
471 long = "http.compression",
472 value_name = "ALGOS",
473 value_delimiter = ',',
474 value_parser = ["zstd", "gzip", "deflate", "br"],
475 default_value = Resettable::from(DefaultRpcServerArgs::get_global().http_compression_algorithms.as_ref().map(|v| v.join(",").into()))
476 )]
477 pub http_compression_algorithms: Option<Vec<String>>,
478
479 #[arg(
483 long = "http.decompression",
484 value_name = "ALGOS",
485 value_delimiter = ',',
486 value_parser = ["zstd", "gzip", "deflate", "br"],
487 default_value = Resettable::from(DefaultRpcServerArgs::get_global().http_decompression_algorithms.as_ref().map(|v| v.join(",").into()))
488 )]
489 pub http_decompression_algorithms: Option<Vec<String>>,
490
491 #[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())))]
493 pub http_api: Option<RpcModuleSelection>,
494
495 #[arg(long = "http.corsdomain", default_value = Resettable::from(DefaultRpcServerArgs::get_global().http_corsdomain.as_ref().map(|v| v.to_string().into())))]
497 pub http_corsdomain: Option<String>,
498
499 #[arg(long, default_value_t = DefaultRpcServerArgs::get_global().ws)]
501 pub ws: bool,
502
503 #[arg(long = "ws.addr", default_value_t = DefaultRpcServerArgs::get_global().ws_addr)]
505 pub ws_addr: IpAddr,
506
507 #[arg(long = "ws.port", default_value_t = DefaultRpcServerArgs::get_global().ws_port)]
509 pub ws_port: u16,
510
511 #[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())))]
513 pub ws_allowed_origins: Option<String>,
514
515 #[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())))]
517 pub ws_api: Option<RpcModuleSelection>,
518
519 #[arg(long, default_value_t = DefaultRpcServerArgs::get_global().ipcdisable)]
521 pub ipcdisable: bool,
522
523 #[arg(long, default_value_t = DefaultRpcServerArgs::get_global().ipcpath.clone())]
525 pub ipcpath: String,
526
527 #[arg(long = "ipc.permissions", default_value = Resettable::from(DefaultRpcServerArgs::get_global().ipc_socket_permissions.as_ref().map(|v| v.to_string().into())))]
531 pub ipc_socket_permissions: Option<String>,
532
533 #[arg(long = "authrpc.addr", default_value_t = DefaultRpcServerArgs::get_global().auth_addr)]
535 pub auth_addr: IpAddr,
536
537 #[arg(long = "authrpc.port", default_value_t = DefaultRpcServerArgs::get_global().auth_port)]
539 pub auth_port: u16,
540
541 #[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())))]
549 pub auth_jwtsecret: Option<PathBuf>,
550
551 #[arg(
556 long = "authrpc.jwtsecret-hex",
557 value_name = "HEX",
558 global = true,
559 required = false,
560 conflicts_with = "auth_jwtsecret"
561 )]
562 pub auth_jwtsecret_hex: Option<JwtSecret>,
563
564 #[arg(long, default_value_t = DefaultRpcServerArgs::get_global().auth_ipc)]
566 pub auth_ipc: bool,
567
568 #[arg(long = "auth-ipc.path", default_value_t = DefaultRpcServerArgs::get_global().auth_ipc_path.clone())]
570 pub auth_ipc_path: String,
571
572 #[arg(long = "disable-auth-server", alias = "disable-engine-api", default_value_t = DefaultRpcServerArgs::get_global().disable_auth_server)]
577 pub disable_auth_server: bool,
578
579 #[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())))]
585 pub rpc_jwtsecret: Option<JwtSecret>,
586
587 #[arg(long = "rpc.disable-metrics", default_value_t = DefaultRpcServerArgs::get_global().rpc_disable_metrics)]
589 pub rpc_disable_metrics: bool,
590
591 #[arg(long = "rpc.max-request-size", alias = "rpc-max-request-size", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_request_size)]
596 pub rpc_max_request_size: MaxU32,
597
598 #[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)]
600 pub rpc_max_response_size: MaxU32,
601
602 #[arg(long = "rpc.max-subscriptions-per-connection", alias = "rpc-max-subscriptions-per-connection", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_subscriptions_per_connection)]
604 pub rpc_max_subscriptions_per_connection: MaxU32,
605
606 #[arg(long = "rpc.max-connections", alias = "rpc-max-connections", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_connections)]
608 pub rpc_max_connections: MaxU32,
609
610 #[arg(long = "rpc.max-tracing-requests", alias = "rpc-max-tracing-requests", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_tracing_requests)]
617 pub rpc_max_tracing_requests: usize,
618
619 #[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)]
625 pub rpc_max_blocking_io_requests: usize,
626
627 #[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)]
629 pub rpc_max_trace_filter_blocks: u64,
630
631 #[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)]
633 pub rpc_max_blocks_per_filter: ZeroAsNoneU64,
634
635 #[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)]
637 pub rpc_max_logs_per_response: ZeroAsNoneU64,
638
639 #[arg(
641 long = "rpc.gascap",
642 alias = "rpc-gascap",
643 value_name = "GAS_CAP",
644 value_parser = MaxOr::new(RangedU64ValueParser::<u64>::new().range(1..)),
645 default_value_t = DefaultRpcServerArgs::get_global().rpc_gas_cap
646 )]
647 pub rpc_gas_cap: u64,
648
649 #[arg(
651 long = "rpc.evm-memory-limit",
652 alias = "rpc-evm-memory-limit",
653 value_name = "MEMORY_LIMIT",
654 value_parser = MaxOr::new(RangedU64ValueParser::<u64>::new().range(1..)),
655 default_value_t = DefaultRpcServerArgs::get_global().rpc_evm_memory_limit
656 )]
657 pub rpc_evm_memory_limit: u64,
658
659 #[arg(
661 long = "rpc.txfeecap",
662 alias = "rpc-txfeecap",
663 value_name = "TX_FEE_CAP",
664 value_parser = parse_ether_value,
665 default_value = "1.0"
666 )]
667 pub rpc_tx_fee_cap: u128,
668
669 #[arg(
671 long = "rpc.max-simulate-blocks",
672 value_name = "BLOCKS_COUNT",
673 default_value_t = DefaultRpcServerArgs::get_global().rpc_max_simulate_blocks
674 )]
675 pub rpc_max_simulate_blocks: u64,
676
677 #[arg(
679 long = "rpc.compute-state-root-for-eth-simulate",
680 env = "RETH_RPC_COMPUTE_STATE_ROOT_FOR_ETH_SIMULATE",
681 default_value_t = DefaultRpcServerArgs::get_global().rpc_compute_state_root_for_eth_simulate
682 )]
683 pub rpc_compute_state_root_for_eth_simulate: bool,
684
685 #[arg(
689 long = "rpc.eth-proof-window",
690 default_value_t = DefaultRpcServerArgs::get_global().rpc_eth_proof_window,
691 value_parser = RangedU64ValueParser::<u64>::new().range(..=constants::MAX_ETH_PROOF_WINDOW)
692 )]
693 pub rpc_eth_proof_window: u64,
694
695 #[arg(long = "rpc.proof-permits", alias = "rpc-proof-permits", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_proof_permits)]
697 pub rpc_proof_permits: usize,
698
699 #[arg(long = "rpc.pending-block", default_value = "full", value_name = "KIND")]
704 pub rpc_pending_block: PendingBlockKind,
705
706 #[arg(long = "rpc.forwarder", alias = "rpc-forwarder", value_name = "FORWARDER")]
708 pub rpc_forwarder: Option<Url>,
709
710 #[arg(long = "builder.disallow", value_name = "PATH", value_parser = reth_cli_util::parsers::read_json_from_file::<AddressSet>, default_value = Resettable::from(DefaultRpcServerArgs::get_global().builder_disallow.as_ref().map(|v| format!("{:?}", v).into())))]
713 pub builder_disallow: Option<AddressSet>,
714
715 #[command(flatten)]
717 pub rpc_state_cache: RpcStateCacheArgs,
718
719 #[command(flatten)]
721 pub gas_price_oracle: GasPriceOracleArgs,
722
723 #[arg(
725 long = "rpc.send-raw-transaction-sync-timeout",
726 value_name = "SECONDS",
727 default_value = "30s",
728 value_parser = parse_duration_from_secs_or_ms,
729 )]
730 pub rpc_send_raw_transaction_sync_timeout: Duration,
731
732 #[arg(long = "testing.skip-invalid-transactions", default_value_t = false)]
737 pub testing_skip_invalid_transactions: bool,
738
739 #[arg(long = "testing.gas-limit", value_name = "GAS_LIMIT", hide = true)]
745 pub testing_gas_limit: Option<u64>,
746
747 #[arg(long = "rpc.force-blob-sidecar-upcasting", default_value_t = false)]
753 pub rpc_force_blob_sidecar_upcasting: bool,
754}
755
756impl RpcServerArgs {
757 pub const fn with_http(mut self) -> Self {
759 self.http = true;
760 self
761 }
762
763 pub fn with_http_api(mut self, http_api: RpcModuleSelection) -> Self {
765 self.http_api = Some(http_api);
766 self
767 }
768
769 pub const fn with_ws(mut self) -> Self {
771 self.ws = true;
772 self
773 }
774
775 pub fn with_ws_api(mut self, ws_api: RpcModuleSelection) -> Self {
777 self.ws_api = Some(ws_api);
778 self
779 }
780
781 pub const fn with_auth_ipc(mut self) -> Self {
783 self.auth_ipc = true;
784 self
785 }
786
787 pub fn with_api(self, api: RpcModuleSelection) -> Self {
791 self.with_http_api(api.clone()).with_ws_api(api)
792 }
793
794 pub fn adjust_instance_ports(&mut self, instance: Option<u16>) {
809 if let Some(instance) = instance {
810 debug_assert_ne!(instance, 0, "instance must be non-zero");
811 self.auth_port += instance * 100 - 100;
813 self.http_port -= instance - 1;
815 self.ws_port += instance * 2 - 2;
817 self.ipcpath = format!("{}-{}", self.ipcpath, instance);
819 }
820 }
821
822 pub const fn with_http_unused_port(mut self) -> Self {
825 self.http_port = 0;
826 self
827 }
828
829 pub const fn with_ws_unused_port(mut self) -> Self {
832 self.ws_port = 0;
833 self
834 }
835
836 pub const fn with_auth_unused_port(mut self) -> Self {
839 self.auth_port = 0;
840 self
841 }
842
843 pub fn with_ipc_random_path(mut self) -> Self {
846 let random_string: String =
847 rand::rng().sample_iter(rand::distr::Alphanumeric).take(8).map(char::from).collect();
848 self.ipcpath = format!("{}-{}", self.ipcpath, random_string);
849 self
850 }
851
852 pub fn with_unused_ports(mut self) -> Self {
855 self = self.with_http_unused_port();
856 self = self.with_ws_unused_port();
857 self = self.with_auth_unused_port();
858 self = self.with_ipc_random_path();
859 self
860 }
861
862 pub fn apply<F>(self, f: F) -> Self
864 where
865 F: FnOnce(Self) -> Self,
866 {
867 f(self)
868 }
869
870 pub const fn with_send_raw_transaction_sync_timeout(mut self, timeout: Duration) -> Self {
872 self.rpc_send_raw_transaction_sync_timeout = timeout;
873 self
874 }
875
876 pub fn is_namespace_enabled(&self, ns: RethRpcModule) -> bool {
878 if self.http && self.http_api.as_ref().is_some_and(|api| api.contains(&ns)) {
879 return true;
880 }
881 if self.ws && self.ws_api.as_ref().is_some_and(|api| api.contains(&ns)) {
882 return true;
883 }
884 !self.ipcdisable
886 }
887
888 pub const fn with_force_blob_sidecar_upcasting(mut self) -> Self {
890 self.rpc_force_blob_sidecar_upcasting = true;
891 self
892 }
893}
894
895impl Default for RpcServerArgs {
896 fn default() -> Self {
897 let DefaultRpcServerArgs {
898 http,
899 http_addr,
900 http_port,
901 http_disable_compression,
902 http_compression_algorithms,
903 http_decompression_algorithms,
904 http_api,
905 http_corsdomain,
906 ws,
907 ws_addr,
908 ws_port,
909 ws_allowed_origins,
910 ws_api,
911 ipcdisable,
912 ipcpath,
913 ipc_socket_permissions,
914 auth_addr,
915 auth_port,
916 auth_jwtsecret,
917 auth_ipc,
918 auth_ipc_path,
919 disable_auth_server,
920 rpc_jwtsecret,
921 rpc_disable_metrics,
922 rpc_max_request_size,
923 rpc_max_response_size,
924 rpc_max_subscriptions_per_connection,
925 rpc_max_connections,
926 rpc_max_tracing_requests,
927 rpc_max_blocking_io_requests,
928 rpc_max_trace_filter_blocks,
929 rpc_max_blocks_per_filter,
930 rpc_max_logs_per_response,
931 rpc_gas_cap,
932 rpc_evm_memory_limit,
933 rpc_tx_fee_cap,
934 rpc_max_simulate_blocks,
935 rpc_compute_state_root_for_eth_simulate,
936 rpc_eth_proof_window,
937 rpc_proof_permits,
938 rpc_pending_block,
939 rpc_forwarder,
940 builder_disallow,
941 rpc_state_cache,
942 gas_price_oracle,
943 rpc_send_raw_transaction_sync_timeout,
944 } = DefaultRpcServerArgs::get_global().clone();
945 Self {
946 http,
947 http_addr,
948 http_port,
949 http_disable_compression,
950 http_compression_algorithms,
951 http_decompression_algorithms,
952 http_api,
953 http_corsdomain,
954 ws,
955 ws_addr,
956 ws_port,
957 ws_allowed_origins,
958 ws_api,
959 ipcdisable,
960 ipcpath,
961 ipc_socket_permissions,
962 auth_addr,
963 auth_port,
964 auth_jwtsecret,
965 auth_jwtsecret_hex: None,
966 auth_ipc,
967 auth_ipc_path,
968 disable_auth_server,
969 rpc_jwtsecret,
970 rpc_disable_metrics,
971 rpc_max_request_size,
972 rpc_max_response_size,
973 rpc_max_subscriptions_per_connection,
974 rpc_max_connections,
975 rpc_max_tracing_requests,
976 rpc_max_blocking_io_requests,
977 rpc_max_trace_filter_blocks,
978 rpc_max_blocks_per_filter,
979 rpc_max_logs_per_response,
980 rpc_gas_cap,
981 rpc_evm_memory_limit,
982 rpc_tx_fee_cap,
983 rpc_max_simulate_blocks,
984 rpc_compute_state_root_for_eth_simulate,
985 rpc_eth_proof_window,
986 rpc_proof_permits,
987 rpc_pending_block,
988 rpc_forwarder,
989 builder_disallow,
990 rpc_state_cache,
991 gas_price_oracle,
992 rpc_send_raw_transaction_sync_timeout,
993 testing_skip_invalid_transactions: false,
994 testing_gas_limit: None,
995 rpc_force_blob_sidecar_upcasting: false,
996 }
997 }
998}
999
1000#[derive(Clone, Debug, Default)]
1002#[non_exhaustive]
1003struct RpcModuleSelectionValueParser;
1004
1005impl TypedValueParser for RpcModuleSelectionValueParser {
1006 type Value = RpcModuleSelection;
1007
1008 fn parse_ref(
1009 &self,
1010 _cmd: &Command,
1011 _arg: Option<&Arg>,
1012 value: &OsStr,
1013 ) -> Result<Self::Value, clap::Error> {
1014 let val =
1015 value.to_str().ok_or_else(|| clap::Error::new(clap::error::ErrorKind::InvalidUtf8))?;
1016 Ok(val
1018 .parse::<RpcModuleSelection>()
1019 .expect("RpcModuleSelection parsing cannot fail with Other variant"))
1020 }
1021
1022 fn possible_values(&self) -> Option<Box<dyn Iterator<Item = PossibleValue> + '_>> {
1023 let values = RethRpcModule::standard_variant_names().map(PossibleValue::new);
1025 Some(Box::new(values))
1026 }
1027}
1028
1029#[cfg(test)]
1030mod tests {
1031 use super::*;
1032 use clap::{Args, Parser};
1033
1034 #[derive(Parser)]
1036 struct CommandParser<T: Args> {
1037 #[command(flatten)]
1038 args: T,
1039 }
1040
1041 #[test]
1042 fn test_rpc_server_args_parser() {
1043 let args =
1044 CommandParser::<RpcServerArgs>::parse_from(["reth", "--http.api", "eth,admin,debug"])
1045 .args;
1046
1047 let apis = args.http_api.unwrap();
1048 let expected = RpcModuleSelection::try_from_selection(["eth", "admin", "debug"]).unwrap();
1049
1050 assert_eq!(apis, expected);
1051 }
1052
1053 #[test]
1054 fn test_rpc_server_eth_call_bundle_args() {
1055 let args =
1056 CommandParser::<RpcServerArgs>::parse_from(["reth", "--http.api", "eth,admin,debug"])
1057 .args;
1058
1059 let apis = args.http_api.unwrap();
1060 let expected = RpcModuleSelection::try_from_selection(["eth", "admin", "debug"]).unwrap();
1061
1062 assert_eq!(apis, expected);
1063 }
1064
1065 #[test]
1066 fn test_rpc_server_args_parser_none() {
1067 let args = CommandParser::<RpcServerArgs>::parse_from(["reth", "--http.api", "none"]).args;
1068 let apis = args.http_api.unwrap();
1069 let expected = RpcModuleSelection::Selection(Default::default());
1070 assert_eq!(apis, expected);
1071 }
1072
1073 #[test]
1074 fn http_compression_algorithms_are_optional() {
1075 let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
1076 assert!(args.http_compression_algorithms.is_none());
1077
1078 let args = CommandParser::<RpcServerArgs>::parse_from([
1079 "reth",
1080 "--http.compression",
1081 "zstd,gzip,deflate,br",
1082 ])
1083 .args;
1084 assert_eq!(
1085 args.http_compression_algorithms.as_deref().unwrap(),
1086 ["zstd", "gzip", "deflate", "br"]
1087 );
1088
1089 let result =
1090 CommandParser::<RpcServerArgs>::try_parse_from(["reth", "--http.compression", "gizp"]);
1091 assert!(result.is_err());
1092 }
1093
1094 #[test]
1095 fn rpc_server_args_default_sanity_test() {
1096 let default_args = RpcServerArgs::default();
1097 let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
1098
1099 assert_eq!(args, default_args);
1100 }
1101
1102 #[test]
1103 fn http_request_decompression_is_opt_in() {
1104 let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
1105 assert!(args.http_decompression_algorithms.is_none());
1106
1107 let args = CommandParser::<RpcServerArgs>::parse_from([
1108 "reth",
1109 "--http.decompression",
1110 "zstd,gzip,deflate,br",
1111 ])
1112 .args;
1113 assert_eq!(
1114 args.http_decompression_algorithms.as_deref().unwrap(),
1115 ["zstd", "gzip", "deflate", "br"]
1116 );
1117 }
1118
1119 #[test]
1120 fn invalid_http_request_decompression_algorithm_is_rejected() {
1121 let result = CommandParser::<RpcServerArgs>::try_parse_from([
1122 "reth",
1123 "--http.decompression",
1124 "gizp",
1125 ]);
1126 assert!(result.is_err());
1127 }
1128
1129 #[test]
1130 fn test_rpc_disable_metrics_arg() {
1131 let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
1132 assert!(!args.rpc_disable_metrics);
1133
1134 let args =
1135 CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.disable-metrics"]).args;
1136 assert!(args.rpc_disable_metrics);
1137 }
1138
1139 #[test]
1140 fn test_rpc_tx_fee_cap_parse_integer() {
1141 let args = CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.txfeecap", "2"]).args;
1142 let expected = 2_000_000_000_000_000_000u128; assert_eq!(args.rpc_tx_fee_cap, expected);
1144 }
1145
1146 #[test]
1147 fn test_rpc_tx_fee_cap_parse_decimal() {
1148 let args =
1149 CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.txfeecap", "1.5"]).args;
1150 let expected = 1_500_000_000_000_000_000u128; assert_eq!(args.rpc_tx_fee_cap, expected);
1152 }
1153
1154 #[test]
1155 fn test_rpc_tx_fee_cap_parse_zero() {
1156 let args = CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.txfeecap", "0"]).args;
1157 assert_eq!(args.rpc_tx_fee_cap, 0); }
1159
1160 #[test]
1161 fn test_rpc_tx_fee_cap_parse_none() {
1162 let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
1163 let expected = 1_000_000_000_000_000_000u128;
1164 assert_eq!(args.rpc_tx_fee_cap, expected); }
1166
1167 #[test]
1168 fn test_rpc_server_args() {
1169 let args = RpcServerArgs {
1170 http: true,
1171 http_addr: "127.0.0.1".parse().unwrap(),
1172 http_port: 8545,
1173 http_disable_compression: false,
1174 http_compression_algorithms: None,
1175 http_decompression_algorithms: None,
1176 http_api: Some(RpcModuleSelection::try_from_selection(["eth", "admin"]).unwrap()),
1177 http_corsdomain: Some("*".to_string()),
1178 ws: true,
1179 ws_addr: "127.0.0.1".parse().unwrap(),
1180 ws_port: 8546,
1181 ws_allowed_origins: Some("*".to_string()),
1182 ws_api: Some(RpcModuleSelection::try_from_selection(["eth", "admin"]).unwrap()),
1183 ipcdisable: false,
1184 ipcpath: "reth.ipc".to_string(),
1185 ipc_socket_permissions: Some("0o666".to_string()),
1186 auth_addr: "127.0.0.1".parse().unwrap(),
1187 auth_port: 8551,
1188 auth_jwtsecret: Some(std::path::PathBuf::from("/tmp/jwt.hex")),
1189 auth_jwtsecret_hex: None,
1190 auth_ipc: false,
1191 auth_ipc_path: "engine.ipc".to_string(),
1192 disable_auth_server: false,
1193 rpc_jwtsecret: Some(
1194 JwtSecret::from_hex(
1195 "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
1196 )
1197 .unwrap(),
1198 ),
1199 rpc_disable_metrics: false,
1200 rpc_max_request_size: 15u32.into(),
1201 rpc_max_response_size: 160u32.into(),
1202 rpc_max_subscriptions_per_connection: 1024u32.into(),
1203 rpc_max_connections: 500u32.into(),
1204 rpc_max_tracing_requests: 16,
1205 rpc_max_blocking_io_requests: 256,
1206 rpc_max_trace_filter_blocks: 4000,
1207 rpc_max_blocks_per_filter: 1000u64.into(),
1208 rpc_max_logs_per_response: 10000u64.into(),
1209 rpc_gas_cap: 50_000_000,
1210 rpc_evm_memory_limit: 256,
1211 rpc_tx_fee_cap: 2_000_000_000_000_000_000u128,
1212 rpc_max_simulate_blocks: 256,
1213 rpc_compute_state_root_for_eth_simulate: false,
1214 rpc_eth_proof_window: 100_000,
1215 rpc_proof_permits: 16,
1216 rpc_pending_block: PendingBlockKind::Full,
1217 rpc_forwarder: Some("http://localhost:8545".parse().unwrap()),
1218 builder_disallow: None,
1219 rpc_state_cache: RpcStateCacheArgs {
1220 max_blocks: 5000,
1221 max_receipts: 2000,
1222 max_headers: 1000,
1223 max_bals: 1000,
1224 max_concurrent_db_requests: 512,
1225 max_cached_tx_hashes: 100_000,
1226 },
1227 gas_price_oracle: GasPriceOracleArgs {
1228 blocks: 20,
1229 ignore_price: 2,
1230 max_price: 500_000_000_000,
1231 percentile: 60,
1232 default_suggested_fee: None,
1233 },
1234 rpc_send_raw_transaction_sync_timeout: std::time::Duration::from_secs(30),
1235 testing_skip_invalid_transactions: true,
1236 testing_gas_limit: None,
1237 rpc_force_blob_sidecar_upcasting: false,
1238 };
1239
1240 let parsed_args = CommandParser::<RpcServerArgs>::parse_from([
1241 "reth",
1242 "--http",
1243 "--http.addr",
1244 "127.0.0.1",
1245 "--http.port",
1246 "8545",
1247 "--http.api",
1248 "eth,admin",
1249 "--http.corsdomain",
1250 "*",
1251 "--ws",
1252 "--ws.addr",
1253 "127.0.0.1",
1254 "--ws.port",
1255 "8546",
1256 "--ws.origins",
1257 "*",
1258 "--ws.api",
1259 "eth,admin",
1260 "--ipcpath",
1261 "reth.ipc",
1262 "--ipc.permissions",
1263 "0o666",
1264 "--authrpc.addr",
1265 "127.0.0.1",
1266 "--authrpc.port",
1267 "8551",
1268 "--authrpc.jwtsecret",
1269 "/tmp/jwt.hex",
1270 "--auth-ipc.path",
1271 "engine.ipc",
1272 "--rpc.jwtsecret",
1273 "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
1274 "--rpc.max-request-size",
1275 "15",
1276 "--rpc.max-response-size",
1277 "160",
1278 "--rpc.max-subscriptions-per-connection",
1279 "1024",
1280 "--rpc.max-connections",
1281 "500",
1282 "--rpc.max-tracing-requests",
1283 "16",
1284 "--rpc.max-blocking-io-requests",
1285 "256",
1286 "--rpc.max-trace-filter-blocks",
1287 "4000",
1288 "--rpc.max-blocks-per-filter",
1289 "1000",
1290 "--rpc.max-logs-per-response",
1291 "10000",
1292 "--rpc.gascap",
1293 "50000000",
1294 "--rpc.evm-memory-limit",
1295 "256",
1296 "--rpc.txfeecap",
1297 "2.0",
1298 "--rpc.max-simulate-blocks",
1299 "256",
1300 "--rpc.eth-proof-window",
1301 "100000",
1302 "--rpc.proof-permits",
1303 "16",
1304 "--rpc.pending-block",
1305 "full",
1306 "--rpc.forwarder",
1307 "http://localhost:8545",
1308 "--rpc-cache.max-blocks",
1309 "5000",
1310 "--rpc-cache.max-receipts",
1311 "2000",
1312 "--rpc-cache.max-headers",
1313 "1000",
1314 "--rpc-cache.max-bals",
1315 "1000",
1316 "--rpc-cache.max-concurrent-db-requests",
1317 "512",
1318 "--gpo.blocks",
1319 "20",
1320 "--gpo.ignoreprice",
1321 "2",
1322 "--gpo.maxprice",
1323 "500000000000",
1324 "--gpo.percentile",
1325 "60",
1326 "--rpc.send-raw-transaction-sync-timeout",
1327 "30s",
1328 "--testing.skip-invalid-transactions",
1329 ])
1330 .args;
1331
1332 assert_eq!(parsed_args, args);
1333 }
1334
1335 #[test]
1336 fn parse_auth_jwtsecret_hex() {
1337 let hex = "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
1338 let args =
1339 CommandParser::<RpcServerArgs>::parse_from(["reth", "--authrpc.jwtsecret-hex", hex])
1340 .args;
1341
1342 let expected = JwtSecret::from_hex(hex).unwrap();
1343 assert_eq!(args.auth_jwtsecret_hex, Some(expected));
1344 assert_eq!(args.auth_jwtsecret, None);
1345 }
1346
1347 #[test]
1348 fn parse_auth_jwtsecret_hex_with_0x_prefix() {
1349 let hex = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
1350 let args =
1351 CommandParser::<RpcServerArgs>::parse_from(["reth", "--authrpc.jwtsecret-hex", hex])
1352 .args;
1353
1354 let expected = JwtSecret::from_hex(hex).unwrap();
1355 assert_eq!(args.auth_jwtsecret_hex, Some(expected));
1356 assert_eq!(args.auth_jwtsecret, None);
1357 }
1358
1359 #[test]
1360 fn test_auth_jwtsecret_and_hex_are_mutually_exclusive() {
1361 let result = CommandParser::<RpcServerArgs>::try_parse_from([
1362 "reth",
1363 "--authrpc.jwtsecret",
1364 "/tmp/jwt.hex",
1365 "--authrpc.jwtsecret-hex",
1366 "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
1367 ]);
1368
1369 assert!(result.is_err());
1370 }
1371}