1const DEFAULT_DEV_BLOCK_TIME_MS: u64 = 3000;
21
22use crate::{
23 chain_spec::DiskChainSpecLoader,
24 common::{
25 chain_spec::{Extensions, LoadSpec},
26 NodeExtraArgs,
27 },
28};
29use chain_spec_builder::ChainSpecBuilder;
30use clap::{Command, CommandFactory, FromArgMatches, ValueEnum};
31use sc_chain_spec::ChainSpec;
32use sc_cli::{
33 CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, NetworkParams,
34 RpcEndpoint, SharedParams, SubstrateCli,
35};
36use sc_service::{config::PrometheusConfig, BasePath};
37use sc_storage_monitor::StorageMonitorParams;
38use std::{
39 fmt::{Display, Formatter},
40 marker::PhantomData,
41 path::PathBuf,
42};
43pub trait CliConfig {
49 fn impl_version() -> String;
51
52 fn description(executable_name: String) -> String {
54 format!(
55 "The command-line arguments provided first will be passed to the parachain node, \n\
56 and the arguments provided after -- will be passed to the relay chain node. \n\
57 \n\
58 Example: \n\
59 \n\
60 {} [parachain-args] -- [relay-chain-args]",
61 executable_name
62 )
63 }
64
65 fn author() -> String;
67
68 fn support_url() -> String;
70
71 fn copyright_start_year() -> u16;
73}
74
75#[derive(Debug, clap::Subcommand)]
77pub enum Subcommand {
78 #[command(subcommand)]
80 Key(sc_cli::KeySubcommand),
81
82 #[deprecated(
94 note = "build-spec will be removed after 1/06/2025. Use chain-spec-builder instead"
95 )]
96 BuildSpec(sc_cli::BuildSpecCmd),
97
98 CheckBlock(sc_cli::CheckBlockCmd),
100
101 ExportBlocks(sc_cli::ExportBlocksCmd),
103
104 ExportState(sc_cli::ExportStateCmd),
106
107 ImportBlocks(sc_cli::ImportBlocksCmd),
109
110 Revert(sc_cli::RevertCmd),
112
113 ChainSpecBuilder(ChainSpecBuilder),
125
126 PurgeChain(cumulus_client_cli::PurgeChainCmd),
128 #[command(alias = "export-genesis-state")]
130 ExportGenesisHead(cumulus_client_cli::ExportGenesisHeadCommand),
131
132 ExportGenesisWasm(cumulus_client_cli::ExportGenesisWasmCommand),
134
135 #[command(subcommand)]
138 Benchmark(frame_benchmarking_cli::BenchmarkCmd),
139}
140
141#[derive(clap::Parser)]
143#[command(
144 propagate_version = true,
145 args_conflicts_with_subcommands = true,
146 subcommand_negates_reqs = true
147)]
148pub struct Cli<Config: CliConfig> {
149 #[arg(skip)]
150 pub(crate) chain_spec_loader: Option<Box<dyn LoadSpec>>,
151
152 #[command(subcommand)]
154 pub subcommand: Option<Subcommand>,
155
156 #[command(flatten)]
158 pub run: cumulus_client_cli::RunCmd,
159
160 #[command(flatten)]
162 pub storage_monitor: StorageMonitorParams,
163
164 #[arg(long, conflicts_with = "instant_seal")]
175 pub dev_block_time: Option<u64>,
176
177 #[arg(long, conflicts_with = "dev_block_time")]
185 pub instant_seal: bool,
186
187 #[arg(long, conflicts_with = "authoring")]
192 pub experimental_use_slot_based: bool,
193
194 #[arg(long, default_value_t = AuthoringPolicy::Lookahead)]
196 pub authoring: AuthoringPolicy,
197
198 #[arg(long)]
206 pub no_hardware_benchmarks: bool,
207
208 #[arg(long)]
213 pub export_pov_to_path: Option<PathBuf>,
214
215 #[arg(raw = true)]
217 pub relay_chain_args: Vec<String>,
218
219 #[arg(long)]
224 pub enable_statement_store: bool,
225
226 #[arg(long, default_value_t = sc_statement_store::DEFAULT_NETWORK_WORKERS)]
230 pub statement_network_workers: usize,
231
232 #[arg(long, default_value_t = sc_statement_store::DEFAULT_RATE_LIMIT)]
239 pub statement_rate_limit: u32,
240
241 #[arg(long, default_value_t = sc_statement_store::DEFAULT_MAX_TOTAL_STATEMENTS)]
247 pub statement_store_max_total_statements: usize,
248
249 #[arg(long, default_value_t = sc_statement_store::DEFAULT_MAX_TOTAL_SIZE)]
255 pub statement_store_max_total_size: usize,
256
257 #[arg(long, default_value_t = sc_statement_store::DEFAULT_PURGE_AFTER_SEC)]
263 pub statement_store_purge_after_sec: u64,
264
265 #[arg(long, value_name = "N", default_value_t = 32)]
267 pub collator_reserved_slots: usize,
268
269 #[command(flatten)]
271 pub hop: sc_hop::HopParams,
272
273 #[arg(skip)]
274 pub(crate) _phantom: PhantomData<Config>,
275}
276
277#[derive(Debug, Clone, Copy)]
279pub(crate) enum DevSealMode {
280 InstantSeal,
282 ManualSeal(u64),
285}
286
287#[derive(PartialEq, Debug, ValueEnum, Clone, Copy)]
289pub enum AuthoringPolicy {
290 Lookahead,
293 SlotBased,
297}
298
299impl Display for AuthoringPolicy {
300 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
301 match self {
302 AuthoringPolicy::Lookahead => write!(f, "lookahead"),
303 AuthoringPolicy::SlotBased => write!(f, "slot-based"),
304 }
305 }
306}
307
308impl<Config: CliConfig> Cli<Config> {
309 pub(crate) fn node_extra_args(&self) -> NodeExtraArgs {
310 NodeExtraArgs {
311 authoring_policy: self
312 .experimental_use_slot_based
313 .then(|| AuthoringPolicy::SlotBased)
314 .unwrap_or(self.authoring),
315 export_pov: self.export_pov_to_path.clone(),
316 max_pov_percentage: self.run.experimental_max_pov_percentage,
317 statement_store_config: self.enable_statement_store.then_some(
318 sc_statement_store::Config {
319 max_total_statements: self.statement_store_max_total_statements,
320 max_total_size: self.statement_store_max_total_size,
321 purge_after_sec: self.statement_store_purge_after_sec,
322 network_workers: self.statement_network_workers,
323 rate_limit: self.statement_rate_limit,
324 },
325 ),
326 storage_monitor: self.storage_monitor.clone(),
327 collator_reserved_slots: self.collator_reserved_slots,
328 hop: self.hop.enabled.then(|| self.hop.clone()),
329 }
330 }
331
332 pub(crate) fn dev_mode(&self) -> Option<DevSealMode> {
334 if self.instant_seal {
335 Some(DevSealMode::InstantSeal)
336 } else if let Some(dev_block_time) = self.dev_block_time {
337 Some(DevSealMode::ManualSeal(dev_block_time))
338 } else if self.run.base.is_dev().unwrap_or(false) {
339 Some(DevSealMode::ManualSeal(DEFAULT_DEV_BLOCK_TIME_MS))
340 } else {
341 None
342 }
343 }
344}
345
346impl<Config: CliConfig> SubstrateCli for Cli<Config> {
347 fn impl_name() -> String {
348 Self::executable_name()
349 }
350
351 fn impl_version() -> String {
352 Config::impl_version()
353 }
354
355 fn description() -> String {
356 Config::description(Self::executable_name())
357 }
358
359 fn author() -> String {
360 Config::author()
361 }
362
363 fn support_url() -> String {
364 Config::support_url()
365 }
366
367 fn copyright_start_year() -> i32 {
368 Config::copyright_start_year() as i32
369 }
370
371 fn load_spec(&self, id: &str) -> Result<Box<dyn ChainSpec>, String> {
372 match &self.chain_spec_loader {
373 Some(chain_spec_loader) => chain_spec_loader.load_spec(id),
374 None => DiskChainSpecLoader.load_spec(id),
375 }
376 }
377}
378
379#[derive(Debug)]
381pub struct RelayChainCli<Config: CliConfig> {
382 pub base: polkadot_cli::RunCmd,
384
385 pub chain_id: Option<String>,
387
388 pub base_path: Option<PathBuf>,
390
391 _phantom: PhantomData<Config>,
392}
393
394impl<Config: CliConfig> RelayChainCli<Config> {
395 fn polkadot_cmd() -> Command {
396 let help_template = color_print::cformat!(
397 "The arguments that are passed to the relay chain node. \n\
398 \n\
399 <bold><underline>RELAY_CHAIN_ARGS:</></> \n\
400 {{options}}",
401 );
402
403 polkadot_cli::RunCmd::command()
404 .no_binary_name(true)
405 .help_template(help_template)
406 }
407
408 pub fn new<'a>(
410 para_config: &sc_service::Configuration,
411 relay_chain_args: impl Iterator<Item = &'a String>,
412 ) -> Self {
413 let polkadot_cmd = Self::polkadot_cmd();
414 let matches = polkadot_cmd.get_matches_from(relay_chain_args);
415 let base = FromArgMatches::from_arg_matches(&matches).unwrap_or_else(|e| e.exit());
416
417 let extension = Extensions::try_get(&*para_config.chain_spec);
418 let chain_id = extension.map(|e| e.relay_chain());
419
420 let base_path = para_config.base_path.path().join("polkadot");
421 Self { base, chain_id, base_path: Some(base_path), _phantom: Default::default() }
422 }
423}
424
425impl<Config: CliConfig> SubstrateCli for RelayChainCli<Config> {
426 fn impl_name() -> String {
427 Cli::<Config>::impl_name()
428 }
429
430 fn impl_version() -> String {
431 Cli::<Config>::impl_version()
432 }
433
434 fn description() -> String {
435 Cli::<Config>::description()
436 }
437
438 fn author() -> String {
439 Cli::<Config>::author()
440 }
441
442 fn support_url() -> String {
443 Cli::<Config>::support_url()
444 }
445
446 fn copyright_start_year() -> i32 {
447 Cli::<Config>::copyright_start_year()
448 }
449
450 fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn ChainSpec>, String> {
451 polkadot_cli::Cli::from_iter([Self::executable_name()].iter()).load_spec(id)
452 }
453}
454
455impl<Config: CliConfig> DefaultConfigurationValues for RelayChainCli<Config> {
456 fn p2p_listen_port() -> u16 {
457 30334
458 }
459
460 fn rpc_listen_port() -> u16 {
461 9945
462 }
463
464 fn prometheus_listen_port() -> u16 {
465 9616
466 }
467}
468
469impl<Config: CliConfig> CliConfiguration<Self> for RelayChainCli<Config> {
470 fn shared_params(&self) -> &SharedParams {
471 self.base.base.shared_params()
472 }
473
474 fn import_params(&self) -> Option<&ImportParams> {
475 self.base.base.import_params()
476 }
477
478 fn network_params(&self) -> Option<&NetworkParams> {
479 self.base.base.network_params()
480 }
481
482 fn keystore_params(&self) -> Option<&KeystoreParams> {
483 self.base.base.keystore_params()
484 }
485
486 fn base_path(&self) -> sc_cli::Result<Option<BasePath>> {
487 Ok(self
488 .shared_params()
489 .base_path()?
490 .or_else(|| self.base_path.clone().map(Into::into)))
491 }
492
493 fn rpc_addr(&self, default_listen_port: u16) -> sc_cli::Result<Option<Vec<RpcEndpoint>>> {
494 self.base.base.rpc_addr(default_listen_port)
495 }
496
497 fn prometheus_config(
498 &self,
499 default_listen_port: u16,
500 chain_spec: &Box<dyn ChainSpec>,
501 ) -> sc_cli::Result<Option<PrometheusConfig>> {
502 self.base.base.prometheus_config(default_listen_port, chain_spec)
503 }
504
505 fn init<F>(
506 &self,
507 _support_url: &String,
508 _impl_version: &String,
509 _logger_hook: F,
510 ) -> sc_cli::Result<()>
511 where
512 F: FnOnce(&mut sc_cli::LoggerBuilder),
513 {
514 unreachable!("PolkadotCli is never initialized; qed");
515 }
516
517 fn chain_id(&self, is_dev: bool) -> sc_cli::Result<String> {
518 let chain_id = self.base.base.chain_id(is_dev)?;
519
520 Ok(if chain_id.is_empty() { self.chain_id.clone().unwrap_or_default() } else { chain_id })
521 }
522
523 fn role(&self, is_dev: bool) -> sc_cli::Result<sc_service::Role> {
524 self.base.base.role(is_dev)
525 }
526
527 fn transaction_pool(
528 &self,
529 is_dev: bool,
530 ) -> sc_cli::Result<sc_service::config::TransactionPoolOptions> {
531 self.base.base.transaction_pool(is_dev)
532 }
533
534 fn trie_cache_maximum_size(&self) -> sc_cli::Result<Option<usize>> {
535 self.base.base.trie_cache_maximum_size()
536 }
537
538 fn rpc_methods(&self) -> sc_cli::Result<sc_service::config::RpcMethods> {
539 self.base.base.rpc_methods()
540 }
541
542 fn rpc_max_connections(&self) -> sc_cli::Result<u32> {
543 self.base.base.rpc_max_connections()
544 }
545
546 fn rpc_cors(&self, is_dev: bool) -> sc_cli::Result<Option<Vec<String>>> {
547 self.base.base.rpc_cors(is_dev)
548 }
549
550 fn default_heap_pages(&self) -> sc_cli::Result<Option<u64>> {
551 self.base.base.default_heap_pages()
552 }
553
554 fn force_authoring(&self) -> sc_cli::Result<bool> {
555 self.base.base.force_authoring()
556 }
557
558 fn disable_grandpa(&self) -> sc_cli::Result<bool> {
559 self.base.base.disable_grandpa()
560 }
561
562 fn max_runtime_instances(&self) -> sc_cli::Result<Option<usize>> {
563 self.base.base.max_runtime_instances()
564 }
565
566 fn announce_block(&self) -> sc_cli::Result<bool> {
567 self.base.base.announce_block()
568 }
569
570 fn telemetry_endpoints(
571 &self,
572 chain_spec: &Box<dyn ChainSpec>,
573 ) -> sc_cli::Result<Option<sc_telemetry::TelemetryEndpoints>> {
574 self.base.base.telemetry_endpoints(chain_spec)
575 }
576
577 fn node_name(&self) -> sc_cli::Result<String> {
578 self.base.base.node_name()
579 }
580}