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