use crate::{
chain_spec::DiskChainSpecLoader,
common::{
chain_spec::{Extensions, LoadSpec},
NodeExtraArgs,
},
};
use clap::{Command, CommandFactory, FromArgMatches};
use sc_chain_spec::ChainSpec;
use sc_cli::{
CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, NetworkParams,
RpcEndpoint, SharedParams, SubstrateCli,
};
use sc_service::{config::PrometheusConfig, BasePath};
use std::{fmt::Debug, marker::PhantomData, path::PathBuf};
pub trait CliConfig {
fn impl_version() -> String;
fn description(executable_name: String) -> String {
format!(
"The command-line arguments provided first will be passed to the parachain node, \n\
and the arguments provided after -- will be passed to the relay chain node. \n\
\n\
Example: \n\
\n\
{} [parachain-args] -- [relay-chain-args]",
executable_name
)
}
fn author() -> String;
fn support_url() -> String;
fn copyright_start_year() -> u16;
}
#[derive(Debug, clap::Subcommand)]
pub enum Subcommand {
#[command(subcommand)]
Key(sc_cli::KeySubcommand),
BuildSpec(sc_cli::BuildSpecCmd),
CheckBlock(sc_cli::CheckBlockCmd),
ExportBlocks(sc_cli::ExportBlocksCmd),
ExportState(sc_cli::ExportStateCmd),
ImportBlocks(sc_cli::ImportBlocksCmd),
Revert(sc_cli::RevertCmd),
PurgeChain(cumulus_client_cli::PurgeChainCmd),
#[command(alias = "export-genesis-state")]
ExportGenesisHead(cumulus_client_cli::ExportGenesisHeadCommand),
ExportGenesisWasm(cumulus_client_cli::ExportGenesisWasmCommand),
#[command(subcommand)]
Benchmark(frame_benchmarking_cli::BenchmarkCmd),
}
#[derive(clap::Parser)]
#[command(
propagate_version = true,
args_conflicts_with_subcommands = true,
subcommand_negates_reqs = true
)]
pub struct Cli<Config: CliConfig> {
#[arg(skip)]
pub(crate) chain_spec_loader: Option<Box<dyn LoadSpec>>,
#[command(subcommand)]
pub subcommand: Option<Subcommand>,
#[command(flatten)]
pub run: cumulus_client_cli::RunCmd,
#[arg(long)]
pub dev_block_time: Option<u64>,
#[arg(long)]
pub experimental_use_slot_based: bool,
#[arg(long)]
pub no_hardware_benchmarks: bool,
#[arg(long)]
pub export_pov_to_path: Option<PathBuf>,
#[arg(raw = true)]
pub relay_chain_args: Vec<String>,
#[arg(skip)]
pub(crate) _phantom: PhantomData<Config>,
}
impl<Config: CliConfig> Cli<Config> {
pub(crate) fn node_extra_args(&self) -> NodeExtraArgs {
NodeExtraArgs {
use_slot_based_consensus: self.experimental_use_slot_based,
export_pov: self.export_pov_to_path.clone(),
}
}
}
impl<Config: CliConfig> SubstrateCli for Cli<Config> {
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<Box<dyn ChainSpec>, String> {
match &self.chain_spec_loader {
Some(chain_spec_loader) => chain_spec_loader.load_spec(id),
None => DiskChainSpecLoader.load_spec(id),
}
}
}
#[derive(Debug)]
pub struct RelayChainCli<Config: CliConfig> {
pub base: polkadot_cli::RunCmd,
pub chain_id: Option<String>,
pub base_path: Option<PathBuf>,
_phantom: PhantomData<Config>,
}
impl<Config: CliConfig> RelayChainCli<Config> {
fn polkadot_cmd() -> Command {
let help_template = color_print::cformat!(
"The arguments that are passed to the relay chain node. \n\
\n\
<bold><underline>RELAY_CHAIN_ARGS:</></> \n\
{{options}}",
);
polkadot_cli::RunCmd::command()
.no_binary_name(true)
.help_template(help_template)
}
pub fn new<'a>(
para_config: &sc_service::Configuration,
relay_chain_args: impl Iterator<Item = &'a String>,
) -> Self {
let polkadot_cmd = Self::polkadot_cmd();
let matches = polkadot_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.clone());
let base_path = para_config.base_path.path().join("polkadot");
Self { base, chain_id, base_path: Some(base_path), _phantom: Default::default() }
}
}
impl<Config: CliConfig> SubstrateCli for RelayChainCli<Config> {
fn impl_name() -> String {
Cli::<Config>::impl_name()
}
fn impl_version() -> String {
Cli::<Config>::impl_version()
}
fn description() -> String {
Cli::<Config>::description()
}
fn author() -> String {
Cli::<Config>::author()
}
fn support_url() -> String {
Cli::<Config>::support_url()
}
fn copyright_start_year() -> i32 {
Cli::<Config>::copyright_start_year()
}
fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn ChainSpec>, String> {
polkadot_cli::Cli::from_iter([Self::executable_name()].iter()).load_spec(id)
}
}
impl<Config: CliConfig> DefaultConfigurationValues for RelayChainCli<Config> {
fn p2p_listen_port() -> u16 {
30334
}
fn rpc_listen_port() -> u16 {
9945
}
fn prometheus_listen_port() -> u16 {
9616
}
}
impl<Config: CliConfig> CliConfiguration<Self> for RelayChainCli<Config> {
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) -> sc_cli::Result<Option<BasePath>> {
Ok(self
.shared_params()
.base_path()?
.or_else(|| self.base_path.clone().map(Into::into)))
}
fn rpc_addr(&self, default_listen_port: u16) -> sc_cli::Result<Option<Vec<RpcEndpoint>>> {
self.base.base.rpc_addr(default_listen_port)
}
fn prometheus_config(
&self,
default_listen_port: u16,
chain_spec: &Box<dyn ChainSpec>,
) -> sc_cli::Result<Option<PrometheusConfig>> {
self.base.base.prometheus_config(default_listen_port, chain_spec)
}
fn init<F>(
&self,
_support_url: &String,
_impl_version: &String,
_logger_hook: F,
) -> sc_cli::Result<()>
where
F: FnOnce(&mut sc_cli::LoggerBuilder),
{
unreachable!("PolkadotCli is never initialized; qed");
}
fn chain_id(&self, is_dev: bool) -> sc_cli::Result<String> {
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) -> sc_cli::Result<sc_service::Role> {
self.base.base.role(is_dev)
}
fn transaction_pool(
&self,
is_dev: bool,
) -> sc_cli::Result<sc_service::config::TransactionPoolOptions> {
self.base.base.transaction_pool(is_dev)
}
fn trie_cache_maximum_size(&self) -> sc_cli::Result<Option<usize>> {
self.base.base.trie_cache_maximum_size()
}
fn rpc_methods(&self) -> sc_cli::Result<sc_service::config::RpcMethods> {
self.base.base.rpc_methods()
}
fn rpc_max_connections(&self) -> sc_cli::Result<u32> {
self.base.base.rpc_max_connections()
}
fn rpc_cors(&self, is_dev: bool) -> sc_cli::Result<Option<Vec<String>>> {
self.base.base.rpc_cors(is_dev)
}
fn default_heap_pages(&self) -> sc_cli::Result<Option<u64>> {
self.base.base.default_heap_pages()
}
fn force_authoring(&self) -> sc_cli::Result<bool> {
self.base.base.force_authoring()
}
fn disable_grandpa(&self) -> sc_cli::Result<bool> {
self.base.base.disable_grandpa()
}
fn max_runtime_instances(&self) -> sc_cli::Result<Option<usize>> {
self.base.base.max_runtime_instances()
}
fn announce_block(&self) -> sc_cli::Result<bool> {
self.base.base.announce_block()
}
fn telemetry_endpoints(
&self,
chain_spec: &Box<dyn ChainSpec>,
) -> sc_cli::Result<Option<sc_telemetry::TelemetryEndpoints>> {
self.base.base.telemetry_endpoints(chain_spec)
}
fn node_name(&self) -> sc_cli::Result<String> {
self.base.base.node_name()
}
}