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