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
//! Code responsible for spawning orchestrator chain node, either in solochain
18
//! or parachain mode. It is extracted outside of `tanssi-node`/`chains/orchestrator-paras/node`
19
//! to be used by `service-container-chain-rpc-provider` which can embed an orchestrator node too.
20

            
21
pub mod parachain;
22
pub mod solochain;
23

            
24
use {
25
    dc_orchestrator_chain_interface::OrchestratorChainInterface,
26
    futures::StreamExt,
27
    nimbus_primitives::NimbusPair,
28
    sp_core::{traits::SpawnEssentialNamed, H256},
29
    sp_keystore::KeystorePtr,
30
    std::sync::Arc,
31
    tc_service_container_chain_spawner::spawner::CcSpawnMsg,
32
    tokio::sync::mpsc,
33
};
34

            
35
/// Background task used to detect changes to container chain assignment,
36
/// and start/stop container chains on demand. The check runs on every new block.
37
pub fn build_check_assigned_para_id(
38
    client: Arc<dyn OrchestratorChainInterface>,
39
    sync_keystore: KeystorePtr,
40
    cc_spawn_tx: mpsc::UnboundedSender<CcSpawnMsg>,
41
    spawner: impl SpawnEssentialNamed,
42
) {
43
    let check_assigned_para_id_task = async move {
44
        // Subscribe to new blocks in order to react to para id assignment
45
        // This must be the stream of finalized blocks, otherwise the collators may rotate to a
46
        // different chain before the block is finalized, and that could lead to a stalled chain
47
        let mut import_notifications = client.finality_notification_stream().await.unwrap();
48

            
49
        while let Some(msg) = import_notifications.next().await {
50
            let block_hash = msg.hash();
51
            let client_set_aside_for_cidp = client.clone();
52
            let sync_keystore = sync_keystore.clone();
53
            let cc_spawn_tx = cc_spawn_tx.clone();
54

            
55
            check_assigned_para_id(
56
                cc_spawn_tx,
57
                sync_keystore,
58
                client_set_aside_for_cidp,
59
                block_hash,
60
            )
61
            .await
62
            .unwrap();
63
        }
64
    };
65

            
66
    spawner.spawn_essential(
67
        "check-assigned-para-id",
68
        None,
69
        Box::pin(check_assigned_para_id_task),
70
    );
71
}
72

            
73
/// Check the parachain assignment using the orchestrator chain client, and send a `CcSpawnMsg` to
74
/// start or stop the required container chains.
75
///
76
/// Checks the assignment for the next block, so if there is a session change on block 15, this will
77
/// detect the assignment change after importing block 14.
78
async fn check_assigned_para_id(
79
    cc_spawn_tx: mpsc::UnboundedSender<CcSpawnMsg>,
80
    sync_keystore: KeystorePtr,
81
    client_set_aside_for_cidp: Arc<dyn OrchestratorChainInterface>,
82
    block_hash: H256,
83
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
84
    // Check current assignment
85
    let current_container_chain_para_id =
86
        tc_consensus::first_eligible_key::<dyn OrchestratorChainInterface, NimbusPair>(
87
            client_set_aside_for_cidp.as_ref(),
88
            &block_hash,
89
            sync_keystore.clone(),
90
        )
91
        .await
92
        .map(|(_nimbus_key, para_id)| para_id);
93

            
94
    // Check assignment in the next session
95
    let next_container_chain_para_id = tc_consensus::first_eligible_key_next_session::<
96
        dyn OrchestratorChainInterface,
97
        NimbusPair,
98
    >(
99
        client_set_aside_for_cidp.as_ref(),
100
        &block_hash,
101
        sync_keystore,
102
    )
103
    .await
104
    .map(|(_nimbus_key, para_id)| para_id);
105

            
106
    cc_spawn_tx.send(CcSpawnMsg::UpdateAssignment {
107
        current: current_container_chain_para_id,
108
        next: next_container_chain_para_id,
109
    })?;
110

            
111
    Ok(())
112
}