1use crate::cli::{Cli, Subcommand, NODE_VERSION};
18use frame_benchmarking_cli::{
19 BenchmarkCmd, ExtrinsicFactory, SubstrateRemarkBuilder, SUBSTRATE_REFERENCE_HARDWARE,
20};
21use futures::future::TryFutureExt;
22use log::{info, warn};
23use polkadot_service::{
24 self,
25 benchmarking::{benchmark_inherent_data, TransferKeepAliveBuilder},
26 HeaderBackend, IdentifyVariant,
27};
28#[cfg(feature = "pyroscope")]
29use pyroscope_pprofrs::{pprof_backend, PprofConfig};
30use sc_cli::SubstrateCli;
31use sc_network_types::PeerId;
32use sp_core::crypto::Ss58AddressFormatRegistry;
33use sp_keyring::Sr25519Keyring;
34
35pub use crate::error::Error;
36#[cfg(feature = "pyroscope")]
37use std::net::ToSocketAddrs;
38use std::{collections::HashSet, time::Duration};
39
40type Result<T> = std::result::Result<T, Error>;
41
42fn get_exec_name() -> Option<String> {
43 std::env::current_exe()
44 .ok()
45 .and_then(|pb| pb.file_name().map(|s| s.to_os_string()))
46 .and_then(|s| s.into_string().ok())
47}
48
49fn get_invulnerable_ah_collators(
50 chain_spec: &Box<dyn polkadot_service::ChainSpec>,
51) -> HashSet<PeerId> {
52 const KUSAMA: [&str; 11] = [
54 "12D3KooWHNEENyCc4R3iDLLFaJiynUp9eDZp7TtS1G6DCp459vVK",
55 "12D3KooWAVqLdQEjSezy7CPEgMLMSTuyfSBdbxPGkmik5x2aL8u4",
56 "12D3KooWBxMiVQdYa5MaQjSWAu3YsfKdrs7vgX9cPk4cCwFVAXEu",
57 "12D3KooWGbRmQ9FjwkzTVTSxfUh854wxc3LUD5agjzcucDarZrNn",
58 "12D3KooWHwXftCGdp73t4BUxW3c9UKjYTvjc7tHsrinT5M8AUmXo",
59 "12D3KooWCTSAq83D99RcT64rrV5X3sGZxc9JQ8nVtd6GbZEKnDqC",
60 "12D3KooWF63ZxKtZMYs5247WQA8fcTiGJb2osXykc31cmjwNLwem",
61 "12D3KooWGowDwrXAh9cxkbPHPHuwMouFHrMcJhCVXcFS2B8vc5Ry",
62 "12D3KooWRhoxXsZypnp1Tady6XSRqXfxu7Bj6hGk8aj6FJ1iU6pt",
63 "12D3KooWJUs11H7S3Hv9BVh72w3yVmHoYTXaoBUg1KQyYk4hL2bB",
64 "12D3KooWAeLjabo2foz6gAQvLRfwF2d3WnpUGDjhg8V5AQUnv5AZ",
65 ];
66
67 const POLKADOT: [&str; 7] = [
68 "12D3KooWEyGg3oUwYfaLWM5AJ2pvXCUxBuXNapX1tQXLsbDmMV6z",
69 "12D3KooWD9dTKLW65NFFLVjqgaXNzb3zKXBfwRS5iovxV6XaoVX6",
70 "12D3KooWPJfGGisRMkiD5yhySZggEhyMSwELb34P2bEuAmUh9RYy",
71 "12D3KooWQB9RBoJEByMtXtD8aC1WR1DJQb3QMXRcsQmNxrghsQLv",
72 "12D3KooWFhBYG98e53DQB7W2JKBL9xWrP83ANkAjzvp4enEJAt3k",
73 "12D3KooWG3GrM6XKMM4gp3cvemdwUvu96ziYoJmqmetLZBXE8bSa",
74 "12D3KooWMRyTLrCEPcAQD6c4EnudL3vVzg9zji3whvsMYPUYevpq",
75 ];
76
77 let invulnerables = if chain_spec.is_kusama() {
78 KUSAMA.to_vec()
79 } else if chain_spec.is_polkadot() {
80 POLKADOT.to_vec()
81 } else {
82 vec![]
83 };
84
85 invulnerables
86 .iter()
87 .filter_map(|invuln_str| {
88 invuln_str
89 .parse::<PeerId>()
90 .map_err(|e| {
91 warn!("Failed to parse AssetHub invulnerable peer from the default list. This should never happen. {:?}", e)
92 })
93 .ok()
94 })
95 .collect()
96}
97
98impl SubstrateCli for Cli {
99 fn impl_name() -> String {
100 "Parity Polkadot".into()
101 }
102
103 fn impl_version() -> String {
104 let commit_hash = env!("SUBSTRATE_CLI_COMMIT_HASH");
105 format!("{}-{commit_hash}", NODE_VERSION)
106 }
107
108 fn description() -> String {
109 env!("CARGO_PKG_DESCRIPTION").into()
110 }
111
112 fn author() -> String {
113 env!("CARGO_PKG_AUTHORS").into()
114 }
115
116 fn support_url() -> String {
117 "https://github.com/paritytech/polkadot-sdk/issues/new".into()
118 }
119
120 fn copyright_start_year() -> i32 {
121 2017
122 }
123
124 fn executable_name() -> String {
125 "polkadot".into()
126 }
127
128 fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {
129 let id = if id == "" {
130 let n = get_exec_name().unwrap_or_default();
131 ["polkadot", "kusama", "westend", "rococo", "versi"]
132 .iter()
133 .cloned()
134 .find(|&chain| n.starts_with(chain))
135 .unwrap_or("polkadot")
136 } else {
137 id
138 };
139 Ok(match id {
140 "kusama" => Box::new(polkadot_service::chain_spec::kusama_config()?),
141 name if name.starts_with("kusama-") && !name.ends_with(".json") =>
142 Err(format!("`{name}` is not supported anymore as the kusama native runtime no longer part of the node."))?,
143 "polkadot" => Box::new(polkadot_service::chain_spec::polkadot_config()?),
144 name if name.starts_with("polkadot-") && !name.ends_with(".json") =>
145 Err(format!("`{name}` is not supported anymore as the polkadot native runtime no longer part of the node."))?,
146 "paseo" => Box::new(polkadot_service::chain_spec::paseo_config()?),
147 "rococo" => Box::new(polkadot_service::chain_spec::rococo_config()?),
148 #[cfg(feature = "rococo-native")]
149 "dev" | "rococo-dev" => Box::new(polkadot_service::chain_spec::rococo_development_config()?),
150 #[cfg(feature = "rococo-native")]
151 "rococo-local" => Box::new(polkadot_service::chain_spec::rococo_local_testnet_config()?),
152 #[cfg(feature = "rococo-native")]
153 "rococo-staging" => Box::new(polkadot_service::chain_spec::rococo_staging_testnet_config()?),
154 #[cfg(not(feature = "rococo-native"))]
155 name if name.starts_with("rococo-") && !name.ends_with(".json") || name == "dev" =>
156 Err(format!("`{}` only supported with `rococo-native` feature enabled.", name))?,
157 "westend" => Box::new(polkadot_service::chain_spec::westend_config()?),
158 #[cfg(feature = "westend-native")]
159 "westend-dev" => Box::new(polkadot_service::chain_spec::westend_development_config()?),
160 #[cfg(feature = "westend-native")]
161 "westend-local" => Box::new(polkadot_service::chain_spec::westend_local_testnet_config()?),
162 #[cfg(feature = "westend-native")]
163 "westend-staging" => Box::new(polkadot_service::chain_spec::westend_staging_testnet_config()?),
164 #[cfg(feature = "rococo-native")]
165 "versi-dev" => Box::new(polkadot_service::chain_spec::versi_development_config()?),
166 #[cfg(feature = "rococo-native")]
167 "versi-local" => Box::new(polkadot_service::chain_spec::versi_local_testnet_config()?),
168 #[cfg(feature = "rococo-native")]
169 "versi-staging" => Box::new(polkadot_service::chain_spec::versi_staging_testnet_config()?),
170 #[cfg(not(feature = "rococo-native"))]
171 name if name.starts_with("versi-") =>
172 Err(format!("`{}` only supported with `rococo-native` feature enabled.", name))?,
173 path => {
174 let path = std::path::PathBuf::from(path);
175
176 let chain_spec = Box::new(polkadot_service::GenericChainSpec::from_json_file(path.clone())?)
177 as Box<dyn polkadot_service::ChainSpec>;
178
179 if self.run.force_rococo ||
182 chain_spec.is_rococo() ||
183 chain_spec.is_versi()
184 {
185 Box::new(polkadot_service::RococoChainSpec::from_json_file(path)?)
186 } else if self.run.force_kusama || chain_spec.is_kusama() {
187 Box::new(polkadot_service::GenericChainSpec::from_json_file(path)?)
188 } else if self.run.force_westend || chain_spec.is_westend() {
189 Box::new(polkadot_service::WestendChainSpec::from_json_file(path)?)
190 } else {
191 chain_spec
192 }
193 },
194 })
195 }
196}
197
198fn set_default_ss58_version(spec: &Box<dyn polkadot_service::ChainSpec>) {
199 let ss58_version = if spec.is_kusama() {
200 Ss58AddressFormatRegistry::KusamaAccount
201 } else if spec.is_westend() {
202 Ss58AddressFormatRegistry::SubstrateAccount
203 } else {
204 Ss58AddressFormatRegistry::PolkadotAccount
205 }
206 .into();
207
208 sp_core::crypto::set_default_ss58_version(ss58_version);
209}
210
211#[cfg(feature = "malus")]
217pub fn run_node(
218 run: Cli,
219 overseer_gen: impl polkadot_service::OverseerGen,
220 malus_finality_delay: Option<u32>,
221) -> Result<()> {
222 run_node_inner(run, overseer_gen, malus_finality_delay, |_logger_builder, _config| {})
223}
224
225fn run_node_inner<F>(
226 cli: Cli,
227 overseer_gen: impl polkadot_service::OverseerGen,
228 maybe_malus_finality_delay: Option<u32>,
229 logger_hook: F,
230) -> Result<()>
231where
232 F: FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration),
233{
234 let runner = cli
235 .create_runner_with_logger_hook::<_, _, F>(&cli.run.base, logger_hook)
236 .map_err(Error::from)?;
237 let chain_spec = &runner.config().chain_spec;
238
239 let enable_beefy = !cli.run.no_beefy;
241
242 set_default_ss58_version(chain_spec);
243
244 if chain_spec.is_kusama() {
245 info!("----------------------------");
246 info!("This chain is not in any way");
247 info!(" endorsed by the ");
248 info!(" KUSAMA FOUNDATION ");
249 info!("----------------------------");
250 }
251
252 let node_version =
253 if cli.run.disable_worker_version_check { None } else { Some(NODE_VERSION.to_string()) };
254
255 let secure_validator_mode = cli.run.base.validator && !cli.run.insecure_validator;
256
257 let collator_protocol_hold_off = cli.run.collator_protocol_hold_off.map(Duration::from_millis);
259 let invulnerable_ah_collators = get_invulnerable_ah_collators(&chain_spec);
260 let experimental_collator_protocol = cli.run.experimental_collator_protocol;
261
262 runner.run_node_until_exit(move |config| async move {
263 let hwbench = (!cli.run.no_hardware_benchmarks)
264 .then(|| {
265 config.database.path().map(|database_path| {
266 let _ = std::fs::create_dir_all(&database_path);
267 sc_sysinfo::gather_hwbench(Some(database_path), &SUBSTRATE_REFERENCE_HARDWARE)
268 })
269 })
270 .flatten();
271
272 let database_source = config.database.clone();
273 let task_manager = polkadot_service::build_full(
274 config,
275 polkadot_service::NewFullParams {
276 is_parachain_node: polkadot_service::IsParachainNode::No,
277 enable_beefy,
278 force_authoring_backoff: cli.run.force_authoring_backoff,
279 telemetry_worker_handle: None,
280 node_version,
281 secure_validator_mode,
282 workers_path: cli.run.workers_path,
283 workers_names: None,
284 overseer_gen,
285 overseer_message_channel_capacity_override: cli
286 .run
287 .overseer_channel_capacity_override,
288 malus_finality_delay: maybe_malus_finality_delay,
289 hwbench,
290 execute_workers_max_num: cli.run.execute_workers_max_num,
291 prepare_workers_hard_max_num: cli.run.prepare_workers_hard_max_num,
292 prepare_workers_soft_max_num: cli.run.prepare_workers_soft_max_num,
293 keep_finalized_for: cli.run.keep_finalized_for,
294 invulnerable_ah_collators,
295 collator_protocol_hold_off,
296 experimental_collator_protocol,
297 collator_reputation_persist_interval: cli
298 .run
299 .collator_reputation_persist_interval
300 .map(std::time::Duration::from_secs),
301 },
302 )
303 .map(|full| full.task_manager)?;
304
305 if let Some(path) = database_source.path() {
306 sc_storage_monitor::StorageMonitorService::try_spawn(
307 cli.storage_monitor,
308 path.to_path_buf(),
309 &task_manager.spawn_essential_handle(),
310 )?;
311 }
312
313 Ok(task_manager)
314 })
315}
316
317pub fn run() -> Result<()> {
319 let cli: Cli = Cli::from_args();
320
321 #[cfg(feature = "pyroscope")]
322 let mut pyroscope_agent_maybe = if let Some(ref agent_addr) = cli.run.pyroscope_server {
323 let address = agent_addr
324 .to_socket_addrs()
325 .map_err(Error::AddressResolutionFailure)?
326 .next()
327 .ok_or_else(|| Error::AddressResolutionMissing)?;
328 let agent = pyroscope::PyroscopeAgent::builder(
330 "http://".to_owned() + address.to_string().as_str(),
331 "polkadot".to_owned(),
332 )
333 .backend(pprof_backend(PprofConfig::new().sample_rate(113)))
334 .build()?;
335 Some(agent.start()?)
336 } else {
337 None
338 };
339
340 #[cfg(not(feature = "pyroscope"))]
341 if cli.run.pyroscope_server.is_some() {
342 return Err(Error::PyroscopeNotCompiledIn);
343 }
344
345 match &cli.subcommand {
346 None => run_node_inner(
347 cli,
348 polkadot_service::ValidatorOverseerGen,
349 None,
350 polkadot_node_metrics::logger_hook(),
351 ),
352 #[allow(deprecated)]
353 Some(Subcommand::BuildSpec(cmd)) => {
354 let runner = cli.create_runner(cmd)?;
355 Ok(runner.sync_run(|config| cmd.run(config.chain_spec, config.network))?)
356 },
357 Some(Subcommand::ExportChainSpec(cmd)) => {
358 let spec = cli.load_spec(&cmd.chain)?;
360 cmd.run(spec).map_err(Into::into)
361 },
362 Some(Subcommand::CheckBlock(cmd)) => {
363 let runner = cli.create_runner(cmd).map_err(Error::SubstrateCli)?;
364 let chain_spec = &runner.config().chain_spec;
365
366 set_default_ss58_version(chain_spec);
367
368 runner.async_run(|mut config| {
369 let (client, _, import_queue, task_manager) =
370 polkadot_service::new_chain_ops(&mut config)?;
371 Ok((cmd.run(client, import_queue).map_err(Error::SubstrateCli), task_manager))
372 })
373 },
374 Some(Subcommand::ExportBlocks(cmd)) => {
375 let runner = cli.create_runner(cmd)?;
376 let chain_spec = &runner.config().chain_spec;
377
378 set_default_ss58_version(chain_spec);
379
380 Ok(runner.async_run(|mut config| {
381 let (client, _, _, task_manager) =
382 polkadot_service::new_chain_ops(&mut config).map_err(Error::PolkadotService)?;
383 Ok((cmd.run(client, config.database).map_err(Error::SubstrateCli), task_manager))
384 })?)
385 },
386 Some(Subcommand::ExportState(cmd)) => {
387 let runner = cli.create_runner(cmd)?;
388 let chain_spec = &runner.config().chain_spec;
389
390 set_default_ss58_version(chain_spec);
391
392 Ok(runner.async_run(|mut config| {
393 let (client, _, _, task_manager) = polkadot_service::new_chain_ops(&mut config)?;
394 Ok((cmd.run(client, config.chain_spec).map_err(Error::SubstrateCli), task_manager))
395 })?)
396 },
397 Some(Subcommand::ImportBlocks(cmd)) => {
398 let runner = cli.create_runner(cmd)?;
399 let chain_spec = &runner.config().chain_spec;
400
401 set_default_ss58_version(chain_spec);
402
403 Ok(runner.async_run(|mut config| {
404 let (client, _, import_queue, task_manager) =
405 polkadot_service::new_chain_ops(&mut config)?;
406 Ok((cmd.run(client, import_queue).map_err(Error::SubstrateCli), task_manager))
407 })?)
408 },
409 Some(Subcommand::PurgeChain(cmd)) => {
410 let runner = cli.create_runner(cmd)?;
411 Ok(runner.sync_run(|config| cmd.run(config.database))?)
412 },
413 Some(Subcommand::Revert(cmd)) => {
414 let runner = cli.create_runner(cmd)?;
415 let chain_spec = &runner.config().chain_spec;
416
417 set_default_ss58_version(chain_spec);
418
419 Ok(runner.async_run(|mut config| {
420 let (client, backend, _, task_manager) =
421 polkadot_service::new_chain_ops(&mut config)?;
422 let task_handle = task_manager.spawn_handle();
423 let aux_revert = Box::new(|client, backend, blocks| {
424 polkadot_service::revert_backend(client, backend, blocks, config, task_handle)
425 .map_err(|err| {
426 match err {
427 polkadot_service::Error::Blockchain(err) => err.into(),
428 err => sc_cli::Error::Application(err.into()),
430 }
431 })
432 });
433 Ok((
434 cmd.run(client, backend, Some(aux_revert)).map_err(Error::SubstrateCli),
435 task_manager,
436 ))
437 })?)
438 },
439 Some(Subcommand::Benchmark(cmd)) => {
440 let runner = cli.create_runner(cmd)?;
441 let chain_spec = &runner.config().chain_spec;
442
443 match cmd {
444 #[cfg(not(feature = "runtime-benchmarks"))]
445 BenchmarkCmd::Storage(_) =>
446 return Err(sc_cli::Error::Input(
447 "Compile with --features=runtime-benchmarks \
448 to enable storage benchmarks."
449 .into(),
450 )
451 .into()),
452 #[cfg(feature = "runtime-benchmarks")]
453 BenchmarkCmd::Storage(cmd) => runner.sync_run(|mut config| {
454 let (client, backend, _, _) = polkadot_service::new_chain_ops(&mut config)?;
455 let db = backend.expose_db();
456 let storage = backend.expose_storage();
457 let shared_trie_cache = backend.expose_shared_trie_cache();
458
459 cmd.run(config, client.clone(), db, storage, shared_trie_cache).map_err(Error::SubstrateCli)
460 }),
461 BenchmarkCmd::Block(cmd) => runner.sync_run(|mut config| {
462 let (client, _, _, _) = polkadot_service::new_chain_ops(&mut config)?;
463
464 cmd.run(client.clone()).map_err(Error::SubstrateCli)
465 }),
466 BenchmarkCmd::Overhead(cmd) => runner.sync_run(|config| {
467 if cmd.params.runtime.is_some() {
468 return Err(sc_cli::Error::Input(
469 "Polkadot binary does not support `--runtime` flag for `benchmark overhead`. Please provide a chain spec or use the `frame-omni-bencher`."
470 .into(),
471 )
472 .into())
473 }
474
475 cmd.run_with_default_builder_and_spec::<polkadot_service::Block, ()>(
476 Some(config.chain_spec),
477 )
478 .map_err(Error::SubstrateCli)
479 }),
480 BenchmarkCmd::Extrinsic(cmd) => runner.sync_run(|mut config| {
481 let (client, _, _, _) = polkadot_service::new_chain_ops(&mut config)?;
482 let header = client.header(client.info().genesis_hash).unwrap().unwrap();
483 let inherent_data = benchmark_inherent_data(header)
484 .map_err(|e| format!("generating inherent data: {:?}", e))?;
485
486 let remark_builder = SubstrateRemarkBuilder::new_from_client(client.clone())?;
487
488 let tka_builder = TransferKeepAliveBuilder::new(
489 client.clone(),
490 Sr25519Keyring::Alice.to_account_id(),
491 config.chain_spec.identify_chain(),
492 );
493
494 let ext_factory =
495 ExtrinsicFactory(vec![Box::new(remark_builder), Box::new(tka_builder)]);
496
497 cmd.run(client.clone(), inherent_data, Vec::new(), &ext_factory)
498 .map_err(Error::SubstrateCli)
499 }),
500 BenchmarkCmd::Pallet(cmd) => {
501 set_default_ss58_version(chain_spec);
502
503 if cfg!(feature = "runtime-benchmarks") {
504 runner.sync_run(|config| {
505 cmd.run_with_spec::<sp_runtime::traits::HashingFor<polkadot_service::Block>, ()>(
506 Some(config.chain_spec),
507 )
508 .map_err(|e| Error::SubstrateCli(e))
509 })
510 } else {
511 Err(sc_cli::Error::Input(
512 "Benchmarking wasn't enabled when building the node. \
513 You can enable it with `--features runtime-benchmarks`."
514 .into(),
515 )
516 .into())
517 }
518 },
519 BenchmarkCmd::Machine(cmd) => runner.sync_run(|config| {
520 cmd.run(&config, SUBSTRATE_REFERENCE_HARDWARE.clone())
521 .map_err(Error::SubstrateCli)
522 }),
523 #[allow(unreachable_patterns)]
526 _ => Err(Error::CommandNotImplemented),
527 }
528 },
529 Some(Subcommand::Key(cmd)) => Ok(cmd.run(&cli)?),
530 Some(Subcommand::ChainInfo(cmd)) => {
531 let runner = cli.create_runner(cmd)?;
532 Ok(runner.sync_run(|config| cmd.run::<polkadot_service::Block>(&config))?)
533 },
534 }?;
535
536 #[cfg(feature = "pyroscope")]
537 if let Some(pyroscope_agent) = pyroscope_agent_maybe.take() {
538 let agent = pyroscope_agent.stop()?;
539 agent.shutdown();
540 }
541 Ok(())
542}