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::{
19
        chain_spec,
20
        cli::{Cli, RelayChainCli, Subcommand},
21
        service::{self, IdentifyVariant, NodeConfig},
22
    },
23
    cumulus_client_cli::extract_genesis_wasm,
24
    cumulus_client_service::storage_proof_size::HostFunctions as ReclaimHostFunctions,
25
    cumulus_primitives_core::ParaId,
26
    dancebox_runtime::Block,
27
    frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE},
28
    log::{info, warn},
29
    node_common::{command::generate_genesis_block, service::NodeBuilderConfig as _},
30
    parity_scale_codec::Encode,
31
    polkadot_service::{GenericChainSpec, WestendChainSpec},
32
    sc_cli::{
33
        ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams,
34
        NetworkParams, Result, SharedParams, SubstrateCli,
35
    },
36
    sc_service::config::{BasePath, PrometheusConfig},
37
    sp_core::hexdisplay::HexDisplay,
38
    sp_runtime::traits::{AccountIdConversion, Block as BlockT},
39
    std::io::Write,
40
    tc_service_container_chain::{chain_spec::RawChainSpec, cli::ContainerChainCli},
41
};
42

            
43
pub mod solochain;
44

            
45
212
fn load_spec(
46
212
    id: &str,
47
212
    para_id: Option<u32>,
48
212
    container_chains: Vec<String>,
49
212
    mock_container_chains: Vec<u32>,
50
212
    invulnerables: Option<Vec<String>>,
51
212
) -> std::result::Result<Box<dyn ChainSpec>, String> {
52
212
    let para_id: ParaId = para_id.unwrap_or(1000).into();
53
212
    let mock_container_chains: Vec<ParaId> =
54
420
        mock_container_chains.iter().map(|&x| x.into()).collect();
55
212
    let invulnerables = invulnerables.unwrap_or(vec![
56
212
        "Alice".to_string(),
57
212
        "Bob".to_string(),
58
212
        "Charlie".to_string(),
59
212
        "Dave".to_string(),
60
212
    ]);
61
212

            
62
212
    Ok(match id {
63
212
        "dev" | "dancebox-dev" | "dancebox_dev" => {
64
4
            Box::new(chain_spec::dancebox::development_config(
65
4
                para_id,
66
4
                container_chains,
67
4
                mock_container_chains,
68
4
                invulnerables,
69
4
            ))
70
        }
71
208
        "" | "dancebox-local" | "dancebox_local" => {
72
16
            Box::new(chain_spec::dancebox::local_dancebox_config(
73
16
                para_id,
74
16
                container_chains,
75
16
                mock_container_chains,
76
16
                invulnerables,
77
16
            ))
78
        }
79
192
        "dancebox" => Box::new(RawChainSpec::from_json_bytes(
80
            &include_bytes!("../../../../specs/dancebox/dancebox-raw-specs.json")[..],
81
        )?),
82
192
        "flashbox-dev" | "flashbox_dev" => Box::new(chain_spec::flashbox::development_config(
83
            para_id,
84
            container_chains,
85
            mock_container_chains,
86
            invulnerables,
87
        )),
88
192
        "flashbox-local" | "flashbox_local" => {
89
10
            Box::new(chain_spec::flashbox::local_flashbox_config(
90
10
                para_id,
91
10
                container_chains,
92
10
                mock_container_chains,
93
10
                invulnerables,
94
10
            ))
95
        }
96
182
        path => Box::new(chain_spec::dancebox::ChainSpec::from_json_file(
97
182
            std::path::PathBuf::from(path),
98
182
        )?),
99
    })
100
212
}
101

            
102
impl SubstrateCli for Cli {
103
618
    fn impl_name() -> String {
104
618
        "Tanssi Collator".into()
105
618
    }
106

            
107
1035
    fn impl_version() -> String {
108
1035
        env!("SUBSTRATE_CLI_IMPL_VERSION").into()
109
1035
    }
110

            
111
208
    fn description() -> String {
112
208
        format!(
113
208
            "Tanssi Collator\n\nThe command-line arguments provided first will be \
114
208
		passed to the parachain node, while the arguments provided after -- will be passed \
115
208
		to the relay chain node.\n\n\
116
208
		{} <parachain-args> -- <relay-chain-args>",
117
208
            Self::executable_name()
118
208
        )
119
208
    }
120

            
121
410
    fn author() -> String {
122
410
        env!("CARGO_PKG_AUTHORS").into()
123
410
    }
124

            
125
208
    fn support_url() -> String {
126
208
        "https://github.com/paritytech/cumulus/issues/new".into()
127
208
    }
128

            
129
202
    fn copyright_start_year() -> i32 {
130
202
        2020
131
202
    }
132

            
133
208
    fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {
134
208
        load_spec(id, self.para_id, vec![], vec![2000, 2001], None)
135
208
    }
136
}
137

            
138
impl SubstrateCli for RelayChainCli {
139
    fn impl_name() -> String {
140
        "Tanssi Collator".into()
141
    }
142

            
143
    fn impl_version() -> String {
144
        env!("SUBSTRATE_CLI_IMPL_VERSION").into()
145
    }
146

            
147
    fn description() -> String {
148
        format!(
149
            "Tanssi Collator\n\nThe command-line arguments provided first will be \
150
		passed to the parachain node, while the arguments provided after -- will be passed \
151
		to the relay chain node.\n\n\
152
		{} <parachain-args> -- <relay-chain-args>",
153
            Self::executable_name()
154
        )
155
    }
156

            
157
    fn author() -> String {
158
        env!("CARGO_PKG_AUTHORS").into()
159
    }
160

            
161
    fn support_url() -> String {
162
        "https://github.com/paritytech/cumulus/issues/new".into()
163
    }
164

            
165
    fn copyright_start_year() -> i32 {
166
        2020
167
    }
168

            
169
    fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {
170
        const STARLIGHT_RAW_SPECS: &[u8] =
171
            include_bytes!("../../../../specs/solochain/starlight-raw-specs.json");
172

            
173
        match id {
174
            "westend_moonbase_relay_testnet" => Ok(Box::new(WestendChainSpec::from_json_bytes(
175
                &include_bytes!("../../../../specs/dancebox/alphanet-relay-raw-specs.json")[..],
176
            )?)),
177
            // Default to starlight if this is a solochain node. Else default to polkadot default.
178
            "" if self.solochain => Ok(Box::new(GenericChainSpec::from_json_bytes(
179
                STARLIGHT_RAW_SPECS,
180
            )?)),
181
            "starlight" | "tanssi" => Ok(Box::new(GenericChainSpec::from_json_bytes(
182
                STARLIGHT_RAW_SPECS,
183
            )?)),
184
            "dancelight" => Ok(Box::new(GenericChainSpec::from_json_bytes(
185
                &include_bytes!("../../../../specs/solochain/dancelight-raw-specs.json")[..],
186
            )?)),
187
            // If we are not using a pre-baked relay spec, then fall back to the
188
            // Polkadot service to interpret the id.
189
            id => polkadot_cli::Cli::from_iter([RelayChainCli::executable_name()].iter())
190
                .load_spec(id),
191
        }
192
    }
193
}
194

            
195
macro_rules! construct_async_run {
196
	(|$components:ident, $cli:ident, $cmd:ident, $config:ident| $( $code:tt )* ) => {{
197
		let runner = $cli.create_runner($cmd)?;
198
		runner.async_run(|$config| {
199
			let $components = NodeConfig::new_builder(&$config, None)?;
200
            let inner = { $( $code )* };
201

            
202
			let task_manager = $components.task_manager;
203
			inner.map(|v| (v, task_manager))
204
		})
205
	}}
206
}
207

            
208
/// Parse command line arguments into service configuration.
209
202
pub fn run() -> Result<()> {
210
202
    let cli = Cli::from_args();
211

            
212
6
    match &cli.subcommand {
213
4
        Some(Subcommand::BuildSpec(cmd)) => {
214
4
            let runner = cli.create_runner(cmd)?;
215
4
            runner.sync_run(|config| {
216
4
                let chain_spec = load_spec(
217
4
                    &cmd.base.chain_id(cmd.base.is_dev()?)?,
218
4
                    cmd.extra.parachain_id,
219
4
                    cmd.extra.add_container_chain.clone().unwrap_or_default(),
220
4
                    cmd.extra.mock_container_chain.clone().unwrap_or_default(),
221
4
                    cmd.extra.invulnerable.clone(),
222
                )?;
223
4
                cmd.base.run(chain_spec, config.network)
224
4
            })
225
        }
226
        Some(Subcommand::CheckBlock(cmd)) => {
227
            construct_async_run!(|components, cli, cmd, config| {
228
                let (_, import_queue) = service::import_queue(&config, &components);
229
                Ok(cmd.run(components.client, import_queue))
230
            })
231
        }
232
        Some(Subcommand::ExportBlocks(cmd)) => {
233
            construct_async_run!(|components, cli, cmd, config| {
234
                Ok(cmd.run(components.client, config.database))
235
            })
236
        }
237
        Some(Subcommand::ExportState(cmd)) => {
238
            construct_async_run!(|components, cli, cmd, config| {
239
                Ok(cmd.run(components.client, config.chain_spec))
240
            })
241
        }
242
        Some(Subcommand::ImportBlocks(cmd)) => {
243
            construct_async_run!(|components, cli, cmd, config| {
244
                let (_, import_queue) = service::import_queue(&config, &components);
245
                Ok(cmd.run(components.client, import_queue))
246
            })
247
        }
248
        Some(Subcommand::Revert(cmd)) => {
249
            construct_async_run!(|components, cli, cmd, config| {
250
                Ok(cmd.run(components.client, components.backend, None))
251
            })
252
        }
253
        Some(Subcommand::PurgeChain(cmd)) => {
254
            let runner = cli.create_runner(cmd)?;
255

            
256
            runner.sync_run(|config| {
257
                let polkadot_cli = RelayChainCli::new(
258
                    &config,
259
                    [RelayChainCli::executable_name()]
260
                        .iter()
261
                        .chain(cli.relaychain_args().iter()),
262
                );
263

            
264
                let polkadot_config = SubstrateCli::create_configuration(
265
                    &polkadot_cli,
266
                    &polkadot_cli,
267
                    config.tokio_handle.clone(),
268
                )
269
                .map_err(|err| format!("Relay chain argument error: {}", err))?;
270

            
271
                cmd.run(config, polkadot_config)
272
            })
273
        }
274
        Some(Subcommand::ExportGenesisHead(cmd)) => {
275
            let runner = cli.create_runner(cmd)?;
276
            runner.sync_run(|config| {
277
                let client = NodeConfig::new_builder(&config, None)?.client;
278
                cmd.run(client)
279
            })
280
        }
281
        Some(Subcommand::ExportGenesisWasm(params)) => {
282
            let mut builder = sc_cli::LoggerBuilder::new("");
283
            builder.with_profiling(sc_tracing::TracingReceiver::Log, "");
284
            let _ = builder.init();
285

            
286
            let raw_wasm_blob =
287
                extract_genesis_wasm(&*cli.load_spec(&params.chain.clone().unwrap_or_default())?)?;
288
            let output_buf = if params.raw {
289
                raw_wasm_blob
290
            } else {
291
                format!("0x{:?}", HexDisplay::from(&raw_wasm_blob)).into_bytes()
292
            };
293

            
294
            if let Some(output) = &params.output {
295
                std::fs::write(output, output_buf)?;
296
            } else {
297
                std::io::stdout().write_all(&output_buf)?;
298
            }
299

            
300
            Ok(())
301
        }
302
        Some(Subcommand::Benchmark(cmd)) => {
303
            let runner = cli.create_runner(cmd)?;
304
            // Switch on the concrete benchmark sub-command-
305
            match cmd {
306
                BenchmarkCmd::Pallet(cmd) => {
307
                    if cfg!(feature = "runtime-benchmarks") {
308
                        runner.sync_run(|config| {
309
                            cmd.run_with_spec::<sp_runtime::traits::HashingFor<Block>, ReclaimHostFunctions>(Some(
310
                                config.chain_spec,
311
                            ))
312
                        })
313
                    } else {
314
                        Err("Benchmarking wasn't enabled when building the node. \
315
			  You can enable it with `--features runtime-benchmarks`."
316
                            .into())
317
                    }
318
                }
319
                BenchmarkCmd::Block(cmd) => runner.sync_run(|config| {
320
                    let client = NodeConfig::new_builder(&config, None)?.client;
321
                    cmd.run(client)
322
                }),
323
                #[cfg(not(feature = "runtime-benchmarks"))]
324
                BenchmarkCmd::Storage(_) => Err(sc_cli::Error::Input(
325
                    "Compile with --features=runtime-benchmarks \
326
						to enable storage benchmarks."
327
                        .into(),
328
                )),
329
                #[cfg(feature = "runtime-benchmarks")]
330
                BenchmarkCmd::Storage(cmd) => runner.sync_run(|config| {
331
                    let builder = NodeConfig::new_builder(&config, None)?;
332
                    let db = builder.backend.expose_db();
333
                    let storage = builder.backend.expose_storage();
334
                    cmd.run(config, builder.client, db, storage)
335
                }),
336
                BenchmarkCmd::Machine(cmd) => {
337
                    runner.sync_run(|config| cmd.run(&config, SUBSTRATE_REFERENCE_HARDWARE.clone()))
338
                }
339
                // NOTE: this allows the Client to leniently implement
340
                // new benchmark commands without requiring a companion MR.
341
                #[allow(unreachable_patterns)]
342
                _ => Err("Benchmarking sub-command unsupported".into()),
343
            }
344
        }
345
        Some(Subcommand::Key(cmd)) => Ok(cmd.run(&cli)?),
346
2
        Some(Subcommand::PrecompileWasm(cmd)) => {
347
2
            let runner = cli.create_runner(cmd)?;
348
2
            runner.async_run(|config| {
349
2
                let partials = NodeConfig::new_builder(&config, None)?;
350
2
                Ok((
351
2
                    cmd.run(partials.backend, config.chain_spec),
352
2
                    partials.task_manager,
353
2
                ))
354
2
            })
355
        }
356
        Some(Subcommand::SoloChain(cmd)) => {
357
            // Cannot use create_configuration function because that needs a chain spec.
358
            // So write our own `create_runner` function that doesn't need chain spec.
359
            let container_chain_cli = cmd.run.normalize();
360
            let runner = solochain::create_runner(&container_chain_cli)?;
361

            
362
            // The expected usage is
363
            // `tanssi-node solochain --flag`
364
            // So `cmd` stores the flags from after `solochain`, and `cli` has the flags from between
365
            // `tanssi-node` and `solo-chain`. We are ignoring the flags from `cli` intentionally.
366
            // Would be nice to error if the user passes any flag there, but it's not easy to detect.
367

            
368
            // Zombienet appends a --chain flag after "solo-chain" subcommand, which is ignored, so it's fine,
369
            // but warn users that this is not expected here.
370
            // We cannot do this before create_runner because logging is not setup there yet.
371
            if container_chain_cli.base.base.shared_params.chain.is_some() {
372
                log::warn!(
373
                    "Ignoring --chain argument: solochain mode does only need the relay chain-spec"
374
                );
375
            }
376

            
377
            let collator_options = container_chain_cli.base.collator_options();
378

            
379
            runner.run_node_until_exit(|config| async move {
380
                let containers_base_path = container_chain_cli
381
                    .base
382
                    .base
383
                    .shared_params
384
                    .base_path
385
                    .as_ref()
386
                    .expect("base_path is always set");
387
                let hwbench = (!cmd.no_hardware_benchmarks)
388
                    .then(|| {
389
                        Some(containers_base_path).map(|database_path| {
390
                            let _ = std::fs::create_dir_all(database_path);
391
                            sc_sysinfo::gather_hwbench(
392
                                Some(database_path),
393
                                &SUBSTRATE_REFERENCE_HARDWARE,
394
                            )
395
                        })
396
                    })
397
                    .flatten();
398

            
399
                let polkadot_cli = solochain::relay_chain_cli_new(
400
                    &config,
401
                    [RelayChainCli::executable_name()]
402
                        .iter()
403
                        .chain(cmd.relay_chain_args.iter()),
404
                );
405
                let tokio_handle = config.tokio_handle.clone();
406
                let polkadot_config =
407
                    SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, tokio_handle)
408
                        .map_err(|err| format!("Relay chain argument error: {}", err))?;
409

            
410
                info!(
411
                    "Is collating: {}",
412
                    if config.role.is_authority() {
413
                        "yes"
414
                    } else {
415
                        "no"
416
                    }
417
                );
418

            
419
                crate::service::start_solochain_node(
420
                    polkadot_config,
421
                    container_chain_cli,
422
                    collator_options,
423
                    hwbench,
424
                )
425
                .await
426
                .map_err(Into::into)
427
            })
428
        }
429
        None => {
430
196
            let runner = cli.create_runner(&cli.run.normalize())?;
431
196
            let collator_options = cli.run.collator_options();
432
196

            
433
196
            runner.run_node_until_exit(|config| async move {
434
196
                let hwbench = (!cli.no_hardware_benchmarks).then(||
435
                    config.database.path().map(|database_path| {
436
                        let _ = std::fs::create_dir_all(database_path);
437
                        sc_sysinfo::gather_hwbench(Some(database_path), &SUBSTRATE_REFERENCE_HARDWARE)
438
196
                    })).flatten();
439

            
440
196
                let para_id = chain_spec::Extensions::try_get(&*config.chain_spec)
441
196
                    .map(|e| e.para_id)
442
196
                    .ok_or("Could not find parachain ID in chain-spec.")?;
443

            
444
196
                let id = ParaId::from(para_id);
445
196

            
446
196
                let polkadot_cli = RelayChainCli::new(
447
196
                    &config,
448
196
                    [RelayChainCli::executable_name()].iter().chain(cli.relaychain_args().iter()),
449
196
                );
450
196

            
451
196
                let extension = chain_spec::Extensions::try_get(&*config.chain_spec);
452
196

            
453
196
                let relay_chain_id = extension.map(|e| e.relay_chain.clone());
454

            
455
196
                let dev_service =
456
196
                    config.chain_spec.is_dev() || relay_chain_id == Some("dev-service".to_string()) || cli.run.dev_service;
457

            
458
196
                if dev_service {
459
196
                    return crate::service::start_dev_node(config, cli.run.sealing, hwbench, id).map_err(Into::into);
460
                }
461

            
462
                let tokio_handle = config.tokio_handle.clone();
463
                let polkadot_config =
464
                    SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, tokio_handle)
465
                        .map_err(|err| format!("Relay chain argument error: {}", err))?;
466

            
467
                let parachain_account =
468
                    AccountIdConversion::<polkadot_primitives::AccountId>::into_account_truncating(&id);
469

            
470
                let block: Block = generate_genesis_block(&*config.chain_spec, sp_runtime::StateVersion::V1)
471
                    .map_err(|e| format!("{:?}", e))?;
472
                let genesis_state = format!("0x{:?}", HexDisplay::from(&block.header().encode()));
473

            
474
                info!("Parachain id: {:?}", id);
475
                info!("Parachain Account: {}", parachain_account);
476
                info!("Parachain genesis state: {}", genesis_state);
477
                info!("Is collating: {}", if config.role.is_authority() { "yes" } else { "no" });
478

            
479
                if let cumulus_client_cli::RelayChainMode::ExternalRpc(rpc_target_urls) =
480
                    collator_options.clone().relay_chain_mode {
481
                    if !rpc_target_urls.is_empty() && !cli.relaychain_args().is_empty() {
482
                        warn!("Detected relay chain node arguments together with --relay-chain-rpc-url. This command starts a minimal Polkadot node that only uses a network-related subset of all relay chain CLI options.");
483
                    }
484
                }
485

            
486
                let mut container_chain_config = None;
487
                // Even if container-chain-args are empty, we need to spawn the container-detection
488
                // collation taks if the role is authority.
489

            
490
                // We need to bake in some container-chain args
491
                if !cli.container_chain_args().is_empty() || config.role.is_authority() {
492
                    let container_chain_cli = ContainerChainCli::new(
493
                        &config,
494
                        [ContainerChainCli::executable_name()].iter().chain(cli.container_chain_args().iter()),
495
                    );
496
                    let tokio_handle = config.tokio_handle.clone();
497
                    container_chain_config = Some((container_chain_cli, tokio_handle));
498
                }
499

            
500
                match config.network.network_backend.unwrap_or(sc_network::config::NetworkBackendType::Libp2p) {
501
                    sc_network::config::NetworkBackendType::Libp2p => {
502
                        crate::service::start_parachain_node::<sc_network::NetworkWorker<_, _>>(
503
                            config,
504
                            polkadot_config,
505
                            container_chain_config,
506
                            collator_options,
507
                            id,
508
                            hwbench,
509
                            cli.run.experimental_max_pov_percentage,
510
                        )
511
                            .await
512
                            .map(|r| r.0)
513
                            .map_err(Into::into)
514
                    }
515
                    sc_network::config::NetworkBackendType::Litep2p => {
516
                        crate::service::start_parachain_node::<sc_network::Litep2pNetworkBackend>(
517
                            config,
518
                            polkadot_config,
519
                            container_chain_config,
520
                            collator_options,
521
                            id,
522
                            hwbench,
523
                            cli.run.experimental_max_pov_percentage,
524
                        )
525
                            .await
526
                            .map(|r| r.0)
527
                            .map_err(Into::into)
528
                    }
529
                }
530
392
            })
531
        }
532
    }
533
202
}
534

            
535
impl DefaultConfigurationValues for RelayChainCli {
536
    fn p2p_listen_port() -> u16 {
537
        30334
538
    }
539

            
540
    fn rpc_listen_port() -> u16 {
541
        9945
542
    }
543

            
544
    fn prometheus_listen_port() -> u16 {
545
        9616
546
    }
547
}
548

            
549
impl CliConfiguration<Self> for RelayChainCli {
550
    fn shared_params(&self) -> &SharedParams {
551
        self.base.base.shared_params()
552
    }
553

            
554
    fn import_params(&self) -> Option<&ImportParams> {
555
        self.base.base.import_params()
556
    }
557

            
558
    fn network_params(&self) -> Option<&NetworkParams> {
559
        self.base.base.network_params()
560
    }
561

            
562
    fn keystore_params(&self) -> Option<&KeystoreParams> {
563
        self.base.base.keystore_params()
564
    }
565

            
566
    fn base_path(&self) -> Result<Option<BasePath>> {
567
        Ok(self
568
            .shared_params()
569
            .base_path()?
570
            .or_else(|| Some(self.base_path.clone().into())))
571
    }
572

            
573
    fn rpc_addr(&self, default_listen_port: u16) -> Result<Option<Vec<sc_cli::RpcEndpoint>>> {
574
        self.base.base.rpc_addr(default_listen_port)
575
    }
576

            
577
    fn prometheus_config(
578
        &self,
579
        default_listen_port: u16,
580
        chain_spec: &Box<dyn ChainSpec>,
581
    ) -> Result<Option<PrometheusConfig>> {
582
        self.base
583
            .base
584
            .prometheus_config(default_listen_port, chain_spec)
585
    }
586

            
587
    fn init<F>(&self, _support_url: &String, _impl_version: &String, _logger_hook: F) -> Result<()>
588
    where
589
        F: FnOnce(&mut sc_cli::LoggerBuilder),
590
    {
591
        unreachable!("PolkadotCli is never initialized; qed");
592
    }
593

            
594
    fn chain_id(&self, is_dev: bool) -> Result<String> {
595
        let chain_id = self.base.base.chain_id(is_dev)?;
596

            
597
        Ok(if chain_id.is_empty() {
598
            self.chain_id.clone().unwrap_or_default()
599
        } else {
600
            chain_id
601
        })
602
    }
603

            
604
    fn role(&self, is_dev: bool) -> Result<sc_service::Role> {
605
        self.base.base.role(is_dev)
606
    }
607

            
608
    fn transaction_pool(&self, is_dev: bool) -> Result<sc_service::config::TransactionPoolOptions> {
609
        self.base.base.transaction_pool(is_dev)
610
    }
611

            
612
    fn trie_cache_maximum_size(&self) -> Result<Option<usize>> {
613
        self.base.base.trie_cache_maximum_size()
614
    }
615

            
616
    fn rpc_methods(&self) -> Result<sc_service::config::RpcMethods> {
617
        self.base.base.rpc_methods()
618
    }
619

            
620
    fn rpc_max_connections(&self) -> Result<u32> {
621
        self.base.base.rpc_max_connections()
622
    }
623

            
624
    fn rpc_cors(&self, is_dev: bool) -> Result<Option<Vec<String>>> {
625
        self.base.base.rpc_cors(is_dev)
626
    }
627

            
628
    fn default_heap_pages(&self) -> Result<Option<u64>> {
629
        self.base.base.default_heap_pages()
630
    }
631

            
632
    fn force_authoring(&self) -> Result<bool> {
633
        self.base.base.force_authoring()
634
    }
635

            
636
    fn disable_grandpa(&self) -> Result<bool> {
637
        self.base.base.disable_grandpa()
638
    }
639

            
640
    fn max_runtime_instances(&self) -> Result<Option<usize>> {
641
        self.base.base.max_runtime_instances()
642
    }
643

            
644
    fn announce_block(&self) -> Result<bool> {
645
        self.base.base.announce_block()
646
    }
647

            
648
    fn telemetry_endpoints(
649
        &self,
650
        chain_spec: &Box<dyn ChainSpec>,
651
    ) -> Result<Option<sc_telemetry::TelemetryEndpoints>> {
652
        self.base.base.telemetry_endpoints(chain_spec)
653
    }
654

            
655
    fn node_name(&self) -> Result<String> {
656
        self.base.base.node_name()
657
    }
658
}
659

            
660
#[cfg(test)]
661
mod tests {
662
    use super::*;
663

            
664
    #[test]
665
1
    fn same_impl_version() {
666
1
        // Impl version depends on version in Cargo.toml
667
1
        // This is to verify we didn't forget to change one of them
668
1
        let v1 = ContainerChainCli::impl_version();
669
1
        let v2 = Cli::impl_version();
670
1

            
671
1
        assert_eq!(v1, v2);
672
1
    }
673
}