referrerpolicy=no-referrer-when-downgrade

polkadot_omni_node_lib/
cli.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Cumulus.
3// SPDX-License-Identifier: Apache-2.0
4
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// 	http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17//! CLI options of the omni-node. See [`Command`].
18
19/// Default block time for dev mode when using `--dev` flag.
20const 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};
43/// Trait that can be used to customize some of the customer-facing info related to the node binary
44/// that is being built using this library.
45///
46/// The related info is shown to the customer as part of logs or help messages.
47/// It does not impact functionality.
48pub trait CliConfig {
49	/// The version of the resulting node binary.
50	fn impl_version() -> String;
51
52	/// The description of the resulting node binary.
53	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	/// The author of the resulting node binary.
66	fn author() -> String;
67
68	/// The support URL for the resulting node binary.
69	fn support_url() -> String;
70
71	/// The starting copyright year of the resulting node binary.
72	fn copyright_start_year() -> u16;
73}
74
75/// Sub-commands supported by the collator.
76#[derive(Debug, clap::Subcommand)]
77pub enum Subcommand {
78	/// Key management CLI utilities
79	#[command(subcommand)]
80	Key(sc_cli::KeySubcommand),
81
82	/// Build a chain specification.
83	///
84	/// The `build-spec` command relies on the chain specification built (hard-coded) into the node
85	/// binary, and may utilize the genesis presets of the runtimes  also embedded in the nodes
86	/// that support  this command. Since `polkadot-omni-node` does not contain any embedded
87	/// runtime, and requires a `chain-spec` path to be passed to its `--chain` flag, the command
88	/// isn't bringing significant value as it does for other node binaries (e.g. the
89	///  `polkadot` binary).
90	///
91	/// For a more versatile `chain-spec` manipulation experience please check out the
92	/// `polkadot-omni-node chain-spec-builder` subcommand.
93	#[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	/// Validate blocks.
99	CheckBlock(sc_cli::CheckBlockCmd),
100
101	/// Export blocks.
102	ExportBlocks(sc_cli::ExportBlocksCmd),
103
104	/// Export the state of a given block into a chain spec.
105	ExportState(sc_cli::ExportStateCmd),
106
107	/// Import blocks.
108	ImportBlocks(sc_cli::ImportBlocksCmd),
109
110	/// Revert the chain to a previous state.
111	Revert(sc_cli::RevertCmd),
112
113	/// Subcommand for generating and managing chain specifications.
114	///
115	/// A `chain-spec-builder` subcommand corresponds to the existing `chain-spec-builder` tool
116	/// (<https://crates.io/crates/staging-chain-spec-builder>), which can be used already standalone.
117	/// It provides the same functionality as the tool but bundled with `polkadot-omni-node` to
118	/// enable easier access to chain-spec generation, patching, converting to raw or validation,
119	/// from a single binary, which can be used as a parachain node tool
120	/// For a detailed usage guide please check out the standalone tool's crates.io or docs.rs
121	/// pages:
122	/// - <https://crates.io/crates/staging-chain-spec-builder>
123	/// - <https://docs.rs/staging-chain-spec-builder/latest/staging_chain_spec_builder/>
124	ChainSpecBuilder(ChainSpecBuilder),
125
126	/// Remove the whole chain.
127	PurgeChain(cumulus_client_cli::PurgeChainCmd),
128	/// Export the genesis state of the parachain.
129	#[command(alias = "export-genesis-state")]
130	ExportGenesisHead(cumulus_client_cli::ExportGenesisHeadCommand),
131
132	/// Export the genesis wasm of the parachain.
133	ExportGenesisWasm(cumulus_client_cli::ExportGenesisWasmCommand),
134
135	/// Sub-commands concerned with benchmarking.
136	/// The pallet benchmarking moved to the `pallet` sub-command.
137	#[command(subcommand)]
138	Benchmark(frame_benchmarking_cli::BenchmarkCmd),
139}
140
141/// CLI Options shipped with `polkadot-omni-node`.
142#[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	/// Possible subcommands. See [`Subcommand`].
153	#[command(subcommand)]
154	pub subcommand: Option<Subcommand>,
155
156	/// The shared parameters with all cumulus-based parachain nodes.
157	#[command(flatten)]
158	pub run: cumulus_client_cli::RunCmd,
159
160	/// Parameters for storage monitoring.
161	#[command(flatten)]
162	pub storage_monitor: StorageMonitorParams,
163
164	/// Start a dev node that produces a block each `dev_block_time` ms.
165	///
166	/// This is a dev option. It enables a manual sealing, meaning blocks are produced manually
167	/// rather than being part of an actual network consensus process. Using the option won't
168	/// result in starting or connecting to a parachain network. The resulting node will work on
169	/// its own, running the wasm blob and artificially producing a block each `dev_block_time` ms,
170	/// as if it was part of a parachain.
171	///
172	/// The `--dev` flag sets the `dev_block_time` to a default value of 3000ms unless explicitly
173	/// provided.
174	#[arg(long, conflicts_with = "instant_seal")]
175	pub dev_block_time: Option<u64>,
176
177	/// Start a dev node with instant seal.
178	///
179	/// This is a dev option that enables instant sealing, meaning blocks are produced
180	/// immediately when transactions are received, rather than at fixed intervals.
181	/// Using this option won't result in starting or connecting to a parachain network.
182	/// The resulting node will work on its own, running the wasm blob and producing blocks
183	/// instantly upon receiving transactions.
184	#[arg(long, conflicts_with = "dev_block_time")]
185	pub instant_seal: bool,
186
187	/// DEPRECATED: This feature has been stabilized, pLease use `--authoring slot-based` instead.
188	///
189	/// Use slot-based collator which can handle elastic scaling.
190	/// Use with care, this flag is unstable and subject to change.
191	#[arg(long, conflicts_with = "authoring")]
192	pub experimental_use_slot_based: bool,
193
194	/// Authoring style to use.
195	#[arg(long, default_value_t = AuthoringPolicy::Lookahead)]
196	pub authoring: AuthoringPolicy,
197
198	/// Disable automatic hardware benchmarks.
199	///
200	/// By default these benchmarks are automatically ran at startup and measure
201	/// the CPU speed, the memory bandwidth and the disk speed.
202	///
203	/// The results are then printed out in the logs, and also sent as part of
204	/// telemetry, if telemetry is enabled.
205	#[arg(long)]
206	pub no_hardware_benchmarks: bool,
207
208	/// Export all `PoVs` build by this collator to the given folder.
209	///
210	/// This is useful for debugging issues that are occurring while validating these `PoVs` on the
211	/// relay chain.
212	#[arg(long)]
213	pub export_pov_to_path: Option<PathBuf>,
214
215	/// Relay chain arguments
216	#[arg(raw = true)]
217	pub relay_chain_args: Vec<String>,
218
219	/// Enable the statement store.
220	///
221	/// The statement store reads the storage of the chain to determine if users are allowed to
222	/// store statements or not.
223	#[arg(long)]
224	pub enable_statement_store: bool,
225
226	/// Number of concurrent workers for statement validation from the network.
227	///
228	/// Only relevant when `--enable-statement-store` is used.
229	#[arg(long, default_value_t = sc_statement_store::DEFAULT_NETWORK_WORKERS)]
230	pub statement_network_workers: usize,
231
232	/// Maximum statements per second per peer before rate limiting kicks in.
233	///
234	/// Uses a token bucket algorithm that allows short bursts up to this limit
235	/// while enforcing the average rate over time.
236	///
237	/// Only relevant when `--enable-statement-store` is used.
238	#[arg(long, default_value_t = sc_statement_store::DEFAULT_RATE_LIMIT)]
239	pub statement_rate_limit: u32,
240
241	/// Maximum number of statements the statement store can hold.
242	///
243	/// Once this limit is reached, lower-priority statements may be evicted.
244	///
245	/// Only relevant when `--enable-statement-store` is used.
246	#[arg(long, default_value_t = sc_statement_store::DEFAULT_MAX_TOTAL_STATEMENTS)]
247	pub statement_store_max_total_statements: usize,
248
249	/// Maximum total data size (in bytes) the statement store can hold.
250	///
251	/// Once this limit is reached, lower-priority statements may be evicted.
252	///
253	/// Only relevant when `--enable-statement-store` is used.
254	#[arg(long, default_value_t = sc_statement_store::DEFAULT_MAX_TOTAL_SIZE)]
255	pub statement_store_max_total_size: usize,
256
257	/// Number of seconds for which removed statements won't be allowed to be added back.
258	///
259	/// This prevents old statements from being re-propagated on the network.
260	///
261	/// Only relevant when `--enable-statement-store` is used.
262	#[arg(long, default_value_t = sc_statement_store::DEFAULT_PURGE_AFTER_SEC)]
263	pub statement_store_purge_after_sec: u64,
264
265	/// Affinity topic advertised by this node. Repeatable; each value is a 32-byte hex
266	/// hash.
267	///
268	/// Makes the node responsible for finding and storing all statements with topics in this
269	/// list.
270	///
271	/// Only relevant when `--enable-statement-store` is used.
272	///
273	/// Hidden: takes effect only on the experimental v2 DHT statement path.
274	#[arg(long = "statement-affinity-topic", value_name = "TOPIC", hide = true)]
275	pub statement_affinity_topics: Vec<sc_statement_store::Topic>,
276
277	/// File with affinity topics advertised by this node, one 32-byte hex topic per line.
278	/// Blank lines and lines starting with `#` are skipped. Adds to `--statement-affinity-topic`.
279	///
280	/// Only relevant when `--enable-statement-store` is used.
281	///
282	/// Hidden: takes effect only on the experimental v2 DHT statement path.
283	#[arg(long = "statement-affinity-topics-file", value_name = "PATH", hide = true)]
284	pub statement_affinity_topics_file: Option<sc_statement_store::AffinityTopicsFile>,
285
286	/// Number of K-closest peers (replication factor) used for DHT-affinity statement routing.
287	///
288	/// Only relevant when `--enable-statement-store` is used.
289	///
290	/// Hidden: takes effect only on the experimental v2 DHT statement path.
291	#[arg(
292		long,
293		value_name = "K",
294		default_value_t = sc_statement_store::DEFAULT_REPLICATION_FACTOR,
295		hide = true
296	)]
297	pub statement_replication_factor: std::num::NonZeroUsize,
298
299	/// Number of peers to gossip a statement to in addition to DHT-affinity routing targets.
300	///
301	/// Only relevant when `--enable-statement-store` is used.
302	///
303	/// Hidden: takes effect only on the experimental v2 DHT statement path.
304	#[arg(
305		long,
306		value_name = "N",
307		default_value_t = sc_statement_store::DEFAULT_GOSSIP_TARGET,
308		hide = true
309	)]
310	pub statement_gossip_target: std::num::NonZeroUsize,
311
312	/// False-positive rate of the topic-affinity bloom filter this node advertises.
313	///
314	/// Only relevant when `--enable-statement-store` is used.
315	///
316	/// Hidden: takes effect only on the experimental v2 DHT statement path.
317	#[arg(
318		long,
319		value_name = "RATE",
320		default_value_t = sc_statement_store::DEFAULT_BLOOM_FALSE_POS_RATE,
321		hide = true
322	)]
323	pub statement_bloom_false_positive_rate: f64,
324
325	/// Seed of the topic-affinity bloom filter this node advertises. Defaults to a random
326	/// value per node.
327	///
328	/// Only relevant when `--enable-statement-store` is used.
329	///
330	/// Hidden: takes effect only on the experimental v2 DHT statement path.
331	#[arg(long, value_name = "SEED", hide = true)]
332	pub statement_bloom_seed: Option<u128>,
333
334	/// Maximum total data size (in bytes) of the statements the store keeps for DHT affinity.
335	/// Defaults to `--statement-store-max-total-size`.
336	///
337	/// Only relevant when `--enable-statement-store` is used.
338	///
339	/// Hidden: takes effect only on the experimental v2 DHT statement path.
340	#[arg(long, value_name = "BYTES", hide = true)]
341	pub statement_store_max_dht_affinity_size: Option<usize>,
342
343	/// Maximum total data size (in bytes) of the transient statements, kept only until
344	/// propagated. Defaults to `--statement-store-max-total-size`.
345	///
346	/// Only relevant when `--enable-statement-store` is used.
347	///
348	/// Hidden: takes effect only on the experimental v2 DHT statement path.
349	#[arg(long, value_name = "BYTES", hide = true)]
350	pub statement_store_max_transient_size: Option<usize>,
351
352	/// Upper bound on collator reserved-peer slots.
353	#[arg(long, value_name = "N", default_value_t = 32)]
354	pub collator_reserved_slots: usize,
355
356	/// HOP (Hand-Off Protocol) configuration parameters.
357	#[command(flatten)]
358	pub hop: sc_hop::HopParams,
359
360	#[arg(skip)]
361	pub(crate) _phantom: PhantomData<Config>,
362}
363
364/// Development sealing mode.
365#[derive(Debug, Clone, Copy)]
366pub(crate) enum DevSealMode {
367	/// Produces blocks immediately upon receiving transactions.
368	InstantSeal,
369	/// Produces blocks at fixed time intervals.
370	/// The u64 parameter represents the block time in milliseconds.
371	ManualSeal(u64),
372}
373
374/// Collator implementation to use.
375#[derive(PartialEq, Debug, ValueEnum, Clone, Copy)]
376pub enum AuthoringPolicy {
377	/// Use the lookahead collator. Builds a block once per imported relay chain block and
378	/// on relay chain forks. Default for asynchronous backing chains.
379	Lookahead,
380	/// Use the slot-based collator. Builds a block based on time. Can utilize multiple cores,
381	/// always builds on the best relay chain block available. Should be used with elastic-scaling
382	/// chains.
383	SlotBased,
384}
385
386impl Display for AuthoringPolicy {
387	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
388		match self {
389			AuthoringPolicy::Lookahead => write!(f, "lookahead"),
390			AuthoringPolicy::SlotBased => write!(f, "slot-based"),
391		}
392	}
393}
394
395impl<Config: CliConfig> Cli<Config> {
396	pub(crate) fn node_extra_args(&self) -> NodeExtraArgs {
397		NodeExtraArgs {
398			authoring_policy: self
399				.experimental_use_slot_based
400				.then(|| AuthoringPolicy::SlotBased)
401				.unwrap_or(self.authoring),
402			export_pov: self.export_pov_to_path.clone(),
403			max_pov_percentage: self.run.experimental_max_pov_percentage,
404			statement_store_config: self.enable_statement_store.then_some(
405				sc_statement_store::Config {
406					max_total_statements: self.statement_store_max_total_statements,
407					max_total_size: self.statement_store_max_total_size,
408					purge_after_sec: self.statement_store_purge_after_sec,
409					network_workers: self.statement_network_workers,
410					rate_limit: self.statement_rate_limit,
411					v2dht: sc_network_statement::v2dht_enabled().then(|| {
412						sc_statement_store::V2DhtConfig {
413							affinity_topics: self.statement_affinity_topics.clone(),
414							affinity_topics_file: self.statement_affinity_topics_file.clone(),
415							bloom_false_pos_rate: self.statement_bloom_false_positive_rate,
416							bloom_seed: self.statement_bloom_seed,
417							replication_factor: self.statement_replication_factor,
418							gossip_target: self.statement_gossip_target,
419							dht_affinity_max_size: self.statement_store_max_dht_affinity_size,
420							transient_max_size: self.statement_store_max_transient_size,
421						}
422					}),
423				},
424			),
425			storage_monitor: self.storage_monitor.clone(),
426			collator_reserved_slots: self.collator_reserved_slots,
427			hop: self.hop.enabled.then(|| self.hop.clone()),
428		}
429	}
430
431	/// Returns the dev seal mode if the node is in dev mode.
432	pub(crate) fn dev_mode(&self) -> Option<DevSealMode> {
433		if self.instant_seal {
434			Some(DevSealMode::InstantSeal)
435		} else if let Some(dev_block_time) = self.dev_block_time {
436			Some(DevSealMode::ManualSeal(dev_block_time))
437		} else if self.run.base.is_dev().unwrap_or(false) {
438			Some(DevSealMode::ManualSeal(DEFAULT_DEV_BLOCK_TIME_MS))
439		} else {
440			None
441		}
442	}
443}
444
445impl<Config: CliConfig> SubstrateCli for Cli<Config> {
446	fn impl_name() -> String {
447		Self::executable_name()
448	}
449
450	fn impl_version() -> String {
451		Config::impl_version()
452	}
453
454	fn description() -> String {
455		Config::description(Self::executable_name())
456	}
457
458	fn author() -> String {
459		Config::author()
460	}
461
462	fn support_url() -> String {
463		Config::support_url()
464	}
465
466	fn copyright_start_year() -> i32 {
467		Config::copyright_start_year() as i32
468	}
469
470	fn load_spec(&self, id: &str) -> Result<Box<dyn ChainSpec>, String> {
471		match &self.chain_spec_loader {
472			Some(chain_spec_loader) => chain_spec_loader.load_spec(id),
473			None => DiskChainSpecLoader.load_spec(id),
474		}
475	}
476}
477
478/// The relay chain CLI flags. These are passed in after a `--` at the end.
479#[derive(Debug)]
480pub struct RelayChainCli<Config: CliConfig> {
481	/// The actual relay chain cli object.
482	pub base: polkadot_cli::RunCmd,
483
484	/// Optional chain id that should be passed to the relay chain.
485	pub chain_id: Option<String>,
486
487	/// The base path that should be used by the relay chain.
488	pub base_path: Option<PathBuf>,
489
490	_phantom: PhantomData<Config>,
491}
492
493impl<Config: CliConfig> RelayChainCli<Config> {
494	fn polkadot_cmd() -> Command {
495		let help_template = color_print::cformat!(
496			"The arguments that are passed to the relay chain node. \n\
497			\n\
498			<bold><underline>RELAY_CHAIN_ARGS:</></> \n\
499			{{options}}",
500		);
501
502		polkadot_cli::RunCmd::command()
503			.no_binary_name(true)
504			.help_template(help_template)
505	}
506
507	/// Parse the relay chain CLI parameters using the parachain `Configuration`.
508	pub fn new<'a>(
509		para_config: &sc_service::Configuration,
510		relay_chain_args: impl Iterator<Item = &'a String>,
511	) -> Self {
512		let polkadot_cmd = Self::polkadot_cmd();
513		let matches = polkadot_cmd.get_matches_from(relay_chain_args);
514		let base = FromArgMatches::from_arg_matches(&matches).unwrap_or_else(|e| e.exit());
515
516		let extension = Extensions::try_get(&*para_config.chain_spec);
517		let chain_id = extension.map(|e| e.relay_chain());
518
519		let base_path = para_config.base_path.path().join("polkadot");
520		Self { base, chain_id, base_path: Some(base_path), _phantom: Default::default() }
521	}
522}
523
524impl<Config: CliConfig> SubstrateCli for RelayChainCli<Config> {
525	fn impl_name() -> String {
526		Cli::<Config>::impl_name()
527	}
528
529	fn impl_version() -> String {
530		Cli::<Config>::impl_version()
531	}
532
533	fn description() -> String {
534		Cli::<Config>::description()
535	}
536
537	fn author() -> String {
538		Cli::<Config>::author()
539	}
540
541	fn support_url() -> String {
542		Cli::<Config>::support_url()
543	}
544
545	fn copyright_start_year() -> i32 {
546		Cli::<Config>::copyright_start_year()
547	}
548
549	fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn ChainSpec>, String> {
550		polkadot_cli::Cli::from_iter([Self::executable_name()].iter()).load_spec(id)
551	}
552}
553
554impl<Config: CliConfig> DefaultConfigurationValues for RelayChainCli<Config> {
555	fn p2p_listen_port() -> u16 {
556		30334
557	}
558
559	fn rpc_listen_port() -> u16 {
560		9945
561	}
562
563	fn prometheus_listen_port() -> u16 {
564		9616
565	}
566}
567
568impl<Config: CliConfig> CliConfiguration<Self> for RelayChainCli<Config> {
569	fn shared_params(&self) -> &SharedParams {
570		self.base.base.shared_params()
571	}
572
573	fn import_params(&self) -> Option<&ImportParams> {
574		self.base.base.import_params()
575	}
576
577	fn network_params(&self) -> Option<&NetworkParams> {
578		self.base.base.network_params()
579	}
580
581	fn keystore_params(&self) -> Option<&KeystoreParams> {
582		self.base.base.keystore_params()
583	}
584
585	fn base_path(&self) -> sc_cli::Result<Option<BasePath>> {
586		Ok(self
587			.shared_params()
588			.base_path()?
589			.or_else(|| self.base_path.clone().map(Into::into)))
590	}
591
592	fn rpc_addr(&self, default_listen_port: u16) -> sc_cli::Result<Option<Vec<RpcEndpoint>>> {
593		self.base.base.rpc_addr(default_listen_port)
594	}
595
596	fn prometheus_config(
597		&self,
598		default_listen_port: u16,
599		chain_spec: &Box<dyn ChainSpec>,
600	) -> sc_cli::Result<Option<PrometheusConfig>> {
601		self.base.base.prometheus_config(default_listen_port, chain_spec)
602	}
603
604	fn init<F>(
605		&self,
606		_support_url: &String,
607		_impl_version: &String,
608		_logger_hook: F,
609	) -> sc_cli::Result<()>
610	where
611		F: FnOnce(&mut sc_cli::LoggerBuilder),
612	{
613		unreachable!("PolkadotCli is never initialized; qed");
614	}
615
616	fn chain_id(&self, is_dev: bool) -> sc_cli::Result<String> {
617		let chain_id = self.base.base.chain_id(is_dev)?;
618
619		Ok(if chain_id.is_empty() { self.chain_id.clone().unwrap_or_default() } else { chain_id })
620	}
621
622	fn role(&self, is_dev: bool) -> sc_cli::Result<sc_service::Role> {
623		self.base.base.role(is_dev)
624	}
625
626	fn transaction_pool(
627		&self,
628		is_dev: bool,
629	) -> sc_cli::Result<sc_service::config::TransactionPoolOptions> {
630		self.base.base.transaction_pool(is_dev)
631	}
632
633	fn trie_cache_maximum_size(&self) -> sc_cli::Result<Option<usize>> {
634		self.base.base.trie_cache_maximum_size()
635	}
636
637	fn rpc_methods(&self) -> sc_cli::Result<sc_service::config::RpcMethods> {
638		self.base.base.rpc_methods()
639	}
640
641	fn rpc_max_connections(&self) -> sc_cli::Result<u32> {
642		self.base.base.rpc_max_connections()
643	}
644
645	fn rpc_cors(&self, is_dev: bool) -> sc_cli::Result<Option<Vec<String>>> {
646		self.base.base.rpc_cors(is_dev)
647	}
648
649	fn default_heap_pages(&self) -> sc_cli::Result<Option<u64>> {
650		self.base.base.default_heap_pages()
651	}
652
653	fn force_authoring(&self) -> sc_cli::Result<bool> {
654		self.base.base.force_authoring()
655	}
656
657	fn disable_grandpa(&self) -> sc_cli::Result<bool> {
658		self.base.base.disable_grandpa()
659	}
660
661	fn max_runtime_instances(&self) -> sc_cli::Result<Option<usize>> {
662		self.base.base.max_runtime_instances()
663	}
664
665	fn announce_block(&self) -> sc_cli::Result<bool> {
666		self.base.base.announce_block()
667	}
668
669	fn telemetry_endpoints(
670		&self,
671		chain_spec: &Box<dyn ChainSpec>,
672	) -> sc_cli::Result<Option<sc_telemetry::TelemetryEndpoints>> {
673		self.base.base.telemetry_endpoints(chain_spec)
674	}
675
676	fn node_name(&self) -> sc_cli::Result<String> {
677		self.base.base.node_name()
678	}
679}
680
681#[cfg(test)]
682mod tests {
683	use super::*;
684	use clap::{CommandFactory, FromArgMatches};
685
686	struct TestCliConfig;
687	impl CliConfig for TestCliConfig {
688		fn impl_version() -> String {
689			"0.0.0".into()
690		}
691		fn author() -> String {
692			"test".into()
693		}
694		fn support_url() -> String {
695			"https://example.invalid".into()
696		}
697		fn copyright_start_year() -> u16 {
698			2025
699		}
700	}
701
702	#[test]
703	fn statement_affinity_topics_accumulate_repeated_flags() {
704		let topic_a = "11".repeat(32);
705		let topic_b = "22".repeat(32);
706		// `propagate_version = true` on the command requires a version; the binary injects one via
707		// `SubstrateCli` at runtime, so set one here to drive clap directly.
708		let matches = Cli::<TestCliConfig>::command().version("0.0.0").get_matches_from([
709			"polkadot-omni-node",
710			"--statement-affinity-topic",
711			&format!("0x{topic_a}"),
712			"--statement-affinity-topic",
713			&format!("0x{topic_b}"),
714		]);
715		let cli = Cli::<TestCliConfig>::from_arg_matches(&matches).expect("args parse");
716		assert_eq!(
717			cli.statement_affinity_topics,
718			vec![sc_statement_store::Topic([0x11; 32]), sc_statement_store::Topic([0x22; 32])]
719		);
720	}
721
722	#[test]
723	fn statement_bloom_flags_parse() {
724		let matches = Cli::<TestCliConfig>::command().version("0.0.0").get_matches_from([
725			"polkadot-omni-node",
726			"--statement-bloom-false-positive-rate",
727			"0.001",
728			"--statement-bloom-seed",
729			"12345678901234567890123456789012345678",
730		]);
731		let cli = Cli::<TestCliConfig>::from_arg_matches(&matches).expect("args parse");
732		assert_eq!(cli.statement_bloom_false_positive_rate, 0.001);
733		assert_eq!(cli.statement_bloom_seed, Some(12345678901234567890123456789012345678));
734
735		let matches = Cli::<TestCliConfig>::command()
736			.version("0.0.0")
737			.get_matches_from(["polkadot-omni-node"]);
738		let cli = Cli::<TestCliConfig>::from_arg_matches(&matches).expect("args parse");
739		assert_eq!(
740			cli.statement_bloom_false_positive_rate,
741			sc_statement_store::DEFAULT_BLOOM_FALSE_POS_RATE
742		);
743		assert_eq!(cli.statement_bloom_seed, None);
744	}
745}