1use jsonrpsee::server::ServerConfigBuilder;
2use reth_node_core::{args::RpcServerArgs, utils::get_or_create_jwt_secret_from_path};
3use reth_rpc::ValidationApiConfig;
4use reth_rpc_eth_types::{EthConfig, EthStateCacheConfig, GasPriceOracleConfig};
5use reth_rpc_layer::{JwtError, JwtSecret};
6use reth_rpc_server_types::RpcModuleSelection;
7use std::{net::SocketAddr, path::PathBuf};
8use tower::layer::util::Identity;
9use tracing::{debug, warn};
10
11use crate::{
12 auth::AuthServerConfig, error::RpcError, IpcServerBuilder, RpcModuleConfig, RpcServerConfig,
13 TransportRpcModuleConfig,
14};
15
16pub trait RethRpcServerConfig {
21 fn is_ipc_enabled(&self) -> bool;
23
24 fn ipc_path(&self) -> &str;
26
27 fn eth_config(&self) -> EthConfig;
29
30 fn flashbots_config(&self) -> ValidationApiConfig;
32
33 fn state_cache_config(&self) -> EthStateCacheConfig;
35
36 fn rpc_max_request_size_bytes(&self) -> u32;
38
39 fn rpc_max_response_size_bytes(&self) -> u32;
41
42 fn gas_price_oracle_config(&self) -> GasPriceOracleConfig;
44
45 fn transport_rpc_module_config(&self) -> TransportRpcModuleConfig;
50
51 fn http_ws_server_builder(&self) -> ServerConfigBuilder;
53
54 fn ipc_server_builder(&self) -> IpcServerBuilder<Identity, Identity>;
56
57 fn rpc_server_config(&self) -> RpcServerConfig;
59
60 fn auth_server_config(&self, jwt_secret: JwtSecret) -> Result<AuthServerConfig, RpcError>;
62
63 fn auth_jwt_secret(&self, default_jwt_path: PathBuf) -> Result<JwtSecret, JwtError>;
77
78 fn rpc_secret_key(&self) -> Option<JwtSecret>;
82}
83
84impl RethRpcServerConfig for RpcServerArgs {
85 fn is_ipc_enabled(&self) -> bool {
86 !self.ipcdisable
88 }
89
90 fn ipc_path(&self) -> &str {
91 self.ipcpath.as_str()
92 }
93
94 fn eth_config(&self) -> EthConfig {
95 EthConfig::default()
96 .max_tracing_requests(self.rpc_max_tracing_requests)
97 .max_blocking_io_requests(self.rpc_max_blocking_io_requests)
98 .max_trace_filter_blocks(self.rpc_max_trace_filter_blocks)
99 .max_blocks_per_filter(self.rpc_max_blocks_per_filter.unwrap_or_max())
100 .max_logs_per_response(self.rpc_max_logs_per_response.unwrap_or_max() as usize)
101 .eth_proof_window(self.rpc_eth_proof_window)
102 .rpc_gas_cap(self.rpc_gas_cap)
103 .rpc_max_simulate_blocks(self.rpc_max_simulate_blocks)
104 .state_cache(self.state_cache_config())
105 .gpo_config(self.gas_price_oracle_config())
106 .proof_permits(self.rpc_proof_permits)
107 .pending_block_kind(self.rpc_pending_block)
108 .raw_tx_forwarder(self.rpc_forwarder.clone())
109 .rpc_evm_memory_limit(self.rpc_evm_memory_limit)
110 .force_blob_sidecar_upcasting(self.rpc_force_blob_sidecar_upcasting)
111 }
112
113 fn flashbots_config(&self) -> ValidationApiConfig {
114 ValidationApiConfig {
115 disallow: self.builder_disallow.clone().unwrap_or_default(),
116 validation_window: self.rpc_eth_proof_window,
117 }
118 }
119
120 fn state_cache_config(&self) -> EthStateCacheConfig {
121 EthStateCacheConfig {
122 max_blocks: self.rpc_state_cache.max_blocks,
123 max_receipts: self.rpc_state_cache.max_receipts,
124 max_headers: self.rpc_state_cache.max_headers,
125 max_bals: self.rpc_state_cache.max_bals,
126 max_concurrent_db_requests: self.rpc_state_cache.max_concurrent_db_requests,
127 max_cached_tx_hashes: self.rpc_state_cache.max_cached_tx_hashes,
128 }
129 }
130
131 fn rpc_max_request_size_bytes(&self) -> u32 {
132 self.rpc_max_request_size.get().saturating_mul(1024 * 1024)
133 }
134
135 fn rpc_max_response_size_bytes(&self) -> u32 {
136 self.rpc_max_response_size.get().saturating_mul(1024 * 1024)
137 }
138
139 fn gas_price_oracle_config(&self) -> GasPriceOracleConfig {
140 self.gas_price_oracle.gas_price_oracle_config()
141 }
142
143 fn transport_rpc_module_config(&self) -> TransportRpcModuleConfig {
144 let mut config = TransportRpcModuleConfig::default()
145 .with_config(RpcModuleConfig::new(self.eth_config()));
146
147 if self.http {
148 config = config.with_http(
149 self.http_api
150 .clone()
151 .unwrap_or_else(|| RpcModuleSelection::standard_modules().into()),
152 );
153 }
154
155 if self.ws {
156 config = config.with_ws(
157 self.ws_api
158 .clone()
159 .unwrap_or_else(|| RpcModuleSelection::standard_modules().into()),
160 );
161 }
162
163 if self.is_ipc_enabled() {
164 config = config.with_ipc(RpcModuleSelection::default_ipc_modules());
165 }
166
167 config
168 }
169
170 fn http_ws_server_builder(&self) -> ServerConfigBuilder {
171 ServerConfigBuilder::new()
172 .max_connections(self.rpc_max_connections.get())
173 .max_request_body_size(self.rpc_max_request_size_bytes())
174 .max_response_body_size(self.rpc_max_response_size_bytes())
175 .max_subscriptions_per_connection(self.rpc_max_subscriptions_per_connection.get())
176 }
177
178 fn ipc_server_builder(&self) -> IpcServerBuilder<Identity, Identity> {
179 IpcServerBuilder::default()
180 .max_subscriptions_per_connection(self.rpc_max_subscriptions_per_connection.get())
181 .max_request_body_size(self.rpc_max_request_size_bytes())
182 .max_response_body_size(self.rpc_max_response_size_bytes())
183 .max_connections(self.rpc_max_connections.get())
184 .set_ipc_socket_permissions(self.ipc_socket_permissions.clone())
185 }
186
187 fn rpc_server_config(&self) -> RpcServerConfig {
188 let mut config = RpcServerConfig::default().with_jwt_secret(self.rpc_secret_key());
189
190 if self.http_api.is_some() && !self.http {
191 warn!(
192 target: "reth::cli",
193 "The --http.api flag is set but --http is not enabled. HTTP RPC API will not be exposed."
194 );
195 }
196
197 if self.ws_api.is_some() && !self.ws {
198 warn!(
199 target: "reth::cli",
200 "The --ws.api flag is set but --ws is not enabled. WS RPC API will not be exposed."
201 );
202 }
203
204 if self.http {
205 let socket_address = SocketAddr::new(self.http_addr, self.http_port);
206 config = config
207 .with_http_address(socket_address)
208 .with_http(self.http_ws_server_builder())
209 .with_http_cors(self.http_corsdomain.clone())
210 .with_http_disable_compression(self.http_disable_compression);
211 }
212
213 if self.ws {
214 let socket_address = SocketAddr::new(self.ws_addr, self.ws_port);
215 config = config
217 .with_ws_address(socket_address)
218 .with_ws(self.http_ws_server_builder())
219 .with_ws_cors(self.ws_allowed_origins.clone());
220 }
221
222 if self.is_ipc_enabled() {
223 config =
224 config.with_ipc(self.ipc_server_builder()).with_ipc_endpoint(self.ipcpath.clone());
225 }
226
227 config
228 }
229
230 fn auth_server_config(&self, jwt_secret: JwtSecret) -> Result<AuthServerConfig, RpcError> {
231 let address = SocketAddr::new(self.auth_addr, self.auth_port);
232
233 let mut builder = AuthServerConfig::builder(jwt_secret).socket_addr(address);
234 if self.auth_ipc {
235 builder = builder
236 .ipc_endpoint(self.auth_ipc_path.clone())
237 .with_ipc_config(self.ipc_server_builder());
238 }
239 Ok(builder.build())
240 }
241
242 fn auth_jwt_secret(&self, default_jwt_path: PathBuf) -> Result<JwtSecret, JwtError> {
243 match self.auth_jwtsecret.as_ref() {
244 Some(fpath) => {
245 debug!(target: "reth::cli", user_path=?fpath, "Reading JWT auth secret file");
246 JwtSecret::from_file(fpath)
247 }
248 None => get_or_create_jwt_secret_from_path(&default_jwt_path),
249 }
250 }
251
252 fn rpc_secret_key(&self) -> Option<JwtSecret> {
253 self.rpc_jwtsecret
254 }
255}
256
257#[cfg(test)]
258mod tests {
259 use clap::{Args, Parser};
260 use reth_node_core::args::RpcServerArgs;
261 use reth_rpc_eth_types::RPC_DEFAULT_GAS_CAP;
262 use reth_rpc_server_types::{constants, RethRpcModule, RpcModuleSelection};
263 use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
264
265 use crate::config::RethRpcServerConfig;
266
267 #[derive(Parser)]
269 struct CommandParser<T: Args> {
270 #[command(flatten)]
271 args: T,
272 }
273
274 #[test]
275 fn test_rpc_gas_cap() {
276 let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
277 let config = args.eth_config();
278 assert_eq!(config.rpc_gas_cap, u64::from(RPC_DEFAULT_GAS_CAP));
279
280 let args =
281 CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.gascap", "1000"]).args;
282 let config = args.eth_config();
283 assert_eq!(config.rpc_gas_cap, 1000);
284
285 let args = CommandParser::<RpcServerArgs>::try_parse_from(["reth", "--rpc.gascap", "0"]);
286 assert!(args.is_err());
287 }
288
289 #[test]
290 fn test_transport_rpc_module_config() {
291 let args = CommandParser::<RpcServerArgs>::parse_from([
292 "reth",
293 "--http.api",
294 "eth,admin,debug",
295 "--http",
296 "--ws",
297 ])
298 .args;
299 let config = args.transport_rpc_module_config();
300 let expected = [RethRpcModule::Eth, RethRpcModule::Admin, RethRpcModule::Debug];
301 assert_eq!(config.http().cloned().unwrap().into_selection(), expected.into());
302 assert_eq!(
303 config.ws().cloned().unwrap().into_selection(),
304 RpcModuleSelection::standard_modules()
305 );
306 }
307
308 #[test]
309 fn test_transport_rpc_module_trim_config() {
310 let args = CommandParser::<RpcServerArgs>::parse_from([
311 "reth",
312 "--http.api",
313 " eth, admin, debug",
314 "--http",
315 "--ws",
316 ])
317 .args;
318 let config = args.transport_rpc_module_config();
319 let expected = [RethRpcModule::Eth, RethRpcModule::Admin, RethRpcModule::Debug];
320 assert_eq!(config.http().cloned().unwrap().into_selection(), expected.into());
321 assert_eq!(
322 config.ws().cloned().unwrap().into_selection(),
323 RpcModuleSelection::standard_modules()
324 );
325 }
326
327 #[test]
328 fn test_unique_rpc_modules() {
329 let args = CommandParser::<RpcServerArgs>::parse_from([
330 "reth",
331 "--http.api",
332 " eth, admin, debug, eth,admin",
333 "--http",
334 "--ws",
335 ])
336 .args;
337 let config = args.transport_rpc_module_config();
338 let expected = [RethRpcModule::Eth, RethRpcModule::Admin, RethRpcModule::Debug];
339 assert_eq!(config.http().cloned().unwrap().into_selection(), expected.into());
340 assert_eq!(
341 config.ws().cloned().unwrap().into_selection(),
342 RpcModuleSelection::standard_modules()
343 );
344 }
345
346 #[test]
347 fn test_rpc_server_config() {
348 let args = CommandParser::<RpcServerArgs>::parse_from([
349 "reth",
350 "--http.api",
351 "eth,admin,debug",
352 "--http",
353 "--ws",
354 "--ws.addr",
355 "127.0.0.1",
356 "--ws.port",
357 "8888",
358 ])
359 .args;
360 let config = args.rpc_server_config();
361 assert_eq!(
362 config.http_address().unwrap(),
363 SocketAddr::V4(SocketAddrV4::new(
364 Ipv4Addr::LOCALHOST,
365 constants::DEFAULT_HTTP_RPC_PORT
366 ))
367 );
368 assert_eq!(
369 config.ws_address().unwrap(),
370 SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8888))
371 );
372 assert_eq!(config.ipc_endpoint().unwrap(), constants::DEFAULT_IPC_ENDPOINT);
373 }
374
375 #[test]
376 fn test_zero_filter_limits() {
377 let args = CommandParser::<RpcServerArgs>::parse_from([
378 "reth",
379 "--rpc-max-blocks-per-filter",
380 "0",
381 "--rpc-max-logs-per-response",
382 "0",
383 ])
384 .args;
385
386 let config = args.eth_config().filter_config();
387 assert_eq!(config.max_blocks_per_filter, Some(u64::MAX));
388 assert_eq!(config.max_logs_per_response, Some(usize::MAX));
389 }
390
391 #[test]
392 fn test_custom_filter_limits() {
393 let args = CommandParser::<RpcServerArgs>::parse_from([
394 "reth",
395 "--rpc-max-blocks-per-filter",
396 "100",
397 "--rpc-max-logs-per-response",
398 "200",
399 ])
400 .args;
401
402 let config = args.eth_config().filter_config();
403 assert_eq!(config.max_blocks_per_filter, Some(100));
404 assert_eq!(config.max_logs_per_response, Some(200));
405 }
406}