1use crate::{
20 error::{Error, Result},
21 params::{
22 ImportParams, KeystoreParams, NetworkParams, OffchainWorkerParams, RpcEndpoint,
23 SharedParams, TransactionPoolParams,
24 },
25 CliConfiguration, PrometheusParams, RpcParams, RuntimeParams, TelemetryParams,
26};
27use clap::Parser;
28use regex::Regex;
29use sc_service::{
30 config::{
31 BasePath, IpNetwork, PrometheusConfig, RpcBatchRequestConfig, TransactionPoolOptions,
32 },
33 ChainSpec, Role,
34};
35use sc_telemetry::TelemetryEndpoints;
36use std::num::NonZeroU32;
37
38#[derive(Debug, Clone, Parser)]
40pub struct RunCmd {
41 #[arg(long)]
47 pub validator: bool,
48
49 #[arg(long)]
54 pub no_grandpa: bool,
55
56 #[arg(long, value_name = "NAME")]
60 pub name: Option<String>,
61
62 #[allow(missing_docs)]
63 #[clap(flatten)]
64 pub rpc_params: RpcParams,
65
66 #[allow(missing_docs)]
67 #[clap(flatten)]
68 pub telemetry_params: TelemetryParams,
69
70 #[allow(missing_docs)]
71 #[clap(flatten)]
72 pub prometheus_params: PrometheusParams,
73
74 #[allow(missing_docs)]
75 #[clap(flatten)]
76 pub runtime_params: RuntimeParams,
77
78 #[allow(missing_docs)]
79 #[clap(flatten)]
80 pub offchain_worker_params: OffchainWorkerParams,
81
82 #[allow(missing_docs)]
83 #[clap(flatten)]
84 pub shared_params: SharedParams,
85
86 #[allow(missing_docs)]
87 #[clap(flatten)]
88 pub import_params: ImportParams,
89
90 #[allow(missing_docs)]
91 #[clap(flatten)]
92 pub network_params: NetworkParams,
93
94 #[allow(missing_docs)]
95 #[clap(flatten)]
96 pub pool_config: TransactionPoolParams,
97
98 #[allow(missing_docs)]
99 #[clap(flatten)]
100 pub keystore_params: KeystoreParams,
101
102 #[arg(long, conflicts_with_all = &["bob", "charlie", "dave", "eve", "ferdie", "one", "two"])]
106 pub alice: bool,
107
108 #[arg(long, conflicts_with_all = &["alice", "charlie", "dave", "eve", "ferdie", "one", "two"])]
112 pub bob: bool,
113
114 #[arg(long, conflicts_with_all = &["alice", "bob", "dave", "eve", "ferdie", "one", "two"])]
118 pub charlie: bool,
119
120 #[arg(long, conflicts_with_all = &["alice", "bob", "charlie", "eve", "ferdie", "one", "two"])]
124 pub dave: bool,
125
126 #[arg(long, conflicts_with_all = &["alice", "bob", "charlie", "dave", "ferdie", "one", "two"])]
130 pub eve: bool,
131
132 #[arg(long, conflicts_with_all = &["alice", "bob", "charlie", "dave", "eve", "one", "two"])]
136 pub ferdie: bool,
137
138 #[arg(long, conflicts_with_all = &["alice", "bob", "charlie", "dave", "eve", "ferdie", "two"])]
142 pub one: bool,
143
144 #[arg(long, conflicts_with_all = &["alice", "bob", "charlie", "dave", "eve", "ferdie", "one"])]
148 pub two: bool,
149
150 #[arg(long)]
152 pub force_authoring: bool,
153
154 #[arg(long, conflicts_with = "base_path")]
164 pub tmp: bool,
165}
166
167impl RunCmd {
168 pub fn get_keyring(&self) -> Option<sp_keyring::Sr25519Keyring> {
170 use sp_keyring::Sr25519Keyring::*;
171
172 if self.alice {
173 Some(Alice)
174 } else if self.bob {
175 Some(Bob)
176 } else if self.charlie {
177 Some(Charlie)
178 } else if self.dave {
179 Some(Dave)
180 } else if self.eve {
181 Some(Eve)
182 } else if self.ferdie {
183 Some(Ferdie)
184 } else if self.one {
185 Some(One)
186 } else if self.two {
187 Some(Two)
188 } else {
189 None
190 }
191 }
192}
193
194impl CliConfiguration for RunCmd {
195 fn shared_params(&self) -> &SharedParams {
196 &self.shared_params
197 }
198
199 fn import_params(&self) -> Option<&ImportParams> {
200 Some(&self.import_params)
201 }
202
203 fn network_params(&self) -> Option<&NetworkParams> {
204 let network_params = &self.network_params;
205 let is_authority = self.role(self.is_dev().ok()?).ok()?.is_authority();
206 if is_authority && network_params.public_addr.is_empty() {
207 eprintln!(
208 "WARNING: No public address specified, validator node may not be reachable.
209 Consider setting `--public-addr` to the public IP address of this node.
210 This will become a hard requirement in future versions."
211 );
212 }
213
214 Some(network_params)
215 }
216
217 fn keystore_params(&self) -> Option<&KeystoreParams> {
218 Some(&self.keystore_params)
219 }
220
221 fn offchain_worker_params(&self) -> Option<&OffchainWorkerParams> {
222 Some(&self.offchain_worker_params)
223 }
224
225 fn node_name(&self) -> Result<String> {
226 let name: String = match (self.name.as_ref(), self.get_keyring()) {
227 (Some(name), _) => name.to_string(),
228 (_, Some(keyring)) => keyring.to_string(),
229 (None, None) => crate::generate_node_name(),
230 };
231
232 is_node_name_valid(&name).map_err(|msg| {
233 Error::Input(format!(
234 "Invalid node name '{}'. Reason: {}. If unsure, use none.",
235 name, msg
236 ))
237 })?;
238
239 Ok(name)
240 }
241
242 fn dev_key_seed(&self, is_dev: bool) -> Result<Option<String>> {
243 Ok(self.get_keyring().map(|a| format!("//{}", a)).or_else(|| {
244 if is_dev {
245 Some("//Alice".into())
246 } else {
247 None
248 }
249 }))
250 }
251
252 fn telemetry_endpoints(
253 &self,
254 chain_spec: &Box<dyn ChainSpec>,
255 ) -> Result<Option<TelemetryEndpoints>> {
256 let params = &self.telemetry_params;
257 Ok(if params.no_telemetry {
258 None
259 } else if !params.telemetry_endpoints.is_empty() {
260 Some(
261 TelemetryEndpoints::new(params.telemetry_endpoints.clone())
262 .map_err(|e| e.to_string())?,
263 )
264 } else {
265 chain_spec.telemetry_endpoints().clone()
266 })
267 }
268
269 fn role(&self, is_dev: bool) -> Result<Role> {
270 let keyring = self.get_keyring();
271 let is_authority = self.validator || is_dev || keyring.is_some();
272
273 Ok(if is_authority { Role::Authority } else { Role::Full })
274 }
275
276 fn force_authoring(&self) -> Result<bool> {
277 Ok(self.shared_params.dev || self.force_authoring)
279 }
280
281 fn prometheus_config(
282 &self,
283 default_listen_port: u16,
284 chain_spec: &Box<dyn ChainSpec>,
285 ) -> Result<Option<PrometheusConfig>> {
286 Ok(self
287 .prometheus_params
288 .prometheus_config(default_listen_port, chain_spec.id().to_string()))
289 }
290
291 fn disable_grandpa(&self) -> Result<bool> {
292 Ok(self.no_grandpa)
293 }
294
295 fn rpc_max_connections(&self) -> Result<u32> {
296 Ok(self.rpc_params.rpc_max_connections)
297 }
298
299 fn rpc_cors(&self, is_dev: bool) -> Result<Option<Vec<String>>> {
300 self.rpc_params.rpc_cors(is_dev)
301 }
302
303 fn rpc_addr(&self, default_listen_port: u16) -> Result<Option<Vec<RpcEndpoint>>> {
304 self.rpc_params.rpc_addr(self.is_dev()?, self.validator, default_listen_port)
305 }
306
307 fn rpc_methods(&self) -> Result<sc_service::config::RpcMethods> {
308 Ok(self.rpc_params.rpc_methods.into())
309 }
310
311 fn rpc_max_request_size(&self) -> Result<u32> {
312 Ok(self.rpc_params.rpc_max_request_size)
313 }
314
315 fn rpc_max_response_size(&self) -> Result<u32> {
316 Ok(self.rpc_params.rpc_max_response_size)
317 }
318
319 fn rpc_max_subscriptions_per_connection(&self) -> Result<u32> {
320 Ok(self.rpc_params.rpc_max_subscriptions_per_connection)
321 }
322
323 fn rpc_buffer_capacity_per_connection(&self) -> Result<u32> {
324 Ok(self.rpc_params.rpc_message_buffer_capacity_per_connection)
325 }
326
327 fn rpc_batch_config(&self) -> Result<RpcBatchRequestConfig> {
328 self.rpc_params.rpc_batch_config()
329 }
330
331 fn rpc_rate_limit(&self) -> Result<Option<NonZeroU32>> {
332 Ok(self.rpc_params.rpc_rate_limit)
333 }
334
335 fn rpc_rate_limit_whitelisted_ips(&self) -> Result<Vec<IpNetwork>> {
336 Ok(self.rpc_params.rpc_rate_limit_whitelisted_ips.clone())
337 }
338
339 fn rpc_rate_limit_trust_proxy_headers(&self) -> Result<bool> {
340 Ok(self.rpc_params.rpc_rate_limit_trust_proxy_headers)
341 }
342
343 fn transaction_pool(&self, is_dev: bool) -> Result<TransactionPoolOptions> {
344 Ok(self.pool_config.transaction_pool(is_dev))
345 }
346
347 fn max_runtime_instances(&self) -> Result<Option<usize>> {
348 Ok(Some(self.runtime_params.max_runtime_instances))
349 }
350
351 fn runtime_cache_size(&self) -> Result<u8> {
352 Ok(self.runtime_params.runtime_cache_size)
353 }
354
355 fn base_path(&self) -> Result<Option<BasePath>> {
356 Ok(if self.tmp {
357 Some(BasePath::new_temp_dir()?)
358 } else {
359 match self.shared_params().base_path()? {
360 Some(r) => Some(r),
361 None if self.shared_params().is_dev() => Some(BasePath::new_temp_dir()?),
363 None => None,
364 }
365 })
366 }
367}
368
369pub fn is_node_name_valid(_name: &str) -> std::result::Result<(), &str> {
371 let name = _name.to_string();
372
373 if name.is_empty() {
374 return Err("Node name cannot be empty");
375 }
376
377 if name.chars().count() >= crate::NODE_NAME_MAX_LENGTH {
378 return Err("Node name too long");
379 }
380
381 let invalid_chars = r"[\\.@]";
382 let re = Regex::new(invalid_chars).unwrap();
383 if re.is_match(&name) {
384 return Err("Node name should not contain invalid chars such as '.' and '@'");
385 }
386
387 let invalid_patterns = r"^https?:";
388 let re = Regex::new(invalid_patterns).unwrap();
389 if re.is_match(&name) {
390 return Err("Node name should not contain urls");
391 }
392
393 Ok(())
394}
395
396#[cfg(test)]
397mod tests {
398 use super::*;
399
400 #[test]
401 fn tests_node_name_good() {
402 assert!(is_node_name_valid("short name").is_ok());
403 assert!(is_node_name_valid("www").is_ok());
404 assert!(is_node_name_valid("aawww").is_ok());
405 assert!(is_node_name_valid("wwwaa").is_ok());
406 assert!(is_node_name_valid("www aa").is_ok());
407 }
408
409 #[test]
410 fn tests_node_name_bad() {
411 assert!(is_node_name_valid("").is_err());
412 assert!(is_node_name_valid(
413 "very very long names are really not very cool for the ui at all, really they're not"
414 )
415 .is_err());
416 assert!(is_node_name_valid("Dots.not.Ok").is_err());
417 assert!(is_node_name_valid("http://visitme").is_err());
420 assert!(is_node_name_valid("http:/visitme").is_err());
421 assert!(is_node_name_valid("http:visitme").is_err());
422 assert!(is_node_name_valid("https://visitme").is_err());
423 assert!(is_node_name_valid("https:/visitme").is_err());
424 assert!(is_node_name_valid("https:visitme").is_err());
425 assert!(is_node_name_valid("www.visit.me").is_err());
426 assert!(is_node_name_valid("www.visit").is_err());
427 assert!(is_node_name_valid("hello\\world").is_err());
428 assert!(is_node_name_valid("visit.www").is_err());
429 assert!(is_node_name_valid("email@domain").is_err());
430 }
431}