Newer
Older
// The validator is not rewarded in this era; so there will be zero payouts to claim for
// this era.
start_active_era(3);
// Collect payouts for an era where the validator did not receive any points.
let call = TestCall::Staking(StakingCall::payout_stakers { validator_stash: 11, era: 2 });
let info = call.get_dispatch_info();
let result = call.dispatch(RuntimeOrigin::signed(20));
assert_ok!(result);
assert_eq!(extract_actual_weight(&result, &info), zero_nom_payouts_weight);
// Reward the validator and its nominators.
Staking::reward_by_ids(vec![(11, 1)]);
start_active_era(4);
// Collect payouts when the validator has `half_max_nom_rewarded` nominators.
let call = TestCall::Staking(StakingCall::payout_stakers { validator_stash: 11, era: 3 });
let info = call.get_dispatch_info();
let result = call.dispatch(RuntimeOrigin::signed(20));
assert_ok!(result);
assert_eq!(extract_actual_weight(&result, &info), half_max_nom_rewarded_weight);
// Add enough nominators so that we are at the limit. They will be active nominators
// in the next era.
for i in half_max_nom_rewarded..max_nom_rewarded {
bond_nominator((1000 + i).into(), balance + i as Balance, vec![11]);
}
start_active_era(5);
// We now have `max_nom_rewarded` nominators actively nominating our validator.
// Reward the validator so we can collect for everyone in the next era.
Staking::reward_by_ids(vec![(11, 1)]);
start_active_era(6);
// Collect payouts when the validator had `half_max_nom_rewarded` nominators.
let call = TestCall::Staking(StakingCall::payout_stakers { validator_stash: 11, era: 5 });
let info = call.get_dispatch_info();
let result = call.dispatch(RuntimeOrigin::signed(20));
assert_ok!(result);
assert_eq!(extract_actual_weight(&result, &info), max_nom_rewarded_weight);
// Try and collect payouts for an era that has already been collected.
let call = TestCall::Staking(StakingCall::payout_stakers { validator_stash: 11, era: 5 });
let info = call.get_dispatch_info();
let result = call.dispatch(RuntimeOrigin::signed(20));
assert!(result.is_err());
// When there is an error the consumed weight == weight when there are 0 nominator payouts.
assert_eq!(extract_actual_weight(&result, &info), zero_nom_payouts_weight);
});
}
#[test]
fn bond_during_era_correctly_populates_claimed_rewards() {
ExtBuilder::default().has_stakers(false).build_and_execute(|| {
bond_validator(9, 1000);
Staking::ledger(&9),
Some(StakingLedger {
stash: 9,
total: 1000,
active: 1000,
unlocking: Default::default(),
mock::start_active_era(5);
bond_validator(11, 1000);
Staking::ledger(&11),
Some(StakingLedger {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
claimed_rewards: (0..5).collect::<Vec<_>>().try_into().unwrap(),
// make sure only era upto history depth is stored
let current_era = 99;
let last_reward_era = 99 - HistoryDepth::get();
mock::start_active_era(current_era);
bond_validator(13, 1000);
Staking::ledger(&13),
Some(StakingLedger {
stash: 13,
total: 1000,
active: 1000,
unlocking: Default::default(),
claimed_rewards: (last_reward_era..current_era)
.collect::<Vec<_>>()
.try_into()
.unwrap(),
#[test]
fn offences_weight_calculated_correctly() {
ExtBuilder::default().nominate(true).build_and_execute(|| {
// On offence with zero offenders: 4 Reads, 1 Write
let zero_offence_weight =
<Test as frame_system::Config>::DbWeight::get().reads_writes(4, 1);
assert_eq!(
Staking::on_offence(&[], &[Perbill::from_percent(50)], 0, DisableStrategy::WhenSlashed),
zero_offence_weight
);
// On Offence with N offenders, Unapplied: 4 Reads, 1 Write + 4 Reads, 5 Writes
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139
4140
4141
4142
4143
4144
let n_offence_unapplied_weight = <Test as frame_system::Config>::DbWeight::get()
.reads_writes(4, 1) +
<Test as frame_system::Config>::DbWeight::get().reads_writes(4, 5);
let offenders: Vec<
OffenceDetails<
<Test as frame_system::Config>::AccountId,
pallet_session::historical::IdentificationTuple<Test>,
>,
> = (1..10)
.map(|i| OffenceDetails {
offender: (i, Staking::eras_stakers(active_era(), i)),
reporters: vec![],
})
.collect();
assert_eq!(
Staking::on_offence(
&offenders,
&[Perbill::from_percent(50)],
0,
DisableStrategy::WhenSlashed
),
n_offence_unapplied_weight
);
// On Offence with one offenders, Applied
let one_offender = [OffenceDetails {
offender: (11, Staking::eras_stakers(active_era(), 11)),
reporters: vec![1],
}];
let n = 1; // Number of offenders
let rw = 3 + 3 * n; // rw reads and writes
let one_offence_unapplied_weight =
<Test as frame_system::Config>::DbWeight::get().reads_writes(4, 1)
+
<Test as frame_system::Config>::DbWeight::get().reads_writes(rw, rw)
+ <Test as frame_system::Config>::DbWeight::get().reads_writes(6, 5)
// `slash_cost` * nominators (1)
+ <Test as frame_system::Config>::DbWeight::get().reads_writes(6, 5)
// `reward_cost` * reporters (1)
+ <Test as frame_system::Config>::DbWeight::get().reads_writes(2, 2)
;
assert_eq!(
Staking::on_offence(
&one_offender,
&[Perbill::from_percent(50)],
0,
DisableStrategy::WhenSlashed{}
),
one_offence_unapplied_weight
);
#[test]
fn payout_creates_controller() {
ExtBuilder::default().has_stakers(false).build_and_execute(|| {
let balance = 1000;
bond_validator(11, balance);
// create a stash/controller pair and nominate
let (stash, controller) = testing_utils::create_unique_stash_controller::<Test>(
0,
100,
RewardDestination::Controller,
false,
)
.unwrap();
assert_ok!(Staking::nominate(RuntimeOrigin::signed(controller), vec![11]));
assert_ok!(Balances::transfer_allow_death(RuntimeOrigin::signed(controller), stash, 100));
assert_eq!(Balances::free_balance(controller), 0);
mock::start_active_era(1);
Staking::reward_by_ids(vec![(11, 1)]);
// compute and ensure the reward amount is greater than zero.
let _ = current_total_payout_for_duration(reward_time_per_era());
mock::start_active_era(2);
assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(controller), 11, 1));
// Controller is created
assert!(Balances::free_balance(controller) > 0);
#[test]
fn payout_to_any_account_works() {
ExtBuilder::default().has_stakers(false).build_and_execute(|| {
let balance = 1000;
// Create a validator:
bond_validator(11, balance); // Default(64)
// Create a stash/controller pair
bond_nominator(1234, 100, vec![11]);
// Update payout location
assert_ok!(Staking::set_payee(RuntimeOrigin::signed(1234), RewardDestination::Account(42)));
// Reward Destination account doesn't exist
assert_eq!(Balances::free_balance(42), 0);
mock::start_active_era(1);
Staking::reward_by_ids(vec![(11, 1)]);
// compute and ensure the reward amount is greater than zero.
let _ = current_total_payout_for_duration(reward_time_per_era());
mock::start_active_era(2);
assert_ok!(Staking::payout_stakers(RuntimeOrigin::signed(1337), 11, 1));
// Payment is successful
assert!(Balances::free_balance(42) > 0);
})
}
#[test]
4240
4241
4242
4243
4244
4245
4246
4247
4248
4249
4250
4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
4288
4289
4290
4291
4292
4293
4294
4295
4296
4297
4298
4299
4300
4301
4302
4303
4304
4305
4306
4307
4308
4309
4310
4311
4312
4313
4314
4315
4316
4317
4318
4319
4320
4321
4322
4323
4324
4325
4326
4327
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342
fn session_buffering_with_offset() {
// similar to live-chains, have some offset for the first session
ExtBuilder::default()
.offset(2)
.period(5)
.session_per_era(5)
.build_and_execute(|| {
assert_eq!(current_era(), 0);
assert_eq!(active_era(), 0);
assert_eq!(Session::current_index(), 0);
start_session(1);
assert_eq!(current_era(), 0);
assert_eq!(active_era(), 0);
assert_eq!(Session::current_index(), 1);
assert_eq!(System::block_number(), 2);
start_session(2);
assert_eq!(current_era(), 0);
assert_eq!(active_era(), 0);
assert_eq!(Session::current_index(), 2);
assert_eq!(System::block_number(), 7);
start_session(3);
assert_eq!(current_era(), 0);
assert_eq!(active_era(), 0);
assert_eq!(Session::current_index(), 3);
assert_eq!(System::block_number(), 12);
// active era is lagging behind by one session, because of how session module works.
start_session(4);
assert_eq!(current_era(), 1);
assert_eq!(active_era(), 0);
assert_eq!(Session::current_index(), 4);
assert_eq!(System::block_number(), 17);
start_session(5);
assert_eq!(current_era(), 1);
assert_eq!(active_era(), 1);
assert_eq!(Session::current_index(), 5);
assert_eq!(System::block_number(), 22);
// go all the way to active 2.
start_active_era(2);
assert_eq!(current_era(), 2);
assert_eq!(active_era(), 2);
assert_eq!(Session::current_index(), 10);
});
}
#[test]
fn session_buffering_no_offset() {
// no offset, first session starts immediately
ExtBuilder::default()
.offset(0)
.period(5)
.session_per_era(5)
.build_and_execute(|| {
assert_eq!(current_era(), 0);
assert_eq!(active_era(), 0);
assert_eq!(Session::current_index(), 0);
start_session(1);
assert_eq!(current_era(), 0);
assert_eq!(active_era(), 0);
assert_eq!(Session::current_index(), 1);
assert_eq!(System::block_number(), 5);
start_session(2);
assert_eq!(current_era(), 0);
assert_eq!(active_era(), 0);
assert_eq!(Session::current_index(), 2);
assert_eq!(System::block_number(), 10);
start_session(3);
assert_eq!(current_era(), 0);
assert_eq!(active_era(), 0);
assert_eq!(Session::current_index(), 3);
assert_eq!(System::block_number(), 15);
// active era is lagging behind by one session, because of how session module works.
start_session(4);
assert_eq!(current_era(), 1);
assert_eq!(active_era(), 0);
assert_eq!(Session::current_index(), 4);
assert_eq!(System::block_number(), 20);
start_session(5);
assert_eq!(current_era(), 1);
assert_eq!(active_era(), 1);
assert_eq!(Session::current_index(), 5);
assert_eq!(System::block_number(), 25);
// go all the way to active 2.
start_active_era(2);
assert_eq!(current_era(), 2);
assert_eq!(active_era(), 2);
assert_eq!(Session::current_index(), 10);
});
}
#[test]
fn cannot_rebond_to_lower_than_ed() {
ExtBuilder::default()
.existential_deposit(11)
.balance_factor(11)
.build_and_execute(|| {
// initial stuff.
assert_eq!(
Staking::ledger(&21).unwrap(),
StakingLedger {
stash: 21,
total: 11 * 1000,
active: 11 * 1000,
unlocking: Default::default(),
}
);
// unbond all of it. must be chilled first.
assert_ok!(Staking::chill(RuntimeOrigin::signed(21)));
assert_ok!(Staking::unbond(RuntimeOrigin::signed(21), 11 * 1000));
assert_eq!(
Staking::ledger(&21).unwrap(),
StakingLedger {
stash: 21,
total: 11 * 1000,
active: 0,
unlocking: bounded_vec![UnlockChunk { value: 11 * 1000, era: 3 }],
}
);
// now bond a wee bit more
Staking::rebond(RuntimeOrigin::signed(21), 5),
Error::<Test>::InsufficientBond
);
})
}
#[test]
fn cannot_bond_extra_to_lower_than_ed() {
ExtBuilder::default()
.existential_deposit(11)
.balance_factor(11)
.build_and_execute(|| {
// initial stuff.
assert_eq!(
Staking::ledger(&21).unwrap(),
StakingLedger {
stash: 21,
total: 11 * 1000,
active: 11 * 1000,
unlocking: Default::default(),
}
);
// unbond all of it. must be chilled first.
assert_ok!(Staking::chill(RuntimeOrigin::signed(21)));
assert_ok!(Staking::unbond(RuntimeOrigin::signed(21), 11 * 1000));
assert_eq!(
Staking::ledger(&21).unwrap(),
StakingLedger {
stash: 21,
total: 11 * 1000,
active: 0,
unlocking: bounded_vec![UnlockChunk { value: 11 * 1000, era: 3 }],
}
);
// now bond a wee bit more
assert_noop!(
Staking::bond_extra(RuntimeOrigin::signed(21), 5),
Error::<Test>::InsufficientBond,
);
})
}
#[test]
fn do_not_die_when_active_is_ed() {
let ed = 10;
ExtBuilder::default()
.existential_deposit(ed)
.build_and_execute(|| {
Staking::ledger(&21).unwrap(),
StakingLedger {
stash: 21,
total: 1000 * ed,
active: 1000 * ed,
unlocking: Default::default(),
// when unbond all of it except ed.
assert_ok!(Staking::unbond(RuntimeOrigin::signed(21), 999 * ed));
start_active_era(3);
assert_ok!(Staking::withdraw_unbonded(RuntimeOrigin::signed(21), 100));
Staking::ledger(&21).unwrap(),
StakingLedger {
stash: 21,
total: ed,
active: ed,
unlocking: Default::default(),
}
);
})
}
#[test]
fn on_finalize_weight_is_nonzero() {
ExtBuilder::default().build_and_execute(|| {
let on_finalize_weight = <Test as frame_system::Config>::DbWeight::get().reads(1);
assert!(<Staking as Hooks<u64>>::on_initialize(1).all_gte(on_finalize_weight));
mod election_data_provider {
use super::*;
use frame_election_provider_support::ElectionDataProvider;
#[test]
fn targets_2sec_block() {
let mut validators = 1000;
while <Test as Config>::WeightInfo::get_npos_targets(validators).all_lt(Weight::from_parts(
2u64 * frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND,
u64::MAX,
)) {
validators += 1;
}
println!("Can create a snapshot of {} validators in 2sec block", validators);
}
#[test]
fn voters_2sec_block() {
// we assume a network only wants up to 1000 validators in most cases, thus having 2000
// candidates is as high as it gets.
let validators = 2000;
let mut nominators = 1000;
while <Test as Config>::WeightInfo::get_npos_voters(validators, nominators).all_lt(
Weight::from_parts(
2u64 * frame_support::weights::constants::WEIGHT_REF_TIME_PER_SECOND,
u64::MAX,
nominators += 1;
}
println!(
"Can create a snapshot of {} nominators [{} validators, each 1 slashing] in 2sec block",
nominators, validators
);
}
#[test]
fn set_minimum_active_stake_is_correct() {
ExtBuilder::default()
.nominate(false)
.add_staker(61, 61, 2_000, StakerStatus::<AccountId>::Nominator(vec![21]))
.add_staker(71, 71, 10, StakerStatus::<AccountId>::Nominator(vec![21]))
.add_staker(81, 81, 50, StakerStatus::<AccountId>::Nominator(vec![21]))
.build_and_execute(|| {
// default bounds are unbounded.
assert_ok!(<Staking as ElectionDataProvider>::electing_voters(
DataProviderBounds::default()
));
assert_eq!(MinimumActiveStake::<Test>::get(), 10);
// remove staker with lower bond by limiting the number of voters and check
// `MinimumActiveStake` again after electing voters.
let bounds = ElectionBoundsBuilder::default().voters_count(5.into()).build();
assert_ok!(<Staking as ElectionDataProvider>::electing_voters(bounds.voters));
assert_eq!(MinimumActiveStake::<Test>::get(), 50);
});
}
#[test]
Gonçalo Pestana
committed
fn set_minimum_active_stake_lower_bond_works() {
// if there are no voters, minimum active stake is zero (should not happen).
ExtBuilder::default().has_stakers(false).build_and_execute(|| {
// default bounds are unbounded.
assert_ok!(<Staking as ElectionDataProvider>::electing_voters(
DataProviderBounds::default()
));
Gonçalo Pestana
committed
assert_eq!(<Test as Config>::VoterList::count(), 0);
assert_eq!(MinimumActiveStake::<Test>::get(), 0);
});
Gonçalo Pestana
committed
// lower non-zero active stake below `MinNominatorBond` is the minimum active stake if
// it is selected as part of the npos voters.
ExtBuilder::default().has_stakers(true).nominate(true).build_and_execute(|| {
assert_eq!(MinNominatorBond::<Test>::get(), 1);
assert_eq!(<Test as Config>::VoterList::count(), 4);
assert_ok!(Staking::bond(RuntimeOrigin::signed(4), 5, Default::default(),));
assert_ok!(Staking::nominate(RuntimeOrigin::signed(4), vec![1]));
assert_eq!(<Test as Config>::VoterList::count(), 5);
let voters_before =
<Staking as ElectionDataProvider>::electing_voters(DataProviderBounds::default())
.unwrap();
Gonçalo Pestana
committed
assert_eq!(MinimumActiveStake::<Test>::get(), 5);
// update minimum nominator bond.
MinNominatorBond::<Test>::set(10);
assert_eq!(MinNominatorBond::<Test>::get(), 10);
// voter list still considers nominator 4 for voting, even though its active stake is
// lower than `MinNominatorBond`.
assert_eq!(<Test as Config>::VoterList::count(), 5);
let voters =
<Staking as ElectionDataProvider>::electing_voters(DataProviderBounds::default())
.unwrap();
Gonçalo Pestana
committed
assert_eq!(voters_before, voters);
// minimum active stake is lower than `MinNominatorBond`.
assert_eq!(MinimumActiveStake::<Test>::get(), 5);
});
}
#[test]
fn set_minimum_active_bond_corrupt_state() {
ExtBuilder::default()
.has_stakers(true)
.nominate(true)
.add_staker(61, 61, 2_000, StakerStatus::<AccountId>::Nominator(vec![21]))
.build_and_execute(|| {
assert_eq!(Staking::weight_of(&101), 500);
let voters = <Staking as ElectionDataProvider>::electing_voters(
DataProviderBounds::default(),
)
.unwrap();
Gonçalo Pestana
committed
assert_eq!(voters.len(), 5);
assert_eq!(MinimumActiveStake::<Test>::get(), 500);
assert_ok!(Staking::unbond(RuntimeOrigin::signed(101), 200));
start_active_era(10);
assert_ok!(Staking::unbond(RuntimeOrigin::signed(101), 100));
start_active_era(20);
// corrupt ledger state by lowering max unlocking chunks bounds.
MaxUnlockingChunks::set(1);
let voters = <Staking as ElectionDataProvider>::electing_voters(
DataProviderBounds::default(),
)
.unwrap();
Gonçalo Pestana
committed
// number of returned voters decreases since ledger entry of stash 101 is now
// corrupt.
assert_eq!(voters.len(), 4);
// minimum active stake does not take into consideration the corrupt entry.
assert_eq!(MinimumActiveStake::<Test>::get(), 2_000);
// voter weight of corrupted ledger entry is 0.
assert_eq!(Staking::weight_of(&101), 0);
// reset max unlocking chunks for try_state to pass.
MaxUnlockingChunks::set(32);
})
#[test]
fn voters_include_self_vote() {
ExtBuilder::default().nominate(false).build_and_execute(|| {
// default bounds are unbounded.
assert!(<Validators<Test>>::iter().map(|(x, _)| x).all(|v| Staking::electing_voters(
DataProviderBounds::default()
)
.unwrap()
.into_iter()
.any(|(w, _, t)| { v == w && t[0] == w })))
})
}
Kian Paimani
committed
// Tests the criteria that in `ElectionDataProvider::voters` function, we try to get at most
// `maybe_max_len` voters, and if some of them end up being skipped, we iterate at most `2 *
// maybe_max_len`.
#[test]
#[should_panic]
Kian Paimani
committed
fn only_iterates_max_2_times_max_allowed_len() {
ExtBuilder::default()
Kian Paimani
committed
.nominate(false)
// the best way to invalidate a bunch of nominators is to have them nominate a lot of
// ppl, but then lower the MaxNomination limit.
.add_staker(
61,
61,
2_000,
StakerStatus::<AccountId>::Nominator(vec![21, 22, 23, 24, 25]),
)
.add_staker(
71,
71,
2_000,
StakerStatus::<AccountId>::Nominator(vec![21, 22, 23, 24, 25]),
)
.add_staker(
81,
81,
2_000,
StakerStatus::<AccountId>::Nominator(vec![21, 22, 23, 24, 25]),
)
.build_and_execute(|| {
let bounds_builder = ElectionBoundsBuilder::default();
Kian Paimani
committed
// all voters ordered by stake,
assert_eq!(
Kian Paimani
committed
<Test as Config>::VoterList::iter().collect::<Vec<_>>(),
vec![61, 71, 81, 11, 21, 31]
);
AbsoluteMaxNominations::set(2);
Kian Paimani
committed
// we want 2 voters now, and in maximum we allow 4 iterations. This is what happens:
// 61 is pruned;
// 71 is pruned;
// 81 is pruned;
// 11 is taken;
// we finish since the 2x limit is reached.
assert_eq!(
Staking::electing_voters(bounds_builder.voters_count(2.into()).build().voters)
.unwrap()
.iter()
.map(|(stash, _, _)| stash)
.copied()
.collect::<Vec<_>>(),
Kian Paimani
committed
vec![11],
);
});
}
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703
4704
4705
4706
4707
4708
4709
4710
4711
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
4736
4737
4738
4739
4740
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
4756
4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
4772
4773
4774
4775
4776
4777
4778
4779
4780
4781
4782
4783
4784
4785
4786
4787
4788
4789
4790
4791
4792
4793
4794
4795
4796
4797
4798
4799
4800
4801
4802
4803
4804
4805
4806
4807
4808
4809
4810
4811
4812
4813
4814
4815
4816
4817
4818
4819
4820
4821
4822
4823
4824
4825
4826
4827
4828
4829
4830
4831
4832
4833
4834
4835
4836
4837
4838
4839
4840
4841
4842
4843
4844
4845
4846
4847
4848
4849
4850
4851
4852
4853
4854
4855
4856
4857
4858
4859
4860
4861
4862
4863
4864
#[test]
fn respects_snapshot_count_limits() {
ExtBuilder::default()
.set_status(41, StakerStatus::Validator)
.build_and_execute(|| {
// sum of all nominators who'd be voters (1), plus the self-votes (4).
assert_eq!(<Test as Config>::VoterList::count(), 5);
let bounds_builder = ElectionBoundsBuilder::default();
// if voter count limit is less..
assert_eq!(
Staking::electing_voters(bounds_builder.voters_count(1.into()).build().voters)
.unwrap()
.len(),
1
);
// if voter count limit is equal..
assert_eq!(
Staking::electing_voters(bounds_builder.voters_count(5.into()).build().voters)
.unwrap()
.len(),
5
);
// if voter count limit is more.
assert_eq!(
Staking::electing_voters(bounds_builder.voters_count(55.into()).build().voters)
.unwrap()
.len(),
5
);
// if target count limit is more..
assert_eq!(
Staking::electable_targets(
bounds_builder.targets_count(6.into()).build().targets
)
.unwrap()
.len(),
4
);
// if target count limit is equal..
assert_eq!(
Staking::electable_targets(
bounds_builder.targets_count(4.into()).build().targets
)
.unwrap()
.len(),
4
);
// if target limit count is less, then we return an error.
assert_eq!(
Staking::electable_targets(
bounds_builder.targets_count(1.into()).build().targets
)
.unwrap_err(),
"Target snapshot too big"
);
});
}
#[test]
fn respects_snapshot_size_limits() {
ExtBuilder::default().build_and_execute(|| {
// voters: set size bounds that allows only for 1 voter.
let bounds = ElectionBoundsBuilder::default().voters_size(26.into()).build();
let elected = Staking::electing_voters(bounds.voters).unwrap();
assert!(elected.encoded_size() == 26 as usize);
let prev_len = elected.len();
// larger size bounds means more quota for voters.
let bounds = ElectionBoundsBuilder::default().voters_size(100.into()).build();
let elected = Staking::electing_voters(bounds.voters).unwrap();
assert!(elected.encoded_size() <= 100 as usize);
assert!(elected.len() > 1 && elected.len() > prev_len);
// targets: set size bounds that allows for only one target to fit in the snapshot.
let bounds = ElectionBoundsBuilder::default().targets_size(10.into()).build();
let elected = Staking::electable_targets(bounds.targets).unwrap();
assert!(elected.encoded_size() == 9 as usize);
let prev_len = elected.len();
// larger size bounds means more space for targets.
let bounds = ElectionBoundsBuilder::default().targets_size(100.into()).build();
let elected = Staking::electable_targets(bounds.targets).unwrap();
assert!(elected.encoded_size() <= 100 as usize);
assert!(elected.len() > 1 && elected.len() > prev_len);
});
}
#[test]
fn nomination_quota_checks_at_nominate_works() {
ExtBuilder::default().nominate(false).build_and_execute(|| {
// stash bond of 222 has a nomination quota of 2 targets.
bond(61, 222);
assert_eq!(Staking::api_nominations_quota(222), 2);
// nominating with targets below the nomination quota works.
assert_ok!(Staking::nominate(RuntimeOrigin::signed(61), vec![11]));
assert_ok!(Staking::nominate(RuntimeOrigin::signed(61), vec![11, 12]));
// nominating with targets above the nomination quota returns error.
assert_noop!(
Staking::nominate(RuntimeOrigin::signed(61), vec![11, 12, 13]),
Error::<Test>::TooManyTargets
);
});
}
#[test]
fn lazy_quota_npos_voters_works_above_quota() {
ExtBuilder::default()
.nominate(false)
.add_staker(
61,
60,
300, // 300 bond has 16 nomination quota.
StakerStatus::<AccountId>::Nominator(vec![21, 22, 23, 24, 25]),
)
.build_and_execute(|| {
// unbond 78 from stash 60 so that it's bonded balance is 222, which has a lower
// nomination quota than at nomination time (max 2 targets).
assert_ok!(Staking::unbond(RuntimeOrigin::signed(61), 78));
assert_eq!(Staking::api_nominations_quota(300 - 78), 2);
// even through 61 has nomination quota of 2 at the time of the election, all the
// nominations (5) will be used.
assert_eq!(
Staking::electing_voters(DataProviderBounds::default())
.unwrap()
.iter()
.map(|(stash, _, targets)| (*stash, targets.len()))
.collect::<Vec<_>>(),
vec![(11, 1), (21, 1), (31, 1), (61, 5)],
);
});
}
#[test]
fn nominations_quota_limits_size_work() {
ExtBuilder::default()
.nominate(false)
.add_staker(
71,
70,
333,
StakerStatus::<AccountId>::Nominator(vec![16, 15, 14, 13, 12, 11, 10]),
)
.build_and_execute(|| {
// nominations of controller 70 won't be added due to voter size limit exceeded.
let bounds = ElectionBoundsBuilder::default().voters_size(100.into()).build();
assert_eq!(
Staking::electing_voters(bounds.voters)
.unwrap()
.iter()
.map(|(stash, _, targets)| (*stash, targets.len()))
.collect::<Vec<_>>(),
vec![(11, 1), (21, 1), (31, 1)],
);
assert_eq!(
*staking_events().last().unwrap(),
Event::SnapshotVotersSizeExceeded { size: 75 }
);
// however, if the election voter size bounds were largers, the snapshot would
// include the electing voters of 70.
let bounds = ElectionBoundsBuilder::default().voters_size(1_000.into()).build();
assert_eq!(
Staking::electing_voters(bounds.voters)
.unwrap()
.iter()
.map(|(stash, _, targets)| (*stash, targets.len()))
.collect::<Vec<_>>(),
vec![(11, 1), (21, 1), (31, 1), (71, 7)],
);
});
}
#[test]
fn estimate_next_election_works() {
ExtBuilder::default().session_per_era(5).period(5).build_and_execute(|| {
// first session is always length 0.
for b in 1..20 {
run_to_block(b);
assert_eq!(Staking::next_election_prediction(System::block_number()), 20);
}
// election
run_to_block(20);
assert_eq!(Staking::next_election_prediction(System::block_number()), 45);
assert_eq!(staking_events().len(), 1);
Gavin Wood
committed
assert_eq!(*staking_events().last().unwrap(), Event::StakersElected);
for b in 21..45 {
run_to_block(b);
assert_eq!(Staking::next_election_prediction(System::block_number()), 45);
}
// election
run_to_block(45);
assert_eq!(Staking::next_election_prediction(System::block_number()), 70);
assert_eq!(staking_events().len(), 3);
Gavin Wood
committed
assert_eq!(*staking_events().last().unwrap(), Event::StakersElected);
thiolliere
committed
Staking::force_no_eras(RuntimeOrigin::root()).unwrap();
assert_eq!(Staking::next_election_prediction(System::block_number()), u64::MAX);
thiolliere
committed
Staking::force_new_era_always(RuntimeOrigin::root()).unwrap();
thiolliere
committed
assert_eq!(Staking::next_election_prediction(System::block_number()), 45 + 5);
Staking::force_new_era(RuntimeOrigin::root()).unwrap();
thiolliere
committed
assert_eq!(Staking::next_election_prediction(System::block_number()), 45 + 5);
// Do a fail election
MinimumValidatorCount::<Test>::put(1000);
run_to_block(50);
// Election: failed, next session is a new election
assert_eq!(Staking::next_election_prediction(System::block_number()), 50 + 5);
// The new era is still forced until a new era is planned.
assert_eq!(ForceEra::<Test>::get(), Forcing::ForceNew);
MinimumValidatorCount::<Test>::put(2);
run_to_block(55);
assert_eq!(Staking::next_election_prediction(System::block_number()), 55 + 25);
assert_eq!(staking_events().len(), 10);
assert_eq!(
*staking_events().last().unwrap(),
Event::ForceEra { mode: Forcing::NotForcing }
);
assert_eq!(
*staking_events().get(staking_events().len() - 2).unwrap(),
Event::StakersElected
);
thiolliere
committed
// The new era has been planned, forcing is changed from `ForceNew` to `NotForcing`.
assert_eq!(ForceEra::<Test>::get(), Forcing::NotForcing);
#[test]
#[should_panic]
fn count_check_works() {
ExtBuilder::default().build_and_execute(|| {
// We should never insert into the validators or nominators map directly as this will
// not keep track of the count. This test should panic as we verify the count is accurate
// after every test using the `post_checks` in `mock`.
Validators::<Test>::insert(987654321, ValidatorPrefs::default());
Nominators::<Test>::insert(
987654321,
Nominations {
targets: Default::default(),
submitted_in: Default::default(),
suppressed: false,
},
);
})
}
#[test]
fn min_bond_checks_work() {
ExtBuilder::default()
.existential_deposit(100)
.balance_factor(100)
.min_nominator_bond(1_000)
.min_validator_bond(1_500)
.build_and_execute(|| {
// 500 is not enough for any role
assert_ok!(Staking::bond(RuntimeOrigin::signed(3), 500, RewardDestination::Controller));
Staking::nominate(RuntimeOrigin::signed(3), vec![1]),
Error::<Test>::InsufficientBond
);
assert_noop!(
Staking::validate(RuntimeOrigin::signed(3), ValidatorPrefs::default()),
assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(3), 500));
assert_ok!(Staking::nominate(RuntimeOrigin::signed(3), vec![1]));
Staking::validate(RuntimeOrigin::signed(3), ValidatorPrefs::default()),
Error::<Test>::InsufficientBond,
);
assert_ok!(Staking::bond_extra(RuntimeOrigin::signed(3), 500));
assert_ok!(Staking::nominate(RuntimeOrigin::signed(3), vec![1]));
assert_ok!(Staking::validate(RuntimeOrigin::signed(3), ValidatorPrefs::default()));
// Can't unbond anything as validator
Staking::unbond(RuntimeOrigin::signed(3), 500),
Error::<Test>::InsufficientBond
);
// Once they are a nominator, they can unbond 500
assert_ok!(Staking::nominate(RuntimeOrigin::signed(3), vec![1]));
assert_ok!(Staking::unbond(RuntimeOrigin::signed(3), 500));
Staking::unbond(RuntimeOrigin::signed(3), 500),
Error::<Test>::InsufficientBond
);
// Once they are chilled they can unbond everything
assert_ok!(Staking::chill(RuntimeOrigin::signed(3)));
assert_ok!(Staking::unbond(RuntimeOrigin::signed(3), 1000));
#[test]
fn chill_other_works() {
ExtBuilder::default()
.existential_deposit(100)