Newer
Older
assert_eq!(Balances::free_balance(11), 1000);
assert_eq!(Balances::free_balance(101), 2000);
// 11 and 21 both have the support of 100
let exposure_11 = Staking::eras_stakers(active_era(), &11);
let exposure_21 = Staking::eras_stakers(active_era(), &21);
assert_eq!(exposure_11.total, 1000 + 125);
assert_eq!(exposure_21.total, 1000 + 375);
on_offence_now(
&[OffenceDetails { offender: (11, exposure_11.clone()), reporters: vec![] }],
&[Perbill::from_percent(10)],
);
// post-slash balance
let nominator_slash_amount_11 = 125 / 10;
assert_eq!(Balances::free_balance(11), 900);
assert_eq!(Balances::free_balance(101), 2000 - nominator_slash_amount_11);
// This is the best way to check that the validator was chilled; `get` will
// return default value.
for (stash, _) in <Staking as Store>::Validators::iter() {
assert!(stash != 11);
}
let nominations = <Staking as Store>::Nominators::get(&101).unwrap();
// and make sure that the vote will be ignored even if the validator
// re-registers.
let last_slash = <Staking as Store>::SlashingSpans::get(&11).unwrap().last_nonzero_slash();
assert!(nominations.submitted_in < last_slash);
// actually re-bond the slashed validator
assert_ok!(Staking::validate(Origin::signed(10), Default::default()));
mock::start_active_era(2);
let exposure_11 = Staking::eras_stakers(active_era(), &11);
let exposure_21 = Staking::eras_stakers(active_era(), &21);
// 10 is re-elected, but without the support of 100
assert_eq!(exposure_11.total, 900);
// 20 is re-elected, with the (almost) entire support of 100
assert_eq!(exposure_21.total, 1000 + 500 - nominator_slash_amount_11);
}
André Silva
committed
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
#[test]
fn non_slashable_offence_doesnt_disable_validator() {
ExtBuilder::default().build_and_execute(|| {
mock::start_active_era(1);
assert_eq_uvec!(Session::validators(), vec![11, 21]);
let exposure_11 = Staking::eras_stakers(Staking::active_era().unwrap().index, &11);
let exposure_21 = Staking::eras_stakers(Staking::active_era().unwrap().index, &21);
// offence with no slash associated
on_offence_now(
&[OffenceDetails { offender: (11, exposure_11.clone()), reporters: vec![] }],
&[Perbill::zero()],
);
// offence that slashes 25% of the bond
on_offence_now(
&[OffenceDetails { offender: (21, exposure_21.clone()), reporters: vec![] }],
&[Perbill::from_percent(25)],
);
// the offence for validator 10 wasn't slashable so it wasn't disabled
assert!(!is_disabled(10));
// whereas validator 20 gets disabled
assert!(is_disabled(20));
});
}
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
#[test]
fn slashing_independent_of_disabling_validator() {
ExtBuilder::default().build_and_execute(|| {
mock::start_active_era(1);
assert_eq_uvec!(Session::validators(), vec![11, 21]);
let exposure_11 = Staking::eras_stakers(Staking::active_era().unwrap().index, &11);
let exposure_21 = Staking::eras_stakers(Staking::active_era().unwrap().index, &21);
let now = Staking::active_era().unwrap().index;
// offence with no slash associated, BUT disabling
on_offence_in_era(
&[OffenceDetails { offender: (11, exposure_11.clone()), reporters: vec![] }],
&[Perbill::zero()],
now,
DisableStrategy::Always,
);
// offence that slashes 25% of the bond, BUT not disabling
on_offence_in_era(
&[OffenceDetails { offender: (21, exposure_21.clone()), reporters: vec![] }],
&[Perbill::from_percent(25)],
now,
DisableStrategy::Never,
);
// the offence for validator 10 was explicitly disabled
assert!(is_disabled(10));
// whereas validator 20 is explicitly not disabled
assert!(!is_disabled(20));
});
}
André Silva
committed
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
#[test]
fn offence_threshold_triggers_new_era() {
ExtBuilder::default()
.validator_count(4)
.set_status(41, StakerStatus::Validator)
.build_and_execute(|| {
mock::start_active_era(1);
assert_eq_uvec!(Session::validators(), vec![11, 21, 31, 41]);
assert_eq!(
<Test as Config>::OffendingValidatorsThreshold::get(),
Perbill::from_percent(75),
);
// we have 4 validators and an offending validator threshold of 75%,
// once the third validator commits an offence a new era should be forced
let exposure_11 = Staking::eras_stakers(Staking::active_era().unwrap().index, &11);
let exposure_21 = Staking::eras_stakers(Staking::active_era().unwrap().index, &21);
let exposure_31 = Staking::eras_stakers(Staking::active_era().unwrap().index, &31);
on_offence_now(
&[OffenceDetails { offender: (11, exposure_11.clone()), reporters: vec![] }],
&[Perbill::zero()],
);
assert_eq!(ForceEra::<Test>::get(), Forcing::NotForcing);
on_offence_now(
&[OffenceDetails { offender: (21, exposure_21.clone()), reporters: vec![] }],
&[Perbill::zero()],
);
assert_eq!(ForceEra::<Test>::get(), Forcing::NotForcing);
on_offence_now(
&[OffenceDetails { offender: (31, exposure_31.clone()), reporters: vec![] }],
&[Perbill::zero()],
);
assert_eq!(ForceEra::<Test>::get(), Forcing::ForceNew);
});
}
#[test]
fn disabled_validators_are_kept_disabled_for_whole_era() {
ExtBuilder::default()
.validator_count(4)
.set_status(41, StakerStatus::Validator)
.build_and_execute(|| {
mock::start_active_era(1);
assert_eq_uvec!(Session::validators(), vec![11, 21, 31, 41]);
assert_eq!(<Test as Config>::SessionsPerEra::get(), 3);
let exposure_11 = Staking::eras_stakers(Staking::active_era().unwrap().index, &11);
let exposure_21 = Staking::eras_stakers(Staking::active_era().unwrap().index, &21);
on_offence_now(
&[OffenceDetails { offender: (11, exposure_11.clone()), reporters: vec![] }],
&[Perbill::zero()],
);
on_offence_now(
&[OffenceDetails { offender: (21, exposure_21.clone()), reporters: vec![] }],
&[Perbill::from_percent(25)],
);
// validator 10 should not be disabled since the offence wasn't slashable
assert!(!is_disabled(10));
// validator 20 gets disabled since it got slashed
assert!(is_disabled(20));
advance_session();
// disabled validators should carry-on through all sessions in the era
assert!(!is_disabled(10));
assert!(is_disabled(20));
// validator 10 should now get disabled
on_offence_now(
&[OffenceDetails { offender: (11, exposure_11.clone()), reporters: vec![] }],
&[Perbill::from_percent(25)],
);
advance_session();
// and both are disabled in the last session of the era
assert!(is_disabled(10));
assert!(is_disabled(20));
mock::start_active_era(2);
// when a new era starts disabled validators get cleared
assert!(!is_disabled(10));
assert!(!is_disabled(20));
});
}
#[test]
fn claim_reward_at_the_last_era_and_no_double_claim_and_invalid_claim() {
// should check that:
// * rewards get paid until history_depth for both validators and nominators
// * an invalid era to claim doesn't update last_reward
// * double claim of one era fails
ExtBuilder::default().nominate(true).build_and_execute(|| {
// Consumed weight for all payout_stakers dispatches that fail
let err_weight = weights::SubstrateWeight::<Test>::payout_stakers_alive_staked(0);
let init_balance_10 = Balances::total_balance(&10);
let init_balance_100 = Balances::total_balance(&100);
let part_for_10 = Perbill::from_rational::<u32>(1000, 1125);
let part_for_100 = Perbill::from_rational::<u32>(125, 1125);
// Check state
Payee::<Test>::insert(11, RewardDestination::Controller);
Payee::<Test>::insert(101, RewardDestination::Controller);
Pallet::<Test>::reward_by_ids(vec![(11, 1)]);
// Compute total payout now for whole duration as other parameter won't change
let total_payout_0 = current_total_payout_for_duration(reward_time_per_era());
mock::start_active_era(1);
Pallet::<Test>::reward_by_ids(vec![(11, 1)]);
// Change total issuance in order to modify total payout
let _ = Balances::deposit_creating(&999, 1_000_000_000);
// Compute total payout now for whole duration as other parameter won't change
let total_payout_1 = current_total_payout_for_duration(reward_time_per_era());
mock::start_active_era(2);
Pallet::<Test>::reward_by_ids(vec![(11, 1)]);
// Change total issuance in order to modify total payout
let _ = Balances::deposit_creating(&999, 1_000_000_000);
// Compute total payout now for whole duration as other parameter won't change
let total_payout_2 = current_total_payout_for_duration(reward_time_per_era());
assert!(total_payout_2 != total_payout_0);
assert!(total_payout_2 != total_payout_1);
mock::start_active_era(Staking::history_depth() + 1);
// This is the latest planned era in staking, not the active era
let current_era = Staking::current_era().unwrap();
// Last kept is 1:
assert!(current_era - Staking::history_depth() == 1);
assert_noop!(
Staking::payout_stakers(Origin::signed(1337), 11, 0),
Error::<Test>::InvalidEraToReward.with_weight(err_weight)
assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 1));
assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 2));
Staking::payout_stakers(Origin::signed(1337), 11, 2),
Error::<Test>::AlreadyClaimed.with_weight(err_weight)
Staking::payout_stakers(Origin::signed(1337), 11, active_era),
Error::<Test>::InvalidEraToReward.with_weight(err_weight)
// Era 0 can't be rewarded anymore and current era can't be rewarded yet
// only era 1 and 2 can be rewarded.
assert_eq!(
Balances::total_balance(&10),
init_balance_10 + part_for_10 * (total_payout_1 + total_payout_2),
);
assert_eq!(
Balances::total_balance(&100),
init_balance_100 + part_for_100 * (total_payout_1 + total_payout_2),
);
});
}
#[test]
fn zero_slash_keeps_nominators() {
ExtBuilder::default().build_and_execute(|| {
mock::start_active_era(1);
assert_eq!(Balances::free_balance(11), 1000);
let exposure = Staking::eras_stakers(active_era(), 11);
assert_eq!(Balances::free_balance(101), 2000);
on_offence_now(
&[OffenceDetails { offender: (11, exposure.clone()), reporters: vec![] }],
&[Perbill::from_percent(0)],
);
assert_eq!(Balances::free_balance(11), 1000);
assert_eq!(Balances::free_balance(101), 2000);
// This is the best way to check that the validator was chilled; `get` will
// return default value.
for (stash, _) in <Staking as Store>::Validators::iter() {
assert!(stash != 11);
}
let nominations = <Staking as Store>::Nominators::get(&101).unwrap();
// and make sure that the vote will not be ignored, because the slash was
// zero.
let last_slash = <Staking as Store>::SlashingSpans::get(&11).unwrap().last_nonzero_slash();
assert!(nominations.submitted_in >= last_slash);
});
}
ExtBuilder::default().initialize_first_session(false).build_and_execute(|| {
use pallet_session::SessionManager;
let val_set = Session::validators();
let init_session = Session::current_index();
let init_active_era = active_era();
// pallet-session is delaying session by one, thus the next session to plan is +2.
assert_eq!(<Staking as SessionManager<_>>::new_session(init_session + 2), None);
assert_eq!(
<Staking as SessionManager<_>>::new_session(init_session + 3),
Some(val_set.clone())
);
assert_eq!(<Staking as SessionManager<_>>::new_session(init_session + 4), None);
assert_eq!(<Staking as SessionManager<_>>::new_session(init_session + 5), None);
assert_eq!(
<Staking as SessionManager<_>>::new_session(init_session + 6),
Some(val_set.clone())
);
<Staking as SessionManager<_>>::end_session(init_session);
<Staking as SessionManager<_>>::start_session(init_session + 1);
assert_eq!(active_era(), init_active_era);
<Staking as SessionManager<_>>::end_session(init_session + 1);
<Staking as SessionManager<_>>::start_session(init_session + 2);
assert_eq!(active_era(), init_active_era);
// Reward current era
Staking::reward_by_ids(vec![(11, 1)]);
// New active era is triggered here.
<Staking as SessionManager<_>>::end_session(init_session + 2);
<Staking as SessionManager<_>>::start_session(init_session + 3);
assert_eq!(active_era(), init_active_era + 1);
<Staking as SessionManager<_>>::end_session(init_session + 3);
<Staking as SessionManager<_>>::start_session(init_session + 4);
assert_eq!(active_era(), init_active_era + 1);
<Staking as SessionManager<_>>::end_session(init_session + 4);
<Staking as SessionManager<_>>::start_session(init_session + 5);
assert_eq!(active_era(), init_active_era + 1);
// Reward current era
Staking::reward_by_ids(vec![(21, 2)]);
// New active era is triggered here.
<Staking as SessionManager<_>>::end_session(init_session + 5);
<Staking as SessionManager<_>>::start_session(init_session + 6);
assert_eq!(active_era(), init_active_era + 2);
// That reward are correct
assert_eq!(Staking::eras_reward_points(init_active_era).total, 1);
assert_eq!(Staking::eras_reward_points(init_active_era + 1).total, 2);
});
}
#[test]
fn test_max_nominator_rewarded_per_validator_and_cant_steal_someone_else_reward() {
ExtBuilder::default().build_and_execute(|| {
for i in 0..=<<Test as Config>::MaxNominatorRewardedPerValidator as Get<_>>::get() {
let stash = 10_000 + i as AccountId;
let controller = 20_000 + i as AccountId;
let balance = 10_000 + i as Balance;
assert_ok!(Staking::bond(
Origin::signed(stash),
controller,
balance,
RewardDestination::Stash
));
assert_ok!(Staking::nominate(Origin::signed(controller), vec![11]));
}
mock::start_active_era(1);
Pallet::<Test>::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);
mock::make_all_reward_payment(1);
// Assert only nominators from 1 to Max are rewarded
for i in 0..=<<Test as Config>::MaxNominatorRewardedPerValidator as Get<_>>::get() {
let stash = 10_000 + i as AccountId;
let balance = 10_000 + i as Balance;
if stash == 10_000 {
assert!(Balances::free_balance(&stash) == balance);
} else {
assert!(Balances::free_balance(&stash) > balance);
}
}
});
}
#[test]
fn set_history_depth_works() {
ExtBuilder::default().build_and_execute(|| {
mock::start_active_era(10);
Staking::set_history_depth(Origin::root(), 20, 0).unwrap();
assert!(<Staking as Store>::ErasTotalStake::contains_key(10 - 4));
assert!(<Staking as Store>::ErasTotalStake::contains_key(10 - 5));
Staking::set_history_depth(Origin::root(), 4, 0).unwrap();
assert!(<Staking as Store>::ErasTotalStake::contains_key(10 - 4));
assert!(!<Staking as Store>::ErasTotalStake::contains_key(10 - 5));
Staking::set_history_depth(Origin::root(), 3, 0).unwrap();
assert!(!<Staking as Store>::ErasTotalStake::contains_key(10 - 4));
assert!(!<Staking as Store>::ErasTotalStake::contains_key(10 - 5));
Staking::set_history_depth(Origin::root(), 8, 0).unwrap();
assert!(!<Staking as Store>::ErasTotalStake::contains_key(10 - 4));
assert!(!<Staking as Store>::ErasTotalStake::contains_key(10 - 5));
});
}
// Test that payout_stakers work in general, including that only the top
// `T::MaxNominatorRewardedPerValidator` nominators are rewarded.
ExtBuilder::default().has_stakers(false).build_and_execute(|| {
// Track the exposure of the validator and all nominators.
let mut total_exposure = balance;
// Track the exposure of the validator and the nominators that will get paid out.
let mut payout_exposure = balance;
bond_validator(11, 10, balance); // Default(64)
assert_eq!(Validators::<Test>::count(), 1);
// Create nominators, targeting stash of validators
for i in 0..100 {
let bond_amount = balance + i as Balance;
bond_nominator(1000 + i, 100 + i, bond_amount, vec![11]);
total_exposure += bond_amount;
if i >= 36 {
payout_exposure += bond_amount;
};
let payout_exposure_part = Perbill::from_rational(payout_exposure, total_exposure);
mock::start_active_era(1);
// compute and ensure the reward amount is greater than zero.
let payout = current_total_payout_for_duration(reward_time_per_era());
let actual_paid_out = payout_exposure_part * payout;
mock::start_active_era(2);
let pre_payout_total_issuance = Balances::total_issuance();
RewardOnUnbalanceWasCalled::set(false);
assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 1));
assert_eq_error_rate!(
Balances::total_issuance(),
pre_payout_total_issuance + actual_paid_out,
1
);
assert!(RewardOnUnbalanceWasCalled::get());
// Top 64 nominators of validator 11 automatically paid out, including the validator
// Validator payout goes to controller.
assert!(Balances::free_balance(&10) > balance);
for i in 36..100 {
assert!(Balances::free_balance(&(100 + i)) > balance + i as Balance);
}
// The bottom 36 do not
for i in 0..36 {
assert_eq!(Balances::free_balance(&(100 + i)), balance + i as Balance);
}
// We track rewards in `claimed_rewards` vec
assert_eq!(
Staking::ledger(&10),
Some(StakingLedger {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
claimed_rewards: vec![1]
})
);
for i in 3..16 {
Staking::reward_by_ids(vec![(11, 1)]);
// compute and ensure the reward amount is greater than zero.
let payout = current_total_payout_for_duration(reward_time_per_era());
let actual_paid_out = payout_exposure_part * payout;
let pre_payout_total_issuance = Balances::total_issuance();
mock::start_active_era(i);
RewardOnUnbalanceWasCalled::set(false);
assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, i - 1));
assert_eq_error_rate!(
Balances::total_issuance(),
pre_payout_total_issuance + actual_paid_out,
1
);
assert!(RewardOnUnbalanceWasCalled::get());
}
// We track rewards in `claimed_rewards` vec
assert_eq!(
Staking::ledger(&10),
Some(StakingLedger {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
claimed_rewards: (1..=14).collect()
})
);
for i in 16..100 {
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(i);
}
// We clean it up as history passes
assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 15));
assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 98));
assert_eq!(
Staking::ledger(&10),
Some(StakingLedger {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
claimed_rewards: vec![15, 98]
})
);
// Out of order claims works.
assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 69));
assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 23));
assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 42));
assert_eq!(
Staking::ledger(&10),
Some(StakingLedger {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
claimed_rewards: vec![15, 23, 42, 69, 98]
})
);
});
}
#[test]
fn payout_stakers_handles_basic_errors() {
// Here we will test payouts handle all errors.
ExtBuilder::default().has_stakers(false).build_and_execute(|| {
// Consumed weight for all payout_stakers dispatches that fail
let err_weight = weights::SubstrateWeight::<Test>::payout_stakers_alive_staked(0);
// Same setup as the test above
let balance = 1000;
bond_validator(11, 10, balance); // Default(64)
// Create nominators, targeting stash
for i in 0..100 {
bond_nominator(1000 + i, 100 + i, balance + i as Balance, vec![11]);
mock::start_active_era(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_noop!(
Staking::payout_stakers(Origin::signed(1337), 11, 2),
Error::<Test>::InvalidEraToReward.with_weight(err_weight)
);
assert_noop!(
Staking::payout_stakers(Origin::signed(1337), 10, 1),
Error::<Test>::NotStash.with_weight(err_weight)
);
for i in 3..100 {
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(i);
}
// We are at era 99, with history depth of 84
// We should be able to payout era 15 through 98 (84 total eras), but not 14 or 99.
assert_noop!(
Staking::payout_stakers(Origin::signed(1337), 11, 14),
Error::<Test>::InvalidEraToReward.with_weight(err_weight)
);
assert_noop!(
Staking::payout_stakers(Origin::signed(1337), 11, 99),
Error::<Test>::InvalidEraToReward.with_weight(err_weight)
);
assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 15));
assert_ok!(Staking::payout_stakers(Origin::signed(1337), 11, 98));
// Can't claim again
assert_noop!(
Staking::payout_stakers(Origin::signed(1337), 11, 15),
Error::<Test>::AlreadyClaimed.with_weight(err_weight)
);
assert_noop!(
Staking::payout_stakers(Origin::signed(1337), 11, 98),
Error::<Test>::AlreadyClaimed.with_weight(err_weight)
);
});
}
#[test]
fn payout_stakers_handles_weight_refund() {
// Note: this test relies on the assumption that `payout_stakers_alive_staked` is solely used by
// `payout_stakers` to calculate the weight of each payout op.
ExtBuilder::default().has_stakers(false).build_and_execute(|| {
let max_nom_rewarded =
<<Test as Config>::MaxNominatorRewardedPerValidator as Get<_>>::get();
// Make sure the configured value is meaningful for our use.
assert!(max_nom_rewarded >= 4);
let half_max_nom_rewarded = max_nom_rewarded / 2;
// Sanity check our max and half max nominator quantities.
assert!(half_max_nom_rewarded > 0);
assert!(max_nom_rewarded > half_max_nom_rewarded);
let max_nom_rewarded_weight =
<Test as Config>::WeightInfo::payout_stakers_alive_staked(max_nom_rewarded);
let half_max_nom_rewarded_weight =
<Test as Config>::WeightInfo::payout_stakers_alive_staked(half_max_nom_rewarded);
let zero_nom_payouts_weight = <Test as Config>::WeightInfo::payout_stakers_alive_staked(0);
assert!(zero_nom_payouts_weight > 0);
assert!(half_max_nom_rewarded_weight > zero_nom_payouts_weight);
assert!(max_nom_rewarded_weight > half_max_nom_rewarded_weight);
let balance = 1000;
bond_validator(11, 10, balance);
start_active_era(1);
// Reward just the validator.
Staking::reward_by_ids(vec![(11, 1)]);
// Add some `half_max_nom_rewarded` nominators who will start backing the validator in the
// next era.
for i in 0..half_max_nom_rewarded {
bond_nominator((1000 + i).into(), (100 + i).into(), balance + i as Balance, vec![11]);
}
start_active_era(2);
// Collect payouts when there are no nominators
let call =
TestRuntimeCall::Staking(StakingCall::payout_stakers { validator_stash: 11, era: 1 });
let info = call.get_dispatch_info();
let result = call.dispatch(Origin::signed(20));
assert_ok!(result);
assert_eq!(extract_actual_weight(&result, &info), zero_nom_payouts_weight);
// 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 =
TestRuntimeCall::Staking(StakingCall::payout_stakers { validator_stash: 11, era: 2 });
let info = call.get_dispatch_info();
let result = call.dispatch(Origin::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 =
TestRuntimeCall::Staking(StakingCall::payout_stakers { validator_stash: 11, era: 3 });
let info = call.get_dispatch_info();
let result = call.dispatch(Origin::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(), (100 + 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 =
TestRuntimeCall::Staking(StakingCall::payout_stakers { validator_stash: 11, era: 5 });
let info = call.get_dispatch_info();
let result = call.dispatch(Origin::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 =
TestRuntimeCall::Staking(StakingCall::payout_stakers { validator_stash: 11, era: 5 });
let info = call.get_dispatch_info();
let result = call.dispatch(Origin::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(|| {
// Era = None
bond_validator(9, 8, 1000);
assert_eq!(
Staking::ledger(&8),
Some(StakingLedger {
stash: 9,
total: 1000,
active: 1000,
unlocking: Default::default(),
claimed_rewards: vec![],
})
);
mock::start_active_era(5);
bond_validator(11, 10, 1000);
assert_eq!(
Staking::ledger(&10),
Some(StakingLedger {
stash: 11,
total: 1000,
active: 1000,
unlocking: Default::default(),
claimed_rewards: (0..5).collect(),
})
);
mock::start_active_era(99);
bond_validator(13, 12, 1000);
assert_eq!(
Staking::ledger(&12),
Some(StakingLedger {
stash: 13,
total: 1000,
active: 1000,
unlocking: Default::default(),
claimed_rewards: (15..99).collect(),
})
);
});
}
#[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
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;
// Create a validator:
bond_validator(11, 10, balance);
// Create a stash/controller pair
bond_nominator(1234, 1337, 100, vec![11]);
// kill controller
assert_ok!(Balances::transfer(Origin::signed(1337), 1234, 100));
assert_eq!(Balances::free_balance(1337), 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(Origin::signed(1337), 11, 1));
// Controller is created
assert!(Balances::free_balance(1337) > 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, 10, balance); // Default(64)
// Create a stash/controller pair
bond_nominator(1234, 1337, 100, vec![11]);
// Update payout location
assert_ok!(Staking::set_payee(Origin::signed(1337), 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(Origin::signed(1337), 11, 1));
// Payment is successful
assert!(Balances::free_balance(42) > 0);
})
}
#[test]
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
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() {