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.http {
194 let socket_address = SocketAddr::new(self.http_addr, self.http_port);
195 config = config
196 .with_http_address(socket_address)
197 .with_http(self.http_ws_server_builder())
198 .with_http_cors(self.http_corsdomain.clone())
199 .with_http_disable_compression(self.http_disable_compression);
200 }
201
202 if self.ws {
203 let socket_address = SocketAddr::new(self.ws_addr, self.ws_port);
204 config = config
206 .with_ws_address(socket_address)
207 .with_ws(self.http_ws_server_builder())
208 .with_ws_cors(self.ws_allowed_origins.clone());
209 }
210
211 if self.is_ipc_enabled() {
212 config =
213 config.with_ipc(self.ipc_server_builder()).with_ipc_endpoint(self.ipcpath.clone());
214 }
215
216 config
217 }
218
219 fn auth_server_config(&self, jwt_secret: JwtSecret) -> Result<AuthServerConfig, RpcError> {
220 let address = SocketAddr::new(self.auth_addr, self.auth_port);
221
222 let mut builder = AuthServerConfig::builder(jwt_secret).socket_addr(address);
223 if self.auth_ipc {
224 builder = builder
225 .ipc_endpoint(self.auth_ipc_path.clone())
226 .with_ipc_config(self.ipc_server_builder());
227 }
228 Ok(builder.build())
229 }
230
231 fn auth_jwt_secret(&self, default_jwt_path: PathBuf) -> Result<JwtSecret, JwtError> {
232 match self.auth_jwtsecret.as_ref() {
233 Some(fpath) => {
234 debug!(target: "reth::cli", user_path=?fpath, "Reading JWT auth secret file");
235 JwtSecret::from_file(fpath)
236 }
237 None => get_or_create_jwt_secret_from_path(&default_jwt_path),
238 }
239 }
240
241 fn rpc_secret_key(&self) -> Option<JwtSecret> {
242 self.rpc_jwtsecret
243 }
244}
245
246#[cfg(test)]
247mod tests {
248 use clap::{Args, Parser};
249 use reth_node_core::args::RpcServerArgs;
250 use reth_rpc_eth_types::RPC_DEFAULT_GAS_CAP;
251 use reth_rpc_server_types::{constants, RethRpcModule, RpcModuleSelection};
252 use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
253
254 use crate::config::RethRpcServerConfig;
255
256 #[derive(Parser)]
258 struct CommandParser<T: Args> {
259 #[command(flatten)]
260 args: T,
261 }
262
263 #[test]
264 fn test_rpc_gas_cap() {
265 let args = CommandParser::<RpcServerArgs>::parse_from(["reth"]).args;
266 let config = args.eth_config();
267 assert_eq!(config.rpc_gas_cap, u64::from(RPC_DEFAULT_GAS_CAP));
268
269 let args =
270 CommandParser::<RpcServerArgs>::parse_from(["reth", "--rpc.gascap", "1000"]).args;
271 let config = args.eth_config();
272 assert_eq!(config.rpc_gas_cap, 1000);
273
274 let args = CommandParser::<RpcServerArgs>::try_parse_from(["reth", "--rpc.gascap", "0"]);
275 assert!(args.is_err());
276 }
277
278 #[test]
279 fn test_transport_rpc_module_config() {
280 let args = CommandParser::<RpcServerArgs>::parse_from([
281 "reth",
282 "--http.api",
283 "eth,admin,debug",
284 "--http",
285 "--ws",
286 ])
287 .args;
288 let config = args.transport_rpc_module_config();
289 let expected = [RethRpcModule::Eth, RethRpcModule::Admin, RethRpcModule::Debug];
290 assert_eq!(config.http().cloned().unwrap().into_selection(), expected.into());
291 assert_eq!(
292 config.ws().cloned().unwrap().into_selection(),
293 RpcModuleSelection::standard_modules()
294 );
295 }
296
297 #[test]
298 fn test_transport_rpc_module_trim_config() {
299 let args = CommandParser::<RpcServerArgs>::parse_from([
300 "reth",
301 "--http.api",
302 " eth, admin, debug",
303 "--http",
304 "--ws",
305 ])
306 .args;
307 let config = args.transport_rpc_module_config();
308 let expected = [RethRpcModule::Eth, RethRpcModule::Admin, RethRpcModule::Debug];
309 assert_eq!(config.http().cloned().unwrap().into_selection(), expected.into());
310 assert_eq!(
311 config.ws().cloned().unwrap().into_selection(),
312 RpcModuleSelection::standard_modules()
313 );
314 }
315
316 #[test]
317 fn test_unique_rpc_modules() {
318 let args = CommandParser::<RpcServerArgs>::parse_from([
319 "reth",
320 "--http.api",
321 " eth, admin, debug, eth,admin",
322 "--http",
323 "--ws",
324 ])
325 .args;
326 let config = args.transport_rpc_module_config();
327 let expected = [RethRpcModule::Eth, RethRpcModule::Admin, RethRpcModule::Debug];
328 assert_eq!(config.http().cloned().unwrap().into_selection(), expected.into());
329 assert_eq!(
330 config.ws().cloned().unwrap().into_selection(),
331 RpcModuleSelection::standard_modules()
332 );
333 }
334
335 #[test]
336 fn test_rpc_server_config() {
337 let args = CommandParser::<RpcServerArgs>::parse_from([
338 "reth",
339 "--http.api",
340 "eth,admin,debug",
341 "--http",
342 "--ws",
343 "--ws.addr",
344 "127.0.0.1",
345 "--ws.port",
346 "8888",
347 ])
348 .args;
349 let config = args.rpc_server_config();
350 assert_eq!(
351 config.http_address().unwrap(),
352 SocketAddr::V4(SocketAddrV4::new(
353 Ipv4Addr::LOCALHOST,
354 constants::DEFAULT_HTTP_RPC_PORT
355 ))
356 );
357 assert_eq!(
358 config.ws_address().unwrap(),
359 SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8888))
360 );
361 assert_eq!(config.ipc_endpoint().unwrap(), constants::DEFAULT_IPC_ENDPOINT);
362 }
363
364 #[test]
365 fn test_zero_filter_limits() {
366 let args = CommandParser::<RpcServerArgs>::parse_from([
367 "reth",
368 "--rpc-max-blocks-per-filter",
369 "0",
370 "--rpc-max-logs-per-response",
371 "0",
372 ])
373 .args;
374
375 let config = args.eth_config().filter_config();
376 assert_eq!(config.max_blocks_per_filter, Some(u64::MAX));
377 assert_eq!(config.max_logs_per_response, Some(usize::MAX));
378 }
379
380 #[test]
381 fn test_custom_filter_limits() {
382 let args = CommandParser::<RpcServerArgs>::parse_from([
383 "reth",
384 "--rpc-max-blocks-per-filter",
385 "100",
386 "--rpc-max-logs-per-response",
387 "200",
388 ])
389 .args;
390
391 let config = args.eth_config().filter_config();
392 assert_eq!(config.max_blocks_per_filter, Some(100));
393 assert_eq!(config.max_logs_per_response, Some(200));
394 }
395}