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

            
18
use {
19
    crate::{
20
        tests::common::*, InactivityTracking, MinimumSelfDelegation, PooledStaking,
21
        TanssiCollatorAssignment, TanssiInvulnerables,
22
    },
23
    frame_support::assert_ok,
24
    pallet_pooled_staking::{ActivePoolKind, SortedEligibleCandidates},
25
    tp_traits::NodeActivityTrackingHelper,
26
};
27

            
28
2
fn init_test_setup() {
29
    // Ensure that BOB is not an invulnerable collator and is part of the sorted eligible candidates list.
30
2
    assert_ok!(TanssiInvulnerables::remove_invulnerable(
31
2
        root_origin(),
32
2
        BOB.into()
33
    ));
34
2
    assert_ok!(TanssiInvulnerables::remove_invulnerable(
35
2
        root_origin(),
36
2
        CHARLIE.into()
37
    ));
38
2
    let stake = MinimumSelfDelegation::get() * 10;
39
2
    assert_ok!(PooledStaking::request_delegate(
40
2
        origin_of(BOB.into()),
41
2
        BOB.into(),
42
2
        ActivePoolKind::AutoCompounding,
43
2
        stake
44
    ));
45
2
    assert_ok!(PooledStaking::request_delegate(
46
2
        origin_of(CHARLIE.into()),
47
2
        CHARLIE.into(),
48
2
        ActivePoolKind::AutoCompounding,
49
2
        stake
50
    ));
51
    // Verify that BOB is
52
    //- a non-invulnerable collator
53
    //- assigned to a container chain
54
    //- part of the sorted eligible candidates list.
55
2
    assert_eq!(
56
2
        TanssiInvulnerables::invulnerables().contains(&BOB.into()),
57
        false
58
    );
59
2
    assert_eq!(
60
2
        TanssiCollatorAssignment::collator_container_chain()
61
2
            .container_chains
62
2
            .iter()
63
2
            .any(|(_, collators)| collators.contains(&BOB.into())),
64
        true
65
    );
66
2
    assert_eq!(
67
2
        <SortedEligibleCandidates<Runtime>>::get()
68
2
            .iter()
69
2
            .any(|c| c.candidate == BOB.into()),
70
        true
71
    );
72
    // Enable offline marking.
73
2
    assert_ok!(InactivityTracking::enable_offline_marking(
74
2
        root_origin(),
75
        true
76
    ));
77
2
}
78

            
79
#[test]
80
1
fn set_collator_offline_using_set_offline_removes_it_from_assigned_collators_and_sorted_eligible_candidates(
81
1
) {
82
1
    ExtBuilder::default()
83
1
        .with_config(pallet_configuration::HostConfiguration {
84
1
            max_collators: 100,
85
1
            min_orchestrator_collators: 0,
86
1
            max_orchestrator_collators: 0,
87
1
            collators_per_container: 2,
88
1
            ..Default::default()
89
1
        })
90
1
        .with_balances(vec![
91
1
            // BOB gets 10k extra tokens for his mapping deposit
92
1
            (AccountId::from(ALICE), 100_000 * UNIT),
93
1
            (AccountId::from(BOB), 210_000 * UNIT),
94
1
            (AccountId::from(CHARLIE), 210_000 * UNIT),
95
1
            (AccountId::from(DAVE), 100_000 * UNIT),
96
1
        ])
97
1
        .with_collators(vec![
98
1
            (AccountId::from(ALICE), 100 * UNIT),
99
1
            (AccountId::from(BOB), 210 * UNIT),
100
1
            (AccountId::from(CHARLIE), 210 * UNIT),
101
1
            (AccountId::from(DAVE), 100 * UNIT),
102
1
        ])
103
1
        .with_empty_parachains(vec![3001u32, 3002u32])
104
1
        .build()
105
1
        .execute_with(|| {
106
1
            init_test_setup();
107
1
            let pending_assignment_before_offline_marking =
108
1
                TanssiCollatorAssignment::pending_collator_container_chain();
109
1
            assert_eq!(pending_assignment_before_offline_marking.is_some(), true);
110
1
            assert_eq!(
111
1
                pending_assignment_before_offline_marking
112
1
                    .unwrap()
113
1
                    .container_chains
114
1
                    .iter()
115
1
                    .any(|(_, collators)| collators.contains(&BOB.into())),
116
                true
117
            );
118
1
            assert_eq!(InactivityTracking::is_node_offline(&BOB.into()), false);
119
1
            assert_ok!(InactivityTracking::set_offline(origin_of(BOB.into())));
120
1
            assert_eq!(InactivityTracking::is_node_offline(&BOB.into()), true);
121
            // Since, BOB is in the current pending assignment, we need to wait uintil session 2
122
            // before it can be removed from the container chain assignment.
123
1
            run_to_session(2);
124
1
            run_block();
125
            // Verify that after being set offline, BOB is no longer:
126
            // - assigned to any container chain
127
            // - in the sorted eligible candidates list
128
1
            assert_eq!(
129
1
                TanssiCollatorAssignment::collator_container_chain()
130
1
                    .container_chains
131
1
                    .iter()
132
2
                    .any(|(_, collators)| collators.contains(&BOB.into())),
133
                false
134
            );
135
1
            assert_eq!(
136
1
                <SortedEligibleCandidates<Runtime>>::get()
137
1
                    .iter()
138
1
                    .any(|c| c.candidate == BOB.into()),
139
                false
140
            );
141
1
        });
142
1
}
143

            
144
#[test]
145
1
fn set_collator_online_adds_it_to_assigned_collators_and_sorted_eligible_candidates() {
146
1
    ExtBuilder::default()
147
1
        .with_config(pallet_configuration::HostConfiguration {
148
1
            max_collators: 100,
149
1
            min_orchestrator_collators: 0,
150
1
            max_orchestrator_collators: 0,
151
1
            collators_per_container: 2,
152
1
            ..Default::default()
153
1
        })
154
1
        .with_balances(vec![
155
1
            // BOB gets 10k extra tokens for his mapping deposit
156
1
            (AccountId::from(ALICE), 100_000 * UNIT),
157
1
            (AccountId::from(BOB), 210_000 * UNIT),
158
1
            (AccountId::from(CHARLIE), 210_000 * UNIT),
159
1
            (AccountId::from(DAVE), 100_000 * UNIT),
160
1
        ])
161
1
        .with_collators(vec![
162
1
            (AccountId::from(ALICE), 100 * UNIT),
163
1
            (AccountId::from(BOB), 210 * UNIT),
164
1
            (AccountId::from(CHARLIE), 210 * UNIT),
165
1
            (AccountId::from(DAVE), 100 * UNIT),
166
1
        ])
167
1
        .with_empty_parachains(vec![3001u32, 3002u32])
168
1
        .build()
169
1
        .execute_with(|| {
170
1
            init_test_setup();
171
1
            assert_eq!(InactivityTracking::is_node_offline(&BOB.into()), false);
172
1
            assert_ok!(InactivityTracking::set_offline(origin_of(BOB.into())));
173
            // Since, BOB is in the current pending assignment, we need to wait uintil session 2
174
            // before it can be removed from the container chain assignment.
175
1
            run_to_session(2);
176
1
            run_block();
177
1
            assert_eq!(InactivityTracking::is_node_offline(&BOB.into()), true);
178
1
            assert_ok!(InactivityTracking::set_online(origin_of(BOB.into())));
179
1
            assert_eq!(InactivityTracking::is_node_offline(&BOB.into()), false);
180
            // Since BOB is set online but not included in the current pending assignment,
181
            // we need to wait at least 2 session before it can be assigned to a container chain.
182
1
            let initial_pending_assignment_after_online_marking =
183
1
                TanssiCollatorAssignment::pending_collator_container_chain();
184
1
            assert_eq!(
185
1
                initial_pending_assignment_after_online_marking.is_some(),
186
                false
187
            );
188
1
            run_to_session(3);
189
1
            run_block();
190
1
            let pending_assignment_after_online_marking =
191
1
                TanssiCollatorAssignment::pending_collator_container_chain();
192
1
            assert_eq!(pending_assignment_after_online_marking.is_some(), true);
193
1
            assert_eq!(
194
1
                pending_assignment_after_online_marking
195
1
                    .unwrap()
196
1
                    .container_chains
197
1
                    .iter()
198
2
                    .any(|(_, collators)| collators.contains(&BOB.into())),
199
                true
200
            );
201
1
            run_to_session(4);
202
1
            run_block();
203
            // Verify that after being set online, BOB is:
204
            // - assigned to any container chain
205
            // - in the sorted eligible candidates list
206
1
            assert_eq!(
207
1
                TanssiCollatorAssignment::collator_container_chain()
208
1
                    .container_chains
209
1
                    .iter()
210
2
                    .any(|(_, collators)| collators.contains(&BOB.into())),
211
                true
212
            );
213
1
            assert_eq!(
214
1
                <SortedEligibleCandidates<Runtime>>::get()
215
1
                    .iter()
216
1
                    .any(|c| c.candidate == BOB.into()),
217
                true
218
            );
219
1
        });
220
1
}