moonbeam_cli/
command.rs

1// Copyright 2019-2025 PureStake Inc.
2// This file is part of Moonbeam.
3
4// Moonbeam is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Moonbeam is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16
17//! This module constructs and executes the appropriate service components for the given subcommand
18
19use crate::cli::{Cli, RelayChainCli, RunCmd, Subcommand};
20use cumulus_client_cli::extract_genesis_wasm;
21use cumulus_primitives_core::ParaId;
22use frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE};
23use log::{info, warn};
24use moonbeam_cli_opt::EthApi;
25
26#[cfg(feature = "moonbase-native")]
27use moonbeam_service::moonbase_runtime;
28#[cfg(feature = "moonbeam-native")]
29use moonbeam_service::moonbeam_runtime;
30#[cfg(feature = "moonriver-native")]
31use moonbeam_service::moonriver_runtime;
32
33use moonbeam_service::{
34	chain_spec, frontier_database_dir, lazy_loading, HostFunctions, IdentifyVariant,
35};
36use parity_scale_codec::Encode;
37use sc_cli::{
38	ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams,
39	NetworkParams, Result, RpcEndpoint, RuntimeVersion, SharedParams, SubstrateCli,
40};
41use sc_service::{
42	config::{BasePath, PrometheusConfig},
43	DatabaseSource, PartialComponents,
44};
45use sp_core::hexdisplay::HexDisplay;
46use sp_runtime::{
47	traits::{
48		AccountIdConversion, Block as BlockT, Hash as HashT, HashingFor, Header as HeaderT, Zero,
49	},
50	StateVersion,
51};
52use std::io::Write;
53
54fn load_spec(
55	id: &str,
56	para_id: ParaId,
57	run_cmd: &RunCmd,
58) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {
59	Ok(match id {
60		// Moonbase networks
61		"moonbase-alpha" | "alphanet" => Box::new(chain_spec::RawChainSpec::from_json_bytes(
62			&include_bytes!("../../../specs/alphanet/parachain-embedded-specs-v8.json")[..],
63		)?),
64		#[cfg(feature = "moonbase-native")]
65		"moonbase-local" => Box::new(chain_spec::moonbase::get_chain_spec(para_id)),
66		#[cfg(feature = "moonbase-native")]
67		"moonbase-dev" | "dev" | "development" => {
68			Box::new(chain_spec::moonbase::development_chain_spec(None, None))
69		}
70		#[cfg(feature = "moonbeam-native")]
71		"staking" => Box::new(chain_spec::moonbeam::get_chain_spec(para_id)),
72		// Moonriver networks
73		"moonriver" => Box::new(chain_spec::RawChainSpec::from_json_bytes(
74			&include_bytes!("../../../specs/moonriver/parachain-embedded-specs.json")[..],
75		)?),
76		#[cfg(feature = "moonriver-native")]
77		"moonriver-dev" => Box::new(chain_spec::moonriver::development_chain_spec(None, None)),
78		#[cfg(feature = "moonriver-native")]
79		"moonriver-local" => Box::new(chain_spec::moonriver::get_chain_spec(para_id)),
80
81		// Moonbeam networks
82		"moonbeam" | "" => Box::new(chain_spec::RawChainSpec::from_json_bytes(
83			&include_bytes!("../../../specs/moonbeam/parachain-embedded-specs.json")[..],
84		)?),
85		#[cfg(feature = "moonbeam-native")]
86		"moonbeam-dev" => Box::new(chain_spec::moonbeam::development_chain_spec(None, None)),
87		#[cfg(feature = "moonbeam-native")]
88		"moonbeam-local" => Box::new(chain_spec::moonbeam::get_chain_spec(para_id)),
89
90		// Specs provided as json specify which runtime to use in their file name. For example,
91		// `moonbeam-custom.json` uses the moonbeam runtime.
92		// `moonbase-dev-workshop.json` uses the moonbase runtime.
93		// If no magic strings match, then the moonbase runtime is used by default.
94		// TODO explore CLI options to make this nicer. eg `--force-moonriver-runtime`
95		path => {
96			let path = std::path::PathBuf::from(path);
97
98			let starts_with = |prefix: &str| {
99				path.file_name()
100					.and_then(|f| f.to_str().map(|s| s.starts_with(&prefix)))
101					.unwrap_or(false)
102			};
103
104			if run_cmd.force_moonbase || starts_with("moonbase") {
105				Box::new(chain_spec::moonbase::ChainSpec::from_json_file(path)?)
106			} else if run_cmd.force_moonriver || starts_with("moonriver") {
107				Box::new(chain_spec::moonriver::ChainSpec::from_json_file(path)?)
108			} else {
109				Box::new(chain_spec::moonbeam::ChainSpec::from_json_file(path)?)
110			}
111		}
112	})
113}
114
115impl SubstrateCli for Cli {
116	fn impl_name() -> String {
117		"Moonbeam Parachain Collator".into()
118	}
119
120	fn impl_version() -> String {
121		env!("SUBSTRATE_CLI_IMPL_VERSION").into()
122	}
123
124	fn description() -> String {
125		format!(
126			"Moonbase Parachain Collator\n\nThe command-line arguments provided first will be \
127		passed to the parachain node, while the arguments provided after -- will be passed \
128		to the relaychain node.\n\n\
129		{} [parachain-args] -- [relaychain-args]",
130			Self::executable_name()
131		)
132	}
133
134	fn author() -> String {
135		env!("CARGO_PKG_AUTHORS").into()
136	}
137
138	fn support_url() -> String {
139		"https://github.com/moonbeam-foundation/moonbeam/issues/new".into()
140	}
141
142	fn copyright_start_year() -> i32 {
143		2019
144	}
145
146	fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {
147		load_spec(id, self.run.parachain_id.unwrap_or(1000).into(), &self.run)
148	}
149}
150
151impl Cli {
152	fn runtime_version(spec: &Box<dyn sc_service::ChainSpec>) -> &'static RuntimeVersion {
153		match spec {
154			#[cfg(feature = "moonriver-native")]
155			spec if spec.is_moonriver() => return &moonbeam_service::moonriver_runtime::VERSION,
156			#[cfg(feature = "moonbeam-native")]
157			spec if spec.is_moonbeam() => return &moonbeam_service::moonbeam_runtime::VERSION,
158			#[cfg(feature = "moonbase-native")]
159			_ => return &moonbeam_service::moonbase_runtime::VERSION,
160			#[cfg(not(feature = "moonbase-native"))]
161			_ => panic!("invalid chain spec"),
162		}
163	}
164}
165
166impl SubstrateCli for RelayChainCli {
167	fn impl_name() -> String {
168		"Moonbeam Parachain Collator".into()
169	}
170
171	fn impl_version() -> String {
172		env!("SUBSTRATE_CLI_IMPL_VERSION").into()
173	}
174
175	fn description() -> String {
176		"Moonbeam Parachain Collator\n\nThe command-line arguments provided first will be \
177		passed to the parachain node, while the arguments provided after -- will be passed \
178		to the relaychain node.\n\n\
179		parachain-collator [parachain-args] -- [relaychain-args]"
180			.into()
181	}
182
183	fn author() -> String {
184		env!("CARGO_PKG_AUTHORS").into()
185	}
186
187	fn support_url() -> String {
188		"https://github.com/moonbeam-foundation/moonbeam/issues/new".into()
189	}
190
191	fn copyright_start_year() -> i32 {
192		2019
193	}
194
195	fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {
196		match id {
197			"westend_moonbase_relay_testnet" => Ok(Box::new(
198				polkadot_service::WestendChainSpec::from_json_bytes(
199					&include_bytes!("../../../specs/alphanet/westend-embedded-specs-v8.json")[..],
200				)?,
201			)),
202			// If we are not using a moonbeam-centric pre-baked relay spec, then fall back to the
203			// Polkadot service to interpret the id.
204			_ => polkadot_cli::Cli::from_iter([RelayChainCli::executable_name()].iter())
205				.load_spec(id),
206		}
207	}
208}
209
210fn validate_trace_environment(cli: &Cli) -> Result<()> {
211	if (cli.run.ethapi.contains(&EthApi::Debug) || cli.run.ethapi.contains(&EthApi::Trace))
212		&& cli
213			.run
214			.base
215			.base
216			.import_params
217			.wasm_runtime_overrides
218			.is_none()
219	{
220		return Err(
221			"`debug` or `trace` namespaces requires `--wasm-runtime-overrides /path/to/overrides`."
222				.into(),
223		);
224	}
225	Ok(())
226}
227
228/// Parse command line arguments into service configuration.
229pub fn run() -> Result<()> {
230	let cli = Cli::from_args();
231	let _ = validate_trace_environment(&cli)?;
232
233	match &cli.subcommand {
234		Some(Subcommand::BuildSpec(params)) => {
235			let runner = cli.create_runner(&params.base)?;
236			runner.sync_run(|config| {
237				if params.mnemonic.is_some() || params.accounts.is_some() {
238					if config.chain_spec.is_moonbeam() {
239						params.base.run(
240							Box::new(chain_spec::moonbeam::development_chain_spec(
241								params.mnemonic.clone(),
242								params.accounts,
243							)),
244							config.network,
245						)
246					} else if config.chain_spec.is_moonriver() {
247						params.base.run(
248							Box::new(chain_spec::moonriver::development_chain_spec(
249								params.mnemonic.clone(),
250								params.accounts,
251							)),
252							config.network,
253						)
254					} else {
255						params.base.run(
256							Box::new(chain_spec::moonbase::development_chain_spec(
257								params.mnemonic.clone(),
258								params.accounts,
259							)),
260							config.network,
261						)
262					}
263				} else {
264					params.base.run(config.chain_spec, config.network)
265				}
266			})
267		}
268		Some(Subcommand::CheckBlock(cmd)) => {
269			let runner = cli.create_runner(cmd)?;
270			let rpc_config = cli.run.new_rpc_config();
271			runner.async_run(|mut config| {
272				let (client, _, import_queue, task_manager) = moonbeam_service::new_chain_ops(
273					&mut config,
274					&rpc_config,
275					cli.node_extra_args(),
276				)?;
277				Ok((cmd.run(client, import_queue), task_manager))
278			})
279		}
280		Some(Subcommand::ExportBlocks(cmd)) => {
281			let runner = cli.create_runner(cmd)?;
282			let rpc_config = cli.run.new_rpc_config();
283			runner.async_run(|mut config| {
284				let (client, _, _, task_manager) = moonbeam_service::new_chain_ops(
285					&mut config,
286					&rpc_config,
287					cli.node_extra_args(),
288				)?;
289				Ok((cmd.run(client, config.database), task_manager))
290			})
291		}
292		Some(Subcommand::ExportState(cmd)) => {
293			let runner = cli.create_runner(cmd)?;
294			let rpc_config = cli.run.new_rpc_config();
295			runner.async_run(|mut config| {
296				let (client, _, _, task_manager) = moonbeam_service::new_chain_ops(
297					&mut config,
298					&rpc_config,
299					cli.node_extra_args(),
300				)?;
301				Ok((cmd.run(client, config.chain_spec), task_manager))
302			})
303		}
304		Some(Subcommand::ImportBlocks(cmd)) => {
305			let runner = cli.create_runner(cmd)?;
306			let rpc_config = cli.run.new_rpc_config();
307			runner.async_run(|mut config| {
308				let (client, _, import_queue, task_manager) = moonbeam_service::new_chain_ops(
309					&mut config,
310					&rpc_config,
311					cli.node_extra_args(),
312				)?;
313				Ok((cmd.run(client, import_queue), task_manager))
314			})
315		}
316		Some(Subcommand::PurgeChain(cmd)) => {
317			let runner = cli.create_runner(cmd)?;
318			runner.sync_run(|config| {
319				// Although the cumulus_client_cli::PurgeCommand will extract the relay chain id,
320				// we need to extract it here to determine whether we are running the dev service.
321				let extension = chain_spec::Extensions::try_get(&*config.chain_spec);
322				let relay_chain_id = extension.map(|e| e.relay_chain.as_str());
323				let dev_service = cli.run.dev_service || relay_chain_id == Some("dev-service");
324
325				// Remove Frontier offchain db
326				let frontier_database_config = match config.database {
327					DatabaseSource::RocksDb { .. } => DatabaseSource::RocksDb {
328						path: frontier_database_dir(&config, "db"),
329						cache_size: 0,
330					},
331					DatabaseSource::ParityDb { .. } => DatabaseSource::ParityDb {
332						path: frontier_database_dir(&config, "paritydb"),
333					},
334					_ => {
335						return Err(format!("Cannot purge `{:?}` database", config.database).into())
336					}
337				};
338				cmd.base.run(frontier_database_config)?;
339
340				if dev_service {
341					// base refers to the encapsulated "regular" sc_cli::PurgeChain command
342					return cmd.base.run(config.database);
343				}
344
345				let polkadot_cli = RelayChainCli::new(
346					&config,
347					[RelayChainCli::executable_name().to_string()]
348						.iter()
349						.chain(cli.relaychain_args.iter()),
350				);
351
352				let polkadot_config = SubstrateCli::create_configuration(
353					&polkadot_cli,
354					&polkadot_cli,
355					config.tokio_handle.clone(),
356				)
357				.map_err(|err| format!("Relay chain argument error: {}", err))?;
358
359				cmd.run(config, polkadot_config)
360			})
361		}
362		Some(Subcommand::Revert(cmd)) => {
363			let runner = cli.create_runner(cmd)?;
364			let chain_spec = &runner.config().chain_spec;
365			let rpc_config = cli.run.new_rpc_config();
366			match chain_spec {
367				#[cfg(feature = "moonriver-native")]
368				spec if spec.is_moonriver() => runner.async_run(|mut config| {
369					let params = moonbeam_service::new_partial::<
370						moonbeam_service::moonriver_runtime::RuntimeApi,
371						moonbeam_service::MoonriverCustomizations,
372					>(&mut config, &rpc_config, cli.node_extra_args())?;
373
374					Ok((
375						cmd.run(params.client, params.backend, None),
376						params.task_manager,
377					))
378				}),
379				#[cfg(feature = "moonbeam-native")]
380				spec if spec.is_moonbeam() => runner.async_run(|mut config| {
381					let params = moonbeam_service::new_partial::<
382						moonbeam_service::moonbeam_runtime::RuntimeApi,
383						moonbeam_service::MoonbeamCustomizations,
384					>(&mut config, &rpc_config, cli.node_extra_args())?;
385
386					Ok((
387						cmd.run(params.client, params.backend, None),
388						params.task_manager,
389					))
390				}),
391				#[cfg(feature = "moonbase-native")]
392				_ => runner.async_run(|mut config| {
393					let params = moonbeam_service::new_partial::<
394						moonbeam_service::moonbase_runtime::RuntimeApi,
395						moonbeam_service::MoonbaseCustomizations,
396					>(&mut config, &rpc_config, cli.node_extra_args())?;
397
398					Ok((
399						cmd.run(params.client, params.backend, None),
400						params.task_manager,
401					))
402				}),
403				#[cfg(not(feature = "moonbase-native"))]
404				_ => panic!("invalid chain spec"),
405			}
406		}
407		Some(Subcommand::ExportGenesisHead(params)) => {
408			let mut builder = sc_cli::LoggerBuilder::new("");
409			builder.with_profiling(sc_tracing::TracingReceiver::Log, "");
410			let _ = builder.init();
411
412			// Cumulus approach here, we directly call the generic load_spec func
413			let chain_spec = load_spec(
414				params.chain.as_deref().unwrap_or_default(),
415				params.parachain_id.unwrap_or(1000).into(),
416				&cli.run,
417			)?;
418			let state_version = Cli::runtime_version(&chain_spec).state_version();
419
420			let output_buf = match chain_spec {
421				#[cfg(feature = "moonriver-native")]
422				chain_spec if chain_spec.is_moonriver() => {
423					let block: moonbeam_service::moonriver_runtime::Block =
424						generate_genesis_block(&*chain_spec, state_version)?;
425					let raw_header = block.header().encode();
426					let output_buf = if params.raw {
427						raw_header
428					} else {
429						format!("0x{:?}", HexDisplay::from(&block.header().encode())).into_bytes()
430					};
431					output_buf
432				}
433				#[cfg(feature = "moonbeam-native")]
434				chain_spec if chain_spec.is_moonbeam() => {
435					let block: moonbeam_service::moonbeam_runtime::Block =
436						generate_genesis_block(&*chain_spec, state_version)?;
437					let raw_header = block.header().encode();
438					let output_buf = if params.raw {
439						raw_header
440					} else {
441						format!("0x{:?}", HexDisplay::from(&block.header().encode())).into_bytes()
442					};
443					output_buf
444				}
445				#[cfg(feature = "moonbase-native")]
446				_ => {
447					let block: moonbeam_service::moonbase_runtime::Block =
448						generate_genesis_block(&*chain_spec, state_version)?;
449					let raw_header = block.header().encode();
450					let output_buf = if params.raw {
451						raw_header
452					} else {
453						format!("0x{:?}", HexDisplay::from(&block.header().encode())).into_bytes()
454					};
455					output_buf
456				}
457				#[cfg(not(feature = "moonbase-native"))]
458				_ => panic!("invalid chain spec"),
459			};
460
461			if let Some(output) = &params.output {
462				std::fs::write(output, output_buf)?;
463			} else {
464				std::io::stdout().write_all(&output_buf)?;
465			}
466
467			Ok(())
468		}
469		Some(Subcommand::ExportGenesisWasm(params)) => {
470			let mut builder = sc_cli::LoggerBuilder::new("");
471			builder.with_profiling(sc_tracing::TracingReceiver::Log, "");
472			let _ = builder.init();
473
474			let raw_wasm_blob = extract_genesis_wasm(
475				&*cli.load_spec(params.chain.as_deref().unwrap_or_default())?,
476			)?;
477			let output_buf = if params.raw {
478				raw_wasm_blob
479			} else {
480				format!("0x{:?}", HexDisplay::from(&raw_wasm_blob)).into_bytes()
481			};
482
483			if let Some(output) = &params.output {
484				std::fs::write(output, output_buf)?;
485			} else {
486				std::io::stdout().write_all(&output_buf)?;
487			}
488
489			Ok(())
490		}
491		Some(Subcommand::Benchmark(cmd)) => {
492			let runner = cli.create_runner(cmd)?;
493
494			// Switch on the concrete benchmark sub-command
495			match cmd {
496				BenchmarkCmd::Pallet(cmd) => {
497					if cfg!(feature = "runtime-benchmarks") {
498						let chain_spec = &runner.config().chain_spec;
499						match chain_spec {
500							#[cfg(feature = "moonriver-native")]
501							spec if spec.is_moonriver() => {
502								return runner.sync_run(|config| {
503									cmd.run_with_spec::<HashingFor<moonriver_runtime::Block>, HostFunctions>(
504										Some(config.chain_spec),
505									)
506								})
507							}
508							#[cfg(feature = "moonbeam-native")]
509							spec if spec.is_moonbeam() => {
510								return runner.sync_run(|config| {
511									cmd.run_with_spec::<HashingFor<moonbeam_runtime::Block>, HostFunctions>(
512										Some(config.chain_spec),
513									)
514								})
515							}
516							#[cfg(feature = "moonbase-native")]
517							_ => {
518								return runner.sync_run(|config| {
519									cmd.run_with_spec::<HashingFor<moonbase_runtime::Block>, HostFunctions>(
520										Some(config.chain_spec),
521									)
522								})
523							}
524							#[cfg(not(feature = "moonbase-native"))]
525							_ => panic!("invalid chain spec"),
526						}
527					} else {
528						Err("Benchmarking wasn't enabled when building the node. \
529					You can enable it with `--features runtime-benchmarks`."
530							.into())
531					}
532				}
533				BenchmarkCmd::Block(cmd) => {
534					let chain_spec = &runner.config().chain_spec;
535					let rpc_config = cli.run.new_rpc_config();
536					match chain_spec {
537						#[cfg(feature = "moonriver-native")]
538						spec if spec.is_moonriver() => {
539							return runner.sync_run(|mut config| {
540								let params =
541									moonbeam_service::new_partial::<
542										moonbeam_service::moonriver_runtime::RuntimeApi,
543										moonbeam_service::MoonriverCustomizations,
544									>(&mut config, &rpc_config, cli.node_extra_args())?;
545
546								cmd.run(params.client)
547							})
548						}
549						#[cfg(feature = "moonbeam-native")]
550						spec if spec.is_moonbeam() => {
551							return runner.sync_run(|mut config| {
552								let params =
553									moonbeam_service::new_partial::<
554										moonbeam_service::moonbeam_runtime::RuntimeApi,
555										moonbeam_service::MoonbeamCustomizations,
556									>(&mut config, &rpc_config, cli.node_extra_args())?;
557
558								cmd.run(params.client)
559							})
560						}
561						#[cfg(feature = "moonbase-native")]
562						_ => {
563							return runner.sync_run(|mut config| {
564								let params =
565									moonbeam_service::new_partial::<
566										moonbeam_service::moonbase_runtime::RuntimeApi,
567										moonbeam_service::MoonbaseCustomizations,
568									>(&mut config, &rpc_config, cli.node_extra_args())?;
569
570								cmd.run(params.client)
571							})
572						}
573						#[cfg(not(feature = "moonbase-native"))]
574						_ => panic!("invalid chain spec"),
575					}
576				}
577				#[cfg(not(feature = "runtime-benchmarks"))]
578				BenchmarkCmd::Storage(_) => Err(
579					"Storage benchmarking can be enabled with `--features runtime-benchmarks`."
580						.into(),
581				),
582				#[cfg(feature = "runtime-benchmarks")]
583				BenchmarkCmd::Storage(cmd) => {
584					let chain_spec = &runner.config().chain_spec;
585					let rpc_config = cli.run.new_rpc_config();
586					match chain_spec {
587						#[cfg(feature = "moonriver-native")]
588						spec if spec.is_moonriver() => {
589							return runner.sync_run(|mut config| {
590								let params =
591									moonbeam_service::new_partial::<
592										moonbeam_service::moonriver_runtime::RuntimeApi,
593										moonbeam_service::MoonriverCustomizations,
594									>(&mut config, &rpc_config, cli.node_extra_args())?;
595
596								let db = params.backend.expose_db();
597								let storage = params.backend.expose_storage();
598
599								cmd.run(config, params.client, db, storage, None)
600							})
601						}
602						#[cfg(feature = "moonbeam-native")]
603						spec if spec.is_moonbeam() => {
604							return runner.sync_run(|mut config| {
605								let params =
606									moonbeam_service::new_partial::<
607										moonbeam_service::moonbeam_runtime::RuntimeApi,
608										moonbeam_service::MoonbeamCustomizations,
609									>(&mut config, &rpc_config, cli.node_extra_args())?;
610
611								let db = params.backend.expose_db();
612								let storage = params.backend.expose_storage();
613
614								cmd.run(config, params.client, db, storage, None)
615							})
616						}
617						#[cfg(feature = "moonbase-native")]
618						_ => {
619							return runner.sync_run(|mut config| {
620								let params =
621									moonbeam_service::new_partial::<
622										moonbeam_service::moonbase_runtime::RuntimeApi,
623										moonbeam_service::MoonbaseCustomizations,
624									>(&mut config, &rpc_config, cli.node_extra_args())?;
625
626								let db = params.backend.expose_db();
627								let storage = params.backend.expose_storage();
628
629								cmd.run(config, params.client, db, storage, None)
630							})
631						}
632						#[cfg(not(feature = "moonbase-native"))]
633						_ => panic!("invalid chain spec"),
634					}
635				}
636				BenchmarkCmd::Overhead(_) => Err("Unsupported benchmarking command".into()),
637				BenchmarkCmd::Extrinsic(_) => Err("Unsupported benchmarking command".into()),
638				BenchmarkCmd::Machine(cmd) => {
639					return runner.sync_run(|config| {
640						cmd.run(
641							&config,
642							frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE.clone(),
643						)
644					});
645				}
646			}
647		}
648		Some(Subcommand::TryRuntime) => Err("The `try-runtime` subcommand has been migrated to a \
649			standalone CLI (https://github.com/paritytech/try-runtime-cli). It is no longer \
650			being maintained here and will be removed entirely some time after January 2024. \
651			Please remove this subcommand from your runtime and use the standalone CLI."
652			.into()),
653		Some(Subcommand::Key(cmd)) => Ok(cmd.run(&cli)?),
654		Some(Subcommand::PrecompileWasm(cmd)) => {
655			let runner = cli.create_runner(cmd)?;
656			let rpc_config = cli.run.new_rpc_config();
657			runner.async_run(|mut config| match &config.chain_spec {
658				#[cfg(feature = "moonriver-native")]
659				spec if spec.is_moonriver() => {
660					let PartialComponents {
661						task_manager,
662						backend,
663						..
664					} = moonbeam_service::new_partial::<
665						moonbeam_service::moonriver_runtime::RuntimeApi,
666						moonbeam_service::MoonriverCustomizations,
667					>(&mut config, &rpc_config, cli.node_extra_args())?;
668
669					Ok((cmd.run(backend, config.chain_spec), task_manager))
670				}
671				#[cfg(feature = "moonbeam-native")]
672				spec if spec.is_moonbeam() => {
673					let PartialComponents {
674						task_manager,
675						backend,
676						..
677					} = moonbeam_service::new_partial::<
678						moonbeam_service::moonbeam_runtime::RuntimeApi,
679						moonbeam_service::MoonbeamCustomizations,
680					>(&mut config, &rpc_config, cli.node_extra_args())?;
681
682					Ok((cmd.run(backend, config.chain_spec), task_manager))
683				}
684				#[cfg(feature = "moonbase-native")]
685				_ => {
686					let PartialComponents {
687						task_manager,
688						backend,
689						..
690					} = moonbeam_service::new_partial::<
691						moonbeam_service::moonbase_runtime::RuntimeApi,
692						moonbeam_service::MoonbaseCustomizations,
693					>(&mut config, &rpc_config, cli.node_extra_args())?;
694
695					Ok((cmd.run(backend, config.chain_spec), task_manager))
696				}
697				#[cfg(not(feature = "moonbase-native"))]
698				_ => panic!("invalid chain spec"),
699			})
700		}
701		None => {
702			let runner = cli.create_runner(&(*cli.run).normalize())?;
703			let collator_options = cli.run.collator_options();
704
705			runner.run_node_until_exit(|mut config| async move {
706				let hwbench = if !cli.run.no_hardware_benchmarks {
707					config.database.path().map(|database_path| {
708						let _ = std::fs::create_dir_all(&database_path);
709						sc_sysinfo::gather_hwbench(
710							Some(database_path),
711							&SUBSTRATE_REFERENCE_HARDWARE,
712						)
713					})
714				} else {
715					None
716				};
717
718				let extension = chain_spec::Extensions::try_get(&*config.chain_spec);
719
720				let rpc_config = cli.run.new_rpc_config();
721
722				// If dev service was requested, start up manual or instant seal.
723				// Otherwise continue with the normal parachain node.
724				// Dev service can be requested in two ways.
725				// 1. by providing the --dev-service flag to the CLI
726				// 2. by specifying "dev-service" in the chain spec's "relay-chain" field.
727				// NOTE: the --dev flag triggers the dev service by way of number 2
728				let relay_chain_id = extension.map(|e| e.relay_chain.as_str());
729				let para_id = extension.map(|e| e.para_id);
730
731				let dev_service = cli.run.dev_service
732					|| config.chain_spec.is_dev()
733					|| relay_chain_id == Some("dev-service");
734				if dev_service {
735					// When running the dev service, just use Alice's author inherent
736					//TODO maybe make the --alice etc flags work here, and consider bringing back
737					// the author-id flag. For now, this will work.
738					let author_id = Some(chain_spec::get_from_seed::<nimbus_primitives::NimbusId>(
739						"Alice",
740					));
741
742					return match &config.chain_spec {
743						#[cfg(feature = "moonriver-native")]
744						spec if spec.is_moonriver() => moonbeam_service::new_dev::<
745							moonbeam_service::moonriver_runtime::RuntimeApi,
746							moonbeam_service::MoonriverCustomizations,
747							sc_network::NetworkWorker<_, _>,
748						>(
749							config,
750							para_id,
751							author_id,
752							cli.run.sealing,
753							rpc_config,
754							hwbench,
755							cli.node_extra_args(),
756						)
757						.await
758						.map_err(Into::into),
759						#[cfg(feature = "moonbeam-native")]
760						spec if spec.is_moonbeam() => moonbeam_service::new_dev::<
761							moonbeam_service::moonbeam_runtime::RuntimeApi,
762							moonbeam_service::MoonbeamCustomizations,
763							sc_network::NetworkWorker<_, _>,
764						>(
765							config,
766							para_id,
767							author_id,
768							cli.run.sealing,
769							rpc_config,
770							hwbench,
771							cli.node_extra_args(),
772						)
773						.await
774						.map_err(Into::into),
775						#[cfg(feature = "moonbase-native")]
776						_ => moonbeam_service::new_dev::<
777							moonbeam_service::moonbase_runtime::RuntimeApi,
778							moonbeam_service::MoonbaseCustomizations,
779							sc_network::NetworkWorker<_, _>,
780						>(
781							config,
782							para_id,
783							author_id,
784							cli.run.sealing,
785							rpc_config,
786							hwbench,
787							cli.node_extra_args(),
788						)
789						.await
790						.map_err(Into::into),
791						#[cfg(not(feature = "moonbase-native"))]
792						_ => panic!("invalid chain spec"),
793					};
794				}
795				#[cfg(feature = "lazy-loading")]
796				if let Some(lazy_loading_remote_rpc) = cli.run.lazy_loading_remote_rpc {
797					let author_id = Some(chain_spec::get_from_seed::<nimbus_primitives::NimbusId>(
798						"Alice",
799					));
800
801					let lazy_loading_config = moonbeam_cli_opt::LazyLoadingConfig {
802						state_rpc: lazy_loading_remote_rpc,
803						from_block: cli.run.lazy_loading_block,
804						state_overrides_path: cli.run.lazy_loading_state_overrides,
805						runtime_override: cli.run.lazy_loading_runtime_override,
806						delay_between_requests: cli.run.lazy_loading_delay_between_requests,
807						max_retries_per_request: cli.run.lazy_loading_max_retries_per_request,
808					};
809
810					let spec_builder = lazy_loading::spec_builder();
811					config.chain_spec = Box::new(spec_builder.build());
812
813					// TODO: create a tokio runtime inside offchain_worker thread (otherwise it will panic)
814					// We just disable it for now, since it is not needed
815					config.offchain_worker.enabled = false;
816
817					return moonbeam_service::lazy_loading::new_lazy_loading_service::<
818						moonbeam_runtime::RuntimeApi,
819						moonbeam_service::MoonbeamCustomizations,
820						sc_network::NetworkWorker<_, _>,
821					>(
822						config,
823						author_id,
824						cli.run.sealing,
825						rpc_config,
826						lazy_loading_config,
827						hwbench,
828					)
829					.await
830					.map_err(Into::into);
831				}
832
833				let polkadot_cli = RelayChainCli::new(
834					&config,
835					[RelayChainCli::executable_name().to_string()]
836						.iter()
837						.chain(cli.relaychain_args.iter()),
838				);
839
840				let para_id = extension.map(|e| e.para_id);
841				let id = ParaId::from(cli.run.parachain_id.clone().or(para_id).unwrap_or(1000));
842
843				let parachain_account =
844					AccountIdConversion::<polkadot_primitives::v8::AccountId>::into_account_truncating(&id);
845
846				let tokio_handle = config.tokio_handle.clone();
847				let polkadot_config =
848					SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, tokio_handle)
849						.map_err(|err| format!("Relay chain argument error: {}", err))?;
850
851				info!("Parachain Account: {}", parachain_account);
852				info!(
853					"Is collating: {}",
854					if config.role.is_authority() {
855						"yes"
856					} else {
857						"no"
858					}
859				);
860
861				if !rpc_config.relay_chain_rpc_urls.is_empty() && cli.relaychain_args.len() > 0 {
862					warn!(
863						"Detected relay chain node arguments together with \
864					--relay-chain-rpc-url. This command starts a minimal Polkadot node that only \
865					uses a network-related subset of all relay chain CLI options."
866					);
867				}
868
869				match &config.chain_spec {
870					#[cfg(feature = "moonriver-native")]
871					spec if spec.is_moonriver() => moonbeam_service::start_node::<
872						moonbeam_service::moonriver_runtime::RuntimeApi,
873						moonbeam_service::MoonriverCustomizations,
874					>(
875						config,
876						polkadot_config,
877						collator_options,
878						id,
879						rpc_config,
880						cli.run.block_authoring_duration,
881						hwbench,
882						cli.node_extra_args(),
883					)
884					.await
885					.map(|r| r.0)
886					.map_err(Into::into),
887					#[cfg(feature = "moonbeam-native")]
888					spec if spec.is_moonbeam() => moonbeam_service::start_node::<
889						moonbeam_service::moonbeam_runtime::RuntimeApi,
890						moonbeam_service::MoonbeamCustomizations,
891					>(
892						config,
893						polkadot_config,
894						collator_options,
895						id,
896						rpc_config,
897						cli.run.block_authoring_duration,
898						hwbench,
899						cli.node_extra_args(),
900					)
901					.await
902					.map(|r| r.0)
903					.map_err(Into::into),
904					#[cfg(feature = "moonbase-native")]
905					_ => moonbeam_service::start_node::<
906						moonbeam_service::moonbase_runtime::RuntimeApi,
907						moonbeam_service::MoonbaseCustomizations,
908					>(
909						config,
910						polkadot_config,
911						collator_options,
912						id,
913						rpc_config,
914						cli.run.block_authoring_duration,
915						hwbench,
916						cli.node_extra_args(),
917					)
918					.await
919					.map(|r| r.0)
920					.map_err(Into::into),
921					#[cfg(not(feature = "moonbase-native"))]
922					_ => panic!("invalid chain spec"),
923				}
924			})
925		}
926	}
927}
928
929impl DefaultConfigurationValues for RelayChainCli {
930	fn p2p_listen_port() -> u16 {
931		30334
932	}
933
934	fn rpc_listen_port() -> u16 {
935		9945
936	}
937
938	fn prometheus_listen_port() -> u16 {
939		9616
940	}
941}
942
943impl CliConfiguration<Self> for RelayChainCli {
944	fn shared_params(&self) -> &SharedParams {
945		self.base.base.shared_params()
946	}
947
948	fn import_params(&self) -> Option<&ImportParams> {
949		self.base.base.import_params()
950	}
951
952	fn network_params(&self) -> Option<&NetworkParams> {
953		self.base.base.network_params()
954	}
955
956	fn keystore_params(&self) -> Option<&KeystoreParams> {
957		self.base.base.keystore_params()
958	}
959
960	fn base_path(&self) -> Result<Option<BasePath>> {
961		Ok(self
962			.shared_params()
963			.base_path()?
964			.or_else(|| Some(self.base_path.clone().into())))
965	}
966
967	fn rpc_addr(&self, default_listen_port: u16) -> Result<Option<Vec<RpcEndpoint>>> {
968		self.base.base.rpc_addr(default_listen_port)
969	}
970
971	fn prometheus_config(
972		&self,
973		default_listen_port: u16,
974		chain_spec: &Box<dyn ChainSpec>,
975	) -> Result<Option<PrometheusConfig>> {
976		self.base
977			.base
978			.prometheus_config(default_listen_port, chain_spec)
979	}
980
981	fn init<F>(&self, _support_url: &String, _impl_version: &String, _logger_hook: F) -> Result<()>
982	where
983		F: FnOnce(&mut sc_cli::LoggerBuilder),
984	{
985		unreachable!("PolkadotCli is never initialized; qed");
986	}
987
988	fn chain_id(&self, is_dev: bool) -> Result<String> {
989		let chain_id = self.base.base.chain_id(is_dev)?;
990
991		Ok(if chain_id.is_empty() {
992			self.chain_id.clone().unwrap_or_default()
993		} else {
994			chain_id
995		})
996	}
997
998	fn role(&self, is_dev: bool) -> Result<sc_service::Role> {
999		self.base.base.role(is_dev)
1000	}
1001
1002	fn transaction_pool(&self, is_dev: bool) -> Result<sc_service::config::TransactionPoolOptions> {
1003		self.base.base.transaction_pool(is_dev)
1004	}
1005
1006	fn rpc_methods(&self) -> Result<sc_service::config::RpcMethods> {
1007		self.base.base.rpc_methods()
1008	}
1009
1010	fn rpc_max_connections(&self) -> Result<u32> {
1011		self.base.base.rpc_max_connections()
1012	}
1013
1014	fn rpc_cors(&self, is_dev: bool) -> Result<Option<Vec<String>>> {
1015		self.base.base.rpc_cors(is_dev)
1016	}
1017
1018	// fn telemetry_external_transport(&self) -> Result<Option<sc_service::config::ExtTransport>> {
1019	// 	self.base.base.telemetry_external_transport()
1020	// }
1021
1022	fn default_heap_pages(&self) -> Result<Option<u64>> {
1023		self.base.base.default_heap_pages()
1024	}
1025
1026	fn force_authoring(&self) -> Result<bool> {
1027		self.base.base.force_authoring()
1028	}
1029
1030	fn disable_grandpa(&self) -> Result<bool> {
1031		self.base.base.disable_grandpa()
1032	}
1033
1034	fn max_runtime_instances(&self) -> Result<Option<usize>> {
1035		self.base.base.max_runtime_instances()
1036	}
1037
1038	fn announce_block(&self) -> Result<bool> {
1039		self.base.base.announce_block()
1040	}
1041}
1042
1043/// Generate the genesis block from a given ChainSpec.
1044pub fn generate_genesis_block<Block: BlockT>(
1045	chain_spec: &dyn ChainSpec,
1046	genesis_state_version: StateVersion,
1047) -> std::result::Result<Block, String> {
1048	let storage = chain_spec.build_storage()?;
1049
1050	let child_roots = storage.children_default.iter().map(|(sk, child_content)| {
1051		let state_root = <<<Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
1052			child_content.data.clone().into_iter().collect(),
1053			genesis_state_version,
1054		);
1055		(sk.clone(), state_root.encode())
1056	});
1057	let state_root = <<<Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
1058		storage.top.clone().into_iter().chain(child_roots).collect(),
1059		genesis_state_version,
1060	);
1061
1062	let extrinsics_root = <<<Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
1063		Vec::new(),
1064		genesis_state_version,
1065	);
1066
1067	Ok(Block::new(
1068		<<Block as BlockT>::Header as HeaderT>::new(
1069			Zero::zero(),
1070			extrinsics_root,
1071			state_root,
1072			Default::default(),
1073			Default::default(),
1074		),
1075		Default::default(),
1076	))
1077}