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_concurrent_db_requests: self.rpc_state_cache.max_concurrent_db_requests,
126 max_cached_tx_hashes: self.rpc_state_cache.max_cached_tx_hashes,
127 }
128 }
129
130 fn rpc_max_request_size_bytes(&self) -> u32 {
131 self.rpc_max_request_size.get().saturating_mul(1024 * 1024)
132 }
133
134 fn rpc_max_response_size_bytes(&self) -> u32 {
135 self.rpc_max_response_size.get().saturating_mul(1024 * 1024)
136 }
137
138 fn gas_price_oracle_config(&self) -> GasPriceOracleConfig {
139 self.gas_price_oracle.gas_price_oracle_config()
140 }
141
142 fn transport_rpc_module_config(&self) -> TransportRpcModuleConfig {
143 let mut config = TransportRpcModuleConfig::default()
144 .with_config(RpcModuleConfig::new(self.eth_config()));
145
146 if self.http {
147 config = config.with_http(
148 self.http_api
149 .clone()
150 .unwrap_or_else(|| RpcModuleSelection::standard_modules().into()),
151 );
152 }
153
154 if self.ws {
155 config = config.with_ws(
156 self.ws_api
157 .clone()
158 .unwrap_or_else(|| RpcModuleSelection::standard_modules().into()),
159 );
160 }
161
162 if self.is_ipc_enabled() {
163 config = config.with_ipc(RpcModuleSelection::default_ipc_modules());
164 }
165
166 config
167 }
168
169 fn http_ws_server_builder(&self) -> ServerConfigBuilder {
170 ServerConfigBuilder::new()
171 .max_connections(self.rpc_max_connections.get())
172 .max_request_body_size(self.rpc_max_request_size_bytes())
173 .max_response_body_size(self.rpc_max_response_size_bytes())
174 .max_subscriptions_per_connection(self.rpc_max_subscriptions_per_connection.get())
175 }
176
177 fn ipc_server_builder(&self) -> IpcServerBuilder<Identity, Identity> {
178 IpcServerBuilder::default()
179 .max_subscriptions_per_connection(self.rpc_max_subscriptions_per_connection.get())
180 .max_request_body_size(self.rpc_max_request_size_bytes())
181 .max_response_body_size(self.rpc_max_response_size_bytes())
182 .max_connections(self.rpc_max_connections.get())
183 .set_ipc_socket_permissions(self.ipc_socket_permissions.clone())
184 }
185
186 fn rpc_server_config(&self) -> RpcServerConfig {
187 let mut config = RpcServerConfig::default().with_jwt_secret(self.rpc_secret_key());
188
189 if self.http_api.is_some() && !self.http {
190 warn!(
191 target: "reth::cli",
192 "The --http.api flag is set but --http is not enabled. HTTP RPC API will not be exposed."
193 );
194 }
195
196 if self.ws_api.is_some() && !self.ws {
197 warn!(
198 target: "reth::cli",
199 "The --ws.api flag is set but --ws is not enabled. WS RPC API will not be exposed."
200 );
201 }
202
203 if self.http {
204 let socket_address = SocketAddr::new(self.http_addr, self.http_port);
205 config = config
206 .with_http_address(socket_address)
207 .with_http(self.http_ws_server_builder())
208 .with_http_cors(self.http_corsdomain.clone())
209 .with_http_disable_compression(self.http_disable_compression);
210 }
211
212 if self.ws {
213 let socket_address = SocketAddr::new(self.ws_addr, self.ws_port);
214 config = config
216 .with_ws_address(socket_address)
217 .with_ws(self.http_ws_server_builder())
218 .with_ws_cors(self.ws_allowed_origins.clone());
219 }
220
221 if self.is_ipc_enabled() {
222 config =
223 config.with_ipc(self.ipc_server_builder()).with_ipc_endpoint(self.ipcpath.clone());
224 }
225
226 config
227 }
228
229 fn auth_server_config(&self, jwt_secret: JwtSecret) -> Result<AuthServerConfig, RpcError> {
230 let address = SocketAddr::new(self.auth_addr, self.auth_port);
231
232 let mut builder = AuthServerConfig::builder(jwt_secret).socket_addr(address);
233 if self.auth_ipc {
234 builder = builder
235 .ipc_endpoint(self.auth_ipc_path.clone())
236 .with_ipc_config(self.ipc_server_builder());
237 }
238 Ok(builder.build())
239 }
240
241 fn auth_jwt_secret(&self, default_jwt_path: PathBuf) -> Result<JwtSecret, JwtError> {
242 match self.auth_jwtsecret.as_ref() {
243 Some(fpath) => {
244 debug!(target: "reth::cli", user_path=?fpath, "Reading JWT auth secret file");
245 JwtSecret::from_file(fpath)
246 }
247 None => get_or_create_jwt_secret_from_path(&default_jwt_path),
248 }
249 }
250
251 fn rpc_secret_key(&self) -> Option<JwtSecret> {
252 self.rpc_jwtsecret
253 }
254}
255
256#[cfg(test)]
257mod tests {
258 use clap::{Args, Parser};
259 use reth_node_core::args::RpcServerArgs;
260 use reth_rpc_eth_types::RPC_DEFAULT_GAS_CAP;
261 use reth_rpc_server_types::{constants, RethRpcModule, RpcModuleSelection};
262 use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
263
264 use crate::config::RethRpcServerConfig;
265
266 #[derive(Parser)]
268 struct CommandParser<T: Args> {
269 #[command(flatten)]
270 args: T,
271 }
272
273 #[test]
274 fn test_rpc_gas_cap() {
275 let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
276 let config = args.eth_config();
277 assert_eq!(config.rpc_gas_cap, u64::from(RPC_DEFAULT_GAS_CAP));
278
279 let args =
280 CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.gascap", "1000"]).args;
281 let config = args.eth_config();
282 assert_eq!(config.rpc_gas_cap, 1000);
283
284 let args = CommandParser::<RpcServerArgs>::try_parse_from(["reth", "--rpc.gascap", "0"]);
285 assert!(args.is_err());
286 }
287
288 #[test]
289 fn test_transport_rpc_module_config() {
290 let args = CommandParser::<RpcServerArgs>::parse_from([
291 "reth",
292 "--http.api",
293 "eth,admin,debug",
294 "--http",
295 "--ws",
296 ])
297 .args;
298 let config = args.transport_rpc_module_config();
299 let expected = [RethRpcModule::Eth, RethRpcModule::Admin, RethRpcModule::Debug];
300 assert_eq!(config.http().cloned().unwrap().into_selection(), expected.into());
301 assert_eq!(
302 config.ws().cloned().unwrap().into_selection(),
303 RpcModuleSelection::standard_modules()
304 );
305 }
306
307 #[test]
308 fn test_transport_rpc_module_trim_config() {
309 let args = CommandParser::<RpcServerArgs>::parse_from([
310 "reth",
311 "--http.api",
312 " eth, admin, debug",
313 "--http",
314 "--ws",
315 ])
316 .args;
317 let config = args.transport_rpc_module_config();
318 let expected = [RethRpcModule::Eth, RethRpcModule::Admin, RethRpcModule::Debug];
319 assert_eq!(config.http().cloned().unwrap().into_selection(), expected.into());
320 assert_eq!(
321 config.ws().cloned().unwrap().into_selection(),
322 RpcModuleSelection::standard_modules()
323 );
324 }
325
326 #[test]
327 fn test_unique_rpc_modules() {
328 let args = CommandParser::<RpcServerArgs>::parse_from([
329 "reth",
330 "--http.api",
331 " eth, admin, debug, eth,admin",
332 "--http",
333 "--ws",
334 ])
335 .args;
336 let config = args.transport_rpc_module_config();
337 let expected = [RethRpcModule::Eth, RethRpcModule::Admin, RethRpcModule::Debug];
338 assert_eq!(config.http().cloned().unwrap().into_selection(), expected.into());
339 assert_eq!(
340 config.ws().cloned().unwrap().into_selection(),
341 RpcModuleSelection::standard_modules()
342 );
343 }
344
345 #[test]
346 fn test_rpc_server_config() {
347 let args = CommandParser::<RpcServerArgs>::parse_from([
348 "reth",
349 "--http.api",
350 "eth,admin,debug",
351 "--http",
352 "--ws",
353 "--ws.addr",
354 "127.0.0.1",
355 "--ws.port",
356 "8888",
357 ])
358 .args;
359 let config = args.rpc_server_config();
360 assert_eq!(
361 config.http_address().unwrap(),
362 SocketAddr::V4(SocketAddrV4::new(
363 Ipv4Addr::LOCALHOST,
364 constants::DEFAULT_HTTP_RPC_PORT
365 ))
366 );
367 assert_eq!(
368 config.ws_address().unwrap(),
369 SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8888))
370 );
371 assert_eq!(config.ipc_endpoint().unwrap(), constants::DEFAULT_IPC_ENDPOINT);
372 }
373
374 #[test]
375 fn test_zero_filter_limits() {
376 let args = CommandParser::<RpcServerArgs>::parse_from([
377 "reth",
378 "--rpc-max-blocks-per-filter",
379 "0",
380 "--rpc-max-logs-per-response",
381 "0",
382 ])
383 .args;
384
385 let config = args.eth_config().filter_config();
386 assert_eq!(config.max_blocks_per_filter, Some(u64::MAX));
387 assert_eq!(config.max_logs_per_response, Some(usize::MAX));
388 }
389
390 #[test]
391 fn test_custom_filter_limits() {
392 let args = CommandParser::<RpcServerArgs>::parse_from([
393 "reth",
394 "--rpc-max-blocks-per-filter",
395 "100",
396 "--rpc-max-logs-per-response",
397 "200",
398 ])
399 .args;
400
401 let config = args.eth_config().filter_config();
402 assert_eq!(config.max_blocks_per_filter, Some(100));
403 assert_eq!(config.max_logs_per_response, Some(200));
404 }
405}