Newer
Older
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],
);
});
}
5155
5156
5157
5158
5159
5160
5161
5162
5163
5164
5165
5166
5167
5168
5169
5170
5171
5172
5173
5174
5175
5176
5177
5178
5179
5180
5181
5182
5183
5184
5185
5186
5187
5188
5189
5190
5191
5192
5193
5194
5195
5196
5197
5198
5199
5200
5201
5202
5203
5204
5205
5206
5207
5208
5209
5210
5211
5212
5213
5214
5215
5216
5217
5218
5219
5220
5221
5222
5223
5224
5225
5226
5227
5228
5229
5230
5231
5232
5233
5234
5235
5236
5237
5238
5239
5240
5241
5242
5243
5244
5245
5246
5247
5248
5249
5250
5251
5252
5253
5254
5255
5256
5257
5258
5259
5260
5261
5262
5263
5264
5265
5266
5267
5268
5269
5270
5271
5272
5273
5274
5275
5276
5277
5278
5279
5280
5281
5282
5283
5284
5285
5286
5287
5288
5289
5290
5291
5292
5293
5294
5295
5296
5297
5298
5299
5300
5301
5302
5303
5304
5305
5306
5307
5308
5309
5310
5311
5312
5313
5314
5315
5316
5317
5318
5319
5320
5321
5322
5323
5324
5325
5326
5327
5328
5329
5330
5331
5332
5333
5334
5335
5336
5337
#[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::Stash));
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)
.balance_factor(100)
.min_nominator_bond(1_000)
.min_validator_bond(1_500)
.build_and_execute(|| {
let initial_validators = Validators::<Test>::count();
let initial_nominators = Nominators::<Test>::count();
for i in 0..15 {
let a = 4 * i;
let b = 4 * i + 2;
let c = 4 * i + 3;
Balances::make_free_balance_be(&a, 100_000);
Balances::make_free_balance_be(&b, 100_000);
Balances::make_free_balance_be(&c, 100_000);
// Nominator
assert_ok!(Staking::bond(RuntimeOrigin::signed(a), 1000, RewardDestination::Stash));
assert_ok!(Staking::nominate(RuntimeOrigin::signed(a), vec![1]));
assert_ok!(Staking::bond(RuntimeOrigin::signed(b), 1500, RewardDestination::Stash));
assert_ok!(Staking::validate(RuntimeOrigin::signed(b), ValidatorPrefs::default()));
// To chill other users, we need to:
// * Set a minimum bond amount
// * Set a limit
// * Set a threshold
//
// If any of these are missing, we do not have enough information to allow the
// `chill_other` to succeed from one user to another.
// Can't chill these users
assert_noop!(
Staking::chill_other(RuntimeOrigin::signed(1337), 0),
Error::<Test>::CannotChillOther
);
assert_noop!(
Staking::chill_other(RuntimeOrigin::signed(1337), 2),
Error::<Test>::CannotChillOther
);
// Change the minimum bond... but no limits.
assert_ok!(Staking::set_staking_configs(
ConfigOp::Set(1_500),
ConfigOp::Set(2_000),
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Remove
// Still can't chill these users
assert_noop!(
Staking::chill_other(RuntimeOrigin::signed(1337), 0),
Error::<Test>::CannotChillOther
);
assert_noop!(
Staking::chill_other(RuntimeOrigin::signed(1337), 2),
Error::<Test>::CannotChillOther
);
// Add limits, but no threshold
assert_ok!(Staking::set_staking_configs(
ConfigOp::Noop,
ConfigOp::Noop,
ConfigOp::Set(10),
ConfigOp::Set(10),
ConfigOp::Noop,
ConfigOp::Noop
));
// Still can't chill these users
assert_noop!(
Staking::chill_other(RuntimeOrigin::signed(1337), 0),
Error::<Test>::CannotChillOther
);
assert_noop!(
Staking::chill_other(RuntimeOrigin::signed(1337), 2),
Error::<Test>::CannotChillOther
);
assert_ok!(Staking::set_staking_configs(
ConfigOp::Noop,
ConfigOp::Noop,
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Noop,
ConfigOp::Noop
// Still can't chill these users
assert_noop!(
Staking::chill_other(RuntimeOrigin::signed(1337), 0),
Error::<Test>::CannotChillOther
);
assert_noop!(
Staking::chill_other(RuntimeOrigin::signed(1337), 2),
Error::<Test>::CannotChillOther
);
// Add threshold and limits
assert_ok!(Staking::set_staking_configs(
ConfigOp::Noop,
ConfigOp::Noop,
ConfigOp::Set(10),
ConfigOp::Set(10),
ConfigOp::Set(Percent::from_percent(75)),
ConfigOp::Noop
));
// 16 people total because tests start with 2 active one
assert_eq!(Nominators::<Test>::count(), 15 + initial_nominators);
assert_eq!(Validators::<Test>::count(), 15 + initial_validators);
// Users can now be chilled down to 7 people, so we try to remove 9 of them (starting
// with 16)
let b = 4 * i;
let d = 4 * i + 2;
assert_ok!(Staking::chill_other(RuntimeOrigin::signed(1337), b));
assert_ok!(Staking::chill_other(RuntimeOrigin::signed(1337), d));
// chill a nominator. Limit is not reached, not chill-able
assert_eq!(Nominators::<Test>::count(), 7);
Staking::chill_other(RuntimeOrigin::signed(1337), 0),
Error::<Test>::CannotChillOther
);
// chill a validator. Limit is reached, chill-able.
assert_eq!(Validators::<Test>::count(), 9);
assert_ok!(Staking::chill_other(RuntimeOrigin::signed(1337), 2));
})
}
#[test]
fn capped_stakers_works() {
ExtBuilder::default().build_and_execute(|| {
let validator_count = Validators::<Test>::count();
let nominator_count = Nominators::<Test>::count();
assert_eq!(nominator_count, 1);
// Change the maximums
let max = 10;
assert_ok!(Staking::set_staking_configs(
ConfigOp::Set(10),
ConfigOp::Set(10),
ConfigOp::Set(max),
ConfigOp::Set(max),
ConfigOp::Remove,
ConfigOp::Remove,
));
// can create `max - validator_count` validators
let mut some_existing_validator = AccountId::default();
for i in 0..max - validator_count {
let (_, controller) = testing_utils::create_stash_controller::<Test>(
i + 10_000_000,
RewardDestination::Stash,
assert_ok!(Staking::validate(
RuntimeOrigin::signed(controller),
ValidatorPrefs::default()
));
some_existing_validator = controller;
}
let (_, last_validator) =
testing_utils::create_stash_controller::<Test>(1337, 100, RewardDestination::Stash)
.unwrap();
Staking::validate(RuntimeOrigin::signed(last_validator), ValidatorPrefs::default()),
Error::<Test>::TooManyValidators,
);
// same with nominators
let mut some_existing_nominator = AccountId::default();
for i in 0..max - nominator_count {
let (_, controller) = testing_utils::create_stash_controller::<Test>(
i + 20_000_000,
RewardDestination::Stash,
assert_ok!(Staking::nominate(RuntimeOrigin::signed(controller), vec![1]));
some_existing_nominator = controller;
}
// one more is too many.
let (_, last_nominator) = testing_utils::create_stash_controller::<Test>(
30_000_000,
100,
RewardDestination::Stash,
)
.unwrap();
assert_noop!(
Staking::nominate(RuntimeOrigin::signed(last_nominator), vec![1]),
Error::<Test>::TooManyNominators
);
assert_ok!(Staking::nominate(RuntimeOrigin::signed(some_existing_nominator), vec![1]));
// Re-validate works fine
assert_ok!(Staking::validate(
RuntimeOrigin::signed(some_existing_validator),
ValidatorPrefs::default()
));
// No problem when we set to `None` again
assert_ok!(Staking::set_staking_configs(
ConfigOp::Noop,
ConfigOp::Noop,
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Noop,
ConfigOp::Noop,
assert_ok!(Staking::nominate(RuntimeOrigin::signed(last_nominator), vec![1]));
assert_ok!(Staking::validate(
RuntimeOrigin::signed(last_validator),
ValidatorPrefs::default()
));
#[test]
fn min_commission_works() {
ExtBuilder::default().build_and_execute(|| {
// account 11 controls the stash of itself.
assert_ok!(Staking::validate(
RuntimeOrigin::signed(11),
ValidatorPrefs { commission: Perbill::from_percent(5), blocked: false }
));
// event emitted should be correct
assert_eq!(
*staking_events().last().unwrap(),
Event::ValidatorPrefsSet {
stash: 11,
prefs: ValidatorPrefs { commission: Perbill::from_percent(5), blocked: false }
}
assert_ok!(Staking::set_staking_configs(
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Remove,
ConfigOp::Set(Perbill::from_percent(10)),
));
// can't make it less than 10 now
assert_noop!(
Staking::validate(
RuntimeOrigin::signed(11),
ValidatorPrefs { commission: Perbill::from_percent(5), blocked: false }
),
Error::<Test>::CommissionTooLow
);
// can only change to higher.
assert_ok!(Staking::validate(
RuntimeOrigin::signed(11),
ValidatorPrefs { commission: Perbill::from_percent(10), blocked: false }
));
assert_ok!(Staking::validate(
RuntimeOrigin::signed(11),
ValidatorPrefs { commission: Perbill::from_percent(15), blocked: false }
));
})
}
#[should_panic]
fn change_of_absolute_max_nominations() {
use frame_election_provider_support::ElectionDataProvider;
ExtBuilder::default()
.add_staker(61, 61, 10, StakerStatus::Nominator(vec![1]))
.add_staker(71, 71, 10, StakerStatus::Nominator(vec![1, 2, 3]))
.balance_factor(10)
.build_and_execute(|| {
// pre-condition
assert_eq!(AbsoluteMaxNominations::get(), 16);
assert_eq!(
Nominators::<Test>::iter()
.map(|(k, n)| (k, n.targets.len()))
.collect::<Vec<_>>(),
vec![(101, 2), (71, 3), (61, 1)]
// default bounds are unbounded.
let bounds = DataProviderBounds::default();
// 3 validators and 3 nominators
assert_eq!(Staking::electing_voters(bounds).unwrap().len(), 3 + 3);
// abrupt change from 16 to 4, everyone should be fine.
AbsoluteMaxNominations::set(4);
assert_eq!(
Nominators::<Test>::iter()
.map(|(k, n)| (k, n.targets.len()))
.collect::<Vec<_>>(),
vec![(101, 2), (71, 3), (61, 1)]
assert_eq!(Staking::electing_voters(bounds).unwrap().len(), 3 + 3);
// abrupt change from 4 to 3, everyone should be fine.
AbsoluteMaxNominations::set(3);
assert_eq!(
Nominators::<Test>::iter()
.map(|(k, n)| (k, n.targets.len()))
.collect::<Vec<_>>(),
vec![(101, 2), (71, 3), (61, 1)]
assert_eq!(Staking::electing_voters(bounds).unwrap().len(), 3 + 3);
// abrupt change from 3 to 2, this should cause some nominators to be non-decodable, and
// thus non-existent unless if they update.
AbsoluteMaxNominations::set(2);
assert_eq!(
Nominators::<Test>::iter()
.map(|(k, n)| (k, n.targets.len()))
.collect::<Vec<_>>(),
vec![(101, 2), (61, 1)]
);
// 70 is still in storage..
assert!(Nominators::<Test>::contains_key(71));
// but its value cannot be decoded and default is returned.
assert!(Nominators::<Test>::get(71).is_none());
assert_eq!(Staking::electing_voters(bounds).unwrap().len(), 3 + 2);
assert!(Nominators::<Test>::contains_key(101));
// abrupt change from 2 to 1, this should cause some nominators to be non-decodable, and
// thus non-existent unless if they update.
AbsoluteMaxNominations::set(1);
assert_eq!(
Nominators::<Test>::iter()
.map(|(k, n)| (k, n.targets.len()))
.collect::<Vec<_>>(),
vec![(61, 1)]
assert!(Nominators::<Test>::contains_key(71));
assert!(Nominators::<Test>::contains_key(61));
assert!(Nominators::<Test>::get(71).is_none());
assert!(Nominators::<Test>::get(61).is_some());
assert_eq!(Staking::electing_voters(bounds).unwrap().len(), 3 + 1);
// now one of them can revive themselves by re-nominating to a proper value.
assert_ok!(Staking::nominate(RuntimeOrigin::signed(71), vec![1]));
assert_eq!(
Nominators::<Test>::iter()
.map(|(k, n)| (k, n.targets.len()))
.collect::<Vec<_>>(),
vec![(71, 1), (61, 1)]
);
// or they can be chilled by any account.
assert!(Nominators::<Test>::contains_key(101));
assert!(Nominators::<Test>::get(101).is_none());
assert_ok!(Staking::chill_other(RuntimeOrigin::signed(71), 101));
assert!(!Nominators::<Test>::contains_key(101));
assert!(Nominators::<Test>::get(101).is_none());
})
}
5860
5861
5862
5863
5864
5865
5866
5867
5868
5869
5870
5871
5872
5873
5874
5875
5876
5877
5878
5879
5880
5881
5882
5883
5884
5885
5886
5887
5888
5889
5890
5891
5892
5893
5894
5895
#[test]
fn nomination_quota_max_changes_decoding() {
use frame_election_provider_support::ElectionDataProvider;
ExtBuilder::default()
.add_staker(60, 61, 10, StakerStatus::Nominator(vec![1]))
.add_staker(70, 71, 10, StakerStatus::Nominator(vec![1, 2, 3]))
.add_staker(30, 330, 10, StakerStatus::Nominator(vec![1, 2, 3, 4]))
.add_staker(50, 550, 10, StakerStatus::Nominator(vec![1, 2, 3, 4]))
.balance_factor(10)
.build_and_execute(|| {
// pre-condition.
assert_eq!(MaxNominationsOf::<Test>::get(), 16);
let unbonded_election = DataProviderBounds::default();
assert_eq!(
Nominators::<Test>::iter()
.map(|(k, n)| (k, n.targets.len()))
.collect::<Vec<_>>(),
vec![(70, 3), (101, 2), (50, 4), (30, 4), (60, 1)]
);
// 4 validators and 4 nominators
assert_eq!(Staking::electing_voters(unbonded_election).unwrap().len(), 4 + 4);
});
}
#[test]
fn api_nominations_quota_works() {
ExtBuilder::default().build_and_execute(|| {
assert_eq!(Staking::api_nominations_quota(10), MaxNominationsOf::<Test>::get());
assert_eq!(Staking::api_nominations_quota(333), MaxNominationsOf::<Test>::get());
assert_eq!(Staking::api_nominations_quota(222), 2);
assert_eq!(Staking::api_nominations_quota(111), 1);
})
}
mod sorted_list_provider {
use super::*;
use frame_election_provider_support::SortedListProvider;
#[test]
fn re_nominate_does_not_change_counters_or_list() {
ExtBuilder::default().nominate(true).build_and_execute(|| {
// given
Kian Paimani
committed
let pre_insert_voter_count =
(Nominators::<Test>::count() + Validators::<Test>::count()) as u32;
assert_eq!(<Test as Config>::VoterList::count(), pre_insert_voter_count);
assert_eq!(
<Test as Config>::VoterList::iter().collect::<Vec<_>>(),
vec![11, 21, 31, 101]
);
// when account 101 renominates
assert_ok!(Staking::nominate(RuntimeOrigin::signed(101), vec![41]));
// then counts don't change
Kian Paimani
committed
assert_eq!(<Test as Config>::VoterList::count(), pre_insert_voter_count);
// and the list is the same
assert_eq!(
<Test as Config>::VoterList::iter().collect::<Vec<_>>(),
vec![11, 21, 31, 101]
);
});
}
#[test]
fn re_validate_does_not_change_counters_or_list() {
ExtBuilder::default().nominate(false).build_and_execute(|| {
// given
let pre_insert_voter_count =
(Nominators::<Test>::count() + Validators::<Test>::count()) as u32;
assert_eq!(<Test as Config>::VoterList::count(), pre_insert_voter_count);
assert_eq!(<Test as Config>::VoterList::iter().collect::<Vec<_>>(), vec![11, 21, 31]);
// when account 11 re-validates
assert_ok!(Staking::validate(RuntimeOrigin::signed(11), Default::default()));
Kian Paimani
committed
// then counts don't change
assert_eq!(<Test as Config>::VoterList::count(), pre_insert_voter_count);
// and the list is the same
Kian Paimani
committed
assert_eq!(<Test as Config>::VoterList::iter().collect::<Vec<_>>(), vec![11, 21, 31]);
});
}
}
#[test]
fn force_apply_min_commission_works() {
let prefs = |c| ValidatorPrefs { commission: Perbill::from_percent(c), blocked: false };
let validators = || Validators::<Test>::iter().collect::<Vec<_>>();
ExtBuilder::default().build_and_execute(|| {
assert_ok!(Staking::validate(RuntimeOrigin::signed(31), prefs(10)));
assert_ok!(Staking::validate(RuntimeOrigin::signed(21), prefs(5)));
// Given
assert_eq!(validators(), vec![(31, prefs(10)), (21, prefs(5)), (11, prefs(0))]);
MinCommission::<Test>::set(Perbill::from_percent(5));
// When applying to a commission greater than min
assert_ok!(Staking::force_apply_min_commission(RuntimeOrigin::signed(1), 31));
// Then the commission is not changed
assert_eq!(validators(), vec![(31, prefs(10)), (21, prefs(5)), (11, prefs(0))]);
// When applying to a commission that is equal to min
assert_ok!(Staking::force_apply_min_commission(RuntimeOrigin::signed(1), 21));
// Then the commission is not changed
assert_eq!(validators(), vec![(31, prefs(10)), (21, prefs(5)), (11, prefs(0))]);
// When applying to a commission that is less than the min
assert_ok!(Staking::force_apply_min_commission(RuntimeOrigin::signed(1), 11));
// Then the commission is bumped to the min
assert_eq!(validators(), vec![(31, prefs(10)), (21, prefs(5)), (11, prefs(5))]);
// When applying commission to a validator that doesn't exist then storage is not altered
assert_noop!(
Staking::force_apply_min_commission(RuntimeOrigin::signed(1), 420),
Error::<Test>::NotStash
);
});
}
#[test]
fn proportional_slash_stop_slashing_if_remaining_zero() {
let c = |era, value| UnlockChunk::<Balance> { era, value };
// we have some chunks, but they are not affected.
let unlocking = bounded_vec![c(1, 10), c(2, 10)];
// Given
let mut ledger = StakingLedger::<Test>::new(123, 20);
ledger.total = 40;
ledger.unlocking = unlocking;
assert_eq!(BondingDuration::get(), 3);
// should not slash more than the amount requested, by accidentally slashing the first chunk.
assert_eq!(ledger.slash(18, 1, 0), 18);
}