// Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Pezcumulus. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! CLI options of the omni-node. See [`Command`]. /// Default block time for dev mode when using `--dev` flag. const DEFAULT_DEV_BLOCK_TIME_MS: u64 = 3000; use crate::{ chain_spec::DiskChainSpecLoader, common::{ chain_spec::{Extensions, LoadSpec}, NodeExtraArgs, }, }; use chain_spec_builder::ChainSpecBuilder; use clap::{Command, CommandFactory, FromArgMatches, ValueEnum}; use pezsc_chain_spec::ChainSpec; use pezsc_cli::{ BizinikiwiCli, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, NetworkParams, RpcEndpoint, SharedParams, }; use pezsc_service::{config::PrometheusConfig, BasePath}; use pezsc_storage_monitor::StorageMonitorParams; use std::{ fmt::{Debug, Display, Formatter}, marker::PhantomData, path::PathBuf, }; /// Trait that can be used to customize some of the customer-facing info related to the node binary /// that is being built using this library. /// /// The related info is shown to the customer as part of logs or help messages. /// It does not impact functionality. pub trait CliConfig { /// The version of the resulting node binary. fn impl_version() -> String; /// The description of the resulting node binary. fn description(executable_name: String) -> String { format!( "The command-line arguments provided first will be passed to the teyrchain node, \n\ and the arguments provided after -- will be passed to the relay chain node. \n\ \n\ Example: \n\ \n\ {} [teyrchain-args] -- [relay-chain-args]", executable_name ) } /// The author of the resulting node binary. fn author() -> String; /// The support URL for the resulting node binary. fn support_url() -> String; /// The starting copyright year of the resulting node binary. fn copyright_start_year() -> u16; } /// Sub-commands supported by the collator. #[derive(Debug, clap::Subcommand)] pub enum Subcommand { /// Key management CLI utilities #[command(subcommand)] Key(pezsc_cli::KeySubcommand), /// Build a chain specification. /// /// The `build-spec` command relies on the chain specification built (hard-coded) into the node /// binary, and may utilize the genesis presets of the runtimes also embedded in the nodes /// that support this command. Since `pezkuwi-omni-node` does not contain any embedded /// runtime, and requires a `chain-spec` path to be passed to its `--chain` flag, the command /// isn't bringing significant value as it does for other node binaries (e.g. the /// `pezkuwi` binary). /// /// For a more versatile `chain-spec` manipulation experience please check out the /// `pezkuwi-omni-node chain-spec-builder` subcommand. #[deprecated( note = "build-spec will be removed after 1/06/2025. Use chain-spec-builder instead" )] BuildSpec(pezsc_cli::BuildSpecCmd), /// Validate blocks. CheckBlock(pezsc_cli::CheckBlockCmd), /// Export blocks. ExportBlocks(pezsc_cli::ExportBlocksCmd), /// Export the state of a given block into a chain spec. ExportState(pezsc_cli::ExportStateCmd), /// Import blocks. ImportBlocks(pezsc_cli::ImportBlocksCmd), /// Revert the chain to a previous state. Revert(pezsc_cli::RevertCmd), /// Subcommand for generating and managing chain specifications. /// /// A `chain-spec-builder` subcommand corresponds to the existing `chain-spec-builder` tool /// (), which can be used already standalone. /// It provides the same functionality as the tool but bundled with `pezkuwi-omni-node` to /// enable easier access to chain-spec generation, patching, converting to raw or validation, /// from a single binary, which can be used as a teyrchain node tool /// For a detailed usage guide please check out the standalone tool's crates.io or docs.rs /// pages: /// - /// - ChainSpecBuilder(ChainSpecBuilder), /// Remove the whole chain. PurgeChain(pezcumulus_client_cli::PurgeChainCmd), /// Export the genesis state of the teyrchain. #[command(alias = "export-genesis-state")] ExportGenesisHead(pezcumulus_client_cli::ExportGenesisHeadCommand), /// Export the genesis wasm of the teyrchain. ExportGenesisWasm(pezcumulus_client_cli::ExportGenesisWasmCommand), /// Sub-commands concerned with benchmarking. /// The pezpallet benchmarking moved to the `pezpallet` sub-command. #[command(subcommand)] Benchmark(pezframe_benchmarking_cli::BenchmarkCmd), } /// CLI Options shipped with `pezkuwi-omni-node`. #[derive(clap::Parser)] #[command( propagate_version = true, args_conflicts_with_subcommands = true, subcommand_negates_reqs = true )] pub struct Cli { #[arg(skip)] pub(crate) chain_spec_loader: Option>, /// Possible subcommands. See [`Subcommand`]. #[command(subcommand)] pub subcommand: Option, /// The shared parameters with all pezcumulus-based teyrchain nodes. #[command(flatten)] pub run: pezcumulus_client_cli::RunCmd, /// Parameters for storage monitoring. #[command(flatten)] pub storage_monitor: StorageMonitorParams, /// Start a dev node that produces a block each `dev_block_time` ms. /// /// This is a dev option. It enables a manual sealing, meaning blocks are produced manually /// rather than being part of an actual network consensus process. Using the option won't /// result in starting or connecting to a teyrchain network. The resulting node will work on /// its own, running the wasm blob and artificially producing a block each `dev_block_time` ms, /// as if it was part of a teyrchain. /// /// The `--dev` flag sets the `dev_block_time` to a default value of 3000ms unless explicitly /// provided. #[arg(long, conflicts_with = "instant_seal")] pub dev_block_time: Option, /// Start a dev node with instant seal. /// /// This is a dev option that enables instant sealing, meaning blocks are produced /// immediately when transactions are received, rather than at fixed intervals. /// Using this option won't result in starting or connecting to a teyrchain network. /// The resulting node will work on its own, running the wasm blob and producing blocks /// instantly upon receiving transactions. #[arg(long, conflicts_with = "dev_block_time")] pub instant_seal: bool, /// DEPRECATED: This feature has been stabilized, pLease use `--authoring slot-based` instead. /// /// Use slot-based collator which can handle elastic scaling. /// Use with care, this flag is unstable and subject to change. #[arg(long, conflicts_with = "authoring")] pub experimental_use_slot_based: bool, /// Authoring style to use. #[arg(long, default_value_t = AuthoringPolicy::Lookahead)] pub authoring: AuthoringPolicy, /// Disable automatic hardware benchmarks. /// /// By default these benchmarks are automatically ran at startup and measure /// the CPU speed, the memory bandwidth and the disk speed. /// /// The results are then printed out in the logs, and also sent as part of /// telemetry, if telemetry is enabled. #[arg(long)] pub no_hardware_benchmarks: bool, /// Export all `PoVs` build by this collator to the given folder. /// /// This is useful for debugging issues that are occurring while validating these `PoVs` on the /// relay chain. #[arg(long)] pub export_pov_to_path: Option, /// Relay chain arguments #[arg(raw = true)] pub relay_chain_args: Vec, /// Enable the statement store. /// /// The statement store is a store for statements validated using the runtime API /// `validate_statement`. It should be enabled for chains that provide this runtime API. #[arg(long)] pub enable_statement_store: bool, #[arg(skip)] pub(crate) _phantom: PhantomData, } /// Development sealing mode. #[derive(Debug, Clone, Copy)] pub(crate) enum DevSealMode { /// Produces blocks immediately upon receiving transactions. InstantSeal, /// Produces blocks at fixed time intervals. /// The u64 parameter represents the block time in milliseconds. ManualSeal(u64), } /// Collator implementation to use. #[derive(PartialEq, Debug, ValueEnum, Clone, Copy)] pub enum AuthoringPolicy { /// Use the lookahead collator. Builds a block once per imported relay chain block and /// on relay chain forks. Default for asynchronous backing chains. Lookahead, /// Use the slot-based collator. Builds a block based on time. Can utilize multiple cores, /// always builds on the best relay chain block available. Should be used with elastic-scaling /// chains. SlotBased, } impl Display for AuthoringPolicy { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { AuthoringPolicy::Lookahead => write!(f, "lookahead"), AuthoringPolicy::SlotBased => write!(f, "slot-based"), } } } impl Cli { pub(crate) fn node_extra_args(&self) -> NodeExtraArgs { NodeExtraArgs { authoring_policy: self .experimental_use_slot_based .then(|| AuthoringPolicy::SlotBased) .unwrap_or(self.authoring), export_pov: self.export_pov_to_path.clone(), max_pov_percentage: self.run.experimental_max_pov_percentage, enable_statement_store: self.enable_statement_store, storage_monitor: self.storage_monitor.clone(), } } /// Returns the dev seal mode if the node is in dev mode. pub(crate) fn dev_mode(&self) -> Option { if self.instant_seal { Some(DevSealMode::InstantSeal) } else if let Some(dev_block_time) = self.dev_block_time { Some(DevSealMode::ManualSeal(dev_block_time)) } else if self.run.base.is_dev().unwrap_or(false) { Some(DevSealMode::ManualSeal(DEFAULT_DEV_BLOCK_TIME_MS)) } else { None } } } impl BizinikiwiCli for Cli { fn impl_name() -> String { Self::executable_name() } fn impl_version() -> String { Config::impl_version() } fn description() -> String { Config::description(Self::executable_name()) } fn author() -> String { Config::author() } fn support_url() -> String { Config::support_url() } fn copyright_start_year() -> i32 { Config::copyright_start_year() as i32 } fn load_spec(&self, id: &str) -> Result, String> { match &self.chain_spec_loader { Some(chain_spec_loader) => chain_spec_loader.load_spec(id), None => DiskChainSpecLoader.load_spec(id), } } } /// The relay chain CLI flags. These are passed in after a `--` at the end. #[derive(Debug)] pub struct RelayChainCli { /// The actual relay chain cli object. pub base: pezkuwi_cli::RunCmd, /// Optional chain id that should be passed to the relay chain. pub chain_id: Option, /// The base path that should be used by the relay chain. pub base_path: Option, _phantom: PhantomData, } impl RelayChainCli { fn pezkuwi_cmd() -> Command { let help_template = color_print::cformat!( "The arguments that are passed to the relay chain node. \n\ \n\ RELAY_CHAIN_ARGS: \n\ {{options}}", ); pezkuwi_cli::RunCmd::command().no_binary_name(true).help_template(help_template) } /// Parse the relay chain CLI parameters using the teyrchain `Configuration`. pub fn new<'a>( para_config: &pezsc_service::Configuration, relay_chain_args: impl Iterator, ) -> Self { let pezkuwi_cmd = Self::pezkuwi_cmd(); let matches = pezkuwi_cmd.get_matches_from(relay_chain_args); let base = FromArgMatches::from_arg_matches(&matches).unwrap_or_else(|e| e.exit()); let extension = Extensions::try_get(&*para_config.chain_spec); let chain_id = extension.map(|e| e.relay_chain()); let base_path = para_config.base_path.path().join("pezkuwi"); Self { base, chain_id, base_path: Some(base_path), _phantom: Default::default() } } } impl BizinikiwiCli for RelayChainCli { fn impl_name() -> String { Cli::::impl_name() } fn impl_version() -> String { Cli::::impl_version() } fn description() -> String { Cli::::description() } fn author() -> String { Cli::::author() } fn support_url() -> String { Cli::::support_url() } fn copyright_start_year() -> i32 { Cli::::copyright_start_year() } fn load_spec(&self, id: &str) -> std::result::Result, String> { pezkuwi_cli::Cli::from_iter([Self::executable_name()].iter()).load_spec(id) } } impl DefaultConfigurationValues for RelayChainCli { fn p2p_listen_port() -> u16 { 30334 } fn rpc_listen_port() -> u16 { 9945 } 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) -> pezsc_cli::Result> { Ok(self .shared_params() .base_path()? .or_else(|| self.base_path.clone().map(Into::into))) } fn rpc_addr(&self, default_listen_port: u16) -> pezsc_cli::Result>> { self.base.base.rpc_addr(default_listen_port) } fn prometheus_config( &self, default_listen_port: u16, chain_spec: &Box, ) -> pezsc_cli::Result> { self.base.base.prometheus_config(default_listen_port, chain_spec) } fn init( &self, _support_url: &String, _impl_version: &String, _logger_hook: F, ) -> pezsc_cli::Result<()> where F: FnOnce(&mut pezsc_cli::LoggerBuilder), { unreachable!("PezkuwiCli is never initialized; qed"); } fn chain_id(&self, is_dev: bool) -> pezsc_cli::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) -> pezsc_cli::Result { self.base.base.role(is_dev) } fn transaction_pool( &self, is_dev: bool, ) -> pezsc_cli::Result { self.base.base.transaction_pool(is_dev) } fn trie_cache_maximum_size(&self) -> pezsc_cli::Result> { self.base.base.trie_cache_maximum_size() } fn rpc_methods(&self) -> pezsc_cli::Result { self.base.base.rpc_methods() } fn rpc_max_connections(&self) -> pezsc_cli::Result { self.base.base.rpc_max_connections() } fn rpc_cors(&self, is_dev: bool) -> pezsc_cli::Result>> { self.base.base.rpc_cors(is_dev) } fn default_heap_pages(&self) -> pezsc_cli::Result> { self.base.base.default_heap_pages() } fn force_authoring(&self) -> pezsc_cli::Result { self.base.base.force_authoring() } fn disable_grandpa(&self) -> pezsc_cli::Result { self.base.base.disable_grandpa() } fn max_runtime_instances(&self) -> pezsc_cli::Result> { self.base.base.max_runtime_instances() } fn announce_block(&self) -> pezsc_cli::Result { self.base.base.announce_block() } fn telemetry_endpoints( &self, chain_spec: &Box, ) -> pezsc_cli::Result> { self.base.base.telemetry_endpoints(chain_spec) } fn node_name(&self) -> pezsc_cli::Result { self.base.base.node_name() } }