1
// Copyright (C) Moondance Labs Ltd.
2
// This file is part of Tanssi.
3

            
4
// Tanssi is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Tanssi is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Tanssi.  If not, see <http://www.gnu.org/licenses/>
16

            
17
use {
18
    crate::cli::{Cli, Subcommand, NODE_VERSION},
19
    frame_benchmarking_cli::{
20
        BenchmarkCmd, ExtrinsicFactory, SubstrateRemarkBuilder, SUBSTRATE_REFERENCE_HARDWARE,
21
    },
22
    futures::future::TryFutureExt,
23
    node_common::service::node_builder::Sealing,
24
    polkadot_service::{
25
        self,
26
        benchmarking::{benchmark_inherent_data, TransferKeepAliveBuilder},
27
        HeaderBackend, IdentifyVariant, ParaId,
28
    },
29
    sc_cli::{CliConfiguration, SubstrateCli},
30
    sp_core::crypto::Ss58AddressFormatRegistry,
31
    sp_keyring::Sr25519Keyring,
32
    tanssi_relay_service::dev_service::build_full as build_full_dev,
33
};
34

            
35
pub use crate::error::Error;
36

            
37
#[cfg(feature = "pyroscope")]
38
use pyroscope_pprofrs::{pprof_backend, PprofConfig};
39

            
40
type Result<T> = std::result::Result<T, Error>;
41

            
42
fn get_exec_name() -> Option<String> {
43
    std::env::current_exe()
44
        .ok()
45
        .and_then(|pb| pb.file_name().map(|s| s.to_os_string()))
46
        .and_then(|s| s.into_string().ok())
47
}
48

            
49
impl SubstrateCli for Cli {
50
4452
    fn impl_name() -> String {
51
4452
        "Tanssi".into()
52
4452
    }
53

            
54
7452
    fn impl_version() -> String {
55
7452
        env!("SUBSTRATE_CLI_IMPL_VERSION").into()
56
7452
    }
57

            
58
3024
    fn description() -> String {
59
3024
        env!("CARGO_PKG_DESCRIPTION").into()
60
3024
    }
61

            
62
2988
    fn author() -> String {
63
2988
        env!("CARGO_PKG_AUTHORS").into()
64
2988
    }
65

            
66
3000
    fn support_url() -> String {
67
3000
        "https://github.com/moondance-labs/tanssi/issues/new".into()
68
3000
    }
69

            
70
1476
    fn copyright_start_year() -> i32 {
71
1476
        2017
72
1476
    }
73

            
74
3000
    fn executable_name() -> String {
75
3000
        "tanssi".into()
76
3000
    }
77

            
78
    #[cfg(not(feature = "runtime-benchmarks"))]
79
1488
    fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {
80
1488
        load_spec(id, vec![], vec![2000, 2001], None, None)
81
1488
    }
82

            
83
    #[cfg(feature = "runtime-benchmarks")]
84
    fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {
85
        load_spec(id, vec![], vec![], None, None)
86
    }
87
}
88

            
89
#[allow(clippy::borrowed_box)]
90
1476
fn set_default_ss58_version(_spec: &Box<dyn polkadot_service::ChainSpec>) {
91
1476
    let ss58_version = Ss58AddressFormatRegistry::PolkadotAccount.into();
92

            
93
1476
    sp_core::crypto::set_default_ss58_version(ss58_version);
94
1476
}
95

            
96
/// Launch a node, accepting arguments just like a regular node,
97
/// accepts an alternative overseer generator, to adjust behavior
98
/// for integration tests as needed.
99
/// `malus_finality_delay` restrict finality votes of this node
100
/// to be at most `best_block - malus_finality_delay` height.
101
#[cfg(feature = "malus")]
102
pub fn run_node(
103
    run: Cli,
104
    overseer_gen: impl polkadot_service::OverseerGen,
105
    malus_finality_delay: Option<u32>,
106
) -> Result<()> {
107
    run_node_inner(
108
        run,
109
        overseer_gen,
110
        malus_finality_delay,
111
        |_logger_builder, _config| {},
112
    )
113
}
114

            
115
1476
fn run_node_inner<F>(
116
1476
    cli: Cli,
117
1476
    overseer_gen: impl polkadot_service::OverseerGen,
118
1476
    maybe_malus_finality_delay: Option<u32>,
119
1476
    logger_hook: F,
120
1476
) -> Result<()>
121
1476
where
122
1476
    F: FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration),
123
{
124
1476
    let runner = cli
125
1476
        .create_runner_with_logger_hook::<sc_cli::RunCmd, _, F>(&cli.run.base, logger_hook)
126
1476
        .map_err(Error::from)?;
127
1476
    let chain_spec = &runner.config().chain_spec;
128

            
129
    // By default, enable BEEFY on all networks, unless explicitly disabled through CLI.
130
1476
    let enable_beefy = !cli.run.no_beefy;
131

            
132
1476
    set_default_ss58_version(chain_spec);
133

            
134
1476
    let node_version = if cli.run.disable_worker_version_check {
135
        None
136
    } else {
137
1476
        Some(NODE_VERSION.to_string())
138
    };
139

            
140
1476
    let secure_validator_mode = cli.run.base.validator && !cli.run.insecure_validator;
141

            
142
1476
    runner.run_node_until_exit(move |config| async move {
143
1476
        let hwbench = (!cli.run.no_hardware_benchmarks)
144
1476
            .then(|| {
145
                config.database.path().map(|database_path| {
146
                    let _ = std::fs::create_dir_all(database_path);
147
                    sc_sysinfo::gather_hwbench(Some(database_path), &SUBSTRATE_REFERENCE_HARDWARE)
148
                })
149
            })
150
1476
            .flatten();
151

            
152
1476
        let database_source = config.database.clone();
153

            
154
1476
        let task_manager = if config.chain_spec.is_dev() || cli.run.dev_service {
155
1476
            log::info!("Starting service in Development mode");
156
1476
            build_full_dev(
157
1476
                Sealing::Manual,
158
1476
                config,
159
1476
                polkadot_service::NewFullParams {
160
1476
                    is_parachain_node: polkadot_service::IsParachainNode::No,
161
1476
                    enable_beefy,
162
1476
                    force_authoring_backoff: cli.run.force_authoring_backoff,
163
1476
                    telemetry_worker_handle: None,
164
1476
                    node_version,
165
1476
                    secure_validator_mode,
166
1476
                    workers_path: cli.run.workers_path,
167
1476
                    workers_names: Some((
168
1476
                        (&"tanssi-relay-prepare-worker").to_string(),
169
1476
                        (&"tanssi-relay-execute-worker").to_string(),
170
1476
                    )),
171
1476
                    overseer_gen,
172
1476
                    overseer_message_channel_capacity_override: cli
173
1476
                        .run
174
1476
                        .overseer_channel_capacity_override,
175
1476
                    malus_finality_delay: maybe_malus_finality_delay,
176
1476
                    hwbench,
177
1476
                    execute_workers_max_num: cli.run.execute_workers_max_num,
178
1476
                    prepare_workers_hard_max_num: cli.run.prepare_workers_hard_max_num,
179
1476
                    prepare_workers_soft_max_num: cli.run.prepare_workers_soft_max_num,
180
1476
                    enable_approval_voting_parallel: cli.run.enable_approval_voting_parallel,
181
1476
                    keep_finalized_for: cli.run.keep_finalized_for,
182
1476
                },
183
            )
184
1476
            .map(|full| full.task_manager)?
185
        } else {
186
            polkadot_service::build_full(
187
                config,
188
                polkadot_service::NewFullParams {
189
                    is_parachain_node: polkadot_service::IsParachainNode::No,
190
                    enable_beefy,
191
                    force_authoring_backoff: cli.run.force_authoring_backoff,
192
                    telemetry_worker_handle: None,
193
                    node_version,
194
                    secure_validator_mode,
195
                    workers_path: cli.run.workers_path,
196
                    workers_names: Some((
197
                        (&"tanssi-relay-prepare-worker").to_string(),
198
                        (&"tanssi-relay-execute-worker").to_string(),
199
                    )),
200
                    overseer_gen,
201
                    overseer_message_channel_capacity_override: cli
202
                        .run
203
                        .overseer_channel_capacity_override,
204
                    malus_finality_delay: maybe_malus_finality_delay,
205
                    hwbench,
206
                    execute_workers_max_num: cli.run.execute_workers_max_num,
207
                    prepare_workers_hard_max_num: cli.run.prepare_workers_hard_max_num,
208
                    prepare_workers_soft_max_num: cli.run.prepare_workers_soft_max_num,
209
                    enable_approval_voting_parallel: cli.run.enable_approval_voting_parallel,
210
                    keep_finalized_for: cli.run.keep_finalized_for,
211
                },
212
            )
213
            .map(|full| full.task_manager)?
214
        };
215

            
216
1476
        if let Some(path) = database_source.path() {
217
1476
            sc_storage_monitor::StorageMonitorService::try_spawn(
218
1476
                cli.storage_monitor,
219
1476
                path.to_path_buf(),
220
1476
                &task_manager.spawn_essential_handle(),
221
            )?;
222
        }
223

            
224
1476
        Ok(task_manager)
225
2952
    })
226
1476
}
227

            
228
/// Parses polkadot specific CLI arguments and run the service.
229
1512
pub fn run() -> Result<()> {
230
1512
    let cli: Cli = Cli::from_args();
231

            
232
    #[cfg(feature = "pyroscope")]
233
    let mut pyroscope_agent_maybe = if let Some(ref agent_addr) = cli.run.pyroscope_server {
234
        let address = agent_addr
235
            .to_socket_addrs()
236
            .map_err(Error::AddressResolutionFailure)?
237
            .next()
238
            .ok_or_else(|| Error::AddressResolutionMissing)?;
239
        // The pyroscope agent requires a `http://` prefix, so we just do that.
240
        let agent = pyro::PyroscopeAgent::builder(
241
            "http://".to_owned() + address.to_string().as_str(),
242
            "polkadot".to_owned(),
243
        )
244
        .backend(pprof_backend(PprofConfig::new().sample_rate(113)))
245
        .build()?;
246
        Some(agent.start()?)
247
    } else {
248
        None
249
    };
250

            
251
    #[cfg(not(feature = "pyroscope"))]
252
1512
    if cli.run.pyroscope_server.is_some() {
253
        return Err(Error::PyroscopeNotCompiledIn);
254
1512
    }
255

            
256
    #[allow(deprecated)]
257
36
    match &cli.subcommand {
258
1476
        None => run_node_inner(
259
1476
            cli,
260
1476
            polkadot_service::ValidatorOverseerGen,
261
1476
            None,
262
1476
            polkadot_node_metrics::logger_hook(),
263
        ),
264
        Some(Subcommand::BuildSpec(cmd)) => {
265
            let runner = cli.create_runner(cmd)?;
266
            let chain_spec = load_spec(
267
                &cmd.base.chain_id(cmd.base.is_dev()?)?,
268
                cmd.add_container_chain.clone().unwrap_or_default(),
269
                cmd.mock_container_chain.clone().unwrap_or_default(),
270
                cmd.invulnerable.clone(),
271
                cmd.authority.clone(),
272
            )?;
273
            Ok(runner.sync_run(|config| cmd.base.run(chain_spec, config.network))?)
274
        }
275
24
        Some(Subcommand::ExportChainSpec(cmd)) => {
276
24
            let chain_spec = load_spec(
277
24
                &cmd.base.chain,
278
24
                cmd.add_container_chain.clone().unwrap_or_default(),
279
24
                cmd.mock_container_chain.clone().unwrap_or_default(),
280
24
                cmd.invulnerable.clone(),
281
24
                cmd.authority.clone(),
282
            )?;
283
24
            cmd.base.run(chain_spec).map_err(Error::SubstrateCli)
284
        }
285
        Some(Subcommand::CheckBlock(cmd)) => {
286
            let runner = cli.create_runner(cmd).map_err(Error::SubstrateCli)?;
287
            let chain_spec = &runner.config().chain_spec;
288

            
289
            set_default_ss58_version(chain_spec);
290

            
291
            runner.async_run(|mut config| {
292
                let (client, _, import_queue, task_manager) =
293
                    polkadot_service::new_chain_ops(&mut config)?;
294
                Ok((
295
                    cmd.run(client, import_queue).map_err(Error::SubstrateCli),
296
                    task_manager,
297
                ))
298
            })
299
        }
300
        Some(Subcommand::ExportBlocks(cmd)) => {
301
            let runner = cli.create_runner(cmd)?;
302
            let chain_spec = &runner.config().chain_spec;
303

            
304
            set_default_ss58_version(chain_spec);
305

            
306
            Ok(runner.async_run(|mut config| {
307
                let (client, _, _, task_manager) =
308
                    polkadot_service::new_chain_ops(&mut config).map_err(Error::PolkadotService)?;
309
                Ok((
310
                    cmd.run(client, config.database)
311
                        .map_err(Error::SubstrateCli),
312
                    task_manager,
313
                ))
314
            })?)
315
        }
316
        Some(Subcommand::ExportState(cmd)) => {
317
            let runner = cli.create_runner(cmd)?;
318
            let chain_spec = &runner.config().chain_spec;
319

            
320
            set_default_ss58_version(chain_spec);
321

            
322
            Ok(runner.async_run(|mut config| {
323
                let (client, _, _, task_manager) = polkadot_service::new_chain_ops(&mut config)?;
324
                Ok((
325
                    cmd.run(client, config.chain_spec)
326
                        .map_err(Error::SubstrateCli),
327
                    task_manager,
328
                ))
329
            })?)
330
        }
331
        Some(Subcommand::ImportBlocks(cmd)) => {
332
            let runner = cli.create_runner(cmd)?;
333
            let chain_spec = &runner.config().chain_spec;
334

            
335
            set_default_ss58_version(chain_spec);
336

            
337
            Ok(runner.async_run(|mut config| {
338
                let (client, _, import_queue, task_manager) =
339
                    polkadot_service::new_chain_ops(&mut config)?;
340
                Ok((
341
                    cmd.run(client, import_queue).map_err(Error::SubstrateCli),
342
                    task_manager,
343
                ))
344
            })?)
345
        }
346
        Some(Subcommand::PurgeChain(cmd)) => {
347
            let runner = cli.create_runner(cmd)?;
348
            Ok(runner.sync_run(|config| cmd.run(config.database))?)
349
        }
350
        Some(Subcommand::Revert(cmd)) => {
351
            let runner = cli.create_runner(cmd)?;
352
            let chain_spec = &runner.config().chain_spec;
353

            
354
            set_default_ss58_version(chain_spec);
355

            
356
            Ok(runner.async_run(|mut config| {
357
                let (client, backend, _, task_manager) =
358
                    polkadot_service::new_chain_ops(&mut config)?;
359
                let spawn_handle = task_manager.spawn_handle();
360
                let aux_revert = Box::new(|client, backend, blocks| {
361
                    polkadot_service::revert_backend(client, backend, blocks, config, spawn_handle)
362
                        .map_err(|err| {
363
                            match err {
364
                                polkadot_service::Error::Blockchain(err) => err.into(),
365
                                // Generic application-specific error.
366
                                err => sc_cli::Error::Application(err.into()),
367
                            }
368
                        })
369
                });
370
                Ok((
371
                    cmd.run(client, backend, Some(aux_revert))
372
                        .map_err(Error::SubstrateCli),
373
                    task_manager,
374
                ))
375
            })?)
376
        }
377
        Some(Subcommand::Benchmark(cmd)) => {
378
            let runner = cli.create_runner(cmd)?;
379
            let chain_spec = &runner.config().chain_spec;
380

            
381
            match cmd {
382
                #[cfg(not(feature = "runtime-benchmarks"))]
383
                BenchmarkCmd::Storage(_) => {
384
                    return Err(sc_cli::Error::Input(
385
                        "Compile with --features=runtime-benchmarks \
386
						to enable storage benchmarks."
387
                            .into(),
388
                    )
389
                    .into())
390
                }
391
                #[cfg(feature = "runtime-benchmarks")]
392
                BenchmarkCmd::Storage(cmd) => runner.sync_run(|mut config| {
393
                    let (client, backend, _, _) =
394
                        polkadot_service::new_chain_ops(&mut config)?;
395
                    let db = backend.expose_db();
396
                    let storage = backend.expose_storage();
397
                    let shared_trie_cache = backend.expose_shared_trie_cache();
398
                    cmd.run(config, client.clone(), db, storage, shared_trie_cache)
399
                        .map_err(Error::SubstrateCli)
400
                }),
401
                BenchmarkCmd::Block(cmd) => runner.sync_run(|mut config| {
402
                    let (client, _, _, _) = polkadot_service::new_chain_ops(&mut config)?;
403

            
404
                    cmd.run(client.clone()).map_err(Error::SubstrateCli)
405
                }),
406
                BenchmarkCmd::Overhead(cmd) => runner.sync_run(|config| {
407
                        if cmd.params.runtime.is_some() {
408
                                return Err(sc_cli::Error::Input(
409
                                        "Polkadot binary does not support `--runtime` flag for `benchmark overhead`. Please provide a chain spec or use the `frame-omni-bencher`."
410
                                                .into(),
411
                                )
412
                                .into())
413
                        }
414

            
415
                        cmd.run_with_default_builder_and_spec::<polkadot_service::Block, ()>(
416
                                Some(config.chain_spec),
417
                        )
418
                        .map_err(Error::SubstrateCli)
419
                }),
420
                BenchmarkCmd::Extrinsic(cmd) => runner.sync_run(|mut config| {
421
                        let (client, _, _, _) = polkadot_service::new_chain_ops(&mut config)?;
422
                        let header = client.header(client.info().genesis_hash).unwrap().unwrap();
423
                        let inherent_data = benchmark_inherent_data(header)
424
                                .map_err(|e| format!("generating inherent data: {:?}", e))?;
425

            
426
                        let remark_builder = SubstrateRemarkBuilder::new_from_client(client.clone())?;
427

            
428
                        let tka_builder = TransferKeepAliveBuilder::new(
429
                                client.clone(),
430
                                Sr25519Keyring::Alice.to_account_id(),
431
                                config.chain_spec.identify_chain(),
432
                        );
433

            
434
                        let ext_factory =
435
                                ExtrinsicFactory(vec![Box::new(remark_builder), Box::new(tka_builder)]);
436

            
437
                        cmd.run(client.clone(), inherent_data, Vec::new(), &ext_factory)
438
                                .map_err(Error::SubstrateCli)
439
                }),
440
                BenchmarkCmd::Pallet(cmd) => {
441
                    set_default_ss58_version(chain_spec);
442

            
443
                    if cfg!(feature = "runtime-benchmarks") {
444
                        runner.sync_run(|config| {
445
                            cmd.run_with_spec::<sp_runtime::traits::HashingFor<polkadot_service::Block>, ()>(
446
                                Some(config.chain_spec),
447
                            )
448
                            .map_err(Error::SubstrateCli)
449
                        })
450
                    } else {
451
                        Err(sc_cli::Error::Input(
452
                            "Benchmarking wasn't enabled when building the node. \
453
				You can enable it with `--features runtime-benchmarks`."
454
                                .into(),
455
                        )
456
                        .into())
457
                    }
458
                }
459
                BenchmarkCmd::Machine(cmd) => runner.sync_run(|config| {
460
                    cmd.run(&config, SUBSTRATE_REFERENCE_HARDWARE.clone())
461
                        .map_err(Error::SubstrateCli)
462
                }),
463
                // NOTE: this allows the Polkadot client to leniently implement
464
                // new benchmark commands.
465
                #[allow(unreachable_patterns)]
466
                _ => Err(Error::CommandNotImplemented),
467
            }
468
        }
469
        Some(Subcommand::Key(cmd)) => Ok(cmd.run(&cli)?),
470
12
        Some(Subcommand::PrecompileWasm(cmd)) => {
471
12
            let runner = cli.create_runner(cmd)?;
472
12
            Ok(runner.async_run(|mut config| {
473
12
                let (_, backend, _, task_manager) = polkadot_service::new_chain_ops(&mut config)?;
474
12
                Ok((
475
12
                    cmd.run(backend, config.chain_spec)
476
12
                        .map_err(Error::SubstrateCli),
477
12
                    task_manager,
478
12
                ))
479
12
            })?)
480
        }
481
        Some(Subcommand::ChainInfo(cmd)) => {
482
            let runner = cli.create_runner(cmd)?;
483
            Ok(runner.sync_run(|config| cmd.run::<polkadot_service::Block>(&config))?)
484
        }
485
    }?;
486

            
487
    #[cfg(feature = "pyroscope")]
488
    if let Some(pyroscope_agent) = pyroscope_agent_maybe.take() {
489
        let agent = pyroscope_agent.stop()?;
490
        agent.shutdown();
491
    }
492
1512
    Ok(())
493
1512
}
494

            
495
1512
fn load_spec(
496
1512
    id: &str,
497
1512
    container_chains: Vec<String>,
498
1512
    mock_container_chains: Vec<u32>,
499
1512
    invulnerables: Option<Vec<String>>,
500
1512
    initial_authorities: Option<Vec<String>>,
501
1512
) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {
502
1512
    let id = if id.is_empty() {
503
        let n = get_exec_name().unwrap_or_default();
504
        ["dancelight"]
505
            .iter()
506
            .cloned()
507
            .find(|&chain| n.starts_with(chain))
508
            .unwrap_or("dancelight")
509
    } else {
510
1512
        id
511
    };
512
1512
    let mock_container_chains: Vec<ParaId> =
513
3000
        mock_container_chains.iter().map(|&x| x.into()).collect();
514
1512
    Ok(match id {
515
        #[cfg(feature = "dancelight-native")]
516
1512
        "dancelight" => Box::new(tanssi_relay_service::chain_spec::dancelight_config()?),
517
        #[cfg(feature = "dancelight-native")]
518
1512
        "dev" | "dancelight-dev" => {
519
            // Default invulnerables for dev mode, used in dev_tanssi_relay tests
520
6
            let invulnerables = invulnerables.unwrap_or(vec![
521
6
                "Bob".to_string(),
522
6
                "Charlie".to_string(),
523
6
                "Dave".to_string(),
524
6
                "Eve".to_string(),
525
            ]);
526

            
527
6
            Box::new(
528
6
                tanssi_relay_service::chain_spec::dancelight_development_config(
529
6
                    container_chains,
530
6
                    mock_container_chains,
531
6
                    invulnerables,
532
                )?,
533
            )
534
        }
535
        #[cfg(feature = "starlight-native")]
536
1506
        "tanssi" => Box::new(tanssi_relay_service::chain_spec::tanssi_config()?),
537
        #[cfg(feature = "starlight-native")]
538
1506
        "starlight-dev" => {
539
            // Default invulnerables for dev mode, used in dev_tanssi_relay tests
540
6
            let invulnerables = invulnerables.unwrap_or(vec![
541
6
                "Bob".to_string(),
542
6
                "Charlie".to_string(),
543
6
                "Dave".to_string(),
544
6
                "Eve".to_string(),
545
            ]);
546

            
547
6
            Box::new(
548
6
                tanssi_relay_service::chain_spec::starlight_development_config(
549
6
                    container_chains,
550
6
                    mock_container_chains,
551
6
                    invulnerables,
552
                )?,
553
            )
554
        }
555
        #[cfg(feature = "dancelight-native")]
556
1500
        "dancelight-local" => {
557
30
            let invulnerables = invulnerables.unwrap_or_default();
558
30
            let initial_authorities =
559
30
                initial_authorities.unwrap_or(vec!["Alice".to_string(), "Bob".to_string()]);
560

            
561
30
            Box::new(
562
30
                tanssi_relay_service::chain_spec::dancelight_local_testnet_config(
563
30
                    container_chains,
564
30
                    mock_container_chains,
565
30
                    invulnerables,
566
30
                    initial_authorities,
567
                )?,
568
            )
569
        }
570
        #[cfg(feature = "starlight-native")]
571
1470
        "starlight-local" => {
572
30
            let invulnerables = invulnerables.unwrap_or_default();
573
30
            let initial_authorities =
574
30
                initial_authorities.unwrap_or(vec!["Alice".to_string(), "Bob".to_string()]);
575

            
576
30
            Box::new(
577
30
                tanssi_relay_service::chain_spec::starlight_local_testnet_config(
578
30
                    container_chains,
579
30
                    mock_container_chains,
580
30
                    invulnerables,
581
30
                    initial_authorities,
582
                )?,
583
            )
584
        }
585
        #[cfg(feature = "dancelight-native")]
586
1440
        "dancelight-staging" => {
587
            Box::new(tanssi_relay_service::chain_spec::dancelight_staging_testnet_config()?)
588
        }
589
        #[cfg(not(feature = "dancelight-native"))]
590
        name if name.starts_with("dancelight-") && !name.ends_with(".json") || name == "dev" => {
591
            Err(format!(
592
                "`{}` only supported with `dancelight-native` feature enabled.",
593
                name
594
            ))?
595
        }
596
1440
        path => {
597
1440
            let path = std::path::PathBuf::from(path);
598

            
599
1440
            (Box::new(polkadot_service::GenericChainSpec::from_json_file(
600
1440
                path.clone(),
601
1440
            )?)) as std::boxed::Box<dyn sc_cli::ChainSpec>
602
        }
603
    })
604
1512
}