Skip to content

Commit 73ccfe0

Browse files
Krayt78kianenigmaAnk4n
authored andcommitted
Fix calling nominate on a validator that doesn’t exist silently succeeds (#8436)
# Description This PR fixes a bug where calling nominate on a validator that doesn’t exist silently succeeds. It also updates all the tests that had an incorrect setup - they were simulating elections with unregistered validators. polkadot address: 14AgwoPjcRiEEJgjfHmvAqkjdERCG26WEvQUoGLuBzcXKMS2 --------- Co-authored-by: Kian Paimani <[email protected]> Co-authored-by: Ankan <[email protected]> Co-authored-by: kianenigma <[email protected]>
1 parent 3283ba6 commit 73ccfe0

File tree

6 files changed

+230
-216
lines changed

6 files changed

+230
-216
lines changed

prdoc/pr_8436.prdoc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
title: 'Fix calling nominate on a validator that doesn’t exist silently succeeds'
2+
3+
doc:
4+
- audience: Runtime Dev
5+
description: |
6+
This PR fixes a bug where calling nominate on a validator that doesn’t exist silently succeeds.
7+
It also updates all the tests that had an incorrect setup - they were simulating elections with unregistered validators.
8+
9+
crates:
10+
- name: pallet-staking-async
11+
bump: major

substrate/frame/staking-async/src/benchmarking.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ pub use frame_benchmarking::{
2929
};
3030
use frame_election_provider_support::SortedListProvider;
3131
use frame_support::{
32+
assert_ok,
3233
pallet_prelude::*,
3334
storage::bounded_vec::BoundedVec,
3435
traits::{Get, TryCollect},
@@ -157,8 +158,21 @@ impl<T: Config> ListScenario<T> {
157158
let i = asset::burn::<T>(asset::total_issuance::<T>());
158159
core::mem::forget(i);
159160

160-
// create accounts with the origin weight
161+
// make sure `account("random_validator", 0, SEED)` is a validator
162+
let validator_account = account("random_validator", 0, SEED);
163+
let validator_stake = asset::existential_deposit::<T>() * 1000u32.into();
164+
asset::set_stakeable_balance::<T>(&validator_account, validator_stake);
165+
assert_ok!(Staking::<T>::bond(
166+
RawOrigin::Signed(validator_account.clone()).into(),
167+
validator_stake / 2u32.into(),
168+
RewardDestination::Staked
169+
));
170+
assert_ok!(Staking::<T>::validate(
171+
RawOrigin::Signed(validator_account.clone()).into(),
172+
Default::default()
173+
));
161174

175+
// create accounts with the origin weight
162176
let (origin_stash1, origin_controller1) = create_stash_controller_with_balance::<T>(
163177
USER_SEED + 2,
164178
origin_weight,
@@ -167,7 +181,7 @@ impl<T: Config> ListScenario<T> {
167181
Staking::<T>::nominate(
168182
RawOrigin::Signed(origin_controller1.clone()).into(),
169183
// NOTE: these don't really need to be validators.
170-
vec![T::Lookup::unlookup(account("random_validator", 0, SEED))],
184+
vec![T::Lookup::unlookup(validator_account.clone())],
171185
)?;
172186

173187
let (_origin_stash2, origin_controller2) = create_stash_controller_with_balance::<T>(
@@ -177,7 +191,7 @@ impl<T: Config> ListScenario<T> {
177191
)?;
178192
Staking::<T>::nominate(
179193
RawOrigin::Signed(origin_controller2).into(),
180-
vec![T::Lookup::unlookup(account("random_validator", 0, SEED))],
194+
vec![T::Lookup::unlookup(validator_account.clone())],
181195
)?;
182196

183197
// find a destination weight that will trigger the worst case scenario
@@ -197,7 +211,7 @@ impl<T: Config> ListScenario<T> {
197211
)?;
198212
Staking::<T>::nominate(
199213
RawOrigin::Signed(dest_controller1).into(),
200-
vec![T::Lookup::unlookup(account("random_validator", 0, SEED))],
214+
vec![T::Lookup::unlookup(validator_account)],
201215
)?;
202216

203217
Ok(ListScenario { origin_stash1, origin_controller1, dest_weight })

substrate/frame/staking-async/src/pallet/mod.rs

Lines changed: 84 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -863,12 +863,20 @@ pub mod pallet {
863863
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
864864
fn build(&self) {
865865
crate::log!(trace, "initializing with {:?}", self);
866+
assert!(
867+
self.validator_count <=
868+
<T::ElectionProvider as ElectionProvider>::MaxWinnersPerPage::get() *
869+
<T::ElectionProvider as ElectionProvider>::Pages::get(),
870+
"validator count is too high, `ElectionProvider` can never fulfill this"
871+
);
866872
ValidatorCount::<T>::put(self.validator_count);
873+
867874
assert!(
868875
self.invulnerables.len() as u32 <= T::MaxInvulnerables::get(),
869876
"Too many invulnerable validators at genesis."
870877
);
871878
<Invulnerables<T>>::put(&self.invulnerables);
879+
872880
ForceEra::<T>::put(self.force_era);
873881
CanceledSlashPayout::<T>::put(self.canceled_payout);
874882
SlashRewardFraction::<T>::put(self.slash_reward_fraction);
@@ -881,39 +889,80 @@ pub mod pallet {
881889
MaxNominatorsCount::<T>::put(x);
882890
}
883891

892+
// First pass: set up all validators and idle stakers
884893
for &(ref stash, balance, ref status) in &self.stakers {
885-
crate::log!(
886-
trace,
887-
"inserting genesis staker: {:?} => {:?} => {:?}",
888-
stash,
889-
balance,
890-
status
891-
);
892-
assert!(
893-
asset::free_to_stake::<T>(stash) >= balance,
894-
"Stash does not have enough balance to bond."
895-
);
896-
assert_ok!(<Pallet<T>>::bond(
897-
T::RuntimeOrigin::from(Some(stash.clone()).into()),
898-
balance,
899-
RewardDestination::Staked,
900-
));
901-
assert_ok!(match status {
902-
crate::StakerStatus::Validator => <Pallet<T>>::validate(
903-
T::RuntimeOrigin::from(Some(stash.clone()).into()),
904-
Default::default(),
905-
),
906-
crate::StakerStatus::Nominator(votes) => <Pallet<T>>::nominate(
907-
T::RuntimeOrigin::from(Some(stash.clone()).into()),
908-
votes.iter().map(|l| T::Lookup::unlookup(l.clone())).collect(),
909-
),
910-
_ => Ok(()),
911-
});
912-
assert!(
913-
ValidatorCount::<T>::get() <=
914-
<T::ElectionProvider as ElectionProvider>::MaxWinnersPerPage::get() *
915-
<T::ElectionProvider as ElectionProvider>::Pages::get()
916-
);
894+
match status {
895+
crate::StakerStatus::Validator => {
896+
crate::log!(
897+
trace,
898+
"inserting genesis validator: {:?} => {:?} => {:?}",
899+
stash,
900+
balance,
901+
status
902+
);
903+
assert!(
904+
asset::free_to_stake::<T>(stash) >= balance,
905+
"Stash does not have enough balance to bond."
906+
);
907+
assert_ok!(<Pallet<T>>::bond(
908+
T::RuntimeOrigin::from(Some(stash.clone()).into()),
909+
balance,
910+
RewardDestination::Staked,
911+
));
912+
assert_ok!(<Pallet<T>>::validate(
913+
T::RuntimeOrigin::from(Some(stash.clone()).into()),
914+
Default::default(),
915+
));
916+
},
917+
crate::StakerStatus::Idle => {
918+
crate::log!(
919+
trace,
920+
"inserting genesis idle staker: {:?} => {:?} => {:?}",
921+
stash,
922+
balance,
923+
status
924+
);
925+
assert!(
926+
asset::free_to_stake::<T>(stash) >= balance,
927+
"Stash does not have enough balance to bond."
928+
);
929+
assert_ok!(<Pallet<T>>::bond(
930+
T::RuntimeOrigin::from(Some(stash.clone()).into()),
931+
balance,
932+
RewardDestination::Staked,
933+
));
934+
},
935+
_ => {},
936+
}
937+
}
938+
939+
// Second pass: set up all nominators (now that validators exist)
940+
for &(ref stash, balance, ref status) in &self.stakers {
941+
match status {
942+
crate::StakerStatus::Nominator(votes) => {
943+
crate::log!(
944+
trace,
945+
"inserting genesis nominator: {:?} => {:?} => {:?}",
946+
stash,
947+
balance,
948+
status
949+
);
950+
assert!(
951+
asset::free_to_stake::<T>(stash) >= balance,
952+
"Stash does not have enough balance to bond."
953+
);
954+
assert_ok!(<Pallet<T>>::bond(
955+
T::RuntimeOrigin::from(Some(stash.clone()).into()),
956+
balance,
957+
RewardDestination::Staked,
958+
));
959+
assert_ok!(<Pallet<T>>::nominate(
960+
T::RuntimeOrigin::from(Some(stash.clone()).into()),
961+
votes.iter().map(|l| T::Lookup::unlookup(l.clone())).collect(),
962+
));
963+
},
964+
_ => {},
965+
}
917966
}
918967

919968
// all voters are reported to the `VoterList`.
@@ -1569,7 +1618,9 @@ pub mod pallet {
15691618
let targets: BoundedVec<_, _> = targets
15701619
.into_iter()
15711620
.map(|n| {
1572-
if old.contains(&n) || !Validators::<T>::get(&n).blocked {
1621+
if old.contains(&n) ||
1622+
(Validators::<T>::contains_key(&n) && !Validators::<T>::get(&n).blocked)
1623+
{
15731624
Ok(n)
15741625
} else {
15751626
Err(Error::<T>::BadTarget.into())

0 commit comments

Comments
 (0)