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
#![cfg(test)]
17
use {
18
    crate::tests::common::*,
19
    frame_support::BoundedBTreeSet,
20
    pallet_inactivity_tracking::pallet::{
21
        ActiveCollatorsForCurrentSession, ActiveContainerChainsForCurrentSession, InactiveCollators,
22
    },
23
    parity_scale_codec::Encode,
24
    sp_consensus_aura::AURA_ENGINE_ID,
25
    sp_runtime::{traits::BlakeTwo256, DigestItem},
26
    test_relay_sproof_builder::{HeaderAs, ParaHeaderSproofBuilder, ParaHeaderSproofBuilderItem},
27
    tp_traits::{NodeActivityTrackingHelper, ParaId},
28
};
29

            
30
1
fn note_blocks_for_container_chain(
31
1
    para_id_1: ParaId,
32
1
    para_id_2: ParaId,
33
1
    start_block: u32,
34
1
    end_block: u32,
35
1
) {
36
1
    // Simulate the inclusion of a block for a container chain
37
1
    let mut sproof = ParaHeaderSproofBuilder::default();
38

            
39
11
    for block_number in start_block..=end_block {
40
11
        let s1 = ParaHeaderSproofBuilderItem {
41
11
            para_id: para_id_1,
42
11
            author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::<u32, BlakeTwo256> {
43
11
                parent_hash: Default::default(),
44
11
                number: block_number,
45
11
                state_root: Default::default(),
46
11
                extrinsics_root: Default::default(),
47
11
                digest: sp_runtime::generic::Digest {
48
11
                    logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, 2u64.encode())],
49
11
                },
50
11
            }),
51
11
        };
52
11
        let s2 = ParaHeaderSproofBuilderItem {
53
11
            para_id: para_id_2,
54
11
            author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::<u32, BlakeTwo256> {
55
11
                parent_hash: Default::default(),
56
11
                number: block_number,
57
11
                state_root: Default::default(),
58
11
                extrinsics_root: Default::default(),
59
11
                digest: sp_runtime::generic::Digest {
60
11
                    logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, 3u64.encode())],
61
11
                },
62
11
            }),
63
11
        };
64
11
        sproof.items.push(s1);
65
11
        sproof.items.push(s2);
66
11
    }
67
1
    set_author_noting_inherent_data(sproof.clone())
68
1
}
69

            
70
6
fn get_collators_set(
71
6
    collators: Vec<cumulus_primitives_core::relay_chain::AccountId>,
72
6
) -> BoundedBTreeSet<
73
6
    cumulus_primitives_core::relay_chain::AccountId,
74
6
    <Runtime as pallet_inactivity_tracking::Config>::MaxCollatorsPerSession,
75
6
> {
76
6
    let mut collator_set: BoundedBTreeSet<
77
6
        cumulus_primitives_core::relay_chain::AccountId,
78
6
        <Runtime as pallet_inactivity_tracking::Config>::MaxCollatorsPerSession,
79
6
    > = BoundedBTreeSet::new();
80
6
    collators.iter().for_each(|collator| {
81
6
        collator_set.try_insert(collator.clone()).ok();
82
6
    });
83
6
    collator_set
84
6
}
85

            
86
5
fn get_chains_set(
87
5
    chains: Vec<ParaId>,
88
5
) -> BoundedBTreeSet<ParaId, <Runtime as pallet_inactivity_tracking::Config>::MaxContainerChains> {
89
5
    let mut chains_set: BoundedBTreeSet<
90
5
        ParaId,
91
5
        <Runtime as pallet_inactivity_tracking::Config>::MaxContainerChains,
92
5
    > = BoundedBTreeSet::new();
93
9
    for para_id in chains {
94
4
        chains_set.try_insert(para_id).unwrap();
95
4
    }
96
5
    chains_set
97
5
}
98

            
99
#[test]
100
1
fn inactivity_tracking_correctly_updates_storages() {
101
1
    ExtBuilder::default()
102
1
        .with_empty_parachains(vec![3000, 3001])
103
1
        .with_collators(vec![
104
1
            (AccountId::from(ALICE), 100_000),
105
1
            (AccountId::from(BOB), 100_000),
106
1
            (AccountId::from(CHARLIE), 100_000),
107
1
            (AccountId::from(DAVE), 100_000),
108
1
        ])
109
1
        .build()
110
1
        .execute_with(|| {
111
1
            let assignment = TanssiCollatorAssignment::collator_container_chain();
112
1
            assert_eq!(
113
1
                assignment.container_chains[&3000.into()],
114
1
                vec![ALICE.into(), BOB.into()]
115
1
            );
116
1
            assert_eq!(
117
1
                assignment.container_chains[&3001.into()],
118
1
                vec![CHARLIE.into(), DAVE.into()]
119
1
            );
120
1
            note_blocks_for_container_chain(3000.into(), 3001.into(), 1, session_to_block(1));
121
1
            run_block();
122
1
            assert_eq!(
123
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
124
1
                get_collators_set(vec![ALICE.into(), DAVE.into()])
125
1
            );
126
1
            assert_eq!(
127
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
128
1
                get_chains_set(vec![3000.into(), 3001.into()])
129
1
            );
130

            
131
1
            run_block();
132
1
            assert_eq!(
133
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
134
1
                get_collators_set(vec![ALICE.into(), DAVE.into()])
135
1
            );
136
1
            assert_eq!(
137
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
138
1
                get_chains_set(vec![3000.into(), 3001.into()])
139
1
            );
140

            
141
1
            run_to_session(1);
142
1
            run_block();
143
1
            assert_eq!(
144
1
                <InactiveCollators<Runtime>>::get(0),
145
1
                get_collators_set(vec![BOB.into(), CHARLIE.into()])
146
1
            );
147
1
            assert_eq!(
148
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
149
1
                get_collators_set(vec![])
150
1
            );
151
1
            assert_eq!(
152
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
153
1
                get_chains_set(vec![])
154
1
            );
155

            
156
1
            run_block();
157
1
            assert_eq!(
158
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
159
1
                get_collators_set(vec![])
160
1
            );
161
1
            assert_eq!(
162
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
163
1
                get_chains_set(vec![])
164
1
            );
165
1
            run_to_session(2);
166
1
            run_block();
167
1

            
168
1
            // Since chains 3000 and 3001 are inactive, all collators should be marked as active
169
1
            assert_eq!(
170
1
                <InactiveCollators<Runtime>>::get(1),
171
1
                get_collators_set(vec![])
172
1
            );
173
1
            assert_eq!(<ActiveCollatorsForCurrentSession<Runtime>>::get().len(), 0);
174
1
            assert_eq!(
175
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
176
1
                get_chains_set(vec![])
177
1
            );
178

            
179
1
            let max_inactive_sessions =
180
1
                <Runtime as pallet_inactivity_tracking::Config>::MaxInactiveSessions::get();
181
1
            run_to_session(max_inactive_sessions - 1);
182
1
            run_block();
183
1
            assert_eq!(
184
1
                InactivityTracking::is_node_inactive(&AccountId::from(ALICE)),
185
1
                false
186
1
            );
187
1
            assert_eq!(
188
1
                InactivityTracking::is_node_inactive(&AccountId::from(BOB)),
189
1
                false
190
1
            );
191
1
            assert_eq!(
192
1
                InactivityTracking::is_node_inactive(&AccountId::from(CHARLIE)),
193
1
                false
194
1
            );
195
1
            assert_eq!(
196
1
                InactivityTracking::is_node_inactive(&AccountId::from(DAVE)),
197
1
                false
198
1
            );
199

            
200
1
            run_to_session(max_inactive_sessions);
201
1
            assert_eq!(
202
1
                InactivityTracking::is_node_inactive(&AccountId::from(ALICE)),
203
1
                false
204
1
            );
205
1
            assert_eq!(
206
1
                InactivityTracking::is_node_inactive(&AccountId::from(BOB)),
207
1
                false
208
1
            );
209
1
            assert_eq!(
210
1
                InactivityTracking::is_node_inactive(&AccountId::from(CHARLIE)),
211
1
                false
212
1
            );
213
1
            assert_eq!(
214
1
                InactivityTracking::is_node_inactive(&AccountId::from(DAVE)),
215
1
                false
216
1
            );
217
1
            assert_eq!(<InactiveCollators<Runtime>>::get(0).is_empty(), false);
218

            
219
1
            run_to_session(max_inactive_sessions + 1);
220
1
            run_block();
221
1

            
222
1
            assert_eq!(<InactiveCollators<Runtime>>::get(0).is_empty(), true);
223
1
        });
224
1
}