1234567891011121314151617181920212223242526272829303132333435use std::time::Duration;3637use codec::Encode;38use cumulus_client_cli::generate_genesis_block;39use cumulus_primitives_core::ParaId;40use log::{debug, info};41use sc_cli::{42 ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams,43 NetworkParams, Result, RuntimeVersion, SharedParams, SubstrateCli,44};45use sc_service::config::{BasePath, PrometheusConfig};46use sp_core::hexdisplay::HexDisplay;47use sp_runtime::traits::{AccountIdConversion, Block as BlockT};48use up_common::types::opaque::{Block, RuntimeId};4950#[cfg(feature = "runtime-benchmarks")]51use crate::chain_spec::default_runtime;52#[cfg(feature = "runtime-benchmarks")]53use crate::service::DefaultRuntimeExecutor;54#[cfg(feature = "quartz-runtime")]55use crate::service::QuartzRuntimeExecutor;56#[cfg(feature = "unique-runtime")]57use crate::service::UniqueRuntimeExecutor;58use crate::{59 chain_spec::{self, RuntimeIdentification, ServiceId, ServiceIdentification},60 cli::{Cli, RelayChainCli, Subcommand},61 service::{new_partial, start_dev_node, start_node, OpalRuntimeExecutor},62};6364macro_rules! no_runtime_err {65 ($runtime_id:expr) => {66 format!(67 "No runtime valid runtime was found for chain {:#?}",68 $runtime_id69 )70 };71}7273fn load_spec(id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {74 Ok(match id {75 "dev" => Box::new(chain_spec::development_config()),76 "" | "local" => Box::new(chain_spec::local_testnet_config()),77 path => {78 let path = std::path::PathBuf::from(path);79 #[allow(clippy::redundant_clone)]80 let chain_spec = Box::new(chain_spec::OpalChainSpec::from_json_file(path.clone())?)81 as Box<dyn sc_service::ChainSpec>;8283 match chain_spec.runtime_id() {84 #[cfg(feature = "unique-runtime")]85 RuntimeId::Unique => Box::new(chain_spec::UniqueChainSpec::from_json_file(path)?),8687 #[cfg(feature = "quartz-runtime")]88 RuntimeId::Quartz => Box::new(chain_spec::QuartzChainSpec::from_json_file(path)?),8990 RuntimeId::Opal => chain_spec,91 runtime_id => return Err(no_runtime_err!(runtime_id)),92 }93 }94 })95}9697impl SubstrateCli for Cli {98 99 fn impl_name() -> String {100 format!("{} Node", Self::node_name())101 }102103 fn impl_version() -> String {104 env!("SUBSTRATE_CLI_IMPL_VERSION").into()105 }106 107 fn description() -> String {108 format!(109 "{} Node\n\nThe command-line arguments provided first will be \110 passed to the parachain node, while the arguments provided after -- will be passed \111 to the relaychain node.\n\n\112 {} [parachain-args] -- [relaychain-args]",113 Self::node_name(),114 Self::executable_name()115 )116 }117118 fn author() -> String {119 env!("CARGO_PKG_AUTHORS").into()120 }121122 123 fn support_url() -> String {124 "support@unique.network".into()125 }126127 fn copyright_start_year() -> i32 {128 2019129 }130131 fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {132 load_spec(id)133 }134}135136impl SubstrateCli for RelayChainCli {137 138 fn impl_name() -> String {139 format!("{} Node", Cli::node_name())140 }141142 fn impl_version() -> String {143 env!("SUBSTRATE_CLI_IMPL_VERSION").into()144 }145 146 fn description() -> String {147 format!(148 "{} Node\n\nThe command-line arguments provided first will be \149 passed to the parachain node, while the arguments provided after -- will be passed \150 to the relaychain node.\n\n\151 parachain-collator [parachain-args] -- [relaychain-args]",152 Cli::node_name()153 )154 }155156 fn author() -> String {157 env!("CARGO_PKG_AUTHORS").into()158 }159 160 fn support_url() -> String {161 "support@unique.network".into()162 }163164 fn copyright_start_year() -> i32 {165 2019166 }167168 fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {169 polkadot_cli::Cli::from_iter([RelayChainCli::executable_name()].iter()).load_spec(id)170 }171}172173macro_rules! async_run_with_runtime {174 (175 $runtime:path, $runtime_api:path, $executor:path,176 $runner:ident, $components:ident, $cli:ident, $cmd:ident, $config:ident,177 $( $code:tt )*178 ) => {179 $runner.async_run(|$config| {180 let $components = new_partial::<181 $runtime, $runtime_api, $executor, _182 >(183 &$config,184 crate::service::parachain_build_import_queue::<$runtime, _, _>,185 )?;186 let task_manager = $components.task_manager;187188 { $( $code )* }.map(|v| (v, task_manager))189 })190 };191}192193macro_rules! construct_async_run {194 (|$components:ident, $cli:ident, $cmd:ident, $config:ident| $( $code:tt )* ) => {{195 let runner = $cli.create_runner($cmd)?;196197 match runner.config().chain_spec.runtime_id() {198 #[cfg(feature = "unique-runtime")]199 RuntimeId::Unique => async_run_with_runtime!(200 unique_runtime::Runtime, unique_runtime::RuntimeApi, UniqueRuntimeExecutor,201 runner, $components, $cli, $cmd, $config, $( $code )*202 ),203204 #[cfg(feature = "quartz-runtime")]205 RuntimeId::Quartz => async_run_with_runtime!(206 quartz_runtime::Runtime, quartz_runtime::RuntimeApi, QuartzRuntimeExecutor,207 runner, $components, $cli, $cmd, $config, $( $code )*208 ),209210 RuntimeId::Opal => async_run_with_runtime!(211 opal_runtime::Runtime, opal_runtime::RuntimeApi, OpalRuntimeExecutor,212 runner, $components, $cli, $cmd, $config, $( $code )*213 ),214215 runtime_id => Err(no_runtime_err!(runtime_id).into())216 }217 }}218}219220macro_rules! sync_run_with_runtime {221 (222 $runtime:path, $runtime_api:path, $executor:path,223 $runner:ident, $components:ident, $cli:ident, $cmd:ident, $config:ident,224 $( $code:tt )*225 ) => {226 $runner.sync_run(|$config| {227 let $components = new_partial::<228 $runtime, $runtime_api, $executor, _229 >(230 &$config,231 crate::service::parachain_build_import_queue::<$runtime, _, _>,232 )?;233234 $( $code )*235 })236 };237}238239macro_rules! construct_sync_run {240 (|$components:ident, $cli:ident, $cmd:ident, $config:ident| $( $code:tt )* ) => {{241 let runner = $cli.create_runner($cmd)?;242243 match runner.config().chain_spec.runtime_id() {244 #[cfg(feature = "unique-runtime")]245 RuntimeId::Unique => sync_run_with_runtime!(246 unique_runtime::Runtime, unique_runtime::RuntimeApi, UniqueRuntimeExecutor,247 runner, $components, $cli, $cmd, $config, $( $code )*248 ),249250 #[cfg(feature = "quartz-runtime")]251 RuntimeId::Quartz => sync_run_with_runtime!(252 quartz_runtime::Runtime, quartz_runtime::RuntimeApi, QuartzRuntimeExecutor,253 runner, $components, $cli, $cmd, $config, $( $code )*254 ),255256 RuntimeId::Opal => sync_run_with_runtime!(257 opal_runtime::Runtime, opal_runtime::RuntimeApi, OpalRuntimeExecutor,258 runner, $components, $cli, $cmd, $config, $( $code )*259 ),260261 runtime_id => Err(no_runtime_err!(runtime_id).into())262 }263 }}264}265266macro_rules! start_node_using_chain_runtime {267 ($start_node_fn:ident($config:expr $(, $($args:expr),+)?) $($code:tt)*) => {268 match $config.chain_spec.runtime_id() {269 #[cfg(feature = "unique-runtime")]270 RuntimeId::Unique => $start_node_fn::<271 unique_runtime::Runtime,272 unique_runtime::RuntimeApi,273 UniqueRuntimeExecutor,274 >($config $(, $($args),+)?) $($code)*,275276 #[cfg(feature = "quartz-runtime")]277 RuntimeId::Quartz => $start_node_fn::<278 quartz_runtime::Runtime,279 quartz_runtime::RuntimeApi,280 QuartzRuntimeExecutor,281 >($config $(, $($args),+)?) $($code)*,282283 RuntimeId::Opal => $start_node_fn::<284 opal_runtime::Runtime,285 opal_runtime::RuntimeApi,286 OpalRuntimeExecutor,287 >($config $(, $($args),+)?) $($code)*,288289 runtime_id => Err(no_runtime_err!(runtime_id).into()),290 }291 };292}293294295pub fn run() -> Result<()> {296 let cli = Cli::from_args();297298 match &cli.subcommand {299 Some(Subcommand::Key(cmd)) => cmd.run(&cli),300 Some(Subcommand::BuildSpec(cmd)) => {301 let runner = cli.create_runner(cmd)?;302 runner.sync_run(|config| cmd.run(config.chain_spec, config.network))303 }304 Some(Subcommand::CheckBlock(cmd)) => {305 construct_async_run!(|components, cli, cmd, config| {306 Ok(cmd.run(components.client, components.import_queue))307 })308 }309 Some(Subcommand::ExportBlocks(cmd)) => {310 construct_async_run!(|components, cli, cmd, config| {311 Ok(cmd.run(components.client, config.database))312 })313 }314 Some(Subcommand::ExportState(cmd)) => {315 construct_async_run!(|components, cli, cmd, config| {316 Ok(cmd.run(components.client, config.chain_spec))317 })318 }319 Some(Subcommand::ImportBlocks(cmd)) => {320 construct_async_run!(|components, cli, cmd, config| {321 Ok(cmd.run(components.client, components.import_queue))322 })323 }324 Some(Subcommand::PurgeChain(cmd)) => {325 let runner = cli.create_runner(cmd)?;326327 runner.sync_run(|config| {328 let polkadot_cli = RelayChainCli::new(329 &config,330 [RelayChainCli::executable_name()]331 .iter()332 .chain(cli.relaychain_args.iter()),333 );334335 let polkadot_config = SubstrateCli::create_configuration(336 &polkadot_cli,337 &polkadot_cli,338 config.tokio_handle.clone(),339 )340 .map_err(|err| format!("Relay chain argument error: {err}"))?;341342 cmd.run(config, polkadot_config)343 })344 }345 Some(Subcommand::Revert(cmd)) => construct_async_run!(|components, cli, cmd, config| {346 Ok(cmd.run(components.client, components.backend, None))347 }),348 Some(Subcommand::ExportGenesisState(cmd)) => {349 construct_sync_run!(|components, cli, cmd, _config| {350 let spec = cli.load_spec(&cmd.shared_params.chain.clone().unwrap_or_default())?;351 cmd.run(&*spec, &*components.client)352 })353 }354 Some(Subcommand::ExportGenesisWasm(cmd)) => {355 construct_sync_run!(|_components, cli, cmd, _config| {356 let spec = cli.load_spec(&cmd.shared_params.chain.clone().unwrap_or_default())?;357 cmd.run(&*spec)358 })359 }360 #[cfg(feature = "runtime-benchmarks")]361 Some(Subcommand::Benchmark(cmd)) => {362 use frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE};363 let runner = cli.create_runner(cmd)?;364 365 match cmd {366 BenchmarkCmd::Pallet(cmd) => {367 runner.sync_run(|config| cmd.run::<Block, DefaultRuntimeExecutor>(config))368 }369 BenchmarkCmd::Block(cmd) => runner.sync_run(|config| {370 let partials = new_partial::<371 default_runtime::RuntimeApi,372 DefaultRuntimeExecutor,373 _,374 >(&config, crate::service::parachain_build_import_queue)?;375 cmd.run(partials.client)376 }),377 BenchmarkCmd::Storage(cmd) => runner.sync_run(|config| {378 let partials = new_partial::<379 default_runtime::RuntimeApi,380 DefaultRuntimeExecutor,381 _,382 >(&config, crate::service::parachain_build_import_queue)?;383 let db = partials.backend.expose_db();384 let storage = partials.backend.expose_storage();385386 cmd.run(config, partials.client.clone(), db, storage)387 }),388 BenchmarkCmd::Machine(cmd) => {389 runner.sync_run(|config| cmd.run(&config, SUBSTRATE_REFERENCE_HARDWARE.clone()))390 }391 BenchmarkCmd::Overhead(_) | BenchmarkCmd::Extrinsic(_) => {392 Err("Unsupported benchmarking command".into())393 }394 }395 }396 #[cfg(feature = "try-runtime")]397 Some(Subcommand::TryRuntime(cmd)) => {398 use std::{future::Future, pin::Pin};399400 use sc_executor::{sp_wasm_interface::ExtendedHostFunctions, NativeExecutionDispatch};401 use try_runtime_cli::block_building_info::timestamp_with_aura_info;402403 let runner = cli.create_runner(cmd)?;404405 406 let registry = &runner407 .config()408 .prometheus_config409 .as_ref()410 .map(|cfg| &cfg.registry);411 let task_manager =412 sc_service::TaskManager::new(runner.config().tokio_handle.clone(), *registry)413 .map_err(|e| format!("Error: {e:?}"))?;414 let info_provider = Some(timestamp_with_aura_info(12000));415416 runner.async_run(|config| -> Result<(Pin<Box<dyn Future<Output = _>>>, _)> {417 Ok((418 match config.chain_spec.runtime_id() {419 #[cfg(feature = "unique-runtime")]420 RuntimeId::Unique => Box::pin(cmd.run::<Block, ExtendedHostFunctions<421 sp_io::SubstrateHostFunctions,422 <UniqueRuntimeExecutor as NativeExecutionDispatch>::ExtendHostFunctions,423 >, _>(info_provider)),424425 #[cfg(feature = "quartz-runtime")]426 RuntimeId::Quartz => Box::pin(cmd.run::<Block, ExtendedHostFunctions<427 sp_io::SubstrateHostFunctions,428 <QuartzRuntimeExecutor as NativeExecutionDispatch>::ExtendHostFunctions,429 >, _>(info_provider)),430431 RuntimeId::Opal => Box::pin(cmd.run::<Block, ExtendedHostFunctions<432 sp_io::SubstrateHostFunctions,433 <OpalRuntimeExecutor as NativeExecutionDispatch>::ExtendHostFunctions,434 >, _>(info_provider)),435 runtime_id => return Err(no_runtime_err!(runtime_id).into()),436 },437 task_manager,438 ))439 })440 }441 #[cfg(not(feature = "try-runtime"))]442 Some(Subcommand::TryRuntime) => {443 Err("Try-runtime must be enabled by `--features try-runtime`.".into())444 }445 None => {446 let runner = cli.create_runner(&cli.run.normalize())?;447 let collator_options = cli.run.collator_options();448449 runner.run_node_until_exit(|config| async move {450 let hwbench = if !cli.no_hardware_benchmarks {451 config.database.path().map(|database_path| {452 let _ = std::fs::create_dir_all(database_path);453 sc_sysinfo::gather_hwbench(Some(database_path))454 })455 } else {456 None457 };458459 let extensions = chain_spec::Extensions::try_get(&*config.chain_spec);460461 let service_id = config.chain_spec.service_id();462 let relay_chain_id = extensions.map(|e| e.relay_chain.clone());463 let is_dev_service = matches![service_id, ServiceId::Dev]464 || relay_chain_id == Some("dev-service".into());465466 if is_dev_service {467 info!("Running Dev service");468469 let mut config = config;470471 config.state_pruning = Some(sc_service::PruningMode::ArchiveAll);472473 return start_node_using_chain_runtime! {474 start_dev_node(config, cli.idle_autoseal_interval, cli.autoseal_finalization_delay, cli.disable_autoseal_on_tx).map_err(Into::into)475 };476 };477478 let para_id = extensions479 .map(|e| e.para_id)480 .ok_or("Could not find parachain ID in chain-spec.")?;481482 let polkadot_cli = RelayChainCli::new(483 &config,484 [RelayChainCli::executable_name()]485 .iter()486 .chain(cli.relaychain_args.iter()),487 );488489 let para_id = ParaId::from(para_id);490491 let parachain_account =492 AccountIdConversion::<polkadot_primitives::AccountId>::into_account_truncating(493 ¶_id,494 );495496 let polkadot_config = SubstrateCli::create_configuration(497 &polkadot_cli,498 &polkadot_cli,499 config.tokio_handle.clone(),500 )501 .map_err(|err| format!("Relay chain argument error: {err}"))?;502503 info!("Parachain id: {:?}", para_id);504 info!("Parachain Account: {}", parachain_account);505 info!(506 "Is collating: {}",507 if config.role.is_authority() {508 "yes"509 } else {510 "no"511 }512 );513514 start_node_using_chain_runtime! {515 start_node(config, polkadot_config, collator_options, para_id, hwbench)516 .await517 .map(|r| r.0)518 .map_err(Into::into)519 }520 })521 }522 }523}524525impl DefaultConfigurationValues for RelayChainCli {526 fn p2p_listen_port() -> u16 {527 30334528 }529530 fn rpc_listen_port() -> u16 {531 9945532 }533534 fn prometheus_listen_port() -> u16 {535 9616536 }537}538539impl CliConfiguration<Self> for RelayChainCli {540 fn shared_params(&self) -> &SharedParams {541 self.base.base.shared_params()542 }543544 fn import_params(&self) -> Option<&ImportParams> {545 self.base.base.import_params()546 }547548 fn network_params(&self) -> Option<&NetworkParams> {549 self.base.base.network_params()550 }551552 fn keystore_params(&self) -> Option<&KeystoreParams> {553 self.base.base.keystore_params()554 }555556 fn base_path(&self) -> Result<Option<BasePath>> {557 Ok(self558 .shared_params()559 .base_path()?560 .or_else(|| Some(self.base_path.clone().into())))561 }562563 fn prometheus_config(564 &self,565 default_listen_port: u16,566 chain_spec: &Box<dyn ChainSpec>,567 ) -> Result<Option<PrometheusConfig>> {568 self.base569 .base570 .prometheus_config(default_listen_port, chain_spec)571 }572573 fn init<F>(574 &self,575 _support_url: &String,576 _impl_version: &String,577 _logger_hook: F,578 _config: &sc_service::Configuration,579 ) -> Result<()> {580 unreachable!("PolkadotCli is never initialized; qed");581 }582583 fn chain_id(&self, is_dev: bool) -> Result<String> {584 let chain_id = self.base.base.chain_id(is_dev)?;585586 Ok(if chain_id.is_empty() {587 self.chain_id.clone().unwrap_or_default()588 } else {589 chain_id590 })591 }592593 fn role(&self, is_dev: bool) -> Result<sc_service::Role> {594 self.base.base.role(is_dev)595 }596597 fn transaction_pool(&self, is_dev: bool) -> Result<sc_service::config::TransactionPoolOptions> {598 self.base.base.transaction_pool(is_dev)599 }600601 fn rpc_methods(&self) -> Result<sc_service::config::RpcMethods> {602 self.base.base.rpc_methods()603 }604605 fn rpc_max_connections(&self) -> Result<u32> {606 self.base.base.rpc_max_connections()607 }608609 fn rpc_cors(&self, is_dev: bool) -> Result<Option<Vec<String>>> {610 self.base.base.rpc_cors(is_dev)611 }612613 fn default_heap_pages(&self) -> Result<Option<u64>> {614 self.base.base.default_heap_pages()615 }616617 fn force_authoring(&self) -> Result<bool> {618 self.base.base.force_authoring()619 }620621 fn disable_grandpa(&self) -> Result<bool> {622 self.base.base.disable_grandpa()623 }624625 fn max_runtime_instances(&self) -> Result<Option<usize>> {626 self.base.base.max_runtime_instances()627 }628629 fn announce_block(&self) -> Result<bool> {630 self.base.base.announce_block()631 }632633 fn telemetry_endpoints(634 &self,635 chain_spec: &Box<dyn ChainSpec>,636 ) -> Result<Option<sc_telemetry::TelemetryEndpoints>> {637 self.base.base.telemetry_endpoints(chain_spec)638 }639}