Skip to main content

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::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
28/// Global static RPC server defaults
29static RPC_SERVER_DEFAULTS: OnceLock<DefaultRpcServerArgs> = OnceLock::new();
30
31/// Default max number of subscriptions per connection.
32pub(crate) const RPC_DEFAULT_MAX_SUBS_PER_CONN: u32 = 1024;
33
34/// Default max request size in MB.
35pub(crate) const RPC_DEFAULT_MAX_REQUEST_SIZE_MB: u32 = 15;
36
37/// Default max response size in MB.
38///
39/// This is only relevant for very large trace responses.
40pub(crate) const RPC_DEFAULT_MAX_RESPONSE_SIZE_MB: u32 = 160;
41
42/// Default number of incoming connections.
43///
44/// This restricts how many active connections (http, ws) the server accepts.
45/// Once exceeded, the server can reject new connections.
46pub(crate) const RPC_DEFAULT_MAX_CONNECTIONS: u32 = 500;
47
48/// Default values for RPC server that can be customized
49///
50/// Global defaults can be set via [`DefaultRpcServerArgs::try_init`].
51#[derive(Debug, Clone)]
52pub struct DefaultRpcServerArgs {
53    http: bool,
54    http_addr: IpAddr,
55    http_port: u16,
56    http_disable_compression: bool,
57    http_api: Option<RpcModuleSelection>,
58    http_corsdomain: Option<String>,
59    ws: bool,
60    ws_addr: IpAddr,
61    ws_port: u16,
62    ws_allowed_origins: Option<String>,
63    ws_api: Option<RpcModuleSelection>,
64    ipcdisable: bool,
65    ipcpath: String,
66    ipc_socket_permissions: Option<String>,
67    auth_addr: IpAddr,
68    auth_port: u16,
69    auth_jwtsecret: Option<PathBuf>,
70    auth_ipc: bool,
71    auth_ipc_path: String,
72    disable_auth_server: bool,
73    rpc_jwtsecret: Option<JwtSecret>,
74    rpc_max_request_size: MaxU32,
75    rpc_max_response_size: MaxU32,
76    rpc_max_subscriptions_per_connection: MaxU32,
77    rpc_max_connections: MaxU32,
78    rpc_max_tracing_requests: usize,
79    rpc_max_blocking_io_requests: usize,
80    rpc_max_trace_filter_blocks: u64,
81    rpc_max_blocks_per_filter: ZeroAsNoneU64,
82    rpc_max_logs_per_response: ZeroAsNoneU64,
83    rpc_gas_cap: u64,
84    rpc_evm_memory_limit: u64,
85    rpc_tx_fee_cap: u128,
86    rpc_max_simulate_blocks: u64,
87    rpc_eth_proof_window: u64,
88    rpc_proof_permits: usize,
89    rpc_pending_block: PendingBlockKind,
90    rpc_forwarder: Option<Url>,
91    builder_disallow: Option<AddressSet>,
92    rpc_state_cache: RpcStateCacheArgs,
93    gas_price_oracle: GasPriceOracleArgs,
94    rpc_send_raw_transaction_sync_timeout: Duration,
95}
96
97impl DefaultRpcServerArgs {
98    /// Initialize the global RPC server defaults with this configuration
99    pub fn try_init(self) -> Result<(), Self> {
100        RPC_SERVER_DEFAULTS.set(self)
101    }
102
103    /// Get a reference to the global RPC server defaults
104    pub fn get_global() -> &'static Self {
105        RPC_SERVER_DEFAULTS.get_or_init(Self::default)
106    }
107
108    /// Set the default HTTP enabled state
109    pub const fn with_http(mut self, v: bool) -> Self {
110        self.http = v;
111        self
112    }
113
114    /// Set the default HTTP address
115    pub const fn with_http_addr(mut self, v: IpAddr) -> Self {
116        self.http_addr = v;
117        self
118    }
119
120    /// Set the default HTTP port
121    pub const fn with_http_port(mut self, v: u16) -> Self {
122        self.http_port = v;
123        self
124    }
125
126    /// Set whether to disable HTTP compression by default
127    pub const fn with_http_disable_compression(mut self, v: bool) -> Self {
128        self.http_disable_compression = v;
129        self
130    }
131
132    /// Set the default HTTP API modules
133    pub fn with_http_api(mut self, v: Option<RpcModuleSelection>) -> Self {
134        self.http_api = v;
135        self
136    }
137
138    /// Set the default HTTP CORS domain
139    pub fn with_http_corsdomain(mut self, v: Option<String>) -> Self {
140        self.http_corsdomain = v;
141        self
142    }
143
144    /// Set the default WS enabled state
145    pub const fn with_ws(mut self, v: bool) -> Self {
146        self.ws = v;
147        self
148    }
149
150    /// Set the default WS address
151    pub const fn with_ws_addr(mut self, v: IpAddr) -> Self {
152        self.ws_addr = v;
153        self
154    }
155
156    /// Set the default WS port
157    pub const fn with_ws_port(mut self, v: u16) -> Self {
158        self.ws_port = v;
159        self
160    }
161
162    /// Set the default WS allowed origins
163    pub fn with_ws_allowed_origins(mut self, v: Option<String>) -> Self {
164        self.ws_allowed_origins = v;
165        self
166    }
167
168    /// Set the default WS API modules
169    pub fn with_ws_api(mut self, v: Option<RpcModuleSelection>) -> Self {
170        self.ws_api = v;
171        self
172    }
173
174    /// Set whether to disable IPC by default
175    pub const fn with_ipcdisable(mut self, v: bool) -> Self {
176        self.ipcdisable = v;
177        self
178    }
179
180    /// Set the default IPC path
181    pub fn with_ipcpath(mut self, v: String) -> Self {
182        self.ipcpath = v;
183        self
184    }
185
186    /// Set the default IPC socket permissions
187    pub fn with_ipc_socket_permissions(mut self, v: Option<String>) -> Self {
188        self.ipc_socket_permissions = v;
189        self
190    }
191
192    /// Set the default auth server address
193    pub const fn with_auth_addr(mut self, v: IpAddr) -> Self {
194        self.auth_addr = v;
195        self
196    }
197
198    /// Set the default auth server port
199    pub const fn with_auth_port(mut self, v: u16) -> Self {
200        self.auth_port = v;
201        self
202    }
203
204    /// Set the default auth JWT secret path
205    pub fn with_auth_jwtsecret(mut self, v: Option<PathBuf>) -> Self {
206        self.auth_jwtsecret = v;
207        self
208    }
209
210    /// Set the default auth IPC enabled state
211    pub const fn with_auth_ipc(mut self, v: bool) -> Self {
212        self.auth_ipc = v;
213        self
214    }
215
216    /// Set the default auth IPC path
217    pub fn with_auth_ipc_path(mut self, v: String) -> Self {
218        self.auth_ipc_path = v;
219        self
220    }
221
222    /// Set whether to disable the auth server by default
223    pub const fn with_disable_auth_server(mut self, v: bool) -> Self {
224        self.disable_auth_server = v;
225        self
226    }
227
228    /// Set the default RPC JWT secret
229    pub const fn with_rpc_jwtsecret(mut self, v: Option<JwtSecret>) -> Self {
230        self.rpc_jwtsecret = v;
231        self
232    }
233
234    /// Set the default max request size
235    pub const fn with_rpc_max_request_size(mut self, v: MaxU32) -> Self {
236        self.rpc_max_request_size = v;
237        self
238    }
239
240    /// Set the default max response size
241    pub const fn with_rpc_max_response_size(mut self, v: MaxU32) -> Self {
242        self.rpc_max_response_size = v;
243        self
244    }
245
246    /// Set the default max subscriptions per connection
247    pub const fn with_rpc_max_subscriptions_per_connection(mut self, v: MaxU32) -> Self {
248        self.rpc_max_subscriptions_per_connection = v;
249        self
250    }
251
252    /// Set the default max connections
253    pub const fn with_rpc_max_connections(mut self, v: MaxU32) -> Self {
254        self.rpc_max_connections = v;
255        self
256    }
257
258    /// Set the default max tracing requests
259    pub const fn with_rpc_max_tracing_requests(mut self, v: usize) -> Self {
260        self.rpc_max_tracing_requests = v;
261        self
262    }
263
264    /// Set the default max blocking IO requests
265    pub const fn with_rpc_max_blocking_io_requests(mut self, v: usize) -> Self {
266        self.rpc_max_blocking_io_requests = v;
267        self
268    }
269
270    /// Set the default max trace filter blocks
271    pub const fn with_rpc_max_trace_filter_blocks(mut self, v: u64) -> Self {
272        self.rpc_max_trace_filter_blocks = v;
273        self
274    }
275
276    /// Set the default max blocks per filter
277    pub const fn with_rpc_max_blocks_per_filter(mut self, v: ZeroAsNoneU64) -> Self {
278        self.rpc_max_blocks_per_filter = v;
279        self
280    }
281
282    /// Set the default max logs per response
283    pub const fn with_rpc_max_logs_per_response(mut self, v: ZeroAsNoneU64) -> Self {
284        self.rpc_max_logs_per_response = v;
285        self
286    }
287
288    /// Set the default gas cap
289    pub const fn with_rpc_gas_cap(mut self, v: u64) -> Self {
290        self.rpc_gas_cap = v;
291        self
292    }
293
294    /// Set the default EVM memory limit
295    pub const fn with_rpc_evm_memory_limit(mut self, v: u64) -> Self {
296        self.rpc_evm_memory_limit = v;
297        self
298    }
299
300    /// Set the default tx fee cap
301    pub const fn with_rpc_tx_fee_cap(mut self, v: u128) -> Self {
302        self.rpc_tx_fee_cap = v;
303        self
304    }
305
306    /// Set the default max simulate blocks
307    pub const fn with_rpc_max_simulate_blocks(mut self, v: u64) -> Self {
308        self.rpc_max_simulate_blocks = v;
309        self
310    }
311
312    /// Set the default eth proof window
313    pub const fn with_rpc_eth_proof_window(mut self, v: u64) -> Self {
314        self.rpc_eth_proof_window = v;
315        self
316    }
317
318    /// Set the default proof permits
319    pub const fn with_rpc_proof_permits(mut self, v: usize) -> Self {
320        self.rpc_proof_permits = v;
321        self
322    }
323
324    /// Set the default pending block kind
325    pub const fn with_rpc_pending_block(mut self, v: PendingBlockKind) -> Self {
326        self.rpc_pending_block = v;
327        self
328    }
329
330    /// Set the default RPC forwarder
331    pub fn with_rpc_forwarder(mut self, v: Option<Url>) -> Self {
332        self.rpc_forwarder = v;
333        self
334    }
335
336    /// Set the default builder disallow addresses
337    pub fn with_builder_disallow(mut self, v: Option<AddressSet>) -> Self {
338        self.builder_disallow = v;
339        self
340    }
341
342    /// Set the default RPC state cache args
343    pub const fn with_rpc_state_cache(mut self, v: RpcStateCacheArgs) -> Self {
344        self.rpc_state_cache = v;
345        self
346    }
347
348    /// Set the default gas price oracle args
349    pub const fn with_gas_price_oracle(mut self, v: GasPriceOracleArgs) -> Self {
350        self.gas_price_oracle = v;
351        self
352    }
353
354    /// Set the default send raw transaction sync timeout
355    pub const fn with_rpc_send_raw_transaction_sync_timeout(mut self, v: Duration) -> Self {
356        self.rpc_send_raw_transaction_sync_timeout = v;
357        self
358    }
359}
360
361impl Default for DefaultRpcServerArgs {
362    fn default() -> Self {
363        Self {
364            http: false,
365            http_addr: Ipv4Addr::LOCALHOST.into(),
366            http_port: constants::DEFAULT_HTTP_RPC_PORT,
367            http_disable_compression: false,
368            http_api: None,
369            http_corsdomain: None,
370            ws: false,
371            ws_addr: Ipv4Addr::LOCALHOST.into(),
372            ws_port: constants::DEFAULT_WS_RPC_PORT,
373            ws_allowed_origins: None,
374            ws_api: None,
375            ipcdisable: false,
376            ipcpath: constants::DEFAULT_IPC_ENDPOINT.to_string(),
377            ipc_socket_permissions: None,
378            auth_addr: Ipv4Addr::LOCALHOST.into(),
379            auth_port: constants::DEFAULT_AUTH_PORT,
380            auth_jwtsecret: None,
381            auth_ipc: false,
382            auth_ipc_path: constants::DEFAULT_ENGINE_API_IPC_ENDPOINT.to_string(),
383            disable_auth_server: false,
384            rpc_jwtsecret: None,
385            rpc_max_request_size: RPC_DEFAULT_MAX_REQUEST_SIZE_MB.into(),
386            rpc_max_response_size: RPC_DEFAULT_MAX_RESPONSE_SIZE_MB.into(),
387            rpc_max_subscriptions_per_connection: RPC_DEFAULT_MAX_SUBS_PER_CONN.into(),
388            rpc_max_connections: RPC_DEFAULT_MAX_CONNECTIONS.into(),
389            rpc_max_tracing_requests: constants::default_max_tracing_requests(),
390            rpc_max_blocking_io_requests: constants::DEFAULT_MAX_BLOCKING_IO_REQUEST,
391            rpc_max_trace_filter_blocks: constants::DEFAULT_MAX_TRACE_FILTER_BLOCKS,
392            rpc_max_blocks_per_filter: constants::DEFAULT_MAX_BLOCKS_PER_FILTER.into(),
393            rpc_max_logs_per_response: (constants::DEFAULT_MAX_LOGS_PER_RESPONSE as u64).into(),
394            rpc_gas_cap: constants::gas_oracle::RPC_DEFAULT_GAS_CAP,
395            rpc_evm_memory_limit: (1 << 32) - 1,
396            rpc_tx_fee_cap: constants::DEFAULT_TX_FEE_CAP_WEI,
397            rpc_max_simulate_blocks: constants::DEFAULT_MAX_SIMULATE_BLOCKS,
398            rpc_eth_proof_window: constants::DEFAULT_ETH_PROOF_WINDOW,
399            rpc_proof_permits: constants::DEFAULT_PROOF_PERMITS,
400            rpc_pending_block: PendingBlockKind::Full,
401            rpc_forwarder: None,
402            builder_disallow: None,
403            rpc_state_cache: RpcStateCacheArgs::default(),
404            gas_price_oracle: GasPriceOracleArgs::default(),
405            rpc_send_raw_transaction_sync_timeout:
406                constants::RPC_DEFAULT_SEND_RAW_TX_SYNC_TIMEOUT_SECS,
407        }
408    }
409}
410
411/// Parameters for configuring the rpc more granularity via CLI
412#[derive(Debug, Clone, Args, PartialEq, Eq)]
413#[command(next_help_heading = "RPC")]
414pub struct RpcServerArgs {
415    /// Enable the HTTP-RPC server
416    #[arg(long, default_value_if("dev", "true", "true"), default_value_t = DefaultRpcServerArgs::get_global().http)]
417    pub http: bool,
418
419    /// Http server address to listen on
420    #[arg(long = "http.addr", default_value_t = DefaultRpcServerArgs::get_global().http_addr)]
421    pub http_addr: IpAddr,
422
423    /// Http server port to listen on
424    #[arg(long = "http.port", default_value_t = DefaultRpcServerArgs::get_global().http_port)]
425    pub http_port: u16,
426
427    /// Disable compression for HTTP responses
428    #[arg(long = "http.disable-compression", default_value_t = DefaultRpcServerArgs::get_global().http_disable_compression)]
429    pub http_disable_compression: bool,
430
431    /// Rpc Modules to be configured for the HTTP server
432    #[arg(long = "http.api", value_parser = RpcModuleSelectionValueParser::default(), default_value = Resettable::from(DefaultRpcServerArgs::get_global().http_api.as_ref().map(|v| v.to_string().into())))]
433    pub http_api: Option<RpcModuleSelection>,
434
435    /// Http Corsdomain to allow request from
436    #[arg(long = "http.corsdomain", default_value = Resettable::from(DefaultRpcServerArgs::get_global().http_corsdomain.as_ref().map(|v| v.to_string().into())))]
437    pub http_corsdomain: Option<String>,
438
439    /// Enable the WS-RPC server
440    #[arg(long, default_value_t = DefaultRpcServerArgs::get_global().ws)]
441    pub ws: bool,
442
443    /// Ws server address to listen on
444    #[arg(long = "ws.addr", default_value_t = DefaultRpcServerArgs::get_global().ws_addr)]
445    pub ws_addr: IpAddr,
446
447    /// Ws server port to listen on
448    #[arg(long = "ws.port", default_value_t = DefaultRpcServerArgs::get_global().ws_port)]
449    pub ws_port: u16,
450
451    /// Origins from which to accept `WebSocket` requests
452    #[arg(id = "ws.origins", long = "ws.origins", alias = "ws.corsdomain", default_value = Resettable::from(DefaultRpcServerArgs::get_global().ws_allowed_origins.as_ref().map(|v| v.to_string().into())))]
453    pub ws_allowed_origins: Option<String>,
454
455    /// Rpc Modules to be configured for the WS server
456    #[arg(long = "ws.api", value_parser = RpcModuleSelectionValueParser::default(), default_value = Resettable::from(DefaultRpcServerArgs::get_global().ws_api.as_ref().map(|v| v.to_string().into())))]
457    pub ws_api: Option<RpcModuleSelection>,
458
459    /// Disable the IPC-RPC server
460    #[arg(long, default_value_t = DefaultRpcServerArgs::get_global().ipcdisable)]
461    pub ipcdisable: bool,
462
463    /// Filename for IPC socket/pipe within the datadir
464    #[arg(long, default_value_t = DefaultRpcServerArgs::get_global().ipcpath.clone())]
465    pub ipcpath: String,
466
467    /// Set the permissions for the IPC socket file, in octal format.
468    ///
469    /// If not specified, the permissions will be set by the system's umask.
470    #[arg(long = "ipc.permissions", default_value = Resettable::from(DefaultRpcServerArgs::get_global().ipc_socket_permissions.as_ref().map(|v| v.to_string().into())))]
471    pub ipc_socket_permissions: Option<String>,
472
473    /// Auth server address to listen on
474    #[arg(long = "authrpc.addr", default_value_t = DefaultRpcServerArgs::get_global().auth_addr)]
475    pub auth_addr: IpAddr,
476
477    /// Auth server port to listen on
478    #[arg(long = "authrpc.port", default_value_t = DefaultRpcServerArgs::get_global().auth_port)]
479    pub auth_port: u16,
480
481    /// Path to a JWT secret to use for the authenticated engine-API RPC server.
482    ///
483    /// This will enforce JWT authentication for all requests coming from the consensus layer.
484    ///
485    /// If no path is provided, a secret will be generated and stored in the datadir under
486    /// `<DIR>/<CHAIN_ID>/jwt.hex`. For mainnet this would be `~/.local/share/reth/mainnet/jwt.hex`
487    /// 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::<AddressSet>, default_value = Resettable::from(DefaultRpcServerArgs::get_global().builder_disallow.as_ref().map(|v| format!("{:?}", v).into())))]
625    pub builder_disallow: Option<AddressSet>,
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 = true)]
649    pub testing_skip_invalid_transactions: bool,
650
651    /// Force upcasting EIP-4844 blob sidecars to EIP-7594 format when Osaka is active.
652    ///
653    /// When enabled, blob transactions submitted via `eth_sendRawTransaction` with EIP-4844
654    /// sidecars will be automatically converted to EIP-7594 format if the next block is Osaka.
655    /// By default this is disabled, meaning transactions are submitted as-is.
656    #[arg(long = "rpc.force-blob-sidecar-upcasting", default_value_t = false)]
657    pub rpc_force_blob_sidecar_upcasting: bool,
658}
659
660impl RpcServerArgs {
661    /// Enables the HTTP-RPC server.
662    pub const fn with_http(mut self) -> Self {
663        self.http = true;
664        self
665    }
666
667    /// Configures modules for the HTTP-RPC server.
668    pub fn with_http_api(mut self, http_api: RpcModuleSelection) -> Self {
669        self.http_api = Some(http_api);
670        self
671    }
672
673    /// Enables the WS-RPC server.
674    pub const fn with_ws(mut self) -> Self {
675        self.ws = true;
676        self
677    }
678
679    /// Configures modules for WS-RPC server.
680    pub fn with_ws_api(mut self, ws_api: RpcModuleSelection) -> Self {
681        self.ws_api = Some(ws_api);
682        self
683    }
684
685    /// Enables the Auth IPC
686    pub const fn with_auth_ipc(mut self) -> Self {
687        self.auth_ipc = true;
688        self
689    }
690
691    /// Configures modules for both the HTTP-RPC server and WS-RPC server.
692    ///
693    /// This is the same as calling both [`Self::with_http_api`] and [`Self::with_ws_api`].
694    pub fn with_api(self, api: RpcModuleSelection) -> Self {
695        self.with_http_api(api.clone()).with_ws_api(api)
696    }
697
698    /// Change rpc port numbers based on the instance number, if provided.
699    /// * The `auth_port` is scaled by a factor of `instance * 100`
700    /// * The `http_port` is scaled by a factor of `-instance`
701    /// * The `ws_port` is scaled by a factor of `instance * 2`
702    /// * The `ipcpath` is appended with the instance number: `/tmp/reth.ipc-<instance>`
703    ///
704    /// # Panics
705    /// Warning: if `instance` is zero in debug mode, this will panic.
706    ///
707    /// This will also panic in debug mode if either:
708    /// * `instance` is greater than `655` (scaling would overflow `u16`)
709    /// * `self.auth_port / 100 + (instance - 1)` would overflow `u16`
710    ///
711    /// In release mode, this will silently wrap around.
712    pub fn adjust_instance_ports(&mut self, instance: Option<u16>) {
713        if let Some(instance) = instance {
714            debug_assert_ne!(instance, 0, "instance must be non-zero");
715            // auth port is scaled by a factor of instance * 100
716            self.auth_port += instance * 100 - 100;
717            // http port is scaled by a factor of -instance
718            self.http_port -= instance - 1;
719            // ws port is scaled by a factor of instance * 2
720            self.ws_port += instance * 2 - 2;
721            // append instance file to ipc path
722            self.ipcpath = format!("{}-{}", self.ipcpath, instance);
723        }
724    }
725
726    /// Set the http port to zero, to allow the OS to assign a random unused port when the rpc
727    /// server binds to a socket.
728    pub const fn with_http_unused_port(mut self) -> Self {
729        self.http_port = 0;
730        self
731    }
732
733    /// Set the ws port to zero, to allow the OS to assign a random unused port when the rpc
734    /// server binds to a socket.
735    pub const fn with_ws_unused_port(mut self) -> Self {
736        self.ws_port = 0;
737        self
738    }
739
740    /// Set the auth port to zero, to allow the OS to assign a random unused port when the rpc
741    /// server binds to a socket.
742    pub const fn with_auth_unused_port(mut self) -> Self {
743        self.auth_port = 0;
744        self
745    }
746
747    /// Append a random string to the ipc path, to prevent possible collisions when multiple nodes
748    /// are being run on the same machine.
749    pub fn with_ipc_random_path(mut self) -> Self {
750        let random_string: String =
751            rand::rng().sample_iter(rand::distr::Alphanumeric).take(8).map(char::from).collect();
752        self.ipcpath = format!("{}-{}", self.ipcpath, random_string);
753        self
754    }
755
756    /// Configure all ports to be set to a random unused port when bound, and set the IPC path to a
757    /// random path.
758    pub fn with_unused_ports(mut self) -> Self {
759        self = self.with_http_unused_port();
760        self = self.with_ws_unused_port();
761        self = self.with_auth_unused_port();
762        self = self.with_ipc_random_path();
763        self
764    }
765
766    /// Apply a function to the args.
767    pub fn apply<F>(self, f: F) -> Self
768    where
769        F: FnOnce(Self) -> Self,
770    {
771        f(self)
772    }
773
774    /// Configures the timeout for send raw transaction sync.
775    pub const fn with_send_raw_transaction_sync_timeout(mut self, timeout: Duration) -> Self {
776        self.rpc_send_raw_transaction_sync_timeout = timeout;
777        self
778    }
779
780    /// Returns `true` if the given RPC namespace is enabled on any transport.
781    pub fn is_namespace_enabled(&self, ns: RethRpcModule) -> bool {
782        if self.http && self.http_api.as_ref().is_some_and(|api| api.contains(&ns)) {
783            return true;
784        }
785        if self.ws && self.ws_api.as_ref().is_some_and(|api| api.contains(&ns)) {
786            return true;
787        }
788        // IPC exposes all modules when enabled
789        !self.ipcdisable
790    }
791
792    /// Enables forced blob sidecar upcasting from EIP-4844 to EIP-7594 format.
793    pub const fn with_force_blob_sidecar_upcasting(mut self) -> Self {
794        self.rpc_force_blob_sidecar_upcasting = true;
795        self
796    }
797}
798
799impl Default for RpcServerArgs {
800    fn default() -> Self {
801        let DefaultRpcServerArgs {
802            http,
803            http_addr,
804            http_port,
805            http_disable_compression,
806            http_api,
807            http_corsdomain,
808            ws,
809            ws_addr,
810            ws_port,
811            ws_allowed_origins,
812            ws_api,
813            ipcdisable,
814            ipcpath,
815            ipc_socket_permissions,
816            auth_addr,
817            auth_port,
818            auth_jwtsecret,
819            auth_ipc,
820            auth_ipc_path,
821            disable_auth_server,
822            rpc_jwtsecret,
823            rpc_max_request_size,
824            rpc_max_response_size,
825            rpc_max_subscriptions_per_connection,
826            rpc_max_connections,
827            rpc_max_tracing_requests,
828            rpc_max_blocking_io_requests,
829            rpc_max_trace_filter_blocks,
830            rpc_max_blocks_per_filter,
831            rpc_max_logs_per_response,
832            rpc_gas_cap,
833            rpc_evm_memory_limit,
834            rpc_tx_fee_cap,
835            rpc_max_simulate_blocks,
836            rpc_eth_proof_window,
837            rpc_proof_permits,
838            rpc_pending_block,
839            rpc_forwarder,
840            builder_disallow,
841            rpc_state_cache,
842            gas_price_oracle,
843            rpc_send_raw_transaction_sync_timeout,
844        } = DefaultRpcServerArgs::get_global().clone();
845        Self {
846            http,
847            http_addr,
848            http_port,
849            http_disable_compression,
850            http_api,
851            http_corsdomain,
852            ws,
853            ws_addr,
854            ws_port,
855            ws_allowed_origins,
856            ws_api,
857            ipcdisable,
858            ipcpath,
859            ipc_socket_permissions,
860            auth_addr,
861            auth_port,
862            auth_jwtsecret,
863            auth_ipc,
864            auth_ipc_path,
865            disable_auth_server,
866            rpc_jwtsecret,
867            rpc_max_request_size,
868            rpc_max_response_size,
869            rpc_max_subscriptions_per_connection,
870            rpc_max_connections,
871            rpc_max_tracing_requests,
872            rpc_max_blocking_io_requests,
873            rpc_max_trace_filter_blocks,
874            rpc_max_blocks_per_filter,
875            rpc_max_logs_per_response,
876            rpc_gas_cap,
877            rpc_evm_memory_limit,
878            rpc_tx_fee_cap,
879            rpc_max_simulate_blocks,
880            rpc_eth_proof_window,
881            rpc_proof_permits,
882            rpc_pending_block,
883            rpc_forwarder,
884            builder_disallow,
885            rpc_state_cache,
886            gas_price_oracle,
887            rpc_send_raw_transaction_sync_timeout,
888            testing_skip_invalid_transactions: true,
889            rpc_force_blob_sidecar_upcasting: false,
890        }
891    }
892}
893
894/// clap value parser for [`RpcModuleSelection`] with configurable validation.
895#[derive(Clone, Debug, Default)]
896#[non_exhaustive]
897struct RpcModuleSelectionValueParser;
898
899impl TypedValueParser for RpcModuleSelectionValueParser {
900    type Value = RpcModuleSelection;
901
902    fn parse_ref(
903        &self,
904        _cmd: &Command,
905        _arg: Option<&Arg>,
906        value: &OsStr,
907    ) -> Result<Self::Value, clap::Error> {
908        let val =
909            value.to_str().ok_or_else(|| clap::Error::new(clap::error::ErrorKind::InvalidUtf8))?;
910        // This will now accept any module name, creating Other(name) for unknowns
911        Ok(val
912            .parse::<RpcModuleSelection>()
913            .expect("RpcModuleSelection parsing cannot fail with Other variant"))
914    }
915
916    fn possible_values(&self) -> Option<Box<dyn Iterator<Item = PossibleValue> + '_>> {
917        // Only show standard modules in help text (excludes "other")
918        let values = RethRpcModule::standard_variant_names().map(PossibleValue::new);
919        Some(Box::new(values))
920    }
921}
922
923#[cfg(test)]
924mod tests {
925    use super::*;
926    use clap::{Args, Parser};
927
928    /// A helper type to parse Args more easily
929    #[derive(Parser)]
930    struct CommandParser<T: Args> {
931        #[command(flatten)]
932        args: T,
933    }
934
935    #[test]
936    fn test_rpc_server_args_parser() {
937        let args =
938            CommandParser::<RpcServerArgs>::parse_from(["reth", "--http.api", "eth,admin,debug"])
939                .args;
940
941        let apis = args.http_api.unwrap();
942        let expected = RpcModuleSelection::try_from_selection(["eth", "admin", "debug"]).unwrap();
943
944        assert_eq!(apis, expected);
945    }
946
947    #[test]
948    fn test_rpc_server_eth_call_bundle_args() {
949        let args =
950            CommandParser::<RpcServerArgs>::parse_from(["reth", "--http.api", "eth,admin,debug"])
951                .args;
952
953        let apis = args.http_api.unwrap();
954        let expected = RpcModuleSelection::try_from_selection(["eth", "admin", "debug"]).unwrap();
955
956        assert_eq!(apis, expected);
957    }
958
959    #[test]
960    fn test_rpc_server_args_parser_none() {
961        let args = CommandParser::<RpcServerArgs>::parse_from(["reth", "--http.api", "none"]).args;
962        let apis = args.http_api.unwrap();
963        let expected = RpcModuleSelection::Selection(Default::default());
964        assert_eq!(apis, expected);
965    }
966
967    #[test]
968    fn rpc_server_args_default_sanity_test() {
969        let default_args = RpcServerArgs::default();
970        let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
971
972        assert_eq!(args, default_args);
973    }
974
975    #[test]
976    fn test_rpc_tx_fee_cap_parse_integer() {
977        let args = CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.txfeecap", "2"]).args;
978        let expected = 2_000_000_000_000_000_000u128; // 2 ETH in wei
979        assert_eq!(args.rpc_tx_fee_cap, expected);
980    }
981
982    #[test]
983    fn test_rpc_tx_fee_cap_parse_decimal() {
984        let args =
985            CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.txfeecap", "1.5"]).args;
986        let expected = 1_500_000_000_000_000_000u128; // 1.5 ETH in wei
987        assert_eq!(args.rpc_tx_fee_cap, expected);
988    }
989
990    #[test]
991    fn test_rpc_tx_fee_cap_parse_zero() {
992        let args = CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.txfeecap", "0"]).args;
993        assert_eq!(args.rpc_tx_fee_cap, 0); // 0 = no cap
994    }
995
996    #[test]
997    fn test_rpc_tx_fee_cap_parse_none() {
998        let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
999        let expected = 1_000_000_000_000_000_000u128;
1000        assert_eq!(args.rpc_tx_fee_cap, expected); // 1 ETH default cap
1001    }
1002
1003    #[test]
1004    fn test_rpc_server_args() {
1005        let args = RpcServerArgs {
1006            http: true,
1007            http_addr: "127.0.0.1".parse().unwrap(),
1008            http_port: 8545,
1009            http_disable_compression: false,
1010            http_api: Some(RpcModuleSelection::try_from_selection(["eth", "admin"]).unwrap()),
1011            http_corsdomain: Some("*".to_string()),
1012            ws: true,
1013            ws_addr: "127.0.0.1".parse().unwrap(),
1014            ws_port: 8546,
1015            ws_allowed_origins: Some("*".to_string()),
1016            ws_api: Some(RpcModuleSelection::try_from_selection(["eth", "admin"]).unwrap()),
1017            ipcdisable: false,
1018            ipcpath: "reth.ipc".to_string(),
1019            ipc_socket_permissions: Some("0o666".to_string()),
1020            auth_addr: "127.0.0.1".parse().unwrap(),
1021            auth_port: 8551,
1022            auth_jwtsecret: Some(std::path::PathBuf::from("/tmp/jwt.hex")),
1023            auth_ipc: false,
1024            auth_ipc_path: "engine.ipc".to_string(),
1025            disable_auth_server: false,
1026            rpc_jwtsecret: Some(
1027                JwtSecret::from_hex(
1028                    "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
1029                )
1030                .unwrap(),
1031            ),
1032            rpc_max_request_size: 15u32.into(),
1033            rpc_max_response_size: 160u32.into(),
1034            rpc_max_subscriptions_per_connection: 1024u32.into(),
1035            rpc_max_connections: 500u32.into(),
1036            rpc_max_tracing_requests: 16,
1037            rpc_max_blocking_io_requests: 256,
1038            rpc_max_trace_filter_blocks: 4000,
1039            rpc_max_blocks_per_filter: 1000u64.into(),
1040            rpc_max_logs_per_response: 10000u64.into(),
1041            rpc_gas_cap: 50_000_000,
1042            rpc_evm_memory_limit: 256,
1043            rpc_tx_fee_cap: 2_000_000_000_000_000_000u128,
1044            rpc_max_simulate_blocks: 256,
1045            rpc_eth_proof_window: 100_000,
1046            rpc_proof_permits: 16,
1047            rpc_pending_block: PendingBlockKind::Full,
1048            rpc_forwarder: Some("http://localhost:8545".parse().unwrap()),
1049            builder_disallow: None,
1050            rpc_state_cache: RpcStateCacheArgs {
1051                max_blocks: 5000,
1052                max_receipts: 2000,
1053                max_headers: 1000,
1054                max_concurrent_db_requests: 512,
1055                max_cached_tx_hashes: 30_000,
1056            },
1057            gas_price_oracle: GasPriceOracleArgs {
1058                blocks: 20,
1059                ignore_price: 2,
1060                max_price: 500_000_000_000,
1061                percentile: 60,
1062                default_suggested_fee: None,
1063            },
1064            rpc_send_raw_transaction_sync_timeout: std::time::Duration::from_secs(30),
1065            testing_skip_invalid_transactions: true,
1066            rpc_force_blob_sidecar_upcasting: false,
1067        };
1068
1069        let parsed_args = CommandParser::<RpcServerArgs>::parse_from([
1070            "reth",
1071            "--http",
1072            "--http.addr",
1073            "127.0.0.1",
1074            "--http.port",
1075            "8545",
1076            "--http.api",
1077            "eth,admin",
1078            "--http.corsdomain",
1079            "*",
1080            "--ws",
1081            "--ws.addr",
1082            "127.0.0.1",
1083            "--ws.port",
1084            "8546",
1085            "--ws.origins",
1086            "*",
1087            "--ws.api",
1088            "eth,admin",
1089            "--ipcpath",
1090            "reth.ipc",
1091            "--ipc.permissions",
1092            "0o666",
1093            "--authrpc.addr",
1094            "127.0.0.1",
1095            "--authrpc.port",
1096            "8551",
1097            "--authrpc.jwtsecret",
1098            "/tmp/jwt.hex",
1099            "--auth-ipc.path",
1100            "engine.ipc",
1101            "--rpc.jwtsecret",
1102            "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
1103            "--rpc.max-request-size",
1104            "15",
1105            "--rpc.max-response-size",
1106            "160",
1107            "--rpc.max-subscriptions-per-connection",
1108            "1024",
1109            "--rpc.max-connections",
1110            "500",
1111            "--rpc.max-tracing-requests",
1112            "16",
1113            "--rpc.max-blocking-io-requests",
1114            "256",
1115            "--rpc.max-trace-filter-blocks",
1116            "4000",
1117            "--rpc.max-blocks-per-filter",
1118            "1000",
1119            "--rpc.max-logs-per-response",
1120            "10000",
1121            "--rpc.gascap",
1122            "50000000",
1123            "--rpc.evm-memory-limit",
1124            "256",
1125            "--rpc.txfeecap",
1126            "2.0",
1127            "--rpc.max-simulate-blocks",
1128            "256",
1129            "--rpc.eth-proof-window",
1130            "100000",
1131            "--rpc.proof-permits",
1132            "16",
1133            "--rpc.pending-block",
1134            "full",
1135            "--rpc.forwarder",
1136            "http://localhost:8545",
1137            "--rpc-cache.max-blocks",
1138            "5000",
1139            "--rpc-cache.max-receipts",
1140            "2000",
1141            "--rpc-cache.max-headers",
1142            "1000",
1143            "--rpc-cache.max-concurrent-db-requests",
1144            "512",
1145            "--gpo.blocks",
1146            "20",
1147            "--gpo.ignoreprice",
1148            "2",
1149            "--gpo.maxprice",
1150            "500000000000",
1151            "--gpo.percentile",
1152            "60",
1153            "--rpc.send-raw-transaction-sync-timeout",
1154            "30s",
1155            "--testing.skip-invalid-transactions",
1156        ])
1157        .args;
1158
1159        assert_eq!(parsed_args, args);
1160    }
1161}