1use crate::{
20 arg_enums::{Cors, RpcMethods},
21 params::{IpNetwork, RpcBatchRequestConfig},
22 RPC_DEFAULT_MAX_CONNECTIONS, RPC_DEFAULT_MAX_REQUEST_SIZE_MB, RPC_DEFAULT_MAX_RESPONSE_SIZE_MB,
23 RPC_DEFAULT_MAX_SUBS_PER_CONN, RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN,
24};
25use clap::Args;
26use std::{
27 net::{Ipv4Addr, Ipv6Addr, SocketAddr},
28 num::NonZeroU32,
29};
30
31const RPC_LISTEN_ADDR: &str = "listen-addr";
32const RPC_CORS: &str = "cors";
33const RPC_MAX_CONNS: &str = "max-connections";
34const RPC_MAX_REQUEST_SIZE: &str = "max-request-size";
35const RPC_MAX_RESPONSE_SIZE: &str = "max-response-size";
36const RPC_MAX_SUBS_PER_CONN: &str = "max-subscriptions-per-connection";
37const RPC_MAX_BUF_CAP_PER_CONN: &str = "max-buffer-capacity-per-connection";
38const RPC_RATE_LIMIT: &str = "rate-limit";
39const RPC_RATE_LIMIT_TRUST_PROXY_HEADERS: &str = "rate-limit-trust-proxy-headers";
40const RPC_RATE_LIMIT_WHITELISTED_IPS: &str = "rate-limit-whitelisted-ips";
41const RPC_RETRY_RANDOM_PORT: &str = "retry-random-port";
42const RPC_METHODS: &str = "methods";
43const RPC_OPTIONAL: &str = "optional";
44const RPC_DISABLE_BATCH: &str = "disable-batch-requests";
45const RPC_BATCH_LIMIT: &str = "max-batch-request-len";
46
47#[derive(Debug, Clone, Args)]
49pub struct RpcParams {
50 #[arg(long)]
59 pub rpc_external: bool,
60
61 #[arg(long)]
65 pub unsafe_rpc_external: bool,
66
67 #[arg(
69 long,
70 value_name = "METHOD SET",
71 value_enum,
72 ignore_case = true,
73 default_value_t = RpcMethods::Auto,
74 verbatim_doc_comment
75 )]
76 pub rpc_methods: RpcMethods,
77
78 #[arg(long)]
85 pub rpc_rate_limit: Option<NonZeroU32>,
86
87 #[arg(long, num_args = 1..)]
91 pub rpc_rate_limit_whitelisted_ips: Vec<IpNetwork>,
92
93 #[arg(long)]
101 pub rpc_rate_limit_trust_proxy_headers: bool,
102
103 #[arg(long, default_value_t = RPC_DEFAULT_MAX_REQUEST_SIZE_MB)]
105 pub rpc_max_request_size: u32,
106
107 #[arg(long, default_value_t = RPC_DEFAULT_MAX_RESPONSE_SIZE_MB)]
109 pub rpc_max_response_size: u32,
110
111 #[arg(long, default_value_t = RPC_DEFAULT_MAX_SUBS_PER_CONN)]
113 pub rpc_max_subscriptions_per_connection: u32,
114
115 #[arg(long, value_name = "PORT")]
117 pub rpc_port: Option<u16>,
118
119 #[arg(
153 long,
154 num_args = 1..,
155 verbatim_doc_comment,
156 conflicts_with_all = &["rpc_external", "unsafe_rpc_external", "rpc_port", "rpc_cors", "rpc_rate_limit_trust_proxy_headers", "rpc_rate_limit", "rpc_rate_limit_whitelisted_ips", "rpc_message_buffer_capacity_per_connection", "rpc_disable_batch_requests", "rpc_max_subscriptions_per_connection", "rpc_max_request_size", "rpc_max_response_size"]
157 )]
158 pub experimental_rpc_endpoint: Vec<RpcEndpoint>,
159
160 #[arg(long, value_name = "COUNT", default_value_t = RPC_DEFAULT_MAX_CONNECTIONS)]
162 pub rpc_max_connections: u32,
163
164 #[arg(long, default_value_t = RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN)]
173 pub rpc_message_buffer_capacity_per_connection: u32,
174
175 #[arg(long, alias = "rpc_no_batch_requests", conflicts_with_all = &["rpc_max_batch_request_len"])]
177 pub rpc_disable_batch_requests: bool,
178
179 #[arg(long, conflicts_with_all = &["rpc_disable_batch_requests"], value_name = "LEN")]
181 pub rpc_max_batch_request_len: Option<u32>,
182
183 #[arg(long, value_name = "ORIGINS")]
190 pub rpc_cors: Option<Cors>,
191}
192
193impl RpcParams {
194 pub fn rpc_cors(&self, is_dev: bool) -> crate::Result<Option<Vec<String>>> {
196 Ok(self
197 .rpc_cors
198 .clone()
199 .unwrap_or_else(|| {
200 if is_dev {
201 log::warn!("Running in --dev mode, RPC CORS has been disabled.");
202 Cors::All
203 } else {
204 Cors::List(vec![
205 "http://localhost:*".into(),
206 "http://127.0.0.1:*".into(),
207 "https://localhost:*".into(),
208 "https://127.0.0.1:*".into(),
209 "https://polkadot.js.org".into(),
210 ])
211 }
212 })
213 .into())
214 }
215
216 pub fn rpc_addr(
218 &self,
219 is_dev: bool,
220 is_validator: bool,
221 default_listen_port: u16,
222 ) -> crate::Result<Option<Vec<RpcEndpoint>>> {
223 if !self.experimental_rpc_endpoint.is_empty() {
224 for endpoint in &self.experimental_rpc_endpoint {
225 if endpoint.rpc_methods == RpcMethods::Unsafe && endpoint.is_global() ||
228 endpoint.listen_addr.ip().is_unspecified()
229 {
230 eprintln!(
231 "It isn't safe to expose RPC publicly without a proxy server that filters \
232 available set of RPC methods."
233 );
234 }
235 }
236
237 return Ok(Some(self.experimental_rpc_endpoint.clone()));
238 }
239
240 let (ipv4, ipv6) = rpc_interface(
241 self.rpc_external,
242 self.unsafe_rpc_external,
243 self.rpc_methods,
244 is_validator,
245 )?;
246
247 let cors = self.rpc_cors(is_dev)?;
248 let port = self.rpc_port.unwrap_or(default_listen_port);
249
250 Ok(Some(vec![
251 RpcEndpoint {
252 batch_config: self.rpc_batch_config()?,
253 max_connections: self.rpc_max_connections,
254 listen_addr: SocketAddr::new(std::net::IpAddr::V4(ipv4), port),
255 rpc_methods: self.rpc_methods,
256 rate_limit: self.rpc_rate_limit,
257 rate_limit_trust_proxy_headers: self.rpc_rate_limit_trust_proxy_headers,
258 rate_limit_whitelisted_ips: self.rpc_rate_limit_whitelisted_ips.clone(),
259 max_payload_in_mb: self.rpc_max_request_size,
260 max_payload_out_mb: self.rpc_max_response_size,
261 max_subscriptions_per_connection: self.rpc_max_subscriptions_per_connection,
262 max_buffer_capacity_per_connection: self.rpc_message_buffer_capacity_per_connection,
263 cors: cors.clone(),
264 retry_random_port: true,
265 is_optional: false,
266 },
267 RpcEndpoint {
268 batch_config: self.rpc_batch_config()?,
269 max_connections: self.rpc_max_connections,
270 listen_addr: SocketAddr::new(std::net::IpAddr::V6(ipv6), port),
271 rpc_methods: self.rpc_methods,
272 rate_limit: self.rpc_rate_limit,
273 rate_limit_trust_proxy_headers: self.rpc_rate_limit_trust_proxy_headers,
274 rate_limit_whitelisted_ips: self.rpc_rate_limit_whitelisted_ips.clone(),
275 max_payload_in_mb: self.rpc_max_request_size,
276 max_payload_out_mb: self.rpc_max_response_size,
277 max_subscriptions_per_connection: self.rpc_max_subscriptions_per_connection,
278 max_buffer_capacity_per_connection: self.rpc_message_buffer_capacity_per_connection,
279 cors: cors.clone(),
280 retry_random_port: true,
281 is_optional: true,
282 },
283 ]))
284 }
285
286 pub fn rpc_batch_config(&self) -> crate::Result<RpcBatchRequestConfig> {
288 let cfg = if self.rpc_disable_batch_requests {
289 RpcBatchRequestConfig::Disabled
290 } else if let Some(l) = self.rpc_max_batch_request_len {
291 RpcBatchRequestConfig::Limit(l)
292 } else {
293 RpcBatchRequestConfig::Unlimited
294 };
295
296 Ok(cfg)
297 }
298}
299
300fn rpc_interface(
301 is_external: bool,
302 is_unsafe_external: bool,
303 rpc_methods: RpcMethods,
304 is_validator: bool,
305) -> crate::Result<(Ipv4Addr, Ipv6Addr)> {
306 if is_external && is_validator && rpc_methods != RpcMethods::Unsafe {
307 return Err(crate::Error::Input(
308 "--rpc-external option shouldn't be used if the node is running as \
309 a validator. Use `--unsafe-rpc-external` or `--rpc-methods=unsafe` if you understand \
310 the risks. See the options description for more information."
311 .to_owned(),
312 ));
313 }
314
315 if is_external || is_unsafe_external {
316 if rpc_methods == RpcMethods::Unsafe {
317 eprintln!(
318 "It isn't safe to expose RPC publicly without a proxy server that filters \
319 available set of RPC methods."
320 );
321 }
322
323 Ok((Ipv4Addr::UNSPECIFIED, Ipv6Addr::UNSPECIFIED))
324 } else {
325 Ok((Ipv4Addr::LOCALHOST, Ipv6Addr::LOCALHOST))
326 }
327}
328
329#[derive(Debug, Clone)]
331pub struct RpcEndpoint {
332 pub listen_addr: SocketAddr,
334 pub batch_config: RpcBatchRequestConfig,
336 pub max_connections: u32,
338 pub max_payload_in_mb: u32,
340 pub max_payload_out_mb: u32,
342 pub max_subscriptions_per_connection: u32,
344 pub max_buffer_capacity_per_connection: u32,
346 pub rate_limit: Option<NonZeroU32>,
348 pub rate_limit_trust_proxy_headers: bool,
350 pub rate_limit_whitelisted_ips: Vec<IpNetwork>,
352 pub cors: Option<Vec<String>>,
354 pub rpc_methods: RpcMethods,
356 pub is_optional: bool,
360 pub retry_random_port: bool,
362}
363
364impl std::str::FromStr for RpcEndpoint {
365 type Err = String;
366
367 fn from_str(s: &str) -> Result<Self, Self::Err> {
368 let mut listen_addr = None;
369 let mut max_connections = None;
370 let mut max_payload_in_mb = None;
371 let mut max_payload_out_mb = None;
372 let mut max_subscriptions_per_connection = None;
373 let mut max_buffer_capacity_per_connection = None;
374 let mut cors: Option<Vec<String>> = None;
375 let mut rpc_methods = None;
376 let mut is_optional = None;
377 let mut disable_batch_requests = None;
378 let mut max_batch_request_len = None;
379 let mut rate_limit = None;
380 let mut rate_limit_trust_proxy_headers = None;
381 let mut rate_limit_whitelisted_ips = Vec::new();
382 let mut retry_random_port = None;
383
384 for input in s.split(',') {
385 let (key, val) = input.trim().split_once('=').ok_or_else(|| invalid_input(input))?;
386 let key = key.trim();
387 let val = val.trim();
388
389 match key {
390 RPC_LISTEN_ADDR => {
391 if listen_addr.is_some() {
392 return Err(only_once_err(RPC_LISTEN_ADDR));
393 }
394 let val: SocketAddr =
395 val.parse().map_err(|_| invalid_value(RPC_LISTEN_ADDR, &val))?;
396 listen_addr = Some(val);
397 },
398 RPC_CORS => {
399 if val.is_empty() {
400 return Err(invalid_value(RPC_CORS, &val));
401 }
402
403 if let Some(cors) = cors.as_mut() {
404 cors.push(val.to_string());
405 } else {
406 cors = Some(vec![val.to_string()]);
407 }
408 },
409 RPC_MAX_CONNS => {
410 if max_connections.is_some() {
411 return Err(only_once_err(RPC_MAX_CONNS));
412 }
413
414 let val = val.parse().map_err(|_| invalid_value(RPC_MAX_CONNS, &val))?;
415 max_connections = Some(val);
416 },
417 RPC_MAX_REQUEST_SIZE => {
418 if max_payload_in_mb.is_some() {
419 return Err(only_once_err(RPC_MAX_REQUEST_SIZE));
420 }
421
422 let val =
423 val.parse().map_err(|_| invalid_value(RPC_MAX_RESPONSE_SIZE, &val))?;
424 max_payload_in_mb = Some(val);
425 },
426 RPC_MAX_RESPONSE_SIZE => {
427 if max_payload_out_mb.is_some() {
428 return Err(only_once_err(RPC_MAX_RESPONSE_SIZE));
429 }
430
431 let val =
432 val.parse().map_err(|_| invalid_value(RPC_MAX_RESPONSE_SIZE, &val))?;
433 max_payload_out_mb = Some(val);
434 },
435 RPC_MAX_SUBS_PER_CONN => {
436 if max_subscriptions_per_connection.is_some() {
437 return Err(only_once_err(RPC_MAX_SUBS_PER_CONN));
438 }
439
440 let val =
441 val.parse().map_err(|_| invalid_value(RPC_MAX_SUBS_PER_CONN, &val))?;
442 max_subscriptions_per_connection = Some(val);
443 },
444 RPC_MAX_BUF_CAP_PER_CONN => {
445 if max_buffer_capacity_per_connection.is_some() {
446 return Err(only_once_err(RPC_MAX_BUF_CAP_PER_CONN));
447 }
448
449 let val =
450 val.parse().map_err(|_| invalid_value(RPC_MAX_BUF_CAP_PER_CONN, &val))?;
451 max_buffer_capacity_per_connection = Some(val);
452 },
453 RPC_RATE_LIMIT => {
454 if rate_limit.is_some() {
455 return Err(only_once_err("rate-limit"));
456 }
457
458 let val = val.parse().map_err(|_| invalid_value(RPC_RATE_LIMIT, &val))?;
459 rate_limit = Some(val);
460 },
461 RPC_RATE_LIMIT_TRUST_PROXY_HEADERS => {
462 if rate_limit_trust_proxy_headers.is_some() {
463 return Err(only_once_err(RPC_RATE_LIMIT_TRUST_PROXY_HEADERS));
464 }
465
466 let val = val
467 .parse()
468 .map_err(|_| invalid_value(RPC_RATE_LIMIT_TRUST_PROXY_HEADERS, &val))?;
469 rate_limit_trust_proxy_headers = Some(val);
470 },
471 RPC_RATE_LIMIT_WHITELISTED_IPS => {
472 let ip: IpNetwork = val
473 .parse()
474 .map_err(|_| invalid_value(RPC_RATE_LIMIT_WHITELISTED_IPS, &val))?;
475 rate_limit_whitelisted_ips.push(ip);
476 },
477 RPC_RETRY_RANDOM_PORT => {
478 if retry_random_port.is_some() {
479 return Err(only_once_err(RPC_RETRY_RANDOM_PORT));
480 }
481 let val =
482 val.parse().map_err(|_| invalid_value(RPC_RETRY_RANDOM_PORT, &val))?;
483 retry_random_port = Some(val);
484 },
485 RPC_METHODS => {
486 if rpc_methods.is_some() {
487 return Err(only_once_err("methods"));
488 }
489 let val = val.parse().map_err(|_| invalid_value(RPC_METHODS, &val))?;
490 rpc_methods = Some(val);
491 },
492 RPC_OPTIONAL => {
493 if is_optional.is_some() {
494 return Err(only_once_err(RPC_OPTIONAL));
495 }
496
497 let val = val.parse().map_err(|_| invalid_value(RPC_OPTIONAL, &val))?;
498 is_optional = Some(val);
499 },
500 RPC_DISABLE_BATCH => {
501 if disable_batch_requests.is_some() {
502 return Err(only_once_err(RPC_DISABLE_BATCH));
503 }
504
505 let val = val.parse().map_err(|_| invalid_value(RPC_DISABLE_BATCH, &val))?;
506 disable_batch_requests = Some(val);
507 },
508 RPC_BATCH_LIMIT => {
509 if max_batch_request_len.is_some() {
510 return Err(only_once_err(RPC_BATCH_LIMIT));
511 }
512
513 let val = val.parse().map_err(|_| invalid_value(RPC_BATCH_LIMIT, &val))?;
514 max_batch_request_len = Some(val);
515 },
516 _ => return Err(invalid_key(key)),
517 }
518 }
519
520 let listen_addr = listen_addr.ok_or("`listen-addr` must be specified exactly once")?;
521
522 let batch_config = match (disable_batch_requests, max_batch_request_len) {
523 (Some(true), Some(_)) => {
524 return Err(format!("`{RPC_BATCH_LIMIT}` and `{RPC_DISABLE_BATCH}` are mutually exclusive and can't be used together"));
525 },
526 (Some(false), None) => RpcBatchRequestConfig::Disabled,
527 (None, Some(len)) => RpcBatchRequestConfig::Limit(len),
528 _ => RpcBatchRequestConfig::Unlimited,
529 };
530
531 Ok(Self {
532 listen_addr,
533 batch_config,
534 max_connections: max_connections.unwrap_or(RPC_DEFAULT_MAX_CONNECTIONS),
535 max_payload_in_mb: max_payload_in_mb.unwrap_or(RPC_DEFAULT_MAX_REQUEST_SIZE_MB),
536 max_payload_out_mb: max_payload_out_mb.unwrap_or(RPC_DEFAULT_MAX_RESPONSE_SIZE_MB),
537 cors,
538 max_buffer_capacity_per_connection: max_buffer_capacity_per_connection
539 .unwrap_or(RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN),
540 max_subscriptions_per_connection: max_subscriptions_per_connection
541 .unwrap_or(RPC_DEFAULT_MAX_SUBS_PER_CONN),
542 rpc_methods: rpc_methods.unwrap_or(RpcMethods::Auto),
543 rate_limit,
544 rate_limit_trust_proxy_headers: rate_limit_trust_proxy_headers.unwrap_or(false),
545 rate_limit_whitelisted_ips,
546 is_optional: is_optional.unwrap_or(false),
547 retry_random_port: retry_random_port.unwrap_or(false),
548 })
549 }
550}
551
552impl Into<sc_service::config::RpcEndpoint> for RpcEndpoint {
553 fn into(self) -> sc_service::config::RpcEndpoint {
554 sc_service::config::RpcEndpoint {
555 batch_config: self.batch_config,
556 listen_addr: self.listen_addr,
557 max_buffer_capacity_per_connection: self.max_buffer_capacity_per_connection,
558 max_connections: self.max_connections,
559 max_payload_in_mb: self.max_payload_in_mb,
560 max_payload_out_mb: self.max_payload_out_mb,
561 max_subscriptions_per_connection: self.max_subscriptions_per_connection,
562 rpc_methods: self.rpc_methods.into(),
563 rate_limit: self.rate_limit,
564 rate_limit_trust_proxy_headers: self.rate_limit_trust_proxy_headers,
565 rate_limit_whitelisted_ips: self.rate_limit_whitelisted_ips,
566 cors: self.cors,
567 retry_random_port: self.retry_random_port,
568 is_optional: self.is_optional,
569 }
570 }
571}
572
573impl RpcEndpoint {
574 pub fn is_global(&self) -> bool {
576 let ip = IpNetwork::from(self.listen_addr.ip());
577 ip.is_global()
578 }
579}
580
581fn only_once_err(reason: &str) -> String {
582 format!("`{reason}` is only allowed be specified once")
583}
584
585fn invalid_input(input: &str) -> String {
586 format!("`{input}`, expects: `key=value`")
587}
588
589fn invalid_value(key: &str, value: &str) -> String {
590 format!("value=`{value}` key=`{key}`")
591}
592
593fn invalid_key(key: &str) -> String {
594 format!("unknown key=`{key}`, see `--help` for available options")
595}
596
597#[cfg(test)]
598mod tests {
599 use super::*;
600 use std::{num::NonZeroU32, str::FromStr};
601
602 #[test]
603 fn parse_rpc_endpoint_works() {
604 assert!(RpcEndpoint::from_str("listen-addr=127.0.0.1:9944").is_ok());
605 assert!(RpcEndpoint::from_str("listen-addr=[::1]:9944").is_ok());
606 assert!(RpcEndpoint::from_str("listen-addr=127.0.0.1:9944,methods=auto").is_ok());
607 assert!(RpcEndpoint::from_str("listen-addr=[::1]:9944,methods=auto").is_ok());
608 assert!(RpcEndpoint::from_str(
609 "listen-addr=127.0.0.1:9944,methods=auto,cors=*,optional=true"
610 )
611 .is_ok());
612
613 assert!(RpcEndpoint::from_str("listen-addrs=127.0.0.1:9944,foo=*").is_err());
614 assert!(RpcEndpoint::from_str("listen-addrs=127.0.0.1:9944,cors=").is_err());
615 }
616
617 #[test]
618 fn parse_rpc_endpoint_all() {
619 let endpoint = RpcEndpoint::from_str(
620 "listen-addr=127.0.0.1:9944,methods=unsafe,cors=*,optional=true,retry-random-port=true,rate-limit=99,\
621 max-batch-request-len=100,rate-limit-trust-proxy-headers=true,max-connections=33,max-request-size=4,\
622 max-response-size=3,max-subscriptions-per-connection=7,max-buffer-capacity-per-connection=8,\
623 rate-limit-whitelisted-ips=192.168.1.0/24,rate-limit-whitelisted-ips=ff01::0/32"
624 ).unwrap();
625 assert_eq!(endpoint.listen_addr, ([127, 0, 0, 1], 9944).into());
626 assert_eq!(endpoint.rpc_methods, RpcMethods::Unsafe);
627 assert_eq!(endpoint.cors, Some(vec!["*".to_string()]));
628 assert_eq!(endpoint.is_optional, true);
629 assert_eq!(endpoint.retry_random_port, true);
630 assert_eq!(endpoint.rate_limit, Some(NonZeroU32::new(99).unwrap()));
631 assert!(matches!(endpoint.batch_config, RpcBatchRequestConfig::Limit(l) if l == 100));
632 assert_eq!(endpoint.rate_limit_trust_proxy_headers, true);
633 assert_eq!(
634 endpoint.rate_limit_whitelisted_ips,
635 vec![
636 IpNetwork::V4("192.168.1.0/24".parse().unwrap()),
637 IpNetwork::V6("ff01::0/32".parse().unwrap())
638 ]
639 );
640 assert_eq!(endpoint.max_connections, 33);
641 assert_eq!(endpoint.max_payload_in_mb, 4);
642 assert_eq!(endpoint.max_payload_out_mb, 3);
643 assert_eq!(endpoint.max_subscriptions_per_connection, 7);
644 assert_eq!(endpoint.max_buffer_capacity_per_connection, 8);
645 }
646
647 #[test]
648 fn parse_rpc_endpoint_multiple_cors() {
649 let addr = RpcEndpoint::from_str(
650 "listen-addr=127.0.0.1:9944,methods=auto,cors=https://polkadot.js.org,cors=*,cors=localhost:*",
651 )
652 .unwrap();
653
654 assert_eq!(
655 addr.cors,
656 Some(vec![
657 "https://polkadot.js.org".to_string(),
658 "*".to_string(),
659 "localhost:*".to_string()
660 ])
661 );
662 }
663
664 #[test]
665 fn parse_rpc_endpoint_whitespaces() {
666 let addr = RpcEndpoint::from_str(
667 " listen-addr = 127.0.0.1:9944, methods = auto, optional = true ",
668 )
669 .unwrap();
670 assert_eq!(addr.rpc_methods, RpcMethods::Auto);
671 assert_eq!(addr.is_optional, true);
672 }
673
674 #[test]
675 fn parse_rpc_endpoint_batch_options_mutually_exclusive() {
676 assert!(RpcEndpoint::from_str(
677 "listen-addr = 127.0.0.1:9944,disable-batch-requests=true,max-batch-request-len=100",
678 )
679 .is_err());
680 }
681}