Newer
Older
RawEvent::StakingElection(ElectionCompute::Signed),
);
})
}
#[test]
fn early_solution_submission_is_rejected() {
// should check that we have a new validator set normally, event says that it comes from
// offchain.
ExtBuilder::default()
.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(),
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
),
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();
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
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();
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
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(),
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
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
),);
// 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(),
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
),
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(),
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
),
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(),
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
),
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();
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
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();
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
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
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(|| {