1
// Copyright 2019-2022 PureStake Inc.
2
// This file is part of Moonbeam.
3

            
4
// Moonbeam 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
// Moonbeam 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 Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16

            
17
//! Test utilities
18
use crate::{
19
	self as pallet_parachain_staking, InflationDistributionAccount, InflationDistributionConfig,
20
};
21
use crate::{
22
	pallet, AwardedPts, Config, Event as ParachainStakingEvent, InflationInfo, Points, Range,
23
	COLLATOR_LOCK_ID, DELEGATOR_LOCK_ID,
24
};
25
use block_author::BlockAuthor as BlockAuthorMap;
26
use frame_support::{
27
	construct_runtime, parameter_types,
28
	traits::{Everything, Get, LockIdentifier, OnFinalize, OnInitialize},
29
	weights::{constants::RocksDbWeight, Weight},
30
};
31
use frame_system::pallet_prelude::BlockNumberFor;
32
use sp_consensus_slots::Slot;
33
use sp_core::H256;
34
use sp_io;
35
use sp_runtime::BuildStorage;
36
use sp_runtime::{
37
	traits::{BlakeTwo256, IdentityLookup},
38
	Perbill, Percent,
39
};
40

            
41
pub type AccountId = u64;
42
pub type Balance = u128;
43
pub type BlockNumber = BlockNumberFor<Test>;
44

            
45
type Block = frame_system::mocking::MockBlockU32<Test>;
46

            
47
// Configure a mock runtime to test the pallet.
48
210467
construct_runtime!(
49
	pub enum Test
50
	{
51
		System: frame_system,
52
		Balances: pallet_balances,
53
		ParachainStaking: pallet_parachain_staking,
54
		BlockAuthor: block_author,
55
	}
56
378024
);
57

            
58
parameter_types! {
59
	pub const BlockHashCount: u32 = 250;
60
	pub const MaximumBlockWeight: Weight = Weight::from_parts(1024, 1);
61
	pub const MaximumBlockLength: u32 = 2 * 1024;
62
	pub const AvailableBlockRatio: Perbill = Perbill::one();
63
	pub const SS58Prefix: u8 = 42;
64
}
65
impl frame_system::Config for Test {
66
	type BaseCallFilter = Everything;
67
	type DbWeight = RocksDbWeight;
68
	type RuntimeOrigin = RuntimeOrigin;
69
	type RuntimeTask = RuntimeTask;
70
	type Nonce = u64;
71
	type Block = Block;
72
	type RuntimeCall = RuntimeCall;
73
	type Hash = H256;
74
	type Hashing = BlakeTwo256;
75
	type AccountId = AccountId;
76
	type Lookup = IdentityLookup<Self::AccountId>;
77
	type RuntimeEvent = RuntimeEvent;
78
	type BlockHashCount = BlockHashCount;
79
	type Version = ();
80
	type PalletInfo = PalletInfo;
81
	type AccountData = pallet_balances::AccountData<Balance>;
82
	type OnNewAccount = ();
83
	type OnKilledAccount = ();
84
	type SystemWeightInfo = ();
85
	type BlockWeights = ();
86
	type BlockLength = ();
87
	type SS58Prefix = SS58Prefix;
88
	type OnSetCode = ();
89
	type MaxConsumers = frame_support::traits::ConstU32<16>;
90
	type SingleBlockMigrations = ();
91
	type MultiBlockMigrator = ();
92
	type PreInherents = ();
93
	type PostInherents = ();
94
	type PostTransactions = ();
95
}
96
parameter_types! {
97
	pub const ExistentialDeposit: u128 = 0;
98
}
99
impl pallet_balances::Config for Test {
100
	type MaxReserves = ();
101
	type ReserveIdentifier = [u8; 4];
102
	type MaxLocks = ();
103
	type Balance = Balance;
104
	type RuntimeEvent = RuntimeEvent;
105
	type DustRemoval = ();
106
	type ExistentialDeposit = ExistentialDeposit;
107
	type AccountStore = System;
108
	type WeightInfo = ();
109
	type RuntimeHoldReason = ();
110
	type FreezeIdentifier = ();
111
	type MaxFreezes = ();
112
	type RuntimeFreezeReason = ();
113
}
114
impl block_author::Config for Test {}
115
const GENESIS_BLOCKS_PER_ROUND: BlockNumber = 5;
116
const GENESIS_COLLATOR_COMMISSION: Perbill = Perbill::from_percent(20);
117
const GENESIS_PARACHAIN_BOND_RESERVE_PERCENT: Percent = Percent::from_percent(30);
118
const GENESIS_NUM_SELECTED_CANDIDATES: u32 = 5;
119
parameter_types! {
120
	pub const MinBlocksPerRound: u32 = 3;
121
	pub const MaxOfflineRounds: u32 = 1;
122
	pub const LeaveCandidatesDelay: u32 = 2;
123
	pub const CandidateBondLessDelay: u32 = 2;
124
	pub const LeaveDelegatorsDelay: u32 = 2;
125
	pub const RevokeDelegationDelay: u32 = 2;
126
	pub const DelegationBondLessDelay: u32 = 2;
127
	pub const RewardPaymentDelay: u32 = 2;
128
	pub const MinSelectedCandidates: u32 = GENESIS_NUM_SELECTED_CANDIDATES;
129
	pub const MaxTopDelegationsPerCandidate: u32 = 4;
130
	pub const MaxBottomDelegationsPerCandidate: u32 = 4;
131
	pub const MaxDelegationsPerDelegator: u32 = 4;
132
	pub const MinCandidateStk: u128 = 10;
133
	pub const MinDelegation: u128 = 3;
134
	pub const MaxCandidates: u32 = 200;
135
}
136

            
137
pub struct StakingRoundSlotProvider;
138
impl Get<Slot> for StakingRoundSlotProvider {
139
227
	fn get() -> Slot {
140
227
		let block_number: u64 = System::block_number().into();
141
227
		Slot::from(block_number)
142
227
	}
143
}
144

            
145
impl Config for Test {
146
	type RuntimeEvent = RuntimeEvent;
147
	type Currency = Balances;
148
	type MonetaryGovernanceOrigin = frame_system::EnsureRoot<AccountId>;
149
	type MinBlocksPerRound = MinBlocksPerRound;
150
	type MaxOfflineRounds = MaxOfflineRounds;
151
	type LeaveCandidatesDelay = LeaveCandidatesDelay;
152
	type CandidateBondLessDelay = CandidateBondLessDelay;
153
	type LeaveDelegatorsDelay = LeaveDelegatorsDelay;
154
	type RevokeDelegationDelay = RevokeDelegationDelay;
155
	type DelegationBondLessDelay = DelegationBondLessDelay;
156
	type RewardPaymentDelay = RewardPaymentDelay;
157
	type MinSelectedCandidates = MinSelectedCandidates;
158
	type MaxTopDelegationsPerCandidate = MaxTopDelegationsPerCandidate;
159
	type MaxBottomDelegationsPerCandidate = MaxBottomDelegationsPerCandidate;
160
	type MaxDelegationsPerDelegator = MaxDelegationsPerDelegator;
161
	type MinCandidateStk = MinCandidateStk;
162
	type MinDelegation = MinDelegation;
163
	type BlockAuthor = BlockAuthor;
164
	type OnCollatorPayout = ();
165
	type PayoutCollatorReward = ();
166
	type OnInactiveCollator = ();
167
	type OnNewRound = ();
168
	type SlotProvider = StakingRoundSlotProvider;
169
	type WeightInfo = ();
170
	type MaxCandidates = MaxCandidates;
171
	type SlotDuration = frame_support::traits::ConstU64<6_000>;
172
	type BlockTime = frame_support::traits::ConstU64<6_000>;
173
}
174

            
175
pub(crate) struct ExtBuilder {
176
	// endowed accounts with balances
177
	balances: Vec<(AccountId, Balance)>,
178
	// [collator, amount]
179
	collators: Vec<(AccountId, Balance)>,
180
	// [delegator, collator, delegation_amount, auto_compound_percent]
181
	delegations: Vec<(AccountId, AccountId, Balance, Percent)>,
182
	// inflation config
183
	inflation: InflationInfo<Balance>,
184
}
185

            
186
impl Default for ExtBuilder {
187
259
	fn default() -> ExtBuilder {
188
259
		ExtBuilder {
189
259
			balances: vec![],
190
259
			delegations: vec![],
191
259
			collators: vec![],
192
259
			inflation: InflationInfo {
193
259
				expect: Range {
194
259
					min: 700,
195
259
					ideal: 700,
196
259
					max: 700,
197
259
				},
198
259
				// not used
199
259
				annual: Range {
200
259
					min: Perbill::from_percent(50),
201
259
					ideal: Perbill::from_percent(50),
202
259
					max: Perbill::from_percent(50),
203
259
				},
204
259
				// unrealistically high parameterization, only for testing
205
259
				round: Range {
206
259
					min: Perbill::from_percent(5),
207
259
					ideal: Perbill::from_percent(5),
208
259
					max: Perbill::from_percent(5),
209
259
				},
210
259
			},
211
259
		}
212
259
	}
213
}
214

            
215
impl ExtBuilder {
216
192
	pub(crate) fn with_balances(mut self, balances: Vec<(AccountId, Balance)>) -> Self {
217
192
		self.balances = balances;
218
192
		self
219
192
	}
220

            
221
179
	pub(crate) fn with_candidates(mut self, collators: Vec<(AccountId, Balance)>) -> Self {
222
179
		self.collators = collators;
223
179
		self
224
179
	}
225

            
226
103
	pub(crate) fn with_delegations(
227
103
		mut self,
228
103
		delegations: Vec<(AccountId, AccountId, Balance)>,
229
103
	) -> Self {
230
103
		self.delegations = delegations
231
103
			.into_iter()
232
8946
			.map(|d| (d.0, d.1, d.2, Percent::zero()))
233
103
			.collect();
234
103
		self
235
103
	}
236

            
237
2
	pub(crate) fn with_auto_compounding_delegations(
238
2
		mut self,
239
2
		delegations: Vec<(AccountId, AccountId, Balance, Percent)>,
240
2
	) -> Self {
241
2
		self.delegations = delegations;
242
2
		self
243
2
	}
244

            
245
	#[allow(dead_code)]
246
	pub(crate) fn with_inflation(mut self, inflation: InflationInfo<Balance>) -> Self {
247
		self.inflation = inflation;
248
		self
249
	}
250

            
251
259
	pub(crate) fn build(self) -> sp_io::TestExternalities {
252
259
		let mut t = frame_system::GenesisConfig::<Test>::default()
253
259
			.build_storage()
254
259
			.expect("Frame system builds valid default genesis config");
255
259

            
256
259
		pallet_balances::GenesisConfig::<Test> {
257
259
			balances: self.balances,
258
259
		}
259
259
		.assimilate_storage(&mut t)
260
259
		.expect("Pallet balances storage can be assimilated");
261
259
		pallet_parachain_staking::GenesisConfig::<Test> {
262
259
			candidates: self.collators,
263
259
			delegations: self.delegations,
264
259
			inflation_config: self.inflation,
265
259
			collator_commission: GENESIS_COLLATOR_COMMISSION,
266
259
			parachain_bond_reserve_percent: GENESIS_PARACHAIN_BOND_RESERVE_PERCENT,
267
259
			blocks_per_round: GENESIS_BLOCKS_PER_ROUND,
268
259
			num_selected_candidates: GENESIS_NUM_SELECTED_CANDIDATES,
269
259
		}
270
259
		.assimilate_storage(&mut t)
271
259
		.expect("Parachain Staking's storage can be assimilated");
272
259

            
273
259
		let mut ext = sp_io::TestExternalities::new(t);
274
259
		ext.execute_with(|| System::set_block_number(1));
275
259
		ext
276
259
	}
277
}
278

            
279
/// Rolls forward one block. Returns the new block number.
280
1125
fn roll_one_block() -> BlockNumber {
281
1125
	Balances::on_finalize(System::block_number());
282
1125
	System::on_finalize(System::block_number());
283
1125
	System::set_block_number(System::block_number() + 1);
284
1125
	System::reset_events();
285
1125
	System::on_initialize(System::block_number());
286
1125
	Balances::on_initialize(System::block_number());
287
1125
	ParachainStaking::on_initialize(System::block_number());
288
1125
	System::block_number()
289
1125
}
290

            
291
/// Rolls to the desired block. Returns the number of blocks played.
292
131
pub(crate) fn roll_to(n: BlockNumber) -> BlockNumber {
293
131
	let mut num_blocks = 0;
294
131
	let mut block = System::block_number();
295
1147
	while block < n {
296
1016
		block = roll_one_block();
297
1016
		num_blocks += 1;
298
1016
	}
299
131
	num_blocks
300
131
}
301

            
302
/// Rolls desired number of blocks. Returns the final block.
303
63
pub(crate) fn roll_blocks(num_blocks: u32) -> BlockNumber {
304
63
	let mut block = System::block_number();
305
109
	for _ in 0..num_blocks {
306
109
		block = roll_one_block();
307
109
	}
308
63
	block
309
63
}
310

            
311
/// Rolls block-by-block to the beginning of the specified round.
312
/// This will complete the block in which the round change occurs.
313
/// Returns the number of blocks played.
314
71
pub(crate) fn roll_to_round_begin(round: BlockNumber) -> BlockNumber {
315
71
	let block = (round - 1) * GENESIS_BLOCKS_PER_ROUND;
316
71
	roll_to(block)
317
71
}
318

            
319
/// Rolls block-by-block to the end of the specified round.
320
/// The block following will be the one in which the specified round change occurs.
321
9
pub(crate) fn roll_to_round_end(round: BlockNumber) -> BlockNumber {
322
9
	let block = round * GENESIS_BLOCKS_PER_ROUND - 1;
323
9
	roll_to(block)
324
9
}
325

            
326
38
pub(crate) fn inflation_configs(
327
38
	pbr: AccountId,
328
38
	pbr_percent: u8,
329
38
	treasury: AccountId,
330
38
	treasury_percent: u8,
331
38
) -> InflationDistributionConfig<AccountId> {
332
38
	[
333
38
		InflationDistributionAccount {
334
38
			account: pbr,
335
38
			percent: Percent::from_percent(pbr_percent),
336
38
		},
337
38
		InflationDistributionAccount {
338
38
			account: treasury,
339
38
			percent: Percent::from_percent(treasury_percent),
340
38
		},
341
38
	]
342
38
	.into()
343
38
}
344

            
345
202
pub(crate) fn events() -> Vec<pallet::Event<Test>> {
346
202
	System::events()
347
202
		.into_iter()
348
712
		.map(|r| r.event)
349
712
		.filter_map(|e| {
350
712
			if let RuntimeEvent::ParachainStaking(inner) = e {
351
547
				Some(inner)
352
			} else {
353
165
				None
354
			}
355
712
		})
356
202
		.collect::<Vec<_>>()
357
202
}
358

            
359
/// Asserts that some events were never emitted.
360
///
361
/// # Example
362
///
363
/// ```
364
/// assert_no_events!();
365
/// ```
366
#[macro_export]
367
macro_rules! assert_no_events {
368
	() => {
369
		similar_asserts::assert_eq!(Vec::<Event<Test>>::new(), crate::mock::events())
370
	};
371
}
372

            
373
/// Asserts that emitted events match exactly the given input.
374
///
375
/// # Example
376
///
377
/// ```
378
/// assert_events_eq!(
379
///		Foo { x: 1, y: 2 },
380
///		Bar { value: "test" },
381
///		Baz { a: 10, b: 20 },
382
/// );
383
/// ```
384
#[macro_export]
385
macro_rules! assert_events_eq {
386
	($event:expr) => {
387
		similar_asserts::assert_eq!(vec![$event], crate::mock::events());
388
	};
389
	($($events:expr,)+) => {
390
		similar_asserts::assert_eq!(vec![$($events,)+], crate::mock::events());
391
	};
392
}
393

            
394
/// Asserts that some emitted events match the given input.
395
///
396
/// # Example
397
///
398
/// ```
399
/// assert_events_emitted!(
400
///		Foo { x: 1, y: 2 },
401
///		Baz { a: 10, b: 20 },
402
/// );
403
/// ```
404
#[macro_export]
405
macro_rules! assert_events_emitted {
406
	($event:expr) => {
407
		[$event].into_iter().for_each(|e| assert!(
408
			crate::mock::events().into_iter().find(|x| x == &e).is_some(),
409
			"Event {:?} was not found in events: \n{:#?}",
410
			e,
411
			crate::mock::events()
412
		));
413
	};
414
	($($events:expr,)+) => {
415
		[$($events,)+].into_iter().for_each(|e| assert!(
416
			crate::mock::events().into_iter().find(|x| x == &e).is_some(),
417
			"Event {:?} was not found in events: \n{:#?}",
418
			e,
419
			crate::mock::events()
420
		));
421
	};
422
}
423

            
424
/// Asserts that some events were never emitted.
425
///
426
/// # Example
427
///
428
/// ```
429
/// assert_events_not_emitted!(
430
///		Foo { x: 1, y: 2 },
431
///		Bar { value: "test" },
432
/// );
433
/// ```
434
#[macro_export]
435
macro_rules! assert_events_not_emitted {
436
	($event:expr) => {
437
		[$event].into_iter().for_each(|e| assert!(
438
			crate::mock::events().into_iter().find(|x| x != &e).is_some(),
439
			"Event {:?} was unexpectedly found in events: \n{:#?}",
440
			e,
441
			crate::mock::events()
442
		));
443
	};
444
	($($events:expr,)+) => {
445
		[$($events,)+].into_iter().for_each(|e| assert!(
446
			crate::mock::events().into_iter().find(|x| x != &e).is_some(),
447
			"Event {:?} was unexpectedly found in events: \n{:#?}",
448
			e,
449
			crate::mock::events()
450
		));
451
	};
452
}
453

            
454
/// Asserts that the emitted events are exactly equal to the input patterns.
455
///
456
/// # Example
457
///
458
/// ```
459
/// assert_events_eq_match!(
460
///		Foo { x: 1, .. },
461
///		Bar { .. },
462
///		Baz { a: 10, b: 20 },
463
/// );
464
/// ```
465
#[macro_export]
466
macro_rules! assert_events_eq_match {
467
	($index:expr;) => {
468
		assert_eq!(
469
			$index,
470
			crate::mock::events().len(),
471
			"Found {} extra event(s): \n{:#?}",
472
			crate::mock::events().len()-$index,
473
			crate::mock::events()
474
		);
475
	};
476
	($index:expr; $event:pat_param, $($events:pat_param,)*) => {
477
		assert!(
478
			matches!(
479
				crate::mock::events().get($index),
480
				Some($event),
481
			),
482
			"Event {:#?} was not found at index {}: \n{:#?}",
483
			stringify!($event),
484
			$index,
485
			crate::mock::events()
486
		);
487
		assert_events_eq_match!($index+1; $($events,)*);
488
	};
489
	($event:pat_param) => {
490
		assert_events_eq_match!(0; $event,);
491
	};
492
	($($events:pat_param,)+) => {
493
		assert_events_eq_match!(0; $($events,)+);
494
	};
495
}
496

            
497
/// Asserts that some emitted events match the input patterns.
498
///
499
/// # Example
500
///
501
/// ```
502
/// assert_events_emitted_match!(
503
///		Foo { x: 1, .. },
504
///		Baz { a: 10, b: 20 },
505
/// );
506
/// ```
507
#[macro_export]
508
macro_rules! assert_events_emitted_match {
509
	($event:pat_param) => {
510
		assert!(
511
			crate::mock::events().into_iter().any(|x| matches!(x, $event)),
512
			"Event {:?} was not found in events: \n{:#?}",
513
			stringify!($event),
514
			crate::mock::events()
515
		);
516
	};
517
	($event:pat_param, $($events:pat_param,)+) => {
518
		assert_events_emitted_match!($event);
519
		$(
520
			assert_events_emitted_match!($events);
521
		)+
522
	};
523
}
524

            
525
/// Asserts that the input patterns match none of the emitted events.
526
///
527
/// # Example
528
///
529
/// ```
530
/// assert_events_not_emitted_match!(
531
///		Foo { x: 1, .. },
532
///		Baz { a: 10, b: 20 },
533
/// );
534
/// ```
535
#[macro_export]
536
macro_rules! assert_events_not_emitted_match {
537
	($event:pat_param) => {
538
		assert!(
539
			crate::mock::events().into_iter().any(|x| !matches!(x, $event)),
540
			"Event {:?} was unexpectedly found in events: \n{:#?}",
541
			stringify!($event),
542
			crate::mock::events()
543
		);
544
	};
545
	($event:pat_param, $($events:pat_param,)+) => {
546
		assert_events_not_emitted_match!($event);
547
		$(
548
			assert_events_not_emitted_match!($events);
549
		)+
550
	};
551
}
552

            
553
// Same storage changes as ParachainStaking::on_finalize
554
67
pub(crate) fn set_author(round: BlockNumber, acc: u64, pts: u32) {
555
67
	<Points<Test>>::mutate(round, |p| *p += pts);
556
67
	<AwardedPts<Test>>::mutate(round, acc, |p| *p += pts);
557
67
}
558

            
559
// Allows to change the block author (default is always 0)
560
3
pub(crate) fn set_block_author(acc: u64) {
561
3
	<BlockAuthorMap<Test>>::set(acc);
562
3
}
563

            
564
/// fn to query the lock amount
565
25
pub(crate) fn query_lock_amount(account_id: u64, id: LockIdentifier) -> Option<Balance> {
566
25
	for lock in Balances::locks(&account_id) {
567
19
		if lock.id == id {
568
19
			return Some(lock.amount);
569
		}
570
	}
571
6
	None
572
25
}
573

            
574
#[test]
575
1
fn geneses() {
576
1
	ExtBuilder::default()
577
1
		.with_balances(vec![
578
1
			(1, 1000),
579
1
			(2, 300),
580
1
			(3, 100),
581
1
			(4, 100),
582
1
			(5, 100),
583
1
			(6, 100),
584
1
			(7, 100),
585
1
			(8, 9),
586
1
			(9, 4),
587
1
		])
588
1
		.with_candidates(vec![(1, 500), (2, 200)])
589
1
		.with_delegations(vec![(3, 1, 100), (4, 1, 100), (5, 2, 100), (6, 2, 100)])
590
1
		.build()
591
1
		.execute_with(|| {
592
1
			assert!(System::events().is_empty());
593
			// collators
594
1
			assert_eq!(
595
1
				ParachainStaking::get_collator_stakable_free_balance(&1),
596
1
				500
597
1
			);
598
1
			assert_eq!(query_lock_amount(1, COLLATOR_LOCK_ID), Some(500));
599
1
			assert!(ParachainStaking::is_candidate(&1));
600
1
			assert_eq!(query_lock_amount(2, COLLATOR_LOCK_ID), Some(200));
601
1
			assert_eq!(
602
1
				ParachainStaking::get_collator_stakable_free_balance(&2),
603
1
				100
604
1
			);
605
1
			assert!(ParachainStaking::is_candidate(&2));
606
			// delegators
607
5
			for x in 3..7 {
608
4
				assert!(ParachainStaking::is_delegator(&x));
609
4
				assert_eq!(ParachainStaking::get_delegator_stakable_balance(&x), 0);
610
4
				assert_eq!(query_lock_amount(x, DELEGATOR_LOCK_ID), Some(100));
611
			}
612
			// uninvolved
613
4
			for x in 7..10 {
614
3
				assert!(!ParachainStaking::is_delegator(&x));
615
			}
616
			// no delegator staking locks
617
1
			assert_eq!(query_lock_amount(7, DELEGATOR_LOCK_ID), None);
618
1
			assert_eq!(ParachainStaking::get_delegator_stakable_balance(&7), 100);
619
1
			assert_eq!(query_lock_amount(8, DELEGATOR_LOCK_ID), None);
620
1
			assert_eq!(ParachainStaking::get_delegator_stakable_balance(&8), 9);
621
1
			assert_eq!(query_lock_amount(9, DELEGATOR_LOCK_ID), None);
622
1
			assert_eq!(ParachainStaking::get_delegator_stakable_balance(&9), 4);
623
			// no collator staking locks
624
1
			assert_eq!(
625
1
				ParachainStaking::get_collator_stakable_free_balance(&7),
626
1
				100
627
1
			);
628
1
			assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&8), 9);
629
1
			assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&9), 4);
630
1
		});
631
1
	ExtBuilder::default()
632
1
		.with_balances(vec![
633
1
			(1, 100),
634
1
			(2, 100),
635
1
			(3, 100),
636
1
			(4, 100),
637
1
			(5, 100),
638
1
			(6, 100),
639
1
			(7, 100),
640
1
			(8, 100),
641
1
			(9, 100),
642
1
			(10, 100),
643
1
		])
644
1
		.with_candidates(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 10)])
645
1
		.with_delegations(vec![
646
1
			(6, 1, 10),
647
1
			(7, 1, 10),
648
1
			(8, 2, 10),
649
1
			(9, 2, 10),
650
1
			(10, 1, 10),
651
1
		])
652
1
		.build()
653
1
		.execute_with(|| {
654
1
			assert!(System::events().is_empty());
655
			// collators
656
5
			for x in 1..5 {
657
4
				assert!(ParachainStaking::is_candidate(&x));
658
4
				assert_eq!(query_lock_amount(x, COLLATOR_LOCK_ID), Some(20));
659
4
				assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&x), 80);
660
			}
661
1
			assert!(ParachainStaking::is_candidate(&5));
662
1
			assert_eq!(query_lock_amount(5, COLLATOR_LOCK_ID), Some(10));
663
1
			assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&5), 90);
664
			// delegators
665
6
			for x in 6..11 {
666
5
				assert!(ParachainStaking::is_delegator(&x));
667
5
				assert_eq!(query_lock_amount(x, DELEGATOR_LOCK_ID), Some(10));
668
5
				assert_eq!(ParachainStaking::get_delegator_stakable_balance(&x), 90);
669
			}
670
1
		});
671
1
}
672

            
673
#[frame_support::pallet]
674
pub mod block_author {
675
	use super::*;
676
	use frame_support::pallet_prelude::*;
677
	use frame_support::traits::Get;
678

            
679
	#[pallet::config]
680
	pub trait Config: frame_system::Config {}
681

            
682
1
	#[pallet::pallet]
683
	pub struct Pallet<T>(_);
684

            
685
12
	#[pallet::storage]
686
	#[pallet::getter(fn block_author)]
687
	pub(super) type BlockAuthor<T> = StorageValue<_, AccountId, ValueQuery>;
688

            
689
	impl<T: Config> Get<AccountId> for Pallet<T> {
690
3
		fn get() -> AccountId {
691
3
			<BlockAuthor<T>>::get()
692
3
		}
693
	}
694
}
695

            
696
#[test]
697
1
fn roll_to_round_begin_works() {
698
1
	ExtBuilder::default().build().execute_with(|| {
699
1
		// these tests assume blocks-per-round of 5, as established by GENESIS_BLOCKS_PER_ROUND
700
1
		assert_eq!(System::block_number(), 1); // we start on block 1
701

            
702
1
		let num_blocks = roll_to_round_begin(1);
703
1
		assert_eq!(System::block_number(), 1); // no-op, we're already on this round
704
1
		assert_eq!(num_blocks, 0);
705

            
706
1
		let num_blocks = roll_to_round_begin(2);
707
1
		assert_eq!(System::block_number(), 5);
708
1
		assert_eq!(num_blocks, 4);
709

            
710
1
		let num_blocks = roll_to_round_begin(3);
711
1
		assert_eq!(System::block_number(), 10);
712
1
		assert_eq!(num_blocks, 5);
713
1
	});
714
1
}
715

            
716
#[test]
717
1
fn roll_to_round_end_works() {
718
1
	ExtBuilder::default().build().execute_with(|| {
719
1
		// these tests assume blocks-per-round of 5, as established by GENESIS_BLOCKS_PER_ROUND
720
1
		assert_eq!(System::block_number(), 1); // we start on block 1
721

            
722
1
		let num_blocks = roll_to_round_end(1);
723
1
		assert_eq!(System::block_number(), 4);
724
1
		assert_eq!(num_blocks, 3);
725

            
726
1
		let num_blocks = roll_to_round_end(2);
727
1
		assert_eq!(System::block_number(), 9);
728
1
		assert_eq!(num_blocks, 5);
729

            
730
1
		let num_blocks = roll_to_round_end(3);
731
1
		assert_eq!(System::block_number(), 14);
732
1
		assert_eq!(num_blocks, 5);
733
1
	});
734
1
}
735

            
736
#[test]
737
#[should_panic]
738
1
fn test_assert_events_eq_fails_if_event_missing() {
739
1
	ExtBuilder::default().build().execute_with(|| {
740
1
		inject_test_events();
741
1

            
742
1
		assert_events_eq!(
743
1
			ParachainStakingEvent::CollatorChosen {
744
1
				round: 2,
745
1
				collator_account: 1,
746
1
				total_exposed_amount: 10,
747
1
			},
748
1
			ParachainStakingEvent::NewRound {
749
1
				starting_block: 10,
750
1
				round: 2,
751
1
				selected_collators_number: 1,
752
1
				total_balance: 10,
753
1
			},
754
1
		);
755
1
	});
756
1
}
757

            
758
#[test]
759
#[should_panic]
760
1
fn test_assert_events_eq_fails_if_event_extra() {
761
1
	ExtBuilder::default().build().execute_with(|| {
762
1
		inject_test_events();
763
1

            
764
1
		assert_events_eq!(
765
1
			ParachainStakingEvent::CollatorChosen {
766
1
				round: 2,
767
1
				collator_account: 1,
768
1
				total_exposed_amount: 10,
769
1
			},
770
1
			ParachainStakingEvent::NewRound {
771
1
				starting_block: 10,
772
1
				round: 2,
773
1
				selected_collators_number: 1,
774
1
				total_balance: 10,
775
1
			},
776
1
			ParachainStakingEvent::Rewarded {
777
1
				account: 1,
778
1
				rewards: 100,
779
1
			},
780
1
			ParachainStakingEvent::Rewarded {
781
1
				account: 1,
782
1
				rewards: 200,
783
1
			},
784
1
		);
785
1
	});
786
1
}
787

            
788
#[test]
789
#[should_panic]
790
1
fn test_assert_events_eq_fails_if_event_wrong_order() {
791
1
	ExtBuilder::default().build().execute_with(|| {
792
1
		inject_test_events();
793
1

            
794
1
		assert_events_eq!(
795
1
			ParachainStakingEvent::Rewarded {
796
1
				account: 1,
797
1
				rewards: 100,
798
1
			},
799
1
			ParachainStakingEvent::CollatorChosen {
800
1
				round: 2,
801
1
				collator_account: 1,
802
1
				total_exposed_amount: 10,
803
1
			},
804
1
			ParachainStakingEvent::NewRound {
805
1
				starting_block: 10,
806
1
				round: 2,
807
1
				selected_collators_number: 1,
808
1
				total_balance: 10,
809
1
			},
810
1
		);
811
1
	});
812
1
}
813

            
814
#[test]
815
#[should_panic]
816
1
fn test_assert_events_eq_fails_if_event_wrong_value() {
817
1
	ExtBuilder::default().build().execute_with(|| {
818
1
		inject_test_events();
819
1

            
820
1
		assert_events_eq!(
821
1
			ParachainStakingEvent::CollatorChosen {
822
1
				round: 2,
823
1
				collator_account: 1,
824
1
				total_exposed_amount: 10,
825
1
			},
826
1
			ParachainStakingEvent::NewRound {
827
1
				starting_block: 10,
828
1
				round: 2,
829
1
				selected_collators_number: 1,
830
1
				total_balance: 10,
831
1
			},
832
1
			ParachainStakingEvent::Rewarded {
833
1
				account: 1,
834
1
				rewards: 50,
835
1
			},
836
1
		);
837
1
	});
838
1
}
839

            
840
#[test]
841
1
fn test_assert_events_eq_passes_if_all_events_present_single() {
842
1
	ExtBuilder::default().build().execute_with(|| {
843
1
		System::deposit_event(ParachainStakingEvent::Rewarded {
844
1
			account: 1,
845
1
			rewards: 100,
846
1
		});
847
1

            
848
1
		assert_events_eq!(ParachainStakingEvent::Rewarded {
849
1
			account: 1,
850
1
			rewards: 100,
851
1
		});
852
1
	});
853
1
}
854

            
855
#[test]
856
1
fn test_assert_events_eq_passes_if_all_events_present_multiple() {
857
1
	ExtBuilder::default().build().execute_with(|| {
858
1
		inject_test_events();
859
1

            
860
1
		assert_events_eq!(
861
1
			ParachainStakingEvent::CollatorChosen {
862
1
				round: 2,
863
1
				collator_account: 1,
864
1
				total_exposed_amount: 10,
865
1
			},
866
1
			ParachainStakingEvent::NewRound {
867
1
				starting_block: 10,
868
1
				round: 2,
869
1
				selected_collators_number: 1,
870
1
				total_balance: 10,
871
1
			},
872
1
			ParachainStakingEvent::Rewarded {
873
1
				account: 1,
874
1
				rewards: 100,
875
1
			},
876
1
		);
877
1
	});
878
1
}
879

            
880
#[test]
881
#[should_panic]
882
1
fn test_assert_events_emitted_fails_if_event_missing() {
883
1
	ExtBuilder::default().build().execute_with(|| {
884
1
		inject_test_events();
885
1

            
886
3
		assert_events_emitted!(ParachainStakingEvent::DelegatorExitScheduled {
887
3
			round: 2,
888
3
			delegator: 3,
889
3
			scheduled_exit: 4,
890
3
		});
891
1
	});
892
1
}
893

            
894
#[test]
895
#[should_panic]
896
1
fn test_assert_events_emitted_fails_if_event_wrong_value() {
897
1
	ExtBuilder::default().build().execute_with(|| {
898
1
		inject_test_events();
899
1

            
900
3
		assert_events_emitted!(ParachainStakingEvent::Rewarded {
901
3
			account: 1,
902
3
			rewards: 50,
903
3
		});
904
1
	});
905
1
}
906

            
907
#[test]
908
1
fn test_assert_events_emitted_passes_if_all_events_present_single() {
909
1
	ExtBuilder::default().build().execute_with(|| {
910
1
		System::deposit_event(ParachainStakingEvent::Rewarded {
911
1
			account: 1,
912
1
			rewards: 100,
913
1
		});
914
1

            
915
1
		assert_events_emitted!(ParachainStakingEvent::Rewarded {
916
1
			account: 1,
917
1
			rewards: 100,
918
1
		});
919
1
	});
920
1
}
921

            
922
#[test]
923
1
fn test_assert_events_emitted_passes_if_all_events_present_multiple() {
924
1
	ExtBuilder::default().build().execute_with(|| {
925
1
		inject_test_events();
926
1

            
927
4
		assert_events_emitted!(
928
4
			ParachainStakingEvent::CollatorChosen {
929
4
				round: 2,
930
4
				collator_account: 1,
931
4
				total_exposed_amount: 10,
932
4
			},
933
4
			ParachainStakingEvent::Rewarded {
934
4
				account: 1,
935
4
				rewards: 100,
936
4
			},
937
4
		);
938
1
	});
939
1
}
940

            
941
#[test]
942
#[should_panic]
943
1
fn test_assert_events_eq_match_fails_if_event_missing() {
944
1
	ExtBuilder::default().build().execute_with(|| {
945
1
		inject_test_events();
946
1

            
947
1
		assert_events_eq_match!(
948
1
			ParachainStakingEvent::CollatorChosen { .. },
949
1
			ParachainStakingEvent::NewRound { .. },
950
1
		);
951
1
	});
952
1
}
953

            
954
#[test]
955
#[should_panic]
956
1
fn test_assert_events_eq_match_fails_if_event_extra() {
957
1
	ExtBuilder::default().build().execute_with(|| {
958
1
		inject_test_events();
959
1

            
960
1
		assert_events_eq_match!(
961
1
			ParachainStakingEvent::CollatorChosen { .. },
962
1
			ParachainStakingEvent::NewRound { .. },
963
1
			ParachainStakingEvent::Rewarded { .. },
964
1
			ParachainStakingEvent::Rewarded { .. },
965
1
		);
966
1
	});
967
1
}
968

            
969
#[test]
970
#[should_panic]
971
1
fn test_assert_events_eq_match_fails_if_event_wrong_order() {
972
1
	ExtBuilder::default().build().execute_with(|| {
973
1
		inject_test_events();
974
1

            
975
1
		assert_events_eq_match!(
976
1
			ParachainStakingEvent::Rewarded { .. },
977
1
			ParachainStakingEvent::CollatorChosen { .. },
978
1
			ParachainStakingEvent::NewRound { .. },
979
1
		);
980
1
	});
981
1
}
982

            
983
#[test]
984
#[should_panic]
985
1
fn test_assert_events_eq_match_fails_if_event_wrong_value() {
986
1
	ExtBuilder::default().build().execute_with(|| {
987
1
		inject_test_events();
988
1

            
989
1
		assert_events_eq_match!(
990
1
			ParachainStakingEvent::CollatorChosen { .. },
991
1
			ParachainStakingEvent::NewRound { .. },
992
1
			ParachainStakingEvent::Rewarded { rewards: 50, .. },
993
1
		);
994
1
	});
995
1
}
996

            
997
#[test]
998
1
fn test_assert_events_eq_match_passes_if_all_events_present_single() {
999
1
	ExtBuilder::default().build().execute_with(|| {
1
		System::deposit_event(ParachainStakingEvent::Rewarded {
1
			account: 1,
1
			rewards: 100,
1
		});
1

            
1
		assert_events_eq_match!(ParachainStakingEvent::Rewarded { account: 1, .. });
1
	});
1
}
#[test]
1
fn test_assert_events_eq_match_passes_if_all_events_present_multiple() {
1
	ExtBuilder::default().build().execute_with(|| {
1
		inject_test_events();
1

            
1
		assert_events_eq_match!(
1
			ParachainStakingEvent::CollatorChosen {
1
				round: 2,
1
				collator_account: 1,
1
				..
1
			},
1
			ParachainStakingEvent::NewRound {
1
				starting_block: 10,
1
				..
1
			},
1
			ParachainStakingEvent::Rewarded {
1
				account: 1,
1
				rewards: 100,
1
			},
1
		);
1
	});
1
}
#[test]
#[should_panic]
1
fn test_assert_events_emitted_match_fails_if_event_missing() {
1
	ExtBuilder::default().build().execute_with(|| {
1
		inject_test_events();
3
		assert_events_emitted_match!(ParachainStakingEvent::DelegatorExitScheduled {
3
			round: 2,
3
			..
3
		});
1
	});
1
}
#[test]
#[should_panic]
1
fn test_assert_events_emitted_match_fails_if_event_wrong_value() {
1
	ExtBuilder::default().build().execute_with(|| {
1
		inject_test_events();
3
		assert_events_emitted_match!(ParachainStakingEvent::Rewarded { rewards: 50, .. });
1
	});
1
}
#[test]
1
fn test_assert_events_emitted_match_passes_if_all_events_present_single() {
1
	ExtBuilder::default().build().execute_with(|| {
1
		System::deposit_event(ParachainStakingEvent::Rewarded {
1
			account: 1,
1
			rewards: 100,
1
		});
1
		assert_events_emitted_match!(ParachainStakingEvent::Rewarded { rewards: 100, .. });
1
	});
1
}
#[test]
1
fn test_assert_events_emitted_match_passes_if_all_events_present_multiple() {
1
	ExtBuilder::default().build().execute_with(|| {
1
		inject_test_events();
4
		assert_events_emitted_match!(
4
			ParachainStakingEvent::CollatorChosen {
4
				total_exposed_amount: 10,
4
				..
4
			},
4
			ParachainStakingEvent::Rewarded {
4
				account: 1,
4
				rewards: 100,
4
			},
4
		);
1
	});
1
}
16
fn inject_test_events() {
16
	[
16
		ParachainStakingEvent::CollatorChosen {
16
			round: 2,
16
			collator_account: 1,
16
			total_exposed_amount: 10,
16
		},
16
		ParachainStakingEvent::NewRound {
16
			starting_block: 10,
16
			round: 2,
16
			selected_collators_number: 1,
16
			total_balance: 10,
16
		},
16
		ParachainStakingEvent::Rewarded {
16
			account: 1,
16
			rewards: 100,
16
		},
16
	]
16
	.into_iter()
16
	.for_each(System::deposit_event);
16
}