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