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

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

            
63
impl SubstrateCli for Cli {
64
204
    fn impl_name() -> String {
65
204
        "Container Chain Simple Node".into()
66
204
    }
67

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

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

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

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

            
90
64
    fn copyright_start_year() -> i32 {
91
64
        2020
92
64
    }
93

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

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

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

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

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

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

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

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

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

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

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

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

            
204
            runner.sync_run(|config| {
205
                let polkadot_cli = RelayChainCli::new(
206
                    &config,
207
                    [RelayChainCli::executable_name()]
208
                        .iter()
209
                        .chain(cli.relaychain_args().iter()),
210
                );
211

            
212
                let polkadot_config = SubstrateCli::create_configuration(
213
                    &polkadot_cli,
214
                    &polkadot_cli,
215
                    config.tokio_handle.clone(),
216
                )
217
                .map_err(|err| format!("Relay chain argument error: {}", err))?;
218

            
219
                cmd.run(config, polkadot_config)
220
            })
221
        }
222
        Some(Subcommand::ExportGenesisHead(cmd)) => {
223
            let runner = cli.create_runner(cmd)?;
224
            runner.sync_run(|config| {
225
                let partials = NodeConfig::new_builder(&config, None)?;
226
                cmd.run(partials.client)
227
            })
228
        }
229
        Some(Subcommand::ExportGenesisWasm(cmd)) => {
230
            let runner = cli.create_runner(cmd)?;
231
            runner.sync_run(|_config| {
232
                let spec = cli.load_spec(&cmd.shared_params.chain.clone().unwrap_or_default())?;
233
                cmd.run(&*spec)
234
            })
235
        }
236
        Some(Subcommand::Benchmark(cmd)) => {
237
            let runner = cli.create_runner(cmd)?;
238

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

            
295
64
            let runner = cli.create_runner(&cli.run.normalize())?;
296
64
            let collator_options = cli.run.collator_options();
297
64

            
298
64
            runner.run_node_until_exit(|config| async move {
299
64
                let hwbench = (!cli.no_hardware_benchmarks).then(||
300
                    config.database.path().map(|database_path| {
301
                        let _ = std::fs::create_dir_all(database_path);
302
                        sc_sysinfo::gather_hwbench(Some(database_path))
303
64
                    })).flatten();
304

            
305
64
                let para_id = chain_spec::Extensions::try_get(&*config.chain_spec)
306
64
                    .map(|e| e.para_id)
307
64
                    .ok_or("Could not find parachain ID in chain-spec.")?;
308

            
309
64
                let polkadot_cli = RelayChainCli::new(
310
64
                    &config,
311
64
                    [RelayChainCli::executable_name()].iter().chain(cli.relaychain_args().iter()),
312
64
                );
313
64

            
314
64
                let extension = chain_spec::Extensions::try_get(&*config.chain_spec);
315
64
                let relay_chain_id = extension.map(|e| e.relay_chain.clone());
316

            
317
64
                let dev_service =
318
64
                    config.chain_spec.is_dev() || relay_chain_id == Some("dev-service".to_string());
319

            
320
64
                let id = ParaId::from(para_id);
321
64

            
322
64
                if dev_service {
323
64
                    return crate::service::start_dev_node(config, cli.run.sealing, id, hwbench).await
324
64
                    .map_err(Into::into)
325
                }
326

            
327

            
328
                let parachain_account =
329
                    AccountIdConversion::<polkadot_primitives::AccountId>::into_account_truncating(&id);
330

            
331
                // We log both genesis states for reference, as fetching it from runtime would take significant time
332
                let block_state_v0: Block = generate_genesis_block(&*config.chain_spec, sp_runtime::StateVersion::V0)
333
                    .map_err(|e| format!("{:?}", e))?;
334
                let block_state_v1: Block = generate_genesis_block(&*config.chain_spec, sp_runtime::StateVersion::V1)
335
                    .map_err(|e| format!("{:?}", e))?;
336

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

            
340
                let tokio_handle = config.tokio_handle.clone();
341
                let polkadot_config =
342
                    SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, tokio_handle)
343
                        .map_err(|err| format!("Relay chain argument error: {}", err))?;
344

            
345
                info!("Parachain id: {:?}", id);
346
                info!("Parachain Account: {}", parachain_account);
347
                info!("Parachain genesis state V0: {}", genesis_state_v0);
348
                info!("Parachain genesis state V1: {}", genesis_state_v1);
349
                info!("Is collating: {}", if config.role.is_authority() { "yes" } else { "no" });
350

            
351
                if let cumulus_client_cli::RelayChainMode::ExternalRpc(rpc_target_urls) =
352
                    collator_options.clone().relay_chain_mode {
353
                    if !rpc_target_urls.is_empty() && !cli.relaychain_args().is_empty() {
354
                        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.");
355
                    }
356
                }
357

            
358
                crate::service::start_parachain_node(
359
                    config,
360
                    polkadot_config,
361
                    collator_options,
362
                    id,
363
                    hwbench,
364
                )
365
                .await
366
                .map(|r| r.0)
367
                .map_err(Into::into)
368
128
            })
369
        }
370
    }
371
70
}
372

            
373
impl DefaultConfigurationValues for RelayChainCli {
374
    fn p2p_listen_port() -> u16 {
375
        30334
376
    }
377

            
378
    fn rpc_listen_port() -> u16 {
379
        9945
380
    }
381

            
382
    fn prometheus_listen_port() -> u16 {
383
        9616
384
    }
385
}
386

            
387
impl CliConfiguration<Self> for RelayChainCli {
388
    fn shared_params(&self) -> &SharedParams {
389
        self.base.base.shared_params()
390
    }
391

            
392
    fn import_params(&self) -> Option<&ImportParams> {
393
        self.base.base.import_params()
394
    }
395

            
396
    fn network_params(&self) -> Option<&NetworkParams> {
397
        self.base.base.network_params()
398
    }
399

            
400
    fn keystore_params(&self) -> Option<&KeystoreParams> {
401
        self.base.base.keystore_params()
402
    }
403

            
404
    fn base_path(&self) -> Result<Option<BasePath>> {
405
        Ok(self
406
            .shared_params()
407
            .base_path()?
408
            .or_else(|| Some(self.base_path.clone().into())))
409
    }
410

            
411
    fn rpc_addr(&self, default_listen_port: u16) -> Result<Option<SocketAddr>> {
412
        self.base.base.rpc_addr(default_listen_port)
413
    }
414
    fn prometheus_config(
415
        &self,
416
        default_listen_port: u16,
417
        chain_spec: &Box<dyn ChainSpec>,
418
    ) -> Result<Option<PrometheusConfig>> {
419
        self.base
420
            .base
421
            .prometheus_config(default_listen_port, chain_spec)
422
    }
423

            
424
    fn init<F>(
425
        &self,
426
        _support_url: &String,
427
        _impl_version: &String,
428
        _logger_hook: F,
429
        _config: &sc_service::Configuration,
430
    ) -> Result<()>
431
    where
432
        F: FnOnce(&mut sc_cli::LoggerBuilder, &sc_service::Configuration),
433
    {
434
        unreachable!("PolkadotCli is never initialized; qed");
435
    }
436

            
437
    fn chain_id(&self, is_dev: bool) -> Result<String> {
438
        let chain_id = self.base.base.chain_id(is_dev)?;
439

            
440
        Ok(if chain_id.is_empty() {
441
            self.chain_id.clone().unwrap_or_default()
442
        } else {
443
            chain_id
444
        })
445
    }
446

            
447
    fn role(&self, is_dev: bool) -> Result<sc_service::Role> {
448
        self.base.base.role(is_dev)
449
    }
450

            
451
    fn transaction_pool(&self, is_dev: bool) -> Result<sc_service::config::TransactionPoolOptions> {
452
        self.base.base.transaction_pool(is_dev)
453
    }
454

            
455
    fn trie_cache_maximum_size(&self) -> Result<Option<usize>> {
456
        self.base.base.trie_cache_maximum_size()
457
    }
458

            
459
    fn rpc_methods(&self) -> Result<sc_service::config::RpcMethods> {
460
        self.base.base.rpc_methods()
461
    }
462

            
463
    fn rpc_max_connections(&self) -> Result<u32> {
464
        self.base.base.rpc_max_connections()
465
    }
466

            
467
    fn rpc_cors(&self, is_dev: bool) -> Result<Option<Vec<String>>> {
468
        self.base.base.rpc_cors(is_dev)
469
    }
470

            
471
    fn default_heap_pages(&self) -> Result<Option<u64>> {
472
        self.base.base.default_heap_pages()
473
    }
474

            
475
    fn force_authoring(&self) -> Result<bool> {
476
        self.base.base.force_authoring()
477
    }
478

            
479
    fn disable_grandpa(&self) -> Result<bool> {
480
        self.base.base.disable_grandpa()
481
    }
482

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

            
487
    fn announce_block(&self) -> Result<bool> {
488
        self.base.base.announce_block()
489
    }
490

            
491
    fn telemetry_endpoints(
492
        &self,
493
        chain_spec: &Box<dyn ChainSpec>,
494
    ) -> Result<Option<sc_telemetry::TelemetryEndpoints>> {
495
        self.base.base.telemetry_endpoints(chain_spec)
496
    }
497

            
498
    fn node_name(&self) -> Result<String> {
499
        self.base.base.node_name()
500
    }
501
}
502

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

            
506
    let runner = cli.create_runner(&cli.run.normalize())?;
507

            
508
    runner.run_node_until_exit(|config| async move {
509
        let orchestrator_chain_interface: Arc<dyn OrchestratorChainInterface>;
510
        let mut task_manager;
511

            
512
        if cli.orchestrator_endpoints.is_empty() {
513
            todo!("Start in process node")
514
        } else {
515
            task_manager = TaskManager::new(config.tokio_handle.clone(), None)
516
                .map_err(|e| sc_cli::Error::Application(Box::new(e)))?;
517

            
518
            orchestrator_chain_interface =
519
                tc_orchestrator_chain_rpc_interface::create_client_and_start_worker(
520
                    cli.orchestrator_endpoints.clone(),
521
                    &mut task_manager,
522
                    None,
523
                )
524
                .await
525
                .map(Arc::new)
526
                .map_err(|e| sc_cli::Error::Application(Box::new(e)))?;
527
        };
528

            
529
        // Spawn assignment watcher
530
        {
531
            let container_chain_cli = ContainerChainCli::new(
532
                &config,
533
                [ContainerChainCli::executable_name()]
534
                    .iter()
535
                    .chain(cli.container_chain_args().iter()),
536
            );
537

            
538
            log::info!("Container chain CLI: {container_chain_cli:?}");
539

            
540
            let para_id = chain_spec::Extensions::try_get(&*config.chain_spec)
541
                .map(|e| e.para_id)
542
                .ok_or("Could not find parachain ID in chain-spec.")?;
543

            
544
            let para_id = ParaId::from(para_id);
545

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

            
549
            let collator_options = cli.run.collator_options();
550

            
551
            let polkadot_cli = RelayChainCli::new(
552
                &config,
553
                [RelayChainCli::executable_name()]
554
                    .iter()
555
                    .chain(cli.relaychain_args().iter()),
556
            );
557

            
558
            let tokio_handle = config.tokio_handle.clone();
559
            let polkadot_config =
560
                SubstrateCli::create_configuration(&polkadot_cli, &polkadot_cli, tokio_handle)
561
                    .map_err(|err| format!("Relay chain argument error: {}", err))?;
562

            
563
            let telemetry = config
564
                .telemetry_endpoints
565
                .clone()
566
                .filter(|x| !x.is_empty())
567
                .map(|endpoints| -> std::result::Result<_, sc_telemetry::Error> {
568
                    let worker = TelemetryWorker::new(16)?;
569
                    let telemetry = worker.handle().new_telemetry(endpoints);
570
                    Ok((worker, telemetry))
571
                })
572
                .transpose()
573
                .map_err(sc_service::Error::Telemetry)?;
574

            
575
            let telemetry_worker_handle = telemetry.as_ref().map(|(worker, _)| worker.handle());
576

            
577
            let (relay_chain_interface, _collation_pair) = build_relay_chain_interface(
578
                polkadot_config,
579
                &config,
580
                telemetry_worker_handle,
581
                &mut task_manager,
582
                collator_options,
583
                None,
584
            )
585
            .await
586
            .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?;
587

            
588
            let relay_chain = crate::chain_spec::Extensions::try_get(&*config.chain_spec)
589
                .map(|e| e.relay_chain.clone())
590
                .ok_or("Could not find relay_chain extension in chain-spec.")?;
591

            
592
            let container_chain_spawner = ContainerChainSpawner {
593
                params: ContainerChainSpawnParams {
594
                    orchestrator_chain_interface,
595
                    container_chain_cli,
596
                    tokio_handle: config.tokio_handle.clone(),
597
                    chain_type: config.chain_spec.chain_type(),
598
                    relay_chain,
599
                    relay_chain_interface,
600
                    sync_keystore: keystore_container.keystore(),
601
                    orchestrator_para_id: para_id,
602
                    collation_params: None,
603
                    spawn_handle: task_manager.spawn_handle().clone(),
604
                    data_preserver: true,
605
                    generate_rpc_builder:
606
                        tc_service_container_chain::rpc::GenerateSubstrateRpcBuilder::<
607
                            container_chain_template_simple_runtime::RuntimeApi,
608
                        >::new(),
609

            
610
                    phantom: PhantomData,
611
                },
612
                state: Default::default(),
613
                // db cleanup task disabled here because it uses collator assignment to decide
614
                // which folders to keep and this is not a collator, this is an rpc node
615
                db_folder_cleanup_done: true,
616
                collate_on_tanssi: Arc::new(|| {
617
                    panic!("Called collate_on_tanssi outside of Tanssi node")
618
                }),
619
                collation_cancellation_constructs: None,
620
            };
621
            let state = container_chain_spawner.state.clone();
622

            
623
            task_manager.spawn_essential_handle().spawn(
624
                "container-chain-assignment-watcher",
625
                None,
626
                tc_service_container_chain::data_preservers::task_watch_assignment(
627
                    container_chain_spawner,
628
                    profile_id,
629
                ),
630
            );
631

            
632
            task_manager.spawn_essential_handle().spawn(
633
                "container-chain-spawner-debug-state",
634
                None,
635
                tc_service_container_chain::monitor::monitor_task(state),
636
            );
637
        }
638

            
639
        Ok(task_manager)
640
    })
641
}