1use crate::{
20 chain_spec::DiskChainSpecLoader,
21 common::{
22 chain_spec::{Extensions, LoadSpec},
23 NodeExtraArgs,
24 },
25};
26use chain_spec_builder::ChainSpecBuilder;
27use clap::{Command, CommandFactory, FromArgMatches, ValueEnum};
28use sc_chain_spec::ChainSpec;
29use sc_cli::{
30 CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, NetworkParams,
31 RpcEndpoint, SharedParams, SubstrateCli,
32};
33use sc_service::{config::PrometheusConfig, BasePath};
34use std::{
35 fmt::{Debug, Display, Formatter},
36 marker::PhantomData,
37 path::PathBuf,
38};
39pub trait CliConfig {
45 fn impl_version() -> String;
47
48 fn description(executable_name: String) -> String {
50 format!(
51 "The command-line arguments provided first will be passed to the parachain node, \n\
52 and the arguments provided after -- will be passed to the relay chain node. \n\
53 \n\
54 Example: \n\
55 \n\
56 {} [parachain-args] -- [relay-chain-args]",
57 executable_name
58 )
59 }
60
61 fn author() -> String;
63
64 fn support_url() -> String;
66
67 fn copyright_start_year() -> u16;
69}
70
71#[derive(Debug, clap::Subcommand)]
73pub enum Subcommand {
74 #[command(subcommand)]
76 Key(sc_cli::KeySubcommand),
77
78 #[deprecated(
90 note = "build-spec will be removed after 1/06/2025. Use chain-spec-builder instead"
91 )]
92 BuildSpec(sc_cli::BuildSpecCmd),
93
94 CheckBlock(sc_cli::CheckBlockCmd),
96
97 ExportBlocks(sc_cli::ExportBlocksCmd),
99
100 ExportState(sc_cli::ExportStateCmd),
102
103 ImportBlocks(sc_cli::ImportBlocksCmd),
105
106 Revert(sc_cli::RevertCmd),
108
109 ChainSpecBuilder(ChainSpecBuilder),
121
122 PurgeChain(cumulus_client_cli::PurgeChainCmd),
124 #[command(alias = "export-genesis-state")]
126 ExportGenesisHead(cumulus_client_cli::ExportGenesisHeadCommand),
127
128 ExportGenesisWasm(cumulus_client_cli::ExportGenesisWasmCommand),
130
131 #[command(subcommand)]
134 Benchmark(frame_benchmarking_cli::BenchmarkCmd),
135}
136
137#[derive(clap::Parser)]
139#[command(
140 propagate_version = true,
141 args_conflicts_with_subcommands = true,
142 subcommand_negates_reqs = true
143)]
144pub struct Cli<Config: CliConfig> {
145 #[arg(skip)]
146 pub(crate) chain_spec_loader: Option<Box<dyn LoadSpec>>,
147
148 #[command(subcommand)]
150 pub subcommand: Option<Subcommand>,
151
152 #[command(flatten)]
154 pub run: cumulus_client_cli::RunCmd,
155
156 #[arg(long)]
167 pub dev_block_time: Option<u64>,
168
169 #[arg(long, conflicts_with = "authoring")]
174 pub experimental_use_slot_based: bool,
175
176 #[arg(long, default_value_t = AuthoringPolicy::Lookahead)]
178 pub authoring: AuthoringPolicy,
179
180 #[arg(long)]
188 pub no_hardware_benchmarks: bool,
189
190 #[arg(long)]
195 pub export_pov_to_path: Option<PathBuf>,
196
197 #[arg(raw = true)]
199 pub relay_chain_args: Vec<String>,
200
201 #[arg(long)]
206 pub enable_statement_store: bool,
207
208 #[arg(skip)]
209 pub(crate) _phantom: PhantomData<Config>,
210}
211
212#[derive(PartialEq, Debug, ValueEnum, Clone, Copy)]
214pub enum AuthoringPolicy {
215 Lookahead,
218 SlotBased,
222}
223
224impl Display for AuthoringPolicy {
225 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
226 match self {
227 AuthoringPolicy::Lookahead => write!(f, "lookahead"),
228 AuthoringPolicy::SlotBased => write!(f, "slot-based"),
229 }
230 }
231}
232
233impl<Config: CliConfig> Cli<Config> {
234 pub(crate) fn node_extra_args(&self) -> NodeExtraArgs {
235 NodeExtraArgs {
236 authoring_policy: self
237 .experimental_use_slot_based
238 .then(|| AuthoringPolicy::SlotBased)
239 .unwrap_or(self.authoring),
240 export_pov: self.export_pov_to_path.clone(),
241 max_pov_percentage: self.run.experimental_max_pov_percentage,
242 enable_statement_store: self.enable_statement_store,
243 }
244 }
245}
246
247impl<Config: CliConfig> SubstrateCli for Cli<Config> {
248 fn impl_name() -> String {
249 Self::executable_name()
250 }
251
252 fn impl_version() -> String {
253 Config::impl_version()
254 }
255
256 fn description() -> String {
257 Config::description(Self::executable_name())
258 }
259
260 fn author() -> String {
261 Config::author()
262 }
263
264 fn support_url() -> String {
265 Config::support_url()
266 }
267
268 fn copyright_start_year() -> i32 {
269 Config::copyright_start_year() as i32
270 }
271
272 fn load_spec(&self, id: &str) -> Result<Box<dyn ChainSpec>, String> {
273 match &self.chain_spec_loader {
274 Some(chain_spec_loader) => chain_spec_loader.load_spec(id),
275 None => DiskChainSpecLoader.load_spec(id),
276 }
277 }
278}
279
280#[derive(Debug)]
282pub struct RelayChainCli<Config: CliConfig> {
283 pub base: polkadot_cli::RunCmd,
285
286 pub chain_id: Option<String>,
288
289 pub base_path: Option<PathBuf>,
291
292 _phantom: PhantomData<Config>,
293}
294
295impl<Config: CliConfig> RelayChainCli<Config> {
296 fn polkadot_cmd() -> Command {
297 let help_template = color_print::cformat!(
298 "The arguments that are passed to the relay chain node. \n\
299 \n\
300 <bold><underline>RELAY_CHAIN_ARGS:</></> \n\
301 {{options}}",
302 );
303
304 polkadot_cli::RunCmd::command()
305 .no_binary_name(true)
306 .help_template(help_template)
307 }
308
309 pub fn new<'a>(
311 para_config: &sc_service::Configuration,
312 relay_chain_args: impl Iterator<Item = &'a String>,
313 ) -> Self {
314 let polkadot_cmd = Self::polkadot_cmd();
315 let matches = polkadot_cmd.get_matches_from(relay_chain_args);
316 let base = FromArgMatches::from_arg_matches(&matches).unwrap_or_else(|e| e.exit());
317
318 let extension = Extensions::try_get(&*para_config.chain_spec);
319 let chain_id = extension.map(|e| e.relay_chain.clone());
320
321 let base_path = para_config.base_path.path().join("polkadot");
322 Self { base, chain_id, base_path: Some(base_path), _phantom: Default::default() }
323 }
324}
325
326impl<Config: CliConfig> SubstrateCli for RelayChainCli<Config> {
327 fn impl_name() -> String {
328 Cli::<Config>::impl_name()
329 }
330
331 fn impl_version() -> String {
332 Cli::<Config>::impl_version()
333 }
334
335 fn description() -> String {
336 Cli::<Config>::description()
337 }
338
339 fn author() -> String {
340 Cli::<Config>::author()
341 }
342
343 fn support_url() -> String {
344 Cli::<Config>::support_url()
345 }
346
347 fn copyright_start_year() -> i32 {
348 Cli::<Config>::copyright_start_year()
349 }
350
351 fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn ChainSpec>, String> {
352 polkadot_cli::Cli::from_iter([Self::executable_name()].iter()).load_spec(id)
353 }
354}
355
356impl<Config: CliConfig> DefaultConfigurationValues for RelayChainCli<Config> {
357 fn p2p_listen_port() -> u16 {
358 30334
359 }
360
361 fn rpc_listen_port() -> u16 {
362 9945
363 }
364
365 fn prometheus_listen_port() -> u16 {
366 9616
367 }
368}
369
370impl<Config: CliConfig> CliConfiguration<Self> for RelayChainCli<Config> {
371 fn shared_params(&self) -> &SharedParams {
372 self.base.base.shared_params()
373 }
374
375 fn import_params(&self) -> Option<&ImportParams> {
376 self.base.base.import_params()
377 }
378
379 fn network_params(&self) -> Option<&NetworkParams> {
380 self.base.base.network_params()
381 }
382
383 fn keystore_params(&self) -> Option<&KeystoreParams> {
384 self.base.base.keystore_params()
385 }
386
387 fn base_path(&self) -> sc_cli::Result<Option<BasePath>> {
388 Ok(self
389 .shared_params()
390 .base_path()?
391 .or_else(|| self.base_path.clone().map(Into::into)))
392 }
393
394 fn rpc_addr(&self, default_listen_port: u16) -> sc_cli::Result<Option<Vec<RpcEndpoint>>> {
395 self.base.base.rpc_addr(default_listen_port)
396 }
397
398 fn prometheus_config(
399 &self,
400 default_listen_port: u16,
401 chain_spec: &Box<dyn ChainSpec>,
402 ) -> sc_cli::Result<Option<PrometheusConfig>> {
403 self.base.base.prometheus_config(default_listen_port, chain_spec)
404 }
405
406 fn init<F>(
407 &self,
408 _support_url: &String,
409 _impl_version: &String,
410 _logger_hook: F,
411 ) -> sc_cli::Result<()>
412 where
413 F: FnOnce(&mut sc_cli::LoggerBuilder),
414 {
415 unreachable!("PolkadotCli is never initialized; qed");
416 }
417
418 fn chain_id(&self, is_dev: bool) -> sc_cli::Result<String> {
419 let chain_id = self.base.base.chain_id(is_dev)?;
420
421 Ok(if chain_id.is_empty() { self.chain_id.clone().unwrap_or_default() } else { chain_id })
422 }
423
424 fn role(&self, is_dev: bool) -> sc_cli::Result<sc_service::Role> {
425 self.base.base.role(is_dev)
426 }
427
428 fn transaction_pool(
429 &self,
430 is_dev: bool,
431 ) -> sc_cli::Result<sc_service::config::TransactionPoolOptions> {
432 self.base.base.transaction_pool(is_dev)
433 }
434
435 fn trie_cache_maximum_size(&self) -> sc_cli::Result<Option<usize>> {
436 self.base.base.trie_cache_maximum_size()
437 }
438
439 fn rpc_methods(&self) -> sc_cli::Result<sc_service::config::RpcMethods> {
440 self.base.base.rpc_methods()
441 }
442
443 fn rpc_max_connections(&self) -> sc_cli::Result<u32> {
444 self.base.base.rpc_max_connections()
445 }
446
447 fn rpc_cors(&self, is_dev: bool) -> sc_cli::Result<Option<Vec<String>>> {
448 self.base.base.rpc_cors(is_dev)
449 }
450
451 fn default_heap_pages(&self) -> sc_cli::Result<Option<u64>> {
452 self.base.base.default_heap_pages()
453 }
454
455 fn force_authoring(&self) -> sc_cli::Result<bool> {
456 self.base.base.force_authoring()
457 }
458
459 fn disable_grandpa(&self) -> sc_cli::Result<bool> {
460 self.base.base.disable_grandpa()
461 }
462
463 fn max_runtime_instances(&self) -> sc_cli::Result<Option<usize>> {
464 self.base.base.max_runtime_instances()
465 }
466
467 fn announce_block(&self) -> sc_cli::Result<bool> {
468 self.base.base.announce_block()
469 }
470
471 fn telemetry_endpoints(
472 &self,
473 chain_spec: &Box<dyn ChainSpec>,
474 ) -> sc_cli::Result<Option<sc_telemetry::TelemetryEndpoints>> {
475 self.base.base.telemetry_endpoints(chain_spec)
476 }
477
478 fn node_name(&self) -> sc_cli::Result<String> {
479 self.base.base.node_name()
480 }
481}