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