From 6e889fd4308b83a3089685eee74a7343b0997c2e Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Thu, 5 Sep 2024 16:11:49 +0800 Subject: [PATCH 1/4] Introduce `RpcParams` in sc-cli --- substrate/client/cli/src/commands/run_cmd.rs | 302 ++---------------- substrate/client/cli/src/params/rpc_params.rs | 290 ++++++++++++++++- 2 files changed, 310 insertions(+), 282 deletions(-) diff --git a/substrate/client/cli/src/commands/run_cmd.rs b/substrate/client/cli/src/commands/run_cmd.rs index f47baf2644e7..f91d18aca749 100644 --- a/substrate/client/cli/src/commands/run_cmd.rs +++ b/substrate/client/cli/src/commands/run_cmd.rs @@ -17,15 +17,12 @@ // along with this program. If not, see . use crate::{ - arg_enums::{Cors, RpcMethods}, error::{Error, Result}, params::{ ImportParams, KeystoreParams, NetworkParams, OffchainWorkerParams, RpcEndpoint, SharedParams, TransactionPoolParams, }, - CliConfiguration, PrometheusParams, RuntimeParams, TelemetryParams, - RPC_DEFAULT_MAX_CONNECTIONS, RPC_DEFAULT_MAX_REQUEST_SIZE_MB, RPC_DEFAULT_MAX_RESPONSE_SIZE_MB, - RPC_DEFAULT_MAX_SUBS_PER_CONN, RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN, + CliConfiguration, PrometheusParams, RpcParams, RuntimeParams, TelemetryParams, }; use clap::Parser; use regex::Regex; @@ -36,10 +33,7 @@ use sc_service::{ ChainSpec, Role, }; use sc_telemetry::TelemetryEndpoints; -use std::{ - net::{Ipv4Addr, Ipv6Addr, SocketAddr}, - num::NonZeroU32, -}; +use std::num::NonZeroU32; /// The `run` command used to run a node. #[derive(Debug, Clone, Parser)] @@ -59,154 +53,16 @@ pub struct RunCmd { #[arg(long)] pub no_grandpa: bool, - /// Listen to all RPC interfaces (default: local). - /// - /// Not all RPC methods are safe to be exposed publicly. - /// - /// Use an RPC proxy server to filter out dangerous methods. More details: - /// . - /// - /// Use `--unsafe-rpc-external` to suppress the warning if you understand the risks. - #[arg(long)] - pub rpc_external: bool, - - /// Listen to all RPC interfaces. - /// - /// Same as `--rpc-external`. - #[arg(long)] - pub unsafe_rpc_external: bool, - - /// RPC methods to expose. - #[arg( - long, - value_name = "METHOD SET", - value_enum, - ignore_case = true, - default_value_t = RpcMethods::Auto, - verbatim_doc_comment - )] - pub rpc_methods: RpcMethods, - - /// RPC rate limiting (calls/minute) for each connection. - /// - /// This is disabled by default. - /// - /// For example `--rpc-rate-limit 10` will maximum allow - /// 10 calls per minute per connection. - #[arg(long)] - pub rpc_rate_limit: Option, - - /// Disable RPC rate limiting for certain ip addresses. - /// - /// Each IP address must be in CIDR notation such as `1.2.3.4/24`. - #[arg(long, num_args = 1..)] - pub rpc_rate_limit_whitelisted_ips: Vec, - - /// Trust proxy headers for disable rate limiting. - /// - /// By default the rpc server will not trust headers such `X-Real-IP`, `X-Forwarded-For` and - /// `Forwarded` and this option will make the rpc server to trust these headers. - /// - /// For instance this may be secure if the rpc server is behind a reverse proxy and that the - /// proxy always sets these headers. - #[arg(long)] - pub rpc_rate_limit_trust_proxy_headers: bool, - - /// Set the maximum RPC request payload size for both HTTP and WS in megabytes. - #[arg(long, default_value_t = RPC_DEFAULT_MAX_REQUEST_SIZE_MB)] - pub rpc_max_request_size: u32, - - /// Set the maximum RPC response payload size for both HTTP and WS in megabytes. - #[arg(long, default_value_t = RPC_DEFAULT_MAX_RESPONSE_SIZE_MB)] - pub rpc_max_response_size: u32, - - /// Set the maximum concurrent subscriptions per connection. - #[arg(long, default_value_t = RPC_DEFAULT_MAX_SUBS_PER_CONN)] - pub rpc_max_subscriptions_per_connection: u32, - - /// Specify JSON-RPC server TCP port. - #[arg(long, value_name = "PORT")] - pub rpc_port: Option, - - /// EXPERIMENTAL: Specify the JSON-RPC server interface and this option which can be enabled - /// several times if you want expose several RPC interfaces with different configurations. - /// - /// The format for this option is: - /// `--experimental-rpc-endpoint" listen-addr=,,..."` where each option is - /// separated by a comma and `listen-addr` is the only required param. - /// - /// The following options are available: - /// • listen-addr: The socket address (ip:port) to listen on. Be careful to not expose the - /// server to the public internet unless you know what you're doing. (required) - /// • disable-batch-requests: Disable batch requests (optional) - /// • max-connections: The maximum number of concurrent connections that the server will - /// accept (optional) - /// • max-request-size: The maximum size of a request body in megabytes (optional) - /// • max-response-size: The maximum size of a response body in megabytes (optional) - /// • max-subscriptions-per-connection: The maximum number of subscriptions per connection - /// (optional) - /// • max-buffer-capacity-per-connection: The maximum buffer capacity per connection - /// (optional) - /// • max-batch-request-len: The maximum number of requests in a batch (optional) - /// • cors: The CORS allowed origins, this can enabled more than once (optional) - /// • methods: Which RPC methods to allow, valid values are "safe", "unsafe" and "auto" - /// (optional) - /// • optional: If the listen address is optional i.e the interface is not required to be - /// available For example this may be useful if some platforms doesn't support ipv6 - /// (optional) - /// • rate-limit: The rate limit in calls per minute for each connection (optional) - /// • rate-limit-trust-proxy-headers: Trust proxy headers for disable rate limiting (optional) - /// • rate-limit-whitelisted-ips: Disable rate limiting for certain ip addresses, this can be - /// enabled more than once (optional) • retry-random-port: If the port is already in use, - /// retry with a random port (optional) - /// - /// Use with care, this flag is unstable and subject to change. - #[arg( - long, - num_args = 1.., - verbatim_doc_comment, - 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"] - )] - pub experimental_rpc_endpoint: Vec, - - /// Maximum number of RPC server connections. - #[arg(long, value_name = "COUNT", default_value_t = RPC_DEFAULT_MAX_CONNECTIONS)] - pub rpc_max_connections: u32, - - /// The number of messages the RPC server is allowed to keep in memory. - /// - /// If the buffer becomes full then the server will not process - /// new messages until the connected client start reading the - /// underlying messages. - /// - /// This applies per connection which includes both - /// JSON-RPC methods calls and subscriptions. - #[arg(long, default_value_t = RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN)] - pub rpc_message_buffer_capacity_per_connection: u32, - - /// Disable RPC batch requests - #[arg(long, alias = "rpc_no_batch_requests", conflicts_with_all = &["rpc_max_batch_request_len"])] - pub rpc_disable_batch_requests: bool, - - /// Limit the max length per RPC batch request - #[arg(long, conflicts_with_all = &["rpc_disable_batch_requests"], value_name = "LEN")] - pub rpc_max_batch_request_len: Option, - - /// Specify browser *origins* allowed to access the HTTP & WS RPC servers. - /// - /// A comma-separated list of origins (protocol://domain or special `null` - /// value). Value of `all` will disable origin validation. Default is to - /// allow localhost and origins. When running in - /// `--dev` mode the default is to allow all origins. - #[arg(long, value_name = "ORIGINS")] - pub rpc_cors: Option, - /// The human-readable name for this node. /// /// It's used as network node name. #[arg(long, value_name = "NAME")] pub name: Option, + #[allow(missing_docs)] + #[clap(flatten)] + pub rpc_params: RpcParams, + #[allow(missing_docs)] #[clap(flatten)] pub telemetry_params: TelemetryParams, @@ -437,136 +293,51 @@ impl CliConfiguration for RunCmd { } fn rpc_max_connections(&self) -> Result { - Ok(self.rpc_max_connections) + Ok(self.rpc_params.rpc_max_connections) } fn rpc_cors(&self, is_dev: bool) -> Result>> { - Ok(self - .rpc_cors - .clone() - .unwrap_or_else(|| { - if is_dev { - log::warn!("Running in --dev mode, RPC CORS has been disabled."); - Cors::All - } else { - Cors::List(vec![ - "http://localhost:*".into(), - "http://127.0.0.1:*".into(), - "https://localhost:*".into(), - "https://127.0.0.1:*".into(), - "https://polkadot.js.org".into(), - ]) - } - }) - .into()) + self.rpc_params.rpc_cors(is_dev) } fn rpc_addr(&self, default_listen_port: u16) -> Result>> { - if !self.experimental_rpc_endpoint.is_empty() { - for endpoint in &self.experimental_rpc_endpoint { - // Technically, `0.0.0.0` isn't a public IP address, but it's a way to listen on - // all interfaces. Thus, we consider it as a public endpoint and warn about it. - if endpoint.rpc_methods == RpcMethods::Unsafe && endpoint.is_global() || - endpoint.listen_addr.ip().is_unspecified() - { - log::warn!( - "It isn't safe to expose RPC publicly without a proxy server that filters \ - available set of RPC methods." - ); - } - } - - return Ok(Some(self.experimental_rpc_endpoint.clone())); - } - - let (ipv4, ipv6) = rpc_interface( - self.rpc_external, - self.unsafe_rpc_external, - self.rpc_methods, - self.validator, - )?; - - let cors = self.rpc_cors(self.is_dev()?)?; - let port = self.rpc_port.unwrap_or(default_listen_port); - - Ok(Some(vec![ - RpcEndpoint { - batch_config: self.rpc_batch_config()?, - max_connections: self.rpc_max_connections, - listen_addr: SocketAddr::new(std::net::IpAddr::V4(ipv4), port), - rpc_methods: self.rpc_methods, - rate_limit: self.rpc_rate_limit, - rate_limit_trust_proxy_headers: self.rpc_rate_limit_trust_proxy_headers, - rate_limit_whitelisted_ips: self.rpc_rate_limit_whitelisted_ips.clone(), - max_payload_in_mb: self.rpc_max_request_size, - max_payload_out_mb: self.rpc_max_response_size, - max_subscriptions_per_connection: self.rpc_max_subscriptions_per_connection, - max_buffer_capacity_per_connection: self.rpc_message_buffer_capacity_per_connection, - cors: cors.clone(), - retry_random_port: true, - is_optional: false, - }, - RpcEndpoint { - batch_config: self.rpc_batch_config()?, - max_connections: self.rpc_max_connections, - listen_addr: SocketAddr::new(std::net::IpAddr::V6(ipv6), port), - rpc_methods: self.rpc_methods, - rate_limit: self.rpc_rate_limit, - rate_limit_trust_proxy_headers: self.rpc_rate_limit_trust_proxy_headers, - rate_limit_whitelisted_ips: self.rpc_rate_limit_whitelisted_ips.clone(), - max_payload_in_mb: self.rpc_max_request_size, - max_payload_out_mb: self.rpc_max_response_size, - max_subscriptions_per_connection: self.rpc_max_subscriptions_per_connection, - max_buffer_capacity_per_connection: self.rpc_message_buffer_capacity_per_connection, - cors: cors.clone(), - retry_random_port: true, - is_optional: true, - }, - ])) + self.rpc_params.rpc_addr(self.is_dev()?, self.validator, default_listen_port) } fn rpc_methods(&self) -> Result { - Ok(self.rpc_methods.into()) + Ok(self.rpc_params.rpc_methods.into()) } fn rpc_max_request_size(&self) -> Result { - Ok(self.rpc_max_request_size) + Ok(self.rpc_params.rpc_max_request_size) } fn rpc_max_response_size(&self) -> Result { - Ok(self.rpc_max_response_size) + Ok(self.rpc_params.rpc_max_response_size) } fn rpc_max_subscriptions_per_connection(&self) -> Result { - Ok(self.rpc_max_subscriptions_per_connection) + Ok(self.rpc_params.rpc_max_subscriptions_per_connection) } fn rpc_buffer_capacity_per_connection(&self) -> Result { - Ok(self.rpc_message_buffer_capacity_per_connection) + Ok(self.rpc_params.rpc_message_buffer_capacity_per_connection) } fn rpc_batch_config(&self) -> Result { - let cfg = if self.rpc_disable_batch_requests { - RpcBatchRequestConfig::Disabled - } else if let Some(l) = self.rpc_max_batch_request_len { - RpcBatchRequestConfig::Limit(l) - } else { - RpcBatchRequestConfig::Unlimited - }; - - Ok(cfg) + self.rpc_params.rpc_batch_config() } fn rpc_rate_limit(&self) -> Result> { - Ok(self.rpc_rate_limit) + Ok(self.rpc_params.rpc_rate_limit) } fn rpc_rate_limit_whitelisted_ips(&self) -> Result> { - Ok(self.rpc_rate_limit_whitelisted_ips.clone()) + Ok(self.rpc_params.rpc_rate_limit_whitelisted_ips.clone()) } fn rpc_rate_limit_trust_proxy_headers(&self) -> Result { - Ok(self.rpc_rate_limit_trust_proxy_headers) + Ok(self.rpc_params.rpc_rate_limit_trust_proxy_headers) } fn transaction_pool(&self, is_dev: bool) -> Result { @@ -600,57 +371,28 @@ pub fn is_node_name_valid(_name: &str) -> std::result::Result<(), &str> { let name = _name.to_string(); if name.is_empty() { - return Err("Node name cannot be empty") + return Err("Node name cannot be empty"); } if name.chars().count() >= crate::NODE_NAME_MAX_LENGTH { - return Err("Node name too long") + return Err("Node name too long"); } let invalid_chars = r"[\\.@]"; let re = Regex::new(invalid_chars).unwrap(); if re.is_match(&name) { - return Err("Node name should not contain invalid chars such as '.' and '@'") + return Err("Node name should not contain invalid chars such as '.' and '@'"); } let invalid_patterns = r"^https?:"; let re = Regex::new(invalid_patterns).unwrap(); if re.is_match(&name) { - return Err("Node name should not contain urls") + return Err("Node name should not contain urls"); } Ok(()) } -fn rpc_interface( - is_external: bool, - is_unsafe_external: bool, - rpc_methods: RpcMethods, - is_validator: bool, -) -> Result<(Ipv4Addr, Ipv6Addr)> { - if is_external && is_validator && rpc_methods != RpcMethods::Unsafe { - return Err(Error::Input( - "--rpc-external option shouldn't be used if the node is running as \ - a validator. Use `--unsafe-rpc-external` or `--rpc-methods=unsafe` if you understand \ - the risks. See the options description for more information." - .to_owned(), - )) - } - - if is_external || is_unsafe_external { - if rpc_methods == RpcMethods::Unsafe { - log::warn!( - "It isn't safe to expose RPC publicly without a proxy server that filters \ - available set of RPC methods." - ); - } - - Ok((Ipv4Addr::UNSPECIFIED, Ipv6Addr::UNSPECIFIED)) - } else { - Ok((Ipv4Addr::LOCALHOST, Ipv6Addr::LOCALHOST)) - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/substrate/client/cli/src/params/rpc_params.rs b/substrate/client/cli/src/params/rpc_params.rs index d0ec1fd00443..181f0b8d9a68 100644 --- a/substrate/client/cli/src/params/rpc_params.rs +++ b/substrate/client/cli/src/params/rpc_params.rs @@ -17,12 +17,16 @@ // along with this program. If not, see . use crate::{ - arg_enums::RpcMethods, + arg_enums::{Cors, RpcMethods}, params::{IpNetwork, RpcBatchRequestConfig}, RPC_DEFAULT_MAX_CONNECTIONS, RPC_DEFAULT_MAX_REQUEST_SIZE_MB, RPC_DEFAULT_MAX_RESPONSE_SIZE_MB, RPC_DEFAULT_MAX_SUBS_PER_CONN, RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN, }; -use std::{net::SocketAddr, num::NonZeroU32}; +use clap::Args; +use std::{ + net::{Ipv4Addr, Ipv6Addr, SocketAddr}, + num::NonZeroU32, +}; const RPC_LISTEN_ADDR: &str = "listen-addr"; const RPC_CORS: &str = "cors"; @@ -40,6 +44,288 @@ const RPC_OPTIONAL: &str = "optional"; const RPC_DISABLE_BATCH: &str = "disable-batch-requests"; const RPC_BATCH_LIMIT: &str = "max-batch-request-len"; +/// Parameters of RPC. +#[derive(Debug, Clone, Args)] +pub struct RpcParams { + /// Listen to all RPC interfaces (default: local). + /// + /// Not all RPC methods are safe to be exposed publicly. + /// + /// Use an RPC proxy server to filter out dangerous methods. More details: + /// . + /// + /// Use `--unsafe-rpc-external` to suppress the warning if you understand the risks. + #[arg(long)] + pub rpc_external: bool, + + /// Listen to all RPC interfaces. + /// + /// Same as `--rpc-external`. + #[arg(long)] + pub unsafe_rpc_external: bool, + + /// RPC methods to expose. + #[arg( + long, + value_name = "METHOD SET", + value_enum, + ignore_case = true, + default_value_t = RpcMethods::Auto, + verbatim_doc_comment + )] + pub rpc_methods: RpcMethods, + + /// RPC rate limiting (calls/minute) for each connection. + /// + /// This is disabled by default. + /// + /// For example `--rpc-rate-limit 10` will maximum allow + /// 10 calls per minute per connection. + #[arg(long)] + pub rpc_rate_limit: Option, + + /// Disable RPC rate limiting for certain ip addresses. + /// + /// Each IP address must be in CIDR notation such as `1.2.3.4/24`. + #[arg(long, num_args = 1..)] + pub rpc_rate_limit_whitelisted_ips: Vec, + + /// Trust proxy headers for disable rate limiting. + /// + /// By default the rpc server will not trust headers such `X-Real-IP`, `X-Forwarded-For` and + /// `Forwarded` and this option will make the rpc server to trust these headers. + /// + /// For instance this may be secure if the rpc server is behind a reverse proxy and that the + /// proxy always sets these headers. + #[arg(long)] + pub rpc_rate_limit_trust_proxy_headers: bool, + + /// Set the maximum RPC request payload size for both HTTP and WS in megabytes. + #[arg(long, default_value_t = RPC_DEFAULT_MAX_REQUEST_SIZE_MB)] + pub rpc_max_request_size: u32, + + /// Set the maximum RPC response payload size for both HTTP and WS in megabytes. + #[arg(long, default_value_t = RPC_DEFAULT_MAX_RESPONSE_SIZE_MB)] + pub rpc_max_response_size: u32, + + /// Set the maximum concurrent subscriptions per connection. + #[arg(long, default_value_t = RPC_DEFAULT_MAX_SUBS_PER_CONN)] + pub rpc_max_subscriptions_per_connection: u32, + + /// Specify JSON-RPC server TCP port. + #[arg(long, value_name = "PORT")] + pub rpc_port: Option, + + /// EXPERIMENTAL: Specify the JSON-RPC server interface and this option which can be enabled + /// several times if you want expose several RPC interfaces with different configurations. + /// + /// The format for this option is: + /// `--experimental-rpc-endpoint" listen-addr=,,..."` where each option is + /// separated by a comma and `listen-addr` is the only required param. + /// + /// The following options are available: + /// • listen-addr: The socket address (ip:port) to listen on. Be careful to not expose the + /// server to the public internet unless you know what you're doing. (required) + /// • disable-batch-requests: Disable batch requests (optional) + /// • max-connections: The maximum number of concurrent connections that the server will + /// accept (optional) + /// • max-request-size: The maximum size of a request body in megabytes (optional) + /// • max-response-size: The maximum size of a response body in megabytes (optional) + /// • max-subscriptions-per-connection: The maximum number of subscriptions per connection + /// (optional) + /// • max-buffer-capacity-per-connection: The maximum buffer capacity per connection + /// (optional) + /// • max-batch-request-len: The maximum number of requests in a batch (optional) + /// • cors: The CORS allowed origins, this can enabled more than once (optional) + /// • methods: Which RPC methods to allow, valid values are "safe", "unsafe" and "auto" + /// (optional) + /// • optional: If the listen address is optional i.e the interface is not required to be + /// available For example this may be useful if some platforms doesn't support ipv6 + /// (optional) + /// • rate-limit: The rate limit in calls per minute for each connection (optional) + /// • rate-limit-trust-proxy-headers: Trust proxy headers for disable rate limiting (optional) + /// • rate-limit-whitelisted-ips: Disable rate limiting for certain ip addresses, this can be + /// enabled more than once (optional) • retry-random-port: If the port is already in use, + /// retry with a random port (optional) + /// + /// Use with care, this flag is unstable and subject to change. + #[arg( + long, + num_args = 1.., + verbatim_doc_comment, + 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"] + )] + pub experimental_rpc_endpoint: Vec, + + /// Maximum number of RPC server connections. + #[arg(long, value_name = "COUNT", default_value_t = RPC_DEFAULT_MAX_CONNECTIONS)] + pub rpc_max_connections: u32, + + /// The number of messages the RPC server is allowed to keep in memory. + /// + /// If the buffer becomes full then the server will not process + /// new messages until the connected client start reading the + /// underlying messages. + /// + /// This applies per connection which includes both + /// JSON-RPC methods calls and subscriptions. + #[arg(long, default_value_t = RPC_DEFAULT_MESSAGE_CAPACITY_PER_CONN)] + pub rpc_message_buffer_capacity_per_connection: u32, + + /// Disable RPC batch requests + #[arg(long, alias = "rpc_no_batch_requests", conflicts_with_all = &["rpc_max_batch_request_len"])] + pub rpc_disable_batch_requests: bool, + + /// Limit the max length per RPC batch request + #[arg(long, conflicts_with_all = &["rpc_disable_batch_requests"], value_name = "LEN")] + pub rpc_max_batch_request_len: Option, + + /// Specify browser *origins* allowed to access the HTTP & WS RPC servers. + /// + /// A comma-separated list of origins (protocol://domain or special `null` + /// value). Value of `all` will disable origin validation. Default is to + /// allow localhost and origins. When running in + /// `--dev` mode the default is to allow all origins. + #[arg(long, value_name = "ORIGINS")] + pub rpc_cors: Option, +} + +impl RpcParams { + /// Returns the RPC CORS configuration. + pub fn rpc_cors(&self, is_dev: bool) -> crate::Result>> { + Ok(self + .rpc_cors + .clone() + .unwrap_or_else(|| { + if is_dev { + log::warn!("Running in --dev mode, RPC CORS has been disabled."); + Cors::All + } else { + Cors::List(vec![ + "http://localhost:*".into(), + "http://127.0.0.1:*".into(), + "https://localhost:*".into(), + "https://127.0.0.1:*".into(), + "https://polkadot.js.org".into(), + ]) + } + }) + .into()) + } + + /// Returns the RPC endpoints. + pub fn rpc_addr( + &self, + is_dev: bool, + is_validator: bool, + default_listen_port: u16, + ) -> crate::Result>> { + if !self.experimental_rpc_endpoint.is_empty() { + for endpoint in &self.experimental_rpc_endpoint { + // Technically, `0.0.0.0` isn't a public IP address, but it's a way to listen on + // all interfaces. Thus, we consider it as a public endpoint and warn about it. + if endpoint.rpc_methods == RpcMethods::Unsafe && endpoint.is_global() || + endpoint.listen_addr.ip().is_unspecified() + { + log::warn!( + "It isn't safe to expose RPC publicly without a proxy server that filters \ + available set of RPC methods." + ); + } + } + + return Ok(Some(self.experimental_rpc_endpoint.clone())); + } + + let (ipv4, ipv6) = rpc_interface( + self.rpc_external, + self.unsafe_rpc_external, + self.rpc_methods, + is_validator, + )?; + + let cors = self.rpc_cors(is_dev)?; + let port = self.rpc_port.unwrap_or(default_listen_port); + + Ok(Some(vec![ + RpcEndpoint { + batch_config: self.rpc_batch_config()?, + max_connections: self.rpc_max_connections, + listen_addr: SocketAddr::new(std::net::IpAddr::V4(ipv4), port), + rpc_methods: self.rpc_methods, + rate_limit: self.rpc_rate_limit, + rate_limit_trust_proxy_headers: self.rpc_rate_limit_trust_proxy_headers, + rate_limit_whitelisted_ips: self.rpc_rate_limit_whitelisted_ips.clone(), + max_payload_in_mb: self.rpc_max_request_size, + max_payload_out_mb: self.rpc_max_response_size, + max_subscriptions_per_connection: self.rpc_max_subscriptions_per_connection, + max_buffer_capacity_per_connection: self.rpc_message_buffer_capacity_per_connection, + cors: cors.clone(), + retry_random_port: true, + is_optional: false, + }, + RpcEndpoint { + batch_config: self.rpc_batch_config()?, + max_connections: self.rpc_max_connections, + listen_addr: SocketAddr::new(std::net::IpAddr::V6(ipv6), port), + rpc_methods: self.rpc_methods, + rate_limit: self.rpc_rate_limit, + rate_limit_trust_proxy_headers: self.rpc_rate_limit_trust_proxy_headers, + rate_limit_whitelisted_ips: self.rpc_rate_limit_whitelisted_ips.clone(), + max_payload_in_mb: self.rpc_max_request_size, + max_payload_out_mb: self.rpc_max_response_size, + max_subscriptions_per_connection: self.rpc_max_subscriptions_per_connection, + max_buffer_capacity_per_connection: self.rpc_message_buffer_capacity_per_connection, + cors: cors.clone(), + retry_random_port: true, + is_optional: true, + }, + ])) + } + + /// Returns the configuration for batch RPC requests. + pub fn rpc_batch_config(&self) -> crate::Result { + let cfg = if self.rpc_disable_batch_requests { + RpcBatchRequestConfig::Disabled + } else if let Some(l) = self.rpc_max_batch_request_len { + RpcBatchRequestConfig::Limit(l) + } else { + RpcBatchRequestConfig::Unlimited + }; + + Ok(cfg) + } +} + +fn rpc_interface( + is_external: bool, + is_unsafe_external: bool, + rpc_methods: RpcMethods, + is_validator: bool, +) -> crate::Result<(Ipv4Addr, Ipv6Addr)> { + if is_external && is_validator && rpc_methods != RpcMethods::Unsafe { + return Err(crate::Error::Input( + "--rpc-external option shouldn't be used if the node is running as \ + a validator. Use `--unsafe-rpc-external` or `--rpc-methods=unsafe` if you understand \ + the risks. See the options description for more information." + .to_owned(), + )); + } + + if is_external || is_unsafe_external { + if rpc_methods == RpcMethods::Unsafe { + log::warn!( + "It isn't safe to expose RPC publicly without a proxy server that filters \ + available set of RPC methods." + ); + } + + Ok((Ipv4Addr::UNSPECIFIED, Ipv6Addr::UNSPECIFIED)) + } else { + Ok((Ipv4Addr::LOCALHOST, Ipv6Addr::LOCALHOST)) + } +} + /// Represent a single RPC endpoint with its configuration. #[derive(Debug, Clone)] pub struct RpcEndpoint { From ba3814a8bea717e77628da690fcb3d1b6adadc2a Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Thu, 5 Sep 2024 17:21:05 +0800 Subject: [PATCH 2/4] Add prdoc --- prdoc/pr_5601.prdoc | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 prdoc/pr_5601.prdoc diff --git a/prdoc/pr_5601.prdoc b/prdoc/pr_5601.prdoc new file mode 100644 index 000000000000..2f5c7436ccff --- /dev/null +++ b/prdoc/pr_5601.prdoc @@ -0,0 +1,10 @@ +title: Introduce `RpcParams` in sc-cli + +doc: + - audience: Node Dev + description: | + Refactors and consolidates all RPC-related parameters in the run command into a dedicated `RpcParams` struct. This change allows downstream users to build custom run command without duplicating code. + +crates: + - name: sc-cli + bump: major From 18287054068a87600352adb55b43f6e6f3307a0b Mon Sep 17 00:00:00 2001 From: Liu-Cheng Xu Date: Thu, 5 Sep 2024 18:38:53 +0800 Subject: [PATCH 3/4] Fix RpcParams update --- cumulus/client/cli/src/lib.rs | 10 +++++----- prdoc/pr_5601.prdoc | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/cumulus/client/cli/src/lib.rs b/cumulus/client/cli/src/lib.rs index 564d7b58c94d..b08ad75c430d 100644 --- a/cumulus/client/cli/src/lib.rs +++ b/cumulus/client/cli/src/lib.rs @@ -96,7 +96,7 @@ impl PurgeChainCmd { Some('y') | Some('Y') => {}, _ => { println!("Aborted"); - return Ok(()) + return Ok(()); }, } } @@ -432,19 +432,19 @@ impl sc_cli::CliConfiguration for NormalizedRunCmd { } fn rpc_max_request_size(&self) -> sc_cli::Result { - Ok(self.base.rpc_max_request_size) + self.base.rpc_max_request_size() } fn rpc_max_response_size(&self) -> sc_cli::Result { - Ok(self.base.rpc_max_response_size) + self.base.rpc_max_response_size() } fn rpc_max_subscriptions_per_connection(&self) -> sc_cli::Result { - Ok(self.base.rpc_max_subscriptions_per_connection) + self.base.rpc_max_subscriptions_per_connection() } fn rpc_buffer_capacity_per_connection(&self) -> sc_cli::Result { - Ok(self.base.rpc_message_buffer_capacity_per_connection) + Ok(self.base.rpc_params.rpc_message_buffer_capacity_per_connection) } fn rpc_batch_config(&self) -> sc_cli::Result { diff --git a/prdoc/pr_5601.prdoc b/prdoc/pr_5601.prdoc index 2f5c7436ccff..3a0ec9ee8714 100644 --- a/prdoc/pr_5601.prdoc +++ b/prdoc/pr_5601.prdoc @@ -6,5 +6,7 @@ doc: Refactors and consolidates all RPC-related parameters in the run command into a dedicated `RpcParams` struct. This change allows downstream users to build custom run command without duplicating code. crates: + - name: cumulus-client-cli + bump: none - name: sc-cli bump: major From fbb060f523df6a309862d3be20a695cc01fe9ee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Sun, 8 Sep 2024 22:19:28 +0200 Subject: [PATCH 4/4] Apply suggestions from code review --- substrate/client/cli/src/params/rpc_params.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/substrate/client/cli/src/params/rpc_params.rs b/substrate/client/cli/src/params/rpc_params.rs index 181f0b8d9a68..f9937b59bbaf 100644 --- a/substrate/client/cli/src/params/rpc_params.rs +++ b/substrate/client/cli/src/params/rpc_params.rs @@ -227,7 +227,7 @@ impl RpcParams { if endpoint.rpc_methods == RpcMethods::Unsafe && endpoint.is_global() || endpoint.listen_addr.ip().is_unspecified() { - log::warn!( + eprintln!( "It isn't safe to expose RPC publicly without a proxy server that filters \ available set of RPC methods." ); @@ -314,7 +314,7 @@ fn rpc_interface( if is_external || is_unsafe_external { if rpc_methods == RpcMethods::Unsafe { - log::warn!( + eprintln!( "It isn't safe to expose RPC publicly without a proxy server that filters \ available set of RPC methods." );