Newer
Older
assert_eq!(Staking::is_current_session_final(), false);
start_session(4);
assert_eq!(Session::current_index(), 4);
assert_eq!(Staking::current_era(), Some(1));
assert_eq!(Staking::is_current_session_final(), true);
start_session(5);
assert_eq!(Session::current_index(), 5);
// era changed.
assert_eq!(Staking::current_era(), Some(2));
assert_eq!(Staking::is_current_session_final(), false);
})
}
#[test]
fn offchain_window_is_triggered() {
ExtBuilder::default()
.session_per_era(5)
.period(10)
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
.election_lookahead(3)
.build()
.execute_with(|| {
run_to_block(7);
assert_session_era!(0, 0);
run_to_block(10);
assert_session_era!(1, 0);
assert_eq!(Staking::era_election_status(), ElectionStatus::Closed);
assert!(Staking::snapshot_nominators().is_none());
assert!(Staking::snapshot_validators().is_none());
run_to_block(36);
assert_session_era!(3, 0);
// fist era has session 0, which has 0 blocks length, so we have in total 40 blocks
// in the era.
run_to_block(37);
assert_session_era!(3, 0);
assert_eq!(Staking::era_election_status(), ElectionStatus::Open(37));
assert!(Staking::snapshot_nominators().is_some());
assert!(Staking::snapshot_validators().is_some());
run_to_block(38);
assert_eq!(Staking::era_election_status(), ElectionStatus::Open(37));
run_to_block(39);
assert_eq!(Staking::era_election_status(), ElectionStatus::Open(37));
run_to_block(40);
assert_session_era!(4, 1);
assert_eq!(Staking::era_election_status(), ElectionStatus::Closed);
assert!(Staking::snapshot_nominators().is_none());
assert!(Staking::snapshot_validators().is_none());
run_to_block(86);
assert_session_era!(8, 1);
assert_eq!(Staking::era_election_status(), ElectionStatus::Closed);
assert!(Staking::snapshot_nominators().is_none());
assert!(Staking::snapshot_validators().is_none());
// second era onwards has 50 blocks per era.
run_to_block(87);
assert_eq!(Staking::era_election_status(), ElectionStatus::Open(87));
assert!(Staking::snapshot_nominators().is_some());
assert!(Staking::snapshot_validators().is_some());
run_to_block(90);
assert_session_era!(9, 2);
assert_eq!(Staking::era_election_status(), ElectionStatus::Closed);
assert!(Staking::snapshot_nominators().is_none());
assert!(Staking::snapshot_validators().is_none());
})
}
fn offchain_window_is_triggered_when_forcing() {
ExtBuilder::default()
.session_per_era(5)
.period(10)
.election_lookahead(3)
.build()
.execute_with(|| {
run_to_block(12);
ForceEra::put(Forcing::ForceNew);
run_to_block(13);
assert_eq!(Staking::era_election_status(), ElectionStatus::Closed);
run_to_block(17); // instead of 47
assert_eq!(Staking::era_election_status(), ElectionStatus::Open(17));
run_to_block(20);
assert_eq!(Staking::era_election_status(), ElectionStatus::Closed);
})
}
#[test]
fn offchain_window_is_triggered_when_force_always() {
ExtBuilder::default()
.session_per_era(5)
.period(10)
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
.election_lookahead(3)
.build()
.execute_with(|| {
ForceEra::put(Forcing::ForceAlways);
run_to_block(16);
assert_eq!(Staking::era_election_status(), ElectionStatus::Closed);
run_to_block(17); // instead of 37
assert_eq!(Staking::era_election_status(), ElectionStatus::Open(17));
run_to_block(20);
assert_eq!(Staking::era_election_status(), ElectionStatus::Closed);
run_to_block(26);
assert_eq!(Staking::era_election_status(), ElectionStatus::Closed);
run_to_block(27); // next one again
assert_eq!(Staking::era_election_status(), ElectionStatus::Open(27));
})
}
#[test]
fn offchain_window_closes_when_forcenone() {
ExtBuilder::default()
.session_per_era(5)
.period(10)
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
.election_lookahead(3)
.build()
.execute_with(|| {
ForceEra::put(Forcing::ForceNone);
run_to_block(36);
assert_session_era!(3, 0);
assert_eq!(Staking::era_election_status(), ElectionStatus::Closed);
// opens
run_to_block(37);
assert_eq!(Staking::era_election_status(), ElectionStatus::Open(37));
assert!(Staking::is_current_session_final());
assert!(Staking::snapshot_validators().is_some());
// closes normally
run_to_block(40);
assert_eq!(Staking::era_election_status(), ElectionStatus::Closed);
assert!(!Staking::is_current_session_final());
assert!(Staking::snapshot_validators().is_none());
assert_session_era!(4, 0);
run_to_block(47);
assert_eq!(Staking::era_election_status(), ElectionStatus::Closed);
assert_session_era!(4, 0);
run_to_block(57);
assert_eq!(Staking::era_election_status(), ElectionStatus::Closed);
assert_session_era!(5, 0);
run_to_block(67);
assert_eq!(Staking::era_election_status(), ElectionStatus::Closed);
// Will not open again as scheduled
run_to_block(87);
assert_eq!(Staking::era_election_status(), ElectionStatus::Closed);
assert_session_era!(8, 0);
run_to_block(90);
assert_eq!(Staking::era_election_status(), ElectionStatus::Closed);
assert_session_era!(9, 0);
fn offchain_window_on_chain_fallback_works() {
ExtBuilder::default().build_and_execute(|| {
start_session(1);
start_session(2);
assert_eq!(Staking::era_election_status(), ElectionStatus::Closed);
// some election must have happened by now.
assert_eq!(
System::events()
.into_iter()
.map(|r| r.event)
.filter_map(|e| {
if let mock::Event::staking(inner) = e {
Some(inner)
} else {
None
}
})
.last()
.unwrap(),
RawEvent::StakingElection(ElectionCompute::OnChain),
);
})
}
#[test]
fn offchain_wont_work_if_snapshot_fails() {
ExtBuilder::default()
.offchain_election_ext()
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
.build()
.execute_with(|| {
run_to_block(12);
assert!(Staking::snapshot_validators().is_some());
assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12));
// validate more than the limit
let limit: NominatorIndex = ValidatorIndex::max_value() as NominatorIndex + 1;
let ctrl = 1_000_000;
for i in 0..limit {
bond_validator((1000 + i).into(), (1000 + i + ctrl).into(), 100);
}
// window stays closed since no snapshot was taken.
run_to_block(27);
assert!(Staking::snapshot_validators().is_none());
assert_eq!(Staking::era_election_status(), ElectionStatus::Closed);
})
}
#[test]
fn staking_is_locked_when_election_window_open() {
ExtBuilder::default()
.offchain_election_ext()
.election_lookahead(3)
.build()
.execute_with(|| {
run_to_block(12);
assert!(Staking::snapshot_validators().is_some());
assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12));
// chill et. al. are now not allowed.
assert_noop!(
Staking::chill(Origin::signed(10)),
Error::<Test>::CallNotAllowed,
);
})
}
#[test]
fn signed_result_can_be_submitted() {
// should check that we have a new validator set normally, event says that it comes from
// offchain.
.offchain_election_ext()
.build()
.execute_with(|| {
run_to_block(12);
assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12));
assert!(Staking::snapshot_validators().is_some());
let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {});
assert_ok!(submit_solution(
Origin::signed(10),
winners,
compact,
score,
));
let queued_result = Staking::queued_elected().unwrap();
assert_eq!(queued_result.compute, ElectionCompute::Signed);
assert_eq!(
System::events()
.into_iter()
.map(|r| r.event)
.filter_map(|e| {
if let mock::Event::staking(inner) = e {
Some(inner)
} else {
None
}
})
.last()
.unwrap(),
RawEvent::SolutionStored(ElectionCompute::Signed),
);
run_to_block(15);
assert_eq!(Staking::era_election_status(), ElectionStatus::Closed);
assert_eq!(
System::events()
.into_iter()
.map(|r| r.event)
.filter_map(|e| {
if let mock::Event::staking(inner) = e {
Some(inner)
} else {
None
}
})
.last()
.unwrap(),
RawEvent::StakingElection(ElectionCompute::Signed),
);
})
}
#[test]
fn signed_result_can_be_submitted_later() {
// same as `signed_result_can_be_submitted` but at a later block.
ExtBuilder::default()
.offchain_election_ext()
.build()
.execute_with(|| {
run_to_block(14);
assert_eq!(Staking::era_election_status(), ElectionStatus::Open(12));
let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {});
assert_ok!(submit_solution(Origin::signed(10), winners, compact, score));
let queued_result = Staking::queued_elected().unwrap();
assert_eq!(queued_result.compute, ElectionCompute::Signed);
run_to_block(15);
assert_eq!(Staking::era_election_status(), ElectionStatus::Closed);
assert_eq!(
System::events()
.into_iter()
.map(|r| r.event)
.filter_map(|e| {
if let mock::Event::staking(inner) = e {
Some(inner)
} else {
None
}
})
.last()
.unwrap(),
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.
.offchain_election_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, true, 2, |_| {});
Staking::kill_stakers_snapshot();
assert_err_with_weight!(
Staking::submit_election_solution(
Origin::signed(10),
winners.clone(),
compact.clone(),
current_era(),
ElectionSize::default(),
Error::<Test>::OffchainElectionEarlySubmission,
Some(<Test as frame_system::Config>::DbWeight::get().reads(1)),
);
})
}
#[test]
fn weak_solution_is_rejected() {
// A solution which is weaker than what we currently have on-chain is rejected.
ExtBuilder::default()
.offchain_election_ext()
.has_stakers(false)
.validator_count(4)
.build()
.execute_with(|| {
build_offchain_election_test_ext();
run_to_block(12);
// a good solution
let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {});
assert_ok!(submit_solution(
Origin::signed(10),
winners,
compact,
score,
));
// a bad solution
let (compact, winners, score) = horrible_npos_solution(false);
assert_err_with_weight!(
submit_solution(
winners.clone(),
compact.clone(),
Error::<Test>::OffchainElectionWeakSubmission,
Some(<Test as frame_system::Config>::DbWeight::get().reads(3))
);
})
}
#[test]
fn better_solution_is_accepted() {
// A solution which is better than what we currently have on-chain is accepted.
ExtBuilder::default()
.offchain_election_ext()
.validator_count(4)
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_election_test_ext();
run_to_block(12);
// a meeeeh solution
let (compact, winners, score) = horrible_npos_solution(false);
assert_ok!(submit_solution(
Origin::signed(10),
winners,
compact,
score,
));
// a better solution
let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {});
assert_ok!(submit_solution(
Origin::signed(10),
winners,
compact,
score,
));
})
}
#[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_election_ext()
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,
_ => unreachable!(),
};
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,
})
)
})
}
fn offchain_worker_runs_with_balancing() {
// Offchain worker balances 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_election_ext()
.validator_count(2)
.max_offchain_iterations(2)
.build();
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,
_ => unreachable!(),
};
assert_eq!(
<Staking as sp_runtime::traits::ValidateUnsigned>::validate_unsigned(
TransactionSource::Local,
&inner,
),
TransactionValidity::Ok(ValidTransaction {
// the proposed slot stake, with balance_solution.
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_election_ext()
ext.execute_with(|| {
run_to_block(12);
// put a good solution on-chain
let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {});
assert_ok!(submit_solution(
Origin::signed(10),
winners,
compact,
score,
),);
// 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,
_ => unreachable!(),
};
// pass this call to ValidateUnsigned
assert_eq!(
<Staking as sp_runtime::traits::ValidateUnsigned>::validate_unsigned(
TransactionSource::Local,
&inner,
),
TransactionValidity::Err(
InvalidTransaction::Custom(<Error<Test>>::OffchainElectionWeakSubmission.as_u8()).into(),
),
)
})
}
#[test]
fn invalid_election_correct_number_of_winners() {
.offchain_election_ext()
.validator_count(4)
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_election_test_ext();
run_to_block(12);
ValidatorCount::put(3);
let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {});
ValidatorCount::put(4);
assert_eq!(winners.len(), 3);
assert_noop!(
submit_solution(
Origin::signed(10),
winners,
compact,
score,
),
Error::<Test>::OffchainElectionBogusWinnerCount,
);
})
}
#[test]
fn invalid_election_solution_size() {
.offchain_election_ext()
.build()
.execute_with(|| {
run_to_block(12);
let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {});
assert_noop!(
Staking::submit_election_solution(
Origin::signed(10),
winners,
compact,
score,
current_era(),
ElectionSize::default(),
Error::<Test>::OffchainElectionBogusElectionSize,
fn invalid_election_correct_number_of_winners_1() {
// if we have too little validators, then the number of candidates is the bound.
ExtBuilder::default()
.offchain_election_ext()
.validator_count(8) // we simply cannot elect 8
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_election_test_ext();
run_to_block(12);
ValidatorCount::put(3);
let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {});
ValidatorCount::put(4);
assert_eq!(winners.len(), 3);
assert_noop!(
Origin::signed(10),
winners,
compact,
score,
),
Error::<Test>::OffchainElectionBogusWinnerCount,
fn invalid_election_correct_number_of_winners_2() {
// if we have too little validators, then the number of candidates is the bound.
ExtBuilder::default()
.offchain_election_ext()
.validator_count(8) // we simply cannot elect 8
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_election_test_ext();
let (compact, winners, score) = prepare_submission_with(true, true, 2, |_| {});
assert_eq!(winners.len(), 4);
// all good. We chose 4 and it works.
assert_ok!(submit_solution(
Origin::signed(10),
winners,
compact,
score,
),);
})
}
#[test]
fn invalid_election_out_of_bound_nominator_index() {
// A nominator index which is simply invalid
ExtBuilder::default()
.offchain_election_ext()
.validator_count(4)
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_election_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, true, 2, |_| {});
// index 9 doesn't exist.
compact.votes1.push((9, 2));
// The error type sadly cannot be more specific now.
assert_noop!(
Origin::signed(10),
winners,
compact,
score,
),
Error::<Test>::OffchainElectionBogusCompact,
fn invalid_election_out_of_bound_validator_index() {
// A validator index which is out of bound
ExtBuilder::default()
.offchain_election_ext()
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_election_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, true, 2, |_| {});
compact.votes1.iter_mut().for_each(|(_, vidx)| if *vidx == 1 { *vidx = 4 });
// The error type sadly cannot be more specific now.
assert_noop!(
Origin::signed(10),
winners,
compact,
score,
),
Error::<Test>::OffchainElectionBogusCompact,
fn invalid_election_out_of_bound_winner_index() {
// A winner index which is simply invalid
ExtBuilder::default()
.offchain_election_ext()
.validator_count(4)
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_election_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, true, 2, |_| {});
// index 4 doesn't exist.
let winners = vec![0, 1, 2, 4];
assert_noop!(
Origin::signed(10),
winners,
compact,
score,
),
Error::<Test>::OffchainElectionBogusWinner,
fn invalid_election_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 `OffchainElectionBogusNomination`.
.offchain_election_ext()
.validator_count(2) // we select only 2.
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_election_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(false, true, 2, |a| {
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
// swap all 11 and 41s in the distribution with non-winners. Note that it is
// important that the count of winners and the count of unique targets remain
// valid.
a.iter_mut().for_each(| StakedAssignment { who, distribution } |
distribution.iter_mut().for_each(|(t, _)| {
if *t == 41 { *t = 31 } else { *t = 21 }
// if it is self vote, correct that.
if *who == 41 { *who = 31 }
if *who == 11 { *who = 21 }
})
);
});
assert_noop!(
submit_solution(
Origin::signed(10),
winners,
compact,
score,
),
Error::<Test>::OffchainElectionBogusNomination,
);
})
}
#[test]
fn offchain_election_unique_target_count_is_checked() {
// Number of unique targets and and winners.len must match.
ExtBuilder::default()
.offchain_election_ext()
.validator_count(2) // we select only 2.
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_election_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(false, true, 2, |a| {
a.iter_mut()
.find(|x| x.who == 5)
// just add any new target.
.map(|x| {
// old value.
assert_eq!(x.distribution, vec![(41, 100)]);
// new value.
x.distribution = vec![(21, 50), (41, 50)]
});
Origin::signed(10),
winners,
compact,
score,
),
Error::<Test>::OffchainElectionBogusWinnerCount,
fn invalid_election_wrong_self_vote() {
// A self vote for someone else.
ExtBuilder::default()
.offchain_election_ext()
.validator_count(4)
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_election_test_ext();
let (compact, winners, score) = prepare_submission_with(true, 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!(
Origin::signed(10),
winners,
compact,
score,
),
Error::<Test>::OffchainElectionBogusSelfVote,
fn invalid_election_wrong_self_vote_2() {
// A self validator voting for someone else next to self vote.
ExtBuilder::default()
.offchain_election_ext()
.validator_count(4)
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_election_test_ext();
let (compact, winners, score) = prepare_submission_with(true, 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!(
Origin::signed(10),
winners,
compact,
score,
),
Error::<Test>::OffchainElectionBogusSelfVote,
fn invalid_election_over_stake() {
// Someone's edge ratios sums to more than 100%.
ExtBuilder::default()
.offchain_election_ext()
.validator_count(4)
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_election_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(true, 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!(
Origin::signed(10),
winners,
compact,
score,
),
Error::<Test>::OffchainElectionBogusCompact,
fn invalid_election_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_election_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_election_ext()
.validator_count(4)
.has_stakers(false)
.build()
.execute_with(|| {
build_offchain_election_test_ext();
let (compact, winners, score) = prepare_submission_with(true, 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!(
Origin::signed(10),
winners,
compact,
score,
),
Error::<Test>::OffchainElectionBogusNomination,
);
})
}
#[test]
fn nomination_slash_filter_is_checked() {
// If a nominator has voted for someone who has been recently slashed, that particular