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
    dancebox_runtime_test_utils::*,
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
    std::collections::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
    // 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
    // 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
            );
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
            );
149
1
            assert_eq!(
150
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
151
1
                get_chains_set(vec![])
152
            );
153
1
            run_block();
154
1
            assert_eq!(
155
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
156
1
                get_collators_set(vec![ALICE.into(), BOB.into()])
157
            );
158
1
            assert_eq!(
159
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
160
1
                get_chains_set(vec![])
161
            );
162

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

            
166
            // 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
            );
171
1
            assert_eq!(
172
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
173
1
                get_collators_set(vec![ALICE.into(), BOB.into()])
174
            );
175
1
            assert_eq!(
176
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
177
1
                get_chains_set(vec![])
178
            );
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
199
            ));
200
1
            assert_eq!(
201
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
202
1
                get_chains_set(vec![])
203
            );
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
            );
210
1
            assert_eq!(
211
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
212
1
                get_chains_set(vec![3000.into()])
213
            );
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
            );
219
1
            assert_eq!(
220
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
221
1
                get_chains_set(vec![3000.into()])
222
            );
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
            );
230
1
            assert_eq!(
231
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
232
1
                get_collators_set(vec![ALICE.into(), BOB.into()])
233
            );
234
1
            assert_eq!(
235
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
236
1
                get_chains_set(vec![])
237
            );
238

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

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

            
255
1
            run_to_session(max_inactive_sessions - 1);
256
1
            run_block();
257
1
            assert!(!InactivityTracking::is_node_inactive(
258
1
                &cumulus_primitives_core::relay_chain::AccountId::from(ALICE)
259
1
            ));
260
1
            assert!(!InactivityTracking::is_node_inactive(
261
1
                &cumulus_primitives_core::relay_chain::AccountId::from(BOB)
262
1
            ));
263
1
            assert!(!InactivityTracking::is_node_inactive(
264
1
                &cumulus_primitives_core::relay_chain::AccountId::from(CHARLIE)
265
1
            ));
266
1
            assert!(!InactivityTracking::is_node_inactive(
267
1
                &cumulus_primitives_core::relay_chain::AccountId::from(DAVE)
268
1
            ));
269
1
            run_to_session(max_inactive_sessions);
270
1
            assert!(!InactivityTracking::is_node_inactive(
271
1
                &cumulus_primitives_core::relay_chain::AccountId::from(ALICE)
272
1
            ));
273
1
            assert!(!InactivityTracking::is_node_inactive(
274
1
                &cumulus_primitives_core::relay_chain::AccountId::from(BOB)
275
1
            ));
276
1
            assert!(!InactivityTracking::is_node_inactive(
277
1
                &cumulus_primitives_core::relay_chain::AccountId::from(CHARLIE)
278
1
            ));
279
1
            assert!(!InactivityTracking::is_node_inactive(
280
1
                &cumulus_primitives_core::relay_chain::AccountId::from(DAVE)
281
1
            ));
282
1
            assert!(!<InactiveCollators<Runtime>>::get(0).is_empty());
283

            
284
1
            run_to_session(max_inactive_sessions + 1);
285
1
            run_block();
286
1
            assert!(<InactiveCollators<Runtime>>::get(0).is_empty());
287
1
        });
288
1
}
289

            
290
#[test]
291
1
fn inactivity_tracking_correctly_updates_storages_with_all_chains_being_inactive() {
292
1
    ExtBuilder::default()
293
1
        .with_empty_parachains(vec![3000, 3001])
294
1
        .with_collators(vec![
295
1
            (
296
1
                cumulus_primitives_core::relay_chain::AccountId::from(ALICE),
297
1
                100_000,
298
1
            ),
299
1
            (
300
1
                cumulus_primitives_core::relay_chain::AccountId::from(BOB),
301
1
                100_000,
302
1
            ),
303
1
            (
304
1
                cumulus_primitives_core::relay_chain::AccountId::from(CHARLIE),
305
1
                100_000,
306
1
            ),
307
1
            (
308
1
                cumulus_primitives_core::relay_chain::AccountId::from(DAVE),
309
1
                100_000,
310
1
            ),
311
1
        ])
312
1
        .build()
313
1
        .execute_with(|| {
314
1
            run_block();
315
1
            assert_eq!(
316
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
317
1
                get_collators_set(vec![ALICE.into(), BOB.into()])
318
            );
319
1
            assert_eq!(
320
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get().len(),
321
                0
322
            );
323
1
            run_to_session(1);
324
1
            assert_eq!(
325
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
326
1
                get_collators_set(vec![ALICE.into(), BOB.into()])
327
            );
328
1
            assert_eq!(
329
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get().len(),
330
                0
331
            );
332
1
            run_block();
333

            
334
            // Since chains 3000 and 3001 are inactive, all collators should be marked as active
335
1
            assert_eq!(
336
1
                <InactiveCollators<Runtime>>::get(0),
337
1
                get_collators_set(vec![])
338
            );
339
1
            assert_eq!(
340
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
341
1
                get_collators_set(vec![ALICE.into(), BOB.into()])
342
            );
343
1
            assert_eq!(
344
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get().len(),
345
                0
346
            );
347

            
348
1
            run_to_session(2);
349
1
            assert_eq!(
350
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
351
1
                get_collators_set(vec![ALICE.into(), BOB.into()])
352
            );
353
1
            assert_eq!(
354
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get().len(),
355
                0
356
            );
357
1
            run_block();
358

            
359
1
            assert_eq!(
360
1
                <InactiveCollators<Runtime>>::get(1),
361
1
                get_collators_set(vec![])
362
            );
363
1
            assert_eq!(
364
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
365
1
                get_collators_set(vec![ALICE.into(), BOB.into()])
366
            );
367
1
            assert_eq!(
368
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get().len(),
369
                0
370
            );
371
1
        });
372
1
}
373

            
374
#[test]
375
1
fn inactivity_tracking_edge_case_one_block_per_collator() {
376
    // Check that all block authors are marked as active, even on edge cases such as the first or
377
    // last block of each session.
378

            
379
    // Skip test if not compiled with fast-runtime
380
1
    let session_period = dancebox_runtime::Period::get();
381
1
    if session_period > 10 {
382
        println!(
383
            "Skipping test because session period must be 10, is {:?}",
384
            session_period
385
        );
386
        return;
387
1
    }
388

            
389
1
    ExtBuilder::default()
390
1
        .with_config(pallet_configuration::HostConfiguration {
391
1
            max_collators: 100,
392
1
            min_orchestrator_collators: 2,
393
1
            max_orchestrator_collators: 100,
394
1
            ..Default::default()
395
1
        })
396
1
        .with_collators(vec![
397
1
            (AccountId::from(ALICE), 100_000),
398
1
            (AccountId::from(BOB), 100_000),
399
1
            (AccountId::from(CHARLIE), 100_000),
400
1
            (AccountId::from(DAVE), 100_000),
401
1
            (AccountId::from([8; 32]), 100_000),
402
1
            (AccountId::from([9; 32]), 100_000),
403
1
            (AccountId::from([10; 32]), 100_000),
404
1
            (AccountId::from([11; 32]), 100_000),
405
1
            (AccountId::from([12; 32]), 100_000),
406
1
            (AccountId::from([13; 32]), 100_000),
407
1
            (AccountId::from([14; 32]), 100_000),
408
1
            (AccountId::from([15; 32]), 100_000),
409
1
            (AccountId::from([16; 32]), 100_000),
410
1
            (AccountId::from([17; 32]), 100_000),
411
1
        ])
412
1
        .build()
413
1
        .execute_with(|| {
414
1
            run_to_session(2);
415

            
416
            // Confirm that all 14 collators have been assigned to orchestrator chain
417
1
            let collators =
418
1
                pallet_collator_assignment::Pallet::<Runtime>::collator_container_chain();
419
1
            let num_collators = collators.orchestrator_chain.len();
420
1
            assert_eq!(num_collators, 14);
421

            
422
            // Since 1 session = 10 blocks, at most 10 block authors will be marked as active per session.
423
            // In tests we use a simple round robin collator selection, so no collator will produce more
424
            // than one block per sessoin. Thus we can assert that we will see exactly 4 inactive collators
425
            // per session.
426
1
            let inactive_collators = InactiveCollators::<Runtime>::get(1);
427
1
            assert_eq!(
428
1
                inactive_collators.len(),
429
1
                num_collators - session_period as usize,
430
                "{:3}: {} inactive: {:?}",
431
                1,
432
                inactive_collators.len(),
433
                inactive_collators
434
            );
435
1
        });
436
1
}
437

            
438
#[test]
439
1
fn inactivity_tracking_edge_case_inactive_at_session_start() {
440
    // Check that an inactive collator can always be marked as inactive, even on edge cases such as the first or
441
    // last block of each session.
442

            
443
    // Skip test if not compiled with fast-runtime
444
1
    let session_period = dancebox_runtime::Period::get();
445
1
    if session_period > 10 {
446
        println!(
447
            "Skipping test because session period must be 10, is {:?}",
448
            session_period
449
        );
450
        return;
451
1
    }
452

            
453
1
    ExtBuilder::default()
454
1
        .with_config(pallet_configuration::HostConfiguration {
455
1
            max_collators: 100,
456
1
            min_orchestrator_collators: 2,
457
1
            max_orchestrator_collators: 100,
458
1
            ..Default::default()
459
1
        })
460
1
        .with_collators(vec![
461
1
            (AccountId::from(ALICE), 100_000),
462
1
            (AccountId::from(BOB), 100_000),
463
1
            (AccountId::from(CHARLIE), 100_000),
464
1
            (AccountId::from(DAVE), 100_000),
465
1
        ])
466
1
        .build()
467
1
        .execute_with(|| {
468
1
            let inactivity_period: u32 =
469
1
                <Runtime as pallet_inactivity_tracking::Config>::MaxInactiveSessions::get();
470
1
            run_to_session(2 + inactivity_period);
471

            
472
            // Mock inactive collator by writing directly into storage
473
5
            for s in 2..(2 + inactivity_period) {
474
5
                InactiveCollators::<Runtime>::insert(
475
5
                    s,
476
5
                    BoundedBTreeSet::try_from(BTreeSet::from([AccountId::from(BOB)])).unwrap(),
477
5
                );
478
5
            }
479

            
480
1
            let expected_session_index = Session::current_index();
481
            // Since run_block ends a block then starts one the last block of the session
482
            // we need to run the loop until session_period - 1
483
1
            for _ in 0..(session_period - 1) {
484
9
                assert_eq!(expected_session_index, Session::current_index());
485
9
                let bob_inactive = pallet_inactivity_tracking::Pallet::<Runtime>::is_node_inactive(
486
9
                    &AccountId::from(BOB),
487
                );
488
9
                assert!(bob_inactive);
489
9
                run_block();
490
            }
491

            
492
            // The next block is a new session
493
1
            run_block();
494
1
            let session_index = Session::current_index();
495
1
            assert_eq!(session_index, 8);
496
1
        });
497
1
}
498

            
499
#[test]
500
1
fn inactivity_tracking_edge_case_parachain_and_parathread_with_one_active_collator() {
501
1
    ExtBuilder::default()
502
1
        .with_config(pallet_configuration::HostConfiguration {
503
1
            max_collators: 100,
504
1
            min_orchestrator_collators: 1,
505
1
            max_orchestrator_collators: 1,
506
1
            collators_per_container: 2,
507
1
            collators_per_parathread: 2,
508
1
            ..Default::default()
509
1
        })
510
1
        .with_para_ids(vec![
511
1
            ParaRegistrationParams {
512
1
                para_id: 3001,
513
1
                parathread_params: Some(ParathreadParams {
514
1
                    slot_frequency: SlotFrequency::default(),
515
1
                }),
516
1
                genesis_data: empty_genesis_data(),
517
1
                block_production_credits: u32::MAX,
518
1
                collator_assignment_credits: u32::MAX,
519
1
            },
520
1
            ParaRegistrationParams {
521
1
                para_id: 3000,
522
1
                parathread_params: None,
523
1
                genesis_data: empty_genesis_data(),
524
1
                block_production_credits: u32::MAX,
525
1
                collator_assignment_credits: u32::MAX,
526
1
            },
527
1
        ])
528
1
        .with_collators(vec![
529
1
            (AccountId::from(ALICE), 100_000),
530
1
            (AccountId::from(BOB), 100_000),
531
1
            (AccountId::from(CHARLIE), 100_000),
532
1
            (AccountId::from(DAVE), 100_000),
533
1
            (AccountId::from([8; 32]), 100_000),
534
1
        ])
535
1
        .build()
536
1
        .execute_with(|| {
537
1
            assert_eq!(
538
1
                Session::current_index(),
539
                0
540
            );
541
1
            assert_eq!(
542
1
                <Runtime as pallet_inactivity_tracking::Config>::ParaFilter::get_parathreads_for_session(),
543
1
                get_chains_set(vec![3001.into()]).into_inner()
544
            );
545
1
            assert_eq!(
546
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
547
1
                get_chains_set(vec![])
548
            );
549
1
            let container_chains_with_collators =
550
1
                <Runtime as pallet_inactivity_tracking::Config>::CurrentCollatorsFetcher::container_chains_with_collators(
551
1
                    ForSession::Current
552
                );
553
1
            let collators_for_parathread= container_chains_with_collators
554
2
                .iter().find(|(para_id, _)| para_id == &3001.into()).unwrap().clone().1;
555
1
            let collators_for_parachain= container_chains_with_collators
556
1
                .iter().find(|(para_id, _)| para_id == &3000.into()).unwrap().clone().1;
557

            
558
1
            assert_eq!(collators_for_parathread, vec![DAVE.into(), [8; 32].into()]);
559
1
            assert_eq!(collators_for_parachain, vec![BOB.into(), CHARLIE.into()]);
560
1
            note_blocks_for_2_container_chain(3000.into(), 3001.into(), 1, 2);
561
1
            run_block();
562
            // Verify that only one non-orchestrator collator is marked as active
563
9
            while Session::current_index() == 0 {
564
8
                assert_eq!(
565
8
                    <ActiveCollatorsForCurrentSession<Runtime>>::get(),
566
8
                    get_collators_set(vec![ALICE.into(), BOB.into(), [8; 32].into()])
567
                );
568
8
                assert_eq!(
569
8
                    <ActiveContainerChainsForCurrentSession<Runtime>>::get(),
570
8
                    get_chains_set(vec![3000.into(), 3001.into()])
571
                );
572
8
                run_block();
573
            }
574
            // Container chain 3001 is an active parathread - so should be handled an inactive chain
575
            // Container chain 3000 is an active parachain.
576
            // So only the inactive collator of the parachain should appear in the inactive collators set
577
1
            assert_eq!(
578
1
                <InactiveCollators<Runtime>>::get(0),
579
1
                get_collators_set(vec![CHARLIE.into()])
580
            );
581
1
            assert_eq!(
582
1
                <ActiveCollatorsForCurrentSession<Runtime>>::get(),
583
1
                get_collators_set(vec![ALICE.into()])
584
            );
585
1
            assert_eq!(
586
1
                <ActiveContainerChainsForCurrentSession<Runtime>>::get().len(),
587
                0
588
            );
589
1
        });
590
1
}
591

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