Newer
Older
.offchain_phragmen_ext()
.build()
.execute_with(|| {
run_to_block(11);
// submission is not yet allowed
assert_eq!(Staking::era_election_status(), ElectionStatus::Closed);
// create all the indices just to build the solution.
Staking::create_stakers_snapshot();
let (compact, winners, score) = prepare_submission_with(true, 2, |_| {});
Staking::kill_stakers_snapshot();
assert_noop!(
Staking::submit_election_solution(
Origin::signed(10),
winners,
compact,
score,
current_era(),
),
Error::<Test>::PhragmenEarlySubmission,
);
})
}
#[test]
fn weak_solution_is_rejected() {
// A solution which is weaker than what we currently have on-chain is rejected.
ExtBuilder::default()
.offchain_phragmen_ext()
.has_stakers(false)
.validator_count(4)
.build()
.execute_with(|| {
build_offchain_phragmen_test_ext();
run_to_block(12);
// a good solution
let (compact, winners, score) = prepare_submission_with(true, 2, |_| {});
assert_ok!(Staking::submit_election_solution(
Origin::signed(10),
winners,
compact,
score,
current_era(),
));
// a bad solution
let (compact, winners, score) = horrible_phragmen_with_post_processing(false);
assert_noop!(
Staking::submit_election_solution(
Origin::signed(10),
winners,
compact,
score,
current_era(),
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
),
Error::<Test>::PhragmenWeakSubmission,
);
})
}
#[test]
fn better_solution_is_accepted() {
// A solution which is better than what we currently have on-chain is accepted.
ExtBuilder::default()
.offchain_phragmen_ext()
.validator_count(4)
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_phragmen_test_ext();
run_to_block(12);
// a meeeeh solution
let (compact, winners, score) = horrible_phragmen_with_post_processing(false);
assert_ok!(Staking::submit_election_solution(
Origin::signed(10),
winners,
compact,
score,
current_era(),
let (compact, winners, score) = prepare_submission_with(true, 2, |_| {});
assert_ok!(Staking::submit_election_solution(
Origin::signed(10),
winners,
compact,
score,
current_era(),
));
})
}
#[test]
fn offchain_worker_runs_when_window_open() {
// at the end of the first finalized block with ElectionStatus::open(_), it should execute.
let mut ext = ExtBuilder::default()
.offchain_phragmen_ext()
.validator_count(2)
.build();
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
ext.execute_with(|| {
run_to_block(12);
// local key 11 is in the elected set.
assert_eq_uvec!(Session::validators(), vec![11, 21]);
assert_eq!(state.read().transactions.len(), 0);
Staking::offchain_worker(12);
assert_eq!(state.read().transactions.len(), 1);
let encoded = state.read().transactions[0].clone();
let extrinsic: Extrinsic = Decode::decode(&mut &*encoded).unwrap();
let call = extrinsic.call;
let inner = match call {
mock::Call::Staking(inner) => inner,
};
assert_eq!(
<Staking as sp_runtime::traits::ValidateUnsigned>::validate_unsigned(
TransactionSource::Local,
&inner,
),
TransactionValidity::Ok(ValidTransaction {
priority: UnsignedPriority::get() + 1125, // the proposed slot stake.
provides: vec![("StakingOffchain", current_era()).encode()],
longevity: 3,
propagate: false,
})
)
})
}
#[test]
fn offchain_worker_runs_with_equalise() {
// Offchain worker equalises based on the number provided by randomness. See the difference
// in the priority, which comes from the computed score.
let mut ext = ExtBuilder::default()
.offchain_phragmen_ext()
.validator_count(2)
.max_offchain_iterations(2)
.build();
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
ext.execute_with(|| {
run_to_block(12);
// local key 11 is in the elected set.
assert_eq_uvec!(Session::validators(), vec![11, 21]);
assert_eq!(state.read().transactions.len(), 0);
Staking::offchain_worker(12);
assert_eq!(state.read().transactions.len(), 1);
let encoded = state.read().transactions[0].clone();
let extrinsic: Extrinsic = Decode::decode(&mut &*encoded).unwrap();
let call = extrinsic.call;
let inner = match call {
mock::Call::Staking(inner) => inner,
};
assert_eq!(
<Staking as sp_runtime::traits::ValidateUnsigned>::validate_unsigned(
TransactionSource::Local,
&inner,
),
TransactionValidity::Ok(ValidTransaction {
// the proposed slot stake, with equalize.
priority: UnsignedPriority::get() + 1250,
requires: vec![],
provides: vec![("StakingOffchain", active_era()).encode()],
longevity: 3,
propagate: false,
})
)
})
}
#[test]
fn mediocre_submission_from_authority_is_early_rejected() {
let mut ext = ExtBuilder::default()
.offchain_phragmen_ext()
.validator_count(4)
.build();
ext.execute_with(|| {
run_to_block(12);
// put a good solution on-chain
let (compact, winners, score) = prepare_submission_with(true, 2, |_| {});
assert_ok!(Staking::submit_election_solution(
Origin::signed(10),
winners,
compact,
score,
current_era(),
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
),);
// now run the offchain worker in the same chain state.
Staking::offchain_worker(12);
assert_eq!(state.read().transactions.len(), 1);
let encoded = state.read().transactions[0].clone();
let extrinsic: Extrinsic = Decode::decode(&mut &*encoded).unwrap();
let call = extrinsic.call;
let inner = match call {
mock::Call::Staking(inner) => inner,
};
// pass this call to ValidateUnsigned
assert_eq!(
<Staking as sp_runtime::traits::ValidateUnsigned>::validate_unsigned(
TransactionSource::Local,
&inner,
),
TransactionValidity::Err(
InvalidTransaction::Custom(<Error<Test>>::PhragmenWeakSubmission.as_u8()).into(),
),
)
})
}
#[test]
fn invalid_phragmen_result_correct_number_of_winners() {
ExtBuilder::default()
.offchain_phragmen_ext()
.validator_count(4)
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_phragmen_test_ext();
run_to_block(12);
ValidatorCount::put(3);
let (compact, winners, score) = prepare_submission_with(true, 2, |_| {});
ValidatorCount::put(4);
assert_eq!(winners.len(), 3);
assert_noop!(
Staking::submit_election_solution(
Origin::signed(10),
winners,
compact,
score,
current_era(),
),
Error::<Test>::PhragmenBogusWinnerCount,
);
})
}
#[test]
fn invalid_phragmen_result_correct_number_of_winners_1() {
// if we have too little validators, then the number of candidates is the bound.
ExtBuilder::default()
.offchain_phragmen_ext()
.validator_count(8) // we simply cannot elect 8
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_phragmen_test_ext();
run_to_block(12);
ValidatorCount::put(3);
let (compact, winners, score) = prepare_submission_with(true, 2, |_| {});
ValidatorCount::put(4);
assert_eq!(winners.len(), 3);
assert_noop!(
Staking::submit_election_solution(
Origin::signed(10),
winners,
compact,
score,
current_era(),
),
Error::<Test>::PhragmenBogusWinnerCount,
);
})
}
#[test]
fn invalid_phragmen_result_correct_number_of_winners_2() {
// if we have too little validators, then the number of candidates is the bound.
ExtBuilder::default()
.offchain_phragmen_ext()
.validator_count(8) // we simply cannot elect 8
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_phragmen_test_ext();
run_to_block(12);
let (compact, winners, score) = prepare_submission_with(true, 2, |_| {});
assert_eq!(winners.len(), 4);
// all good. We chose 4 and it works.
assert_ok!(Staking::submit_election_solution(
Origin::signed(10),
winners,
compact,
score,
current_era(),
),);
})
}
#[test]
fn invalid_phragmen_result_out_of_bound_nominator_index() {
// A nominator index which is simply invalid
ExtBuilder::default()
.offchain_phragmen_ext()
.validator_count(4)
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_phragmen_test_ext();
run_to_block(12);
assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4);
assert_eq!(Staking::snapshot_validators().unwrap().len(), 4);
let (mut compact, winners, score) = prepare_submission_with(true, 2, |_| {});
// index 9 doesn't exist.
compact.votes1.push((9, 2));
// The error type sadly cannot be more specific now.
assert_noop!(
Staking::submit_election_solution(
Origin::signed(10),
winners,
compact,
score,
current_era(),
),
Error::<Test>::PhragmenBogusCompact,
);
})
}
#[test]
fn invalid_phragmen_result_out_of_bound_validator_index() {
// A validator index which is out of bound
ExtBuilder::default()
.offchain_phragmen_ext()
.validator_count(4)
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_phragmen_test_ext();
run_to_block(12);
assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4);
assert_eq!(Staking::snapshot_validators().unwrap().len(), 4);
let (mut compact, winners, score) = prepare_submission_with(true, 2, |_| {});
// index 4 doesn't exist.
compact.votes1.push((3, 4));
// The error type sadly cannot be more specific now.
assert_noop!(
Staking::submit_election_solution(
Origin::signed(10),
winners,
compact,
score,
current_era(),
),
Error::<Test>::PhragmenBogusCompact,
);
})
}
#[test]
fn invalid_phragmen_result_out_of_bound_winner_index() {
// A winner index which is simply invalid
ExtBuilder::default()
.offchain_phragmen_ext()
.validator_count(4)
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_phragmen_test_ext();
run_to_block(12);
assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4);
assert_eq!(Staking::snapshot_validators().unwrap().len(), 4);
let (compact, _, score) = prepare_submission_with(true, 2, |_| {});
// index 4 doesn't exist.
let winners = vec![0, 1, 2, 4];
assert_noop!(
Staking::submit_election_solution(
Origin::signed(10),
winners,
compact,
score,
current_era(),
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
),
Error::<Test>::PhragmenBogusWinner,
);
})
}
#[test]
fn invalid_phragmen_result_non_winner_validator_index() {
// An edge that points to a correct validator index who is NOT a winner. This is very
// similar to the test that raises `PhragmenBogusNomination`.
ExtBuilder::default()
.offchain_phragmen_ext()
.validator_count(2) // we select only 2.
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_phragmen_test_ext();
run_to_block(12);
assert_eq!(Staking::snapshot_nominators().unwrap().len(), 5 + 4);
assert_eq!(Staking::snapshot_validators().unwrap().len(), 4);
let (compact, winners, score) = prepare_submission_with(true, 2, |a| {
a.iter_mut()
.find(|x| x.who == 5)
// all 3 cannot be among the winners. Although, all of them are validator
// candidates.
.map(|x| x.distribution = vec![(21, 50), (41, 30), (31, 20)]);
});
assert_noop!(
Staking::submit_election_solution(
Origin::signed(10),
winners,
compact,
score,
current_era(),
),
Error::<Test>::PhragmenBogusEdge,
);
})
}
#[test]
fn invalid_phragmen_result_wrong_self_vote() {
// A self vote for someone else.
ExtBuilder::default()
.offchain_phragmen_ext()
.validator_count(4)
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_phragmen_test_ext();
run_to_block(12);
let (compact, winners, score) = prepare_submission_with(true, 2, |a| {
// mutate a self vote to target someone else. That someone else is still among the
// winners
a.iter_mut().find(|x| x.who == 11).map(|x| {
x.distribution
.iter_mut()
.find(|y| y.0 == 11)
.map(|y| y.0 = 21)
});
});
assert_noop!(
Staking::submit_election_solution(
Origin::signed(10),
winners,
compact,
score,
current_era(),
),
Error::<Test>::PhragmenBogusSelfVote,
);
})
}
#[test]
fn invalid_phragmen_result_wrong_self_vote_2() {
// A self validator voting for someone else next to self vote.
ExtBuilder::default()
.offchain_phragmen_ext()
.validator_count(4)
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_phragmen_test_ext();
run_to_block(12);
let (compact, winners, score) = prepare_submission_with(true, 2, |a| {
// Remove the self vote.
a.retain(|x| x.who != 11);
// add is as a new double vote
a.push(StakedAssignment {
who: 11,
distribution: vec![(11, 50), (21, 50)],
});
});
// This raises score issue.
assert_noop!(
Staking::submit_election_solution(
Origin::signed(10),
winners,
compact,
score,
current_era(),
),
Error::<Test>::PhragmenBogusSelfVote,
);
})
}
#[test]
fn invalid_phragmen_result_over_stake() {
// Someone's edge ratios sums to more than 100%.
ExtBuilder::default()
.offchain_phragmen_ext()
.validator_count(4)
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_phragmen_test_ext();
run_to_block(12);
// Note: we don't reduce here to be able to tweak votes3. votes3 will vanish if you
// reduce.
let (mut compact, winners, score) = prepare_submission_with(false, 0, |_| {});
if let Some(c) = compact.votes3.iter_mut().find(|x| x.0 == 0) {
// by default it should have been (0, [(2, 33%), (1, 33%)], 0)
// now the sum is above 100%
c.1 = [(2, percent(66)), (1, percent(66))];
}
assert_noop!(
Staking::submit_election_solution(
Origin::signed(10),
winners,
compact,
score,
current_era(),
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
),
Error::<Test>::PhragmenBogusCompact,
);
})
}
#[test]
fn invalid_phragmen_result_under_stake() {
// at the time of this writing, we cannot under stake someone. The compact assignment works
// in a way that some of the stakes are presented by the submitter, and the last one is read
// from chain by subtracting the rest from total. Hence, the sum is always correct.
// This test is only here as a demonstration.
}
#[test]
fn invalid_phragmen_result_invalid_target_stealing() {
// A valid voter who voted for someone who is a candidate, and is a correct winner, but is
// actually NOT nominated by this nominator.
ExtBuilder::default()
.offchain_phragmen_ext()
.validator_count(4)
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_phragmen_test_ext();
run_to_block(12);
let (compact, winners, score) = prepare_submission_with(false, 0, |a| {
// 3 only voted for 20 and 40. We add a fake vote to 30. The stake sum is still
// correctly 100.
a.iter_mut()
.find(|x| x.who == 3)
.map(|x| x.distribution = vec![(21, 50), (41, 30), (31, 20)]);
});
assert_noop!(
Staking::submit_election_solution(
Origin::signed(10),
winners,
compact,
score,
current_era(),
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
),
Error::<Test>::PhragmenBogusNomination,
);
})
}
#[test]
fn nomination_slash_filter_is_checked() {
// If a nominator has voted for someone who has been recently slashed, that particular
// nomination should be disabled for the upcoming election. A solution must respect this
// rule.
ExtBuilder::default()
.offchain_phragmen_ext()
.validator_count(4)
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_phragmen_test_ext();
// finalize the round with fallback. This is needed since all nominator submission
// are in era zero and we want this one to pass with no problems.
run_to_block(15);
// go to the next session to trigger mock::start_era and bump the active era
run_to_block(20);
// slash 10. This must happen outside of the election window.
let offender_expo = Staking::eras_stakers(Staking::active_era().unwrap().index, 11);
on_offence_now(
&[OffenceDetails {
offender: (11, offender_expo.clone()),
reporters: vec![],
}],
&[Perbill::from_percent(50)],
);
// validate 10 again for the next round. But this guy will not have the votes that
// it should have had from 1 and 2.
assert_ok!(Staking::validate(
Origin::signed(10),
Default::default()
));
// open the election window and create snapshots.
run_to_block(32);
// a solution that has been prepared after the slash.
let (compact, winners, score) = prepare_submission_with(false, 0, |a| {
// no one is allowed to vote for 10, except for itself.
a.into_iter()
.filter(|s| s.who != 11)
.for_each(|s|
assert!(s.distribution.iter().find(|(t, _)| *t == 11).is_none())
);
});
// can be submitted.
assert_ok!(Staking::submit_election_solution(
Origin::signed(10),
winners,
compact,
score,
current_era(),
let (compact, winners, score) = prepare_submission_with(false, 0, |a| {
// add back the vote that has been filtered out.
a.push(StakedAssignment {
who: 1,
distribution: vec![(11, 100)]
});
});
// is rejected.
assert_noop!(
Staking::submit_election_solution(
Origin::signed(10),
winners,
compact,
score,
current_era(),
),
Error::<Test>::PhragmenSlashedNomination,
);
})
}
#[test]
fn invalid_phragmen_result_wrong_score() {
// A valid voter who's total distributed stake is more than what they bond
ExtBuilder::default()
.offchain_phragmen_ext()
.validator_count(4)
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_phragmen_test_ext();
run_to_block(12);
let (compact, winners, mut score) = prepare_submission_with(true, 2, |_| {});
score[0] += 1;
assert_noop!(
Staking::submit_election_solution(
Origin::signed(10),
winners,
compact,
score,
current_era(),
),
Error::<Test>::PhragmenBogusScore,
);
})
}
#[test]
fn offchain_storage_is_set() {
let mut ext = ExtBuilder::default()
.offchain_phragmen_ext()
.validator_count(4)
.build();
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
ext.execute_with(|| {
use offchain_election::OFFCHAIN_HEAD_DB;
use sp_runtime::offchain::storage::StorageValueRef;
run_to_block(12);
Staking::offchain_worker(12);
// it works
assert_eq!(state.read().transactions.len(), 1);
// and it is set
let storage = StorageValueRef::persistent(&OFFCHAIN_HEAD_DB);
assert_eq!(storage.get::<BlockNumber>().unwrap().unwrap(), 12);
})
}
#[test]
fn offchain_storage_prevents_duplicate() {
let mut ext = ExtBuilder::default()
.offchain_phragmen_ext()
.validator_count(4)
.build();
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
ext.execute_with(|| {
use offchain_election::OFFCHAIN_HEAD_DB;
use sp_runtime::offchain::storage::StorageValueRef;
let storage = StorageValueRef::persistent(&OFFCHAIN_HEAD_DB);
run_to_block(12);
// first run -- ok
assert_eq!(
offchain_election::set_check_offchain_execution_status::<Test>(12),
Ok(()),
);
assert_eq!(storage.get::<BlockNumber>().unwrap().unwrap(), 12);
// re-execute after the next. not allowed.
assert_eq!(
offchain_election::set_check_offchain_execution_status::<Test>(13),
Err("recently executed."),
);
// a fork like situation -- re-execute 10, 11, 12. But it won't go through.
assert_eq!(
offchain_election::set_check_offchain_execution_status::<Test>(10),
Err("fork."),
);
assert_eq!(
offchain_election::set_check_offchain_execution_status::<Test>(11),
Err("fork."),
);
assert_eq!(
offchain_election::set_check_offchain_execution_status::<Test>(12),
Err("recently executed."),
);
})
}
#[test]
#[should_panic]
fn offence_is_blocked_when_window_open() {
ExtBuilder::default()
.offchain_phragmen_ext()
.validator_count(4)
.has_stakers(false)
.build()
.execute_with(|| {
run_to_block(12);
assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12));
let offender_expo = Staking::eras_stakers(Staking::active_era().unwrap().index, 10);
// panic from the impl in mock
on_offence_now(
&[OffenceDetails {
offender: (10, offender_expo.clone()),
reporters: vec![],
}],
&[Perbill::from_percent(10)],
);
})
}
}
fn slash_kicks_validators_not_nominators_and_disables_nominator_for_kicked_validator() {
ExtBuilder::default().build_and_execute(|| {
assert_eq_uvec!(Session::validators(), vec![11, 21]);
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(Staking::active_era().unwrap().index, &11);
let exposure_21 = Staking::eras_stakers(Staking::active_era().unwrap().index, &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()));
let exposure_11 = Staking::eras_stakers(Staking::active_era().unwrap().index, &11);
let exposure_21 = Staking::eras_stakers(Staking::active_era().unwrap().index, &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);
}
#[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(|| {
let init_balance_10 = Balances::total_balance(&10);
let init_balance_100 = Balances::total_balance(&100);
let part_for_10 = Perbill::from_rational_approximation::<u32>(1000, 1125);
let part_for_100 = Perbill::from_rational_approximation::<u32>(125, 1125);
// Check state
Payee::<Test>::insert(11, RewardDestination::Controller);
Payee::<Test>::insert(101, RewardDestination::Controller);
<Module<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(3000);
assert!(total_payout_0 > 10); // Test is meaningful if reward something
<Module<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(3000);
assert!(total_payout_1 > 10); // Test is meaningful if reward something
assert!(total_payout_1 != total_payout_0);
<Module<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(3000);
assert!(total_payout_2 > 10); // Test is meaningful if reward something
assert!(total_payout_2 != total_payout_0);
assert!(total_payout_2 != total_payout_1);
mock::start_era(Staking::history_depth() + 1);
let active_era = Staking::active_era().unwrap().index;
// 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),
// Fail: Era out of history
Error::<Test>::InvalidEraToReward
);
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),
Staking::payout_stakers(Origin::signed(1337), 11, active_era),
// Fail: Era not finished yet
Error::<Test>::InvalidEraToReward
);
// 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(|| {
assert_eq!(Balances::free_balance(11), 1000);
let exposure = Staking::eras_stakers(Staking::active_era().unwrap().index, 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().build_and_execute(|| {
use pallet_session::SessionManager;
let val_set = Session::validators();
let init_session = Session::current_index();
let init_active_era = Staking::active_era().unwrap().index;
// 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()));