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, 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(long, default_value_t = DefaultRpcServerArgs::get_global().auth_ipc)]
553 pub auth_ipc: bool,
554
555 #[arg(long = "auth-ipc.path", default_value_t = DefaultRpcServerArgs::get_global().auth_ipc_path.clone())]
557 pub auth_ipc_path: String,
558
559 #[arg(long = "disable-auth-server", alias = "disable-engine-api", default_value_t = DefaultRpcServerArgs::get_global().disable_auth_server)]
564 pub disable_auth_server: bool,
565
566 #[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())))]
572 pub rpc_jwtsecret: Option<JwtSecret>,
573
574 #[arg(long = "rpc.disable-metrics", default_value_t = DefaultRpcServerArgs::get_global().rpc_disable_metrics)]
576 pub rpc_disable_metrics: bool,
577
578 #[arg(long = "rpc.max-request-size", alias = "rpc-max-request-size", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_request_size)]
583 pub rpc_max_request_size: MaxU32,
584
585 #[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)]
587 pub rpc_max_response_size: MaxU32,
588
589 #[arg(long = "rpc.max-subscriptions-per-connection", alias = "rpc-max-subscriptions-per-connection", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_subscriptions_per_connection)]
591 pub rpc_max_subscriptions_per_connection: MaxU32,
592
593 #[arg(long = "rpc.max-connections", alias = "rpc-max-connections", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_connections)]
595 pub rpc_max_connections: MaxU32,
596
597 #[arg(long = "rpc.max-tracing-requests", alias = "rpc-max-tracing-requests", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_tracing_requests)]
604 pub rpc_max_tracing_requests: usize,
605
606 #[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)]
612 pub rpc_max_blocking_io_requests: usize,
613
614 #[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)]
616 pub rpc_max_trace_filter_blocks: u64,
617
618 #[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)]
620 pub rpc_max_blocks_per_filter: ZeroAsNoneU64,
621
622 #[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)]
624 pub rpc_max_logs_per_response: ZeroAsNoneU64,
625
626 #[arg(
628 long = "rpc.gascap",
629 alias = "rpc-gascap",
630 value_name = "GAS_CAP",
631 value_parser = MaxOr::new(RangedU64ValueParser::<u64>::new().range(1..)),
632 default_value_t = DefaultRpcServerArgs::get_global().rpc_gas_cap
633 )]
634 pub rpc_gas_cap: u64,
635
636 #[arg(
638 long = "rpc.evm-memory-limit",
639 alias = "rpc-evm-memory-limit",
640 value_name = "MEMORY_LIMIT",
641 value_parser = MaxOr::new(RangedU64ValueParser::<u64>::new().range(1..)),
642 default_value_t = DefaultRpcServerArgs::get_global().rpc_evm_memory_limit
643 )]
644 pub rpc_evm_memory_limit: u64,
645
646 #[arg(
648 long = "rpc.txfeecap",
649 alias = "rpc-txfeecap",
650 value_name = "TX_FEE_CAP",
651 value_parser = parse_ether_value,
652 default_value = "1.0"
653 )]
654 pub rpc_tx_fee_cap: u128,
655
656 #[arg(
658 long = "rpc.max-simulate-blocks",
659 value_name = "BLOCKS_COUNT",
660 default_value_t = DefaultRpcServerArgs::get_global().rpc_max_simulate_blocks
661 )]
662 pub rpc_max_simulate_blocks: u64,
663
664 #[arg(
666 long = "rpc.compute-state-root-for-eth-simulate",
667 env = "RETH_RPC_COMPUTE_STATE_ROOT_FOR_ETH_SIMULATE",
668 default_value_t = DefaultRpcServerArgs::get_global().rpc_compute_state_root_for_eth_simulate
669 )]
670 pub rpc_compute_state_root_for_eth_simulate: bool,
671
672 #[arg(
676 long = "rpc.eth-proof-window",
677 default_value_t = DefaultRpcServerArgs::get_global().rpc_eth_proof_window,
678 value_parser = RangedU64ValueParser::<u64>::new().range(..=constants::MAX_ETH_PROOF_WINDOW)
679 )]
680 pub rpc_eth_proof_window: u64,
681
682 #[arg(long = "rpc.proof-permits", alias = "rpc-proof-permits", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_proof_permits)]
684 pub rpc_proof_permits: usize,
685
686 #[arg(long = "rpc.pending-block", default_value = "full", value_name = "KIND")]
691 pub rpc_pending_block: PendingBlockKind,
692
693 #[arg(long = "rpc.forwarder", alias = "rpc-forwarder", value_name = "FORWARDER")]
695 pub rpc_forwarder: Option<Url>,
696
697 #[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())))]
700 pub builder_disallow: Option<AddressSet>,
701
702 #[command(flatten)]
704 pub rpc_state_cache: RpcStateCacheArgs,
705
706 #[command(flatten)]
708 pub gas_price_oracle: GasPriceOracleArgs,
709
710 #[arg(
712 long = "rpc.send-raw-transaction-sync-timeout",
713 value_name = "SECONDS",
714 default_value = "30s",
715 value_parser = parse_duration_from_secs_or_ms,
716 )]
717 pub rpc_send_raw_transaction_sync_timeout: Duration,
718
719 #[arg(long = "testing.skip-invalid-transactions", default_value_t = false)]
724 pub testing_skip_invalid_transactions: bool,
725
726 #[arg(long = "testing.gas-limit", value_name = "GAS_LIMIT", hide = true)]
732 pub testing_gas_limit: Option<u64>,
733
734 #[arg(long = "rpc.force-blob-sidecar-upcasting", default_value_t = false)]
740 pub rpc_force_blob_sidecar_upcasting: bool,
741}
742
743impl RpcServerArgs {
744 pub const fn with_http(mut self) -> Self {
746 self.http = true;
747 self
748 }
749
750 pub fn with_http_api(mut self, http_api: RpcModuleSelection) -> Self {
752 self.http_api = Some(http_api);
753 self
754 }
755
756 pub const fn with_ws(mut self) -> Self {
758 self.ws = true;
759 self
760 }
761
762 pub fn with_ws_api(mut self, ws_api: RpcModuleSelection) -> Self {
764 self.ws_api = Some(ws_api);
765 self
766 }
767
768 pub const fn with_auth_ipc(mut self) -> Self {
770 self.auth_ipc = true;
771 self
772 }
773
774 pub fn with_api(self, api: RpcModuleSelection) -> Self {
778 self.with_http_api(api.clone()).with_ws_api(api)
779 }
780
781 pub fn adjust_instance_ports(&mut self, instance: Option<u16>) {
796 if let Some(instance) = instance {
797 debug_assert_ne!(instance, 0, "instance must be non-zero");
798 self.auth_port += instance * 100 - 100;
800 self.http_port -= instance - 1;
802 self.ws_port += instance * 2 - 2;
804 self.ipcpath = format!("{}-{}", self.ipcpath, instance);
806 }
807 }
808
809 pub const fn with_http_unused_port(mut self) -> Self {
812 self.http_port = 0;
813 self
814 }
815
816 pub const fn with_ws_unused_port(mut self) -> Self {
819 self.ws_port = 0;
820 self
821 }
822
823 pub const fn with_auth_unused_port(mut self) -> Self {
826 self.auth_port = 0;
827 self
828 }
829
830 pub fn with_ipc_random_path(mut self) -> Self {
833 let random_string: String =
834 rand::rng().sample_iter(rand::distr::Alphanumeric).take(8).map(char::from).collect();
835 self.ipcpath = format!("{}-{}", self.ipcpath, random_string);
836 self
837 }
838
839 pub fn with_unused_ports(mut self) -> Self {
842 self = self.with_http_unused_port();
843 self = self.with_ws_unused_port();
844 self = self.with_auth_unused_port();
845 self = self.with_ipc_random_path();
846 self
847 }
848
849 pub fn apply<F>(self, f: F) -> Self
851 where
852 F: FnOnce(Self) -> Self,
853 {
854 f(self)
855 }
856
857 pub const fn with_send_raw_transaction_sync_timeout(mut self, timeout: Duration) -> Self {
859 self.rpc_send_raw_transaction_sync_timeout = timeout;
860 self
861 }
862
863 pub fn is_namespace_enabled(&self, ns: RethRpcModule) -> bool {
865 if self.http && self.http_api.as_ref().is_some_and(|api| api.contains(&ns)) {
866 return true;
867 }
868 if self.ws && self.ws_api.as_ref().is_some_and(|api| api.contains(&ns)) {
869 return true;
870 }
871 !self.ipcdisable
873 }
874
875 pub const fn with_force_blob_sidecar_upcasting(mut self) -> Self {
877 self.rpc_force_blob_sidecar_upcasting = true;
878 self
879 }
880}
881
882impl Default for RpcServerArgs {
883 fn default() -> Self {
884 let DefaultRpcServerArgs {
885 http,
886 http_addr,
887 http_port,
888 http_disable_compression,
889 http_compression_algorithms,
890 http_decompression_algorithms,
891 http_api,
892 http_corsdomain,
893 ws,
894 ws_addr,
895 ws_port,
896 ws_allowed_origins,
897 ws_api,
898 ipcdisable,
899 ipcpath,
900 ipc_socket_permissions,
901 auth_addr,
902 auth_port,
903 auth_jwtsecret,
904 auth_ipc,
905 auth_ipc_path,
906 disable_auth_server,
907 rpc_jwtsecret,
908 rpc_disable_metrics,
909 rpc_max_request_size,
910 rpc_max_response_size,
911 rpc_max_subscriptions_per_connection,
912 rpc_max_connections,
913 rpc_max_tracing_requests,
914 rpc_max_blocking_io_requests,
915 rpc_max_trace_filter_blocks,
916 rpc_max_blocks_per_filter,
917 rpc_max_logs_per_response,
918 rpc_gas_cap,
919 rpc_evm_memory_limit,
920 rpc_tx_fee_cap,
921 rpc_max_simulate_blocks,
922 rpc_compute_state_root_for_eth_simulate,
923 rpc_eth_proof_window,
924 rpc_proof_permits,
925 rpc_pending_block,
926 rpc_forwarder,
927 builder_disallow,
928 rpc_state_cache,
929 gas_price_oracle,
930 rpc_send_raw_transaction_sync_timeout,
931 } = DefaultRpcServerArgs::get_global().clone();
932 Self {
933 http,
934 http_addr,
935 http_port,
936 http_disable_compression,
937 http_compression_algorithms,
938 http_decompression_algorithms,
939 http_api,
940 http_corsdomain,
941 ws,
942 ws_addr,
943 ws_port,
944 ws_allowed_origins,
945 ws_api,
946 ipcdisable,
947 ipcpath,
948 ipc_socket_permissions,
949 auth_addr,
950 auth_port,
951 auth_jwtsecret,
952 auth_ipc,
953 auth_ipc_path,
954 disable_auth_server,
955 rpc_jwtsecret,
956 rpc_disable_metrics,
957 rpc_max_request_size,
958 rpc_max_response_size,
959 rpc_max_subscriptions_per_connection,
960 rpc_max_connections,
961 rpc_max_tracing_requests,
962 rpc_max_blocking_io_requests,
963 rpc_max_trace_filter_blocks,
964 rpc_max_blocks_per_filter,
965 rpc_max_logs_per_response,
966 rpc_gas_cap,
967 rpc_evm_memory_limit,
968 rpc_tx_fee_cap,
969 rpc_max_simulate_blocks,
970 rpc_compute_state_root_for_eth_simulate,
971 rpc_eth_proof_window,
972 rpc_proof_permits,
973 rpc_pending_block,
974 rpc_forwarder,
975 builder_disallow,
976 rpc_state_cache,
977 gas_price_oracle,
978 rpc_send_raw_transaction_sync_timeout,
979 testing_skip_invalid_transactions: false,
980 testing_gas_limit: None,
981 rpc_force_blob_sidecar_upcasting: false,
982 }
983 }
984}
985
986#[derive(Clone, Debug, Default)]
988#[non_exhaustive]
989struct RpcModuleSelectionValueParser;
990
991impl TypedValueParser for RpcModuleSelectionValueParser {
992 type Value = RpcModuleSelection;
993
994 fn parse_ref(
995 &self,
996 _cmd: &Command,
997 _arg: Option<&Arg>,
998 value: &OsStr,
999 ) -> Result<Self::Value, clap::Error> {
1000 let val =
1001 value.to_str().ok_or_else(|| clap::Error::new(clap::error::ErrorKind::InvalidUtf8))?;
1002 Ok(val
1004 .parse::<RpcModuleSelection>()
1005 .expect("RpcModuleSelection parsing cannot fail with Other variant"))
1006 }
1007
1008 fn possible_values(&self) -> Option<Box<dyn Iterator<Item = PossibleValue> + '_>> {
1009 let values = RethRpcModule::standard_variant_names().map(PossibleValue::new);
1011 Some(Box::new(values))
1012 }
1013}
1014
1015#[cfg(test)]
1016mod tests {
1017 use super::*;
1018 use clap::{Args, Parser};
1019
1020 #[derive(Parser)]
1022 struct CommandParser<T: Args> {
1023 #[command(flatten)]
1024 args: T,
1025 }
1026
1027 #[test]
1028 fn test_rpc_server_args_parser() {
1029 let args =
1030 CommandParser::<RpcServerArgs>::parse_from(["reth", "--http.api", "eth,admin,debug"])
1031 .args;
1032
1033 let apis = args.http_api.unwrap();
1034 let expected = RpcModuleSelection::try_from_selection(["eth", "admin", "debug"]).unwrap();
1035
1036 assert_eq!(apis, expected);
1037 }
1038
1039 #[test]
1040 fn test_rpc_server_eth_call_bundle_args() {
1041 let args =
1042 CommandParser::<RpcServerArgs>::parse_from(["reth", "--http.api", "eth,admin,debug"])
1043 .args;
1044
1045 let apis = args.http_api.unwrap();
1046 let expected = RpcModuleSelection::try_from_selection(["eth", "admin", "debug"]).unwrap();
1047
1048 assert_eq!(apis, expected);
1049 }
1050
1051 #[test]
1052 fn test_rpc_server_args_parser_none() {
1053 let args = CommandParser::<RpcServerArgs>::parse_from(["reth", "--http.api", "none"]).args;
1054 let apis = args.http_api.unwrap();
1055 let expected = RpcModuleSelection::Selection(Default::default());
1056 assert_eq!(apis, expected);
1057 }
1058
1059 #[test]
1060 fn http_compression_algorithms_are_optional() {
1061 let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
1062 assert!(args.http_compression_algorithms.is_none());
1063
1064 let args = CommandParser::<RpcServerArgs>::parse_from([
1065 "reth",
1066 "--http.compression",
1067 "zstd,gzip,deflate,br",
1068 ])
1069 .args;
1070 assert_eq!(
1071 args.http_compression_algorithms.as_deref().unwrap(),
1072 ["zstd", "gzip", "deflate", "br"]
1073 );
1074
1075 let result =
1076 CommandParser::<RpcServerArgs>::try_parse_from(["reth", "--http.compression", "gizp"]);
1077 assert!(result.is_err());
1078 }
1079
1080 #[test]
1081 fn rpc_server_args_default_sanity_test() {
1082 let default_args = RpcServerArgs::default();
1083 let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
1084
1085 assert_eq!(args, default_args);
1086 }
1087
1088 #[test]
1089 fn http_request_decompression_is_opt_in() {
1090 let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
1091 assert!(args.http_decompression_algorithms.is_none());
1092
1093 let args = CommandParser::<RpcServerArgs>::parse_from([
1094 "reth",
1095 "--http.decompression",
1096 "zstd,gzip,deflate,br",
1097 ])
1098 .args;
1099 assert_eq!(
1100 args.http_decompression_algorithms.as_deref().unwrap(),
1101 ["zstd", "gzip", "deflate", "br"]
1102 );
1103 }
1104
1105 #[test]
1106 fn invalid_http_request_decompression_algorithm_is_rejected() {
1107 let result = CommandParser::<RpcServerArgs>::try_parse_from([
1108 "reth",
1109 "--http.decompression",
1110 "gizp",
1111 ]);
1112 assert!(result.is_err());
1113 }
1114
1115 #[test]
1116 fn test_rpc_disable_metrics_arg() {
1117 let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
1118 assert!(!args.rpc_disable_metrics);
1119
1120 let args =
1121 CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.disable-metrics"]).args;
1122 assert!(args.rpc_disable_metrics);
1123 }
1124
1125 #[test]
1126 fn test_rpc_tx_fee_cap_parse_integer() {
1127 let args = CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.txfeecap", "2"]).args;
1128 let expected = 2_000_000_000_000_000_000u128; assert_eq!(args.rpc_tx_fee_cap, expected);
1130 }
1131
1132 #[test]
1133 fn test_rpc_tx_fee_cap_parse_decimal() {
1134 let args =
1135 CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.txfeecap", "1.5"]).args;
1136 let expected = 1_500_000_000_000_000_000u128; assert_eq!(args.rpc_tx_fee_cap, expected);
1138 }
1139
1140 #[test]
1141 fn test_rpc_tx_fee_cap_parse_zero() {
1142 let args = CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.txfeecap", "0"]).args;
1143 assert_eq!(args.rpc_tx_fee_cap, 0); }
1145
1146 #[test]
1147 fn test_rpc_tx_fee_cap_parse_none() {
1148 let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
1149 let expected = 1_000_000_000_000_000_000u128;
1150 assert_eq!(args.rpc_tx_fee_cap, expected); }
1152
1153 #[test]
1154 fn test_rpc_server_args() {
1155 let args = RpcServerArgs {
1156 http: true,
1157 http_addr: "127.0.0.1".parse().unwrap(),
1158 http_port: 8545,
1159 http_disable_compression: false,
1160 http_compression_algorithms: None,
1161 http_decompression_algorithms: None,
1162 http_api: Some(RpcModuleSelection::try_from_selection(["eth", "admin"]).unwrap()),
1163 http_corsdomain: Some("*".to_string()),
1164 ws: true,
1165 ws_addr: "127.0.0.1".parse().unwrap(),
1166 ws_port: 8546,
1167 ws_allowed_origins: Some("*".to_string()),
1168 ws_api: Some(RpcModuleSelection::try_from_selection(["eth", "admin"]).unwrap()),
1169 ipcdisable: false,
1170 ipcpath: "reth.ipc".to_string(),
1171 ipc_socket_permissions: Some("0o666".to_string()),
1172 auth_addr: "127.0.0.1".parse().unwrap(),
1173 auth_port: 8551,
1174 auth_jwtsecret: Some(std::path::PathBuf::from("/tmp/jwt.hex")),
1175 auth_ipc: false,
1176 auth_ipc_path: "engine.ipc".to_string(),
1177 disable_auth_server: false,
1178 rpc_jwtsecret: Some(
1179 JwtSecret::from_hex(
1180 "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
1181 )
1182 .unwrap(),
1183 ),
1184 rpc_disable_metrics: false,
1185 rpc_max_request_size: 15u32.into(),
1186 rpc_max_response_size: 160u32.into(),
1187 rpc_max_subscriptions_per_connection: 1024u32.into(),
1188 rpc_max_connections: 500u32.into(),
1189 rpc_max_tracing_requests: 16,
1190 rpc_max_blocking_io_requests: 256,
1191 rpc_max_trace_filter_blocks: 4000,
1192 rpc_max_blocks_per_filter: 1000u64.into(),
1193 rpc_max_logs_per_response: 10000u64.into(),
1194 rpc_gas_cap: 50_000_000,
1195 rpc_evm_memory_limit: 256,
1196 rpc_tx_fee_cap: 2_000_000_000_000_000_000u128,
1197 rpc_max_simulate_blocks: 256,
1198 rpc_compute_state_root_for_eth_simulate: false,
1199 rpc_eth_proof_window: 100_000,
1200 rpc_proof_permits: 16,
1201 rpc_pending_block: PendingBlockKind::Full,
1202 rpc_forwarder: Some("http://localhost:8545".parse().unwrap()),
1203 builder_disallow: None,
1204 rpc_state_cache: RpcStateCacheArgs {
1205 max_blocks: 5000,
1206 max_receipts: 2000,
1207 max_headers: 1000,
1208 max_bals: 1000,
1209 max_concurrent_db_requests: 512,
1210 max_cached_tx_hashes: 30_000,
1211 },
1212 gas_price_oracle: GasPriceOracleArgs {
1213 blocks: 20,
1214 ignore_price: 2,
1215 max_price: 500_000_000_000,
1216 percentile: 60,
1217 default_suggested_fee: None,
1218 },
1219 rpc_send_raw_transaction_sync_timeout: std::time::Duration::from_secs(30),
1220 testing_skip_invalid_transactions: true,
1221 testing_gas_limit: None,
1222 rpc_force_blob_sidecar_upcasting: false,
1223 };
1224
1225 let parsed_args = CommandParser::<RpcServerArgs>::parse_from([
1226 "reth",
1227 "--http",
1228 "--http.addr",
1229 "127.0.0.1",
1230 "--http.port",
1231 "8545",
1232 "--http.api",
1233 "eth,admin",
1234 "--http.corsdomain",
1235 "*",
1236 "--ws",
1237 "--ws.addr",
1238 "127.0.0.1",
1239 "--ws.port",
1240 "8546",
1241 "--ws.origins",
1242 "*",
1243 "--ws.api",
1244 "eth,admin",
1245 "--ipcpath",
1246 "reth.ipc",
1247 "--ipc.permissions",
1248 "0o666",
1249 "--authrpc.addr",
1250 "127.0.0.1",
1251 "--authrpc.port",
1252 "8551",
1253 "--authrpc.jwtsecret",
1254 "/tmp/jwt.hex",
1255 "--auth-ipc.path",
1256 "engine.ipc",
1257 "--rpc.jwtsecret",
1258 "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
1259 "--rpc.max-request-size",
1260 "15",
1261 "--rpc.max-response-size",
1262 "160",
1263 "--rpc.max-subscriptions-per-connection",
1264 "1024",
1265 "--rpc.max-connections",
1266 "500",
1267 "--rpc.max-tracing-requests",
1268 "16",
1269 "--rpc.max-blocking-io-requests",
1270 "256",
1271 "--rpc.max-trace-filter-blocks",
1272 "4000",
1273 "--rpc.max-blocks-per-filter",
1274 "1000",
1275 "--rpc.max-logs-per-response",
1276 "10000",
1277 "--rpc.gascap",
1278 "50000000",
1279 "--rpc.evm-memory-limit",
1280 "256",
1281 "--rpc.txfeecap",
1282 "2.0",
1283 "--rpc.max-simulate-blocks",
1284 "256",
1285 "--rpc.eth-proof-window",
1286 "100000",
1287 "--rpc.proof-permits",
1288 "16",
1289 "--rpc.pending-block",
1290 "full",
1291 "--rpc.forwarder",
1292 "http://localhost:8545",
1293 "--rpc-cache.max-blocks",
1294 "5000",
1295 "--rpc-cache.max-receipts",
1296 "2000",
1297 "--rpc-cache.max-headers",
1298 "1000",
1299 "--rpc-cache.max-bals",
1300 "1000",
1301 "--rpc-cache.max-concurrent-db-requests",
1302 "512",
1303 "--gpo.blocks",
1304 "20",
1305 "--gpo.ignoreprice",
1306 "2",
1307 "--gpo.maxprice",
1308 "500000000000",
1309 "--gpo.percentile",
1310 "60",
1311 "--rpc.send-raw-transaction-sync-timeout",
1312 "30s",
1313 "--testing.skip-invalid-transactions",
1314 ])
1315 .args;
1316
1317 assert_eq!(parsed_args, args);
1318 }
1319}