1use clap::ValueEnum;
18use cumulus_client_cli::{ExportGenesisHeadCommand, ExportGenesisWasmCommand};
19use polkadot_service::{ChainSpec, ParaId, PrometheusConfig};
20use sc_cli::{
21 CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams, NetworkParams,
22 Result as CliResult, RpcEndpoint, SharedParams, SubstrateCli,
23};
24use sc_service::BasePath;
25use std::{
26 fmt::{Display, Formatter},
27 path::PathBuf,
28};
29
30#[derive(Debug, clap::Parser)]
31#[command(
32 version,
33 propagate_version = true,
34 args_conflicts_with_subcommands = true,
35 subcommand_negates_reqs = true
36)]
37pub struct TestCollatorCli {
38 #[command(subcommand)]
39 pub subcommand: Option<Subcommand>,
40
41 #[command(flatten)]
42 pub run: cumulus_client_cli::RunCmd,
43
44 #[arg(raw = true)]
46 pub relaychain_args: Vec<String>,
47
48 #[arg(long)]
49 pub disable_block_announcements: bool,
50
51 #[arg(long)]
52 pub fail_pov_recovery: bool,
53
54 #[arg(long, default_value_t = AuthoringPolicy::Lookahead)]
56 pub authoring: AuthoringPolicy,
57
58 #[arg(long, value_name = "N", default_value_t = 32)]
60 pub collator_reserved_slots: usize,
61}
62
63#[derive(PartialEq, Debug, ValueEnum, Clone, Copy)]
65pub enum AuthoringPolicy {
66 Lookahead,
69 SlotBased,
72}
73
74impl Display for AuthoringPolicy {
75 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
76 match self {
77 AuthoringPolicy::Lookahead => write!(f, "lookahead"),
78 AuthoringPolicy::SlotBased => write!(f, "slot-based"),
79 }
80 }
81}
82
83#[derive(Debug, clap::Subcommand)]
84pub enum Subcommand {
85 #[deprecated(
89 note = "build-spec command will be removed after 1/04/2026. Use export-chain-spec command instead"
90 )]
91 BuildSpec(sc_cli::BuildSpecCmd),
92
93 ExportChainSpec(sc_cli::ExportChainSpecCmd),
95
96 #[command(alias = "export-genesis-state")]
98 ExportGenesisHead(ExportGenesisHeadCommand),
99
100 ExportGenesisWasm(ExportGenesisWasmCommand),
102}
103
104#[derive(Debug)]
105pub struct RelayChainCli {
106 pub base: polkadot_cli::RunCmd,
108
109 pub chain_id: Option<String>,
111
112 pub base_path: Option<PathBuf>,
114}
115
116impl RelayChainCli {
117 pub fn new<'a>(
119 para_config: &sc_service::Configuration,
120 relay_chain_args: impl Iterator<Item = &'a String>,
121 ) -> Self {
122 let base_path = para_config.base_path.path().join("polkadot");
123 Self {
124 base_path: Some(base_path),
125 chain_id: None,
126 base: clap::Parser::parse_from(relay_chain_args),
127 }
128 }
129}
130
131impl CliConfiguration<Self> for RelayChainCli {
132 fn shared_params(&self) -> &SharedParams {
133 self.base.base.shared_params()
134 }
135
136 fn import_params(&self) -> Option<&ImportParams> {
137 self.base.base.import_params()
138 }
139
140 fn network_params(&self) -> Option<&NetworkParams> {
141 self.base.base.network_params()
142 }
143
144 fn keystore_params(&self) -> Option<&KeystoreParams> {
145 self.base.base.keystore_params()
146 }
147
148 fn base_path(&self) -> CliResult<Option<BasePath>> {
149 Ok(self
150 .shared_params()
151 .base_path()?
152 .or_else(|| self.base_path.clone().map(Into::into)))
153 }
154
155 fn rpc_addr(&self, default_listen_port: u16) -> CliResult<Option<Vec<RpcEndpoint>>> {
156 self.base.base.rpc_addr(default_listen_port)
157 }
158
159 fn prometheus_config(
160 &self,
161 default_listen_port: u16,
162 chain_spec: &Box<dyn ChainSpec>,
163 ) -> CliResult<Option<PrometheusConfig>> {
164 self.base.base.prometheus_config(default_listen_port, chain_spec)
165 }
166
167 fn init<F>(
168 &self,
169 _support_url: &String,
170 _impl_version: &String,
171 _logger_hook: F,
172 ) -> CliResult<()>
173 where
174 F: FnOnce(&mut sc_cli::LoggerBuilder),
175 {
176 unreachable!("PolkadotCli is never initialized; qed");
177 }
178
179 fn chain_id(&self, is_dev: bool) -> CliResult<String> {
180 let chain_id = self.base.base.chain_id(is_dev)?;
181
182 Ok(if chain_id.is_empty() { self.chain_id.clone().unwrap_or_default() } else { chain_id })
183 }
184
185 fn role(&self, is_dev: bool) -> CliResult<sc_service::Role> {
186 self.base.base.role(is_dev)
187 }
188
189 fn transaction_pool(
190 &self,
191 is_dev: bool,
192 ) -> CliResult<sc_service::config::TransactionPoolOptions> {
193 self.base.base.transaction_pool(is_dev)
194 }
195
196 fn trie_cache_maximum_size(&self) -> CliResult<Option<usize>> {
197 self.base.base.trie_cache_maximum_size()
198 }
199
200 fn rpc_methods(&self) -> CliResult<sc_service::config::RpcMethods> {
201 self.base.base.rpc_methods()
202 }
203
204 fn rpc_max_connections(&self) -> CliResult<u32> {
205 self.base.base.rpc_max_connections()
206 }
207
208 fn rpc_cors(&self, is_dev: bool) -> CliResult<Option<Vec<String>>> {
209 self.base.base.rpc_cors(is_dev)
210 }
211
212 fn default_heap_pages(&self) -> CliResult<Option<u64>> {
213 self.base.base.default_heap_pages()
214 }
215
216 fn force_authoring(&self) -> CliResult<bool> {
217 self.base.base.force_authoring()
218 }
219
220 fn disable_grandpa(&self) -> CliResult<bool> {
221 self.base.base.disable_grandpa()
222 }
223
224 fn max_runtime_instances(&self) -> CliResult<Option<usize>> {
225 self.base.base.max_runtime_instances()
226 }
227
228 fn announce_block(&self) -> CliResult<bool> {
229 self.base.base.announce_block()
230 }
231
232 fn telemetry_endpoints(
233 &self,
234 chain_spec: &Box<dyn ChainSpec>,
235 ) -> CliResult<Option<sc_telemetry::TelemetryEndpoints>> {
236 self.base.base.telemetry_endpoints(chain_spec)
237 }
238
239 fn node_name(&self) -> CliResult<String> {
240 self.base.base.node_name()
241 }
242}
243
244impl DefaultConfigurationValues for RelayChainCli {
245 fn p2p_listen_port() -> u16 {
246 30334
247 }
248
249 fn rpc_listen_port() -> u16 {
250 9945
251 }
252
253 fn prometheus_listen_port() -> u16 {
254 9616
255 }
256}
257
258impl SubstrateCli for TestCollatorCli {
259 fn impl_name() -> String {
260 "Cumulus zombienet test parachain".into()
261 }
262
263 fn impl_version() -> String {
264 String::new()
265 }
266
267 fn description() -> String {
268 format!(
269 "Cumulus zombienet test parachain\n\nThe command-line arguments provided first will be \
270 passed to the parachain node, while the arguments provided after -- will be passed \
271 to the relaychain node.\n\n\
272 {} [parachain-args] -- [relaychain-args]",
273 Self::executable_name()
274 )
275 }
276
277 fn author() -> String {
278 env!("CARGO_PKG_AUTHORS").into()
279 }
280
281 fn support_url() -> String {
282 "https://github.com/paritytech/polkadot-sdk/issues/new".into()
283 }
284
285 fn copyright_start_year() -> i32 {
286 2017
287 }
288
289 fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {
290 Ok(match id {
291 "" => {
292 tracing::info!("Using default test service chain spec.");
293 Box::new(cumulus_test_service::get_chain_spec(Some(ParaId::from(2000)))) as Box<_>
294 },
295 "elastic-scaling-mvp" => {
296 tracing::info!("Using elastic-scaling mvp chain spec.");
297 Box::new(cumulus_test_service::get_elastic_scaling_chain_spec(Some(ParaId::from(
298 2100,
299 )))) as Box<_>
300 },
301 "elastic-scaling" => {
302 tracing::info!("Using elastic-scaling chain spec.");
303 Box::new(cumulus_test_service::get_elastic_scaling_chain_spec(Some(ParaId::from(
304 2200,
305 )))) as Box<_>
306 },
307 "elastic-scaling-500ms" => {
308 tracing::info!("Using elastic-scaling 500ms chain spec.");
309 Box::new(cumulus_test_service::get_elastic_scaling_500ms_chain_spec(Some(
310 ParaId::from(2300),
311 ))) as Box<_>
312 },
313 "block-bundling" => {
314 tracing::info!("Using block-bundling chain spec.");
315 Box::new(cumulus_test_service::get_block_bundling_chain_spec(Some(ParaId::from(
316 2400,
317 )))) as Box<_>
318 },
319 "sync-backing" => {
320 tracing::info!("Using sync backing chain spec.");
321 Box::new(cumulus_test_service::get_sync_backing_chain_spec(Some(ParaId::from(
322 2500,
323 )))) as Box<_>
324 },
325 "async-backing" => {
326 tracing::info!("Using async backing chain spec.");
327 Box::new(cumulus_test_service::get_async_backing_chain_spec(Some(ParaId::from(
328 2500,
329 )))) as Box<_>
330 },
331 "relay-parent-offset" => Box::new(
332 cumulus_test_service::get_relay_parent_offset_chain_spec(Some(ParaId::from(2600))),
333 ) as Box<_>,
334 "default-test" => {
335 tracing::info!("Using default test chain spec (no authority-discovery).");
336 Box::new(cumulus_test_service::get_chain_spec(Some(ParaId::from(1000)))) as Box<_>
337 },
338 "with-authority-discovery" => {
339 tracing::info!("Using with-authority-discovery chain spec.");
340 Box::new(cumulus_test_service::get_with_authority_discovery_chain_spec(Some(
341 ParaId::from(1000),
342 ))) as Box<_>
343 },
344 "async-backing-v3" => {
345 tracing::info!("Using async backing V3 chain spec.");
346 Box::new(cumulus_test_service::get_async_backing_v3_chain_spec(Some(ParaId::from(
347 2700,
348 )))) as Box<_>
349 },
350 "async-backing-v3-rpo" => {
351 tracing::info!("Using async backing V3 with relay parent offset chain spec.");
352 Box::new(cumulus_test_service::get_async_backing_v3_rpo_chain_spec(Some(
353 ParaId::from(2700),
354 ))) as Box<_>
355 },
356 "elastic-scaling-v3" => {
357 tracing::info!("Using elastic scaling V3 chain spec.");
358 Box::new(cumulus_test_service::get_elastic_scaling_v3_chain_spec(Some(
359 ParaId::from(2900),
360 ))) as Box<_>
361 },
362 path => {
363 let chain_spec: sc_chain_spec::GenericChainSpec =
364 sc_chain_spec::GenericChainSpec::from_json_file(path.into())?;
365 Box::new(chain_spec)
366 },
367 })
368 }
369}
370
371impl SubstrateCli for RelayChainCli {
372 fn impl_name() -> String {
373 "Polkadot collator".into()
374 }
375
376 fn impl_version() -> String {
377 String::new()
378 }
379
380 fn description() -> String {
381 format!(
382 "Polkadot collator\n\nThe command-line arguments provided first will be \
383 passed to the parachain node, while the arguments provided after -- will be passed \
384 to the relay chain node.\n\n\
385 {} [parachain-args] -- [relay_chain-args]",
386 Self::executable_name()
387 )
388 }
389
390 fn author() -> String {
391 env!("CARGO_PKG_AUTHORS").into()
392 }
393
394 fn support_url() -> String {
395 "https://github.com/paritytech/polkadot-sdk/issues/new".into()
396 }
397
398 fn copyright_start_year() -> i32 {
399 2017
400 }
401
402 fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {
403 <polkadot_cli::Cli as SubstrateCli>::from_iter([RelayChainCli::executable_name()].iter())
404 .load_spec(id)
405 }
406}