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(Box::new(match id {
291 "" => {
292 tracing::info!("Using default test service chain spec.");
293 cumulus_test_service::get_chain_spec(
294 cumulus_test_runtime::WASM_BINARY,
295 Some(ParaId::from(2000)),
296 )
297 },
298 "elastic-scaling-mvp" => {
299 tracing::info!("Using elastic-scaling mvp chain spec.");
300 cumulus_test_service::get_chain_spec(
301 cumulus_test_runtime::elastic_scaling::WASM_BINARY,
302 Some(ParaId::from(2100)),
303 )
304 },
305 "elastic-scaling" => {
306 tracing::info!("Using elastic-scaling chain spec.");
307 cumulus_test_service::get_chain_spec(
308 cumulus_test_runtime::elastic_scaling::WASM_BINARY,
309 Some(ParaId::from(2200)),
310 )
311 },
312 "elastic-scaling-500ms" => {
313 tracing::info!("Using elastic-scaling 500ms chain spec.");
314 cumulus_test_service::get_chain_spec(
315 cumulus_test_runtime::elastic_scaling_500ms::WASM_BINARY,
316 Some(ParaId::from(2300)),
317 )
318 },
319 "block-bundling" => {
320 tracing::info!("Using block-bundling chain spec.");
321 cumulus_test_service::get_chain_spec(
322 cumulus_test_runtime::block_bundling::WASM_BINARY,
323 Some(ParaId::from(2400)),
324 )
325 },
326 "sync-backing" => {
327 tracing::info!("Using sync backing chain spec.");
328 cumulus_test_service::get_chain_spec(
329 cumulus_test_runtime::sync_backing::WASM_BINARY,
330 Some(ParaId::from(2500)),
331 )
332 },
333 "async-backing" => {
334 tracing::info!("Using async backing chain spec.");
335 cumulus_test_service::get_chain_spec(
336 cumulus_test_runtime::WASM_BINARY,
337 Some(ParaId::from(2500)),
338 )
339 },
340 "relay-parent-offset" => cumulus_test_service::get_chain_spec(
341 cumulus_test_runtime::relay_parent_offset::WASM_BINARY,
342 Some(ParaId::from(2600)),
343 ),
344 "with-authority-discovery" => {
345 tracing::info!("Using with-authority-discovery chain spec.");
346 cumulus_test_service::get_chain_spec(
347 cumulus_test_runtime::with_authority_discovery::WASM_BINARY,
348 Some(ParaId::from(1000)),
349 )
350 },
351 "v3" => {
352 tracing::info!("Using async backing V3 chain spec.");
353 cumulus_test_service::get_chain_spec(
354 cumulus_test_runtime::v3::WASM_BINARY,
355 Some(ParaId::from(2700)),
356 )
357 },
358 "v3-rpo-2" => {
359 tracing::info!("Using async backing V3 with relay parent offset 2 chain spec.");
360 cumulus_test_service::get_chain_spec(
361 cumulus_test_runtime::v3_rpo_2::WASM_BINARY,
362 Some(ParaId::from(2700)),
363 )
364 },
365 "v3-rpo-4" => {
366 tracing::info!("Using async backing V3 with relay parent offset 4 chain spec.");
367 cumulus_test_service::get_chain_spec(
368 cumulus_test_runtime::v3_rpo_4::WASM_BINARY,
369 Some(ParaId::from(2700)),
370 )
371 },
372 "v3-rpo-6" => {
373 tracing::info!("Using async backing V3 with relay parent offset 6 chain spec.");
374 cumulus_test_service::get_chain_spec(
375 cumulus_test_runtime::v3_rpo_6::WASM_BINARY,
376 Some(ParaId::from(2700)),
377 )
378 },
379 "v3-rpo-15" => {
380 tracing::info!("Using async backing V3 with relay parent offset 15 chain spec.");
381 cumulus_test_service::get_chain_spec(
382 cumulus_test_runtime::v3_rpo_15::WASM_BINARY,
383 Some(ParaId::from(2700)),
384 )
385 },
386 "elastic-scaling-v3" => {
387 tracing::info!("Using elastic scaling V3 chain spec.");
388 cumulus_test_service::get_chain_spec(
389 cumulus_test_runtime::elastic_scaling_v3::WASM_BINARY,
390 Some(ParaId::from(2800)),
391 )
392 },
393 path => {
394 let chain_spec: sc_chain_spec::GenericChainSpec =
395 sc_chain_spec::GenericChainSpec::from_json_file(path.into())?;
396 chain_spec
397 },
398 }))
399 }
400}
401
402impl SubstrateCli for RelayChainCli {
403 fn impl_name() -> String {
404 "Polkadot collator".into()
405 }
406
407 fn impl_version() -> String {
408 String::new()
409 }
410
411 fn description() -> String {
412 format!(
413 "Polkadot collator\n\nThe command-line arguments provided first will be \
414 passed to the parachain node, while the arguments provided after -- will be passed \
415 to the relay chain node.\n\n\
416 {} [parachain-args] -- [relay_chain-args]",
417 Self::executable_name()
418 )
419 }
420
421 fn author() -> String {
422 env!("CARGO_PKG_AUTHORS").into()
423 }
424
425 fn support_url() -> String {
426 "https://github.com/paritytech/polkadot-sdk/issues/new".into()
427 }
428
429 fn copyright_start_year() -> i32 {
430 2017
431 }
432
433 fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {
434 <polkadot_cli::Cli as SubstrateCli>::from_iter([RelayChainCli::executable_name()].iter())
435 .load_spec(id)
436 }
437}