// Copyright 2019-2021 Parity Technologies (UK) Ltd. // This file is part of Cumulus. // Cumulus is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Cumulus is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . use crate::{ chain_spec, cli::{Cli, RelayChainCli, Subcommand}, }; use codec::Encode; use cumulus_primitives_core::ParaId; use cumulus_client_service::genesis::generate_genesis_block; use log::info; use parachain_runtime::Block; use polkadot_parachain::primitives::AccountIdConversion; use sc_cli::{ ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, NetworkParams, Result, RuntimeVersion, SharedParams, SubstrateCli, }; use sc_service::{ config::{BasePath, PrometheusConfig}, PartialComponents, }; use sp_core::hexdisplay::HexDisplay; use sp_runtime::traits::Block as BlockT; use std::{io::Write, net::SocketAddr}; fn load_spec( id: &str, para_id: ParaId, ) -> std::result::Result, String> { match id { "staging" => Ok(Box::new(chain_spec::staging_test_net(para_id))), "tick" => Ok(Box::new(chain_spec::ChainSpec::from_json_bytes( &include_bytes!("../res/tick.json")[..], )?)), "trick" => Ok(Box::new(chain_spec::ChainSpec::from_json_bytes( &include_bytes!("../res/trick.json")[..], )?)), "track" => Ok(Box::new(chain_spec::ChainSpec::from_json_bytes( &include_bytes!("../res/track.json")[..], )?)), "" => Ok(Box::new(chain_spec::get_chain_spec(para_id))), path => Ok(Box::new(chain_spec::ChainSpec::from_json_file( path.into(), )?)), } } impl SubstrateCli for Cli { fn impl_name() -> String { "Cumulus Test Parachain Collator".into() } fn impl_version() -> String { env!("SUBSTRATE_CLI_IMPL_VERSION").into() } fn description() -> String { format!( "Cumulus test parachain collator\n\nThe command-line arguments provided first will be \ passed to the parachain node, while the arguments provided after -- will be passed \ to the relaychain node.\n\n\ {} [parachain-args] -- [relaychain-args]", Self::executable_name() ) } fn author() -> String { env!("CARGO_PKG_AUTHORS").into() } fn support_url() -> String { "https://github.com/paritytech/cumulus/issues/new".into() } fn copyright_start_year() -> i32 { 2017 } fn load_spec(&self, id: &str) -> std::result::Result, String> { load_spec(id, self.run.parachain_id.unwrap_or(100).into()) } fn native_runtime_version(_: &Box) -> &'static RuntimeVersion { ¶chain_runtime::VERSION } } impl SubstrateCli for RelayChainCli { fn impl_name() -> String { "Cumulus Test Parachain Collator".into() } fn impl_version() -> String { env!("SUBSTRATE_CLI_IMPL_VERSION").into() } fn description() -> String { "Cumulus test parachain collator\n\nThe command-line arguments provided first will be \ passed to the parachain node, while the arguments provided after -- will be passed \ to the relaychain node.\n\n\ rococo-collator [parachain-args] -- [relaychain-args]" .into() } fn author() -> String { env!("CARGO_PKG_AUTHORS").into() } fn support_url() -> String { "https://github.com/paritytech/cumulus/issues/new".into() } fn copyright_start_year() -> i32 { 2017 } fn load_spec(&self, id: &str) -> std::result::Result, String> { polkadot_cli::Cli::from_iter([RelayChainCli::executable_name().to_string()].iter()) .load_spec(id) } fn native_runtime_version(chain_spec: &Box) -> &'static RuntimeVersion { polkadot_cli::Cli::native_runtime_version(chain_spec) } } fn extract_genesis_wasm(chain_spec: &Box) -> Result> { let mut storage = chain_spec.build_storage()?; storage .top .remove(sp_core::storage::well_known_keys::CODE) .ok_or_else(|| "Could not find wasm file in genesis state!".into()) } /// Parse command line arguments into service configuration. pub fn run() -> Result<()> { let cli = Cli::from_args(); match &cli.subcommand { Some(Subcommand::BuildSpec(cmd)) => { let runner = cli.create_runner(cmd)?; runner.sync_run(|config| cmd.run(config.chain_spec, config.network)) } Some(Subcommand::CheckBlock(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|config| { let PartialComponents { client, task_manager, import_queue, .. } = crate::service::new_partial(&config)?; Ok((cmd.run(client, import_queue), task_manager)) }) } Some(Subcommand::ExportBlocks(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|config| { let PartialComponents { client, task_manager, .. } = crate::service::new_partial(&config)?; Ok((cmd.run(client, config.database), task_manager)) }) } Some(Subcommand::ExportState(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|config| { let PartialComponents { client, task_manager, .. } = crate::service::new_partial(&config)?; Ok((cmd.run(client, config.chain_spec), task_manager)) }) } Some(Subcommand::ImportBlocks(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|config| { let PartialComponents { client, task_manager, import_queue, .. } = crate::service::new_partial(&config)?; Ok((cmd.run(client, import_queue), task_manager)) }) } Some(Subcommand::PurgeChain(cmd)) => { let runner = cli.create_runner(cmd)?; runner.sync_run(|config| { let polkadot_cli = RelayChainCli::new( &config, [RelayChainCli::executable_name().to_string()] .iter() .chain(cli.relaychain_args.iter()), ); let polkadot_config = SubstrateCli::create_configuration( &polkadot_cli, &polkadot_cli, config.task_executor.clone(), ) .map_err(|err| format!("Relay chain argument error: {}", err))?; cmd.run(config, polkadot_config) }) } Some(Subcommand::Revert(cmd)) => { let runner = cli.create_runner(cmd)?; runner.async_run(|config| { let PartialComponents { client, task_manager, backend, .. } = crate::service::new_partial(&config)?; Ok((cmd.run(client, backend), task_manager)) }) } Some(Subcommand::ExportGenesisState(params)) => { let mut builder = sc_cli::LoggerBuilder::new(""); builder.with_profiling(sc_tracing::TracingReceiver::Log, ""); let _ = builder.init(); let block: Block = generate_genesis_block(&load_spec( ¶ms.chain.clone().unwrap_or_default(), params.parachain_id.unwrap_or(100).into(), )?)?; let raw_header = block.header().encode(); let output_buf = if params.raw { raw_header } else { format!("0x{:?}", HexDisplay::from(&block.header().encode())).into_bytes() }; if let Some(output) = ¶ms.output { std::fs::write(output, output_buf)?; } else { std::io::stdout().write_all(&output_buf)?; } Ok(()) } Some(Subcommand::ExportGenesisWasm(params)) => { let mut builder = sc_cli::LoggerBuilder::new(""); builder.with_profiling(sc_tracing::TracingReceiver::Log, ""); let _ = builder.init(); let raw_wasm_blob = extract_genesis_wasm(&cli.load_spec(¶ms.chain.clone().unwrap_or_default())?)?; let output_buf = if params.raw { raw_wasm_blob } else { format!("0x{:?}", HexDisplay::from(&raw_wasm_blob)).into_bytes() }; if let Some(output) = ¶ms.output { std::fs::write(output, output_buf)?; } else { std::io::stdout().write_all(&output_buf)?; } Ok(()) } None => { let runner = cli.create_runner(&*cli.run)?; runner.run_node_until_exit(|config| async move { // TODO let key = sp_core::Pair::generate().0; let para_id = chain_spec::Extensions::try_get(&*config.chain_spec).map(|e| e.para_id); let polkadot_cli = RelayChainCli::new( &config, [RelayChainCli::executable_name().to_string()] .iter() .chain(cli.relaychain_args.iter()), ); let id = ParaId::from(cli.run.parachain_id.or(para_id).unwrap_or(100)); let parachain_account = AccountIdConversion::::into_account(&id); let block: Block = generate_genesis_block(&config.chain_spec).map_err(|e| format!("{:?}", e))?; let genesis_state = format!("0x{:?}", HexDisplay::from(&block.header().encode())); let task_executor = config.task_executor.clone(); let polkadot_config = SubstrateCli::create_configuration( &polkadot_cli, &polkadot_cli, task_executor, ) .map_err(|err| format!("Relay chain argument error: {}", err))?; let collator = cli.run.base.validator || cli.collator; info!("Parachain id: {:?}", id); info!("Parachain Account: {}", parachain_account); info!("Parachain genesis state: {}", genesis_state); info!("Is collating: {}", if collator { "yes" } else { "no" }); crate::service::start_node(config, key, polkadot_config, id, collator) .await .map(|r| r.0) .map_err(Into::into) }) } } } impl DefaultConfigurationValues for RelayChainCli { fn p2p_listen_port() -> u16 { 30334 } fn rpc_ws_listen_port() -> u16 { 9945 } fn rpc_http_listen_port() -> u16 { 9934 } fn prometheus_listen_port() -> u16 { 9616 } } impl CliConfiguration for RelayChainCli { fn shared_params(&self) -> &SharedParams { self.base.base.shared_params() } fn import_params(&self) -> Option<&ImportParams> { self.base.base.import_params() } fn network_params(&self) -> Option<&NetworkParams> { self.base.base.network_params() } fn keystore_params(&self) -> Option<&KeystoreParams> { self.base.base.keystore_params() } fn base_path(&self) -> Result> { Ok(self .shared_params() .base_path() .or_else(|| self.base_path.clone().map(Into::into))) } fn rpc_http(&self, default_listen_port: u16) -> Result> { self.base.base.rpc_http(default_listen_port) } fn rpc_ipc(&self) -> Result> { self.base.base.rpc_ipc() } fn rpc_ws(&self, default_listen_port: u16) -> Result> { self.base.base.rpc_ws(default_listen_port) } fn prometheus_config(&self, default_listen_port: u16) -> Result> { self.base.base.prometheus_config(default_listen_port) } fn init(&self) -> Result<()> { unreachable!("PolkadotCli is never initialized; qed"); } fn chain_id(&self, is_dev: bool) -> Result { let chain_id = self.base.base.chain_id(is_dev)?; Ok(if chain_id.is_empty() { self.chain_id.clone().unwrap_or_default() } else { chain_id }) } fn role(&self, is_dev: bool) -> Result { self.base.base.role(is_dev) } fn transaction_pool(&self) -> Result { self.base.base.transaction_pool() } fn state_cache_child_ratio(&self) -> Result> { self.base.base.state_cache_child_ratio() } fn rpc_methods(&self) -> Result { self.base.base.rpc_methods() } fn rpc_ws_max_connections(&self) -> Result> { self.base.base.rpc_ws_max_connections() } fn rpc_cors(&self, is_dev: bool) -> Result>> { self.base.base.rpc_cors(is_dev) } fn telemetry_external_transport(&self) -> Result> { self.base.base.telemetry_external_transport() } fn default_heap_pages(&self) -> Result> { self.base.base.default_heap_pages() } fn force_authoring(&self) -> Result { self.base.base.force_authoring() } fn disable_grandpa(&self) -> Result { self.base.base.disable_grandpa() } fn max_runtime_instances(&self) -> Result> { self.base.base.max_runtime_instances() } fn announce_block(&self) -> Result { self.base.base.announce_block() } fn telemetry_endpoints( &self, chain_spec: &Box, ) -> Result> { self.base.base.telemetry_endpoints(chain_spec) } }