1234567891011121314151617181920212223242526272829303132333435use crate::{36 chain_spec::{self, RuntimeIdentification, ServiceId, ServiceIdentification},37 cli::{Cli, RelayChainCli, Subcommand},38 service::{new_partial, start_node, start_dev_node},39};40#[cfg(feature = "runtime-benchmarks")]41use crate::chain_spec::default_runtime;4243#[cfg(feature = "unique-runtime")]44use crate::service::UniqueRuntimeExecutor;4546#[cfg(feature = "quartz-runtime")]47use crate::service::QuartzRuntimeExecutor;4849use crate::service::OpalRuntimeExecutor;5051#[cfg(feature = "runtime-benchmarks")]52use crate::service::DefaultRuntimeExecutor;5354use codec::Encode;55use cumulus_primitives_core::ParaId;56use cumulus_client_cli::generate_genesis_block;57use log::{debug, info};58use sc_cli::{59 ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams,60 NetworkParams, Result, RuntimeVersion, SharedParams, SubstrateCli,61};62use sc_service::config::{BasePath, PrometheusConfig};63use sp_core::hexdisplay::HexDisplay;64use sp_runtime::traits::{AccountIdConversion, Block as BlockT};6566use up_common::types::opaque::{Block, RuntimeId};6768macro_rules! no_runtime_err {69 ($runtime_id:expr) => {70 format!(71 "No runtime valid runtime was found for chain {:#?}",72 $runtime_id73 )74 };75}7677fn load_spec(id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {78 Ok(match id {79 "dev" => Box::new(chain_spec::development_config()),80 "" | "local" => Box::new(chain_spec::local_testnet_config()),81 path => {82 let path = std::path::PathBuf::from(path);83 #[allow(clippy::redundant_clone)]84 let chain_spec = Box::new(chain_spec::OpalChainSpec::from_json_file(path.clone())?)85 as Box<dyn sc_service::ChainSpec>;8687 match chain_spec.runtime_id() {88 #[cfg(feature = "unique-runtime")]89 RuntimeId::Unique => Box::new(chain_spec::UniqueChainSpec::from_json_file(path)?),9091 #[cfg(feature = "quartz-runtime")]92 RuntimeId::Quartz => Box::new(chain_spec::QuartzChainSpec::from_json_file(path)?),9394 RuntimeId::Opal => chain_spec,95 runtime_id => return Err(no_runtime_err!(runtime_id)),96 }97 }98 })99}100101impl SubstrateCli for Cli {102 103 fn impl_name() -> String {104 format!("{} Node", Self::node_name())105 }106107 fn impl_version() -> String {108 env!("SUBSTRATE_CLI_IMPL_VERSION").into()109 }110 111 fn description() -> String {112 format!(113 "{} Node\n\nThe command-line arguments provided first will be \114 passed to the parachain node, while the arguments provided after -- will be passed \115 to the relaychain node.\n\n\116 {} [parachain-args] -- [relaychain-args]",117 Self::node_name(),118 Self::executable_name()119 )120 }121122 fn author() -> String {123 env!("CARGO_PKG_AUTHORS").into()124 }125126 127 fn support_url() -> String {128 "support@unique.network".into()129 }130131 fn copyright_start_year() -> i32 {132 2019133 }134135 fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {136 load_spec(id)137 }138}139140impl SubstrateCli for RelayChainCli {141 142 fn impl_name() -> String {143 format!("{} Node", Cli::node_name())144 }145146 fn impl_version() -> String {147 env!("SUBSTRATE_CLI_IMPL_VERSION").into()148 }149 150 fn description() -> String {151 format!(152 "{} Node\n\nThe command-line arguments provided first will be \153 passed to the parachain node, while the arguments provided after -- will be passed \154 to the relaychain node.\n\n\155 parachain-collator [parachain-args] -- [relaychain-args]",156 Cli::node_name()157 )158 }159160 fn author() -> String {161 env!("CARGO_PKG_AUTHORS").into()162 }163 164 fn support_url() -> String {165 "support@unique.network".into()166 }167168 fn copyright_start_year() -> i32 {169 2019170 }171172 fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {173 polkadot_cli::Cli::from_iter([RelayChainCli::executable_name()].iter()).load_spec(id)174 }175}176177macro_rules! async_run_with_runtime {178 (179 $runtime:path, $runtime_api:path, $executor:path,180 $runner:ident, $components:ident, $cli:ident, $cmd:ident, $config:ident,181 $( $code:tt )*182 ) => {183 $runner.async_run(|$config| {184 let $components = new_partial::<185 $runtime, $runtime_api, $executor, _186 >(187 &$config,188 crate::service::parachain_build_import_queue::<$runtime, _, _>,189 )?;190 let task_manager = $components.task_manager;191192 { $( $code )* }.map(|v| (v, task_manager))193 })194 };195}196197macro_rules! construct_async_run {198 (|$components:ident, $cli:ident, $cmd:ident, $config:ident| $( $code:tt )* ) => {{199 let runner = $cli.create_runner($cmd)?;200201 match runner.config().chain_spec.runtime_id() {202 #[cfg(feature = "unique-runtime")]203 RuntimeId::Unique => async_run_with_runtime!(204 unique_runtime::Runtime, unique_runtime::RuntimeApi, UniqueRuntimeExecutor,205 runner, $components, $cli, $cmd, $config, $( $code )*206 ),207208 #[cfg(feature = "quartz-runtime")]209 RuntimeId::Quartz => async_run_with_runtime!(210 quartz_runtime::Runtime, quartz_runtime::RuntimeApi, QuartzRuntimeExecutor,211 runner, $components, $cli, $cmd, $config, $( $code )*212 ),213214 RuntimeId::Opal => async_run_with_runtime!(215 opal_runtime::Runtime, opal_runtime::RuntimeApi, OpalRuntimeExecutor,216 runner, $components, $cli, $cmd, $config, $( $code )*217 ),218219 runtime_id => Err(no_runtime_err!(runtime_id).into())220 }221 }}222}223224macro_rules! sync_run_with_runtime {225 (226 $runtime:path, $runtime_api:path, $executor:path,227 $runner:ident, $components:ident, $cli:ident, $cmd:ident, $config:ident,228 $( $code:tt )*229 ) => {230 $runner.sync_run(|$config| {231 let $components = new_partial::<232 $runtime, $runtime_api, $executor, _233 >(234 &$config,235 crate::service::parachain_build_import_queue::<$runtime, _, _>,236 )?;237238 $( $code )*239 })240 };241}242243macro_rules! construct_sync_run {244 (|$components:ident, $cli:ident, $cmd:ident, $config:ident| $( $code:tt )* ) => {{245 let runner = $cli.create_runner($cmd)?;246247 match runner.config().chain_spec.runtime_id() {248 #[cfg(feature = "unique-runtime")]249 RuntimeId::Unique => sync_run_with_runtime!(250 unique_runtime::Runtime, unique_runtime::RuntimeApi, UniqueRuntimeExecutor,251 runner, $components, $cli, $cmd, $config, $( $code )*252 ),253254 #[cfg(feature = "quartz-runtime")]255 RuntimeId::Quartz => sync_run_with_runtime!(256 quartz_runtime::Runtime, quartz_runtime::RuntimeApi, QuartzRuntimeExecutor,257 runner, $components, $cli, $cmd, $config, $( $code )*258 ),259260 RuntimeId::Opal => sync_run_with_runtime!(261 opal_runtime::Runtime, opal_runtime::RuntimeApi, OpalRuntimeExecutor,262 runner, $components, $cli, $cmd, $config, $( $code )*263 ),264265 runtime_id => Err(no_runtime_err!(runtime_id).into())266 }267 }}268}269270macro_rules! start_node_using_chain_runtime {271 ($start_node_fn:ident($config:expr $(, $($args:expr),+)?) $($code:tt)*) => {272 match $config.chain_spec.runtime_id() {273 #[cfg(feature = "unique-runtime")]274 RuntimeId::Unique => $start_node_fn::<275 unique_runtime::Runtime,276 unique_runtime::RuntimeApi,277 UniqueRuntimeExecutor,278 >($config $(, $($args),+)?) $($code)*,279280 #[cfg(feature = "quartz-runtime")]281 RuntimeId::Quartz => $start_node_fn::<282 quartz_runtime::Runtime,283 quartz_runtime::RuntimeApi,284 QuartzRuntimeExecutor,285 >($config $(, $($args),+)?) $($code)*,286287 RuntimeId::Opal => $start_node_fn::<288 opal_runtime::Runtime,289 opal_runtime::RuntimeApi,290 OpalRuntimeExecutor,291 >($config $(, $($args),+)?) $($code)*,292293 runtime_id => Err(no_runtime_err!(runtime_id).into()),294 }295 };296}297298299pub fn run() -> Result<()> {300 let cli = Cli::from_args();301302 match &cli.subcommand {303 Some(Subcommand::Key(cmd)) => cmd.run(&cli),304 Some(Subcommand::BuildSpec(cmd)) => {305 let runner = cli.create_runner(cmd)?;306 runner.sync_run(|config| cmd.run(config.chain_spec, config.network))307 }308 Some(Subcommand::CheckBlock(cmd)) => {309 construct_async_run!(|components, cli, cmd, config| {310 Ok(cmd.run(components.client, components.import_queue))311 })312 }313 Some(Subcommand::ExportBlocks(cmd)) => {314 construct_async_run!(|components, cli, cmd, config| {315 Ok(cmd.run(components.client, config.database))316 })317 }318 Some(Subcommand::ExportState(cmd)) => {319 construct_async_run!(|components, cli, cmd, config| {320 Ok(cmd.run(components.client, config.chain_spec))321 })322 }323 Some(Subcommand::ImportBlocks(cmd)) => {324 construct_async_run!(|components, cli, cmd, config| {325 Ok(cmd.run(components.client, components.import_queue))326 })327 }328 Some(Subcommand::PurgeChain(cmd)) => {329 let runner = cli.create_runner(cmd)?;330331 runner.sync_run(|config| {332 let polkadot_cli = RelayChainCli::new(333 &config,334 [RelayChainCli::executable_name()]335 .iter()336 .chain(cli.relaychain_args.iter()),337 );338339 let polkadot_config = SubstrateCli::create_configuration(340 &polkadot_cli,341 &polkadot_cli,342 config.tokio_handle.clone(),343 )344 .map_err(|err| format!("Relay chain argument error: {err}"))?;345346 cmd.run(config, polkadot_config)347 })348 }349 Some(Subcommand::Revert(cmd)) => construct_async_run!(|components, cli, cmd, config| {350 Ok(cmd.run(components.client, components.backend, None))351 }),352 Some(Subcommand::ExportGenesisState(cmd)) => {353 construct_sync_run!(|components, cli, cmd, _config| {354 let spec = cli.load_spec(&cmd.shared_params.chain.clone().unwrap_or_default())?;355 cmd.run(&*spec, &*components.client)356 })357 }358 Some(Subcommand::ExportGenesisWasm(cmd)) => {359 construct_sync_run!(|_components, cli, cmd, _config| {360 let spec = cli.load_spec(&cmd.shared_params.chain.clone().unwrap_or_default())?;361 cmd.run(&*spec)362 })363 }364 #[cfg(feature = "runtime-benchmarks")]365 Some(Subcommand::Benchmark(cmd)) => {366 use frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE};367 let runner = cli.create_runner(cmd)?;368 369 match cmd {370 BenchmarkCmd::Pallet(cmd) => {371 runner.sync_run(|config| cmd.run::<Block, DefaultRuntimeExecutor>(config))372 }373 BenchmarkCmd::Block(cmd) => runner.sync_run(|config| {374 let partials = new_partial::<375 default_runtime::RuntimeApi,376 DefaultRuntimeExecutor,377 _,378 >(&config, crate::service::parachain_build_import_queue)?;379 cmd.run(partials.client)380 }),381 BenchmarkCmd::Storage(cmd) => runner.sync_run(|config| {382 let partials = new_partial::<383 default_runtime::RuntimeApi,384 DefaultRuntimeExecutor,385 _,386 >(&config, crate::service::parachain_build_import_queue)?;387 let db = partials.backend.expose_db();388 let storage = partials.backend.expose_storage();389390 cmd.run(config, partials.client.clone(), db, storage)391 }),392 BenchmarkCmd::Machine(cmd) => {393 runner.sync_run(|config| cmd.run(&config, SUBSTRATE_REFERENCE_HARDWARE.clone()))394 }395 BenchmarkCmd::Overhead(_) | BenchmarkCmd::Extrinsic(_) => {396 Err("Unsupported benchmarking command".into())397 }398 }399 }400 #[cfg(feature = "try-runtime")]401 Some(Subcommand::TryRuntime(cmd)) => {402 use std::{future::Future, pin::Pin};403404 use sc_executor::{sp_wasm_interface::ExtendedHostFunctions, NativeExecutionDispatch};405 use try_runtime_cli::block_building_info::timestamp_with_aura_info;406407 let runner = cli.create_runner(cmd)?;408409 410 let registry = &runner411 .config()412 .prometheus_config413 .as_ref()414 .map(|cfg| &cfg.registry);415 let task_manager =416 sc_service::TaskManager::new(runner.config().tokio_handle.clone(), *registry)417 .map_err(|e| format!("Error: {e:?}"))?;418 let info_provider = Some(timestamp_with_aura_info(12000));419420 runner.async_run(|config| -> Result<(Pin<Box<dyn Future<Output = _>>>, _)> {421 Ok((422 match config.chain_spec.runtime_id() {423 #[cfg(feature = "unique-runtime")]424 RuntimeId::Unique => Box::pin(cmd.run::<Block, ExtendedHostFunctions<425 sp_io::SubstrateHostFunctions,426 <UniqueRuntimeExecutor as NativeExecutionDispatch>::ExtendHostFunctions,427 >, _>(info_provider)),428429 #[cfg(feature = "quartz-runtime")]430 RuntimeId::Quartz => Box::pin(cmd.run::<Block, ExtendedHostFunctions<431 sp_io::SubstrateHostFunctions,432 <QuartzRuntimeExecutor as NativeExecutionDispatch>::ExtendHostFunctions,433 >, _>(info_provider)),434435 RuntimeId::Opal => Box::pin(cmd.run::<Block, ExtendedHostFunctions<436 sp_io::SubstrateHostFunctions,437 <OpalRuntimeExecutor as NativeExecutionDispatch>::ExtendHostFunctions,438 >, _>(info_provider)),439 runtime_id => return Err(no_runtime_err!(runtime_id).into()),440 },441 task_manager,442 ))443 })444 }445 #[cfg(not(feature = "try-runtime"))]446 Some(Subcommand::TryRuntime) => {447 Err("Try-runtime must be enabled by `--features try-runtime`.".into())448 }449 None => {450 let runner = cli.create_runner(&cli.run.normalize())?;451 let collator_options = cli.run.collator_options();452453 runner.run_node_until_exit(|config| async move {454 let hwbench = if !cli.no_hardware_benchmarks {455 config.database.path().map(|database_path| {456 let _ = std::fs::create_dir_all(database_path);457 sc_sysinfo::gather_hwbench(Some(database_path))458 })459 } else {460 None461 };462463 let extensions = chain_spec::Extensions::try_get(&*config.chain_spec);464465 let service_id = config.chain_spec.service_id();466 let relay_chain_id = extensions.map(|e| e.relay_chain.clone());467 let is_dev_service = matches![service_id, ServiceId::Dev]468 || relay_chain_id == Some("dev-service".into());469470 if is_dev_service {471 info!("Running Dev service");472473 let mut config = config;474475 config.state_pruning = Some(sc_service::PruningMode::ArchiveAll);476477 return start_node_using_chain_runtime! {478 start_dev_node(config, cli.idle_autoseal_interval, cli.autoseal_finalization_delay, cli.disable_autoseal_on_tx).map_err(Into::into)479 };480 };481482 let para_id = extensions483 .map(|e| e.para_id)484 .ok_or("Could not find parachain ID in chain-spec.")?;485486 let polkadot_cli = RelayChainCli::new(487 &config,488 [RelayChainCli::executable_name()]489 .iter()490 .chain(cli.relaychain_args.iter()),491 );492493 let para_id = ParaId::from(para_id);494495 let parachain_account =496 AccountIdConversion::<polkadot_primitives::AccountId>::into_account_truncating(497 ¶_id,498 );499500 let polkadot_config = SubstrateCli::create_configuration(501 &polkadot_cli,502 &polkadot_cli,503 config.tokio_handle.clone(),504 )505 .map_err(|err| format!("Relay chain argument error: {err}"))?;506507 info!("Parachain id: {:?}", para_id);508 info!("Parachain Account: {}", parachain_account);509 info!(510 "Is collating: {}",511 if config.role.is_authority() {512 "yes"513 } else {514 "no"515 }516 );517518 start_node_using_chain_runtime! {519 start_node(config, polkadot_config, collator_options, para_id, hwbench)520 .await521 .map(|r| r.0)522 .map_err(Into::into)523 }524 })525 }526 }527}528529impl DefaultConfigurationValues for RelayChainCli {530 fn p2p_listen_port() -> u16 {531 30334532 }533534 fn rpc_listen_port() -> u16 {535 9945536 }537538 fn prometheus_listen_port() -> u16 {539 9616540 }541}542543impl CliConfiguration<Self> for RelayChainCli {544 fn shared_params(&self) -> &SharedParams {545 self.base.base.shared_params()546 }547548 fn import_params(&self) -> Option<&ImportParams> {549 self.base.base.import_params()550 }551552 fn network_params(&self) -> Option<&NetworkParams> {553 self.base.base.network_params()554 }555556 fn keystore_params(&self) -> Option<&KeystoreParams> {557 self.base.base.keystore_params()558 }559560 fn base_path(&self) -> Result<Option<BasePath>> {561 Ok(self562 .shared_params()563 .base_path()?564 .or_else(|| Some(self.base_path.clone().into())))565 }566567 fn prometheus_config(568 &self,569 default_listen_port: u16,570 chain_spec: &Box<dyn ChainSpec>,571 ) -> Result<Option<PrometheusConfig>> {572 self.base573 .base574 .prometheus_config(default_listen_port, chain_spec)575 }576577 fn init<F>(578 &self,579 _support_url: &String,580 _impl_version: &String,581 _logger_hook: F,582 _config: &sc_service::Configuration,583 ) -> Result<()> {584 unreachable!("PolkadotCli is never initialized; qed");585 }586587 fn chain_id(&self, is_dev: bool) -> Result<String> {588 let chain_id = self.base.base.chain_id(is_dev)?;589590 Ok(if chain_id.is_empty() {591 self.chain_id.clone().unwrap_or_default()592 } else {593 chain_id594 })595 }596597 fn role(&self, is_dev: bool) -> Result<sc_service::Role> {598 self.base.base.role(is_dev)599 }600601 fn transaction_pool(&self, is_dev: bool) -> Result<sc_service::config::TransactionPoolOptions> {602 self.base.base.transaction_pool(is_dev)603 }604605 fn rpc_methods(&self) -> Result<sc_service::config::RpcMethods> {606 self.base.base.rpc_methods()607 }608609 fn rpc_max_connections(&self) -> Result<u32> {610 self.base.base.rpc_max_connections()611 }612613 fn rpc_cors(&self, is_dev: bool) -> Result<Option<Vec<String>>> {614 self.base.base.rpc_cors(is_dev)615 }616617 fn default_heap_pages(&self) -> Result<Option<u64>> {618 self.base.base.default_heap_pages()619 }620621 fn force_authoring(&self) -> Result<bool> {622 self.base.base.force_authoring()623 }624625 fn disable_grandpa(&self) -> Result<bool> {626 self.base.base.disable_grandpa()627 }628629 fn max_runtime_instances(&self) -> Result<Option<usize>> {630 self.base.base.max_runtime_instances()631 }632633 fn announce_block(&self) -> Result<bool> {634 self.base.base.announce_block()635 }636637 fn telemetry_endpoints(638 &self,639 chain_spec: &Box<dyn ChainSpec>,640 ) -> Result<Option<sc_telemetry::TelemetryEndpoints>> {641 self.base.base.telemetry_endpoints(chain_spec)642 }643}