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, frontier_database_dir, NodeConfig},
22
    },
23
    container_chain_template_frontier_runtime::Block,
24
    core::marker::PhantomData,
25
    cumulus_client_service::{
26
        build_relay_chain_interface, storage_proof_size::HostFunctions as ReclaimHostFunctions,
27
    },
28
    cumulus_primitives_core::ParaId,
29
    dc_orchestrator_chain_interface::OrchestratorChainInterface,
30
    frame_benchmarking_cli::{BenchmarkCmd, SUBSTRATE_REFERENCE_HARDWARE},
31
    log::{info, warn},
32
    node_common::{command::generate_genesis_block, service::NodeBuilderConfig as _},
33
    parity_scale_codec::Encode,
34
    polkadot_cli::IdentifyVariant,
35
    sc_cli::{
36
        ChainSpec, CliConfiguration, DefaultConfigurationValues, ImportParams, KeystoreParams,
37
        NetworkParams, Result, SharedParams, SubstrateCli,
38
    },
39
    sc_service::{
40
        config::{BasePath, PrometheusConfig},
41
        DatabaseSource, KeystoreContainer, TaskManager,
42
    },
43
    sc_telemetry::TelemetryWorker,
44
    sp_core::hexdisplay::HexDisplay,
45
    sp_runtime::traits::{AccountIdConversion, Block as BlockT},
46
    std::{net::SocketAddr, sync::Arc},
47
    tc_service_container_chain::{
48
        cli::ContainerChainCli,
49
        spawner::{ContainerChainSpawnParams, ContainerChainSpawner},
50
    },
51
};
52

            
53
144
fn load_spec(id: &str, para_id: ParaId) -> std::result::Result<Box<dyn ChainSpec>, String> {
54
144
    Ok(match id {
55
144
        "dev" => Box::new(chain_spec::development_config(para_id, vec![])),
56
142
        "template-rococo" => Box::new(chain_spec::local_testnet_config(para_id, vec![])),
57
142
        "" | "local" => Box::new(chain_spec::local_testnet_config(para_id, vec![])),
58
142
        path => Box::new(chain_spec::ChainSpec::from_json_file(
59
142
            std::path::PathBuf::from(path),
60
142
        )?),
61
    })
62
144
}
63

            
64
impl SubstrateCli for Cli {
65
426
    fn impl_name() -> String {
66
426
        "Container Chain Frontier Node".into()
67
426
    }
68

            
69
714
    fn impl_version() -> String {
70
714
        env!("SUBSTRATE_CLI_IMPL_VERSION").into()
71
714
    }
72

            
73
144
    fn description() -> String {
74
144
        format!(
75
144
            "Container Chain Frontier Node\n\nThe command-line arguments provided first will be \
76
144
		passed to the parachain node, while the arguments provided after -- will be passed \
77
144
		to the relay chain node.\n\n\
78
144
		{} <parachain-args> -- <relay-chain-args>",
79
144
            Self::executable_name()
80
144
        )
81
144
    }
82

            
83
282
    fn author() -> String {
84
282
        env!("CARGO_PKG_AUTHORS").into()
85
282
    }
86

            
87
144
    fn support_url() -> String {
88
144
        "https://github.com/paritytech/cumulus/issues/new".into()
89
144
    }
90

            
91
138
    fn copyright_start_year() -> i32 {
92
138
        2020
93
138
    }
94

            
95
144
    fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {
96
144
        load_spec(id, self.para_id.unwrap_or(2000).into())
97
144
    }
98
}
99

            
100
impl SubstrateCli for RelayChainCli {
101
    fn impl_name() -> String {
102
        "Container Chain Frontier Node".into()
103
    }
104

            
105
    fn impl_version() -> String {
106
        env!("SUBSTRATE_CLI_IMPL_VERSION").into()
107
    }
108

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

            
119
    fn author() -> String {
120
        env!("CARGO_PKG_AUTHORS").into()
121
    }
122

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

            
127
    fn copyright_start_year() -> i32 {
128
        2020
129
    }
130

            
131
    fn load_spec(&self, id: &str) -> std::result::Result<Box<dyn sc_service::ChainSpec>, String> {
132
        polkadot_cli::Cli::from_iter([RelayChainCli::executable_name()].iter()).load_spec(id)
133
    }
134
}
135

            
136
macro_rules! construct_async_run {
137
	(|$components:ident, $cli:ident, $cmd:ident, $config:ident| $( $code:tt )* ) => {{
138
		let runner = $cli.create_runner($cmd)?;
139
		runner.async_run(|mut $config| {
140
			let $components = NodeConfig::new_builder(&mut $config, None)?;
141
			let inner = { $( $code )* };
142

            
143
            let task_manager = $components.task_manager;
144
			inner.map(|v| (v, task_manager))
145
		})
146
	}}
147
}
148

            
149
/// Parse command line arguments into service configuration.
150
144
pub fn run() -> Result<()> {
151
144
    let cli = Cli::from_args();
152

            
153
6
    match &cli.subcommand {
154
4
        Some(Subcommand::BuildSpec(cmd)) => {
155
4
            let runner = cli.create_runner(cmd)?;
156
4
            runner.sync_run(|config| {
157
4
                let chain_spec = if let Some(para_id) = cmd.parachain_id {
158
                    if cmd.base.shared_params.dev {
159
                        Box::new(chain_spec::development_config(
160
                            para_id.into(),
161
                            cmd.add_bootnode.clone(),
162
                        ))
163
                    } else {
164
                        Box::new(chain_spec::local_testnet_config(
165
                            para_id.into(),
166
                            cmd.add_bootnode.clone(),
167
                        ))
168
                    }
169
                } else {
170
4
                    config.chain_spec
171
                };
172
4
                cmd.base.run(chain_spec, config.network)
173
4
            })
174
        }
175
        Some(Subcommand::CheckBlock(cmd)) => {
176
            construct_async_run!(|components, cli, cmd, config| {
177
                let (_, import_queue) = service::import_queue(&config, &components);
178
                Ok(cmd.run(components.client, import_queue))
179
            })
180
        }
181
        Some(Subcommand::ExportBlocks(cmd)) => {
182
            construct_async_run!(|components, cli, cmd, config| {
183
                Ok(cmd.run(components.client, config.database))
184
            })
185
        }
186
        Some(Subcommand::ExportState(cmd)) => {
187
            construct_async_run!(|components, cli, cmd, config| {
188
                Ok(cmd.run(components.client, config.chain_spec))
189
            })
190
        }
191
        Some(Subcommand::ImportBlocks(cmd)) => {
192
            construct_async_run!(|components, cli, cmd, config| {
193
                let (_, import_queue) = service::import_queue(&config, &components);
194
                Ok(cmd.run(components.client, import_queue))
195
            })
196
        }
197
        Some(Subcommand::Revert(cmd)) => {
198
            construct_async_run!(|components, cli, cmd, config| {
199
                Ok(cmd.run(components.client, components.backend, None))
200
            })
201
        }
202
        Some(Subcommand::PurgeChain(cmd)) => {
203
            let runner = cli.create_runner(cmd)?;
204

            
205
            runner.sync_run(|config| {
206
                // Remove Frontier offchain db
207
                let frontier_database_config = match config.database {
208
                    DatabaseSource::RocksDb { .. } => DatabaseSource::RocksDb {
209
                        path: frontier_database_dir(&config, "db"),
210
                        cache_size: 0,
211
                    },
212
                    DatabaseSource::ParityDb { .. } => DatabaseSource::ParityDb {
213
                        path: frontier_database_dir(&config, "paritydb"),
214
                    },
215
                    _ => {
216
                        return Err(format!("Cannot purge `{:?}` database", config.database).into())
217
                    }
218
                };
219

            
220
                cmd.base.run(frontier_database_config)?;
221

            
222
                let polkadot_cli = RelayChainCli::new(
223
                    &config,
224
                    [RelayChainCli::executable_name()]
225
                        .iter()
226
                        .chain(cli.relaychain_args().iter()),
227
                );
228

            
229
                let polkadot_config = SubstrateCli::create_configuration(
230
                    &polkadot_cli,
231
                    &polkadot_cli,
232
                    config.tokio_handle.clone(),
233
                )
234
                .map_err(|err| format!("Relay chain argument error: {}", err))?;
235

            
236
                cmd.run(config, polkadot_config)
237
            })
238
        }
239
        Some(Subcommand::ExportGenesisHead(cmd)) => {
240
            let runner = cli.create_runner(cmd)?;
241
            runner.sync_run(|config| {
242
                let partials = NodeConfig::new_builder(&config, None)?;
243
                cmd.run(partials.client)
244
            })
245
        }
246
        Some(Subcommand::ExportGenesisWasm(cmd)) => {
247
            let runner = cli.create_runner(cmd)?;
248
            runner.sync_run(|_config| {
249
                let spec = cli.load_spec(&cmd.shared_params.chain.clone().unwrap_or_default())?;
250
                cmd.run(&*spec)
251
            })
252
        }
253
        Some(Subcommand::Benchmark(cmd)) => {
254
            let runner = cli.create_runner(cmd)?;
255
            // Switch on the concrete benchmark sub-command-
256
            match cmd {
257
                BenchmarkCmd::Pallet(cmd) => {
258
                    if cfg!(feature = "runtime-benchmarks") {
259
                        runner.sync_run(|config| {
260
                            cmd.run_with_spec::<sp_runtime::traits::HashingFor<Block>, ReclaimHostFunctions>(Some(
261
                                config.chain_spec,
262
                            ))
263
                        })
264
                    } else {
265
                        Err("Benchmarking wasn't enabled when building the node. \
266
			  You can enable it with `--features runtime-benchmarks`."
267
                            .into())
268
                    }
269
                }
270
                BenchmarkCmd::Block(cmd) => runner.sync_run(|config| {
271
                    let partials = NodeConfig::new_builder(&config, None)?;
272
                    cmd.run(partials.client)
273
                }),
274
                #[cfg(not(feature = "runtime-benchmarks"))]
275
                BenchmarkCmd::Storage(_) => Err(sc_cli::Error::Input(
276
                    "Compile with --features=runtime-benchmarks \
277
						to enable storage benchmarks."
278
                        .into(),
279
                )),
280
                #[cfg(feature = "runtime-benchmarks")]
281
                BenchmarkCmd::Storage(cmd) => runner.sync_run(|config| {
282
                    let partials = NodeConfig::new_builder(&config, None)?;
283
                    let db = partials.backend.expose_db();
284
                    let storage = partials.backend.expose_storage();
285
                    cmd.run(config, partials.client.clone(), db, storage)
286
                }),
287
                BenchmarkCmd::Machine(cmd) => {
288
                    runner.sync_run(|config| cmd.run(&config, SUBSTRATE_REFERENCE_HARDWARE.clone()))
289
                }
290
                // NOTE: this allows the Client to leniently implement
291
                // new benchmark commands without requiring a companion MR.
292
                #[allow(unreachable_patterns)]
293
                _ => Err("Benchmarking sub-command unsupported".into()),
294
            }
295
        }
296
2
        Some(Subcommand::PrecompileWasm(cmd)) => {
297
2
            let runner = cli.create_runner(cmd)?;
298
2
            runner.async_run(|config| {
299
2
                let partials = NodeConfig::new_builder(&config, None)?;
300
2
                Ok((
301
2
                    cmd.run(partials.backend, config.chain_spec),
302
2
                    partials.task_manager,
303
2
                ))
304
2
            })
305
        }
306
        None => {
307
138
            if let Some(profile_id) = cli.rpc_provider_profile_id {
308
                return rpc_provider_mode(cli, profile_id);
309
138
            }
310

            
311
138
            let runner = cli.create_runner(&cli.run.normalize())?;
312
138
            let collator_options = cli.run.collator_options();
313
138

            
314
138
            runner.run_node_until_exit(|config| async move {
315
138
                let relaychain_args = cli.relaychain_args();
316
138
				let hwbench = (!cli.no_hardware_benchmarks).then(||
317
					config.database.path().map(|database_path| {
318
						let _ = std::fs::create_dir_all(database_path);
319
						sc_sysinfo::gather_hwbench(Some(database_path))
320
138
					})).flatten();
321

            
322
138
				let para_id = chain_spec::Extensions::try_get(&*config.chain_spec)
323
138
					.map(|e| e.para_id)
324
138
					.ok_or("Could not find parachain ID in chain-spec.")?;
325

            
326
138
				let polkadot_cli = RelayChainCli::new(
327
138
					&config,
328
138
					[RelayChainCli::executable_name()].iter().chain(relaychain_args.iter()),
329
138
				);
330
138

            
331
138
                let rpc_config = crate::cli::RpcConfig {
332
138
					eth_log_block_cache: cli.run.eth_log_block_cache,
333
138
					eth_statuses_cache: cli.run.eth_statuses_cache,
334
138
					fee_history_limit: cli.run.fee_history_limit,
335
138
					max_past_logs: cli.run.max_past_logs,
336
138
				};
337
138

            
338
138
                let extension = chain_spec::Extensions::try_get(&*config.chain_spec);
339
138

            
340
138
				let relay_chain_id = extension.map(|e| e.relay_chain.clone());
341

            
342
138
                let dev_service =
343
138
					config.chain_spec.is_dev() || relay_chain_id == Some("dev-service".to_string());
344

            
345
138
                let id = ParaId::from(para_id);
346
138

            
347
138
				if dev_service {
348
138
					return crate::service::start_dev_node(config, cli.run.sealing, rpc_config, id, hwbench).await
349
138
                    .map_err(Into::into)
350
				}
351

            
352

            
353
				let parachain_account =
354
					AccountIdConversion::<polkadot_primitives::AccountId>::into_account_truncating(&id);
355

            
356
                // We log both genesis states for reference, as fetching it from runtime would take significant time
357
				let block_state_v0: Block = generate_genesis_block(&*config.chain_spec, sp_runtime::StateVersion::V0)
358
					.map_err(|e| format!("{:?}", e))?;
359
                let block_state_v1: Block = generate_genesis_block(&*config.chain_spec, sp_runtime::StateVersion::V1)
360
					.map_err(|e| format!("{:?}", e))?;
361

            
362
				let genesis_state_v0 = format!("0x{:?}", HexDisplay::from(&block_state_v0.header().encode()));
363
				let genesis_state_v1 = format!("0x{:?}", HexDisplay::from(&block_state_v1.header().encode()));
364

            
365
				let tokio_handle = config.tokio_handle.clone();
366
				let polkadot_config =
367
					SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, tokio_handle)
368
						.map_err(|err| format!("Relay chain argument error: {}", err))?;
369

            
370
				info!("Parachain id: {:?}", id);
371
				info!("Parachain Account: {}", parachain_account);
372
				info!("Parachain genesis state V0: {}", genesis_state_v0);
373
                info!("Parachain genesis state V1: {}", genesis_state_v1);
374

            
375
				info!("Is collating: {}", if config.role.is_authority() { "yes" } else { "no" });
376

            
377
                if let cumulus_client_cli::RelayChainMode::ExternalRpc(rpc_target_urls) =
378
                collator_options.clone().relay_chain_mode {
379
                    if !rpc_target_urls.is_empty() && !relaychain_args.is_empty() {
380
                        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.");
381
                    }
382
                }
383

            
384
                crate::service::start_parachain_node(
385
                    config,
386
                    polkadot_config,
387
                    collator_options,
388
                    id,
389
                    rpc_config,
390
                    hwbench,
391
                )
392
                    .await
393
                    .map(|r| r.0)
394
                    .map_err(Into::into)
395
276
			})
396
        }
397
    }
398
144
}
399

            
400
impl DefaultConfigurationValues for RelayChainCli {
401
    fn p2p_listen_port() -> u16 {
402
        30334
403
    }
404

            
405
    fn rpc_listen_port() -> u16 {
406
        9945
407
    }
408

            
409
    fn prometheus_listen_port() -> u16 {
410
        9616
411
    }
412
}
413

            
414
impl CliConfiguration<Self> for RelayChainCli {
415
    fn shared_params(&self) -> &SharedParams {
416
        self.base.base.shared_params()
417
    }
418

            
419
    fn import_params(&self) -> Option<&ImportParams> {
420
        self.base.base.import_params()
421
    }
422

            
423
    fn network_params(&self) -> Option<&NetworkParams> {
424
        self.base.base.network_params()
425
    }
426

            
427
    fn keystore_params(&self) -> Option<&KeystoreParams> {
428
        self.base.base.keystore_params()
429
    }
430

            
431
    fn base_path(&self) -> Result<Option<BasePath>> {
432
        Ok(self
433
            .shared_params()
434
            .base_path()?
435
            .or_else(|| Some(self.base_path.clone().into())))
436
    }
437

            
438
    fn rpc_addr(&self, default_listen_port: u16) -> Result<Option<SocketAddr>> {
439
        self.base.base.rpc_addr(default_listen_port)
440
    }
441

            
442
    fn prometheus_config(
443
        &self,
444
        default_listen_port: u16,
445
        chain_spec: &Box<dyn ChainSpec>,
446
    ) -> Result<Option<PrometheusConfig>> {
447
        self.base
448
            .base
449
            .prometheus_config(default_listen_port, chain_spec)
450
    }
451

            
452
    fn init<F>(
453
        &self,
454
        _support_url: &String,
455
        _impl_version: &String,
456
        _logger_hook: F,
457
        _config: &sc_service::Configuration,
458
    ) -> Result<()>
459
    where
460
        F: FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration),
461
    {
462
        unreachable!("PolkadotCli is never initialized; qed");
463
    }
464

            
465
    fn chain_id(&self, is_dev: bool) -> Result<String> {
466
        let chain_id = self.base.base.chain_id(is_dev)?;
467

            
468
        Ok(if chain_id.is_empty() {
469
            self.chain_id.clone().unwrap_or_default()
470
        } else {
471
            chain_id
472
        })
473
    }
474

            
475
    fn role(&self, is_dev: bool) -> Result<sc_service::Role> {
476
        self.base.base.role(is_dev)
477
    }
478

            
479
    fn transaction_pool(&self, is_dev: bool) -> Result<sc_service::config::TransactionPoolOptions> {
480
        self.base.base.transaction_pool(is_dev)
481
    }
482

            
483
    fn trie_cache_maximum_size(&self) -> Result<Option<usize>> {
484
        self.base.base.trie_cache_maximum_size()
485
    }
486

            
487
    fn rpc_methods(&self) -> Result<sc_service::config::RpcMethods> {
488
        self.base.base.rpc_methods()
489
    }
490

            
491
    fn rpc_max_connections(&self) -> Result<u32> {
492
        self.base.base.rpc_max_connections()
493
    }
494

            
495
    fn rpc_cors(&self, is_dev: bool) -> Result<Option<Vec<String>>> {
496
        self.base.base.rpc_cors(is_dev)
497
    }
498

            
499
    fn default_heap_pages(&self) -> Result<Option<u64>> {
500
        self.base.base.default_heap_pages()
501
    }
502

            
503
    fn force_authoring(&self) -> Result<bool> {
504
        self.base.base.force_authoring()
505
    }
506

            
507
    fn disable_grandpa(&self) -> Result<bool> {
508
        self.base.base.disable_grandpa()
509
    }
510

            
511
    fn max_runtime_instances(&self) -> Result<Option<usize>> {
512
        self.base.base.max_runtime_instances()
513
    }
514

            
515
    fn announce_block(&self) -> Result<bool> {
516
        self.base.base.announce_block()
517
    }
518

            
519
    fn telemetry_endpoints(
520
        &self,
521
        chain_spec: &Box<dyn ChainSpec>,
522
    ) -> Result<Option<sc_telemetry::TelemetryEndpoints>> {
523
        self.base.base.telemetry_endpoints(chain_spec)
524
    }
525

            
526
    fn node_name(&self) -> Result<String> {
527
        self.base.base.node_name()
528
    }
529
}
530

            
531
fn rpc_provider_mode(cli: Cli, profile_id: u64) -> Result<()> {
532
    log::info!("Starting in RPC provider mode!");
533

            
534
    let runner = cli.create_runner(&cli.run.normalize())?;
535

            
536
    runner.run_node_until_exit(|config| async move {
537
        let orchestrator_chain_interface: Arc<dyn OrchestratorChainInterface>;
538
        let mut task_manager;
539

            
540
        if cli.orchestrator_endpoints.is_empty() {
541
            todo!("Start in process node")
542
        } else {
543
            task_manager = TaskManager::new(config.tokio_handle.clone(), None)
544
                .map_err(|e| sc_cli::Error::Application(Box::new(e)))?;
545

            
546
            orchestrator_chain_interface =
547
                tc_orchestrator_chain_rpc_interface::create_client_and_start_worker(
548
                    cli.orchestrator_endpoints.clone(),
549
                    &mut task_manager,
550
                    None,
551
                )
552
                .await
553
                .map(Arc::new)
554
                .map_err(|e| sc_cli::Error::Application(Box::new(e)))?;
555
        };
556

            
557
        // Spawn assignment watcher
558
        {
559
            let mut container_chain_cli = ContainerChainCli::new(
560
                &config,
561
                [ContainerChainCli::executable_name()]
562
                    .iter()
563
                    .chain(cli.container_chain_args().iter()),
564
            );
565

            
566
            // If the container chain args have no --wasmtime-precompiled flag, use the same as the orchestrator
567
            if container_chain_cli
568
                .base
569
                .base
570
                .import_params
571
                .wasmtime_precompiled
572
                .is_none()
573
            {
574
                container_chain_cli
575
                    .base
576
                    .base
577
                    .import_params
578
                    .wasmtime_precompiled
579
                    .clone_from(&config.wasmtime_precompiled);
580
            }
581

            
582
            log::info!("Container chain CLI: {container_chain_cli:?}");
583

            
584
            let para_id = chain_spec::Extensions::try_get(&*config.chain_spec)
585
                .map(|e| e.para_id)
586
                .ok_or("Could not find parachain ID in chain-spec.")?;
587

            
588
            let para_id = ParaId::from(para_id);
589

            
590
            // TODO: Once there is an embeded node this should use it.
591
            let keystore_container = KeystoreContainer::new(&config.keystore)?;
592

            
593
            let collator_options = cli.run.collator_options();
594

            
595
            let polkadot_cli = RelayChainCli::new(
596
                &config,
597
                [RelayChainCli::executable_name()]
598
                    .iter()
599
                    .chain(cli.relaychain_args().iter()),
600
            );
601

            
602
            let tokio_handle = config.tokio_handle.clone();
603
            let polkadot_config =
604
                SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, tokio_handle)
605
                    .map_err(|err| format!("Relay chain argument error: {}", err))?;
606

            
607
            let telemetry = config
608
                .telemetry_endpoints
609
                .clone()
610
                .filter(|x| !x.is_empty())
611
                .map(|endpoints| -> std::result::Result<_, sc_telemetry::Error> {
612
                    let worker = TelemetryWorker::new(16)?;
613
                    let telemetry = worker.handle().new_telemetry(endpoints);
614
                    Ok((worker, telemetry))
615
                })
616
                .transpose()
617
                .map_err(sc_service::Error::Telemetry)?;
618

            
619
            let telemetry_worker_handle = telemetry.as_ref().map(|(worker, _)| worker.handle());
620

            
621
            let (relay_chain_interface, _collation_pair) = build_relay_chain_interface(
622
                polkadot_config,
623
                &config,
624
                telemetry_worker_handle,
625
                &mut task_manager,
626
                collator_options,
627
                None,
628
            )
629
            .await
630
            .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?;
631

            
632
            let relay_chain = crate::chain_spec::Extensions::try_get(&*config.chain_spec)
633
                .map(|e| e.relay_chain.clone())
634
                .ok_or("Could not find relay_chain extension in chain-spec.")?;
635

            
636
            let rpc_config = crate::cli::RpcConfig {
637
                eth_log_block_cache: cli.run.eth_log_block_cache,
638
                eth_statuses_cache: cli.run.eth_statuses_cache,
639
                fee_history_limit: cli.run.fee_history_limit,
640
                max_past_logs: cli.run.max_past_logs,
641
            };
642

            
643
            let container_chain_spawner = ContainerChainSpawner {
644
                params: ContainerChainSpawnParams {
645
                    orchestrator_chain_interface,
646
                    container_chain_cli,
647
                    tokio_handle: config.tokio_handle.clone(),
648
                    chain_type: config.chain_spec.chain_type(),
649
                    relay_chain,
650
                    relay_chain_interface,
651
                    sync_keystore: keystore_container.keystore(),
652
                    orchestrator_para_id: para_id,
653
                    collation_params: None,
654
                    spawn_handle: task_manager.spawn_handle().clone(),
655
                    data_preserver: true,
656
                    generate_rpc_builder: crate::rpc::GenerateFrontierRpcBuilder::<
657
                        container_chain_template_frontier_runtime::RuntimeApi,
658
                    > {
659
                        rpc_config,
660
                        phantom: PhantomData,
661
                    },
662

            
663
                    phantom: PhantomData,
664
                },
665
                state: Default::default(),
666
                // db cleanup task disabled here because it uses collator assignment to decide
667
                // which folders to keep and this is not a collator, this is an rpc node
668
                db_folder_cleanup_done: true,
669
                collate_on_tanssi: Arc::new(|| {
670
                    panic!("Called collate_on_tanssi outside of Tanssi node")
671
                }),
672
                collation_cancellation_constructs: None,
673
            };
674
            let state = container_chain_spawner.state.clone();
675

            
676
            task_manager.spawn_essential_handle().spawn(
677
                "container-chain-assignment-watcher",
678
                None,
679
                tc_service_container_chain::data_preservers::task_watch_assignment(
680
                    container_chain_spawner,
681
                    profile_id,
682
                ),
683
            );
684

            
685
            task_manager.spawn_essential_handle().spawn(
686
                "container-chain-spawner-debug-state",
687
                None,
688
                tc_service_container_chain::monitor::monitor_task(state),
689
            );
690
        }
691

            
692
        Ok(task_manager)
693
    })
694
}