1
// Copyright 2019-2025 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
};
24
use block_author::BlockAuthor as BlockAuthorMap;
25
use frame_support::{
26
	construct_runtime, parameter_types,
27
	traits::{fungible::InspectFreeze, Everything, Get, OnFinalize, OnInitialize, VariantCountOf},
28
	weights::{constants::RocksDbWeight, Weight},
29
};
30
use frame_system::pallet_prelude::BlockNumberFor;
31
use sp_consensus_slots::Slot;
32
use sp_core::H256;
33
use sp_io;
34
use sp_runtime::BuildStorage;
35
use sp_runtime::{
36
	traits::{BlakeTwo256, IdentityLookup},
37
	Perbill, Percent,
38
};
39

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

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

            
46
// Configure a mock runtime to test the pallet.
47
46547
construct_runtime!(
48
46547
	pub enum Test
49
46547
	{
50
46547
		System: frame_system,
51
46547
		Balances: pallet_balances,
52
46547
		ParachainStaking: pallet_parachain_staking::{Pallet, Call, Storage, Event<T>, FreezeReason},
53
46547
		BlockAuthor: block_author,
54
46547
	}
55
46547
);
56

            
57
parameter_types! {
58
	pub const BlockHashCount: u32 = 250;
59
	pub const MaximumBlockWeight: Weight = Weight::from_parts(1024, 1);
60
	pub const MaximumBlockLength: u32 = 2 * 1024;
61
	pub const AvailableBlockRatio: Perbill = Perbill::one();
62
	pub const SS58Prefix: u8 = 42;
63
}
64
impl frame_system::Config for Test {
65
	type BaseCallFilter = Everything;
66
	type DbWeight = RocksDbWeight;
67
	type RuntimeOrigin = RuntimeOrigin;
68
	type RuntimeTask = RuntimeTask;
69
	type Nonce = u64;
70
	type Block = Block;
71
	type RuntimeCall = RuntimeCall;
72
	type Hash = H256;
73
	type Hashing = BlakeTwo256;
74
	type AccountId = AccountId;
75
	type Lookup = IdentityLookup<Self::AccountId>;
76
	type RuntimeEvent = RuntimeEvent;
77
	type BlockHashCount = BlockHashCount;
78
	type Version = ();
79
	type PalletInfo = PalletInfo;
80
	type AccountData = pallet_balances::AccountData<Balance>;
81
	type OnNewAccount = ();
82
	type OnKilledAccount = ();
83
	type SystemWeightInfo = ();
84
	type BlockWeights = ();
85
	type BlockLength = ();
86
	type SS58Prefix = SS58Prefix;
87
	type OnSetCode = ();
88
	type MaxConsumers = frame_support::traits::ConstU32<16>;
89
	type SingleBlockMigrations = ();
90
	type MultiBlockMigrator = ();
91
	type PreInherents = ();
92
	type PostInherents = ();
93
	type PostTransactions = ();
94
	type ExtensionsWeightInfo = ();
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 = RuntimeFreezeReason;
111
	type MaxFreezes = VariantCountOf<Self::RuntimeFreezeReason>;
112
	type RuntimeFreezeReason = RuntimeFreezeReason;
113
	type DoneSlashHandler = ();
114
}
115
impl block_author::Config for Test {}
116
const GENESIS_BLOCKS_PER_ROUND: BlockNumber = 5;
117
const GENESIS_COLLATOR_COMMISSION: Perbill = Perbill::from_percent(20);
118
const GENESIS_PARACHAIN_BOND_RESERVE_PERCENT: Percent = Percent::from_percent(30);
119
const GENESIS_NUM_SELECTED_CANDIDATES: u32 = 5;
120

            
121
pub const POINTS_PER_BLOCK: u32 = 20;
122
pub const POINTS_PER_ROUND: u32 = GENESIS_BLOCKS_PER_ROUND * POINTS_PER_BLOCK;
123

            
124
parameter_types! {
125
	pub const MinBlocksPerRound: u32 = 3;
126
	pub const MaxOfflineRounds: u32 = 2;
127
	pub const LeaveCandidatesDelay: u32 = 2;
128
	pub const CandidateBondLessDelay: u32 = 2;
129
	pub const LeaveDelegatorsDelay: u32 = 2;
130
	pub const RevokeDelegationDelay: u32 = 2;
131
	pub const DelegationBondLessDelay: u32 = 2;
132
	pub const RewardPaymentDelay: u32 = 2;
133
	pub const MinSelectedCandidates: u32 = GENESIS_NUM_SELECTED_CANDIDATES;
134
	pub const MaxTopDelegationsPerCandidate: u32 = 4;
135
	pub const MaxBottomDelegationsPerCandidate: u32 = 4;
136
	pub const MaxDelegationsPerDelegator: u32 = 4;
137
	pub const MinCandidateStk: u128 = 10;
138
	pub const MinDelegation: u128 = 3;
139
	pub const MaxCandidates: u32 = 200;
140
}
141

            
142
pub struct StakingRoundSlotProvider;
143
impl Get<Slot> for StakingRoundSlotProvider {
144
236
	fn get() -> Slot {
145
236
		let block_number: u64 = System::block_number().into();
146
236
		Slot::from(block_number)
147
236
	}
148
}
149

            
150
parameter_types! {
151
	pub const LinearInflationThreshold: Option<Balance> = Some(1_200_000_000);
152
	pub const MaxScheduledRequestsPerDelegator: u32 = 50;
153
}
154

            
155
impl Config for Test {
156
	type RuntimeEvent = RuntimeEvent;
157
	type Currency = Balances;
158
	type RuntimeFreezeReason = RuntimeFreezeReason;
159
	type MonetaryGovernanceOrigin = frame_system::EnsureRoot<AccountId>;
160
	type MinBlocksPerRound = MinBlocksPerRound;
161
	type MaxOfflineRounds = MaxOfflineRounds;
162
	type LeaveCandidatesDelay = LeaveCandidatesDelay;
163
	type CandidateBondLessDelay = CandidateBondLessDelay;
164
	type LeaveDelegatorsDelay = LeaveDelegatorsDelay;
165
	type RevokeDelegationDelay = RevokeDelegationDelay;
166
	type DelegationBondLessDelay = DelegationBondLessDelay;
167
	type RewardPaymentDelay = RewardPaymentDelay;
168
	type MinSelectedCandidates = MinSelectedCandidates;
169
	type MaxTopDelegationsPerCandidate = MaxTopDelegationsPerCandidate;
170
	type MaxBottomDelegationsPerCandidate = MaxBottomDelegationsPerCandidate;
171
	type MaxDelegationsPerDelegator = MaxDelegationsPerDelegator;
172
	type MaxScheduledRequestsPerDelegator = MaxScheduledRequestsPerDelegator;
173
	type MinCandidateStk = MinCandidateStk;
174
	type MinDelegation = MinDelegation;
175
	type BlockAuthor = BlockAuthor;
176
	type OnCollatorPayout = ();
177
	type PayoutCollatorReward = ();
178
	type OnInactiveCollator = ();
179
	type OnNewRound = ();
180
	type SlotProvider = StakingRoundSlotProvider;
181
	type WeightInfo = ();
182
	type MaxCandidates = MaxCandidates;
183
	type SlotDuration = frame_support::traits::ConstU64<6_000>;
184
	type BlockTime = frame_support::traits::ConstU64<6_000>;
185
	type LinearInflationThreshold = LinearInflationThreshold;
186
}
187

            
188
pub(crate) struct ExtBuilder {
189
	// endowed accounts with balances
190
	balances: Vec<(AccountId, Balance)>,
191
	// [collator, amount]
192
	collators: Vec<(AccountId, Balance)>,
193
	// [delegator, collator, delegation_amount, auto_compound_percent]
194
	delegations: Vec<(AccountId, AccountId, Balance, Percent)>,
195
	// inflation config
196
	inflation: InflationInfo<Balance>,
197
}
198

            
199
impl Default for ExtBuilder {
200
273
	fn default() -> ExtBuilder {
201
273
		ExtBuilder {
202
273
			balances: vec![],
203
273
			delegations: vec![],
204
273
			collators: vec![],
205
273
			inflation: InflationInfo {
206
273
				expect: Range {
207
273
					min: 700,
208
273
					ideal: 700,
209
273
					max: 700,
210
273
				},
211
273
				// not used
212
273
				annual: Range {
213
273
					min: Perbill::from_percent(50),
214
273
					ideal: Perbill::from_percent(50),
215
273
					max: Perbill::from_percent(50),
216
273
				},
217
273
				// unrealistically high parameterization, only for testing
218
273
				round: Range {
219
273
					min: Perbill::from_percent(5),
220
273
					ideal: Perbill::from_percent(5),
221
273
					max: Perbill::from_percent(5),
222
273
				},
223
273
			},
224
273
		}
225
273
	}
226
}
227

            
228
impl ExtBuilder {
229
213
	pub(crate) fn with_balances(mut self, balances: Vec<(AccountId, Balance)>) -> Self {
230
213
		self.balances = balances;
231
213
		self
232
213
	}
233

            
234
183
	pub(crate) fn with_candidates(mut self, collators: Vec<(AccountId, Balance)>) -> Self {
235
183
		self.collators = collators;
236
183
		self
237
183
	}
238

            
239
108
	pub(crate) fn with_delegations(
240
108
		mut self,
241
108
		delegations: Vec<(AccountId, AccountId, Balance)>,
242
108
	) -> Self {
243
108
		self.delegations = delegations
244
108
			.into_iter()
245
8952
			.map(|d| (d.0, d.1, d.2, Percent::zero()))
246
108
			.collect();
247
108
		self
248
108
	}
249

            
250
2
	pub(crate) fn with_auto_compounding_delegations(
251
2
		mut self,
252
2
		delegations: Vec<(AccountId, AccountId, Balance, Percent)>,
253
2
	) -> Self {
254
2
		self.delegations = delegations;
255
2
		self
256
2
	}
257

            
258
	#[allow(dead_code)]
259
	pub(crate) fn with_inflation(mut self, inflation: InflationInfo<Balance>) -> Self {
260
		self.inflation = inflation;
261
		self
262
	}
263

            
264
273
	pub(crate) fn build(self) -> sp_io::TestExternalities {
265
273
		let mut t = frame_system::GenesisConfig::<Test>::default()
266
273
			.build_storage()
267
273
			.expect("Frame system builds valid default genesis config");
268
273

            
269
273
		pallet_balances::GenesisConfig::<Test> {
270
273
			balances: self.balances,
271
273
			dev_accounts: None,
272
273
		}
273
273
		.assimilate_storage(&mut t)
274
273
		.expect("Pallet balances storage can be assimilated");
275
273
		pallet_parachain_staking::GenesisConfig::<Test> {
276
273
			candidates: self.collators,
277
273
			delegations: self.delegations,
278
273
			inflation_config: self.inflation,
279
273
			collator_commission: GENESIS_COLLATOR_COMMISSION,
280
273
			parachain_bond_reserve_percent: GENESIS_PARACHAIN_BOND_RESERVE_PERCENT,
281
273
			blocks_per_round: GENESIS_BLOCKS_PER_ROUND,
282
273
			num_selected_candidates: GENESIS_NUM_SELECTED_CANDIDATES,
283
273
		}
284
273
		.assimilate_storage(&mut t)
285
273
		.expect("Parachain Staking's storage can be assimilated");
286
273

            
287
273
		let mut ext = sp_io::TestExternalities::new(t);
288
273
		ext.execute_with(|| System::set_block_number(1));
289
273
		ext
290
273
	}
291
}
292

            
293
/// Rolls forward one block. Returns the new block number.
294
1176
fn roll_one_block() -> BlockNumber {
295
1176
	ParachainStaking::on_finalize(System::block_number());
296
1176
	Balances::on_finalize(System::block_number());
297
1176
	System::on_finalize(System::block_number());
298
1176
	System::set_block_number(System::block_number() + 1);
299
1176
	System::reset_events();
300
1176
	System::on_initialize(System::block_number());
301
1176
	Balances::on_initialize(System::block_number());
302
1176
	ParachainStaking::on_initialize(System::block_number());
303
1176
	System::block_number()
304
1176
}
305

            
306
/// Rolls to the desired block. Returns the number of blocks played.
307
135
pub(crate) fn roll_to(n: BlockNumber) -> BlockNumber {
308
135
	let mut num_blocks = 0;
309
135
	let mut block = System::block_number();
310
1202
	while block < n {
311
1067
		block = roll_one_block();
312
1067
		num_blocks += 1;
313
1067
	}
314
135
	num_blocks
315
135
}
316

            
317
/// Rolls desired number of blocks. Returns the final block.
318
63
pub(crate) fn roll_blocks(num_blocks: u32) -> BlockNumber {
319
63
	let mut block = System::block_number();
320
109
	for _ in 0..num_blocks {
321
109
		block = roll_one_block();
322
109
	}
323
63
	block
324
63
}
325

            
326
/// Rolls block-by-block to the beginning of the specified round.
327
/// This will complete the block in which the round change occurs.
328
/// Returns the number of blocks played.
329
74
pub(crate) fn roll_to_round_begin(round: BlockNumber) -> BlockNumber {
330
74
	let r = ParachainStaking::round();
331
74

            
332
74
	// Return 0 if target round has already passed
333
74
	if round < r.current + 1 {
334
3
		return 0;
335
71
	}
336
71

            
337
71
	// Calculate target block by adding round length for each round difference
338
71
	let target = r.first + (round - r.current) * r.length;
339
71
	roll_to(target)
340
74
}
341

            
342
/// Rolls block-by-block to the end of the specified round.
343
/// The block following will be the one in which the specified round change occurs.
344
12
pub(crate) fn roll_to_round_end(round: BlockNumber) -> BlockNumber {
345
12
	let r = ParachainStaking::round();
346
12

            
347
12
	// Return 0 if target round has already passed
348
12
	if round < r.current {
349
1
		return 0;
350
11
	}
351
11

            
352
11
	// Calculate target block by adding round length for each round difference
353
11
	let target = r.first + (round - r.current + 1) * r.length - 1;
354
11
	roll_to(target)
355
12
}
356

            
357
34
pub(crate) fn inflation_configs(
358
34
	pbr: AccountId,
359
34
	pbr_percent: u8,
360
34
	treasury: AccountId,
361
34
	treasury_percent: u8,
362
34
) -> InflationDistributionConfig<AccountId> {
363
34
	[
364
34
		InflationDistributionAccount {
365
34
			account: pbr,
366
34
			percent: Percent::from_percent(pbr_percent),
367
34
		},
368
34
		InflationDistributionAccount {
369
34
			account: treasury,
370
34
			percent: Percent::from_percent(treasury_percent),
371
34
		},
372
34
	]
373
34
	.into()
374
34
}
375

            
376
201
pub(crate) fn events() -> Vec<pallet::Event<Test>> {
377
201
	System::events()
378
201
		.into_iter()
379
713
		.map(|r| r.event)
380
713
		.filter_map(|e| {
381
713
			if let RuntimeEvent::ParachainStaking(inner) = e {
382
547
				Some(inner)
383
			} else {
384
166
				None
385
			}
386
713
		})
387
201
		.collect::<Vec<_>>()
388
201
}
389

            
390
/// Asserts that some events were never emitted.
391
///
392
/// # Example
393
///
394
/// ```
395
/// assert_no_events!();
396
/// ```
397
#[macro_export]
398
macro_rules! assert_no_events {
399
	() => {
400
		similar_asserts::assert_eq!(Vec::<Event<Test>>::new(), crate::mock::events())
401
	};
402
}
403

            
404
/// Asserts that emitted events match exactly the given input.
405
///
406
/// # Example
407
///
408
/// ```
409
/// assert_events_eq!(
410
///		Foo { x: 1, y: 2 },
411
///		Bar { value: "test" },
412
///		Baz { a: 10, b: 20 },
413
/// );
414
/// ```
415
#[macro_export]
416
macro_rules! assert_events_eq {
417
	($event:expr) => {
418
		similar_asserts::assert_eq!(vec![$event], crate::mock::events());
419
	};
420
	($($events:expr,)+) => {
421
		similar_asserts::assert_eq!(vec![$($events,)+], crate::mock::events());
422
	};
423
}
424

            
425
/// Asserts that some emitted events match the given input.
426
///
427
/// # Example
428
///
429
/// ```
430
/// assert_events_emitted!(
431
///		Foo { x: 1, y: 2 },
432
///		Baz { a: 10, b: 20 },
433
/// );
434
/// ```
435
#[macro_export]
436
macro_rules! assert_events_emitted {
437
	($event:expr) => {
438
35
		[$event].into_iter().for_each(|e| assert!(
439
90
			crate::mock::events().into_iter().find(|x| x == &e).is_some(),
440
2
			"Event {:?} was not found in events: \n{:#?}",
441
2
			e,
442
2
			crate::mock::events()
443
		));
444
	};
445
	($($events:expr,)+) => {
446
3
		[$($events,)+].into_iter().for_each(|e| assert!(
447
6
			crate::mock::events().into_iter().find(|x| x == &e).is_some(),
448
			"Event {:?} was not found in events: \n{:#?}",
449
			e,
450
			crate::mock::events()
451
		));
452
	};
453
}
454

            
455
/// Asserts that some events were never emitted.
456
///
457
/// # Example
458
///
459
/// ```
460
/// assert_events_not_emitted!(
461
///		Foo { x: 1, y: 2 },
462
///		Bar { value: "test" },
463
/// );
464
/// ```
465
#[macro_export]
466
macro_rules! assert_events_not_emitted {
467
	($event:expr) => {
468
		[$event].into_iter().for_each(|e| assert!(
469
			crate::mock::events().into_iter().find(|x| x != &e).is_some(),
470
			"Event {:?} was unexpectedly found in events: \n{:#?}",
471
			e,
472
			crate::mock::events()
473
		));
474
	};
475
	($($events:expr,)+) => {
476
		[$($events,)+].into_iter().for_each(|e| assert!(
477
			crate::mock::events().into_iter().find(|x| x != &e).is_some(),
478
			"Event {:?} was unexpectedly found in events: \n{:#?}",
479
			e,
480
			crate::mock::events()
481
		));
482
	};
483
}
484

            
485
/// Asserts that the emitted events are exactly equal to the input patterns.
486
///
487
/// # Example
488
///
489
/// ```
490
/// assert_events_eq_match!(
491
///		Foo { x: 1, .. },
492
///		Bar { .. },
493
///		Baz { a: 10, b: 20 },
494
/// );
495
/// ```
496
#[macro_export]
497
macro_rules! assert_events_eq_match {
498
	($index:expr;) => {
499
		assert_eq!(
500
			$index,
501
			crate::mock::events().len(),
502
			"Found {} extra event(s): \n{:#?}",
503
			crate::mock::events().len()-$index,
504
			crate::mock::events()
505
		);
506
	};
507
	($index:expr; $event:pat_param, $($events:pat_param,)*) => {
508
		assert!(
509
			matches!(
510
				crate::mock::events().get($index),
511
				Some($event),
512
			),
513
			"Event {:#?} was not found at index {}: \n{:#?}",
514
			stringify!($event),
515
			$index,
516
			crate::mock::events()
517
		);
518
		assert_events_eq_match!($index+1; $($events,)*);
519
	};
520
	($event:pat_param) => {
521
		assert_events_eq_match!(0; $event,);
522
	};
523
	($($events:pat_param,)+) => {
524
		assert_events_eq_match!(0; $($events,)+);
525
	};
526
}
527

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

            
556
/// Asserts that the input patterns match none of the emitted events.
557
///
558
/// # Example
559
///
560
/// ```
561
/// assert_events_not_emitted_match!(
562
///		Foo { x: 1, .. },
563
///		Baz { a: 10, b: 20 },
564
/// );
565
/// ```
566
#[macro_export]
567
macro_rules! assert_events_not_emitted_match {
568
	($event:pat_param) => {
569
		assert!(
570
			crate::mock::events().into_iter().any(|x| !matches!(x, $event)),
571
			"Event {:?} was unexpectedly found in events: \n{:#?}",
572
			stringify!($event),
573
			crate::mock::events()
574
		);
575
	};
576
	($event:pat_param, $($events:pat_param,)+) => {
577
		assert_events_not_emitted_match!($event);
578
		$(
579
			assert_events_not_emitted_match!($events);
580
		)+
581
	};
582
}
583

            
584
// Same storage changes as ParachainStaking::on_finalize
585
65
pub(crate) fn set_author(round: BlockNumber, acc: u64, pts: u32) {
586
65
	<Points<Test>>::mutate(round, |p| *p += pts);
587
65
	<AwardedPts<Test>>::mutate(round, acc, |p| *p += pts);
588
65
}
589

            
590
// Allows to change the block author (default is always 0)
591
6
pub(crate) fn set_block_author(acc: u64) {
592
6
	<BlockAuthorMap<Test>>::set(acc);
593
6
}
594

            
595
/// fn to query the freeze amount for a specific reason
596
53
pub(crate) fn query_freeze_amount(account_id: u64, reason: &RuntimeFreezeReason) -> Balance {
597
53
	Balances::balance_frozen(reason, &account_id)
598
53
}
599

            
600
#[test]
601
1
fn geneses() {
602
1
	ExtBuilder::default()
603
1
		.with_balances(vec![
604
1
			(1, 1000),
605
1
			(2, 300),
606
1
			(3, 100),
607
1
			(4, 100),
608
1
			(5, 100),
609
1
			(6, 100),
610
1
			(7, 100),
611
1
			(8, 9),
612
1
			(9, 4),
613
1
		])
614
1
		.with_candidates(vec![(1, 500), (2, 200)])
615
1
		.with_delegations(vec![(3, 1, 100), (4, 1, 100), (5, 2, 100), (6, 2, 100)])
616
1
		.build()
617
1
		.execute_with(|| {
618
1
			assert!(System::events().is_empty());
619
			// collators
620
1
			assert_eq!(
621
1
				ParachainStaking::get_collator_stakable_free_balance(&1),
622
1
				500
623
1
			);
624
1
			let collator_freeze_reason: RuntimeFreezeReason =
625
1
				pallet_parachain_staking::pallet::FreezeReason::StakingCollator.into();
626
1
			let delegator_freeze_reason: RuntimeFreezeReason =
627
1
				pallet_parachain_staking::pallet::FreezeReason::StakingDelegator.into();
628
1
			assert_eq!(query_freeze_amount(1, &collator_freeze_reason), 500);
629
1
			assert!(ParachainStaking::is_candidate(&1));
630
1
			assert_eq!(query_freeze_amount(2, &collator_freeze_reason), 200);
631
1
			assert_eq!(
632
1
				ParachainStaking::get_collator_stakable_free_balance(&2),
633
1
				100
634
1
			);
635
1
			assert!(ParachainStaking::is_candidate(&2));
636
			// delegators
637
5
			for x in 3..7 {
638
4
				assert!(ParachainStaking::is_delegator(&x));
639
4
				assert_eq!(ParachainStaking::get_delegator_stakable_balance(&x), 0);
640
4
				assert_eq!(query_freeze_amount(x, &delegator_freeze_reason), 100);
641
			}
642
			// uninvolved
643
4
			for x in 7..10 {
644
3
				assert!(!ParachainStaking::is_delegator(&x));
645
			}
646
			// no delegator staking locks
647
1
			assert_eq!(query_freeze_amount(7, &delegator_freeze_reason), 0);
648
1
			assert_eq!(ParachainStaking::get_delegator_stakable_balance(&7), 100);
649
1
			assert_eq!(query_freeze_amount(8, &delegator_freeze_reason), 0);
650
1
			assert_eq!(ParachainStaking::get_delegator_stakable_balance(&8), 9);
651
1
			assert_eq!(query_freeze_amount(9, &delegator_freeze_reason), 0);
652
1
			assert_eq!(ParachainStaking::get_delegator_stakable_balance(&9), 4);
653
			// no collator staking locks
654
1
			assert_eq!(
655
1
				ParachainStaking::get_collator_stakable_free_balance(&7),
656
1
				100
657
1
			);
658
1
			assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&8), 9);
659
1
			assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&9), 4);
660
1
		});
661
1
	ExtBuilder::default()
662
1
		.with_balances(vec![
663
1
			(1, 100),
664
1
			(2, 100),
665
1
			(3, 100),
666
1
			(4, 100),
667
1
			(5, 100),
668
1
			(6, 100),
669
1
			(7, 100),
670
1
			(8, 100),
671
1
			(9, 100),
672
1
			(10, 100),
673
1
		])
674
1
		.with_candidates(vec![(1, 20), (2, 20), (3, 20), (4, 20), (5, 10)])
675
1
		.with_delegations(vec![
676
1
			(6, 1, 10),
677
1
			(7, 1, 10),
678
1
			(8, 2, 10),
679
1
			(9, 2, 10),
680
1
			(10, 1, 10),
681
1
		])
682
1
		.build()
683
1
		.execute_with(|| {
684
1
			// Define freeze reasons once
685
1
			let collator_freeze_reason: RuntimeFreezeReason =
686
1
				pallet_parachain_staking::pallet::FreezeReason::StakingCollator.into();
687
1
			let delegator_freeze_reason: RuntimeFreezeReason =
688
1
				pallet_parachain_staking::pallet::FreezeReason::StakingDelegator.into();
689
1
			assert!(System::events().is_empty());
690
			// collators
691
5
			for x in 1..5 {
692
4
				assert!(ParachainStaking::is_candidate(&x));
693
4
				assert_eq!(query_freeze_amount(x, &collator_freeze_reason), 20);
694
4
				assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&x), 80);
695
			}
696
1
			assert!(ParachainStaking::is_candidate(&5));
697
1
			assert_eq!(query_freeze_amount(5, &collator_freeze_reason), 10);
698
1
			assert_eq!(ParachainStaking::get_collator_stakable_free_balance(&5), 90);
699
			// delegators
700
6
			for x in 6..11 {
701
5
				assert!(ParachainStaking::is_delegator(&x));
702
5
				assert_eq!(query_freeze_amount(x, &delegator_freeze_reason), 10);
703
5
				assert_eq!(ParachainStaking::get_delegator_stakable_balance(&x), 90);
704
			}
705
1
		});
706
1
}
707

            
708
#[frame_support::pallet]
709
pub mod block_author {
710
	use super::*;
711
	use frame_support::pallet_prelude::*;
712
	use frame_support::traits::Get;
713

            
714
	#[pallet::config]
715
	pub trait Config: frame_system::Config {}
716

            
717
3
	#[pallet::pallet]
718
	pub struct Pallet<T>(_);
719

            
720
2364
	#[pallet::storage]
721
	#[pallet::getter(fn block_author)]
722
	pub(super) type BlockAuthor<T> = StorageValue<_, AccountId, ValueQuery>;
723

            
724
	impl<T: Config> Get<AccountId> for Pallet<T> {
725
1176
		fn get() -> AccountId {
726
1176
			<BlockAuthor<T>>::get()
727
1176
		}
728
	}
729
}
730

            
731
#[test]
732
1
fn roll_to_round_begin_works() {
733
1
	ExtBuilder::default().build().execute_with(|| {
734
1
		// these tests assume blocks-per-round of 5, as established by GENESIS_BLOCKS_PER_ROUND
735
1
		assert_eq!(System::block_number(), 1); // we start on block 1
736

            
737
1
		let num_blocks = roll_to_round_begin(1);
738
1
		assert_eq!(System::block_number(), 1); // no-op, we're already on this round
739
1
		assert_eq!(num_blocks, 0);
740

            
741
1
		let num_blocks = roll_to_round_begin(2);
742
1
		assert_eq!(System::block_number(), 5);
743
1
		assert_eq!(num_blocks, 4);
744

            
745
1
		let num_blocks = roll_to_round_begin(3);
746
1
		assert_eq!(System::block_number(), 10);
747
1
		assert_eq!(num_blocks, 5);
748
1
	});
749
1
}
750

            
751
#[test]
752
1
fn roll_to_round_end_works() {
753
1
	ExtBuilder::default().build().execute_with(|| {
754
1
		// these tests assume blocks-per-round of 5, as established by GENESIS_BLOCKS_PER_ROUND
755
1
		assert_eq!(System::block_number(), 1); // we start on block 1
756

            
757
1
		let num_blocks = roll_to_round_end(1);
758
1
		assert_eq!(System::block_number(), 4);
759
1
		assert_eq!(num_blocks, 3);
760

            
761
1
		let num_blocks = roll_to_round_end(2);
762
1
		assert_eq!(System::block_number(), 9);
763
1
		assert_eq!(num_blocks, 5);
764

            
765
1
		let num_blocks = roll_to_round_end(3);
766
1
		assert_eq!(System::block_number(), 14);
767
1
		assert_eq!(num_blocks, 5);
768
1
	});
769
1
}
770

            
771
#[test]
772
#[should_panic]
773
1
fn test_assert_events_eq_fails_if_event_missing() {
774
1
	ExtBuilder::default().build().execute_with(|| {
775
1
		inject_test_events();
776
1

            
777
1
		assert_events_eq!(
778
1
			ParachainStakingEvent::CollatorChosen {
779
1
				round: 2,
780
1
				collator_account: 1,
781
1
				total_exposed_amount: 10,
782
1
			},
783
1
			ParachainStakingEvent::NewRound {
784
1
				starting_block: 10,
785
1
				round: 2,
786
1
				selected_collators_number: 1,
787
1
				total_balance: 10,
788
1
			},
789
1
		);
790
1
	});
791
1
}
792

            
793
#[test]
794
#[should_panic]
795
1
fn test_assert_events_eq_fails_if_event_extra() {
796
1
	ExtBuilder::default().build().execute_with(|| {
797
1
		inject_test_events();
798
1

            
799
1
		assert_events_eq!(
800
1
			ParachainStakingEvent::CollatorChosen {
801
1
				round: 2,
802
1
				collator_account: 1,
803
1
				total_exposed_amount: 10,
804
1
			},
805
1
			ParachainStakingEvent::NewRound {
806
1
				starting_block: 10,
807
1
				round: 2,
808
1
				selected_collators_number: 1,
809
1
				total_balance: 10,
810
1
			},
811
1
			ParachainStakingEvent::Rewarded {
812
1
				account: 1,
813
1
				rewards: 100,
814
1
			},
815
1
			ParachainStakingEvent::Rewarded {
816
1
				account: 1,
817
1
				rewards: 200,
818
1
			},
819
1
		);
820
1
	});
821
1
}
822

            
823
#[test]
824
#[should_panic]
825
1
fn test_assert_events_eq_fails_if_event_wrong_order() {
826
1
	ExtBuilder::default().build().execute_with(|| {
827
1
		inject_test_events();
828
1

            
829
1
		assert_events_eq!(
830
1
			ParachainStakingEvent::Rewarded {
831
1
				account: 1,
832
1
				rewards: 100,
833
1
			},
834
1
			ParachainStakingEvent::CollatorChosen {
835
1
				round: 2,
836
1
				collator_account: 1,
837
1
				total_exposed_amount: 10,
838
1
			},
839
1
			ParachainStakingEvent::NewRound {
840
1
				starting_block: 10,
841
1
				round: 2,
842
1
				selected_collators_number: 1,
843
1
				total_balance: 10,
844
1
			},
845
1
		);
846
1
	});
847
1
}
848

            
849
#[test]
850
#[should_panic]
851
1
fn test_assert_events_eq_fails_if_event_wrong_value() {
852
1
	ExtBuilder::default().build().execute_with(|| {
853
1
		inject_test_events();
854
1

            
855
1
		assert_events_eq!(
856
1
			ParachainStakingEvent::CollatorChosen {
857
1
				round: 2,
858
1
				collator_account: 1,
859
1
				total_exposed_amount: 10,
860
1
			},
861
1
			ParachainStakingEvent::NewRound {
862
1
				starting_block: 10,
863
1
				round: 2,
864
1
				selected_collators_number: 1,
865
1
				total_balance: 10,
866
1
			},
867
1
			ParachainStakingEvent::Rewarded {
868
1
				account: 1,
869
1
				rewards: 50,
870
1
			},
871
1
		);
872
1
	});
873
1
}
874

            
875
#[test]
876
1
fn test_assert_events_eq_passes_if_all_events_present_single() {
877
1
	ExtBuilder::default().build().execute_with(|| {
878
1
		System::deposit_event(ParachainStakingEvent::Rewarded {
879
1
			account: 1,
880
1
			rewards: 100,
881
1
		});
882
1

            
883
1
		assert_events_eq!(ParachainStakingEvent::Rewarded {
884
1
			account: 1,
885
1
			rewards: 100,
886
1
		});
887
1
	});
888
1
}
889

            
890
#[test]
891
1
fn test_assert_events_eq_passes_if_all_events_present_multiple() {
892
1
	ExtBuilder::default().build().execute_with(|| {
893
1
		inject_test_events();
894
1

            
895
1
		assert_events_eq!(
896
1
			ParachainStakingEvent::CollatorChosen {
897
1
				round: 2,
898
1
				collator_account: 1,
899
1
				total_exposed_amount: 10,
900
1
			},
901
1
			ParachainStakingEvent::NewRound {
902
1
				starting_block: 10,
903
1
				round: 2,
904
1
				selected_collators_number: 1,
905
1
				total_balance: 10,
906
1
			},
907
1
			ParachainStakingEvent::Rewarded {
908
1
				account: 1,
909
1
				rewards: 100,
910
1
			},
911
1
		);
912
1
	});
913
1
}
914

            
915
#[test]
916
#[should_panic]
917
1
fn test_assert_events_emitted_fails_if_event_missing() {
918
1
	ExtBuilder::default().build().execute_with(|| {
919
1
		inject_test_events();
920
1

            
921
1
		assert_events_emitted!(ParachainStakingEvent::DelegatorExitScheduled {
922
1
			round: 2,
923
1
			delegator: 3,
924
1
			scheduled_exit: 4,
925
1
		});
926
1
	});
927
1
}
928

            
929
#[test]
930
#[should_panic]
931
1
fn test_assert_events_emitted_fails_if_event_wrong_value() {
932
1
	ExtBuilder::default().build().execute_with(|| {
933
1
		inject_test_events();
934
1

            
935
1
		assert_events_emitted!(ParachainStakingEvent::Rewarded {
936
1
			account: 1,
937
1
			rewards: 50,
938
1
		});
939
1
	});
940
1
}
941

            
942
#[test]
943
1
fn test_assert_events_emitted_passes_if_all_events_present_single() {
944
1
	ExtBuilder::default().build().execute_with(|| {
945
1
		System::deposit_event(ParachainStakingEvent::Rewarded {
946
1
			account: 1,
947
1
			rewards: 100,
948
1
		});
949
1

            
950
1
		assert_events_emitted!(ParachainStakingEvent::Rewarded {
951
1
			account: 1,
952
1
			rewards: 100,
953
1
		});
954
1
	});
955
1
}
956

            
957
#[test]
958
1
fn test_assert_events_emitted_passes_if_all_events_present_multiple() {
959
1
	ExtBuilder::default().build().execute_with(|| {
960
1
		inject_test_events();
961
1

            
962
1
		assert_events_emitted!(
963
1
			ParachainStakingEvent::CollatorChosen {
964
1
				round: 2,
965
1
				collator_account: 1,
966
1
				total_exposed_amount: 10,
967
1
			},
968
1
			ParachainStakingEvent::Rewarded {
969
1
				account: 1,
970
1
				rewards: 100,
971
1
			},
972
1
		);
973
1
	});
974
1
}
975

            
976
#[test]
977
#[should_panic]
978
1
fn test_assert_events_eq_match_fails_if_event_missing() {
979
1
	ExtBuilder::default().build().execute_with(|| {
980
1
		inject_test_events();
981
1

            
982
1
		assert_events_eq_match!(
983
1
			ParachainStakingEvent::CollatorChosen { .. },
984
1
			ParachainStakingEvent::NewRound { .. },
985
1
		);
986
1
	});
987
1
}
988

            
989
#[test]
990
#[should_panic]
991
1
fn test_assert_events_eq_match_fails_if_event_extra() {
992
1
	ExtBuilder::default().build().execute_with(|| {
993
1
		inject_test_events();
994
1

            
995
1
		assert_events_eq_match!(
996
1
			ParachainStakingEvent::CollatorChosen { .. },
997
1
			ParachainStakingEvent::NewRound { .. },
998
1
			ParachainStakingEvent::Rewarded { .. },
999
1
			ParachainStakingEvent::Rewarded { .. },
1
		);
1
	});
1
}
#[test]
#[should_panic]
1
fn test_assert_events_eq_match_fails_if_event_wrong_order() {
1
	ExtBuilder::default().build().execute_with(|| {
1
		inject_test_events();
1

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

            
1
		assert_events_eq_match!(
1
			ParachainStakingEvent::CollatorChosen { .. },
1
			ParachainStakingEvent::NewRound { .. },
1
			ParachainStakingEvent::Rewarded { rewards: 50, .. },
1
		);
1
	});
1
}
#[test]
1
fn test_assert_events_eq_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

            
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();
1

            
1
		assert_events_emitted_match!(ParachainStakingEvent::DelegatorExitScheduled {
1
			round: 2,
1
			..
1
		});
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();
1

            
1
		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

            
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();
1

            
1
		assert_events_emitted_match!(
1
			ParachainStakingEvent::CollatorChosen {
1
				total_exposed_amount: 10,
1
				..
1
			},
1
			ParachainStakingEvent::Rewarded {
1
				account: 1,
1
				rewards: 100,
1
			},
1
		);
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
}