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

            
19
use {
20
    cumulus_primitives_core::ParaId,
21
    dancebox_runtime::{RewardsCollatorCommission, StreamPayment, TransactionPayment},
22
    dancebox_runtime_test_utils::*,
23
    dp_consensus::runtime_decl_for_tanssi_authority_assignment_api::TanssiAuthorityAssignmentApiV1,
24
    dp_core::well_known_keys,
25
    frame_support::{
26
        assert_err, assert_noop, assert_ok, migrations::SteppedMigration,
27
        pallet_prelude::StorageVersion, weights::WeightMeter, BoundedVec,
28
    },
29
    frame_system::ConsumedWeight,
30
    nimbus_primitives::NIMBUS_KEY_ID,
31
    pallet_author_noting_runtime_api::runtime_decl_for_author_noting_api::AuthorNotingApi,
32
    pallet_balances::Instance1,
33
    pallet_migrations::Migration,
34
    pallet_pooled_staking::{
35
        traits::IsCandidateEligible, ActivePoolKind, EligibleCandidate, PendingOperationKey,
36
        PendingOperationQuery, PoolKind, PoolsKey, SharesOrStake,
37
    },
38
    pallet_registrar_runtime_api::{
39
        runtime_decl_for_registrar_api::RegistrarApi, ContainerChainGenesisData,
40
    },
41
    pallet_stream_payment::StreamConfig,
42
    parity_scale_codec::Encode,
43
    rococo_runtime_constants::fee::Perbill,
44
    sp_consensus_aura::AURA_ENGINE_ID,
45
    sp_core::Get,
46
    sp_runtime::{
47
        traits::{BadOrigin, BlakeTwo256, OpaqueKeys},
48
        DigestItem, FixedU128,
49
    },
50
    tanssi_runtime_common::migrations::{
51
        HostConfigurationV3, MigrateConfigurationAddFullRotationMode,
52
        MigrateServicesPaymentAddCollatorAssignmentCredits, RegistrarPendingVerificationValueToMap,
53
    },
54
    test_relay_sproof_builder::{HeaderAs, ParaHeaderSproofBuilder, ParaHeaderSproofBuilderItem},
55
    tp_stream_payment_common::{
56
        AssetId as StreamPaymentAssetId, TimeUnit as StreamPaymentTimeUnit,
57
    },
58
    tp_traits::{ContainerChainBlockInfo, SlotFrequency},
59
    xcm::latest::prelude::*,
60
};
61

            
62
#[test]
63
1
fn genesis_balances() {
64
1
    ExtBuilder::default()
65
1
        .with_balances(vec![
66
1
            // Alice gets 10k extra tokens for her mapping deposit
67
1
            (AccountId::from(ALICE), 210_000 * UNIT),
68
1
            (AccountId::from(BOB), 100_000 * UNIT),
69
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
70
1
            (AccountId::from(DAVE), 100_000 * UNIT),
71
1
        ])
72
1
        .with_collators(vec![
73
1
            // Remove ALICE and BOB from collators
74
1
            (AccountId::from(CHARLIE), 100 * UNIT),
75
1
            (AccountId::from(DAVE), 100 * UNIT),
76
1
        ])
77
1
        .build()
78
1
        .execute_with(|| {
79
1
            assert_eq!(
80
1
                Balances::usable_balance(AccountId::from(ALICE)),
81
                210_000 * UNIT,
82
            );
83
1
            assert_eq!(
84
1
                Balances::usable_balance(AccountId::from(BOB)),
85
                100_000 * UNIT,
86
            );
87
1
        });
88
1
}
89

            
90
#[test]
91
1
fn genesis_para_registrar() {
92
1
    ExtBuilder::default()
93
1
        .with_empty_parachains(vec![1001, 1002])
94
1
        .build()
95
1
        .execute_with(|| {
96
1
            assert_eq!(
97
1
                Registrar::registered_para_ids(),
98
1
                vec![1001.into(), 1002.into()]
99
            );
100
1
        });
101
1
}
102

            
103
#[test]
104
1
fn genesis_para_registrar_deregister() {
105
1
    ExtBuilder::default()
106
1
        .with_empty_parachains(vec![1001, 1002])
107
1
        .build()
108
1
        .execute_with(|| {
109
1
            assert_eq!(
110
1
                Registrar::registered_para_ids(),
111
1
                vec![1001.into(), 1002.into()]
112
            );
113

            
114
1
            run_to_block(2);
115
1
            assert_ok!(Registrar::deregister(root_origin(), 1002.into()), ());
116

            
117
            // Pending
118
1
            assert_eq!(
119
1
                Registrar::pending_registered_para_ids(),
120
1
                vec![(2u32, BoundedVec::try_from(vec![1001u32.into()]).unwrap())]
121
            );
122

            
123
1
            run_to_session(1);
124
1
            assert_eq!(
125
1
                Registrar::pending_registered_para_ids(),
126
1
                vec![(2u32, BoundedVec::try_from(vec![1001u32.into()]).unwrap())]
127
            );
128
1
            assert_eq!(
129
1
                Registrar::registered_para_ids(),
130
1
                vec![1001.into(), 1002.into()]
131
            );
132

            
133
1
            run_to_session(2);
134
1
            assert_eq!(Registrar::pending_registered_para_ids(), vec![]);
135
1
            assert_eq!(Registrar::registered_para_ids(), vec![1001.into()]);
136
1
        });
137
1
}
138

            
139
#[test]
140
1
fn genesis_para_registrar_runtime_api() {
141
1
    ExtBuilder::default()
142
1
        .with_empty_parachains(vec![1001, 1002])
143
1
        .build()
144
1
        .execute_with(|| {
145
1
            assert_eq!(
146
1
                Registrar::registered_para_ids(),
147
1
                vec![1001.into(), 1002.into()]
148
            );
149
1
            assert_eq!(Runtime::registered_paras(), vec![1001.into(), 1002.into()]);
150

            
151
1
            run_to_block(2);
152
1
            assert_ok!(Registrar::deregister(root_origin(), 1002.into()), ());
153
1
            assert_eq!(Runtime::registered_paras(), vec![1001.into(), 1002.into()]);
154

            
155
1
            run_to_session(1);
156
1
            assert_eq!(
157
1
                Registrar::registered_para_ids(),
158
1
                vec![1001.into(), 1002.into()]
159
            );
160
1
            assert_eq!(Runtime::registered_paras(), vec![1001.into(), 1002.into()]);
161

            
162
1
            run_to_session(2);
163
1
            assert_eq!(Registrar::registered_para_ids(), vec![1001.into()]);
164
1
            assert_eq!(Runtime::registered_paras(), vec![1001.into()]);
165
1
        });
166
1
}
167

            
168
#[test]
169
1
fn genesis_para_registrar_container_chain_genesis_data_runtime_api() {
170
1
    let genesis_data_1001 = empty_genesis_data();
171
1
    let genesis_data_1002 = ContainerChainGenesisData {
172
1
        storage: BoundedVec::try_from(vec![(b"key".to_vec(), b"value".to_vec()).into()]).unwrap(),
173
1
        name: Default::default(),
174
1
        id: Default::default(),
175
1
        fork_id: Default::default(),
176
1
        extensions: BoundedVec::try_from(vec![]).unwrap(),
177
1
        properties: Default::default(),
178
1
    };
179
1
    ExtBuilder::default()
180
1
        .with_para_ids(vec![
181
1
            ParaRegistrationParams {
182
1
                para_id: 1001,
183
1
                genesis_data: genesis_data_1001.clone(),
184
1
                block_production_credits: u32::MAX,
185
1
                collator_assignment_credits: u32::MAX,
186
1
                parathread_params: None,
187
1
            },ParaRegistrationParams {
188
1
                para_id: 1002,
189
1
                genesis_data: genesis_data_1002.clone(),
190
1
                block_production_credits: u32::MAX,
191
1
                collator_assignment_credits: u32::MAX,
192
1
                parathread_params: None,
193
1
            },
194
1
        ])
195
1
        .build()
196
1
        .execute_with(|| {
197
1
            assert_eq!(
198
1
                Registrar::registered_para_ids(),
199
1
                vec![1001.into(), 1002.into()]
200
            );
201
1
            assert_eq!(Runtime::registered_paras(), vec![1001.into(), 1002.into()]);
202

            
203
1
            assert_eq!(
204
1
                Runtime::genesis_data(1001.into()).as_ref(),
205
1
                Some(&genesis_data_1001)
206
            );
207
1
            assert_eq!(
208
1
                Runtime::genesis_data(1002.into()).as_ref(),
209
1
                Some(&genesis_data_1002)
210
            );
211
1
            assert_eq!(Runtime::genesis_data(1003.into()).as_ref(), None);
212

            
213
            // This API cannot be used to get the genesis data of the orchestrator chain,
214
            // with id 100
215
            // TODO: where is that 100 defined?
216
1
            assert_eq!(Runtime::genesis_data(100.into()).as_ref(), None);
217

            
218
1
            run_to_block(2);
219
1
            assert_ok!(Registrar::deregister(root_origin(), 1002.into()), ());
220

            
221
1
            assert_eq!(Runtime::genesis_data(1002.into()).as_ref(), Some(&genesis_data_1002), "Deregistered container chain genesis data should not be removed until after 2 sessions");
222

            
223
1
            let genesis_data_1003 = ContainerChainGenesisData {
224
1
                storage: BoundedVec::try_from(vec![(b"key3".to_vec(), b"value3".to_vec()).into()]).unwrap(),
225
1
                name: Default::default(),
226
1
                id: Default::default(),
227
1
                fork_id: Default::default(),
228
1
                extensions: BoundedVec::try_from(vec![]).unwrap(),
229
1
                properties: Default::default(),
230
1
            };
231
1
            assert_ok!(
232
1
                Registrar::register(
233
1
                    origin_of(ALICE.into()),
234
1
                    1003.into(),
235
1
                    genesis_data_1003.clone(),
236
1
                    None
237
                ),
238
                ()
239
            );
240

            
241
            // Registered container chains are inserted immediately
242
1
            assert_eq!(
243
1
                Runtime::genesis_data(1003.into()).as_ref(),
244
1
                Some(&genesis_data_1003)
245
            );
246

            
247
            // Deregistered container chain genesis data is removed after 2 sessions
248
1
            run_to_session(2u32);
249
1
            assert_eq!(Runtime::genesis_data(1002.into()).as_ref(), None);
250
1
        });
251
1
}
252

            
253
#[test]
254
1
fn test_author_collation_aura() {
255
1
    ExtBuilder::default()
256
1
        .with_empty_parachains(vec![1001, 1002])
257
1
        .build()
258
1
        .execute_with(|| {
259
1
            run_to_block(5);
260
            // Assert current slot gets updated
261
1
            assert_eq!(current_slot(), 4u64);
262
            // slot 4, alice
263
1
            assert!(current_author() == AccountId::from(ALICE));
264

            
265
1
            run_to_block(6);
266

            
267
1
            assert_eq!(current_slot(), 5u64);
268
            // slot 5, bob
269
1
            assert!(current_author() == AccountId::from(BOB));
270
1
        });
271
1
}
272

            
273
#[test]
274
1
fn test_author_collation_aura_change_of_authorities_on_session() {
275
1
    ExtBuilder::default()
276
1
        .with_balances(vec![
277
1
            // Alice gets 10k extra tokens for her mapping deposit
278
1
            (AccountId::from(ALICE), 210_000 * UNIT),
279
1
            (AccountId::from(BOB), 100_000 * UNIT),
280
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
281
1
            (AccountId::from(DAVE), 100_000 * UNIT),
282
1
        ])
283
1
        .with_empty_parachains(vec![1001, 1002])
284
1
        .build()
285
1
        .execute_with(|| {
286
1
            run_to_block(2);
287
            // Assert current slot gets updated
288
1
            assert_eq!(current_slot(), 1u64);
289
1
            assert!(current_author() == AccountId::from(BOB));
290

            
291
            // We change invulnerables
292
            // We first need to set the keys
293
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
294
1
            let bob_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
295

            
296
1
            let charlie_id = get_aura_id_from_seed(&AccountId::from(CHARLIE).to_string());
297
1
            let dave_id = get_aura_id_from_seed(&AccountId::from(DAVE).to_string());
298

            
299
            // Set CHARLIE and DAVE keys
300
1
            assert_ok!(Session::set_keys(
301
1
                origin_of(CHARLIE.into()),
302
1
                dancebox_runtime::SessionKeys {
303
1
                    nimbus: charlie_id.clone(),
304
1
                },
305
1
                vec![]
306
            ));
307
1
            assert_ok!(Session::set_keys(
308
1
                origin_of(DAVE.into()),
309
1
                dancebox_runtime::SessionKeys {
310
1
                    nimbus: dave_id.clone(),
311
1
                },
312
1
                vec![]
313
            ));
314

            
315
            // Change invulnerables
316
1
            assert_ok!(Invulnerables::remove_invulnerable(
317
1
                root_origin(),
318
1
                ALICE.into()
319
            ));
320
1
            assert_ok!(Invulnerables::remove_invulnerable(
321
1
                root_origin(),
322
1
                BOB.into()
323
            ));
324
1
            assert_ok!(Invulnerables::add_invulnerable(
325
1
                root_origin(),
326
1
                CHARLIE.into()
327
            ));
328
1
            assert_ok!(Invulnerables::add_invulnerable(root_origin(), DAVE.into()));
329

            
330
            // SESSION CHANGE. First session. it takes 2 sessions to see the change
331
1
            run_to_session(1u32);
332
1
            let author = get_orchestrator_current_author().unwrap();
333

            
334
1
            assert_eq!(current_author(), author);
335
1
            assert!(authorities() == vec![alice_id.clone(), bob_id.clone()]);
336

            
337
            // Invulnerables should have triggered on new session authorities change
338
1
            run_to_session(2u32);
339
1
            let author_after_changes = get_orchestrator_current_author().unwrap();
340

            
341
1
            assert_eq!(current_author(), author_after_changes);
342
1
            assert_eq!(authorities(), vec![charlie_id, dave_id]);
343
1
        });
344
1
}
345

            
346
#[test]
347
1
fn test_author_collation_aura_add_assigned_to_paras() {
348
1
    ExtBuilder::default()
349
1
        .with_balances(vec![
350
1
            // Alice gets 10k extra tokens for her mapping deposit
351
1
            (AccountId::from(ALICE), 210_000 * UNIT),
352
1
            (AccountId::from(BOB), 100_000 * UNIT),
353
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
354
1
            (AccountId::from(DAVE), 100_000 * UNIT),
355
1
        ])
356
1
        .with_empty_parachains(vec![1001, 1002])
357
1
        .build()
358
1
        .execute_with(|| {
359
1
            run_to_block(2);
360
            // Assert current slot gets updated
361
1
            assert_eq!(current_slot(), 1u64);
362
1
            assert!(current_author() == AccountId::from(BOB));
363

            
364
            // We change invulnerables
365
            // We first need to set the keys
366
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
367
1
            let bob_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
368

            
369
1
            let charlie_id = get_aura_id_from_seed(&AccountId::from(CHARLIE).to_string());
370
1
            let dave_id = get_aura_id_from_seed(&AccountId::from(DAVE).to_string());
371

            
372
            // Set CHARLIE and DAVE keys
373
1
            assert_ok!(Session::set_keys(
374
1
                origin_of(CHARLIE.into()),
375
1
                dancebox_runtime::SessionKeys { nimbus: charlie_id },
376
1
                vec![]
377
            ));
378
1
            assert_ok!(Session::set_keys(
379
1
                origin_of(DAVE.into()),
380
1
                dancebox_runtime::SessionKeys { nimbus: dave_id },
381
1
                vec![]
382
            ));
383

            
384
            // Add new invulnerables
385
1
            assert_ok!(Invulnerables::add_invulnerable(
386
1
                root_origin(),
387
1
                CHARLIE.into()
388
            ));
389
1
            assert_ok!(Invulnerables::add_invulnerable(root_origin(), DAVE.into()));
390

            
391
            // SESSION CHANGE. First session. it takes 2 sessions to see the change
392
1
            run_to_session(1u32);
393
1
            let author = get_orchestrator_current_author().unwrap();
394

            
395
1
            assert_eq!(current_author(), author);
396
1
            assert_eq!(authorities(), vec![alice_id.clone(), bob_id.clone()]);
397

            
398
            // Invulnerables should have triggered on new session authorities change
399
            // However charlie and dave shoudl have gone to one para (1001)
400
1
            run_to_session(2u32);
401
1
            assert_eq!(authorities(), vec![alice_id, bob_id]);
402
1
            let assignment = CollatorAssignment::collator_container_chain();
403
1
            assert_eq!(
404
1
                assignment.container_chains[&1001u32.into()],
405
1
                vec![CHARLIE.into(), DAVE.into()]
406
            );
407
1
        });
408
1
}
409

            
410
#[test]
411
1
fn test_authors_without_paras() {
412
1
    ExtBuilder::default()
413
1
        .with_balances(vec![
414
1
            // Alice gets 10k extra tokens for her mapping deposit
415
1
            (AccountId::from(ALICE), 210_000 * UNIT),
416
1
            (AccountId::from(BOB), 100_000 * UNIT),
417
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
418
1
            (AccountId::from(DAVE), 100_000 * UNIT),
419
1
        ])
420
1
        .with_collators(vec![
421
1
            (AccountId::from(ALICE), 210 * UNIT),
422
1
            (AccountId::from(BOB), 100 * UNIT),
423
1
            (AccountId::from(CHARLIE), 100 * UNIT),
424
1
            (AccountId::from(DAVE), 100 * UNIT),
425
1
        ])
426
1
        .build()
427
1
        .execute_with(|| {
428
1
            run_to_block(2);
429
            // Assert current slot gets updated
430
1
            assert_eq!(current_slot(), 1u64);
431
1
            assert!(current_author() == AccountId::from(BOB));
432

            
433
            // Only Alice and Bob collate for our chain
434
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
435
1
            let bob_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
436
1
            let charlie_id = get_aura_id_from_seed(&AccountId::from(CHARLIE).to_string());
437
1
            let dave_id = get_aura_id_from_seed(&AccountId::from(DAVE).to_string());
438

            
439
            // It does not matter if we insert more collators, only two will be assigned
440
1
            assert_eq!(authorities(), vec![alice_id.clone(), bob_id.clone()]);
441

            
442
            // Set moondance collators to min 2 max 5
443
1
            assert_ok!(
444
1
                Configuration::set_min_orchestrator_collators(root_origin(), 2),
445
                ()
446
            );
447
1
            assert_ok!(
448
1
                Configuration::set_max_orchestrator_collators(root_origin(), 5),
449
                ()
450
            );
451

            
452
1
            run_to_session(2);
453
1
            assert_eq!(authorities(), vec![alice_id, bob_id, charlie_id, dave_id]);
454
1
        });
455
1
}
456

            
457
#[test]
458
1
fn test_authors_paras_inserted_a_posteriori() {
459
1
    ExtBuilder::default()
460
1
        .with_balances(vec![
461
1
            // Alice gets 10k extra tokens for her mapping deposit
462
1
            (AccountId::from(ALICE), 210_000 * UNIT),
463
1
            (AccountId::from(BOB), 100_000 * UNIT),
464
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
465
1
            (AccountId::from(DAVE), 100_000 * UNIT),
466
1
        ])
467
1
        .with_collators(vec![
468
1
            (AccountId::from(ALICE), 210 * UNIT),
469
1
            (AccountId::from(BOB), 100 * UNIT),
470
1
            (AccountId::from(CHARLIE), 100 * UNIT),
471
1
            (AccountId::from(DAVE), 100 * UNIT),
472
1
        ])
473
1
        .build()
474
1
        .execute_with(|| {
475
1
            run_to_block(2);
476
            // Assert current slot gets updated
477
1
            assert_eq!(current_slot(), 1u64);
478
1
            assert!(current_author() == AccountId::from(BOB));
479

            
480
            // Alice and Bob collate in our chain
481
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
482
1
            let bob_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
483

            
484
1
            assert_eq!(authorities(), vec![alice_id, bob_id]);
485

            
486
1
            assert_ok!(Registrar::register(
487
1
                origin_of(ALICE.into()),
488
1
                1001.into(),
489
1
                empty_genesis_data(),
490
1
                None
491
            ));
492
1
            set_dummy_boot_node(origin_of(ALICE.into()), 1001.into());
493
1
            assert_ok!(Registrar::mark_valid_for_collating(
494
1
                root_origin(),
495
1
                1001.into()
496
            ));
497
1
            assert_ok!(ServicesPayment::purchase_credits(
498
1
                origin_of(ALICE.into()),
499
1
                1001.into(),
500
1
                block_credits_to_required_balance(1000, 1001.into())
501
            ));
502
1
            assert_ok!(Registrar::register(
503
1
                origin_of(ALICE.into()),
504
1
                1002.into(),
505
1
                empty_genesis_data(),
506
1
                None
507
            ));
508
1
            set_dummy_boot_node(origin_of(ALICE.into()), 1002.into());
509
1
            assert_ok!(Registrar::mark_valid_for_collating(
510
1
                root_origin(),
511
1
                1002.into()
512
            ));
513
1
            assert_ok!(ServicesPayment::purchase_credits(
514
1
                origin_of(ALICE.into()),
515
1
                1002.into(),
516
1
                block_credits_to_required_balance(1000, 1002.into())
517
            ));
518

            
519
            // Assignment should happen after 2 sessions
520
1
            run_to_session(1u32);
521
1
            let assignment = CollatorAssignment::collator_container_chain();
522
1
            assert!(assignment.container_chains.is_empty());
523
1
            run_to_session(2u32);
524

            
525
            // Charlie and Dave should be assigned to para 1001
526
1
            let assignment = CollatorAssignment::collator_container_chain();
527
1
            assert_eq!(
528
1
                assignment.container_chains[&1001u32.into()],
529
1
                vec![CHARLIE.into(), DAVE.into()]
530
            );
531
1
        });
532
1
}
533

            
534
#[test]
535
1
fn test_authors_paras_inserted_a_posteriori_with_collators_already_assigned() {
536
1
    ExtBuilder::default()
537
1
        .with_balances(vec![
538
1
            // Alice gets 10k extra tokens for her mapping deposit
539
1
            (AccountId::from(ALICE), 210_000 * UNIT),
540
1
            (AccountId::from(BOB), 100_000 * UNIT),
541
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
542
1
            (AccountId::from(DAVE), 100_000 * UNIT),
543
1
        ])
544
1
        .with_collators(vec![
545
1
            (AccountId::from(ALICE), 210 * UNIT),
546
1
            (AccountId::from(BOB), 100 * UNIT),
547
1
            (AccountId::from(CHARLIE), 100 * UNIT),
548
1
            (AccountId::from(DAVE), 100 * UNIT),
549
1
        ])
550
1
        .with_config(pallet_configuration::HostConfiguration {
551
1
            max_collators: 100,
552
1
            min_orchestrator_collators: 2,
553
1
            max_orchestrator_collators: 5,
554
1
            collators_per_container: 2,
555
1
            full_rotation_period: 24,
556
1
            ..Default::default()
557
1
        })
558
1
        .build()
559
1
        .execute_with(|| {
560
1
            run_to_block(2);
561
            // Assert current slot gets updated
562
1
            assert_eq!(current_slot(), 1u64);
563
1
            assert!(current_author() == AccountId::from(BOB));
564

            
565
            // Alice and Bob collate in our chain
566
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
567
1
            let bob_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
568
1
            let charlie_id = get_aura_id_from_seed(&AccountId::from(CHARLIE).to_string());
569
1
            let dave_id = get_aura_id_from_seed(&AccountId::from(DAVE).to_string());
570

            
571
1
            assert_eq!(authorities(), vec![alice_id, bob_id, charlie_id, dave_id]);
572

            
573
1
            assert_ok!(Registrar::register(
574
1
                origin_of(ALICE.into()),
575
1
                1001.into(),
576
1
                empty_genesis_data(),
577
1
                None
578
            ));
579
1
            set_dummy_boot_node(origin_of(ALICE.into()), 1001.into());
580
1
            assert_ok!(Registrar::mark_valid_for_collating(
581
1
                root_origin(),
582
1
                1001.into()
583
            ));
584
1
            assert_ok!(ServicesPayment::purchase_credits(
585
1
                origin_of(ALICE.into()),
586
1
                1001.into(),
587
1
                block_credits_to_required_balance(1000, 1001.into())
588
            ));
589

            
590
            // Assignment should happen after 2 sessions
591
1
            run_to_session(1u32);
592
1
            let assignment = CollatorAssignment::collator_container_chain();
593
1
            assert!(assignment.container_chains.is_empty());
594
1
            run_to_session(2u32);
595

            
596
            // Charlie and Dave are now assigned to para 1001
597
1
            let assignment = CollatorAssignment::collator_container_chain();
598
1
            assert_eq!(
599
1
                assignment.container_chains[&1001u32.into()],
600
1
                vec![CHARLIE.into(), DAVE.into()]
601
            );
602
1
            assert_eq!(
603
                assignment.orchestrator_chain,
604
1
                vec![ALICE.into(), BOB.into()]
605
            );
606
1
        });
607
1
}
608

            
609
#[test]
610
1
fn test_paras_registered_but_zero_credits() {
611
1
    ExtBuilder::default()
612
1
        .with_balances(vec![
613
1
            // Alice gets 10k extra tokens for her mapping deposit
614
1
            (AccountId::from(ALICE), 210_000 * UNIT),
615
1
            (AccountId::from(BOB), 100_000 * UNIT),
616
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
617
1
            (AccountId::from(DAVE), 100_000 * UNIT),
618
1
        ])
619
1
        .with_collators(vec![
620
1
            (AccountId::from(ALICE), 210 * UNIT),
621
1
            (AccountId::from(BOB), 100 * UNIT),
622
1
            (AccountId::from(CHARLIE), 100 * UNIT),
623
1
            (AccountId::from(DAVE), 100 * UNIT),
624
1
        ])
625
1
        .build()
626
1
        .execute_with(|| {
627
1
            run_to_block(2);
628
            // Assert current slot gets updated
629
1
            assert_eq!(current_slot(), 1u64);
630
1
            assert!(current_author() == AccountId::from(BOB));
631

            
632
            // Alice and Bob collate in our chain
633
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
634
1
            let bob_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
635

            
636
1
            assert_eq!(authorities(), vec![alice_id, bob_id]);
637

            
638
1
            assert_ok!(Registrar::register(
639
1
                origin_of(ALICE.into()),
640
1
                1001.into(),
641
1
                empty_genesis_data(),
642
1
                None
643
            ));
644
1
            set_dummy_boot_node(origin_of(ALICE.into()), 1001.into());
645
1
            assert_ok!(Registrar::mark_valid_for_collating(
646
1
                root_origin(),
647
1
                1001.into()
648
            ));
649
            // Need to reset credits to 0 because now parachains are given free credits on register
650
1
            assert_ok!(ServicesPayment::set_block_production_credits(
651
1
                root_origin(),
652
1
                1001.into(),
653
                0
654
            ));
655

            
656
            // Assignment should happen after 2 sessions
657
1
            run_to_session(1u32);
658
1
            let assignment = CollatorAssignment::collator_container_chain();
659
1
            assert!(assignment.container_chains.is_empty());
660
1
            run_to_session(2u32);
661

            
662
            // Nobody should be assigned to para 1001
663
1
            let assignment = CollatorAssignment::collator_container_chain();
664
1
            assert_eq!(assignment.container_chains.get(&1001u32.into()), None,);
665
1
        });
666
1
}
667

            
668
#[test]
669
1
fn test_paras_registered_but_not_enough_credits() {
670
1
    ExtBuilder::default()
671
1
        .with_balances(vec![
672
1
            // Alice gets 10k extra tokens for her mapping deposit
673
1
            (AccountId::from(ALICE), 210_000 * UNIT),
674
1
            (AccountId::from(BOB), 100_000 * UNIT),
675
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
676
1
            (AccountId::from(DAVE), 100_000 * UNIT),
677
1
        ])
678
1
        .with_collators(vec![
679
1
            (AccountId::from(ALICE), 210 * UNIT),
680
1
            (AccountId::from(BOB), 100 * UNIT),
681
1
            (AccountId::from(CHARLIE), 100 * UNIT),
682
1
            (AccountId::from(DAVE), 100 * UNIT),
683
1
        ])
684
1
        .build()
685
1
        .execute_with(|| {
686
1
            run_to_block(2);
687
            // Assert current slot gets updated
688
1
            assert_eq!(current_slot(), 1u64);
689
1
            assert!(current_author() == AccountId::from(BOB));
690

            
691
            // Alice and Bob collate in our chain
692
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
693
1
            let bob_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
694

            
695
1
            assert_eq!(authorities(), vec![alice_id, bob_id]);
696

            
697
1
            assert_ok!(Registrar::register(
698
1
                origin_of(ALICE.into()),
699
1
                1001.into(),
700
1
                empty_genesis_data(),
701
1
                None
702
            ));
703
1
            set_dummy_boot_node(origin_of(ALICE.into()), 1001.into());
704
1
            assert_ok!(Registrar::mark_valid_for_collating(
705
1
                root_origin(),
706
1
                1001.into()
707
            ));
708
            // Need to reset credits to 0 because now parachains are given free credits on register
709
1
            assert_ok!(ServicesPayment::set_block_production_credits(
710
1
                root_origin(),
711
1
                1001.into(),
712
                0
713
            ));
714
            // Purchase 1 credit less that what is needed
715
1
            let credits_1001 = dancebox_runtime::Period::get() - 1;
716
1
            assert_ok!(ServicesPayment::set_block_production_credits(
717
1
                root_origin(),
718
1
                1001.into(),
719
1
                credits_1001
720
            ));
721

            
722
            // Assignment should happen after 2 sessions
723
1
            run_to_session(1u32);
724
1
            let assignment = CollatorAssignment::collator_container_chain();
725
1
            assert!(assignment.container_chains.is_empty());
726
1
            run_to_session(2u32);
727
            // Nobody should be assigned to para 1001
728
1
            let assignment = CollatorAssignment::collator_container_chain();
729
1
            assert_eq!(assignment.container_chains.get(&1001u32.into()), None);
730

            
731
            // Now purchase the missing block credit
732
1
            assert_ok!(ServicesPayment::set_block_production_credits(
733
1
                root_origin(),
734
1
                1001.into(),
735
1
                credits_1001 + 1
736
            ));
737

            
738
1
            run_to_session(4u32);
739
            // Charlie and Dave should be assigned to para 1001
740
1
            let assignment = CollatorAssignment::collator_container_chain();
741
1
            assert_eq!(
742
1
                assignment.container_chains[&1001u32.into()],
743
1
                vec![CHARLIE.into(), DAVE.into()]
744
            );
745
1
        });
746
1
}
747

            
748
#[test]
749
1
fn test_paras_registered_but_only_credits_for_1_session() {
750
1
    ExtBuilder::default()
751
1
        .with_balances(vec![
752
1
            // Alice gets 10k extra tokens for her mapping deposit
753
1
            (AccountId::from(ALICE), 210_000 * UNIT),
754
1
            (AccountId::from(BOB), 100_000 * UNIT),
755
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
756
1
            (AccountId::from(DAVE), 100_000 * UNIT),
757
1
        ])
758
1
        .with_collators(vec![
759
1
            (AccountId::from(ALICE), 210 * UNIT),
760
1
            (AccountId::from(BOB), 100 * UNIT),
761
1
            (AccountId::from(CHARLIE), 100 * UNIT),
762
1
            (AccountId::from(DAVE), 100 * UNIT),
763
1
        ])
764
1
        .build()
765
1
        .execute_with(|| {
766
1
            run_to_block(2);
767
            // Assert current slot gets updated
768
1
            assert_eq!(current_slot(), 1u64);
769
1
            assert!(current_author() == AccountId::from(BOB));
770

            
771
            // Alice and Bob collate in our chain
772
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
773
1
            let bob_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
774

            
775
1
            assert_eq!(authorities(), vec![alice_id, bob_id]);
776

            
777
1
            assert_ok!(Registrar::register(
778
1
                origin_of(ALICE.into()),
779
1
                1001.into(),
780
1
                empty_genesis_data(),
781
1
                None
782
            ));
783
1
            set_dummy_boot_node(origin_of(ALICE.into()), 1001.into());
784
1
            assert_ok!(Registrar::mark_valid_for_collating(
785
1
                root_origin(),
786
1
                1001.into()
787
            ));
788
            // Need to reset credits to 0 because now parachains are given free credits on register
789
1
            assert_ok!(ServicesPayment::set_block_production_credits(
790
1
                root_origin(),
791
1
                1001.into(),
792
                0
793
            ));
794
            // Purchase only enough credits for 1 session
795
1
            let credits_1001 = dancebox_runtime::Period::get();
796
1
            assert_ok!(ServicesPayment::set_block_production_credits(
797
1
                root_origin(),
798
1
                1001.into(),
799
1
                credits_1001
800
            ));
801

            
802
            // Assignment should happen after 2 sessions
803
1
            run_to_session(1u32);
804
1
            let assignment = CollatorAssignment::collator_container_chain();
805
1
            assert!(assignment.container_chains.is_empty());
806
1
            run_to_session(2u32);
807
            // Charlie and Dave should be assigned to para 1001
808
1
            let assignment = CollatorAssignment::collator_container_chain();
809
1
            assert_eq!(
810
1
                assignment.container_chains[&1001u32.into()],
811
1
                vec![CHARLIE.into(), DAVE.into()]
812
            );
813

            
814
            // No credits are consumed if the container chain is not producing blocks
815
1
            run_block();
816
1
            let credits =
817
1
                pallet_services_payment::BlockProductionCredits::<Runtime>::get(ParaId::from(1001))
818
1
                    .unwrap_or_default();
819
1
            assert_eq!(credits, credits_1001);
820

            
821
            // Simulate block inclusion from container chain 1001
822
1
            let mut sproof = ParaHeaderSproofBuilder::default();
823
1
            let slot: u64 = 5;
824
1
            let s = ParaHeaderSproofBuilderItem {
825
1
                para_id: 1001.into(),
826
1
                author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::<u32, BlakeTwo256> {
827
1
                    parent_hash: Default::default(),
828
1
                    number: 1,
829
1
                    state_root: Default::default(),
830
1
                    extrinsics_root: Default::default(),
831
1
                    digest: sp_runtime::generic::Digest {
832
1
                        logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())],
833
1
                    },
834
1
                }),
835
1
            };
836
1
            sproof.items.push(s);
837
1
            set_author_noting_inherent_data(sproof);
838

            
839
1
            run_block();
840
1
            let credits =
841
1
                pallet_services_payment::BlockProductionCredits::<Runtime>::get(ParaId::from(1001))
842
1
                    .unwrap_or_default();
843
1
            assert_eq!(credits, credits_1001 - 1);
844

            
845
1
            run_to_session(4u32);
846
            // Nobody should be assigned to para 1001
847
1
            let assignment = CollatorAssignment::collator_container_chain();
848
1
            assert_eq!(assignment.container_chains.get(&1001u32.into()), None,);
849

            
850
            // The container chain only produced one block, so it only consumed one block credit.
851
            // (it could have produced more blocks, but at most it would have consumed `Period::get()` credits)
852
1
            let credits =
853
1
                pallet_services_payment::BlockProductionCredits::<Runtime>::get(ParaId::from(1001))
854
1
                    .unwrap_or_default();
855
1
            assert_eq!(credits, credits_1001 - 1);
856
1
        });
857
1
}
858

            
859
#[test]
860
1
fn test_parachains_deregister_collators_re_assigned() {
861
1
    ExtBuilder::default()
862
1
        .with_balances(vec![
863
1
            // Alice gets 10k extra tokens for her mapping deposit
864
1
            (AccountId::from(ALICE), 210_000 * UNIT),
865
1
            (AccountId::from(BOB), 100_000 * UNIT),
866
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
867
1
            (AccountId::from(DAVE), 100_000 * UNIT),
868
1
        ])
869
1
        .with_collators(vec![
870
1
            (AccountId::from(ALICE), 210 * UNIT),
871
1
            (AccountId::from(BOB), 100 * UNIT),
872
1
            (AccountId::from(CHARLIE), 100 * UNIT),
873
1
            (AccountId::from(DAVE), 100 * UNIT),
874
1
        ])
875
1
        .with_empty_parachains(vec![1001, 1002])
876
1
        .build()
877
1
        .execute_with(|| {
878
1
            run_to_block(2);
879
            // Assert current slot gets updated
880
1
            assert_eq!(current_slot(), 1u64);
881
1
            assert!(current_author() == AccountId::from(BOB));
882

            
883
            // Alice and Bob are authorities
884
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
885
1
            let bob_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
886

            
887
1
            assert_eq!(authorities(), vec![alice_id, bob_id]);
888

            
889
            // Charlie and Dave to 1001
890
1
            let assignment = CollatorAssignment::collator_container_chain();
891
1
            assert_eq!(
892
1
                assignment.container_chains[&1001u32.into()],
893
1
                vec![CHARLIE.into(), DAVE.into()]
894
            );
895

            
896
1
            assert_ok!(Registrar::deregister(root_origin(), 1001.into()), ());
897

            
898
            // Assignment should happen after 2 sessions
899
1
            run_to_session(1u32);
900

            
901
1
            let assignment = CollatorAssignment::collator_container_chain();
902
1
            assert_eq!(
903
1
                assignment.container_chains[&1001u32.into()],
904
1
                vec![CHARLIE.into(), DAVE.into()]
905
            );
906

            
907
1
            run_to_session(2u32);
908

            
909
            // Charlie and Dave should be assigne dot para 1002 this time
910
1
            let assignment = CollatorAssignment::collator_container_chain();
911
1
            assert_eq!(
912
1
                assignment.container_chains[&1002u32.into()],
913
1
                vec![CHARLIE.into(), DAVE.into()]
914
            );
915
1
        });
916
1
}
917

            
918
#[test]
919
1
fn test_parachains_deregister_collators_config_change_reassigned() {
920
1
    ExtBuilder::default()
921
1
        .with_balances(vec![
922
1
            // Alice gets 10k extra tokens for her mapping deposit
923
1
            (AccountId::from(ALICE), 210_000 * UNIT),
924
1
            (AccountId::from(BOB), 100_000 * UNIT),
925
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
926
1
            (AccountId::from(DAVE), 100_000 * UNIT),
927
1
        ])
928
1
        .with_collators(vec![
929
1
            (AccountId::from(ALICE), 210 * UNIT),
930
1
            (AccountId::from(BOB), 100 * UNIT),
931
1
            (AccountId::from(CHARLIE), 100 * UNIT),
932
1
            (AccountId::from(DAVE), 100 * UNIT),
933
1
        ])
934
1
        .with_empty_parachains(vec![1001, 1002])
935
1
        .build()
936
1
        .execute_with(|| {
937
1
            run_to_block(2);
938
            // Assert current slot gets updated
939
1
            assert_eq!(current_slot(), 1u64);
940
1
            assert!(current_author() == AccountId::from(BOB));
941

            
942
            // Alice and Bob are authorities
943
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
944
1
            let bob_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
945

            
946
1
            assert_eq!(authorities(), vec![alice_id, bob_id]);
947

            
948
            // Set orchestrator collators to 1
949
1
            assert_ok!(
950
1
                Configuration::set_max_orchestrator_collators(root_origin(), 1),
951
                ()
952
            );
953

            
954
            // Set container chain collators to 3
955
1
            assert_ok!(
956
1
                Configuration::set_collators_per_container(root_origin(), 3),
957
                ()
958
            );
959

            
960
            // Charlie and Dave to 1001
961
1
            let assignment = CollatorAssignment::collator_container_chain();
962
1
            assert_eq!(
963
1
                assignment.container_chains[&1001u32.into()],
964
1
                vec![CHARLIE.into(), DAVE.into()]
965
            );
966

            
967
            // Assignment should happen after 2 sessions
968
1
            run_to_session(1u32);
969

            
970
1
            let assignment = CollatorAssignment::collator_container_chain();
971
1
            assert_eq!(
972
1
                assignment.container_chains[&1001u32.into()],
973
1
                vec![CHARLIE.into(), DAVE.into()]
974
            );
975

            
976
1
            run_to_session(2u32);
977

            
978
            // Charlie, Dave and BOB should be assigne dot para 1001 this time
979
1
            let assignment = CollatorAssignment::collator_container_chain();
980
1
            assert_eq!(
981
1
                assignment.container_chains[&1001u32.into()],
982
1
                vec![CHARLIE.into(), DAVE.into(), BOB.into()]
983
            );
984

            
985
1
            assert_eq!(assignment.orchestrator_chain, vec![ALICE.into()]);
986
1
        });
987
1
}
988

            
989
#[test]
990
1
fn test_orchestrator_collators_with_non_sufficient_collators() {
991
1
    ExtBuilder::default()
992
1
        .with_balances(vec![
993
1
            // Alice gets 10k extra tokens for her mapping deposit
994
1
            (AccountId::from(ALICE), 210_000 * UNIT),
995
1
        ])
996
1
        .with_collators(vec![(AccountId::from(ALICE), 210 * UNIT)])
997
1
        .with_empty_parachains(vec![1001, 1002])
998
1
        .build()
999
1
        .execute_with(|| {
1
            run_to_block(2);
            // Assert current slot gets updated
1
            assert_eq!(current_slot(), 1u64);
1
            assert!(current_author() == AccountId::from(ALICE));
            // Alice and Bob are authorities
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
1
            assert_eq!(authorities(), vec![alice_id]);
1
        });
1
}
#[test]
1
fn test_configuration_on_session_change() {
1
    ExtBuilder::default().build().execute_with(|| {
1
        assert_eq!(Configuration::config().max_collators, 100);
1
        assert_eq!(Configuration::config().min_orchestrator_collators, 2);
1
        assert_eq!(Configuration::config().collators_per_container, 2);
1
        assert_ok!(Configuration::set_max_collators(root_origin(), 50), ());
1
        run_to_session(1u32);
1
        assert_ok!(
1
            Configuration::set_min_orchestrator_collators(root_origin(), 20),
            ()
        );
1
        assert_eq!(Configuration::config().max_collators, 100);
1
        assert_eq!(Configuration::config().min_orchestrator_collators, 2);
1
        assert_eq!(Configuration::config().collators_per_container, 2);
1
        run_to_session(2u32);
1
        assert_ok!(
1
            Configuration::set_collators_per_container(root_origin(), 10),
            ()
        );
1
        assert_eq!(Configuration::config().max_collators, 50);
1
        assert_eq!(Configuration::config().min_orchestrator_collators, 2);
1
        assert_eq!(Configuration::config().collators_per_container, 2);
1
        run_to_session(3u32);
1
        assert_eq!(Configuration::config().max_collators, 50);
1
        assert_eq!(Configuration::config().min_orchestrator_collators, 20);
1
        assert_eq!(Configuration::config().collators_per_container, 2);
1
        run_to_session(4u32);
1
        assert_eq!(Configuration::config().max_collators, 50);
1
        assert_eq!(Configuration::config().min_orchestrator_collators, 20);
1
        assert_eq!(Configuration::config().collators_per_container, 10);
1
    });
1
}
#[test]
1
fn test_author_collation_aura_add_assigned_to_paras_runtime_api() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
            // Assert current slot gets updated
1
            assert_eq!(current_slot(), 1u64);
1
            assert!(current_author() == AccountId::from(BOB));
1
            assert_eq!(
1
                dancebox_runtime_test_utils::parachain_collators(100.into()),
1
                Some(vec![ALICE.into(), BOB.into()])
            );
1
            assert_eq!(
1
                dancebox_runtime_test_utils::parachain_collators(1001.into()),
1
                Some(vec![])
            );
1
            assert_eq!(
1
                dancebox_runtime_test_utils::current_collator_parachain_assignment(ALICE.into()),
1
                Some(100.into())
            );
1
            assert_eq!(
1
                dancebox_runtime_test_utils::future_collator_parachain_assignment(ALICE.into()),
1
                Some(100.into())
            );
1
            assert_eq!(
1
                dancebox_runtime_test_utils::current_collator_parachain_assignment(CHARLIE.into()),
                None
            );
1
            assert_eq!(
1
                dancebox_runtime_test_utils::future_collator_parachain_assignment(CHARLIE.into()),
                None
            );
            // We change invulnerables
            // We first need to set the keys
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
1
            let bob_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
1
            let charlie_id = get_aura_id_from_seed(&AccountId::from(CHARLIE).to_string());
1
            let dave_id = get_aura_id_from_seed(&AccountId::from(DAVE).to_string());
            // Set CHARLIE and DAVE keys
1
            assert_ok!(Session::set_keys(
1
                origin_of(CHARLIE.into()),
1
                dancebox_runtime::SessionKeys { nimbus: charlie_id },
1
                vec![]
            ));
1
            assert_ok!(Session::set_keys(
1
                origin_of(DAVE.into()),
1
                dancebox_runtime::SessionKeys { nimbus: dave_id },
1
                vec![]
            ));
            // Set new invulnerables
1
            assert_ok!(Invulnerables::add_invulnerable(
1
                root_origin(),
1
                CHARLIE.into()
            ));
1
            assert_ok!(Invulnerables::add_invulnerable(root_origin(), DAVE.into()));
            // SESSION CHANGE. First session. it takes 2 sessions to see the change
1
            run_to_session(1u32);
1
            let author = get_orchestrator_current_author().unwrap();
1
            assert_eq!(current_author(), author);
1
            assert_eq!(authorities(), vec![alice_id.clone(), bob_id.clone()]);
1
            assert_eq!(
1
                dancebox_runtime_test_utils::parachain_collators(100.into()),
1
                Some(vec![ALICE.into(), BOB.into()])
            );
1
            assert_eq!(
1
                dancebox_runtime_test_utils::parachain_collators(1001.into()),
1
                Some(vec![])
            );
1
            assert_eq!(
1
                dancebox_runtime_test_utils::current_collator_parachain_assignment(CHARLIE.into()),
                None
            );
1
            assert_eq!(
1
                dancebox_runtime_test_utils::future_collator_parachain_assignment(CHARLIE.into()),
1
                Some(1001.into())
            );
            // Invulnerables should have triggered on new session authorities change
            // However charlie and dave shoudl have gone to one para (1001)
1
            run_to_session(2u32);
1
            assert_eq!(authorities(), vec![alice_id, bob_id]);
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert_eq!(
1
                assignment.container_chains[&1001u32.into()],
1
                vec![CHARLIE.into(), DAVE.into()]
            );
1
            assert_eq!(
1
                dancebox_runtime_test_utils::parachain_collators(100.into()),
1
                Some(vec![ALICE.into(), BOB.into()])
            );
1
            assert_eq!(
1
                dancebox_runtime_test_utils::parachain_collators(1001.into()),
1
                Some(vec![CHARLIE.into(), DAVE.into()])
            );
1
            assert_eq!(
1
                dancebox_runtime_test_utils::current_collator_parachain_assignment(CHARLIE.into()),
1
                Some(1001.into())
            );
1
            assert_eq!(
1
                dancebox_runtime_test_utils::future_collator_parachain_assignment(CHARLIE.into()),
1
                Some(1001.into())
            );
            // Remove BOB
1
            assert_ok!(Invulnerables::remove_invulnerable(
1
                root_origin(),
1
                BOB.into()
            ));
1
            run_to_session(3u32);
1
            assert_eq!(
1
                dancebox_runtime_test_utils::parachain_collators(100.into()),
1
                Some(vec![ALICE.into(), BOB.into()])
            );
1
            assert_eq!(
1
                dancebox_runtime_test_utils::parachain_collators(1001.into()),
1
                Some(vec![CHARLIE.into(), DAVE.into()])
            );
1
            assert_eq!(
1
                dancebox_runtime_test_utils::current_collator_parachain_assignment(BOB.into()),
1
                Some(100.into())
            );
1
            assert_eq!(
1
                dancebox_runtime_test_utils::future_collator_parachain_assignment(BOB.into()),
                None
            );
1
            run_to_session(4u32);
1
            assert_eq!(
1
                dancebox_runtime_test_utils::parachain_collators(100.into()),
1
                Some(vec![ALICE.into(), CHARLIE.into()])
            );
1
            assert_eq!(
1
                dancebox_runtime_test_utils::parachain_collators(1001.into()),
1
                Some(vec![])
            );
1
            assert_eq!(
1
                dancebox_runtime_test_utils::current_collator_parachain_assignment(BOB.into()),
                None
            );
1
            assert_eq!(
1
                dancebox_runtime_test_utils::future_collator_parachain_assignment(BOB.into()),
                None
            );
1
        });
1
}
#[test]
1
fn test_consensus_runtime_api() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
1
            let bob_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
1
            let charlie_id = get_aura_id_from_seed(&AccountId::from(CHARLIE).to_string());
1
            let dave_id = get_aura_id_from_seed(&AccountId::from(DAVE).to_string());
1
            assert_eq!(
1
                Runtime::para_id_authorities(100.into()),
1
                Some(vec![alice_id.clone(), bob_id.clone()])
            );
1
            assert_eq!(Runtime::para_id_authorities(1001.into()), Some(vec![]));
1
            assert_eq!(
1
                Runtime::check_para_id_assignment(alice_id.clone()),
1
                Some(100.into())
            );
1
            assert_eq!(
1
                Runtime::check_para_id_assignment(bob_id.clone()),
1
                Some(100.into())
            );
1
            assert_eq!(Runtime::check_para_id_assignment(charlie_id.clone()), None);
1
            assert_eq!(Runtime::check_para_id_assignment(dave_id.clone()), None);
            // Set CHARLIE and DAVE keys
1
            assert_ok!(Session::set_keys(
1
                origin_of(CHARLIE.into()),
1
                dancebox_runtime::SessionKeys {
1
                    nimbus: charlie_id.clone(),
1
                },
1
                vec![]
            ));
1
            assert_ok!(Session::set_keys(
1
                origin_of(DAVE.into()),
1
                dancebox_runtime::SessionKeys {
1
                    nimbus: dave_id.clone(),
1
                },
1
                vec![]
            ));
            // Set new invulnerables
1
            assert_ok!(Invulnerables::add_invulnerable(
1
                root_origin(),
1
                CHARLIE.into()
            ));
1
            assert_ok!(Invulnerables::add_invulnerable(root_origin(), DAVE.into()));
1
            run_to_session(2u32);
1
            assert_eq!(
1
                Runtime::para_id_authorities(100.into()),
1
                Some(vec![alice_id.clone(), bob_id.clone()])
            );
1
            assert_eq!(
1
                Runtime::para_id_authorities(1001.into()),
1
                Some(vec![charlie_id.clone(), dave_id.clone()])
            );
1
            assert_eq!(
1
                Runtime::check_para_id_assignment(alice_id),
1
                Some(100.into())
            );
1
            assert_eq!(Runtime::check_para_id_assignment(bob_id), Some(100.into()));
1
            assert_eq!(
1
                Runtime::check_para_id_assignment(charlie_id),
1
                Some(1001.into())
            );
1
            assert_eq!(
1
                Runtime::check_para_id_assignment(dave_id),
1
                Some(1001.into())
            );
1
        });
1
}
#[test]
1
fn test_consensus_runtime_api_session_changes() {
    // The test shoul return always the assiignment on the next epoch
    // Meaning that we need to see before the session change block
    // if we can predict correctly
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
1
            let bob_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
1
            let charlie_id = get_aura_id_from_seed(&AccountId::from(CHARLIE).to_string());
1
            let dave_id = get_aura_id_from_seed(&AccountId::from(DAVE).to_string());
1
            assert_eq!(
1
                Runtime::para_id_authorities(100.into()),
1
                Some(vec![alice_id.clone(), bob_id.clone()])
            );
1
            assert_eq!(Runtime::para_id_authorities(1001.into()), Some(vec![]));
1
            assert_eq!(
1
                Runtime::check_para_id_assignment(alice_id.clone()),
1
                Some(100.into())
            );
1
            assert_eq!(
1
                Runtime::check_para_id_assignment(bob_id.clone()),
1
                Some(100.into())
            );
1
            assert_eq!(Runtime::check_para_id_assignment(charlie_id.clone()), None);
1
            assert_eq!(Runtime::check_para_id_assignment(dave_id.clone()), None);
            // Set CHARLIE and DAVE keys
1
            assert_ok!(Session::set_keys(
1
                origin_of(CHARLIE.into()),
1
                dancebox_runtime::SessionKeys {
1
                    nimbus: charlie_id.clone(),
1
                },
1
                vec![]
            ));
1
            assert_ok!(Session::set_keys(
1
                origin_of(DAVE.into()),
1
                dancebox_runtime::SessionKeys {
1
                    nimbus: dave_id.clone(),
1
                },
1
                vec![]
            ));
            // Set new invulnerables
1
            assert_ok!(Invulnerables::add_invulnerable(
1
                root_origin(),
1
                CHARLIE.into()
            ));
1
            assert_ok!(Invulnerables::add_invulnerable(root_origin(), DAVE.into()));
1
            let session_two_edge = dancebox_runtime::Period::get() * 2;
            // Let's run just 2 blocks before the session 2 change first
            // Prediction should still be identical, as we are not in the
            // edge of a session change
1
            run_to_block(session_two_edge - 2);
1
            assert_eq!(
1
                Runtime::para_id_authorities(100.into()),
1
                Some(vec![alice_id.clone(), bob_id.clone()])
            );
1
            assert_eq!(Runtime::para_id_authorities(1001.into()), Some(vec![]));
1
            assert_eq!(
1
                Runtime::check_para_id_assignment(alice_id.clone()),
1
                Some(100.into())
            );
1
            assert_eq!(
1
                Runtime::check_para_id_assignment(bob_id.clone()),
1
                Some(100.into())
            );
1
            assert_eq!(Runtime::check_para_id_assignment(charlie_id.clone()), None);
1
            assert_eq!(Runtime::check_para_id_assignment(dave_id.clone()), None);
            // Now we run to session edge -1. Here we should predict already with
            // authorities of the next block!
1
            run_to_block(session_two_edge - 1);
1
            assert_eq!(
1
                Runtime::para_id_authorities(100.into()),
1
                Some(vec![alice_id.clone(), bob_id.clone()])
            );
1
            assert_eq!(
1
                Runtime::para_id_authorities(1001.into()),
1
                Some(vec![charlie_id.clone(), dave_id.clone()])
            );
1
            assert_eq!(
1
                Runtime::check_para_id_assignment(alice_id),
1
                Some(100.into())
            );
1
            assert_eq!(Runtime::check_para_id_assignment(bob_id), Some(100.into()));
1
            assert_eq!(
1
                Runtime::check_para_id_assignment(charlie_id),
1
                Some(1001.into())
            );
1
            assert_eq!(
1
                Runtime::check_para_id_assignment(dave_id),
1
                Some(1001.into())
            );
1
        });
1
}
#[test]
1
fn test_consensus_runtime_api_next_session() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
1
            let bob_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
1
            let charlie_id = get_aura_id_from_seed(&AccountId::from(CHARLIE).to_string());
1
            let dave_id = get_aura_id_from_seed(&AccountId::from(DAVE).to_string());
1
            assert_eq!(
1
                Runtime::para_id_authorities(100.into()),
1
                Some(vec![alice_id.clone(), bob_id.clone()])
            );
1
            assert_eq!(Runtime::para_id_authorities(1001.into()), Some(vec![]));
1
            assert_eq!(
1
                Runtime::check_para_id_assignment(alice_id.clone()),
1
                Some(100.into())
            );
1
            assert_eq!(
1
                Runtime::check_para_id_assignment(bob_id.clone()),
1
                Some(100.into())
            );
1
            assert_eq!(Runtime::check_para_id_assignment(charlie_id.clone()), None);
1
            assert_eq!(Runtime::check_para_id_assignment(dave_id.clone()), None);
            // In the next session the assignment will not change
1
            assert_eq!(
1
                Runtime::check_para_id_assignment_next_session(alice_id.clone()),
1
                Some(100.into())
            );
1
            assert_eq!(
1
                Runtime::check_para_id_assignment_next_session(bob_id.clone()),
1
                Some(100.into())
            );
1
            assert_eq!(
1
                Runtime::check_para_id_assignment_next_session(charlie_id.clone()),
                None,
            );
1
            assert_eq!(
1
                Runtime::check_para_id_assignment_next_session(dave_id.clone()),
                None,
            );
            // Set CHARLIE and DAVE keys
1
            assert_ok!(Session::set_keys(
1
                origin_of(CHARLIE.into()),
1
                dancebox_runtime::SessionKeys {
1
                    nimbus: charlie_id.clone(),
1
                },
1
                vec![]
            ));
1
            assert_ok!(Session::set_keys(
1
                origin_of(DAVE.into()),
1
                dancebox_runtime::SessionKeys {
1
                    nimbus: dave_id.clone(),
1
                },
1
                vec![]
            ));
            // Set new invulnerables
1
            assert_ok!(Invulnerables::add_invulnerable(
1
                root_origin(),
1
                CHARLIE.into()
            ));
1
            assert_ok!(Invulnerables::add_invulnerable(root_origin(), DAVE.into()));
1
            let session_two_edge = dancebox_runtime::Period::get() * 2;
            // Let's run just 2 blocks before the session 2 change first
            // Prediction should still be identical, as we are not in the
            // edge of a session change
1
            run_to_block(session_two_edge - 2);
1
            assert_eq!(
1
                Runtime::para_id_authorities(100.into()),
1
                Some(vec![alice_id.clone(), bob_id.clone()])
            );
1
            assert_eq!(Runtime::para_id_authorities(1001.into()), Some(vec![]));
1
            assert_eq!(
1
                Runtime::check_para_id_assignment(alice_id.clone()),
1
                Some(100.into())
            );
1
            assert_eq!(
1
                Runtime::check_para_id_assignment(bob_id.clone()),
1
                Some(100.into())
            );
1
            assert_eq!(Runtime::check_para_id_assignment(charlie_id.clone()), None);
1
            assert_eq!(Runtime::check_para_id_assignment(dave_id.clone()), None);
            // But in the next session the assignment will change, so future api returns different value
1
            assert_eq!(
1
                Runtime::check_para_id_assignment_next_session(alice_id.clone()),
1
                Some(100.into())
            );
1
            assert_eq!(
1
                Runtime::check_para_id_assignment_next_session(bob_id.clone()),
1
                Some(100.into())
            );
1
            assert_eq!(
1
                Runtime::check_para_id_assignment_next_session(charlie_id.clone()),
1
                Some(1001.into()),
            );
1
            assert_eq!(
1
                Runtime::check_para_id_assignment_next_session(dave_id.clone()),
1
                Some(1001.into()),
            );
            // Now we run to session edge -1. Here we should predict already with
            // authorities of the next block!
1
            run_to_block(session_two_edge - 1);
1
            assert_eq!(
1
                Runtime::para_id_authorities(100.into()),
1
                Some(vec![alice_id.clone(), bob_id.clone()])
            );
1
            assert_eq!(
1
                Runtime::para_id_authorities(1001.into()),
1
                Some(vec![charlie_id.clone(), dave_id.clone()])
            );
1
            assert_eq!(
1
                Runtime::check_para_id_assignment(alice_id.clone()),
1
                Some(100.into())
            );
1
            assert_eq!(
1
                Runtime::check_para_id_assignment(bob_id.clone()),
1
                Some(100.into())
            );
1
            assert_eq!(
1
                Runtime::check_para_id_assignment(charlie_id.clone()),
1
                Some(1001.into())
            );
1
            assert_eq!(
1
                Runtime::check_para_id_assignment(dave_id.clone()),
1
                Some(1001.into())
            );
            // check_para_id_assignment_next_session returns the same value as check_para_id_assignment
            // because we are on a session boundary
1
            assert_eq!(
1
                Runtime::check_para_id_assignment_next_session(alice_id),
1
                Some(100.into())
            );
1
            assert_eq!(
1
                Runtime::check_para_id_assignment_next_session(bob_id),
1
                Some(100.into())
            );
1
            assert_eq!(
1
                Runtime::check_para_id_assignment_next_session(charlie_id),
1
                Some(1001.into())
            );
1
            assert_eq!(
1
                Runtime::check_para_id_assignment_next_session(dave_id),
1
                Some(1001.into())
            );
1
        });
1
}
#[test]
1
fn test_author_noting_self_para_id_not_noting() {
1
    ExtBuilder::default().build().execute_with(|| {
1
        let mut sproof = ParaHeaderSproofBuilder::default();
1
        let slot: u64 = 5;
1
        let self_para = parachain_info::Pallet::<Runtime>::get();
1
        let s = ParaHeaderSproofBuilderItem {
1
            para_id: self_para,
1
            author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::<u32, BlakeTwo256> {
1
                parent_hash: Default::default(),
1
                number: Default::default(),
1
                state_root: Default::default(),
1
                extrinsics_root: Default::default(),
1
                digest: sp_runtime::generic::Digest {
1
                    logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())],
1
                },
1
            }),
1
        };
1
        sproof.items.push(s);
1
        set_author_noting_inherent_data(sproof);
1
        assert_eq!(AuthorNoting::latest_author(self_para), None);
1
    });
1
}
#[test]
1
fn test_author_noting_not_self_para() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
1
            let mut sproof = ParaHeaderSproofBuilder::default();
1
            let slot: u64 = 5;
1
            let other_para: ParaId = 1001u32.into();
            // Charlie and Dave to 1001
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert_eq!(
1
                assignment.container_chains[&1001u32.into()],
1
                vec![CHARLIE.into(), DAVE.into()]
            );
1
            let s = ParaHeaderSproofBuilderItem {
1
                para_id: other_para,
1
                author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::<u32, BlakeTwo256> {
1
                    parent_hash: Default::default(),
1
                    number: 1,
1
                    state_root: Default::default(),
1
                    extrinsics_root: Default::default(),
1
                    digest: sp_runtime::generic::Digest {
1
                        logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())],
1
                    },
1
                }),
1
            };
1
            sproof.items.push(s);
1
            set_author_noting_inherent_data(sproof);
1
            assert_eq!(
1
                AuthorNoting::latest_author(other_para),
1
                Some(ContainerChainBlockInfo {
1
                    block_number: 1,
1
                    author: AccountId::from(DAVE),
1
                    latest_slot_number: 0.into(),
1
                })
            );
1
        });
1
}
#[test]
1
fn test_author_noting_set_author_and_kill_author_data() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
1
            let other_para: ParaId = 1001u32.into();
1
            assert_ok!(AuthorNoting::set_author(
1
                root_origin(),
1
                other_para,
                1,
1
                AccountId::from(DAVE),
1
                1.into()
            ));
1
            assert_eq!(
1
                AuthorNoting::latest_author(other_para),
1
                Some(ContainerChainBlockInfo {
1
                    block_number: 1,
1
                    author: AccountId::from(DAVE),
1
                    latest_slot_number: 1.into(),
1
                })
            );
1
            assert_ok!(AuthorNoting::kill_author_data(root_origin(), other_para));
1
            assert_eq!(AuthorNoting::latest_author(other_para), None);
1
        });
1
}
#[test]
1
fn test_author_noting_set_author_and_kill_author_data_bad_origin() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
1
            let other_para: ParaId = 1001u32.into();
1
            assert_noop!(
1
                AuthorNoting::set_author(
1
                    origin_of(ALICE.into()),
1
                    other_para,
                    1,
1
                    AccountId::from(DAVE),
1
                    1.into()
                ),
1
                BadOrigin
            );
1
            assert_noop!(
1
                AuthorNoting::kill_author_data(origin_of(ALICE.into()), other_para),
1
                BadOrigin
            );
1
        });
1
}
#[test]
1
fn test_author_noting_runtime_api() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
1
            let mut sproof = ParaHeaderSproofBuilder::default();
1
            let slot: u64 = 5;
1
            let other_para: ParaId = 1001u32.into();
            // Charlie and Dave to 1001
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert_eq!(
1
                assignment.container_chains[&1001u32.into()],
1
                vec![CHARLIE.into(), DAVE.into()]
            );
1
            let s = ParaHeaderSproofBuilderItem {
1
                para_id: other_para,
1
                author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::<u32, BlakeTwo256> {
1
                    parent_hash: Default::default(),
1
                    number: 1,
1
                    state_root: Default::default(),
1
                    extrinsics_root: Default::default(),
1
                    digest: sp_runtime::generic::Digest {
1
                        logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())],
1
                    },
1
                }),
1
            };
1
            sproof.items.push(s);
1
            set_author_noting_inherent_data(sproof);
1
            assert_eq!(
1
                AuthorNoting::latest_author(other_para),
1
                Some(ContainerChainBlockInfo {
1
                    block_number: 1,
1
                    author: AccountId::from(DAVE),
1
                    latest_slot_number: 0.into(),
1
                })
            );
1
            assert_eq!(
1
                Runtime::latest_author(other_para),
1
                Some(AccountId::from(DAVE))
            );
1
            assert_eq!(Runtime::latest_block_number(other_para), Some(1));
1
        });
1
}
#[test]
1
fn test_collator_assignment_rotation() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
            // Charlie and Dave to 1001
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            let initial_assignment = assignment.clone();
1
            assert_eq!(
1
                assignment.container_chains[&1001u32.into()],
1
                vec![CHARLIE.into(), DAVE.into()]
            );
1
            let rotation_period = Configuration::config().full_rotation_period;
1
            run_to_session(rotation_period - 2);
1
            set_parachain_inherent_data_random_seed([1; 32]);
1
            run_block();
1
            assert!(CollatorAssignment::pending_collator_container_chain().is_none());
1
            run_to_session(rotation_period - 1);
1
            assert_eq!(
1
                CollatorAssignment::collator_container_chain(),
                initial_assignment,
            );
1
            assert!(CollatorAssignment::pending_collator_container_chain().is_some());
1
            run_to_session(rotation_period);
            // Assignment changed
1
            assert_ne!(
1
                CollatorAssignment::collator_container_chain(),
                initial_assignment,
            );
1
        });
1
}
#[test]
1
fn session_keys_key_type_id() {
1
    assert_eq!(
1
        dancebox_runtime::SessionKeys::key_ids(),
1
        vec![NIMBUS_KEY_ID]
    );
1
}
#[test]
1
fn test_session_keys_with_authority_mapping() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
1
            let key_mapping_session_0 = AuthorityMapping::authority_id_mapping(0).unwrap();
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
1
            let bob_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
1
            let alice_id_2 = get_aura_id_from_seed("ALICE2");
1
            let bob_id_2 = get_aura_id_from_seed("BOB2");
1
            assert_eq!(key_mapping_session_0.len(), 2);
1
            assert_eq!(key_mapping_session_0.get(&alice_id), Some(&ALICE.into()));
1
            assert_eq!(key_mapping_session_0.get(&bob_id), Some(&BOB.into()));
            // Everything should match to aura
1
            assert_eq!(authorities(), vec![alice_id.clone(), bob_id.clone()]);
            // Change Alice and Bob keys to something different
            // for now lets change it to alice_2 and bob_2
1
            assert_ok!(Session::set_keys(
1
                origin_of(ALICE.into()),
1
                dancebox_runtime::SessionKeys {
1
                    nimbus: alice_id_2.clone(),
1
                },
1
                vec![]
            ));
1
            assert_ok!(Session::set_keys(
1
                origin_of(BOB.into()),
1
                dancebox_runtime::SessionKeys {
1
                    nimbus: bob_id_2.clone(),
1
                },
1
                vec![]
            ));
1
            run_to_session(1u32);
1
            let key_mapping_session_0 = AuthorityMapping::authority_id_mapping(0).unwrap();
1
            assert_eq!(key_mapping_session_0.len(), 2);
1
            assert_eq!(key_mapping_session_0.get(&alice_id), Some(&ALICE.into()));
1
            assert_eq!(key_mapping_session_0.get(&bob_id), Some(&BOB.into()));
1
            let key_mapping_session_1 = AuthorityMapping::authority_id_mapping(1).unwrap();
1
            assert_eq!(key_mapping_session_1.len(), 2);
1
            assert_eq!(key_mapping_session_1.get(&alice_id), Some(&ALICE.into()));
1
            assert_eq!(key_mapping_session_1.get(&bob_id), Some(&BOB.into()));
            // Everything should match to aura
1
            assert_eq!(authorities(), vec![alice_id.clone(), bob_id.clone()]);
            //
1
            run_to_session(2u32);
1
            assert!(AuthorityMapping::authority_id_mapping(0).is_none());
1
            let key_mapping_session_1 = AuthorityMapping::authority_id_mapping(1).unwrap();
1
            assert_eq!(key_mapping_session_1.len(), 2);
1
            assert_eq!(key_mapping_session_1.get(&alice_id), Some(&ALICE.into()));
1
            assert_eq!(key_mapping_session_1.get(&bob_id), Some(&BOB.into()));
1
            let key_mapping_session_2 = AuthorityMapping::authority_id_mapping(2).unwrap();
1
            assert_eq!(key_mapping_session_2.len(), 2);
1
            assert_eq!(key_mapping_session_2.get(&alice_id_2), Some(&ALICE.into()));
1
            assert_eq!(key_mapping_session_2.get(&bob_id_2), Some(&BOB.into()));
            // Everything should match to aura
1
            assert_eq!(authorities(), vec![alice_id_2, bob_id_2]);
1
        });
1
}
#[test]
1
fn test_session_keys_with_authority_assignment() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
1
            let bob_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
1
            let alice_id_2 = get_aura_id_from_seed("ALICE2");
1
            let bob_id_2 = get_aura_id_from_seed("BOB2");
1
            let key_mapping_session_0 = AuthorityAssignment::collator_container_chain(0).unwrap();
1
            assert_eq!(
                key_mapping_session_0.orchestrator_chain,
1
                vec![alice_id.clone(), bob_id.clone()],
            );
1
            assert_eq!(
1
                CollatorAssignment::collator_container_chain().orchestrator_chain,
1
                vec![AccountId::from(ALICE), AccountId::from(BOB)],
            );
1
            let key_mapping_session_1 = AuthorityAssignment::collator_container_chain(1).unwrap();
1
            assert_eq!(key_mapping_session_1, key_mapping_session_0,);
1
            let old_assignment_session_1 =
1
                CollatorAssignment::pending_collator_container_chain().unwrap();
1
            assert_eq!(
                old_assignment_session_1,
1
                CollatorAssignment::collator_container_chain(),
            );
1
            let key_mapping_session_2 = AuthorityAssignment::collator_container_chain(2);
1
            assert!(key_mapping_session_2.is_none());
            // Everything should match to aura
1
            assert_eq!(authorities(), vec![alice_id.clone(), bob_id.clone()]);
            // Change Alice and Bob keys to something different
            // for now lets change it to alice_2 and bob_2
1
            assert_ok!(Session::set_keys(
1
                origin_of(ALICE.into()),
1
                dancebox_runtime::SessionKeys {
1
                    nimbus: alice_id_2.clone(),
1
                },
1
                vec![]
            ));
1
            assert_ok!(Session::set_keys(
1
                origin_of(BOB.into()),
1
                dancebox_runtime::SessionKeys {
1
                    nimbus: bob_id_2.clone(),
1
                },
1
                vec![]
            ));
1
            run_to_session(1u32);
1
            let old_key_mapping_session_1 = key_mapping_session_1;
            // Session 0 got removed
1
            let key_mapping_session_0 = AuthorityAssignment::collator_container_chain(0);
1
            assert!(key_mapping_session_0.is_none());
            // The values at session 1 did not change
1
            let key_mapping_session_1 = AuthorityAssignment::collator_container_chain(1).unwrap();
1
            assert_eq!(key_mapping_session_1, old_key_mapping_session_1,);
1
            assert_eq!(
1
                CollatorAssignment::collator_container_chain(),
                old_assignment_session_1,
            );
            // Session 2 uses the new keys
1
            let key_mapping_session_2 = AuthorityAssignment::collator_container_chain(2).unwrap();
1
            assert_eq!(
                key_mapping_session_2.orchestrator_chain,
1
                vec![alice_id_2.clone(), bob_id_2.clone()],
            );
1
            assert_eq!(CollatorAssignment::pending_collator_container_chain(), None);
1
            let key_mapping_session_3 = AuthorityAssignment::collator_container_chain(3);
1
            assert!(key_mapping_session_3.is_none());
            // Everything should match to aura
1
            assert_eq!(authorities(), vec![alice_id, bob_id]);
1
            run_to_session(2u32);
            // Session 1 got removed
1
            let key_mapping_session_1 = AuthorityAssignment::collator_container_chain(1);
1
            assert!(key_mapping_session_1.is_none());
            // Session 2 uses the new keys
1
            let key_mapping_session_2 = AuthorityAssignment::collator_container_chain(2).unwrap();
1
            assert_eq!(
                key_mapping_session_2.orchestrator_chain,
1
                vec![alice_id_2.clone(), bob_id_2.clone()],
            );
1
            assert_eq!(
                old_assignment_session_1,
1
                CollatorAssignment::collator_container_chain(),
            );
            // Session 3 uses the new keys
1
            let key_mapping_session_3 = AuthorityAssignment::collator_container_chain(3).unwrap();
1
            assert_eq!(
                key_mapping_session_3.orchestrator_chain,
1
                vec![alice_id_2.clone(), bob_id_2.clone()],
            );
1
            assert_eq!(CollatorAssignment::pending_collator_container_chain(), None);
1
            let key_mapping_session_4 = AuthorityAssignment::collator_container_chain(4);
1
            assert!(key_mapping_session_4.is_none());
            // Everything should match to aura
1
            assert_eq!(authorities(), vec![alice_id_2, bob_id_2]);
1
        });
1
}
2
fn call_transfer(
2
    dest: sp_runtime::MultiAddress<sp_runtime::AccountId32, ()>,
2
    value: u128,
2
) -> RuntimeCall {
2
    RuntimeCall::Balances(pallet_balances::Call::transfer_allow_death { dest, value })
2
}
#[test]
1
fn test_proxy_any() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
1
            let delay = 0;
1
            assert_ok!(Proxy::add_proxy(
1
                origin_of(ALICE.into()),
1
                AccountId::from(BOB).into(),
1
                ProxyType::Any,
1
                delay
            ));
1
            let balance_before = System::account(AccountId::from(BOB)).data.free;
1
            let call = Box::new(call_transfer(AccountId::from(BOB).into(), 200_000));
1
            assert_ok!(Proxy::proxy(
1
                origin_of(BOB.into()),
1
                AccountId::from(ALICE).into(),
1
                None,
1
                call
            ));
1
            let balance_after = System::account(AccountId::from(BOB)).data.free;
1
            assert_eq!(balance_after, balance_before + 200_000);
1
        });
1
}
#[test]
1
fn test_proxy_non_transfer() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
1
            let delay = 0;
1
            assert_ok!(Proxy::add_proxy(
1
                origin_of(ALICE.into()),
1
                AccountId::from(BOB).into(),
1
                ProxyType::NonTransfer,
1
                delay
            ));
1
            let balance_before = System::account(AccountId::from(BOB)).data.free;
1
            let call = Box::new(call_transfer(AccountId::from(BOB).into(), 200_000));
            // The extrinsic succeeds but the call is filtered, so no transfer is actually done
1
            assert_ok!(Proxy::proxy(
1
                origin_of(BOB.into()),
1
                AccountId::from(ALICE).into(),
1
                None,
1
                call
            ));
1
            let balance_after = System::account(AccountId::from(BOB)).data.free;
1
            assert_eq!(balance_after, balance_before);
1
        });
1
}
#[test]
1
fn test_proxy_utility() {
    // All proxy types should be able to use Utility pallet, but we ensure
    // subcalls don't allow to circumvent filters.
    // Dummy match to ensure we update this test when adding new proxy types.
1
    match ProxyType::Any {
        ProxyType::Any
        | ProxyType::NonTransfer
        | ProxyType::Governance
        | ProxyType::Staking
        | ProxyType::CancelProxy
        | ProxyType::Balances
        | ProxyType::Registrar
        | ProxyType::SudoRegistrar
1
        | ProxyType::SessionKeyManagement => (),
    };
    // All except for any
1
    let proxy_types = &[
1
        ProxyType::NonTransfer,
1
        ProxyType::Governance,
1
        ProxyType::Staking,
1
        ProxyType::CancelProxy,
1
        ProxyType::Balances,
1
        ProxyType::Registrar,
1
        ProxyType::SudoRegistrar,
1
        ProxyType::SessionKeyManagement,
1
    ];
9
    for &proxy_type in proxy_types {
8
        ExtBuilder::default()
8
            .with_balances(vec![
8
                // Alice gets 10k extra tokens for her mapping deposit
8
                (AccountId::from(ALICE), 210_000 * UNIT),
8
                (AccountId::from(BOB), 100_000 * UNIT),
8
                (AccountId::from(CHARLIE), 100_000 * UNIT),
8
                (AccountId::from(DAVE), 100_000 * UNIT),
8
            ])
8
            .with_collators(vec![
8
                (AccountId::from(ALICE), 210 * UNIT),
8
                (AccountId::from(BOB), 100 * UNIT),
8
            ])
8
            .with_sudo(AccountId::from(ALICE))
8
            .build()
8
            .execute_with(|| {
8
                assert_ok!(Proxy::add_proxy(
8
                    origin_of(ALICE.into()),
8
                    AccountId::from(BOB).into(),
8
                    proxy_type,
                    0
                ));
8
                let free_balance = Balances::free_balance(AccountId::from(BOB));
8
                assert_ok!(Proxy::proxy(
8
                    origin_of(BOB.into()),
8
                    AccountId::from(ALICE).into(),
8
                    None,
8
                    Box::new(
8
                        pallet_sudo::Call::sudo {
8
                            call: Box::new(
8
                                pallet_utility::Call::batch {
8
                                    calls: vec![pallet_balances::Call::force_set_balance {
8
                                        who: AccountId::from(BOB).into(),
8
                                        new_free: 42424242424242
8
                                    }
8
                                    .into()]
8
                                }
8
                                .into()
8
                            )
8
                        }
8
                        .into()
                    )
                ));
8
                assert_eq!(Balances::free_balance(AccountId::from(BOB)), free_balance);
8
            });
    }
1
}
#[test]
1
fn check_well_known_keys() {
    use frame_support::traits::PalletInfo;
    // Pallet is named "Paras" in Polkadot.
1
    assert_eq!(
        well_known_keys::PARAS_HEADS_INDEX,
1
        frame_support::storage::storage_prefix(b"Paras", b"Heads")
    );
    // Tanssi storage. Since we cannot access the storages themselves,
    // we test the pallet prefix matches and then compute manually the full prefix.
1
    assert_eq!(
1
        dancebox_runtime::PalletInfo::name::<AuthorityAssignment>(),
        Some("AuthorityAssignment")
    );
1
    assert_eq!(
        well_known_keys::AUTHORITY_ASSIGNMENT_PREFIX,
1
        frame_support::storage::storage_prefix(b"AuthorityAssignment", b"CollatorContainerChain")
    );
1
    assert_eq!(
1
        dancebox_runtime::PalletInfo::name::<Session>(),
        Some("Session")
    );
1
    assert_eq!(
        well_known_keys::SESSION_INDEX,
1
        frame_support::storage::storage_prefix(b"Session", b"CurrentIndex")
    );
1
}
#[test]
1
fn test_staking_no_candidates_in_genesis() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
1
            let initial_candidates =
1
                pallet_pooled_staking::SortedEligibleCandidates::<Runtime>::get().to_vec();
1
            assert_eq!(initial_candidates, vec![]);
1
        });
1
}
#[test]
1
fn test_staking_join() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
1
            let balance_before = System::account(AccountId::from(ALICE)).data.free;
1
            assert_eq!(System::account(AccountId::from(ALICE)).data.reserved, 0);
1
            let stake = MinimumSelfDelegation::get() * 10;
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(ALICE.into()),
1
                ALICE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                stake
            ));
            // Immediately after joining, Alice is the top candidate
1
            let eligible_candidates =
1
                pallet_pooled_staking::SortedEligibleCandidates::<Runtime>::get().to_vec();
1
            assert_eq!(
                eligible_candidates,
1
                vec![EligibleCandidate {
1
                    candidate: ALICE.into(),
1
                    stake
1
                }]
            );
            // And staked amount is immediately marked as "reserved"
1
            let balance_after = System::account(AccountId::from(ALICE)).data.free;
1
            assert_eq!(balance_before - balance_after, stake);
1
            assert_eq!(System::account(AccountId::from(ALICE)).data.reserved, stake);
1
        });
1
}
#[test]
1
fn test_staking_join_no_keys_registered() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![
1
            1001,
1
            1002,
1
        ])
1

            
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
1
            let stake = MinimumSelfDelegation::get() * 10;
1
            let new_account = AccountId::from([42u8; 32]);
1
            assert_ok!(Balances::transfer_allow_death(
1
                origin_of(ALICE.into()),
1
                new_account.clone().into(),
1
                stake * 2
            ));
1
            let balance_before = System::account(new_account.clone()).data.free;
1
            assert_eq!(System::account(new_account.clone()).data.reserved, 0);
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(new_account.clone()),
1
                new_account.clone(),
1
                ActivePoolKind::AutoCompounding,
1
                stake
            ));
            // The new account should be the top candidate but it has no keys registered in
            // pallet_session, so it is not eligible
1
            assert!(!<Runtime as pallet_pooled_staking::Config>::EligibleCandidatesFilter::is_candidate_eligible(&new_account));
1
            let eligible_candidates =
1
                pallet_pooled_staking::SortedEligibleCandidates::<Runtime>::get().to_vec();
1
            assert_eq!(eligible_candidates, vec![]);
            // And staked amount is immediately marked as "reserved"
1
            let balance_after = System::account(new_account.clone()).data.free;
1
            assert_eq!(balance_before - balance_after, stake);
1
            assert_eq!(System::account(new_account.clone()).data.reserved, stake);
1
        });
1
}
#[test]
1
fn test_staking_register_keys_after_joining() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![
1
            1001,
1
            1002,
1
        ])
1

            
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
1
            let stake = MinimumSelfDelegation::get() * 10;
1
            let new_account = AccountId::from([42u8; 32]);
1
            assert_ok!(Balances::transfer_allow_death(
1
                origin_of(ALICE.into()),
1
                new_account.clone().into(),
1
                stake * 2
            ));
1
            let balance_before = System::account(new_account.clone()).data.free;
1
            assert_eq!(System::account(new_account.clone()).data.reserved, 0);
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(new_account.clone()),
1
                new_account.clone(),
1
                ActivePoolKind::AutoCompounding,
1
                stake
            ));
            // The new account should be the top candidate but it has no keys registered in
            // pallet_session, so it is not eligible
1
            assert!(!<Runtime as pallet_pooled_staking::Config>::EligibleCandidatesFilter::is_candidate_eligible(&new_account));
1
            let eligible_candidates =
1
                pallet_pooled_staking::SortedEligibleCandidates::<Runtime>::get().to_vec();
1
            assert_eq!(eligible_candidates, vec![]);
            // And staked amount is immediately marked as "reserved"
1
            let balance_after = System::account(new_account.clone()).data.free;
1
            assert_eq!(balance_before - balance_after, stake);
1
            assert_eq!(System::account(new_account.clone()).data.reserved, stake);
            // Now register the keys
1
            let new_account_id = get_aura_id_from_seed(&new_account.to_string());
1
            assert_ok!(Session::set_keys(
1
                origin_of(new_account.clone()),
1
                dancebox_runtime::SessionKeys {
1
                    nimbus: new_account_id,
1
                },
1
                vec![]
            ));
            // Now eligible according to filter
1
            assert!(<Runtime as pallet_pooled_staking::Config>::EligibleCandidatesFilter::is_candidate_eligible(&new_account));
            // But not eligible according to pallet_pooled_staking, need to manually update candidate list
1
            let eligible_candidates =
1
                pallet_pooled_staking::SortedEligibleCandidates::<Runtime>::get().to_vec();
1
            assert_eq!(eligible_candidates, vec![]);
            // Update candidate list
1
            assert_ok!(PooledStaking::update_candidate_position(
1
                origin_of(BOB.into()),
1
                vec![new_account.clone()]
            ));
            // Now it is eligible
1
            let eligible_candidates =
1
                pallet_pooled_staking::SortedEligibleCandidates::<Runtime>::get().to_vec();
1
            assert_eq!(
                eligible_candidates,
1
                vec![EligibleCandidate {
1
                    candidate: new_account.clone(),
1
                    stake
1
                }]
            );
1
        });
1
}
#[test]
1
fn test_staking_join_bad_origin() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
1
            let stake = 10 * MinimumSelfDelegation::get();
1
            assert_noop!(
1
                PooledStaking::request_delegate(
1
                    root_origin(),
1
                    ALICE.into(),
1
                    ActivePoolKind::AutoCompounding,
1
                    stake
                ),
1
                BadOrigin,
            );
1
        });
1
}
#[test]
1
fn test_staking_join_below_self_delegation_min() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
1
            let stake1 = MinimumSelfDelegation::get() / 3;
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(ALICE.into()),
1
                ALICE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                stake1
            ));
            // Since stake is below MinimumSelfDelegation, the join operation succeeds
            // but the candidate is not eligible
1
            let eligible_candidates =
1
                pallet_pooled_staking::SortedEligibleCandidates::<Runtime>::get().to_vec();
1
            assert_eq!(eligible_candidates, vec![],);
1
            let stake2 = MinimumSelfDelegation::get() - stake1 - 1;
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(ALICE.into()),
1
                ALICE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                stake2,
            ));
            // Still below, missing 1 unit
1
            let eligible_candidates =
1
                pallet_pooled_staking::SortedEligibleCandidates::<Runtime>::get().to_vec();
1
            assert_eq!(eligible_candidates, vec![],);
1
            let stake3 = 1;
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(ALICE.into()),
1
                ALICE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                stake3,
            ));
            // Increasing the stake to above MinimumSelfDelegation makes the candidate eligible
1
            let eligible_candidates =
1
                pallet_pooled_staking::SortedEligibleCandidates::<Runtime>::get().to_vec();
1
            assert_eq!(
                eligible_candidates,
1
                vec![EligibleCandidate {
1
                    candidate: ALICE.into(),
1
                    stake: stake1 + stake2 + stake3
1
                }],
            );
1
        });
1
}
#[test]
1
fn test_staking_join_no_self_delegation() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000_000_000 * UNIT),
1
            (AccountId::from(BOB), 100_000_000_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000_000_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000_000_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210_000_000 * UNIT),
1
            (AccountId::from(BOB), 100_000_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000_000 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
            // Bob delegates to Alice, but Alice is not a valid candidate (not enough self-delegation)
1
            let stake = 10 * MinimumSelfDelegation::get();
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(BOB.into()),
1
                ALICE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                stake,
            ));
1
            let eligible_candidates =
1
                pallet_pooled_staking::SortedEligibleCandidates::<Runtime>::get().to_vec();
1
            assert_eq!(eligible_candidates, vec![],);
1
        });
1
}
#[test]
1
fn test_staking_join_before_self_delegation() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000_000_000 * UNIT),
1
            (AccountId::from(BOB), 100_000_000_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000_000_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000_000_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210_000_000 * UNIT),
1
            (AccountId::from(BOB), 100_000_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000_000 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
            // Bob delegates to Alice, but Alice is not a valid candidate (not enough self-delegation)
1
            let stake = 10 * MinimumSelfDelegation::get();
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(BOB.into()),
1
                ALICE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                stake
            ));
1
            let eligible_candidates =
1
                pallet_pooled_staking::SortedEligibleCandidates::<Runtime>::get().to_vec();
1
            assert_eq!(eligible_candidates, vec![],);
1
            run_to_session(2);
1
            assert_ok!(PooledStaking::execute_pending_operations(
1
                origin_of(ALICE.into()),
1
                vec![PendingOperationQuery {
1
                    delegator: BOB.into(),
1
                    operation: PendingOperationKey::JoiningAutoCompounding {
1
                        candidate: ALICE.into(),
1
                        at: 0,
1
                    }
1
                }]
            ),);
            // Now Alice joins with enough self-delegation
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(ALICE.into()),
1
                ALICE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                stake
            ));
            // Alice is a valid candidate, and Bob's stake is also counted
1
            let eligible_candidates =
1
                pallet_pooled_staking::SortedEligibleCandidates::<Runtime>::get().to_vec();
1
            assert_eq!(
                eligible_candidates,
1
                vec![EligibleCandidate {
1
                    candidate: ALICE.into(),
1
                    stake: stake * 2,
1
                }],
            );
1
        });
1
}
#[test]
1
fn test_staking_join_twice_in_same_block() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
1
            let stake1 = 10 * MinimumSelfDelegation::get();
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(ALICE.into()),
1
                ALICE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                stake1
            ));
1
            let stake2 = 9 * MinimumSelfDelegation::get();
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(ALICE.into()),
1
                ALICE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                stake2
            ));
            // Both operations succeed and the total stake is the sum of the individual stakes
1
            let eligible_candidates =
1
                pallet_pooled_staking::SortedEligibleCandidates::<Runtime>::get().to_vec();
1
            assert_eq!(
                eligible_candidates,
1
                vec![EligibleCandidate {
1
                    candidate: ALICE.into(),
1
                    stake: stake1 + stake2,
1
                }]
            );
1
            run_to_session(2);
1
            assert_ok!(PooledStaking::execute_pending_operations(
1
                origin_of(ALICE.into()),
1
                vec![PendingOperationQuery {
1
                    delegator: ALICE.into(),
1
                    operation: PendingOperationKey::JoiningAutoCompounding {
1
                        candidate: ALICE.into(),
1
                        at: 0,
1
                    }
1
                }]
            ),);
            // TODO: ensure the total stake has been moved to auto compounding pool
1
        });
1
}
#[test]
1
fn test_staking_join_execute_before_time() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
1
            let stake = 10 * MinimumSelfDelegation::get();
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(ALICE.into()),
1
                ALICE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                stake
            ));
            // Immediately after joining, Alice is the top candidate
1
            let eligible_candidates =
1
                pallet_pooled_staking::SortedEligibleCandidates::<Runtime>::get().to_vec();
1
            assert_eq!(
                eligible_candidates,
1
                vec![EligibleCandidate {
1
                    candidate: ALICE.into(),
1
                    stake
1
                }]
            );
            // We called request_delegate in session 0, we will be able to execute it starting from session 2
1
            let start_of_session_2 = session_to_block(2);
            // Session 2 starts at block 600, but run_to_session runs to block 601, so subtract 2 here to go to 599
1
            run_to_block(start_of_session_2 - 2);
1
            assert_noop!(
1
                PooledStaking::execute_pending_operations(
1
                    origin_of(ALICE.into()),
1
                    vec![PendingOperationQuery {
1
                        delegator: ALICE.into(),
1
                        operation: PendingOperationKey::JoiningAutoCompounding {
1
                            candidate: ALICE.into(),
1
                            at: 0,
1
                        }
1
                    }]
                ),
1
                pallet_pooled_staking::Error::<Runtime>::RequestCannotBeExecuted(0),
            );
1
            run_to_block(start_of_session_2);
1
            assert_ok!(PooledStaking::execute_pending_operations(
1
                origin_of(ALICE.into()),
1
                vec![PendingOperationQuery {
1
                    delegator: ALICE.into(),
1
                    operation: PendingOperationKey::JoiningAutoCompounding {
1
                        candidate: ALICE.into(),
1
                        at: 0,
1
                    }
1
                }]
            ),);
1
        });
1
}
#[test]
1
fn test_staking_join_execute_any_origin() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
1
            let stake = 10 * MinimumSelfDelegation::get();
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(ALICE.into()),
1
                ALICE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                stake
            ));
            // Immediately after joining, Alice is the top candidate
1
            let eligible_candidates =
1
                pallet_pooled_staking::SortedEligibleCandidates::<Runtime>::get().to_vec();
1
            assert_eq!(
                eligible_candidates,
1
                vec![EligibleCandidate {
1
                    candidate: ALICE.into(),
1
                    stake
1
                }]
            );
            // We called request_delegate in session 0, we will be able to execute it starting from session 2
1
            run_to_session(2);
            // Anyone can execute pending operations for anyone else
1
            assert_ok!(PooledStaking::execute_pending_operations(
1
                origin_of(BOB.into()),
1
                vec![PendingOperationQuery {
1
                    delegator: ALICE.into(),
1
                    operation: PendingOperationKey::JoiningAutoCompounding {
1
                        candidate: ALICE.into(),
1
                        at: 0,
1
                    }
1
                }]
            ),);
1
        });
1
}
#[test]
1
fn test_staking_join_execute_bad_origin() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
1
            let stake = 10 * MinimumSelfDelegation::get();
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(ALICE.into()),
1
                ALICE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                stake
            ));
            // Immediately after joining, Alice is the top candidate
1
            let eligible_candidates =
1
                pallet_pooled_staking::SortedEligibleCandidates::<Runtime>::get().to_vec();
1
            assert_eq!(
                eligible_candidates,
1
                vec![EligibleCandidate {
1
                    candidate: ALICE.into(),
1
                    stake
1
                }]
            );
            // We called request_delegate in session 0, we will be able to execute it starting from session 2
1
            run_to_session(2);
1
            assert_noop!(
1
                PooledStaking::execute_pending_operations(
1
                    root_origin(),
1
                    vec![PendingOperationQuery {
1
                        delegator: ALICE.into(),
1
                        operation: PendingOperationKey::JoiningAutoCompounding {
1
                            candidate: ALICE.into(),
1
                            at: 0,
1
                        }
1
                    }]
                ),
1
                BadOrigin,
            );
1
        });
1
}
struct A {
    delegator: AccountId,
    candidate: AccountId,
    target_pool: ActivePoolKind,
    stake: u128,
}
// Setup test environment with provided delegations already being executed. Input function f gets executed at start session 2
9
fn setup_staking_join_and_execute<R>(ops: Vec<A>, f: impl FnOnce() -> R) {
9
    ExtBuilder::default()
9
        .with_balances(vec![
9
            // Alice gets 10k extra tokens for her mapping deposit
9
            (AccountId::from(ALICE), 210_000 * UNIT),
9
            (AccountId::from(BOB), 100_000 * UNIT),
9
            (AccountId::from(CHARLIE), 100_000 * UNIT),
9
            (AccountId::from(DAVE), 100_000 * UNIT),
9
        ])
9
        .with_collators(vec![
9
            (AccountId::from(ALICE), 210 * UNIT),
9
            (AccountId::from(BOB), 100 * UNIT),
9
            (AccountId::from(CHARLIE), 100 * UNIT),
9
            (AccountId::from(DAVE), 100 * UNIT),
9
        ])
9
        .with_empty_parachains(vec![1001, 1002])
9
        .build()
9
        .execute_with(|| {
9
            run_to_block(2);
9
            for op in ops.iter() {
9
                assert_ok!(PooledStaking::request_delegate(
9
                    origin_of(op.delegator.clone()),
9
                    op.candidate.clone(),
9
                    op.target_pool,
9
                    op.stake,
                ));
            }
            // We called request_delegate in session 0, we will be able to execute it starting from session 2
9
            run_to_session(2);
9
            for op in ops.iter() {
9
                let operation = match op.target_pool {
                    ActivePoolKind::AutoCompounding => {
9
                        PendingOperationKey::JoiningAutoCompounding {
9
                            candidate: op.candidate.clone(),
9
                            at: 0,
9
                        }
                    }
                    ActivePoolKind::ManualRewards => PendingOperationKey::JoiningManualRewards {
                        candidate: op.candidate.clone(),
                        at: 0,
                    },
                };
9
                assert_ok!(PooledStaking::execute_pending_operations(
9
                    origin_of(op.delegator.clone()),
9
                    vec![PendingOperationQuery {
9
                        delegator: op.delegator.clone(),
9
                        operation,
9
                    }]
                ));
            }
9
            f()
9
        });
9
}
#[test]
1
fn test_staking_leave_exact_amount() {
1
    setup_staking_join_and_execute(
1
        vec![A {
1
            delegator: ALICE.into(),
1
            candidate: ALICE.into(),
1
            target_pool: ActivePoolKind::AutoCompounding,
1
            stake: 10 * MinimumSelfDelegation::get(),
1
        }],
1
        || {
1
            let stake = 10 * MinimumSelfDelegation::get();
1
            assert_ok!(PooledStaking::request_undelegate(
1
                origin_of(ALICE.into()),
1
                ALICE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                SharesOrStake::Stake(stake),
            ));
            // Immediately after calling request_undelegate, Alice is no longer a candidate
1
            let eligible_candidates =
1
                pallet_pooled_staking::SortedEligibleCandidates::<Runtime>::get().to_vec();
1
            assert_eq!(eligible_candidates, vec![]);
1
        },
    )
1
}
#[test]
1
fn test_staking_leave_bad_origin() {
1
    setup_staking_join_and_execute(
1
        vec![A {
1
            delegator: ALICE.into(),
1
            candidate: ALICE.into(),
1
            target_pool: ActivePoolKind::AutoCompounding,
1
            stake: 10 * MinimumSelfDelegation::get(),
1
        }],
1
        || {
1
            let stake = 10 * MinimumSelfDelegation::get();
1
            assert_noop!(
1
                PooledStaking::request_undelegate(
1
                    root_origin(),
1
                    ALICE.into(),
1
                    ActivePoolKind::AutoCompounding,
1
                    SharesOrStake::Stake(stake),
                ),
1
                BadOrigin
            );
1
        },
    )
1
}
#[test]
1
fn test_staking_leave_more_than_allowed() {
1
    setup_staking_join_and_execute(
1
        vec![A {
1
            delegator: ALICE.into(),
1
            candidate: ALICE.into(),
1
            target_pool: ActivePoolKind::AutoCompounding,
1
            stake: 10 * MinimumSelfDelegation::get(),
1
        }],
1
        || {
1
            let stake = 10 * MinimumSelfDelegation::get();
1
            assert_noop!(
1
                PooledStaking::request_undelegate(
1
                    origin_of(ALICE.into()),
1
                    ALICE.into(),
1
                    ActivePoolKind::AutoCompounding,
1
                    SharesOrStake::Stake(stake + 1 * MinimumSelfDelegation::get()),
                ),
1
                pallet_pooled_staking::Error::<Runtime>::MathUnderflow,
            );
1
        },
    );
1
}
#[test]
1
fn test_staking_leave_in_separate_transactions() {
1
    let stake = 10 * MinimumSelfDelegation::get();
1
    setup_staking_join_and_execute(
1
        vec![A {
1
            delegator: ALICE.into(),
1
            candidate: ALICE.into(),
1
            target_pool: ActivePoolKind::AutoCompounding,
1
            stake,
1
        }],
1
        || {
1
            let half_stake = stake / 2;
1
            assert_ok!(PooledStaking::request_undelegate(
1
                origin_of(ALICE.into()),
1
                ALICE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                SharesOrStake::Stake(half_stake),
            ));
            // Alice is still a valid candidate, now with less stake
1
            let eligible_candidates =
1
                pallet_pooled_staking::SortedEligibleCandidates::<Runtime>::get().to_vec();
1
            let remaining_stake = stake - half_stake;
1
            assert_eq!(
                eligible_candidates,
1
                vec![EligibleCandidate {
1
                    candidate: ALICE.into(),
1
                    stake: remaining_stake,
1
                }],
            );
1
            assert_ok!(PooledStaking::request_undelegate(
1
                origin_of(ALICE.into()),
1
                ALICE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                SharesOrStake::Stake(remaining_stake),
            ));
            // Unstaked remaining stake, so no longer a valid candidate
1
            let eligible_candidates =
1
                pallet_pooled_staking::SortedEligibleCandidates::<Runtime>::get().to_vec();
1
            assert_eq!(eligible_candidates, vec![],);
1
        },
    );
1
}
#[test]
1
fn test_staking_leave_all_except_some_dust() {
1
    let stake = 10 * MinimumSelfDelegation::get();
1
    setup_staking_join_and_execute(
1
        vec![A {
1
            delegator: ALICE.into(),
1
            candidate: ALICE.into(),
1
            target_pool: ActivePoolKind::AutoCompounding,
1
            stake,
1
        }],
1
        || {
1
            let dust = MinimumSelfDelegation::get() / 2;
1
            assert_ok!(PooledStaking::request_undelegate(
1
                origin_of(ALICE.into()),
1
                ALICE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                SharesOrStake::Stake(stake - dust),
            ));
            // Alice still has some stake left, but not enough to reach MinimumSelfDelegation
1
            assert_eq!(
1
                pallet_pooled_staking::Pools::<Runtime>::get(
1
                    AccountId::from(ALICE),
1
                    PoolsKey::CandidateTotalStake
                ),
                dust,
            );
1
            let eligible_candidates =
1
                pallet_pooled_staking::SortedEligibleCandidates::<Runtime>::get().to_vec();
1
            assert_eq!(eligible_candidates, vec![],);
            // Leave with remaining stake
1
            assert_ok!(PooledStaking::request_undelegate(
1
                origin_of(ALICE.into()),
1
                ALICE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                SharesOrStake::Stake(dust),
            ));
            // Alice has no more stake left
1
            assert_eq!(
1
                pallet_pooled_staking::Pools::<Runtime>::get(
1
                    AccountId::from(ALICE),
1
                    PoolsKey::CandidateTotalStake
                ),
                0,
            );
1
        },
    );
1
}
#[test]
1
fn test_staking_leave_execute_before_time() {
1
    let stake = 10 * MinimumSelfDelegation::get();
1
    setup_staking_join_and_execute(
1
        vec![A {
1
            delegator: ALICE.into(),
1
            candidate: ALICE.into(),
1
            target_pool: ActivePoolKind::AutoCompounding,
1
            stake,
1
        }],
1
        || {
1
            let balance_before = System::account(AccountId::from(ALICE)).data.free;
1
            let at = Session::current_index();
1
            assert_ok!(PooledStaking::request_undelegate(
1
                origin_of(ALICE.into()),
1
                ALICE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                SharesOrStake::Stake(stake),
            ));
            // Request undelegate does not change account balance
1
            assert_eq!(
                balance_before,
1
                System::account(AccountId::from(ALICE)).data.free
            );
            // We called request_delegate in session 0, we will be able to execute it starting from session 2
1
            let start_of_session_4 = session_to_block(4);
            // Session 4 starts at block 1200, but run_to_session runs to block 1201, so subtract 2 here to go to 1999
1
            run_to_block(start_of_session_4 - 2);
1
            assert_noop!(
1
                PooledStaking::execute_pending_operations(
1
                    origin_of(ALICE.into()),
1
                    vec![PendingOperationQuery {
1
                        delegator: ALICE.into(),
1
                        operation: PendingOperationKey::Leaving {
1
                            candidate: ALICE.into(),
1
                            at,
1
                        }
1
                    }]
                ),
1
                pallet_pooled_staking::Error::<Runtime>::RequestCannotBeExecuted(0)
            );
1
        },
    );
1
}
#[test]
1
fn test_staking_leave_execute_any_origin() {
1
    let stake = 10 * MinimumSelfDelegation::get();
1
    setup_staking_join_and_execute(
1
        vec![A {
1
            delegator: ALICE.into(),
1
            candidate: ALICE.into(),
1
            target_pool: ActivePoolKind::AutoCompounding,
1
            stake,
1
        }],
1
        || {
1
            let balance_before = System::account(AccountId::from(ALICE)).data.free;
1
            let at = Session::current_index();
1
            assert_ok!(PooledStaking::request_undelegate(
1
                origin_of(ALICE.into()),
1
                ALICE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                SharesOrStake::Stake(stake),
            ));
            // Request undelegate does not change account balance
1
            assert_eq!(
                balance_before,
1
                System::account(AccountId::from(ALICE)).data.free
            );
1
            run_to_session(4);
1
            let balance_before = System::account(AccountId::from(ALICE)).data.free;
1
            assert_ok!(PooledStaking::execute_pending_operations(
                // Any signed origin can execute this, the stake will go to Alice account
1
                origin_of(BOB.into()),
1
                vec![PendingOperationQuery {
1
                    delegator: ALICE.into(),
1
                    operation: PendingOperationKey::Leaving {
1
                        candidate: ALICE.into(),
1
                        at,
1
                    }
1
                }]
            ),);
1
            let balance_after = System::account(AccountId::from(ALICE)).data.free;
1
            assert_eq!(balance_after - balance_before, stake);
1
        },
    );
1
}
#[test]
1
fn test_staking_leave_execute_bad_origin() {
1
    let stake = 10 * MinimumSelfDelegation::get();
1
    setup_staking_join_and_execute(
1
        vec![A {
1
            delegator: ALICE.into(),
1
            candidate: ALICE.into(),
1
            target_pool: ActivePoolKind::AutoCompounding,
1
            stake,
1
        }],
1
        || {
1
            let at = Session::current_index();
1
            assert_ok!(PooledStaking::request_undelegate(
1
                origin_of(ALICE.into()),
1
                ALICE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                SharesOrStake::Stake(stake),
            ));
1
            run_to_session(4);
1
            assert_noop!(
1
                PooledStaking::execute_pending_operations(
1
                    root_origin(),
1
                    vec![PendingOperationQuery {
1
                        delegator: ALICE.into(),
1
                        operation: PendingOperationKey::Leaving {
1
                            candidate: ALICE.into(),
1
                            at,
1
                        }
1
                    }]
                ),
1
                BadOrigin
            );
1
        },
    );
1
}
#[test]
1
fn test_staking_swap() {
1
    setup_staking_join_and_execute(
1
        vec![A {
1
            delegator: ALICE.into(),
1
            candidate: ALICE.into(),
1
            target_pool: ActivePoolKind::AutoCompounding,
1
            stake: 10 * MinimumSelfDelegation::get(),
1
        }],
1
        || {
1
            let stake = 10 * MinimumSelfDelegation::get();
1
            assert_ok!(PooledStaking::swap_pool(
1
                origin_of(ALICE.into()),
1
                ALICE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                SharesOrStake::Stake(stake),
            ));
1
            assert_eq!(
1
                PooledStaking::computed_stake(
1
                    ALICE.into(),
1
                    ALICE.into(),
1
                    PoolKind::AutoCompounding
                ),
1
                Some(0u32.into())
            );
1
            assert_eq!(
1
                PooledStaking::computed_stake(ALICE.into(), ALICE.into(), PoolKind::ManualRewards),
1
                Some(stake)
            );
1
            assert_ok!(PooledStaking::swap_pool(
1
                origin_of(ALICE.into()),
1
                ALICE.into(),
1
                ActivePoolKind::ManualRewards,
1
                SharesOrStake::Stake(stake),
            ));
1
            assert_eq!(
1
                PooledStaking::computed_stake(
1
                    ALICE.into(),
1
                    ALICE.into(),
1
                    PoolKind::AutoCompounding
                ),
1
                Some(stake)
            );
1
            assert_eq!(
1
                PooledStaking::computed_stake(ALICE.into(), ALICE.into(), PoolKind::ManualRewards),
1
                Some(0u32.into())
            );
1
        },
    )
1
}
#[test]
1
fn test_pallet_session_takes_validators_from_invulnerables_and_staking() {
    // Alice, Bob, Charlie are invulnerables
    // Alice, Dave are in pallet_staking
    // Expected collators are Alice, Bob, Charlie, Dave
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000_000_000 * UNIT),
1
            (AccountId::from(BOB), 100_000_000_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000_000_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000_000_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210_000_000 * UNIT),
1
            (AccountId::from(BOB), 100_000_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000_000 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
1
            let stake = 10 * MinimumSelfDelegation::get();
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(ALICE.into()),
1
                ALICE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                stake,
            ));
            // Register Dave in pallet_session (invulnerables are automatically registered)
1
            let dave_account_id = get_aura_id_from_seed(&AccountId::from(DAVE).to_string());
1
            assert_ok!(Session::set_keys(
1
                origin_of(DAVE.into()),
1
                dancebox_runtime::SessionKeys {
1
                    nimbus: dave_account_id,
1
                },
1
                vec![]
            ));
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(DAVE.into()),
1
                DAVE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                stake,
            ));
1
            let eligible_candidates =
1
                pallet_pooled_staking::SortedEligibleCandidates::<Runtime>::get().to_vec();
1
            assert_eq!(
                eligible_candidates,
1
                vec![
1
                    EligibleCandidate {
1
                        candidate: ALICE.into(),
1
                        stake
1
                    },
1
                    EligibleCandidate {
1
                        candidate: DAVE.into(),
1
                        stake
1
                    },
                ]
            );
1
            assert_eq!(
1
                pallet_invulnerables::Invulnerables::<Runtime>::get().to_vec(),
1
                vec![
1
                    AccountId::from(ALICE),
1
                    AccountId::from(BOB),
1
                    AccountId::from(CHARLIE),
                ]
            );
            // Need to trigger new session to update pallet_session
1
            run_to_session(2);
1
            assert_eq!(
1
                Session::validators(),
1
                vec![
1
                    AccountId::from(ALICE),
1
                    AccountId::from(BOB),
1
                    AccountId::from(CHARLIE),
1
                    AccountId::from(DAVE),
                ]
            );
1
        });
1
}
#[test]
1
fn test_pallet_session_limits_num_validators() {
    // Set max_collators = 2, now only the first 2 invulnerables are valid collators
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000_000_000 * UNIT),
1
            (AccountId::from(BOB), 100_000_000_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000_000_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000_000_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210_000_000 * UNIT),
1
            (AccountId::from(BOB), 100_000_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000_000 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .with_config(pallet_configuration::HostConfiguration {
1
            max_collators: 2,
1
            min_orchestrator_collators: 2,
1
            max_orchestrator_collators: 2,
1
            collators_per_container: 2,
1
            full_rotation_period: 24,
1
            ..Default::default()
1
        })
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
1
            let stake = 10 * MinimumSelfDelegation::get();
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(ALICE.into()),
1
                ALICE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                stake,
            ));
            // Register Dave in pallet_session (invulnerables are automatically registered)
1
            let dave_account_id = get_aura_id_from_seed(&AccountId::from(DAVE).to_string());
1
            assert_ok!(Session::set_keys(
1
                origin_of(DAVE.into()),
1
                dancebox_runtime::SessionKeys {
1
                    nimbus: dave_account_id,
1
                },
1
                vec![]
            ));
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(DAVE.into()),
1
                DAVE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                stake,
            ));
1
            let eligible_candidates =
1
                pallet_pooled_staking::SortedEligibleCandidates::<Runtime>::get().to_vec();
1
            assert_eq!(
                eligible_candidates,
1
                vec![
1
                    EligibleCandidate {
1
                        candidate: ALICE.into(),
1
                        stake
1
                    },
1
                    EligibleCandidate {
1
                        candidate: DAVE.into(),
1
                        stake
1
                    },
                ]
            );
1
            assert_eq!(
1
                pallet_invulnerables::Invulnerables::<Runtime>::get().to_vec(),
1
                vec![
1
                    AccountId::from(ALICE),
1
                    AccountId::from(BOB),
1
                    AccountId::from(CHARLIE),
                ]
            );
            // Need to trigger new session to update pallet_session
1
            run_to_session(2);
1
            assert_eq!(
1
                Session::validators(),
1
                vec![AccountId::from(ALICE), AccountId::from(BOB),]
            );
1
        });
1
}
#[test]
1
fn test_pallet_session_limits_num_validators_from_staking() {
    // Set max_collators = 2, take 1 invulnerable and the rest from staking
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000_000_000 * UNIT),
1
            (AccountId::from(BOB), 100_000_000_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000_000_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000_000_000 * UNIT),
1
        ])
1
        .with_collators(vec![(AccountId::from(ALICE), 210_000_000 * UNIT)])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .with_config(pallet_configuration::HostConfiguration {
1
            max_collators: 2,
1
            min_orchestrator_collators: 2,
1
            max_orchestrator_collators: 2,
1
            collators_per_container: 2,
1
            full_rotation_period: 24,
1
            ..Default::default()
1
        })
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
1
            let stake = 10 * MinimumSelfDelegation::get();
            // Register accounts in pallet_session (invulnerables are automatically registered)
1
            let bob_account_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
1
            assert_ok!(Session::set_keys(
1
                origin_of(BOB.into()),
1
                dancebox_runtime::SessionKeys {
1
                    nimbus: bob_account_id,
1
                },
1
                vec![]
            ));
1
            let charlie_account_id = get_aura_id_from_seed(&AccountId::from(CHARLIE).to_string());
1
            assert_ok!(Session::set_keys(
1
                origin_of(CHARLIE.into()),
1
                dancebox_runtime::SessionKeys {
1
                    nimbus: charlie_account_id,
1
                },
1
                vec![]
            ));
1
            let dave_account_id = get_aura_id_from_seed(&AccountId::from(DAVE).to_string());
1
            assert_ok!(Session::set_keys(
1
                origin_of(DAVE.into()),
1
                dancebox_runtime::SessionKeys {
1
                    nimbus: dave_account_id,
1
                },
1
                vec![]
            ));
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(BOB.into()),
1
                BOB.into(),
1
                ActivePoolKind::AutoCompounding,
1
                stake,
            ));
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(CHARLIE.into()),
1
                CHARLIE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                stake,
            ));
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(DAVE.into()),
1
                DAVE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                stake,
            ));
1
            let eligible_candidates =
1
                pallet_pooled_staking::SortedEligibleCandidates::<Runtime>::get().to_vec();
1
            assert_eq!(
                eligible_candidates,
1
                vec![
1
                    EligibleCandidate {
1
                        candidate: BOB.into(),
1
                        stake
1
                    },
1
                    EligibleCandidate {
1
                        candidate: CHARLIE.into(),
1
                        stake
1
                    },
1
                    EligibleCandidate {
1
                        candidate: DAVE.into(),
1
                        stake
1
                    },
                ]
            );
1
            assert_eq!(
1
                pallet_invulnerables::Invulnerables::<Runtime>::get().to_vec(),
1
                vec![AccountId::from(ALICE),]
            );
            // Need to trigger new session to update pallet_session
1
            run_to_session(2);
1
            assert_eq!(
1
                Session::validators(),
1
                vec![AccountId::from(ALICE), AccountId::from(BOB),]
            );
1
        });
1
}
#[test]
1
fn test_reward_to_staking_candidate() {
    // Alice, Bob, Charlie are invulnerables
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000_000_000 * UNIT),
1
            (AccountId::from(BOB), 100_000_000_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000_000_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000_000_000 * UNIT),
1
        ])
1
        .with_collators(vec![(AccountId::from(ALICE), 210_000_000 * UNIT)])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
1
            let dave_account_id = get_aura_id_from_seed(&AccountId::from(DAVE).to_string());
1
            assert_ok!(Session::set_keys(
1
                origin_of(DAVE.into()),
1
                dancebox_runtime::SessionKeys {
1
                    nimbus: dave_account_id,
1
                },
1
                vec![]
            ));
            // We make delegations to DAVE so that she is an elligible candidate.
1
            let stake = 10 * MinimumSelfDelegation::get();
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(DAVE.into()),
1
                DAVE.into(),
1
                ActivePoolKind::ManualRewards,
1
                stake,
            ));
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(BOB.into()),
1
                DAVE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                stake,
            ));
            // wait few sessions for the request to be executable
1
            run_to_session(3u32);
1
            assert_ok!(PooledStaking::execute_pending_operations(
1
                origin_of(ALICE.into()),
1
                vec![
1
                    PendingOperationQuery {
1
                        delegator: DAVE.into(),
1
                        operation: PendingOperationKey::JoiningManualRewards {
1
                            candidate: DAVE.into(),
1
                            at: 0
1
                        }
1
                    },
1
                    PendingOperationQuery {
1
                        delegator: BOB.into(),
1
                        operation: PendingOperationKey::JoiningAutoCompounding {
1
                            candidate: DAVE.into(),
1
                            at: 0
1
                        }
1
                    }
                ]
            ));
            // wait for next session so that DAVE is elected
1
            run_to_session(4u32);
1
            assert_eq!(
1
                Session::validators(),
1
                vec![AccountId::from(ALICE), AccountId::from(DAVE)]
            );
1
            let account: AccountId = DAVE.into();
1
            let balance_before = System::account(account.clone()).data.free;
1
            let summary = (0..100)
1
                .find_map(|_| {
1
                    let summary = run_block();
1
                    if summary.author_id == DAVE.into() {
1
                        Some(summary)
                    } else {
                        None
                    }
1
                })
1
                .unwrap_or_else(|| panic!("DAVE doesn't seem to author any blocks"));
1
            let balance_after = System::account(account).data.free;
1
            let all_rewards = RewardsPortion::get() * summary.inflation;
            // rewards are shared between orchestrator and registered paras
1
            let orchestrator_rewards = all_rewards / 3;
1
            let candidate_rewards = RewardsCollatorCommission::get() * orchestrator_rewards;
1
            assert_eq!(
                candidate_rewards,
1
                balance_after - balance_before,
                "dave should get the correct reward portion"
            );
1
        });
1
}
#[test]
1
fn test_reward_to_invulnerable() {
    // Alice, Bob, Charlie are invulnerables
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
            // We make delegations to ALICE so that she is an elligible candidate.
            // However since she is an invulnerable she should get all the
            // rewards.
1
            let stake = 10 * MinimumSelfDelegation::get();
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(ALICE.into()),
1
                ALICE.into(),
1
                ActivePoolKind::ManualRewards,
1
                stake,
            ));
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(BOB.into()),
1
                ALICE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                stake,
            ));
            // wait few sessions for the request to be executable
1
            run_to_session(3u32);
1
            assert_ok!(PooledStaking::execute_pending_operations(
1
                origin_of(ALICE.into()),
1
                vec![
1
                    PendingOperationQuery {
1
                        delegator: ALICE.into(),
1
                        operation: PendingOperationKey::JoiningAutoCompounding {
1
                            candidate: ALICE.into(),
1
                            at: 0
1
                        }
1
                    },
1
                    PendingOperationQuery {
1
                        delegator: BOB.into(),
1
                        operation: PendingOperationKey::JoiningAutoCompounding {
1
                            candidate: ALICE.into(),
1
                            at: 0
1
                        }
1
                    }
                ]
            ));
            // wait for next session so that ALICE is elected
1
            run_to_session(4u32);
1
            let account: AccountId = ALICE.into();
1
            let balance_before = System::account(account.clone()).data.free;
1
            let summary = (0..100)
2
                .find_map(|_| {
2
                    let summary = run_block();
2
                    if summary.author_id == ALICE.into() {
1
                        Some(summary)
                    } else {
1
                        None
                    }
2
                })
1
                .unwrap_or_else(|| panic!("ALICE doesn't seem to author any blocks"));
1
            let balance_after = System::account(account).data.free;
1
            let all_rewards = RewardsPortion::get() * summary.inflation;
            // rewards are shared between orchestrator and registered paras
1
            let orchestrator_rewards = all_rewards / 3;
1
            assert_eq!(
                orchestrator_rewards,
1
                balance_after - balance_before,
                "alice should get the correct reward portion"
            );
1
        });
1
}
#[test]
1
fn test_reward_to_invulnerable_with_key_change() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![(AccountId::from(ALICE), 210 * UNIT)])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
1
            run_to_session(2u32);
            // change key, this should be reflected 2 sessions afterward
1
            let alice_new_key = get_aura_id_from_seed(&AccountId::from(DAVE).to_string());
1
            assert_ok!(Session::set_keys(
1
                origin_of(ALICE.into()),
1
                dancebox_runtime::SessionKeys {
1
                    nimbus: alice_new_key,
1
                },
1
                vec![]
            ));
1
            run_to_session(4u32);
1
            let account: AccountId = ALICE.into();
1
            let balance_before = System::account(account.clone()).data.free;
1
            let summary = run_block();
1
            assert_eq!(summary.author_id, ALICE.into());
1
            let balance_after = System::account(account).data.free;
1
            let all_rewards = RewardsPortion::get() * summary.inflation;
            // rewards are shared between orchestrator and registered paras
1
            let orchestrator_rewards = all_rewards / 3;
1
            assert_eq!(
                orchestrator_rewards,
1
                balance_after - balance_before,
                "alice should get the correct reward portion"
            );
1
        });
1
}
#[test]
1
fn test_migration_config_add_full_rotation_mode() {
1
    ExtBuilder::default().build().execute_with(|| {
        const CONFIGURATION_ACTIVE_CONFIG_KEY: &[u8] =
            &hex_literal::hex!("06de3d8a54d27e44a9d5ce189618f22db4b49d95320d9021994c850f25b8e385");
        const CONFIGURATION_PENDING_CONFIGS_KEY: &[u8] =
            &hex_literal::hex!("06de3d8a54d27e44a9d5ce189618f22d53b4123b2e186e07fb7bad5dda5f55c0");
        // Modify active config
1
        frame_support::storage::unhashed::put_raw(
1
            CONFIGURATION_ACTIVE_CONFIG_KEY,
1
            &HostConfigurationV3 {
1
                max_collators: 5,
1
                min_orchestrator_collators: 2,
1
                max_orchestrator_collators: 1,
1
                collators_per_container: 3,
1
                full_rotation_period: 4,
1
                collators_per_parathread: 2,
1
                parathreads_per_collator: 1,
1
                target_container_chain_fullness: Perbill::from_percent(45),
1
                max_parachain_cores_percentage: Some(Perbill::from_percent(75)),
1
            }
1
            .encode(),
        );
        // Modify pending configs
1
        frame_support::storage::unhashed::put_raw(
1
            CONFIGURATION_PENDING_CONFIGS_KEY,
1
            &vec![
1
                (
1
                    1234u32,
1
                    HostConfigurationV3 {
1
                        max_collators: 1,
1
                        min_orchestrator_collators: 4,
1
                        max_orchestrator_collators: 45,
1
                        collators_per_container: 5,
1
                        full_rotation_period: 1,
1
                        collators_per_parathread: 1,
1
                        parathreads_per_collator: 1,
1
                        target_container_chain_fullness: Perbill::from_percent(65),
1
                        max_parachain_cores_percentage: Some(Perbill::from_percent(75)),
1
                    },
1
                ),
1
                (
1
                    5678u32,
1
                    HostConfigurationV3 {
1
                        max_collators: 1,
1
                        min_orchestrator_collators: 4,
1
                        max_orchestrator_collators: 45,
1
                        collators_per_container: 5,
1
                        full_rotation_period: 1,
1
                        collators_per_parathread: 1,
1
                        parathreads_per_collator: 1,
1
                        target_container_chain_fullness: Perbill::from_percent(65),
1
                        max_parachain_cores_percentage: Some(Perbill::from_percent(75)),
1
                    },
1
                ),
1
            ]
1
            .encode(),
        );
1
        let migration = MigrateConfigurationAddFullRotationMode::<Runtime>(Default::default());
1
        migration.migrate(Default::default());
1
        let expected_active = pallet_configuration::HostConfiguration {
1
            max_collators: 5,
1
            min_orchestrator_collators: 2,
1
            max_orchestrator_collators: 1,
1
            collators_per_container: 3,
1
            full_rotation_period: 4,
1
            collators_per_parathread: 2,
1
            parathreads_per_collator: 1,
1
            target_container_chain_fullness: Perbill::from_percent(45),
1
            max_parachain_cores_percentage: Some(Perbill::from_percent(75)),
1
            ..Default::default()
1
        };
1
        assert_eq!(Configuration::config(), expected_active);
1
        let expected_pending = vec![
1
            (
1
                1234u32,
1
                pallet_configuration::HostConfiguration {
1
                    max_collators: 1,
1
                    min_orchestrator_collators: 4,
1
                    max_orchestrator_collators: 45,
1
                    collators_per_container: 5,
1
                    full_rotation_period: 1,
1
                    collators_per_parathread: 1,
1
                    parathreads_per_collator: 1,
1
                    target_container_chain_fullness: Perbill::from_percent(65),
1
                    max_parachain_cores_percentage: Some(Perbill::from_percent(75)),
1
                    ..Default::default()
1
                },
1
            ),
1
            (
1
                5678u32,
1
                pallet_configuration::HostConfiguration {
1
                    max_collators: 1,
1
                    min_orchestrator_collators: 4,
1
                    max_orchestrator_collators: 45,
1
                    collators_per_container: 5,
1
                    full_rotation_period: 1,
1
                    collators_per_parathread: 1,
1
                    parathreads_per_collator: 1,
1
                    target_container_chain_fullness: Perbill::from_percent(65),
1
                    max_parachain_cores_percentage: Some(Perbill::from_percent(75)),
1
                    ..Default::default()
1
                },
1
            ),
        ];
1
        assert_eq!(Configuration::pending_configs(), expected_pending);
1
    });
1
}
#[test]
1
fn test_migration_registrar_pending_verification() {
1
    ExtBuilder::default().build().execute_with(|| {
        const REGISTRAR_PENDING_VERIFICATION_KEY: &[u8] =
            &hex_literal::hex!("3fba98689ebed1138735e0e7a5a790ab57a35de516113188134ad8e43c6d55ec");
        // Modify active config
1
        let para_ids: Vec<ParaId> = vec![2000.into(), 2001.into(), 2002.into(), 3000.into()];
1
        frame_support::storage::unhashed::put(REGISTRAR_PENDING_VERIFICATION_KEY, &para_ids);
1
        let migration = RegistrarPendingVerificationValueToMap::<Runtime>(Default::default());
1
        migration.migrate(Default::default());
1
        let empty_key =
1
            frame_support::storage::unhashed::get_raw(REGISTRAR_PENDING_VERIFICATION_KEY);
1
        assert_eq!(empty_key, None);
5
        for para_id in para_ids {
4
            let exists_in_map =
4
                pallet_registrar::PendingVerification::<Runtime>::get(para_id).is_some();
4
            assert!(
4
                exists_in_map,
                "After migration, para id {:?} does not exist in storage map",
                para_id
            );
        }
1
    });
1
}
#[test]
1
fn test_collator_assignment_gives_priority_to_invulnerables() {
    // Set max_collators = 2, take 1 invulnerable and the rest from staking
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000_000_000 * UNIT),
1
            (AccountId::from(BOB), 100_000_000_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000_000_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000_000_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210_000_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000_000 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
1
            let stake = 10 * MinimumSelfDelegation::get();
            // Register accounts in pallet_session (invulnerables are automatically registered)
1
            let bob_account_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
1
            assert_ok!(Session::set_keys(
1
                origin_of(BOB.into()),
1
                dancebox_runtime::SessionKeys {
1
                    nimbus: bob_account_id,
1
                },
1
                vec![]
            ));
1
            let charlie_account_id = get_aura_id_from_seed(&AccountId::from(CHARLIE).to_string());
1
            assert_ok!(Session::set_keys(
1
                origin_of(CHARLIE.into()),
1
                dancebox_runtime::SessionKeys {
1
                    nimbus: charlie_account_id,
1
                },
1
                vec![]
            ));
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(BOB.into()),
1
                BOB.into(),
1
                ActivePoolKind::AutoCompounding,
1
                stake,
            ));
1
            assert_ok!(PooledStaking::request_delegate(
1
                origin_of(CHARLIE.into()),
1
                CHARLIE.into(),
1
                ActivePoolKind::AutoCompounding,
1
                stake,
            ));
1
            let eligible_candidates =
1
                pallet_pooled_staking::SortedEligibleCandidates::<Runtime>::get().to_vec();
1
            assert_eq!(
                eligible_candidates,
1
                vec![
1
                    EligibleCandidate {
1
                        candidate: BOB.into(),
1
                        stake
1
                    },
1
                    EligibleCandidate {
1
                        candidate: CHARLIE.into(),
1
                        stake
1
                    },
                ]
            );
1
            assert_eq!(
1
                pallet_invulnerables::Invulnerables::<Runtime>::get().to_vec(),
1
                vec![AccountId::from(ALICE), AccountId::from(DAVE)]
            );
1
            set_parachain_inherent_data_random_seed([1; 32]);
1
            run_block();
            // Need to trigger new session to update pallet_session
1
            run_to_session(2);
1
            assert_eq!(
1
                Session::validators(),
1
                vec![
1
                    AccountId::from(ALICE),
1
                    AccountId::from(DAVE),
1
                    AccountId::from(BOB),
1
                    AccountId::from(CHARLIE)
                ]
            );
            // Need to trigger full rotation to ensure invulnerables are assigned
1
            let rotation_period = Configuration::config().full_rotation_period;
1
            run_to_session(rotation_period);
1
            assert!(
1
                CollatorAssignment::collator_container_chain()
1
                    .orchestrator_chain
1
                    .contains(&AccountId::from(ALICE)),
                "CollatorAssignment did not give priority to invulnerable ALICE: {:?}",
                CollatorAssignment::collator_container_chain()
            );
1
            assert!(
1
                CollatorAssignment::collator_container_chain()
1
                    .orchestrator_chain
1
                    .contains(&AccountId::from(DAVE)),
                "CollatorAssignment did not give priority to invulnerable DAVE: {:?}",
                CollatorAssignment::collator_container_chain()
            );
1
        });
1
}
#[test]
1
fn test_can_buy_credits_before_registering_para() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
            // Try to buy the maximum amount of credits
1
            let balance_before = System::account(AccountId::from(ALICE)).data.free;
1
            assert_ok!(ServicesPayment::purchase_credits(
1
                origin_of(ALICE.into()),
1
                1001.into(),
1
                block_credits_to_required_balance(u32::MAX, 1001.into())
            ));
1
            let balance_after = System::account(AccountId::from(ALICE)).data.free;
            // Now parachain tank should have this amount
1
            let balance_tank = System::account(ServicesPayment::parachain_tank(1001.into()))
1
                .data
1
                .free;
1
            assert_eq!(
                balance_tank,
1
                block_credits_to_required_balance(u32::MAX, 1001.into())
            );
1
            let expected_cost = block_credits_to_required_balance(u32::MAX, 1001.into());
1
            assert_eq!(balance_before - balance_after, expected_cost);
1
        });
1
}
#[test]
1
fn test_cannot_mark_valid_para_with_no_bootnodes() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
1
            assert_ok!(Registrar::register(
1
                origin_of(ALICE.into()),
1
                1001.into(),
1
                empty_genesis_data(),
1
                None
            ));
1
            assert_noop!(
1
                Registrar::mark_valid_for_collating(root_origin(), 1001.into()),
1
                pallet_data_preservers::Error::<Runtime>::NoBootNodes,
            );
1
        });
1
}
#[test]
1
fn test_can_buy_credits_before_registering_para_and_receive_free_credits() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
            // Try to buy (MaxCreditsStored - 1) credits
1
            let balance_before = System::account(AccountId::from(ALICE)).data.free;
1
            assert_ok!(ServicesPayment::purchase_credits(
1
                origin_of(ALICE.into()),
1
                1001.into(),
1
                block_credits_to_required_balance(
1
                    dancebox_runtime::FreeBlockProductionCredits::get() - 1,
1
                    1001.into()
                )
            ));
1
            let balance_after = System::account(AccountId::from(ALICE)).data.free;
            // Now parachain tank should have this amount
1
            let balance_tank = System::account(ServicesPayment::parachain_tank(1001.into()))
1
                .data
1
                .free;
1
            assert_eq!(
                balance_tank,
1
                block_credits_to_required_balance(
1
                    dancebox_runtime::FreeBlockProductionCredits::get() - 1,
1
                    1001.into()
                )
            );
1
            let expected_cost = block_credits_to_required_balance(
1
                dancebox_runtime::FreeBlockProductionCredits::get() - 1,
1
                1001.into(),
            );
1
            assert_eq!(balance_before - balance_after, expected_cost);
            // Now register para
1
            assert_ok!(Registrar::register(
1
                origin_of(ALICE.into()),
1
                1001.into(),
1
                empty_genesis_data(),
1
                None
            ));
1
            set_dummy_boot_node(origin_of(ALICE.into()), 1001.into());
1
            assert_ok!(Registrar::mark_valid_for_collating(
1
                root_origin(),
1
                1001.into()
            ));
            // We received aññ free credits, because we cannot have more than MaxCreditsStored
1
            let credits =
1
                pallet_services_payment::BlockProductionCredits::<Runtime>::get(ParaId::from(1001))
1
                    .unwrap_or_default();
1
            assert_eq!(credits, dancebox_runtime::FreeBlockProductionCredits::get());
1
        });
1
}
#[test]
1
fn test_deregister_and_register_again_does_not_give_free_credits() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
            // Register
1
            assert_ok!(Registrar::register(
1
                origin_of(ALICE.into()),
1
                1001.into(),
1
                empty_genesis_data(),
1
                None
            ),);
1
            set_dummy_boot_node(origin_of(ALICE.into()), 1001.into());
1
            assert_ok!(Registrar::mark_valid_for_collating(
1
                root_origin(),
1
                1001.into()
            ),);
            // We received free credits
1
            let credits =
1
                pallet_services_payment::BlockProductionCredits::<Runtime>::get(ParaId::from(1001))
1
                    .unwrap_or_default();
1
            assert_eq!(credits, dancebox_runtime::FreeBlockProductionCredits::get());
            // Deregister after 1 session
1
            run_to_session(1);
1
            assert_ok!(Registrar::deregister(root_origin(), 1001.into()), ());
1
            run_to_session(3);
1
            let credits_before_2nd_register =
1
                pallet_services_payment::BlockProductionCredits::<Runtime>::get(ParaId::from(1001))
1
                    .unwrap_or_default();
            // We spent some credits because this container chain had collators for 1 session
1
            assert_ne!(
                credits_before_2nd_register,
1
                dancebox_runtime::FreeBlockProductionCredits::get()
            );
            // Register again
1
            assert_ok!(Registrar::register(
1
                origin_of(ALICE.into()),
1
                1001.into(),
1
                empty_genesis_data(),
1
                None
            ),);
1
            set_dummy_boot_node(origin_of(ALICE.into()), 1001.into());
1
            assert_ok!(Registrar::mark_valid_for_collating(
1
                root_origin(),
1
                1001.into()
            ),);
            // No more free credits
1
            let credits =
1
                pallet_services_payment::BlockProductionCredits::<Runtime>::get(ParaId::from(1001))
1
                    .unwrap_or_default();
1
            assert_eq!(credits, credits_before_2nd_register);
1
        });
1
}
#[test]
1
fn test_sudo_can_register_foreign_assets_and_manager_change_paremeters() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1

            
1
        .build()
1
        .execute_with(|| {
            // We register the asset with Alice as manager
1
            assert_ok!(ForeignAssetsCreator::create_foreign_asset(root_origin(), Location::parent(), 1, AccountId::from(ALICE), true, 1), ());
1
            assert_eq!(ForeignAssetsCreator::foreign_asset_for_id(1), Some(Location::parent()));
1
            assert_eq!(ForeignAssetsCreator::asset_id_for_foreign(Location::parent()), Some(1));
            // Alice now can change parameters like metadata from the asset
1
            assert_ok!(ForeignAssets::set_metadata(origin_of(ALICE.into()), 1, b"xcDot".to_vec(), b"xcDot".to_vec(), 12));
1
            assert_eq!(<ForeignAssets as frame_support::traits::fungibles::metadata::Inspect<AccountId>>::name(1),  b"xcDot".to_vec());
1
            assert_eq!(<ForeignAssets as frame_support::traits::fungibles::metadata::Inspect<AccountId>>::symbol(1),  b"xcDot".to_vec());
1
            assert_eq!(<ForeignAssets as frame_support::traits::fungibles::metadata::Inspect<AccountId>>::decimals(1),  12);
            // Any other person cannot do this
1
            assert_noop!(
1
                ForeignAssets::set_metadata(origin_of(BOB.into()), 1, b"dummy".to_vec(), b"dummy".to_vec(), 12),
1
                pallet_assets::Error::<Runtime, Instance1>::NoPermission
            );
            // Alice now can mint
1
            assert_ok!(ForeignAssets::mint(origin_of(ALICE.into()), 1, AccountId::from(BOB).into(), 1000));
1
            assert_eq!(<ForeignAssets as frame_support::traits::fungibles::Inspect<AccountId>>::total_issuance(1),  1000);
1
            assert_eq!(<ForeignAssets as frame_support::traits::fungibles::Inspect<AccountId>>::balance(1, &AccountId::from(BOB)),  1000);
1
        });
1
}
#[test]
1
fn test_assets_cannot_be_created_from_signed_origins() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .build()
1
        .execute_with(|| {
            // We try to register the asset with Alice as origin
            // Any other person cannot do this
1
            assert_noop!(
1
                ForeignAssetsCreator::create_foreign_asset(
1
                    origin_of(ALICE.into()),
1
                    Location::parent(),
                    1,
1
                    AccountId::from(ALICE),
                    true,
                    1
                ),
1
                BadOrigin
            );
1
            assert_noop!(
1
                ForeignAssets::create(origin_of(ALICE.into()), 1, AccountId::from(ALICE).into(), 1),
1
                BadOrigin
            );
1
        });
1
}
#[test]
1
fn test_asset_rate_can_be_set_from_sudo_but_not_from_signed() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .build()
1
        .execute_with(|| {
            // We try to set the rate from non-sudo
1
            assert_noop!(
1
                AssetRate::create(origin_of(ALICE.into()), Box::new(1), FixedU128::from_u32(1)),
1
                BadOrigin
            );
            // We try to set the rate from sudo
1
            assert_ok!(AssetRate::create(
1
                root_origin(),
1
                Box::new(1),
1
                FixedU128::from_u32(1)
            ));
1
            assert_eq!(
1
                pallet_asset_rate::ConversionRateToNative::<Runtime>::get(1),
1
                Some(FixedU128::from_u32(1))
            );
1
        });
1
}
#[test]
1
fn test_division_by_0() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .build()
1
        .execute_with(|| {
            // We try to set 0 rate to make sure we dont overflow
1
            assert_ok!(AssetRate::create(
1
                root_origin(),
1
                Box::new(1),
1
                FixedU128::from_u32(0)
            ));
            use frame_support::traits::tokens::ConversionToAssetBalance;
1
            let balance = AssetRate::to_asset_balance(1, 1);
1
            assert!(balance.is_err());
1
        });
1
}
#[test]
1
fn test_register_parathread() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
            // Register
1
            assert_ok!(Registrar::register_parathread(
1
                origin_of(ALICE.into()),
1
                3001.into(),
1
                SlotFrequency { min: 1, max: 1 },
1
                empty_genesis_data(),
1
                None
            ));
1
            set_dummy_boot_node(origin_of(ALICE.into()), 3001.into());
1
            assert_ok!(Registrar::mark_valid_for_collating(
1
                root_origin(),
1
                3001.into()
            ));
1
            run_to_session(2);
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert_eq!(
1
                assignment.container_chains[&ParaId::from(3001)],
1
                vec![CHARLIE.into()]
            );
1
        });
1
}
#[test]
1
fn test_register_parathread_genesis() {
1
    let parathread_params = tp_traits::ParathreadParams {
1
        slot_frequency: SlotFrequency { min: 1, max: 1 },
1
    };
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .with_para_ids(vec![ParaRegistrationParams {
1
            para_id: 3001,
1
            genesis_data: empty_genesis_data(),
1
            block_production_credits: u32::MAX,
1
            collator_assignment_credits: u32::MAX,
1
            parathread_params: Some(parathread_params.clone()),
1
        }])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
1
            assert_eq!(Registrar::registered_para_ids(), vec![3001.into()]);
1
            assert_eq!(
1
                pallet_registrar::ParathreadParams::<Runtime>::get(ParaId::from(3001)),
1
                Some(parathread_params)
            );
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert_eq!(
1
                assignment.container_chains[&ParaId::from(3001)],
1
                vec![CHARLIE.into()]
            );
1
        });
1
}
#[test]
1
fn test_ed_plus_block_credit_session_purchase_works() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
            // Assert current slot gets updated
1
            assert_eq!(current_slot(), 1u64);
1
            assert!(current_author() == AccountId::from(BOB));
            // Alice and Bob collate in our chain
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
1
            let bob_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
1
            assert_eq!(authorities(), vec![alice_id, bob_id]);
1
            assert_ok!(Registrar::register(
1
                origin_of(ALICE.into()),
1
                1001.into(),
1
                empty_genesis_data(),
1
                None
            ));
1
            set_dummy_boot_node(origin_of(ALICE.into()), 1001.into());
1
            assert_ok!(Registrar::mark_valid_for_collating(
1
                root_origin(),
1
                1001.into()
            ));
            // Need to reset credits to 0 because now parachains are given free credits on register
1
            assert_ok!(ServicesPayment::set_block_production_credits(
1
                root_origin(),
1
                1001.into(),
                0
            ));
1
            let credits_1001 =
1
                block_credits_to_required_balance(dancebox_runtime::Period::get(), 1001.into())
1
                    + dancebox_runtime::EXISTENTIAL_DEPOSIT;
            // Fill the tank
1
            assert_ok!(ServicesPayment::purchase_credits(
1
                origin_of(ALICE.into()),
1
                1001.into(),
1
                credits_1001
            ));
            // Assignment should happen after 2 sessions
1
            run_to_session(1u32);
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert!(assignment.container_chains.is_empty());
1
            run_to_session(2u32);
            // Charlie and Dave should be assigned to para 1001
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert_eq!(
1
                assignment.container_chains[&1001u32.into()],
1
                vec![CHARLIE.into(), DAVE.into()]
            );
            // Simulate block inclusion from container chain 1001
1
            let mut sproof: ParaHeaderSproofBuilder = ParaHeaderSproofBuilder::default();
1
            let slot: u64 = 5;
1
            let s = ParaHeaderSproofBuilderItem {
1
                para_id: 1001.into(),
1
                author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::<u32, BlakeTwo256> {
1
                    parent_hash: Default::default(),
1
                    number: 1,
1
                    state_root: Default::default(),
1
                    extrinsics_root: Default::default(),
1
                    digest: sp_runtime::generic::Digest {
1
                        logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())],
1
                    },
1
                }),
1
            };
1
            sproof.items.push(s);
1
            set_author_noting_inherent_data(sproof);
1
            run_block();
            // After this it should not be assigned anymore, since credits are not payable
1
            run_to_session(3u32);
            // Nobody should be assigned to para 1001
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert_eq!(assignment.container_chains.get(&1001u32.into()), None,);
1
        });
1
}
#[test]
1
fn test_ed_plus_block_credit_session_minus_1_purchase_fails() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
            // Assert current slot gets updated
1
            assert_eq!(current_slot(), 1u64);
1
            assert!(current_author() == AccountId::from(BOB));
            // Alice and Bob collate in our chain
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
1
            let bob_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
1
            assert_eq!(authorities(), vec![alice_id, bob_id]);
1
            assert_ok!(Registrar::register(
1
                origin_of(ALICE.into()),
1
                1001.into(),
1
                empty_genesis_data(),
1
                None
            ));
1
            set_dummy_boot_node(origin_of(ALICE.into()), 1001.into());
1
            assert_ok!(Registrar::mark_valid_for_collating(
1
                root_origin(),
1
                1001.into()
            ));
            // Need to reset credits to 0 because now parachains are given free credits on register
1
            assert_ok!(ServicesPayment::set_block_production_credits(
1
                root_origin(),
1
                1001.into(),
                0
            ));
1
            let credits_1001 =
1
                block_credits_to_required_balance(dancebox_runtime::Period::get(), 1001.into())
1
                    + dancebox_runtime::EXISTENTIAL_DEPOSIT
1
                    - 1;
            // Fill the tank
1
            assert_ok!(ServicesPayment::purchase_credits(
1
                origin_of(ALICE.into()),
1
                1001.into(),
1
                credits_1001
            ));
            // Assignment should happen after 2 sessions
1
            run_to_session(1u32);
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert!(assignment.container_chains.is_empty());
1
            run_to_session(2u32);
            // Charlie and Dave should not be assigned to para 1001
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert_eq!(assignment.container_chains.get(&1001u32.into()), None,);
1
        });
1
}
#[test]
1
fn test_reassignment_ed_plus_two_block_credit_session_purchase_works() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
            // Assert current slot gets updated
1
            assert_eq!(current_slot(), 1u64);
1
            assert!(current_author() == AccountId::from(BOB));
            // Alice and Bob collate in our chain
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
1
            let bob_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
1
            assert_eq!(authorities(), vec![alice_id, bob_id]);
1
            assert_ok!(Registrar::register(
1
                origin_of(ALICE.into()),
1
                1001.into(),
1
                empty_genesis_data(),
1
                None
            ));
1
            set_dummy_boot_node(origin_of(ALICE.into()), 1001.into());
1
            assert_ok!(Registrar::mark_valid_for_collating(
1
                root_origin(),
1
                1001.into()
            ));
            // Need to reset credits to 0 because now parachains are given free credits on register
1
            assert_ok!(ServicesPayment::set_block_production_credits(
1
                root_origin(),
1
                1001.into(),
                0
            ));
            // On reassignment the blocks credits needed should be enough for the current session and the next one
1
            let credits_1001 =
1
                block_credits_to_required_balance(dancebox_runtime::Period::get() * 2, 1001.into())
1
                    + dancebox_runtime::EXISTENTIAL_DEPOSIT;
            // Fill the tank
1
            assert_ok!(ServicesPayment::purchase_credits(
1
                origin_of(ALICE.into()),
1
                1001.into(),
1
                credits_1001
            ));
            // Assignment should happen after 2 sessions
1
            run_to_session(1u32);
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert!(assignment.container_chains.is_empty());
1
            run_to_session(2u32);
            // Charlie and Dave should be assigned to para 1001
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert_eq!(
1
                assignment.container_chains[&1001u32.into()],
1
                vec![CHARLIE.into(), DAVE.into()]
            );
            // Simulate block inclusion from container chain 1001
1
            let mut sproof: ParaHeaderSproofBuilder = ParaHeaderSproofBuilder::default();
1
            let slot: u64 = 5;
1
            let s = ParaHeaderSproofBuilderItem {
1
                para_id: 1001.into(),
1
                author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::<u32, BlakeTwo256> {
1
                    parent_hash: Default::default(),
1
                    number: 1,
1
                    state_root: Default::default(),
1
                    extrinsics_root: Default::default(),
1
                    digest: sp_runtime::generic::Digest {
1
                        logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())],
1
                    },
1
                }),
1
            };
1
            sproof.items.push(s);
1
            set_author_noting_inherent_data(sproof);
1
            run_block();
            // Session 3 should still be assigned
1
            run_to_session(3u32);
            // Charlie and Dave should be assigned to para 1001
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert_eq!(
1
                assignment.container_chains[&1001u32.into()],
1
                vec![CHARLIE.into(), DAVE.into()]
            );
            // After this it should not be assigned anymore, since credits are not payable
1
            run_to_session(4u32);
            // Nobody should be assigned to para 1001
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert_eq!(assignment.container_chains.get(&1001u32.into()), None,);
1
        });
1
}
#[test]
1
fn test_reassignment_ed_plus_two_block_credit_session_minus_1_purchase_fails() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
            // Assert current slot gets updated
1
            assert_eq!(current_slot(), 1u64);
1
            assert!(current_author() == AccountId::from(BOB));
            // Alice and Bob collate in our chain
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
1
            let bob_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
1
            assert_eq!(authorities(), vec![alice_id, bob_id]);
1
            assert_ok!(Registrar::register(
1
                origin_of(ALICE.into()),
1
                1001.into(),
1
                empty_genesis_data(),
1
                None
            ));
1
            set_dummy_boot_node(origin_of(ALICE.into()), 1001.into());
1
            assert_ok!(Registrar::mark_valid_for_collating(
1
                root_origin(),
1
                1001.into()
            ));
            // Need to reset credits to 0 because now parachains are given free credits on register
1
            assert_ok!(ServicesPayment::set_block_production_credits(
1
                root_origin(),
1
                1001.into(),
                0
            ));
1
            let credits_1001 =
1
                block_credits_to_required_balance(dancebox_runtime::Period::get() * 2, 1001.into())
1
                    + dancebox_runtime::EXISTENTIAL_DEPOSIT
1
                    - 1;
            // Fill the tank
1
            assert_ok!(ServicesPayment::purchase_credits(
1
                origin_of(ALICE.into()),
1
                1001.into(),
1
                credits_1001
            ));
            // Assignment should happen after 2 sessions
1
            run_to_session(1u32);
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert!(assignment.container_chains.is_empty());
1
            run_to_session(2u32);
            // Charlie and Dave should be assigned to para 1001
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert_eq!(
1
                assignment.container_chains[&1001u32.into()],
1
                vec![CHARLIE.into(), DAVE.into()]
            );
            // Simulate block inclusion from container chain 1001
1
            let mut sproof: ParaHeaderSproofBuilder = ParaHeaderSproofBuilder::default();
1
            let slot: u64 = 5;
1
            let s = ParaHeaderSproofBuilderItem {
1
                para_id: 1001.into(),
1
                author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::<u32, BlakeTwo256> {
1
                    parent_hash: Default::default(),
1
                    number: 1,
1
                    state_root: Default::default(),
1
                    extrinsics_root: Default::default(),
1
                    digest: sp_runtime::generic::Digest {
1
                        logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())],
1
                    },
1
                }),
1
            };
1
            sproof.items.push(s);
1
            set_author_noting_inherent_data(sproof);
1
            run_block();
            // After this it should not be assigned anymore, since credits are not payable
1
            run_to_session(3u32);
            // Nobody should be assigned to para 1001
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert_eq!(assignment.container_chains.get(&1001u32.into()), None,);
1
        });
1
}
#[test]
1
fn test_block_credits_with_purchase_can_be_combined() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
            // Assert current slot gets updated
1
            assert_eq!(current_slot(), 1u64);
1
            assert!(current_author() == AccountId::from(BOB));
            // Alice and Bob collate in our chain
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
1
            let bob_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
1
            assert_eq!(authorities(), vec![alice_id, bob_id]);
1
            assert_ok!(Registrar::register(
1
                origin_of(ALICE.into()),
1
                1001.into(),
1
                empty_genesis_data(),
1
                None
            ));
1
            set_dummy_boot_node(origin_of(ALICE.into()), 1001.into());
1
            assert_ok!(Registrar::mark_valid_for_collating(
1
                root_origin(),
1
                1001.into()
            ));
            // Need to reset credits to 0 because now parachains are given free credits on register
1
            assert_ok!(ServicesPayment::set_block_production_credits(
1
                root_origin(),
1
                1001.into(),
1
                dancebox_runtime::Period::get()
            ));
1
            let credits_1001 =
1
                block_credits_to_required_balance(dancebox_runtime::Period::get(), 1001.into())
1
                    + dancebox_runtime::EXISTENTIAL_DEPOSIT;
            // Fill the tank
1
            assert_ok!(ServicesPayment::purchase_credits(
1
                origin_of(ALICE.into()),
1
                1001.into(),
1
                credits_1001
            ));
            // Assignment should happen after 2 sessions
1
            run_to_session(1u32);
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert!(assignment.container_chains.is_empty());
1
            run_to_session(2u32);
            // Charlie and Dave should be assigned to para 1001
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert_eq!(
1
                assignment.container_chains[&1001u32.into()],
1
                vec![CHARLIE.into(), DAVE.into()]
            );
1
        });
1
}
#[test]
1
fn stream_payment_works() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            (AccountId::from(ALICE), 100_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .build()
1
        .execute_with(|| {
            use pallet_stream_payment::{ChangeKind, StreamConfig};
1
            assert_ok!(StreamPayment::open_stream(
1
                origin_of(ALICE.into()),
1
                BOB.into(),
1
                StreamConfig {
1
                    rate: 2 * UNIT,
1
                    asset_id: StreamPaymentAssetId::Native,
1
                    time_unit: StreamPaymentTimeUnit::BlockNumber,
1
                    minimum_request_deadline_delay: 0,
1
                    soft_minimum_deposit: 0,
1
                },
1
                1_000 * UNIT,
            ));
1
            run_block();
1
            assert_ok!(StreamPayment::perform_payment(origin_of(CHARLIE.into()), 0));
1
            assert_eq!(
1
                Balances::free_balance(AccountId::from(BOB)),
                100_000 * UNIT + 2 * UNIT
            );
1
            assert_ok!(StreamPayment::request_change(
1
                origin_of(ALICE.into()),
                0,
1
                ChangeKind::Suggestion,
1
                StreamConfig {
1
                    rate: 1 * UNIT,
1
                    asset_id: StreamPaymentAssetId::Native,
1
                    time_unit: StreamPaymentTimeUnit::BlockNumber,
1
                    minimum_request_deadline_delay: 0,
1
                    soft_minimum_deposit: 0,
1
                },
1
                None,
            ));
1
            assert_ok!(StreamPayment::accept_requested_change(
1
                origin_of(BOB.into()),
                0,
                1, // nonce
1
                None,
            ));
1
            run_block();
1
            assert_ok!(StreamPayment::close_stream(origin_of(BOB.into()), 0));
1
            assert_eq!(
1
                Balances::free_balance(AccountId::from(BOB)),
                100_000 * UNIT + 3 * UNIT
            );
1
            assert_eq!(
1
                Balances::free_balance(AccountId::from(ALICE)),
                100_000 * UNIT - 3 * UNIT
            );
1
        });
1
}
#[test]
1
fn test_ed_plus_collator_assignment_session_purchase_works() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
            // Assert current slot gets updated
1
            assert_eq!(current_slot(), 1u64);
1
            assert!(current_author() == AccountId::from(BOB));
            // Alice and Bob collate in our chain
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
1
            let bob_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
1
            assert_eq!(authorities(), vec![alice_id, bob_id]);
1
            assert_ok!(Registrar::register(
1
                origin_of(ALICE.into()),
1
                1001.into(),
1
                empty_genesis_data(),
1
                None
            ));
1
            set_dummy_boot_node(origin_of(ALICE.into()), 1001.into());
1
            assert_ok!(Registrar::mark_valid_for_collating(
1
                root_origin(),
1
                1001.into()
            ));
            // Need to reset credits to 0 because now parachains are given free credits on register
1
            assert_ok!(ServicesPayment::set_collator_assignment_credits(
1
                root_origin(),
1
                1001.into(),
                0
            ));
1
            let credits_1001 = collator_assignment_credits_to_required_balance(1, 1001.into())
1
                + dancebox_runtime::EXISTENTIAL_DEPOSIT;
            // Fill the tank
1
            assert_ok!(ServicesPayment::purchase_credits(
1
                origin_of(ALICE.into()),
1
                1001.into(),
1
                credits_1001
            ));
            // Assignment should happen after 2 sessions
1
            run_to_session(1u32);
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert!(assignment.container_chains.is_empty());
1
            run_to_session(2u32);
            // Charlie and Dave should be assigned to para 1001
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert_eq!(
1
                assignment.container_chains[&1001u32.into()],
1
                vec![CHARLIE.into(), DAVE.into()]
            );
            // Simulate block inclusion from container chain 1001
1
            let mut sproof: ParaHeaderSproofBuilder = ParaHeaderSproofBuilder::default();
1
            let slot: u64 = 5;
1
            let s = ParaHeaderSproofBuilderItem {
1
                para_id: 1001.into(),
1
                author_id: HeaderAs::NonEncoded(sp_runtime::generic::Header::<u32, BlakeTwo256> {
1
                    parent_hash: Default::default(),
1
                    number: 1,
1
                    state_root: Default::default(),
1
                    extrinsics_root: Default::default(),
1
                    digest: sp_runtime::generic::Digest {
1
                        logs: vec![DigestItem::PreRuntime(AURA_ENGINE_ID, slot.encode())],
1
                    },
1
                }),
1
            };
1
            sproof.items.push(s);
1
            set_author_noting_inherent_data(sproof);
1
            run_block();
            // After this it should not be assigned anymore, since credits are not payable
1
            run_to_session(4u32);
            // Nobody should be assigned to para 1001
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert_eq!(assignment.container_chains.get(&1001u32.into()), None,);
1
        });
1
}
#[test]
1
fn test_ed_plus_collator_assignment_credit_session_minus_1_purchase_fails() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
            // Assert current slot gets updated
1
            assert_eq!(current_slot(), 1u64);
1
            assert!(current_author() == AccountId::from(BOB));
            // Alice and Bob collate in our chain
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
1
            let bob_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
1
            assert_eq!(authorities(), vec![alice_id, bob_id]);
1
            assert_ok!(Registrar::register(
1
                origin_of(ALICE.into()),
1
                1001.into(),
1
                empty_genesis_data(),
1
                None
            ));
1
            set_dummy_boot_node(origin_of(ALICE.into()), 1001.into());
1
            assert_ok!(Registrar::mark_valid_for_collating(
1
                root_origin(),
1
                1001.into()
            ));
            // Need to reset credits to 0 because now parachains are given free credits on register
1
            assert_ok!(ServicesPayment::set_collator_assignment_credits(
1
                root_origin(),
1
                1001.into(),
                0
            ));
1
            let credits_1001 = collator_assignment_credits_to_required_balance(1, 1001.into())
1
                + dancebox_runtime::EXISTENTIAL_DEPOSIT
1
                - 1;
            // Fill the tank
1
            assert_ok!(ServicesPayment::purchase_credits(
1
                origin_of(ALICE.into()),
1
                1001.into(),
1
                credits_1001
            ));
            // Assignment should happen after 2 sessions
1
            run_to_session(1u32);
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert!(assignment.container_chains.is_empty());
1
            run_to_session(2u32);
            // Charlie and Dave should not be assigned to para 1001
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert_eq!(assignment.container_chains.get(&1001u32.into()), None,);
1
        });
1
}
#[test]
1
fn test_collator_assignment_credits_with_purchase_can_be_combined() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
            // Assert current slot gets updated
1
            assert_eq!(current_slot(), 1u64);
1
            assert!(current_author() == AccountId::from(BOB));
            // Alice and Bob collate in our chain
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
1
            let bob_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
1
            assert_eq!(authorities(), vec![alice_id, bob_id]);
1
            assert_ok!(Registrar::register(
1
                origin_of(ALICE.into()),
1
                1001.into(),
1
                empty_genesis_data(),
1
                None
            ));
1
            set_dummy_boot_node(origin_of(ALICE.into()), 1001.into());
1
            assert_ok!(Registrar::mark_valid_for_collating(
1
                root_origin(),
1
                1001.into()
            ));
            // We assign one session to free credits
1
            assert_ok!(ServicesPayment::set_collator_assignment_credits(
1
                root_origin(),
1
                1001.into(),
                1
            ));
            // We buy another session through the tank
1
            let credits_1001 = collator_assignment_credits_to_required_balance(1, 1001.into())
1
                + dancebox_runtime::EXISTENTIAL_DEPOSIT;
            // Fill the tank
1
            assert_ok!(ServicesPayment::purchase_credits(
1
                origin_of(ALICE.into()),
1
                1001.into(),
1
                credits_1001
            ));
            // Assignment should happen after 2 sessions
1
            run_to_session(1u32);
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert!(assignment.container_chains.is_empty());
1
            run_to_session(2u32);
            // Charlie and Dave should be assigned to para 1001
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert_eq!(
1
                assignment.container_chains[&1001u32.into()],
1
                vec![CHARLIE.into(), DAVE.into()]
            );
1
        });
1
}
#[test]
1
fn test_block_credits_and_collator_assignation_credits_through_tank() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
            // Assert current slot gets updated
1
            assert_eq!(current_slot(), 1u64);
1
            assert!(current_author() == AccountId::from(BOB));
            // Alice and Bob collate in our chain
1
            let alice_id = get_aura_id_from_seed(&AccountId::from(ALICE).to_string());
1
            let bob_id = get_aura_id_from_seed(&AccountId::from(BOB).to_string());
1
            assert_eq!(authorities(), vec![alice_id, bob_id]);
1
            assert_ok!(Registrar::register(
1
                origin_of(ALICE.into()),
1
                1001.into(),
1
                empty_genesis_data(),
1
                None
            ));
1
            set_dummy_boot_node(origin_of(ALICE.into()), 1001.into());
1
            assert_ok!(Registrar::mark_valid_for_collating(
1
                root_origin(),
1
                1001.into()
            ));
            // We make all free credits 0
1
            assert_ok!(ServicesPayment::set_collator_assignment_credits(
1
                root_origin(),
1
                1001.into(),
                0
            ));
1
            assert_ok!(ServicesPayment::set_block_production_credits(
1
                root_origin(),
1
                1001.into(),
                0
            ));
            // We buy 2 sessions through tank
1
            let collator_assignation_credits =
1
                collator_assignment_credits_to_required_balance(2, 1001.into());
1
            let block_production_credits =
1
                block_credits_to_required_balance(dancebox_runtime::Period::get() * 2, 1001.into());
            // Fill the tank
1
            assert_ok!(ServicesPayment::purchase_credits(
1
                origin_of(ALICE.into()),
1
                1001.into(),
1
                collator_assignation_credits
1
                    + block_production_credits
1
                    + dancebox_runtime::EXISTENTIAL_DEPOSIT
            ));
            // Assignment should happen after 2 sessions
1
            run_to_session(1u32);
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert!(assignment.container_chains.is_empty());
1
            run_to_session(2u32);
            // Charlie and Dave should be assigned to para 1001
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert_eq!(
1
                assignment.container_chains[&1001u32.into()],
1
                vec![CHARLIE.into(), DAVE.into()]
            );
            // After this it should not be assigned anymore, since credits are not payable
1
            run_to_session(4u32);
            // Nobody should be assigned to para 1001
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert_eq!(assignment.container_chains.get(&1001u32.into()), None,);
1
        });
1
}
#[test]
1
fn test_migration_services_collator_assignment_payment() {
1
    ExtBuilder::default().build().execute_with(|| {
        // Register a new parachain with no credits
1
        assert_ok!(Registrar::register(
1
            origin_of(ALICE.into()),
1
            1001.into(),
1
            empty_genesis_data(),
1
            None
        ));
1
        set_dummy_boot_node(origin_of(ALICE.into()), 1001.into());
1
        assert_ok!(Registrar::mark_valid_for_collating(
1
            root_origin(),
1
            1001.into()
        ));
        // Register another parachain with no credits, do not mark this as valid for collation
1
        assert_ok!(Registrar::register(
1
            origin_of(ALICE.into()),
1
            1002.into(),
1
            empty_genesis_data(),
1
            None
        ));
1
        set_dummy_boot_node(origin_of(ALICE.into()), 1002.into());
1
        assert_ok!(Registrar::mark_valid_for_collating(
1
            root_origin(),
1
            1002.into()
        ));
        // Need to reset credits to 0 because now parachains are given free credits on register
1
        assert_ok!(ServicesPayment::set_collator_assignment_credits(
1
            root_origin(),
1
            1001.into(),
            0
        ));
1
        assert_ok!(ServicesPayment::set_collator_assignment_credits(
1
            root_origin(),
1
            1002.into(),
            0
        ));
1
        let credits_1001 =
1
            pallet_services_payment::CollatorAssignmentCredits::<Runtime>::get(ParaId::from(1001))
1
                .unwrap_or_default();
1
        assert_eq!(credits_1001, 0);
1
        let credits_1002 =
1
            pallet_services_payment::CollatorAssignmentCredits::<Runtime>::get(ParaId::from(1002))
1
                .unwrap_or_default();
1
        assert_eq!(credits_1002, 0);
        // Apply migration
1
        let migration =
1
            MigrateServicesPaymentAddCollatorAssignmentCredits::<Runtime>(Default::default());
1
        migration.migrate(Default::default());
        // Both parachains have been given credits
1
        let credits_1001 =
1
            pallet_services_payment::CollatorAssignmentCredits::<Runtime>::get(ParaId::from(1001))
1
                .unwrap_or_default();
1
        assert_eq!(
            credits_1001,
1
            dancebox_runtime::FreeCollatorAssignmentCredits::get()
        );
1
        let credits_1002 =
1
            pallet_services_payment::CollatorAssignmentCredits::<Runtime>::get(ParaId::from(1002))
1
                .unwrap_or_default();
1
        assert_eq!(
            credits_1002,
1
            dancebox_runtime::FreeCollatorAssignmentCredits::get()
        );
1
    });
1
}
#[test]
1
fn test_max_collators_uses_pending_value() {
    // Start with max_collators = 100, and collators_per_container = 2
    // Set max_collators = 2, and collators_per_container = 3
    // It should be impossible to have more than 2 collators per container at any point in time
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            // Alice gets 10k extra tokens for her mapping deposit
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001])
1
        .with_config(pallet_configuration::HostConfiguration {
1
            max_collators: 100,
1
            min_orchestrator_collators: 1,
1
            max_orchestrator_collators: 1,
1
            collators_per_container: 2,
1
            full_rotation_period: 24,
1
            ..Default::default()
1
        })
1
        .build()
1
        .execute_with(|| {
1
            run_to_block(2);
            // Initial assignment: 1 collator in orchestrator chain and 2 collators in container 1001
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert_eq!(assignment.container_chains[&1001u32.into()].len(), 2);
1
            assert_eq!(assignment.orchestrator_chain.len(), 1);
1
            assert_ok!(Configuration::set_max_collators(root_origin(), 2));
1
            assert_ok!(Configuration::set_collators_per_container(root_origin(), 3));
            // Check invariant for all intermediate assignments. We set collators_per_container = 3
            // but we also set max_collators = 2, so no collators will be assigned to container
            // chains after the change is applied.
5
            for session in 1..=4 {
4
                run_to_session(session);
4
                let assignment = CollatorAssignment::collator_container_chain();
4
                assert!(
4
                    assignment.container_chains[&1001u32.into()].len() <= 2,
                    "session {}: {} collators assigned to container chain 1001",
                    session,
                    assignment.container_chains[&1001u32.into()].len()
                );
            }
            // Final assignment: because max_collators = 2, there are only 2 collators, one in
            // orchestrator chain, and the other one idle
1
            let assignment = CollatorAssignment::collator_container_chain();
1
            assert_eq!(assignment.container_chains[&1001u32.into()].len(), 0);
1
            assert_eq!(assignment.orchestrator_chain.len(), 1);
1
        });
1
}
#[test]
1
fn test_slow_adjusting_multiplier_changes_in_response_to_consumed_weight() {
1
    ExtBuilder::default()
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .build()
1
        .execute_with(|| {
1
            end_block();
            // If the block is full, the multiplier increases
1
            let before_multiplier = TransactionPayment::next_fee_multiplier();
1
            start_block();
1
            let max_block_weights = dancebox_runtime::RuntimeBlockWeights::get();
1
            frame_support::storage::unhashed::put(
1
                &frame_support::storage::storage_prefix(b"System", b"BlockWeight"),
3
                &ConsumedWeight::new(|class| {
3
                    max_block_weights
3
                        .get(class)
3
                        .max_total
3
                        .unwrap_or(Weight::MAX)
3
                }),
            );
1
            end_block();
1
            let current_multiplier = TransactionPayment::next_fee_multiplier();
1
            assert!(current_multiplier > before_multiplier);
            // If the block is empty, the multiplier decreases
1
            let before_multiplier = TransactionPayment::next_fee_multiplier();
1
            start_block();
1
            frame_support::storage::unhashed::put(
1
                &frame_support::storage::storage_prefix(b"System", b"BlockWeight"),
3
                &ConsumedWeight::new(|_class| Weight::zero()),
            );
1
            end_block();
1
            let current_multiplier = TransactionPayment::next_fee_multiplier();
1
            assert!(current_multiplier < before_multiplier);
1
        });
1
}
#[test]
1
fn test_collator_assignment_tip_priority_on_congestion() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002, 1003])
1
        .build()
1
        .execute_with(|| {
1
            let para_id = 1003u32;
1
            let tank_funds = 100 * UNIT;
1
            let max_tip = 1 * UNIT;
1
            assert_eq!(
1
                CollatorAssignment::collator_container_chain().container_chains[&1003u32.into()]
1
                    .len(),
                0
            );
            // Send funds to tank
1
            assert_ok!(ServicesPayment::purchase_credits(
1
                origin_of(ALICE.into()),
1
                para_id.into(),
1
                tank_funds,
            ));
            // Set tip for 1003
1
            assert_ok!(ServicesPayment::set_max_tip(
1
                root_origin(),
1
                para_id.into(),
1
                Some(max_tip),
            ));
1
            run_to_session(2);
1
            assert_eq!(
1
                CollatorAssignment::collator_container_chain().container_chains[&para_id.into()]
1
                    .len(),
                2,
            );
1
        });
1
}
#[test]
1
fn test_collator_assignment_tip_charged_on_congestion() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002, 1003])
1
        .build()
1
        .execute_with(|| {
1
            let tank_funds = 100 * UNIT;
1
            let max_tip = 1 * UNIT;
1
            let para_id = 1003u32;
            // Send funds to tank
1
            assert_ok!(ServicesPayment::purchase_credits(
1
                origin_of(ALICE.into()),
1
                para_id.into(),
1
                tank_funds,
            ));
            // Set tip for para_id
1
            assert_ok!(ServicesPayment::set_max_tip(
1
                root_origin(),
1
                para_id.into(),
1
                Some(max_tip),
            ));
1
            run_to_session(1);
1
            assert_eq!(
1
                Balances::usable_balance(ServicesPayment::parachain_tank(para_id.into())),
1
                tank_funds - max_tip,
            );
1
        });
1
}
#[test]
1
fn test_collator_assignment_tip_not_assigned_on_insufficient_balance() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002, 1003])
1
        .build()
1
        .execute_with(|| {
1
            let tank_funds = 1 * UNIT;
1
            let max_tip = 1 * UNIT;
1
            let para_id = 1003u32;
            // Send insufficient funds to tank for tip for 2 sessions
1
            assert_ok!(ServicesPayment::purchase_credits(
1
                origin_of(ALICE.into()),
1
                para_id.into(),
1
                tank_funds,
            ));
            // Set tip for para_id
1
            assert_ok!(ServicesPayment::set_max_tip(
1
                root_origin(),
1
                para_id.into(),
1
                Some(max_tip),
            ));
1
            run_to_session(1);
1
            assert_eq!(
1
                CollatorAssignment::collator_container_chain().container_chains[&para_id.into()]
1
                    .len(),
                0
            );
1
        });
1
}
#[test]
1
fn test_collator_assignment_tip_only_charge_willing_paras() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
            (AccountId::from(EVE), 100_000 * UNIT),
1
            (AccountId::from(FERDIE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
            (AccountId::from(EVE), 100 * UNIT),
1
            (AccountId::from(FERDIE), 100 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002, 1003])
1
        .build()
1
        .execute_with(|| {
1
            let tank_funds = 100 * UNIT;
1
            let max_tip = 1 * UNIT;
1
            let para_id_with_tip = 1003u32;
1
            let para_id_without_tip = 1001u32;
            // Send funds to tank to both paras
1
            assert_ok!(ServicesPayment::purchase_credits(
1
                origin_of(ALICE.into()),
1
                para_id_with_tip.into(),
1
                tank_funds,
            ));
1
            assert_ok!(ServicesPayment::purchase_credits(
1
                origin_of(ALICE.into()),
1
                para_id_without_tip.into(),
1
                tank_funds,
            ));
            // Only set tip for 1003
1
            assert_ok!(ServicesPayment::set_max_tip(
1
                root_origin(),
1
                para_id_with_tip.into(),
1
                Some(max_tip),
            ));
1
            run_to_session(2);
1
            let assignment = CollatorAssignment::collator_container_chain().container_chains;
            // 2 out of the 3 paras should have collators assigned, with one paying tip to get
            // prioritized, and the other selected at random that should not be charged any tips
1
            assert_eq!(assignment[&para_id_with_tip.into()].len(), 2);
1
            assert_eq!(
1
                Balances::usable_balance(ServicesPayment::parachain_tank(para_id_with_tip.into())),
1
                tank_funds - max_tip * 2,
            );
1
            assert_eq!(assignment[&para_id_without_tip.into()].len(), 2);
1
            assert_eq!(
1
                Balances::usable_balance(ServicesPayment::parachain_tank(
1
                    para_id_without_tip.into()
                )),
                tank_funds,
            );
1
        });
1
}
#[test]
1
fn test_collator_assignment_tip_withdraw_min_tip() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
            (AccountId::from(CHARLIE), 100_000 * UNIT),
1
            (AccountId::from(DAVE), 100_000 * UNIT),
1
            (AccountId::from(EVE), 100_000 * UNIT),
1
            (AccountId::from(FERDIE), 100_000 * UNIT),
1
        ])
1
        .with_collators(vec![
1
            (AccountId::from(ALICE), 210 * UNIT),
1
            (AccountId::from(BOB), 100 * UNIT),
1
            (AccountId::from(CHARLIE), 100 * UNIT),
1
            (AccountId::from(DAVE), 100 * UNIT),
1
            (AccountId::from(EVE), 100 * UNIT),
1
            (AccountId::from(FERDIE), 100 * UNIT),
1
        ])
1
        .with_empty_parachains(vec![1001, 1002, 1003])
1
        .build()
1
        .execute_with(|| {
1
            let tank_funds = 100 * UNIT;
1
            let max_tip_1003 = 3 * UNIT;
1
            let max_tip_1002 = 2 * UNIT;
1
            let para_id_1003 = 1003u32;
1
            let para_id_1002 = 1002u32;
            // Send funds to tank to both paras
1
            assert_ok!(ServicesPayment::purchase_credits(
1
                origin_of(ALICE.into()),
1
                para_id_1003.into(),
1
                tank_funds,
            ));
1
            assert_ok!(ServicesPayment::purchase_credits(
1
                origin_of(ALICE.into()),
1
                para_id_1002.into(),
1
                tank_funds,
            ));
            // Set tips
1
            assert_ok!(ServicesPayment::set_max_tip(
1
                root_origin(),
1
                para_id_1003.into(),
1
                Some(max_tip_1003),
            ));
1
            assert_ok!(ServicesPayment::set_max_tip(
1
                root_origin(),
1
                para_id_1002.into(),
1
                Some(max_tip_1002),
            ));
1
            run_to_session(2);
1
            assert_eq!(
1
                CollatorAssignment::collator_container_chain().container_chains
1
                    [&para_id_1003.into()]
1
                    .len(),
                2
            );
1
            assert_eq!(
1
                CollatorAssignment::collator_container_chain().container_chains
1
                    [&para_id_1002.into()]
1
                    .len(),
                2
            );
            // Should have withdrawn the lowest tip from both paras
1
            assert_eq!(
1
                Balances::usable_balance(ServicesPayment::parachain_tank(para_id_1003.into())),
1
                tank_funds - max_tip_1002 * 2,
            );
1
            assert_eq!(
1
                Balances::usable_balance(ServicesPayment::parachain_tank(para_id_1002.into())),
1
                tank_funds - max_tip_1002 * 2,
            );
1
        });
1
}
#[test]
1
fn test_migration_registrar_reserves_to_hold() {
1
    ExtBuilder::default()
1
        .with_balances(vec![(AccountId::from(DAVE), 100_000 * UNIT)])
1
        .build()
1
        .execute_with(|| {
            use {
                frame_support::traits::{fungible::InspectHold, ReservableCurrency},
                pallet_registrar::DepositInfo,
                tanssi_runtime_common::migrations::RegistrarReserveToHoldMigration,
            };
1
            let deposit: Balance = 100 * UNIT;
1
            let account: AccountId = DAVE.into();
1
            assert_ok!(Balances::reserve(&account, deposit));
1
            pallet_registrar::RegistrarDeposit::<Runtime>::insert(
1
                ParaId::from(1001),
1
                DepositInfo {
1
                    creator: account.clone(),
1
                    deposit,
1
                },
            );
1
            assert_eq!(Balances::reserved_balance(&account), deposit.clone(),);
            // Apply migration
1
            let migration = RegistrarReserveToHoldMigration::<Runtime>(Default::default());
1
            migration.migrate(Default::default());
            // Holds also count as reserved balance, so the total reserved amount
            // shouldn't change after the migration
1
            assert_eq!(Balances::reserved_balance(&account), deposit.clone(),);
1
            assert_eq!(
1
                Balances::balance_on_hold(
1
                    &pallet_registrar::HoldReason::RegistrarDeposit.into(),
1
                    &account
                ),
                deposit
            );
1
        })
1
}
#[test]
1
fn test_migration_stream_payment_config_new_fields() {
1
    ExtBuilder::default().build().execute_with(|| {
        use pallet_stream_payment::{
            migrations::{OldChangeRequest, OldStream, OldStreamConfig},
            ChangeKind, ChangeRequest, DepositChange, Party, Stream, StreamConfig,
        };
        use tanssi_runtime_common::migrations::MigrateStreamPaymentNewConfigFields;
1
        frame_support::storage::unhashed::put(
1
            &pallet_stream_payment::Streams::<Runtime>::hashed_key_for(0),
1
            &OldStream::<AccountId, StreamPaymentTimeUnit, StreamPaymentAssetId, Balance> {
1
                source: ALICE.into(),
1
                target: BOB.into(),
1
                config: OldStreamConfig {
1
                    time_unit: StreamPaymentTimeUnit::Timestamp,
1
                    asset_id: StreamPaymentAssetId::Native,
1
                    rate: 41,
1
                },
1
                deposit: 42,
1
                last_time_updated: 43,
1
                request_nonce: 44,
1
                pending_request: Some(OldChangeRequest {
1
                    requester: Party::Source,
1
                    kind: ChangeKind::Mandatory { deadline: 45 },
1
                    new_config: OldStreamConfig {
1
                        time_unit: StreamPaymentTimeUnit::BlockNumber,
1
                        asset_id: StreamPaymentAssetId::Native,
1
                        rate: 46,
1
                    },
1
                    deposit_change: Some(DepositChange::Absolute(47)),
1
                }),
1
                opening_deposit: 48,
1
            },
        );
1
        frame_support::storage::unhashed::put(
1
            &pallet_stream_payment::Streams::<Runtime>::hashed_key_for(1),
1
            &OldStream::<AccountId, StreamPaymentTimeUnit, StreamPaymentAssetId, Balance> {
1
                source: CHARLIE.into(),
1
                target: ALICE.into(),
1
                config: OldStreamConfig {
1
                    time_unit: StreamPaymentTimeUnit::Timestamp,
1
                    asset_id: StreamPaymentAssetId::Native,
1
                    rate: 100,
1
                },
1
                deposit: 101,
1
                last_time_updated: 102,
1
                request_nonce: 103,
1
                pending_request: None,
1
                opening_deposit: 104,
1
            },
        );
1
        let migration = MigrateStreamPaymentNewConfigFields::<Runtime>(Default::default());
1
        migration.migrate(Default::default());
1
        assert_eq!(
1
            pallet_stream_payment::Streams::<Runtime>::get(0).unwrap(),
1
            Stream {
1
                source: ALICE.into(),
1
                target: BOB.into(),
1
                config: StreamConfig {
1
                    time_unit: StreamPaymentTimeUnit::Timestamp,
1
                    asset_id: StreamPaymentAssetId::Native,
1
                    rate: 41,
1
                    minimum_request_deadline_delay: 0,
1
                    soft_minimum_deposit: 0,
1
                },
1
                deposit: 42,
1
                last_time_updated: 43,
1
                request_nonce: 44,
1
                pending_request: Some(ChangeRequest {
1
                    requester: Party::Source,
1
                    kind: ChangeKind::Mandatory { deadline: 45 },
1
                    new_config: StreamConfig {
1
                        time_unit: StreamPaymentTimeUnit::BlockNumber,
1
                        asset_id: StreamPaymentAssetId::Native,
1
                        rate: 46,
1
                        minimum_request_deadline_delay: 0,
1
                        soft_minimum_deposit: 0,
1
                    },
1
                    deposit_change: Some(DepositChange::Absolute(47)),
1
                }),
1
                opening_deposit: 48,
1
            }
        );
1
        assert_eq!(
1
            pallet_stream_payment::Streams::<Runtime>::get(1).unwrap(),
1
            Stream {
1
                source: CHARLIE.into(),
1
                target: ALICE.into(),
1
                config: StreamConfig {
1
                    time_unit: StreamPaymentTimeUnit::Timestamp,
1
                    asset_id: StreamPaymentAssetId::Native,
1
                    rate: 100,
1
                    minimum_request_deadline_delay: 0,
1
                    soft_minimum_deposit: 0,
1
                },
1
                deposit: 101,
1
                last_time_updated: 102,
1
                request_nonce: 103,
1
                pending_request: None,
1
                opening_deposit: 104,
1
            }
        );
1
    })
1
}
#[test]
1
fn test_migration_pooled_staking_summaries() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
        ])
1
        .build()
1
        .execute_with(|| {
            use pallet_pooled_staking::{
                CandidateSummaries, CandidateSummary, DelegatorCandidateSummaries,
                DelegatorCandidateSummary, Pools, PoolsKey,
            };
            // We'll write directly shares to storage, then run the migrations and
            // checks the summaries matches.
1
            let alice = AccountId::from(ALICE);
1
            let bob = AccountId::from(BOB);
1
            let charlie = AccountId::from(CHARLIE);
1
            Pools::<Runtime>::insert(
1
                alice.clone(),
1
                PoolsKey::JoiningShares {
1
                    delegator: alice.clone(),
1
                },
                42,
            );
1
            Pools::<Runtime>::insert(
1
                alice.clone(),
1
                PoolsKey::AutoCompoundingShares {
1
                    delegator: bob.clone(),
1
                },
                42,
            );
1
            Pools::<Runtime>::insert(
1
                bob.clone(),
1
                PoolsKey::ManualRewardsShares {
1
                    delegator: alice.clone(),
1
                },
                42,
            );
            // let's test 0 value is ignored
1
            Pools::<Runtime>::insert(
1
                bob.clone(),
1
                PoolsKey::ManualRewardsShares {
1
                    delegator: charlie.clone(),
1
                },
                0,
            );
            // Run migration
1
            StorageVersion::new(0u16).put::<PooledStaking>(); // set version required for migration to run
1
            pallet_pooled_staking::migrations::MigrationGenerateSummaries::<Runtime>::step(
1
                None,
1
                &mut WeightMeter::new(),
            )
1
            .expect("to run without errors");
            // Check output
1
            assert_eq!(CandidateSummaries::<Runtime>::iter().count(), 2);
1
            assert_eq!(
1
                CandidateSummaries::<Runtime>::get(&alice),
1
                CandidateSummary {
1
                    delegators: 2,
1
                    joining_delegators: 1,
1
                    auto_compounding_delegators: 1,
1
                    ..Default::default()
1
                }
            );
1
            assert_eq!(
1
                CandidateSummaries::<Runtime>::get(&bob),
1
                CandidateSummary {
1
                    delegators: 1,
1
                    manual_rewards_delegators: 1,
1
                    ..Default::default()
1
                }
            );
1
            assert_eq!(DelegatorCandidateSummaries::<Runtime>::iter().count(), 3);
1
            assert_eq!(
1
                DelegatorCandidateSummaries::<Runtime>::get(&alice, &alice),
1
                DelegatorCandidateSummary::default().with_joining(true)
            );
1
            assert_eq!(
1
                DelegatorCandidateSummaries::<Runtime>::get(&alice, &bob),
1
                DelegatorCandidateSummary::default().with_manual(true)
            );
1
            assert_eq!(
1
                DelegatorCandidateSummaries::<Runtime>::get(&bob, &alice),
1
                DelegatorCandidateSummary::default().with_auto(true)
            );
1
        });
1
}
#[test]
1
fn test_container_deregister_unassign_data_preserver() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
        ])
1
        .build()
1
        .execute_with(|| {
            use pallet_data_preservers::{
                AssignerParameterOf, NodeType, ParaIdsFilter, Profile, ProviderRequestOf,
            };
1
            let profile = Profile {
1
                bootnode_url: Some(b"test".to_vec().try_into().unwrap()),
1
                direct_rpc_urls: Default::default(),
1
                proxy_rpc_urls: Default::default(),
1
                para_ids: ParaIdsFilter::AnyParaId,
1
                node_type: NodeType::Substrate,
1
                assignment_request: ProviderRequestOf::<Runtime>::Free,
1
                additional_info: Default::default(),
1
            };
1
            let para_id = ParaId::from(1002);
1
            let profile_id = 0u64;
1
            assert_ok!(Registrar::register(
1
                origin_of(ALICE.into()),
1
                para_id,
1
                empty_genesis_data(),
1
                None
            ));
1
            assert_ok!(DataPreservers::create_profile(
1
                origin_of(BOB.into()),
1
                profile.clone(),
            ));
            // Start assignment
1
            assert_ok!(DataPreservers::start_assignment(
1
                origin_of(ALICE.into()),
1
                profile_id,
1
                para_id,
1
                AssignerParameterOf::<Runtime>::Free
            ));
1
            assert!(pallet_data_preservers::Assignments::<Runtime>::get(para_id).contains(&0u64));
            // Deregister from Registrar
1
            assert_ok!(Registrar::deregister(root_origin(), para_id), ());
            // Check DataPreserver assignment has been cleared
1
            assert!(pallet_data_preservers::Assignments::<Runtime>::get(para_id).is_empty());
1
        });
1
}
#[test]
1
fn test_data_preserver_with_stream_payment() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
        ])
1
        .build()
1
        .execute_with(|| {
            use pallet_data_preservers::{
                AssignerParameterOf, NodeType, ParaIdsFilter, Profile, ProviderRequestOf,
            };
1
            let profile = Profile {
1
                bootnode_url: Some(b"test".to_vec().try_into().unwrap()),
1
                direct_rpc_urls: Default::default(),
1
                proxy_rpc_urls: Default::default(),
1
                para_ids: ParaIdsFilter::AnyParaId,
1
                node_type: NodeType::Substrate,
1
                additional_info: Default::default(),
1
                assignment_request: ProviderRequestOf::<Runtime>::StreamPayment {
1
                    config: StreamConfig {
1
                        time_unit: StreamPaymentTimeUnit::BlockNumber,
1
                        asset_id: StreamPaymentAssetId::Native,
1
                        rate: 42,
1
                        minimum_request_deadline_delay: 0,
1
                        soft_minimum_deposit: 0,
1
                    },
1
                },
1
            };
1
            let para_id = ParaId::from(1002);
1
            let profile_id = 0u64;
1
            assert_ok!(Registrar::register(
1
                origin_of(ALICE.into()),
1
                para_id,
1
                empty_genesis_data(),
1
                None
            ));
1
            assert_ok!(DataPreservers::create_profile(
1
                origin_of(BOB.into()),
1
                profile.clone(),
            ));
            // Start assignment
1
            assert_ok!(DataPreservers::start_assignment(
1
                origin_of(ALICE.into()),
1
                profile_id,
1
                para_id,
1
                AssignerParameterOf::<Runtime>::StreamPayment {
1
                    initial_deposit: 1_000
1
                }
            ));
1
            assert!(
1
                pallet_data_preservers::Assignments::<Runtime>::get(para_id).contains(&profile_id)
            );
1
            let profile = pallet_data_preservers::Profiles::<Runtime>::get(profile_id)
1
                .expect("profile to exists");
1
            let (assigned_para_id, witness) = profile.assignment.expect("profile to be assigned");
1
            assert_eq!(assigned_para_id, para_id);
1
            assert_eq!(
                witness,
                tp_data_preservers_common::AssignmentWitness::StreamPayment { stream_id: 0 }
            );
1
        });
1
}
#[test]
1
fn test_data_preserver_kind_needs_to_match() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            (AccountId::from(ALICE), 210_000 * UNIT),
1
            (AccountId::from(BOB), 100_000 * UNIT),
1
        ])
1
        .build()
1
        .execute_with(|| {
            use pallet_data_preservers::{
                AssignerParameterOf, NodeType, ParaIdsFilter, Profile, ProviderRequestOf,
            };
1
            let profile = Profile {
1
                bootnode_url: Some(b"test".to_vec().try_into().unwrap()),
1
                direct_rpc_urls: Default::default(),
1
                proxy_rpc_urls: Default::default(),
1
                para_ids: ParaIdsFilter::AnyParaId,
1
                node_type: NodeType::Substrate,
1
                additional_info: Default::default(),
1
                assignment_request: ProviderRequestOf::<Runtime>::Free,
1
            };
1
            let para_id = ParaId::from(1002);
1
            let profile_id = 0u64;
1
            assert_ok!(Registrar::register(
1
                origin_of(ALICE.into()),
1
                para_id,
1
                empty_genesis_data(),
1
                None
            ));
1
            assert_ok!(DataPreservers::create_profile(
1
                origin_of(BOB.into()),
1
                profile.clone(),
            ));
            // Start assignment
1
            assert_err!(
1
                DataPreservers::start_assignment(
1
                    origin_of(ALICE.into()),
1
                    profile_id,
1
                    para_id,
1
                    AssignerParameterOf::<Runtime>::StreamPayment {
1
                        initial_deposit: 1_000
1
                    }
                ),
1
                pallet_data_preservers::Error::<Runtime>::AssignmentPaymentRequestParameterMismatch
            );
1
        });
1
}
#[test]
1
fn test_registrar_extrinsic_permissions() {
1
    ExtBuilder::default()
1
        .with_balances(vec![
1
            (
1
                cumulus_primitives_core::relay_chain::AccountId::from(ALICE),
1
                210_000 * UNIT,
1
            ),
1
            (
1
                cumulus_primitives_core::relay_chain::AccountId::from(BOB),
1
                100_000 * UNIT,
1
            ),
1
        ])
1
        .with_empty_parachains(vec![1001])
1
        .build()
1
        .execute_with(|| {
1
            let para_id = ParaId::from(1001);
            // Pause container chain should fail if not para manager
1
            assert_noop!(
1
                Registrar::pause_container_chain(origin_of(BOB.into()), para_id),
1
                BadOrigin
            );
            // Set Bob as para manager
1
            assert_ok!(Registrar::set_para_manager(
1
                root_origin(),
1
                para_id,
1
                AccountId::from(BOB)
            ));
            // Pause container chain should succeed if para manager
1
            assert_ok!(
1
                Registrar::pause_container_chain(origin_of(BOB.into()), para_id),
                ()
            );
1
        });
1
}