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
#![cfg(test)]
18
use {
19
    crate::tests::common::*,
20
    frame_support::{assert_ok, BoundedBTreeSet},
21
    pallet_inactivity_tracking::pallet::{
22
        ActiveCollatorsForCurrentSession, ActiveContainerChainsForCurrentSession, InactiveCollators,
23
    },
24
    parity_scale_codec::Encode,
25
    sp_consensus_aura::AURA_ENGINE_ID,
26
    sp_runtime::{traits::BlakeTwo256, DigestItem},
27
    sp_std::collections::btree_set::BTreeSet,
28
    test_relay_sproof_builder::{HeaderAs, ParaHeaderSproofBuilder, ParaHeaderSproofBuilderItem},
29
    tp_traits::{
30
        ForSession, GetContainerChainsWithCollators, MaybeSelfChainBlockAuthor,
31
        NodeActivityTrackingHelper, ParaId, ParathreadHelper, ParathreadParams, SlotFrequency,
32
    },
33
};
34

            
35
2
fn note_blocks_for_container_chain(para_id: ParaId, start_block: u32, end_block: u32, slot: u64) {
36
2
    // Simulate the inclusion of a block for a container chain
37
2
    let mut sproof = ParaHeaderSproofBuilder::default();
38

            
39
13
    for block_number in start_block..=end_block {
40
13
        let s = ParaHeaderSproofBuilderItem {
41
13
            para_id,
42
13
            author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::<u32, BlakeTwo256> {
43
13
                parent_hash: Default::default(),
44
13
                number: block_number,
45
13
                state_root: Default::default(),
46
13
                extrinsics_root: Default::default(),
47
13
                digest: sp_runtime::generic::Digest {
48
13
                    logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())],
49
13
                },
50
13
            }),
51
13
        };
52
13
        sproof.items.push(s);
53
13
    }
54
2
    set_author_noting_inherent_data(sproof.clone())
55
2
}
56

            
57
1
fn note_blocks_for_2_container_chain(
58
1
    para_id_1: ParaId,
59
1
    para_id_2: ParaId,
60
1
    start_block: u32,
61
1
    end_block: u32,
62
1
) {
63
1
    // Simulate the inclusion of a block for a container chain
64
1
    let mut sproof = ParaHeaderSproofBuilder::default();
65

            
66
2
    for block_number in start_block..=end_block {
67
2
        let s1 = ParaHeaderSproofBuilderItem {
68
2
            para_id: para_id_1,
69
2
            author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::<u32, BlakeTwo256> {
70
2
                parent_hash: Default::default(),
71
2
                number: block_number,
72
2
                state_root: Default::default(),
73
2
                extrinsics_root: Default::default(),
74
2
                digest: sp_runtime::generic::Digest {
75
2
                    logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, 2u64.encode())],
76
2
                },
77
2
            }),
78
2
        };
79
2
        let s2 = ParaHeaderSproofBuilderItem {
80
2
            para_id: para_id_2,
81
2
            author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::<u32, BlakeTwo256> {
82
2
                parent_hash: Default::default(),
83
2
                number: block_number,
84
2
                state_root: Default::default(),
85
2
                extrinsics_root: Default::default(),
86
2
                digest: sp_runtime::generic::Digest {
87
2
                    logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, 3u64.encode())],
88
2
                },
89
2
            }),
90
2
        };
91
2
        sproof.items.push(s1);
92
2
        sproof.items.push(s2);
93
2
    }
94
1
    set_author_noting_inherent_data(sproof.clone())
95
1
}
96

            
97
38
fn get_collators_set(
98
38
    collators: Vec<cumulus_primitives_core::relay_chain::AccountId>,
99
38
) -> BoundedBTreeSet<
100
38
    cumulus_primitives_core::relay_chain::AccountId,
101
38
    <Runtime as pallet_inactivity_tracking::Config>::MaxCollatorsPerSession,
102
38
> {
103
38
    let mut collator_set: BoundedBTreeSet<
104
38
        cumulus_primitives_core::relay_chain::AccountId,
105
38
        <Runtime as pallet_inactivity_tracking::Config>::MaxCollatorsPerSession,
106
38
    > = BoundedBTreeSet::new();
107
71
    collators.iter().for_each(|collator| {
108
71
        collator_set.try_insert(collator.clone()).ok();
109
71
    });
110
38
    collator_set
111
38
}
112

            
113
28
fn get_chains_set(
114
28
    chains: Vec<ParaId>,
115
28
) -> BoundedBTreeSet<ParaId, <Runtime as pallet_inactivity_tracking::Config>::MaxContainerChains> {
116
28
    let mut chains_set: BoundedBTreeSet<
117
28
        ParaId,
118
28
        <Runtime as pallet_inactivity_tracking::Config>::MaxContainerChains,
119
28
    > = BoundedBTreeSet::new();
120
56
    for para_id in chains {
121
28
        chains_set.try_insert(para_id).unwrap();
122
28
    }
123
28
    chains_set
124
28
}
125

            
126
#[test]
127
1
fn inactivity_tracking_correctly_updates_storages_on_orchestrator_chains_author_noting() {
128
1
    ExtBuilder::default()
129
1
        .with_empty_parachains(vec![3000])
130
1
        .with_collators(vec![
131
1
            (AccountId::from(ALICE), 100_000),
132
1
            (AccountId::from(BOB), 100_000),
133
1
            (AccountId::from(CHARLIE), 100_000),
134
1
            (AccountId::from(DAVE), 100_000),
135
1
        ])
136
1
        .build()
137
1
        .execute_with(|| {
138
1
            assert_eq!(<Runtime as pallet_inactivity_tracking::Config>::GetSelfChainBlockAuthor::get_block_author(), Some(ALICE.into()));
139
1
            assert_eq!(
140
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
141
1
                get_collators_set(vec![ALICE.into()])
142
1
            );
143
1
            run_block();
144
1
            assert_eq!(<Runtime as pallet_inactivity_tracking::Config>::GetSelfChainBlockAuthor::get_block_author(), Some(BOB.into()));
145
1
            assert_eq!(
146
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
147
1
                get_collators_set(vec![ALICE.into(), BOB.into()])
148
1
            );
149
1
            assert_eq!(
150
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
151
1
                get_chains_set(vec![])
152
1
            );
153
1
            run_block();
154
1
            assert_eq!(
155
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
156
1
                get_collators_set(vec![ALICE.into(), BOB.into()])
157
1
            );
158
1
            assert_eq!(
159
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
160
1
                get_chains_set(vec![])
161
1
            );
162

            
163
1
            run_to_session(1);
164
1
            run_block();
165
1

            
166
1
            // Since chain 3000 is inactive, all collators should be marked as active
167
1
            assert_eq!(
168
1
                <InactiveCollators<Runtime>>::get(0),
169
1
                get_collators_set(vec![])
170
1
            );
171
1
            assert_eq!(
172
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
173
1
                get_collators_set(vec![ALICE.into(), BOB.into()])
174
1
            );
175
1
            assert_eq!(
176
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
177
1
                get_chains_set(vec![])
178
1
            );
179
1
        });
180
1
}
181

            
182
#[test]
183
1
fn inactivity_tracking_correctly_updates_storages_on_container_chains_author_noting() {
184
1
    ExtBuilder::default()
185
1
        .with_empty_parachains(vec![3000])
186
1
        .with_collators(vec![
187
1
            (AccountId::from(ALICE), 100_000),
188
1
            (AccountId::from(BOB), 100_000),
189
1
            (AccountId::from(CHARLIE), 100_000),
190
1
            (AccountId::from(DAVE), 100_000),
191
1
        ])
192
1
        .build()
193
1
        .execute_with(|| {
194
1
            let max_inactive_sessions =
195
1
                <Runtime as pallet_inactivity_tracking::Config>::MaxInactiveSessions::get();
196
1
            assert_ok!(Configuration::set_max_orchestrator_collators(
197
1
                root_origin(),
198
1
                1
199
1
            ));
200
1
            assert_eq!(
201
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
202
1
                get_chains_set(vec![])
203
1
            );
204
1
            note_blocks_for_container_chain(3000.into(), 1, session_to_block(1), 1);
205
1
            run_block();
206
1
            assert_eq!(
207
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
208
1
                get_collators_set(vec![ALICE.into(), BOB.into(), DAVE.into()])
209
1
            );
210
1
            assert_eq!(
211
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
212
1
                get_chains_set(vec![3000.into()])
213
1
            );
214
1
            run_block();
215
1
            assert_eq!(
216
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
217
1
                get_collators_set(vec![ALICE.into(), BOB.into(), DAVE.into()])
218
1
            );
219
1
            assert_eq!(
220
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
221
1
                get_chains_set(vec![3000.into()])
222
1
            );
223

            
224
1
            run_to_session(1);
225
1
            run_block();
226
1
            assert_eq!(
227
1
                <InactiveCollators<Runtime>>::get(0),
228
1
                get_collators_set(vec![CHARLIE.into()])
229
1
            );
230
1
            assert_eq!(
231
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
232
1
                get_collators_set(vec![ALICE.into(), BOB.into()])
233
1
            );
234
1
            assert_eq!(
235
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
236
1
                get_chains_set(vec![])
237
1
            );
238

            
239
1
            run_to_session(2);
240
1
            run_block();
241
1

            
242
1
            assert_eq!(
243
1
                <InactiveCollators<Runtime>>::get(1),
244
1
                get_collators_set(vec![])
245
1
            );
246
1
            assert_eq!(
247
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
248
1
                get_collators_set(vec![ALICE.into()])
249
1
            );
250
1
            assert_eq!(
251
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
252
1
                get_chains_set(vec![])
253
1
            );
254

            
255
1
            run_to_session(max_inactive_sessions - 1);
256
1
            run_block();
257
1
            assert_eq!(
258
1
                InactivityTracking::is_node_inactive(
259
1
                    &cumulus_primitives_core::relay_chain::AccountId::from(ALICE)
260
1
                ),
261
1
                false
262
1
            );
263
1
            assert_eq!(
264
1
                InactivityTracking::is_node_inactive(
265
1
                    &cumulus_primitives_core::relay_chain::AccountId::from(BOB)
266
1
                ),
267
1
                false
268
1
            );
269
1
            assert_eq!(
270
1
                InactivityTracking::is_node_inactive(
271
1
                    &cumulus_primitives_core::relay_chain::AccountId::from(CHARLIE)
272
1
                ),
273
1
                false
274
1
            );
275
1
            assert_eq!(
276
1
                InactivityTracking::is_node_inactive(
277
1
                    &cumulus_primitives_core::relay_chain::AccountId::from(DAVE)
278
1
                ),
279
1
                false
280
1
            );
281
1
            run_to_session(max_inactive_sessions);
282
1
            assert_eq!(
283
1
                InactivityTracking::is_node_inactive(
284
1
                    &cumulus_primitives_core::relay_chain::AccountId::from(ALICE)
285
1
                ),
286
1
                false
287
1
            );
288
1
            assert_eq!(
289
1
                InactivityTracking::is_node_inactive(
290
1
                    &cumulus_primitives_core::relay_chain::AccountId::from(BOB)
291
1
                ),
292
1
                false
293
1
            );
294
1
            assert_eq!(
295
1
                InactivityTracking::is_node_inactive(
296
1
                    &cumulus_primitives_core::relay_chain::AccountId::from(CHARLIE)
297
1
                ),
298
1
                false
299
1
            );
300
1
            assert_eq!(
301
1
                InactivityTracking::is_node_inactive(
302
1
                    &cumulus_primitives_core::relay_chain::AccountId::from(DAVE)
303
1
                ),
304
1
                false
305
1
            );
306
1
            assert_eq!(<InactiveCollators<Runtime>>::get(0).is_empty(), false);
307

            
308
1
            run_to_session(max_inactive_sessions + 1);
309
1
            run_block();
310
1
            assert_eq!(<InactiveCollators<Runtime>>::get(0).is_empty(), true);
311
1
        });
312
1
}
313

            
314
#[test]
315
1
fn inactivity_tracking_correctly_updates_storages_with_all_chains_being_inactive() {
316
1
    ExtBuilder::default()
317
1
        .with_empty_parachains(vec![3000, 3001])
318
1
        .with_collators(vec![
319
1
            (
320
1
                cumulus_primitives_core::relay_chain::AccountId::from(ALICE),
321
1
                100_000,
322
1
            ),
323
1
            (
324
1
                cumulus_primitives_core::relay_chain::AccountId::from(BOB),
325
1
                100_000,
326
1
            ),
327
1
            (
328
1
                cumulus_primitives_core::relay_chain::AccountId::from(CHARLIE),
329
1
                100_000,
330
1
            ),
331
1
            (
332
1
                cumulus_primitives_core::relay_chain::AccountId::from(DAVE),
333
1
                100_000,
334
1
            ),
335
1
        ])
336
1
        .build()
337
1
        .execute_with(|| {
338
1
            run_block();
339
1
            assert_eq!(
340
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
341
1
                get_collators_set(vec![ALICE.into(), BOB.into()])
342
1
            );
343
1
            assert_eq!(
344
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get().len(),
345
1
                0
346
1
            );
347
1
            run_to_session(1);
348
1
            assert_eq!(
349
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
350
1
                get_collators_set(vec![ALICE.into(), BOB.into()])
351
1
            );
352
1
            assert_eq!(
353
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get().len(),
354
1
                0
355
1
            );
356
1
            run_block();
357
1

            
358
1
            // Since chains 3000 and 3001 are inactive, all collators should be marked as active
359
1
            assert_eq!(
360
1
                <InactiveCollators<Runtime>>::get(0),
361
1
                get_collators_set(vec![])
362
1
            );
363
1
            assert_eq!(
364
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
365
1
                get_collators_set(vec![ALICE.into(), BOB.into()])
366
1
            );
367
1
            assert_eq!(
368
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get().len(),
369
1
                0
370
1
            );
371

            
372
1
            run_to_session(2);
373
1
            assert_eq!(
374
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
375
1
                get_collators_set(vec![ALICE.into(), BOB.into()])
376
1
            );
377
1
            assert_eq!(
378
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get().len(),
379
1
                0
380
1
            );
381
1
            run_block();
382
1

            
383
1
            assert_eq!(
384
1
                <InactiveCollators<Runtime>>::get(1),
385
1
                get_collators_set(vec![])
386
1
            );
387
1
            assert_eq!(
388
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
389
1
                get_collators_set(vec![ALICE.into(), BOB.into()])
390
1
            );
391
1
            assert_eq!(
392
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get().len(),
393
1
                0
394
1
            );
395
1
        });
396
1
}
397

            
398
#[test]
399
1
fn inactivity_tracking_edge_case_one_block_per_collator() {
400
1
    // Check that all block authors are marked as active, even on edge cases such as the first or
401
1
    // last block of each session.
402
1

            
403
1
    // Skip test if not compiled with fast-runtime
404
1
    let session_period = crate::Period::get();
405
1
    if session_period > 10 {
406
        println!(
407
            "Skipping test because session period must be 10, is {:?}",
408
            session_period
409
        );
410
        return;
411
1
    }
412
1

            
413
1
    ExtBuilder::default()
414
1
        .with_config(pallet_configuration::HostConfiguration {
415
1
            max_collators: 100,
416
1
            min_orchestrator_collators: 2,
417
1
            max_orchestrator_collators: 100,
418
1
            ..Default::default()
419
1
        })
420
1
        .with_collators(vec![
421
1
            (AccountId::from(ALICE), 100_000),
422
1
            (AccountId::from(BOB), 100_000),
423
1
            (AccountId::from(CHARLIE), 100_000),
424
1
            (AccountId::from(DAVE), 100_000),
425
1
            (AccountId::from([8; 32]), 100_000),
426
1
            (AccountId::from([9; 32]), 100_000),
427
1
            (AccountId::from([10; 32]), 100_000),
428
1
            (AccountId::from([11; 32]), 100_000),
429
1
            (AccountId::from([12; 32]), 100_000),
430
1
            (AccountId::from([13; 32]), 100_000),
431
1
            (AccountId::from([14; 32]), 100_000),
432
1
            (AccountId::from([15; 32]), 100_000),
433
1
            (AccountId::from([16; 32]), 100_000),
434
1
            (AccountId::from([17; 32]), 100_000),
435
1
        ])
436
1
        .build()
437
1
        .execute_with(|| {
438
1
            run_to_session(2);
439
1

            
440
1
            // Confirm that all 14 collators have been assigned to orchestrator chain
441
1
            let collators =
442
1
                pallet_collator_assignment::Pallet::<Runtime>::collator_container_chain();
443
1
            let num_collators = collators.orchestrator_chain.len();
444
1
            assert_eq!(num_collators, 14);
445

            
446
            // Since 1 session = 10 blocks, at most 10 block authors will be marked as active per session.
447
            // In tests we use a simple round robin collator selection, so no collator will produce more
448
            // than one block per sessoin. Thus we can assert that we will see exactly 4 inactive collators
449
            // per session.
450
1
            let inactive_collators = InactiveCollators::<Runtime>::get(1);
451
1
            assert_eq!(
452
1
                inactive_collators.len(),
453
1
                num_collators - session_period as usize,
454
                "{:3}: {} inactive: {:?}",
455
                1,
456
                inactive_collators.len(),
457
                inactive_collators
458
            );
459
1
        });
460
1
}
461

            
462
#[test]
463
1
fn inactivity_tracking_edge_case_inactive_at_session_start() {
464
1
    // Check that an inactive collator can always be marked as inactive, even on edge cases such as the first or
465
1
    // last block of each session.
466
1

            
467
1
    // Skip test if not compiled with fast-runtime
468
1
    let session_period = crate::Period::get();
469
1
    if session_period > 10 {
470
        println!(
471
            "Skipping test because session period must be 10, is {:?}",
472
            session_period
473
        );
474
        return;
475
1
    }
476
1

            
477
1
    ExtBuilder::default()
478
1
        .with_config(pallet_configuration::HostConfiguration {
479
1
            max_collators: 100,
480
1
            min_orchestrator_collators: 2,
481
1
            max_orchestrator_collators: 100,
482
1
            ..Default::default()
483
1
        })
484
1
        .with_collators(vec![
485
1
            (AccountId::from(ALICE), 100_000),
486
1
            (AccountId::from(BOB), 100_000),
487
1
            (AccountId::from(CHARLIE), 100_000),
488
1
            (AccountId::from(DAVE), 100_000),
489
1
        ])
490
1
        .build()
491
1
        .execute_with(|| {
492
1
            let inactivity_period: u32 =
493
1
                <Runtime as pallet_inactivity_tracking::Config>::MaxInactiveSessions::get();
494
1
            run_to_session(2 + inactivity_period);
495

            
496
            // Mock inactive collator by writing directly into storage
497
5
            for s in 2..(2 + inactivity_period) {
498
5
                InactiveCollators::<Runtime>::insert(
499
5
                    s,
500
5
                    BoundedBTreeSet::try_from(BTreeSet::from([AccountId::from(BOB)])).unwrap(),
501
5
                );
502
5
            }
503

            
504
1
            let expected_session_index = Session::current_index();
505
1
            // Since run_block ends a block then starts one the last block of the session
506
1
            // we need to run the loop until session_period - 1
507
1
            for _ in 0..(session_period - 1) {
508
9
                assert_eq!(expected_session_index, Session::current_index());
509
9
                let bob_inactive = pallet_inactivity_tracking::Pallet::<Runtime>::is_node_inactive(
510
9
                    &AccountId::from(BOB),
511
9
                );
512
9
                assert!(bob_inactive);
513
9
                run_block();
514
            }
515

            
516
            // The next block is a new session
517
1
            run_block();
518
1
            let session_index = Session::current_index();
519
1
            assert_eq!(session_index, 8);
520
1
        });
521
1
}
522

            
523
#[test]
524
1
fn inactivity_tracking_edge_case_parachain_and_parathread_with_one_active_collator() {
525
1
    ExtBuilder::default()
526
1
        .with_config(pallet_configuration::HostConfiguration {
527
1
            max_collators: 100,
528
1
            min_orchestrator_collators: 1,
529
1
            max_orchestrator_collators: 1,
530
1
            collators_per_container: 2,
531
1
            collators_per_parathread: 2,
532
1
            ..Default::default()
533
1
        })
534
1
        .with_para_ids(vec![
535
1
            ParaRegistrationParams {
536
1
                para_id: 3001,
537
1
                parathread_params: Some(ParathreadParams {
538
1
                    slot_frequency: SlotFrequency::default(),
539
1
                }),
540
1
                genesis_data: empty_genesis_data(),
541
1
                block_production_credits: u32::MAX,
542
1
                collator_assignment_credits: u32::MAX,
543
1
            },
544
1
            ParaRegistrationParams {
545
1
                para_id: 3000,
546
1
                parathread_params: None,
547
1
                genesis_data: empty_genesis_data(),
548
1
                block_production_credits: u32::MAX,
549
1
                collator_assignment_credits: u32::MAX,
550
1
            },
551
1
        ])
552
1
        .with_collators(vec![
553
1
            (AccountId::from(ALICE), 100_000),
554
1
            (AccountId::from(BOB), 100_000),
555
1
            (AccountId::from(CHARLIE), 100_000),
556
1
            (AccountId::from(DAVE), 100_000),
557
1
            (AccountId::from([8; 32]), 100_000),
558
1
        ])
559
1
        .build()
560
1
        .execute_with(|| {
561
1
            assert_eq!(
562
1
                Session::current_index(),
563
1
                0
564
1
            );
565
1
            assert_eq!(
566
1
                <Runtime as pallet_inactivity_tracking::Config>::ParaFilter::get_parathreads_for_session(),
567
1
                get_chains_set(vec![3001.into()]).into_inner()
568
1
            );
569
1
            assert_eq!(
570
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
571
1
                get_chains_set(vec![])
572
1
            );
573
1
            let container_chains_with_collators =
574
1
                <Runtime as pallet_inactivity_tracking::Config>::CurrentCollatorsFetcher::container_chains_with_collators(
575
1
                    ForSession::Current
576
1
                );
577
1
            let collators_for_parathread= container_chains_with_collators
578
2
                .iter().find(|(para_id, _)| para_id == &3001.into()).unwrap().clone().1;
579
1
            let collators_for_parachain= container_chains_with_collators
580
1
                .iter().find(|(para_id, _)| para_id == &3000.into()).unwrap().clone().1;
581
1

            
582
1
            assert_eq!(collators_for_parathread, vec![DAVE.into(), [8; 32].into()]);
583
1
            assert_eq!(collators_for_parachain, vec![BOB.into(), CHARLIE.into()]);
584
1
            note_blocks_for_2_container_chain(3000.into(), 3001.into(), 1, 2);
585
1
            run_block();
586
            // Verify that only one non-orchestrator collator is marked as active
587
9
            while Session::current_index() == 0 {
588
8
                assert_eq!(
589
8
                    <ActiveCollatorsForCurrentSession<Runtime>>::get(),
590
8
                    get_collators_set(vec![ALICE.into(), BOB.into(), [8; 32].into()])
591
8
                );
592
8
                assert_eq!(
593
8
                    <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
594
8
                    get_chains_set(vec![3000.into(), 3001.into()])
595
8
                );
596
8
                run_block();
597
            }
598
            // Container chain 3001 is an active parathread - so should be handled an inactive chain
599
            // Container chain 3000 is an active parachain.
600
            // So only the inactive collator of the parachain should appear in the inactive collators set
601
1
            assert_eq!(
602
1
                <InactiveCollators<Runtime>>::get(0),
603
1
                get_collators_set(vec![CHARLIE.into()])
604
1
            );
605
1
            assert_eq!(
606
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
607
1
                get_collators_set(vec![ALICE.into()])
608
1
            );
609
1
            assert_eq!(
610
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get().len(),
611
1
                0
612
1
            );
613
1
        });
614
1
}
615

            
616
#[test]
617
1
fn inactivity_tracking_edge_case_active_parachain_with_one_active_collator_and_inactive_parathread()
618
1
{
619
1
    ExtBuilder::default()
620
1
        .with_config(pallet_configuration::HostConfiguration {
621
1
            max_collators: 100,
622
1
            min_orchestrator_collators: 1,
623
1
            max_orchestrator_collators: 1,
624
1
            collators_per_container: 2,
625
1
            collators_per_parathread: 2,
626
1
            ..Default::default()
627
1
        })
628
1
        .with_para_ids(vec![
629
1
            ParaRegistrationParams {
630
1
                para_id: 3001,
631
1
                parathread_params: Some(ParathreadParams {
632
1
                    slot_frequency: SlotFrequency::default(),
633
1
                }),
634
1
                genesis_data: empty_genesis_data(),
635
1
                block_production_credits: u32::MAX,
636
1
                collator_assignment_credits: u32::MAX,
637
1
            },
638
1
            ParaRegistrationParams {
639
1
                para_id: 3000,
640
1
                parathread_params: None,
641
1
                genesis_data: empty_genesis_data(),
642
1
                block_production_credits: u32::MAX,
643
1
                collator_assignment_credits: u32::MAX,
644
1
            },
645
1
        ])
646
1
        .with_collators(vec![
647
1
            (AccountId::from(ALICE), 100_000),
648
1
            (AccountId::from(BOB), 100_000),
649
1
            (AccountId::from(CHARLIE), 100_000),
650
1
            (AccountId::from(DAVE), 100_000),
651
1
            (AccountId::from([8; 32]), 100_000),
652
1
        ])
653
1
        .build()
654
1
        .execute_with(|| {
655
1
            assert_eq!(
656
1
                Session::current_index(),
657
1
                0
658
1
            );
659
1
            assert_eq!(
660
1
                <Runtime as pallet_inactivity_tracking::Config>::ParaFilter::get_parathreads_for_session(),
661
1
                get_chains_set(vec![3001.into()]).into_inner()
662
1
            );
663
1
            assert_eq!(
664
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
665
1
                get_chains_set(vec![])
666
1
            );
667
1
            let container_chains_with_collators =
668
1
                <Runtime as pallet_inactivity_tracking::Config>::CurrentCollatorsFetcher::container_chains_with_collators(
669
1
                    ForSession::Current
670
1
                );
671
1
            let collators_for_parathread= container_chains_with_collators
672
2
                .iter().find(|(para_id, _)| para_id == &3001.into()).unwrap().clone().1;
673
1
            let collators_for_parachain= container_chains_with_collators
674
1
                .iter().find(|(para_id, _)| para_id == &3000.into()).unwrap().clone().1;
675
1
            assert_eq!(collators_for_parathread, vec![DAVE.into(), [8; 32].into()]);
676
1
            assert_eq!(collators_for_parachain, vec![BOB.into(), CHARLIE.into()]);
677
1
            note_blocks_for_container_chain(3000.into(), 1, 2, 1);
678
1
            run_block();
679
            // Verify that only one non-orchestrator collator is marked as active
680
9
            while Session::current_index() == 0 {
681
8
                assert_eq!(
682
8
                    <ActiveCollatorsForCurrentSession<Runtime>>::get(),
683
8
                    get_collators_set(vec![ALICE.into(), CHARLIE.into()])
684
8
                );
685
8
                assert_eq!(
686
8
                    <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
687
8
                    get_chains_set(vec![3000.into()])
688
8
                );
689
8
                run_block();
690
            }
691
            // Container chain 3001 is an inactive parathread
692
            // Container chain 3000 is an active parachain.
693
            // So only the inactive collator of the parachain should appear in the inactive collators set
694
1
            assert_eq!(
695
1
                <InactiveCollators<Runtime>>::get(0),
696
1
                get_collators_set(vec![BOB.into()])
697
1
            );
698
1
            assert_eq!(
699
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
700
1
                get_collators_set(vec![ALICE.into()])
701
1
            );
702
1
            assert_eq!(
703
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get().len(),
704
1
                0
705
1
            );
706
1
        });
707
1
}