reth_node_core/args/
rpc_server.rs

1//! clap [Args](clap::Args) for RPC related arguments.
2
3use 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
29/// Global static RPC server defaults
30static RPC_SERVER_DEFAULTS: OnceLock<DefaultRpcServerArgs> = OnceLock::new();
31
32/// Default max number of subscriptions per connection.
33pub(crate) const RPC_DEFAULT_MAX_SUBS_PER_CONN: u32 = 1024;
34
35/// Default max request size in MB.
36pub(crate) const RPC_DEFAULT_MAX_REQUEST_SIZE_MB: u32 = 15;
37
38/// Default max response size in MB.
39///
40/// This is only relevant for very large trace responses.
41pub(crate) const RPC_DEFAULT_MAX_RESPONSE_SIZE_MB: u32 = 160;
42
43/// Default number of incoming connections.
44///
45/// This restricts how many active connections (http, ws) the server accepts.
46/// Once exceeded, the server can reject new connections.
47pub(crate) const RPC_DEFAULT_MAX_CONNECTIONS: u32 = 500;
48
49/// Default values for RPC server that can be customized
50///
51/// Global defaults can be set via [`DefaultRpcServerArgs::try_init`].
52#[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    /// Initialize the global RPC server defaults with this configuration
100    pub fn try_init(self) -> Result<(), Self> {
101        RPC_SERVER_DEFAULTS.set(self)
102    }
103
104    /// Get a reference to the global RPC server defaults
105    pub fn get_global() -> &'static Self {
106        RPC_SERVER_DEFAULTS.get_or_init(Self::default)
107    }
108
109    /// Set the default HTTP enabled state
110    pub const fn with_http(mut self, v: bool) -> Self {
111        self.http = v;
112        self
113    }
114
115    /// Set the default HTTP address
116    pub const fn with_http_addr(mut self, v: IpAddr) -> Self {
117        self.http_addr = v;
118        self
119    }
120
121    /// Set the default HTTP port
122    pub const fn with_http_port(mut self, v: u16) -> Self {
123        self.http_port = v;
124        self
125    }
126
127    /// Set whether to disable HTTP compression by default
128    pub const fn with_http_disable_compression(mut self, v: bool) -> Self {
129        self.http_disable_compression = v;
130        self
131    }
132
133    /// Set the default HTTP API modules
134    pub fn with_http_api(mut self, v: Option<RpcModuleSelection>) -> Self {
135        self.http_api = v;
136        self
137    }
138
139    /// Set the default HTTP CORS domain
140    pub fn with_http_corsdomain(mut self, v: Option<String>) -> Self {
141        self.http_corsdomain = v;
142        self
143    }
144
145    /// Set the default WS enabled state
146    pub const fn with_ws(mut self, v: bool) -> Self {
147        self.ws = v;
148        self
149    }
150
151    /// Set the default WS address
152    pub const fn with_ws_addr(mut self, v: IpAddr) -> Self {
153        self.ws_addr = v;
154        self
155    }
156
157    /// Set the default WS port
158    pub const fn with_ws_port(mut self, v: u16) -> Self {
159        self.ws_port = v;
160        self
161    }
162
163    /// Set the default WS allowed origins
164    pub fn with_ws_allowed_origins(mut self, v: Option<String>) -> Self {
165        self.ws_allowed_origins = v;
166        self
167    }
168
169    /// Set the default WS API modules
170    pub fn with_ws_api(mut self, v: Option<RpcModuleSelection>) -> Self {
171        self.ws_api = v;
172        self
173    }
174
175    /// Set whether to disable IPC by default
176    pub const fn with_ipcdisable(mut self, v: bool) -> Self {
177        self.ipcdisable = v;
178        self
179    }
180
181    /// Set the default IPC path
182    pub fn with_ipcpath(mut self, v: String) -> Self {
183        self.ipcpath = v;
184        self
185    }
186
187    /// Set the default IPC socket permissions
188    pub fn with_ipc_socket_permissions(mut self, v: Option<String>) -> Self {
189        self.ipc_socket_permissions = v;
190        self
191    }
192
193    /// Set the default auth server address
194    pub const fn with_auth_addr(mut self, v: IpAddr) -> Self {
195        self.auth_addr = v;
196        self
197    }
198
199    /// Set the default auth server port
200    pub const fn with_auth_port(mut self, v: u16) -> Self {
201        self.auth_port = v;
202        self
203    }
204
205    /// Set the default auth JWT secret path
206    pub fn with_auth_jwtsecret(mut self, v: Option<PathBuf>) -> Self {
207        self.auth_jwtsecret = v;
208        self
209    }
210
211    /// Set the default auth IPC enabled state
212    pub const fn with_auth_ipc(mut self, v: bool) -> Self {
213        self.auth_ipc = v;
214        self
215    }
216
217    /// Set the default auth IPC path
218    pub fn with_auth_ipc_path(mut self, v: String) -> Self {
219        self.auth_ipc_path = v;
220        self
221    }
222
223    /// Set whether to disable the auth server by default
224    pub const fn with_disable_auth_server(mut self, v: bool) -> Self {
225        self.disable_auth_server = v;
226        self
227    }
228
229    /// Set the default RPC JWT secret
230    pub const fn with_rpc_jwtsecret(mut self, v: Option<JwtSecret>) -> Self {
231        self.rpc_jwtsecret = v;
232        self
233    }
234
235    /// Set the default max request size
236    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    /// Set the default max response size
242    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    /// Set the default max subscriptions per connection
248    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    /// Set the default max connections
254    pub const fn with_rpc_max_connections(mut self, v: MaxU32) -> Self {
255        self.rpc_max_connections = v;
256        self
257    }
258
259    /// Set the default max tracing requests
260    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    /// Set the default max blocking IO requests
266    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    /// Set the default max trace filter blocks
272    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    /// Set the default max blocks per filter
278    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    /// Set the default max logs per response
284    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    /// Set the default gas cap
290    pub const fn with_rpc_gas_cap(mut self, v: u64) -> Self {
291        self.rpc_gas_cap = v;
292        self
293    }
294
295    /// Set the default EVM memory limit
296    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    /// Set the default tx fee cap
302    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    /// Set the default max simulate blocks
308    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    /// Set the default eth proof window
314    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    /// Set the default proof permits
320    pub const fn with_rpc_proof_permits(mut self, v: usize) -> Self {
321        self.rpc_proof_permits = v;
322        self
323    }
324
325    /// Set the default pending block kind
326    pub const fn with_rpc_pending_block(mut self, v: PendingBlockKind) -> Self {
327        self.rpc_pending_block = v;
328        self
329    }
330
331    /// Set the default RPC forwarder
332    pub fn with_rpc_forwarder(mut self, v: Option<Url>) -> Self {
333        self.rpc_forwarder = v;
334        self
335    }
336
337    /// Set the default builder disallow addresses
338    pub fn with_builder_disallow(mut self, v: Option<HashSet<Address>>) -> Self {
339        self.builder_disallow = v;
340        self
341    }
342
343    /// Set the default RPC state cache args
344    pub const fn with_rpc_state_cache(mut self, v: RpcStateCacheArgs) -> Self {
345        self.rpc_state_cache = v;
346        self
347    }
348
349    /// Set the default gas price oracle args
350    pub const fn with_gas_price_oracle(mut self, v: GasPriceOracleArgs) -> Self {
351        self.gas_price_oracle = v;
352        self
353    }
354
355    /// Set the default send raw transaction sync timeout
356    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/// Parameters for configuring the rpc more granularity via CLI
413#[derive(Debug, Clone, Args, PartialEq, Eq)]
414#[command(next_help_heading = "RPC")]
415pub struct RpcServerArgs {
416    /// Enable the HTTP-RPC server
417    #[arg(long, default_value_if("dev", "true", "true"), default_value_t = DefaultRpcServerArgs::get_global().http)]
418    pub http: bool,
419
420    /// Http server address to listen on
421    #[arg(long = "http.addr", default_value_t = DefaultRpcServerArgs::get_global().http_addr)]
422    pub http_addr: IpAddr,
423
424    /// Http server port to listen on
425    #[arg(long = "http.port", default_value_t = DefaultRpcServerArgs::get_global().http_port)]
426    pub http_port: u16,
427
428    /// Disable compression for HTTP responses
429    #[arg(long = "http.disable-compression", default_value_t = DefaultRpcServerArgs::get_global().http_disable_compression)]
430    pub http_disable_compression: bool,
431
432    /// Rpc Modules to be configured for the HTTP server
433    #[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    /// Http Corsdomain to allow request from
437    #[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    /// Enable the WS-RPC server
441    #[arg(long, default_value_t = DefaultRpcServerArgs::get_global().ws)]
442    pub ws: bool,
443
444    /// Ws server address to listen on
445    #[arg(long = "ws.addr", default_value_t = DefaultRpcServerArgs::get_global().ws_addr)]
446    pub ws_addr: IpAddr,
447
448    /// Ws server port to listen on
449    #[arg(long = "ws.port", default_value_t = DefaultRpcServerArgs::get_global().ws_port)]
450    pub ws_port: u16,
451
452    /// Origins from which to accept `WebSocket` requests
453    #[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    /// Rpc Modules to be configured for the WS server
457    #[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    /// Disable the IPC-RPC server
461    #[arg(long, default_value_t = DefaultRpcServerArgs::get_global().ipcdisable)]
462    pub ipcdisable: bool,
463
464    /// Filename for IPC socket/pipe within the datadir
465    #[arg(long, default_value_t = DefaultRpcServerArgs::get_global().ipcpath.clone())]
466    pub ipcpath: String,
467
468    /// Set the permissions for the IPC socket file, in octal format.
469    ///
470    /// If not specified, the permissions will be set by the system's umask.
471    #[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    /// Auth server address to listen on
475    #[arg(long = "authrpc.addr", default_value_t = DefaultRpcServerArgs::get_global().auth_addr)]
476    pub auth_addr: IpAddr,
477
478    /// Auth server port to listen on
479    #[arg(long = "authrpc.port", default_value_t = DefaultRpcServerArgs::get_global().auth_port)]
480    pub auth_port: u16,
481
482    /// Path to a JWT secret to use for the authenticated engine-API RPC server.
483    ///
484    /// This will enforce JWT authentication for all requests coming from the consensus layer.
485    ///
486    /// If no path is provided, a secret will be generated and stored in the datadir under
487    /// `<DIR>/<CHAIN_ID>/jwt.hex`. For mainnet this would be `~/.reth/mainnet/jwt.hex` by default.
488    #[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    /// Enable auth engine API over IPC
492    #[arg(long, default_value_t = DefaultRpcServerArgs::get_global().auth_ipc)]
493    pub auth_ipc: bool,
494
495    /// Filename for auth IPC socket/pipe within the datadir
496    #[arg(long = "auth-ipc.path", default_value_t = DefaultRpcServerArgs::get_global().auth_ipc_path.clone())]
497    pub auth_ipc_path: String,
498
499    /// Disable the auth/engine API server.
500    ///
501    /// This will prevent the authenticated engine-API server from starting. Use this if you're
502    /// running a node that doesn't need to serve engine API requests.
503    #[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    /// Hex encoded JWT secret to authenticate the regular RPC server(s), see `--http.api` and
507    /// `--ws.api`.
508    ///
509    /// This is __not__ used for the authenticated engine-API RPC server, see
510    /// `--authrpc.jwtsecret`.
511    #[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    /// Set the maximum RPC request payload size for both HTTP and WS in megabytes.
515    #[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    /// Set the maximum RPC response payload size for both HTTP and WS in megabytes.
519    #[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    /// Set the maximum concurrent subscriptions per connection.
523    #[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    /// Maximum number of RPC server connections.
527    #[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    /// Maximum number of concurrent tracing requests.
531    ///
532    /// By default this chooses a sensible value based on the number of available cores.
533    /// Tracing requests are generally CPU bound.
534    /// Choosing a value that is higher than the available CPU cores can have a negative impact on
535    /// the performance of the node and affect the node's ability to maintain sync.
536    #[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    /// Maximum number of concurrent blocking IO requests.
540    ///
541    /// Blocking IO requests include `eth_call`, `eth_estimateGas`, and similar methods that
542    /// require EVM execution. These are spawned as blocking tasks to avoid blocking the async
543    /// runtime.
544    #[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    /// Maximum number of blocks for `trace_filter` requests.
548    #[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    /// Maximum number of blocks that could be scanned per filter request. (0 = entire chain)
552    #[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    /// Maximum number of logs that can be returned in a single response. (0 = no limit)
556    #[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    /// Maximum gas limit for `eth_call` and call tracing RPC methods.
560    #[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    /// Maximum memory the EVM can allocate per RPC request.
570    #[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    /// Maximum eth transaction fee (in ether) that can be sent via the RPC APIs (0 = no cap)
580    #[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    /// Maximum number of blocks for `eth_simulateV1` call.
590    #[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    /// The maximum proof window for historical proof generation.
598    /// This value allows for generating historical proofs up to
599    /// configured number of blocks from current tip (up to `tip - window`).
600    #[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    /// Maximum number of concurrent getproof requests.
608    #[arg(long = "rpc.proof-permits", alias = "rpc-proof-permits", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_proof_permits)]
609    pub rpc_proof_permits: usize,
610
611    /// Configures the pending block behavior for RPC responses.
612    ///
613    /// Options: full (include all transactions), empty (header only), none (disable pending
614    /// blocks).
615    #[arg(long = "rpc.pending-block", default_value = "full", value_name = "KIND")]
616    pub rpc_pending_block: PendingBlockKind,
617
618    /// Endpoint to forward transactions to.
619    #[arg(long = "rpc.forwarder", alias = "rpc-forwarder", value_name = "FORWARDER")]
620    pub rpc_forwarder: Option<Url>,
621
622    /// Path to file containing disallowed addresses, json-encoded list of strings. Block
623    /// validation API will reject blocks containing transactions from these addresses.
624    #[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    /// State cache configuration.
628    #[command(flatten)]
629    pub rpc_state_cache: RpcStateCacheArgs,
630
631    /// Gas price oracle configuration.
632    #[command(flatten)]
633    pub gas_price_oracle: GasPriceOracleArgs,
634
635    /// Timeout for `send_raw_transaction_sync` RPC method.
636    #[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    /// Skip invalid transactions in `testing_buildBlockV1` instead of failing.
645    ///
646    /// When enabled, transactions that fail execution will be skipped, and all subsequent
647    /// transactions from the same sender will also be skipped.
648    #[arg(long = "testing.skip-invalid-transactions", default_value_t = false)]
649    pub testing_skip_invalid_transactions: bool,
650}
651
652impl RpcServerArgs {
653    /// Enables the HTTP-RPC server.
654    pub const fn with_http(mut self) -> Self {
655        self.http = true;
656        self
657    }
658
659    /// Configures modules for the HTTP-RPC server.
660    pub fn with_http_api(mut self, http_api: RpcModuleSelection) -> Self {
661        self.http_api = Some(http_api);
662        self
663    }
664
665    /// Enables the WS-RPC server.
666    pub const fn with_ws(mut self) -> Self {
667        self.ws = true;
668        self
669    }
670
671    /// Configures modules for WS-RPC server.
672    pub fn with_ws_api(mut self, ws_api: RpcModuleSelection) -> Self {
673        self.ws_api = Some(ws_api);
674        self
675    }
676
677    /// Enables the Auth IPC
678    pub const fn with_auth_ipc(mut self) -> Self {
679        self.auth_ipc = true;
680        self
681    }
682
683    /// Configures modules for both the HTTP-RPC server and WS-RPC server.
684    ///
685    /// This is the same as calling both [`Self::with_http_api`] and [`Self::with_ws_api`].
686    pub fn with_api(self, api: RpcModuleSelection) -> Self {
687        self.with_http_api(api.clone()).with_ws_api(api)
688    }
689
690    /// Change rpc port numbers based on the instance number, if provided.
691    /// * The `auth_port` is scaled by a factor of `instance * 100`
692    /// * The `http_port` is scaled by a factor of `-instance`
693    /// * The `ws_port` is scaled by a factor of `instance * 2`
694    /// * The `ipcpath` is appended with the instance number: `/tmp/reth.ipc-<instance>`
695    ///
696    /// # Panics
697    /// Warning: if `instance` is zero in debug mode, this will panic.
698    ///
699    /// This will also panic in debug mode if either:
700    /// * `instance` is greater than `655` (scaling would overflow `u16`)
701    /// * `self.auth_port / 100 + (instance - 1)` would overflow `u16`
702    ///
703    /// In release mode, this will silently wrap around.
704    pub fn adjust_instance_ports(&mut self, instance: Option<u16>) {
705        if let Some(instance) = instance {
706            debug_assert_ne!(instance, 0, "instance must be non-zero");
707            // auth port is scaled by a factor of instance * 100
708            self.auth_port += instance * 100 - 100;
709            // http port is scaled by a factor of -instance
710            self.http_port -= instance - 1;
711            // ws port is scaled by a factor of instance * 2
712            self.ws_port += instance * 2 - 2;
713            // append instance file to ipc path
714            self.ipcpath = format!("{}-{}", self.ipcpath, instance);
715        }
716    }
717
718    /// Set the http port to zero, to allow the OS to assign a random unused port when the rpc
719    /// server binds to a socket.
720    pub const fn with_http_unused_port(mut self) -> Self {
721        self.http_port = 0;
722        self
723    }
724
725    /// Set the ws port to zero, to allow the OS to assign a random unused port when the rpc
726    /// server binds to a socket.
727    pub const fn with_ws_unused_port(mut self) -> Self {
728        self.ws_port = 0;
729        self
730    }
731
732    /// Set the auth port to zero, to allow the OS to assign a random unused port when the rpc
733    /// server binds to a socket.
734    pub const fn with_auth_unused_port(mut self) -> Self {
735        self.auth_port = 0;
736        self
737    }
738
739    /// Append a random string to the ipc path, to prevent possible collisions when multiple nodes
740    /// are being run on the same machine.
741    pub fn with_ipc_random_path(mut self) -> Self {
742        let random_string: String =
743            rand::rng().sample_iter(rand::distr::Alphanumeric).take(8).map(char::from).collect();
744        self.ipcpath = format!("{}-{}", self.ipcpath, random_string);
745        self
746    }
747
748    /// Configure all ports to be set to a random unused port when bound, and set the IPC path to a
749    /// random path.
750    pub fn with_unused_ports(mut self) -> Self {
751        self = self.with_http_unused_port();
752        self = self.with_ws_unused_port();
753        self = self.with_auth_unused_port();
754        self = self.with_ipc_random_path();
755        self
756    }
757
758    /// Apply a function to the args.
759    pub fn apply<F>(self, f: F) -> Self
760    where
761        F: FnOnce(Self) -> Self,
762    {
763        f(self)
764    }
765
766    /// Configures the timeout for send raw transaction sync.
767    pub const fn with_send_raw_transaction_sync_timeout(mut self, timeout: Duration) -> Self {
768        self.rpc_send_raw_transaction_sync_timeout = timeout;
769        self
770    }
771}
772
773impl Default for RpcServerArgs {
774    fn default() -> Self {
775        let DefaultRpcServerArgs {
776            http,
777            http_addr,
778            http_port,
779            http_disable_compression,
780            http_api,
781            http_corsdomain,
782            ws,
783            ws_addr,
784            ws_port,
785            ws_allowed_origins,
786            ws_api,
787            ipcdisable,
788            ipcpath,
789            ipc_socket_permissions,
790            auth_addr,
791            auth_port,
792            auth_jwtsecret,
793            auth_ipc,
794            auth_ipc_path,
795            disable_auth_server,
796            rpc_jwtsecret,
797            rpc_max_request_size,
798            rpc_max_response_size,
799            rpc_max_subscriptions_per_connection,
800            rpc_max_connections,
801            rpc_max_tracing_requests,
802            rpc_max_blocking_io_requests,
803            rpc_max_trace_filter_blocks,
804            rpc_max_blocks_per_filter,
805            rpc_max_logs_per_response,
806            rpc_gas_cap,
807            rpc_evm_memory_limit,
808            rpc_tx_fee_cap,
809            rpc_max_simulate_blocks,
810            rpc_eth_proof_window,
811            rpc_proof_permits,
812            rpc_pending_block,
813            rpc_forwarder,
814            builder_disallow,
815            rpc_state_cache,
816            gas_price_oracle,
817            rpc_send_raw_transaction_sync_timeout,
818        } = DefaultRpcServerArgs::get_global().clone();
819        Self {
820            http,
821            http_addr,
822            http_port,
823            http_disable_compression,
824            http_api,
825            http_corsdomain,
826            ws,
827            ws_addr,
828            ws_port,
829            ws_allowed_origins,
830            ws_api,
831            ipcdisable,
832            ipcpath,
833            ipc_socket_permissions,
834            auth_addr,
835            auth_port,
836            auth_jwtsecret,
837            auth_ipc,
838            auth_ipc_path,
839            disable_auth_server,
840            rpc_jwtsecret,
841            rpc_max_request_size,
842            rpc_max_response_size,
843            rpc_max_subscriptions_per_connection,
844            rpc_max_connections,
845            rpc_max_tracing_requests,
846            rpc_max_blocking_io_requests,
847            rpc_max_trace_filter_blocks,
848            rpc_max_blocks_per_filter,
849            rpc_max_logs_per_response,
850            rpc_gas_cap,
851            rpc_evm_memory_limit,
852            rpc_tx_fee_cap,
853            rpc_max_simulate_blocks,
854            rpc_eth_proof_window,
855            rpc_proof_permits,
856            rpc_pending_block,
857            rpc_forwarder,
858            builder_disallow,
859            rpc_state_cache,
860            gas_price_oracle,
861            rpc_send_raw_transaction_sync_timeout,
862            testing_skip_invalid_transactions: false,
863        }
864    }
865}
866
867/// clap value parser for [`RpcModuleSelection`] with configurable validation.
868#[derive(Clone, Debug, Default)]
869#[non_exhaustive]
870struct RpcModuleSelectionValueParser;
871
872impl TypedValueParser for RpcModuleSelectionValueParser {
873    type Value = RpcModuleSelection;
874
875    fn parse_ref(
876        &self,
877        _cmd: &Command,
878        _arg: Option<&Arg>,
879        value: &OsStr,
880    ) -> Result<Self::Value, clap::Error> {
881        let val =
882            value.to_str().ok_or_else(|| clap::Error::new(clap::error::ErrorKind::InvalidUtf8))?;
883        // This will now accept any module name, creating Other(name) for unknowns
884        Ok(val
885            .parse::<RpcModuleSelection>()
886            .expect("RpcModuleSelection parsing cannot fail with Other variant"))
887    }
888
889    fn possible_values(&self) -> Option<Box<dyn Iterator<Item = PossibleValue> + '_>> {
890        // Only show standard modules in help text (excludes "other")
891        let values = RethRpcModule::standard_variant_names().map(PossibleValue::new);
892        Some(Box::new(values))
893    }
894}
895
896#[cfg(test)]
897mod tests {
898    use super::*;
899    use clap::{Args, Parser};
900
901    /// A helper type to parse Args more easily
902    #[derive(Parser)]
903    struct CommandParser<T: Args> {
904        #[command(flatten)]
905        args: T,
906    }
907
908    #[test]
909    fn test_rpc_server_args_parser() {
910        let args =
911            CommandParser::<RpcServerArgs>::parse_from(["reth", "--http.api", "eth,admin,debug"])
912                .args;
913
914        let apis = args.http_api.unwrap();
915        let expected = RpcModuleSelection::try_from_selection(["eth", "admin", "debug"]).unwrap();
916
917        assert_eq!(apis, expected);
918    }
919
920    #[test]
921    fn test_rpc_server_eth_call_bundle_args() {
922        let args =
923            CommandParser::<RpcServerArgs>::parse_from(["reth", "--http.api", "eth,admin,debug"])
924                .args;
925
926        let apis = args.http_api.unwrap();
927        let expected = RpcModuleSelection::try_from_selection(["eth", "admin", "debug"]).unwrap();
928
929        assert_eq!(apis, expected);
930    }
931
932    #[test]
933    fn test_rpc_server_args_parser_none() {
934        let args = CommandParser::<RpcServerArgs>::parse_from(["reth", "--http.api", "none"]).args;
935        let apis = args.http_api.unwrap();
936        let expected = RpcModuleSelection::Selection(Default::default());
937        assert_eq!(apis, expected);
938    }
939
940    #[test]
941    fn rpc_server_args_default_sanity_test() {
942        let default_args = RpcServerArgs::default();
943        let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
944
945        assert_eq!(args, default_args);
946    }
947
948    #[test]
949    fn test_rpc_tx_fee_cap_parse_integer() {
950        let args = CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.txfeecap", "2"]).args;
951        let expected = 2_000_000_000_000_000_000u128; // 2 ETH in wei
952        assert_eq!(args.rpc_tx_fee_cap, expected);
953    }
954
955    #[test]
956    fn test_rpc_tx_fee_cap_parse_decimal() {
957        let args =
958            CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.txfeecap", "1.5"]).args;
959        let expected = 1_500_000_000_000_000_000u128; // 1.5 ETH in wei
960        assert_eq!(args.rpc_tx_fee_cap, expected);
961    }
962
963    #[test]
964    fn test_rpc_tx_fee_cap_parse_zero() {
965        let args = CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.txfeecap", "0"]).args;
966        assert_eq!(args.rpc_tx_fee_cap, 0); // 0 = no cap
967    }
968
969    #[test]
970    fn test_rpc_tx_fee_cap_parse_none() {
971        let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
972        let expected = 1_000_000_000_000_000_000u128;
973        assert_eq!(args.rpc_tx_fee_cap, expected); // 1 ETH default cap
974    }
975
976    #[test]
977    fn test_rpc_server_args() {
978        let args = RpcServerArgs {
979            http: true,
980            http_addr: "127.0.0.1".parse().unwrap(),
981            http_port: 8545,
982            http_disable_compression: false,
983            http_api: Some(RpcModuleSelection::try_from_selection(["eth", "admin"]).unwrap()),
984            http_corsdomain: Some("*".to_string()),
985            ws: true,
986            ws_addr: "127.0.0.1".parse().unwrap(),
987            ws_port: 8546,
988            ws_allowed_origins: Some("*".to_string()),
989            ws_api: Some(RpcModuleSelection::try_from_selection(["eth", "admin"]).unwrap()),
990            ipcdisable: false,
991            ipcpath: "reth.ipc".to_string(),
992            ipc_socket_permissions: Some("0o666".to_string()),
993            auth_addr: "127.0.0.1".parse().unwrap(),
994            auth_port: 8551,
995            auth_jwtsecret: Some(std::path::PathBuf::from("/tmp/jwt.hex")),
996            auth_ipc: false,
997            auth_ipc_path: "engine.ipc".to_string(),
998            disable_auth_server: false,
999            rpc_jwtsecret: Some(
1000                JwtSecret::from_hex(
1001                    "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
1002                )
1003                .unwrap(),
1004            ),
1005            rpc_max_request_size: 15u32.into(),
1006            rpc_max_response_size: 160u32.into(),
1007            rpc_max_subscriptions_per_connection: 1024u32.into(),
1008            rpc_max_connections: 500u32.into(),
1009            rpc_max_tracing_requests: 16,
1010            rpc_max_blocking_io_requests: 256,
1011            rpc_max_trace_filter_blocks: 4000,
1012            rpc_max_blocks_per_filter: 1000u64.into(),
1013            rpc_max_logs_per_response: 10000u64.into(),
1014            rpc_gas_cap: 50_000_000,
1015            rpc_evm_memory_limit: 256,
1016            rpc_tx_fee_cap: 2_000_000_000_000_000_000u128,
1017            rpc_max_simulate_blocks: 256,
1018            rpc_eth_proof_window: 100_000,
1019            rpc_proof_permits: 16,
1020            rpc_pending_block: PendingBlockKind::Full,
1021            rpc_forwarder: Some("http://localhost:8545".parse().unwrap()),
1022            builder_disallow: None,
1023            rpc_state_cache: RpcStateCacheArgs {
1024                max_blocks: 5000,
1025                max_receipts: 2000,
1026                max_headers: 1000,
1027                max_concurrent_db_requests: 512,
1028            },
1029            gas_price_oracle: GasPriceOracleArgs {
1030                blocks: 20,
1031                ignore_price: 2,
1032                max_price: 500_000_000_000,
1033                percentile: 60,
1034                default_suggested_fee: None,
1035            },
1036            rpc_send_raw_transaction_sync_timeout: std::time::Duration::from_secs(30),
1037            testing_skip_invalid_transactions: true,
1038        };
1039
1040        let parsed_args = CommandParser::<RpcServerArgs>::parse_from([
1041            "reth",
1042            "--http",
1043            "--http.addr",
1044            "127.0.0.1",
1045            "--http.port",
1046            "8545",
1047            "--http.api",
1048            "eth,admin",
1049            "--http.corsdomain",
1050            "*",
1051            "--ws",
1052            "--ws.addr",
1053            "127.0.0.1",
1054            "--ws.port",
1055            "8546",
1056            "--ws.origins",
1057            "*",
1058            "--ws.api",
1059            "eth,admin",
1060            "--ipcpath",
1061            "reth.ipc",
1062            "--ipc.permissions",
1063            "0o666",
1064            "--authrpc.addr",
1065            "127.0.0.1",
1066            "--authrpc.port",
1067            "8551",
1068            "--authrpc.jwtsecret",
1069            "/tmp/jwt.hex",
1070            "--auth-ipc.path",
1071            "engine.ipc",
1072            "--rpc.jwtsecret",
1073            "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
1074            "--rpc.max-request-size",
1075            "15",
1076            "--rpc.max-response-size",
1077            "160",
1078            "--rpc.max-subscriptions-per-connection",
1079            "1024",
1080            "--rpc.max-connections",
1081            "500",
1082            "--rpc.max-tracing-requests",
1083            "16",
1084            "--rpc.max-blocking-io-requests",
1085            "256",
1086            "--rpc.max-trace-filter-blocks",
1087            "4000",
1088            "--rpc.max-blocks-per-filter",
1089            "1000",
1090            "--rpc.max-logs-per-response",
1091            "10000",
1092            "--rpc.gascap",
1093            "50000000",
1094            "--rpc.evm-memory-limit",
1095            "256",
1096            "--rpc.txfeecap",
1097            "2.0",
1098            "--rpc.max-simulate-blocks",
1099            "256",
1100            "--rpc.eth-proof-window",
1101            "100000",
1102            "--rpc.proof-permits",
1103            "16",
1104            "--rpc.pending-block",
1105            "full",
1106            "--rpc.forwarder",
1107            "http://localhost:8545",
1108            "--rpc-cache.max-blocks",
1109            "5000",
1110            "--rpc-cache.max-receipts",
1111            "2000",
1112            "--rpc-cache.max-headers",
1113            "1000",
1114            "--rpc-cache.max-concurrent-db-requests",
1115            "512",
1116            "--gpo.blocks",
1117            "20",
1118            "--gpo.ignoreprice",
1119            "2",
1120            "--gpo.maxprice",
1121            "500000000000",
1122            "--gpo.percentile",
1123            "60",
1124            "--rpc.send-raw-transaction-sync-timeout",
1125            "30s",
1126            "--testing.skip-invalid-transactions",
1127        ])
1128        .args;
1129
1130        assert_eq!(parsed_args, args);
1131    }
1132}