1234567891011121314151617181920212223242526272829303132333435use crate::{36 chain_spec::{self, RuntimeIdentification},37 cli::{Cli, RelayChainCli, Subcommand},38 service::new_partial,39};4041#[cfg(feature = "unique-runtime")]42use crate::service::UniqueRuntimeExecutor;4344#[cfg(feature = "quartz-runtime")]45use crate::service::QuartzRuntimeExecutor;4647#[cfg(feature = "opal-runtime")]48use crate::service::OpalRuntimeExecutor;4950use codec::Encode;51use cumulus_primitives_core::ParaId;52use cumulus_client_service::genesis::generate_genesis_block;53use log::info;54use polkadot_parachain::primitives::AccountIdConversion;55use sc_cli::{56 ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams,57 NetworkParams, Result, RuntimeVersion, SharedParams, SubstrateCli,58};59use sc_service::{60 config::{BasePath, PrometheusConfig},61};62use sp_core::hexdisplay::HexDisplay;63use sp_runtime::traits::Block as BlockT;64use std::{io::Write, net::SocketAddr};6566use unique_runtime_common::types::Block;6768macro_rules! no_runtime_err {69 ($chain_spec:expr) => {70 format!("No runtime valid runtime was found, chain id: {}",71 $chain_spec.id())72 };73}7475fn load_spec(id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {76 Ok(match id {77 "westend-local" => Box::new(chain_spec::local_testnet_westend_config()),78 "rococo-local" => Box::new(chain_spec::local_testnet_rococo_config()),79 "dev" => Box::new(chain_spec::development_config()),80 "" | "local" => Box::new(chain_spec::local_testnet_rococo_config()),81 path => Box::new(chain_spec::ChainSpec::from_json_file(82 std::path::PathBuf::from(path),83 )?),84 })85}8687impl SubstrateCli for Cli {88 89 fn impl_name() -> String {90 "Unique Node".into()91 }9293 fn impl_version() -> String {94 env!("SUBSTRATE_CLI_IMPL_VERSION").into()95 }96 97 fn description() -> String {98 format!(99 "Unique Node\n\nThe command-line arguments provided first will be \100 passed to the parachain node, while the arguments provided after -- will be passed \101 to the relaychain node.\n\n\102 {} [parachain-args] -- [relaychain-args]",103 Self::executable_name()104 )105 }106107 fn author() -> String {108 env!("CARGO_PKG_AUTHORS").into()109 }110111 112 fn support_url() -> String {113 "support@unique.network".into()114 }115116 fn copyright_start_year() -> i32 {117 2019118 }119120 fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {121 load_spec(id)122 }123124 fn native_runtime_version(chain_spec: &Box<dyn ChainSpec>) -> &'static RuntimeVersion {125 #[cfg(feature = "unique-runtime")]126 if chain_spec.is_unique() {127 return &unique_runtime::VERSION;128 }129130 #[cfg(feature = "quartz-runtime")]131 if chain_spec.is_quartz() {132 return &quartz_runtime::VERSION;133 }134135 #[cfg(feature = "opal-runtime")]136 if chain_spec.is_opal() {137 return &opal_runtime::VERSION;138 }139140 panic!("{}", no_runtime_err!(chain_spec));141 }142}143144impl SubstrateCli for RelayChainCli {145 146 fn impl_name() -> String {147 "Unique Node".into()148 }149150 fn impl_version() -> String {151 env!("SUBSTRATE_CLI_IMPL_VERSION").into()152 }153 154 fn description() -> String {155 "Unique Node\n\nThe command-line arguments provided first will be \156 passed to the parachain node, while the arguments provided after -- will be passed \157 to the relaychain node.\n\n\158 parachain-collator [parachain-args] -- [relaychain-args]"159 .into()160 }161162 fn author() -> String {163 env!("CARGO_PKG_AUTHORS").into()164 }165 166 fn support_url() -> String {167 "support@unique.network".into()168 }169170 fn copyright_start_year() -> i32 {171 2019172 }173174 fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {175 polkadot_cli::Cli::from_iter([RelayChainCli::executable_name()].iter()).load_spec(id)176 }177178 fn native_runtime_version(chain_spec: &Box<dyn ChainSpec>) -> &'static RuntimeVersion {179 polkadot_cli::Cli::native_runtime_version(chain_spec)180 }181}182183#[allow(clippy::borrowed_box)]184fn extract_genesis_wasm(chain_spec: &Box<dyn sc_service::ChainSpec>) -> Result<Vec<u8>> {185 let mut storage = chain_spec.build_storage()?;186187 storage188 .top189 .remove(sp_core::storage::well_known_keys::CODE)190 .ok_or_else(|| "Could not find wasm file in genesis state!".into())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 #[cfg(feature = "unique-runtime")]198 if runner.config().chain_spec.is_unique() {199 return runner.async_run(|$config| {200 let $components = new_partial::<201 unique_runtime::RuntimeApi, UniqueRuntimeExecutor, _202 >(203 &$config,204 crate::service::parachain_build_import_queue,205 )?;206 let task_manager = $components.task_manager;207 { $( $code )* }.map(|v| (v, task_manager))208 });209 }210211 #[cfg(feature = "quartz-runtime")]212 if runner.config().chain_spec.is_quartz() {213 return runner.async_run(|$config| {214 let $components = new_partial::<215 quartz_runtime::RuntimeApi, QuartzRuntimeExecutor, _216 >(217 &$config,218 crate::service::parachain_build_import_queue,219 )?;220 let task_manager = $components.task_manager;221 { $( $code )* }.map(|v| (v, task_manager))222 });223 }224225 #[cfg(feature = "opal-runtime")]226 if runner.config().chain_spec.is_opal() {227 return runner.async_run(|$config| {228 let $components = new_partial::<229 opal_runtime::RuntimeApi, OpalRuntimeExecutor, _230 >(231 &$config,232 crate::service::parachain_build_import_queue,233 )?;234 let task_manager = $components.task_manager;235 { $( $code )* }.map(|v| (v, task_manager))236 });237 }238239 Err(no_runtime_err!(runner.config().chain_spec).into())240 }}241}242243244pub fn run() -> Result<()> {245 let cli = Cli::from_args();246247 match &cli.subcommand {248 Some(Subcommand::BuildSpec(cmd)) => {249 let runner = cli.create_runner(cmd)?;250 runner.sync_run(|config| cmd.run(config.chain_spec, config.network))251 }252 Some(Subcommand::CheckBlock(cmd)) => {253 construct_async_run!(|components, cli, cmd, config| {254 Ok(cmd.run(components.client, components.import_queue))255 })256 }257 Some(Subcommand::ExportBlocks(cmd)) => {258 construct_async_run!(|components, cli, cmd, config| {259 Ok(cmd.run(components.client, config.database))260 })261 }262 Some(Subcommand::ExportState(cmd)) => {263 construct_async_run!(|components, cli, cmd, config| {264 Ok(cmd.run(components.client, config.chain_spec))265 })266 }267 Some(Subcommand::ImportBlocks(cmd)) => {268 construct_async_run!(|components, cli, cmd, config| {269 Ok(cmd.run(components.client, components.import_queue))270 })271 }272 Some(Subcommand::PurgeChain(cmd)) => {273 let runner = cli.create_runner(cmd)?;274275 runner.sync_run(|config| {276 let polkadot_cli = RelayChainCli::new(277 &config,278 [RelayChainCli::executable_name()]279 .iter()280 .chain(cli.relaychain_args.iter()),281 );282283 let polkadot_config = SubstrateCli::create_configuration(284 &polkadot_cli,285 &polkadot_cli,286 config.tokio_handle.clone(),287 )288 .map_err(|err| format!("Relay chain argument error: {}", err))?;289290 cmd.run(config, polkadot_config)291 })292 }293 Some(Subcommand::Revert(cmd)) => construct_async_run!(|components, cli, cmd, config| {294 Ok(cmd.run(components.client, components.backend))295 }),296 Some(Subcommand::ExportGenesisState(params)) => {297 let mut builder = sc_cli::LoggerBuilder::new("");298 builder.with_profiling(sc_tracing::TracingReceiver::Log, "");299 let _ = builder.init();300301 let spec = load_spec(¶ms.chain.clone().unwrap_or_default())?;302 let state_version = Cli::native_runtime_version(&spec).state_version();303 let block: Block = generate_genesis_block(&spec, state_version)?;304 let raw_header = block.header().encode();305 let output_buf = if params.raw {306 raw_header307 } else {308 format!("0x{:?}", HexDisplay::from(&block.header().encode())).into_bytes()309 };310311 if let Some(output) = ¶ms.output {312 std::fs::write(output, output_buf)?;313 } else {314 std::io::stdout().write_all(&output_buf)?;315 }316317 Ok(())318 }319 Some(Subcommand::ExportGenesisWasm(params)) => {320 let mut builder = sc_cli::LoggerBuilder::new("");321 builder.with_profiling(sc_tracing::TracingReceiver::Log, "");322 let _ = builder.init();323324 let raw_wasm_blob =325 extract_genesis_wasm(&cli.load_spec(¶ms.chain.clone().unwrap_or_default())?)?;326 let output_buf = if params.raw {327 raw_wasm_blob328 } else {329 format!("0x{:?}", HexDisplay::from(&raw_wasm_blob)).into_bytes()330 };331332 if let Some(output) = ¶ms.output {333 std::fs::write(output, output_buf)?;334 } else {335 std::io::stdout().write_all(&output_buf)?;336 }337338 Ok(())339 }340 Some(Subcommand::Benchmark(cmd)) => {341 if cfg!(feature = "runtime-benchmarks") {342 let runner = cli.create_runner(cmd)?;343 runner.sync_run(|config| {344 #[cfg(feature = "unique-runtime")]345 if config.chain_spec.is_unique() {346 return cmd.run::<Block, UniqueRuntimeExecutor>(config);347 }348349 #[cfg(feature = "quartz-runtime")]350 if config.chain_spec.is_quartz() {351 return cmd.run::<Block, QuartzRuntimeExecutor>(config);352 }353354 #[cfg(feature = "opal-runtime")]355 if config.chain_spec.is_opal() {356 return cmd.run::<Block, OpalRuntimeExecutor>(config);357 }358359 Err(no_runtime_err!(config.chain_spec).into())360 })361 } else {362 Err("Benchmarking wasn't enabled when building the node. \363 You can enable it with `--features runtime-benchmarks`."364 .into())365 }366 }367 None => {368 let runner = cli.create_runner(&cli.run.normalize())?;369370 runner.run_node_until_exit(|config| async move {371 let para_id = chain_spec::Extensions::try_get(&*config.chain_spec)372 .map(|e| e.para_id)373 .ok_or("Could not find parachain ID in chain-spec.")?;374375 let polkadot_cli = RelayChainCli::new(376 &config,377 [RelayChainCli::executable_name()]378 .iter()379 .chain(cli.relaychain_args.iter()),380 );381382 let id = ParaId::from(para_id);383384 let parachain_account =385 AccountIdConversion::<polkadot_primitives::v0::AccountId>::into_account(&id);386387 let state_version =388 RelayChainCli::native_runtime_version(&config.chain_spec).state_version();389 let block: Block = generate_genesis_block(&config.chain_spec, state_version)390 .map_err(|e| format!("{:?}", e))?;391 let genesis_state = format!("0x{:?}", HexDisplay::from(&block.header().encode()));392 let genesis_hash = format!("0x{:?}", HexDisplay::from(&block.header().hash().0));393394 let polkadot_config = SubstrateCli::create_configuration(395 &polkadot_cli,396 &polkadot_cli,397 config.tokio_handle.clone(),398 )399 .map_err(|err| format!("Relay chain argument error: {}", err))?;400401 info!("Parachain id: {:?}", id);402 info!("Parachain Account: {}", parachain_account);403 info!("Parachain genesis state: {}", genesis_state);404 info!("Parachain genesis hash: {}", genesis_hash);405 info!(406 "Is collating: {}",407 if config.role.is_authority() {408 "yes"409 } else {410 "no"411 }412 );413414 #[cfg(feature = "unique-runtime")]415 if config.chain_spec.is_unique() {416 return crate::service::start_node::<417 unique_runtime::Runtime,418 unique_runtime::RuntimeApi,419 UniqueRuntimeExecutor,420 >(config, polkadot_config, id)421 .await422 .map(|r| r.0)423 .map_err(Into::into);424 }425426 #[cfg(feature = "quartz-runtime")]427 if config.chain_spec.is_quartz() {428 return crate::service::start_node::<429 quartz_runtime::Runtime,430 quartz_runtime::RuntimeApi,431 QuartzRuntimeExecutor,432 >(config, polkadot_config, id)433 .await434 .map(|r| r.0)435 .map_err(Into::into);436 }437438 #[cfg(feature = "opal-runtime")]439 if config.chain_spec.is_opal() {440 return crate::service::start_node::<441 opal_runtime::Runtime,442 opal_runtime::RuntimeApi,443 OpalRuntimeExecutor,444 >(config, polkadot_config, id)445 .await446 .map(|r| r.0)447 .map_err(Into::into);448 }449450 Err(no_runtime_err!(config.chain_spec).into())451 })452 }453 }454}455456impl DefaultConfigurationValues for RelayChainCli {457 fn p2p_listen_port() -> u16 {458 30334459 }460461 fn rpc_ws_listen_port() -> u16 {462 9945463 }464465 fn rpc_http_listen_port() -> u16 {466 9934467 }468469 fn prometheus_listen_port() -> u16 {470 9616471 }472}473474impl CliConfiguration<Self> for RelayChainCli {475 fn shared_params(&self) -> &SharedParams {476 self.base.base.shared_params()477 }478479 fn import_params(&self) -> Option<&ImportParams> {480 self.base.base.import_params()481 }482483 fn network_params(&self) -> Option<&NetworkParams> {484 self.base.base.network_params()485 }486487 fn keystore_params(&self) -> Option<&KeystoreParams> {488 self.base.base.keystore_params()489 }490491 fn base_path(&self) -> Result<Option<BasePath>> {492 Ok(self493 .shared_params()494 .base_path()495 .or_else(|| self.base_path.clone().map(Into::into)))496 }497498 fn rpc_http(&self, default_listen_port: u16) -> Result<Option<SocketAddr>> {499 self.base.base.rpc_http(default_listen_port)500 }501502 fn rpc_ipc(&self) -> Result<Option<String>> {503 self.base.base.rpc_ipc()504 }505506 fn rpc_ws(&self, default_listen_port: u16) -> Result<Option<SocketAddr>> {507 self.base.base.rpc_ws(default_listen_port)508 }509510 fn prometheus_config(511 &self,512 default_listen_port: u16,513 chain_spec: &Box<dyn ChainSpec>,514 ) -> Result<Option<PrometheusConfig>> {515 self.base516 .base517 .prometheus_config(default_listen_port, chain_spec)518 }519520 fn init<F>(521 &self,522 _support_url: &String,523 _impl_version: &String,524 _logger_hook: F,525 _config: &sc_service::Configuration,526 ) -> Result<()> {527 unreachable!("PolkadotCli is never initialized; qed");528 }529530 fn chain_id(&self, is_dev: bool) -> Result<String> {531 let chain_id = self.base.base.chain_id(is_dev)?;532533 Ok(if chain_id.is_empty() {534 self.chain_id.clone().unwrap_or_default()535 } else {536 chain_id537 })538 }539540 fn role(&self, is_dev: bool) -> Result<sc_service::Role> {541 self.base.base.role(is_dev)542 }543544 fn transaction_pool(&self) -> Result<sc_service::config::TransactionPoolOptions> {545 self.base.base.transaction_pool()546 }547548 fn state_cache_child_ratio(&self) -> Result<Option<usize>> {549 self.base.base.state_cache_child_ratio()550 }551552 fn rpc_methods(&self) -> Result<sc_service::config::RpcMethods> {553 self.base.base.rpc_methods()554 }555556 fn rpc_ws_max_connections(&self) -> Result<Option<usize>> {557 self.base.base.rpc_ws_max_connections()558 }559560 fn rpc_cors(&self, is_dev: bool) -> Result<Option<Vec<String>>> {561 self.base.base.rpc_cors(is_dev)562 }563564 fn default_heap_pages(&self) -> Result<Option<u64>> {565 self.base.base.default_heap_pages()566 }567568 fn force_authoring(&self) -> Result<bool> {569 self.base.base.force_authoring()570 }571572 fn disable_grandpa(&self) -> Result<bool> {573 self.base.base.disable_grandpa()574 }575576 fn max_runtime_instances(&self) -> Result<Option<usize>> {577 self.base.base.max_runtime_instances()578 }579580 fn announce_block(&self) -> Result<bool> {581 self.base.base.announce_block()582 }583584 fn telemetry_endpoints(585 &self,586 chain_spec: &Box<dyn ChainSpec>,587 ) -> Result<Option<sc_telemetry::TelemetryEndpoints>> {588 self.base.base.telemetry_endpoints(chain_spec)589 }590}