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 crate::OfflineStatus;
18
use {super::*, crate::EnableMarkingOffline};
19

            
20
7
fn make_collator_inactive(collator: AccountId) {
21
7
    run_to_block(u64::from(get_max_inactive_sessions()) * 5u64);
22
14
    for session_index in 0..get_max_inactive_sessions() {
23
14
        InactiveCollators::<Test>::insert(session_index, get_collator_set(vec![collator]));
24
14
    }
25
7
}
26
#[test]
27
1
fn enabling_and_disabling_offline_marking_works() {
28
1
    ExtBuilder.build().execute_with(|| {
29
1
        assert!(!EnableMarkingOffline::<Test>::get());
30
1
        assert_ok!(Pallet::<Test>::enable_offline_marking(
31
1
            RuntimeOrigin::root(),
32
            true
33
        ));
34
1
        assert!(EnableMarkingOffline::<Test>::get());
35
1
        assert_ok!(Pallet::<Test>::enable_offline_marking(
36
1
            RuntimeOrigin::root(),
37
            false
38
        ));
39
1
        assert!(!EnableMarkingOffline::<Test>::get());
40
1
    });
41
1
}
42

            
43
#[test]
44
1
fn enabling_and_disabling_offline_marking_fails_for_non_root() {
45
1
    ExtBuilder.build().execute_with(|| {
46
1
        assert_noop!(
47
1
            Pallet::<Test>::enable_offline_marking(RuntimeOrigin::signed(COLLATOR_1), true),
48
1
            BadOrigin
49
        );
50
1
    });
51
1
}
52
#[test]
53
1
fn set_offline_works() {
54
1
    ExtBuilder.build().execute_with(|| {
55
1
        assert!(OfflineCollators::<Test>::get(COLLATOR_1).is_none());
56
1
        assert_ok!(Pallet::<Test>::enable_offline_marking(
57
1
            RuntimeOrigin::root(),
58
            true
59
        ));
60
1
        assert_ok!(Pallet::<Test>::set_offline(RuntimeOrigin::signed(
61
            COLLATOR_1
62
        )));
63
1
        System::assert_last_event(
64
1
            Event::CollatorStatusUpdated {
65
1
                collator: COLLATOR_1,
66
1
                is_offline: true,
67
1
            }
68
1
            .into(),
69
        );
70
1
        assert!(OfflineCollators::<Test>::get(COLLATOR_1).is_some());
71
1
        assert_eq!(
72
1
            OfflineCollators::<Test>::get(COLLATOR_1),
73
            Some(OfflineStatus::Disabled)
74
        );
75
1
    });
76
1
}
77
#[test]
78
1
fn set_offline_fails_if_offline_marking_is_not_enabled() {
79
1
    ExtBuilder.build().execute_with(|| {
80
1
        assert_noop!(
81
1
            Pallet::<Test>::set_offline(RuntimeOrigin::signed(COLLATOR_1)),
82
1
            Error::<Test>::MarkingOfflineNotEnabled
83
        );
84
1
    });
85
1
}
86
#[test]
87
1
fn set_offline_fails_if_collator_is_not_in_eligible_candidates() {
88
1
    ExtBuilder.build().execute_with(|| {
89
1
        assert_ok!(Pallet::<Test>::enable_offline_marking(
90
1
            RuntimeOrigin::root(),
91
            true
92
        ));
93
1
        assert_noop!(
94
1
            Pallet::<Test>::set_offline(RuntimeOrigin::signed(COLLATOR_3)),
95
1
            Error::<Test>::CollatorNotEligibleCandidate
96
        );
97
1
    });
98
1
}
99

            
100
#[test]
101
1
fn set_offline_fails_for_offline_collators() {
102
1
    ExtBuilder.build().execute_with(|| {
103
1
        assert_ok!(Pallet::<Test>::enable_offline_marking(
104
1
            RuntimeOrigin::root(),
105
            true
106
        ));
107
1
        OfflineCollators::<Test>::insert(COLLATOR_1, OfflineStatus::Disabled);
108
1
        assert!(OfflineCollators::<Test>::get(COLLATOR_1).is_some());
109
1
        assert_noop!(
110
1
            Pallet::<Test>::set_offline(RuntimeOrigin::signed(COLLATOR_1)),
111
1
            Error::<Test>::CollatorNotOnline
112
        );
113
1
        OfflineCollators::<Test>::insert(
114
            COLLATOR_1,
115
1
            OfflineStatus::Notified { cooldown_end: 0u32 },
116
        );
117
1
        assert!(OfflineCollators::<Test>::get(COLLATOR_1).is_some());
118
1
        assert_noop!(
119
1
            Pallet::<Test>::set_offline(RuntimeOrigin::signed(COLLATOR_1)),
120
1
            Error::<Test>::CollatorNotOnline
121
        );
122
1
    });
123
1
}
124

            
125
#[test]
126
1
fn set_offline_fails_if_collator_is_invulnerable() {
127
1
    ExtBuilder.build().execute_with(|| {
128
1
        assert_ok!(Pallet::<Test>::enable_offline_marking(
129
1
            RuntimeOrigin::root(),
130
            true
131
        ));
132
1
        assert_noop!(
133
1
            Pallet::<Test>::set_offline(RuntimeOrigin::signed(COLLATOR_2)),
134
1
            Error::<Test>::MarkingInvulnerableOfflineInvalid
135
        );
136
1
    });
137
1
}
138

            
139
#[test]
140
1
fn set_online_works_for_notified_offline_collator() {
141
1
    ExtBuilder.build().execute_with(|| {
142
1
        OfflineCollators::<Test>::insert(
143
            COLLATOR_1,
144
1
            OfflineStatus::Notified { cooldown_end: 0u32 },
145
        );
146
        // We need to advance to at least session 1 so that the cooldown period has passed
147
1
        run_to_block(5u64);
148
1
        assert!(OfflineCollators::<Test>::get(COLLATOR_1).is_some());
149

            
150
1
        assert_ok!(Pallet::<Test>::set_online(RuntimeOrigin::signed(
151
            COLLATOR_1
152
        )));
153
1
        System::assert_last_event(
154
1
            Event::CollatorStatusUpdated {
155
1
                collator: COLLATOR_1,
156
1
                is_offline: false,
157
1
            }
158
1
            .into(),
159
        );
160
1
        assert!(OfflineCollators::<Test>::get(COLLATOR_1).is_none());
161
1
    });
162
1
}
163

            
164
#[test]
165
1
fn set_online_works_for_disabled_offline_collator() {
166
1
    ExtBuilder.build().execute_with(|| {
167
1
        OfflineCollators::<Test>::insert(COLLATOR_1, OfflineStatus::Disabled);
168
1
        assert!(OfflineCollators::<Test>::get(COLLATOR_1).is_some());
169
1
        assert_ok!(Pallet::<Test>::set_online(RuntimeOrigin::signed(
170
            COLLATOR_1
171
        )));
172
1
        System::assert_last_event(
173
1
            Event::CollatorStatusUpdated {
174
1
                collator: COLLATOR_1,
175
1
                is_offline: false,
176
1
            }
177
1
            .into(),
178
        );
179
1
        assert!(OfflineCollators::<Test>::get(COLLATOR_1).is_none());
180
1
    });
181
1
}
182

            
183
#[test]
184
1
fn set_online_fails_for_online_collators() {
185
1
    ExtBuilder.build().execute_with(|| {
186
1
        assert!(OfflineCollators::<Test>::get(COLLATOR_1).is_none());
187
1
        assert_noop!(
188
1
            Pallet::<Test>::set_online(RuntimeOrigin::signed(COLLATOR_1)),
189
1
            Error::<Test>::CollatorNotOffline
190
        );
191
1
    });
192
1
}
193

            
194
#[test]
195
1
fn set_online_fails_for_offline_collator_within_cooldown_period() {
196
1
    ExtBuilder.build().execute_with(|| {
197
1
        OfflineCollators::<Test>::insert(
198
            COLLATOR_1,
199
1
            OfflineStatus::Notified { cooldown_end: 1u32 },
200
        );
201
1
        assert!(OfflineCollators::<Test>::get(COLLATOR_1).is_some());
202
1
        assert_noop!(
203
1
            Pallet::<Test>::set_online(RuntimeOrigin::signed(COLLATOR_1)),
204
1
            Error::<Test>::CollatorNotReadyToBeOnline
205
        );
206
1
    });
207
1
}
208

            
209
#[test]
210
1
fn notify_inactive_collator_works() {
211
1
    ExtBuilder.build().execute_with(|| {
212
1
        make_collator_inactive(COLLATOR_1);
213
1
        assert_ok!(Pallet::<Test>::enable_offline_marking(
214
1
            RuntimeOrigin::root(),
215
            true
216
        ));
217
1
        assert!(OfflineCollators::<Test>::get(COLLATOR_1).is_none());
218
1
        assert_eq!(CurrentSessionIndexGetter::session_index(), 2);
219
1
        assert_ok!(Pallet::<Test>::notify_inactive_collator(
220
1
            RuntimeOrigin::signed(COLLATOR_3),
221
            COLLATOR_1
222
        ));
223
1
        System::assert_last_event(
224
1
            Event::CollatorStatusUpdated {
225
1
                collator: COLLATOR_1,
226
1
                is_offline: true,
227
1
            }
228
1
            .into(),
229
        );
230
1
        assert!(OfflineCollators::<Test>::get(COLLATOR_1).is_some());
231
        // Since we are currently in session 2 and the cooldown period is 1 session
232
        // the collator should be marked as offline util session 3
233
1
        assert_eq!(
234
1
            OfflineCollators::<Test>::get(COLLATOR_1),
235
            Some(OfflineStatus::Notified { cooldown_end: 3u32 })
236
        );
237
1
    });
238
1
}
239

            
240
#[test]
241
1
fn notify_inactive_collator_fails_if_collator_is_active() {
242
1
    ExtBuilder.build().execute_with(|| {
243
1
        assert_ok!(Pallet::<Test>::enable_offline_marking(
244
1
            RuntimeOrigin::root(),
245
            true
246
        ));
247
1
        assert_noop!(
248
1
            Pallet::<Test>::notify_inactive_collator(RuntimeOrigin::signed(COLLATOR_3), COLLATOR_1),
249
1
            Error::<Test>::CollatorCannotBeNotifiedAsInactive
250
        );
251
1
    });
252
1
}
253

            
254
#[test]
255
1
fn notify_inactive_collator_fails_if_offline_marking_is_not_enabled() {
256
1
    ExtBuilder.build().execute_with(|| {
257
1
        make_collator_inactive(COLLATOR_1);
258
1
        assert_noop!(
259
1
            Pallet::<Test>::notify_inactive_collator(RuntimeOrigin::signed(COLLATOR_3), COLLATOR_1),
260
1
            Error::<Test>::MarkingOfflineNotEnabled
261
        );
262
1
    });
263
1
}
264

            
265
#[test]
266
1
fn notify_inactive_collator_fails_for_collator_not_in_sorted_eligible_collators() {
267
1
    ExtBuilder.build().execute_with(|| {
268
1
        make_collator_inactive(COLLATOR_3);
269
1
        assert_ok!(Pallet::<Test>::enable_offline_marking(
270
1
            RuntimeOrigin::root(),
271
            true
272
        ));
273
1
        assert_noop!(
274
1
            Pallet::<Test>::notify_inactive_collator(RuntimeOrigin::signed(COLLATOR_2), COLLATOR_3),
275
1
            Error::<Test>::CollatorNotEligibleCandidate
276
        );
277
1
    });
278
1
}
279

            
280
#[test]
281
1
fn notify_inactive_collator_fails_for_notified_offline_collators() {
282
1
    ExtBuilder.build().execute_with(|| {
283
1
        make_collator_inactive(COLLATOR_1);
284
1
        assert_ok!(Pallet::<Test>::enable_offline_marking(
285
1
            RuntimeOrigin::root(),
286
            true
287
        ));
288
1
        OfflineCollators::<Test>::insert(
289
            COLLATOR_1,
290
1
            OfflineStatus::Notified { cooldown_end: 1u32 },
291
        );
292
1
        assert!(OfflineCollators::<Test>::get(COLLATOR_1).is_some());
293
1
        assert_noop!(
294
1
            Pallet::<Test>::notify_inactive_collator(RuntimeOrigin::signed(COLLATOR_3), COLLATOR_1),
295
1
            Error::<Test>::CollatorAlreadyNotifiedOffline
296
        );
297
1
    });
298
1
}
299

            
300
#[test]
301
1
fn notify_inactive_collator_fails_if_collator_is_invulnerable() {
302
1
    ExtBuilder.build().execute_with(|| {
303
1
        make_collator_inactive(COLLATOR_2);
304
1
        assert_ok!(Pallet::<Test>::enable_offline_marking(
305
1
            RuntimeOrigin::root(),
306
            true
307
        ));
308
1
        assert_noop!(
309
1
            Pallet::<Test>::notify_inactive_collator(RuntimeOrigin::signed(COLLATOR_3), COLLATOR_2),
310
1
            Error::<Test>::MarkingInvulnerableOfflineInvalid
311
        );
312
1
    });
313
1
}
314

            
315
#[test]
316
1
fn calling_set_online_after_set_offline_works() {
317
1
    ExtBuilder.build().execute_with(|| {
318
1
        assert!(OfflineCollators::<Test>::get(COLLATOR_1).is_none());
319
1
        assert_ok!(Pallet::<Test>::enable_offline_marking(
320
1
            RuntimeOrigin::root(),
321
            true
322
        ));
323
1
        assert_ok!(Pallet::<Test>::set_offline(RuntimeOrigin::signed(
324
            COLLATOR_1
325
        )));
326
1
        assert!(OfflineCollators::<Test>::get(COLLATOR_1).is_some());
327
1
        assert_ok!(Pallet::<Test>::set_online(RuntimeOrigin::signed(
328
            COLLATOR_1
329
        )));
330
1
        System::assert_last_event(
331
1
            Event::CollatorStatusUpdated {
332
1
                collator: COLLATOR_1,
333
1
                is_offline: false,
334
1
            }
335
1
            .into(),
336
        );
337
1
        assert!(OfflineCollators::<Test>::get(COLLATOR_1).is_none());
338
1
    });
339
1
}
340
#[test]
341
1
fn calling_set_online_after_notify_inactive_collator_fails() {
342
1
    ExtBuilder.build().execute_with(|| {
343
1
        make_collator_inactive(COLLATOR_1);
344
1
        assert_ok!(Pallet::<Test>::enable_offline_marking(
345
1
            RuntimeOrigin::root(),
346
            true
347
        ));
348
1
        assert!(OfflineCollators::<Test>::get(COLLATOR_1).is_none());
349
1
        assert_ok!(Pallet::<Test>::notify_inactive_collator(
350
1
            RuntimeOrigin::signed(COLLATOR_3),
351
            COLLATOR_1
352
        ));
353
1
        assert!(OfflineCollators::<Test>::get(COLLATOR_1).is_some());
354
1
        assert_noop!(
355
1
            Pallet::<Test>::set_online(RuntimeOrigin::signed(COLLATOR_1)),
356
1
            Error::<Test>::CollatorNotReadyToBeOnline
357
        );
358
1
    });
359
1
}
360

            
361
#[test]
362
1
fn notify_inactive_collator_overrides_disabled_offline_collator_record() {
363
1
    ExtBuilder.build().execute_with(|| {
364
1
        make_collator_inactive(COLLATOR_1);
365
1
        assert_ok!(Pallet::<Test>::enable_offline_marking(
366
1
            RuntimeOrigin::root(),
367
            true
368
        ));
369
1
        assert!(OfflineCollators::<Test>::get(COLLATOR_1).is_none());
370
1
        assert_ok!(Pallet::<Test>::set_offline(RuntimeOrigin::signed(
371
            COLLATOR_1
372
        )));
373
1
        assert!(OfflineCollators::<Test>::get(COLLATOR_1).is_some());
374
1
        assert_eq!(CurrentSessionIndexGetter::session_index(), 2);
375
1
        assert_ok!(Pallet::<Test>::notify_inactive_collator(
376
1
            RuntimeOrigin::signed(COLLATOR_3),
377
            COLLATOR_1
378
        ));
379
1
        System::assert_last_event(
380
1
            Event::CollatorStatusUpdated {
381
1
                collator: COLLATOR_1,
382
1
                is_offline: true,
383
1
            }
384
1
            .into(),
385
        );
386
1
        assert!(OfflineCollators::<Test>::get(COLLATOR_1).is_some());
387
        // Since we are currently in session 2 and the cooldown period is 1 session
388
        // the collator should be marked as offline util session 3
389
1
        assert_eq!(
390
1
            OfflineCollators::<Test>::get(COLLATOR_1),
391
            Some(OfflineStatus::Notified { cooldown_end: 3u32 })
392
        );
393
1
    });
394
1
}