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