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