referrerpolicy=no-referrer-when-downgrade

pallet_staking_async/pallet/
impls.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! `pallet-staking-async`'s main `impl` blocks.
19
20use crate::{
21	asset,
22	election_size_tracker::StaticTracker,
23	log,
24	session_rotation::{self, Eras, Rotator},
25	slashing::OffenceRecord,
26	weights::WeightInfo,
27	BalanceOf, Exposure, Forcing, LedgerIntegrityState, MaxNominationsOf, Nominations,
28	NominationsQuota, PositiveImbalanceOf, RewardDestination, SnapshotStatus, StakingLedger,
29	ValidatorPrefs, STAKING_ID,
30};
31use alloc::{boxed::Box, vec, vec::Vec};
32use frame_election_provider_support::{
33	bounds::CountBound, data_provider, DataProviderBounds, ElectionDataProvider, ElectionProvider,
34	PageIndex, ScoreProvider, SortedListProvider, VoteWeight, VoterOf,
35};
36use frame_support::{
37	defensive,
38	dispatch::WithPostDispatchInfo,
39	pallet_prelude::*,
40	traits::{
41		Defensive, DefensiveSaturating, Get, Imbalance, InspectLockableCurrency, LockableCurrency,
42		OnUnbalanced,
43	},
44	weights::Weight,
45};
46use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin};
47use pallet_staking_async_rc_client::{self as rc_client};
48use sp_runtime::{
49	traits::{CheckedAdd, Saturating, StaticLookup, Zero},
50	ArithmeticError, DispatchResult, Perbill,
51};
52use sp_staking::{
53	currency_to_vote::CurrencyToVote,
54	EraIndex, OnStakingUpdate, Page, SessionIndex, Stake,
55	StakingAccount::{self, Controller, Stash},
56	StakingInterface,
57};
58
59use super::pallet::*;
60
61#[cfg(feature = "try-runtime")]
62use frame_support::ensure;
63#[cfg(any(test, feature = "try-runtime"))]
64use sp_runtime::TryRuntimeError;
65
66/// The maximum number of iterations that we do whilst iterating over `T::VoterList` in
67/// `get_npos_voters`.
68///
69/// In most cases, if we want n items, we iterate exactly n times. In rare cases, if a voter is
70/// invalid (for any reason) the iteration continues. With this constant, we iterate at most 2 * n
71/// times and then give up.
72const NPOS_MAX_ITERATIONS_COEFFICIENT: u32 = 2;
73
74impl<T: Config> Pallet<T> {
75	/// Returns the minimum required bond for participation, considering nominators,
76	/// and the chainโ€™s existential deposit.
77	///
78	/// This function computes the smallest allowed bond among `MinValidatorBond` and
79	/// `MinNominatorBond`, but ensures it is not below the existential deposit required to keep an
80	/// account alive.
81	pub(crate) fn min_chilled_bond() -> BalanceOf<T> {
82		MinValidatorBond::<T>::get()
83			.min(MinNominatorBond::<T>::get())
84			.max(asset::existential_deposit::<T>())
85	}
86
87	/// Returns the minimum required bond for validators, defaulting to `MinNominatorBond` if not
88	/// set or less than `MinNominatorBond`.
89	pub(crate) fn min_validator_bond() -> BalanceOf<T> {
90		MinValidatorBond::<T>::get().max(Self::min_nominator_bond())
91	}
92
93	/// Returns the minimum required bond for nominators, considering the chainโ€™s existential
94	/// deposit.
95	pub(crate) fn min_nominator_bond() -> BalanceOf<T> {
96		MinNominatorBond::<T>::get().max(asset::existential_deposit::<T>())
97	}
98
99	/// Fetches the ledger associated with a controller or stash account, if any.
100	pub fn ledger(account: StakingAccount<T::AccountId>) -> Result<StakingLedger<T>, Error<T>> {
101		StakingLedger::<T>::get(account)
102	}
103
104	pub fn payee(account: StakingAccount<T::AccountId>) -> Option<RewardDestination<T::AccountId>> {
105		StakingLedger::<T>::reward_destination(account)
106	}
107
108	/// Fetches the controller bonded to a stash account, if any.
109	pub fn bonded(stash: &T::AccountId) -> Option<T::AccountId> {
110		StakingLedger::<T>::paired_account(Stash(stash.clone()))
111	}
112
113	/// Inspects and returns the corruption state of a ledger and direct bond, if any.
114	///
115	/// Note: all operations in this method access directly the `Bonded` and `Ledger` storage maps
116	/// instead of using the [`StakingLedger`] API since the bond and/or ledger may be corrupted.
117	/// It is also meant to check state for direct bonds and may not work as expected for virtual
118	/// bonds.
119	pub(crate) fn inspect_bond_state(
120		stash: &T::AccountId,
121	) -> Result<LedgerIntegrityState, Error<T>> {
122		// look at any old unmigrated lock as well.
123		let hold_or_lock = asset::staked::<T>(&stash)
124			.max(T::OldCurrency::balance_locked(STAKING_ID, &stash).into());
125
126		let controller = <Bonded<T>>::get(stash).ok_or_else(|| {
127			if hold_or_lock == Zero::zero() {
128				Error::<T>::NotStash
129			} else {
130				Error::<T>::BadState
131			}
132		})?;
133
134		match Ledger::<T>::get(controller) {
135			Some(ledger) =>
136				if ledger.stash != *stash {
137					Ok(LedgerIntegrityState::Corrupted)
138				} else {
139					if hold_or_lock != ledger.total {
140						Ok(LedgerIntegrityState::LockCorrupted)
141					} else {
142						Ok(LedgerIntegrityState::Ok)
143					}
144				},
145			None => Ok(LedgerIntegrityState::CorruptedKilled),
146		}
147	}
148
149	/// The total balance that can be slashed from a stash account as of right now.
150	pub fn slashable_balance_of(stash: &T::AccountId) -> BalanceOf<T> {
151		// Weight note: consider making the stake accessible through stash.
152		Self::ledger(Stash(stash.clone())).map(|l| l.active).unwrap_or_default()
153	}
154
155	/// Internal impl of [`Self::slashable_balance_of`] that returns [`VoteWeight`].
156	pub fn slashable_balance_of_vote_weight(
157		stash: &T::AccountId,
158		issuance: BalanceOf<T>,
159	) -> VoteWeight {
160		T::CurrencyToVote::to_vote(Self::slashable_balance_of(stash), issuance)
161	}
162
163	/// Returns a closure around `slashable_balance_of_vote_weight` that can be passed around.
164	///
165	/// This prevents call sites from repeatedly requesting `total_issuance` from backend. But it is
166	/// important to be only used while the total issuance is not changing.
167	pub fn weight_of_fn() -> Box<dyn Fn(&T::AccountId) -> VoteWeight> {
168		// NOTE: changing this to unboxed `impl Fn(..)` return type and the pallet will still
169		// compile, while some types in mock fail to resolve.
170		let issuance = asset::total_issuance::<T>();
171		Box::new(move |who: &T::AccountId| -> VoteWeight {
172			Self::slashable_balance_of_vote_weight(who, issuance)
173		})
174	}
175
176	/// Same as `weight_of_fn`, but made for one time use.
177	pub fn weight_of(who: &T::AccountId) -> VoteWeight {
178		let issuance = asset::total_issuance::<T>();
179		Self::slashable_balance_of_vote_weight(who, issuance)
180	}
181
182	pub(super) fn do_bond_extra(stash: &T::AccountId, additional: BalanceOf<T>) -> DispatchResult {
183		let mut ledger = Self::ledger(StakingAccount::Stash(stash.clone()))?;
184
185		// for virtual stakers, we don't need to check the balance. Since they are only accessed
186		// via low level apis, we can assume that the caller has done the due diligence.
187		let extra = if Self::is_virtual_staker(stash) {
188			additional
189		} else {
190			// additional amount or actual balance of stash whichever is lower.
191			additional.min(asset::free_to_stake::<T>(stash))
192		};
193
194		ledger.total = ledger.total.checked_add(&extra).ok_or(ArithmeticError::Overflow)?;
195		ledger.active = ledger.active.checked_add(&extra).ok_or(ArithmeticError::Overflow)?;
196		// last check: the new active amount of ledger must be more than min bond.
197		ensure!(ledger.active >= Self::min_chilled_bond(), Error::<T>::InsufficientBond);
198
199		// NOTE: ledger must be updated prior to calling `Self::weight_of`.
200		ledger.update()?;
201		// update this staker in the sorted list, if they exist in it.
202		if T::VoterList::contains(stash) {
203			// This might fail if the voter list is locked.
204			let _ = T::VoterList::on_update(&stash, Self::weight_of(stash));
205		}
206
207		Self::deposit_event(Event::<T>::Bonded { stash: stash.clone(), amount: extra });
208
209		Ok(())
210	}
211
212	pub(super) fn do_withdraw_unbonded(controller: &T::AccountId) -> Result<Weight, DispatchError> {
213		let mut ledger = Self::ledger(Controller(controller.clone()))?;
214		let (stash, old_total) = (ledger.stash.clone(), ledger.total);
215		if let Some(current_era) = CurrentEra::<T>::get() {
216			ledger = ledger.consolidate_unlocked(current_era)
217		}
218		let new_total = ledger.total;
219
220		let used_weight = if ledger.unlocking.is_empty() &&
221			(ledger.active < Self::min_chilled_bond() || ledger.active.is_zero())
222		{
223			// This account must have called `unbond()` with some value that caused the active
224			// portion to fall below existential deposit + will have no more unlocking chunks
225			// left. We can now safely remove all staking-related information.
226			Self::kill_stash(&ledger.stash)?;
227
228			T::WeightInfo::withdraw_unbonded_kill()
229		} else {
230			// This was the consequence of a partial unbond. just update the ledger and move on.
231			ledger.update()?;
232
233			// This is only an update, so we use less overall weight.
234			T::WeightInfo::withdraw_unbonded_update()
235		};
236
237		// `old_total` should never be less than the new total because
238		// `consolidate_unlocked` strictly subtracts balance.
239		if new_total < old_total {
240			// Already checked that this won't overflow by entry condition.
241			let value = old_total.defensive_saturating_sub(new_total);
242			Self::deposit_event(Event::<T>::Withdrawn { stash, amount: value });
243
244			// notify listeners.
245			T::EventListeners::on_withdraw(controller, value);
246		}
247
248		Ok(used_weight)
249	}
250
251	pub(super) fn do_payout_stakers(
252		validator_stash: T::AccountId,
253		era: EraIndex,
254	) -> DispatchResultWithPostInfo {
255		let page = Eras::<T>::get_next_claimable_page(era, &validator_stash).ok_or_else(|| {
256			Error::<T>::AlreadyClaimed.with_weight(T::WeightInfo::payout_stakers_alive_staked(0))
257		})?;
258
259		Self::do_payout_stakers_by_page(validator_stash, era, page)
260	}
261
262	pub(super) fn do_payout_stakers_by_page(
263		validator_stash: T::AccountId,
264		era: EraIndex,
265		page: Page,
266	) -> DispatchResultWithPostInfo {
267		// Validate input data
268		let current_era = CurrentEra::<T>::get().ok_or_else(|| {
269			Error::<T>::InvalidEraToReward
270				.with_weight(T::WeightInfo::payout_stakers_alive_staked(0))
271		})?;
272
273		let history_depth = T::HistoryDepth::get();
274
275		ensure!(
276			era <= current_era && era >= current_era.saturating_sub(history_depth),
277			Error::<T>::InvalidEraToReward
278				.with_weight(T::WeightInfo::payout_stakers_alive_staked(0))
279		);
280
281		ensure!(
282			page < Eras::<T>::exposure_page_count(era, &validator_stash),
283			Error::<T>::InvalidPage.with_weight(T::WeightInfo::payout_stakers_alive_staked(0))
284		);
285
286		// Note: if era has no reward to be claimed, era may be future.
287		let era_payout = Eras::<T>::get_validators_reward(era).ok_or_else(|| {
288			Error::<T>::InvalidEraToReward
289				.with_weight(T::WeightInfo::payout_stakers_alive_staked(0))
290		})?;
291
292		let account = StakingAccount::Stash(validator_stash.clone());
293		let ledger = Self::ledger(account.clone()).or_else(|_| {
294			if StakingLedger::<T>::is_bonded(account) {
295				Err(Error::<T>::NotController.into())
296			} else {
297				Err(Error::<T>::NotStash.with_weight(T::WeightInfo::payout_stakers_alive_staked(0)))
298			}
299		})?;
300
301		ledger.clone().update()?;
302
303		let stash = ledger.stash.clone();
304
305		if Eras::<T>::is_rewards_claimed(era, &stash, page) {
306			return Err(Error::<T>::AlreadyClaimed
307				.with_weight(T::WeightInfo::payout_stakers_alive_staked(0)))
308		}
309
310		Eras::<T>::set_rewards_as_claimed(era, &stash, page);
311
312		let exposure = Eras::<T>::get_paged_exposure(era, &stash, page).ok_or_else(|| {
313			Error::<T>::InvalidEraToReward
314				.with_weight(T::WeightInfo::payout_stakers_alive_staked(0))
315		})?;
316
317		// Input data seems good, no errors allowed after this point
318
319		// Get Era reward points. It has TOTAL and INDIVIDUAL
320		// Find the fraction of the era reward that belongs to the validator
321		// Take that fraction of the eras rewards to split to nominator and validator
322		//
323		// Then look at the validator, figure out the proportion of their reward
324		// which goes to them and each of their nominators.
325
326		let era_reward_points = Eras::<T>::get_reward_points(era);
327		let total_reward_points = era_reward_points.total;
328		let validator_reward_points =
329			era_reward_points.individual.get(&stash).copied().unwrap_or_else(Zero::zero);
330
331		// Nothing to do if they have no reward points.
332		if validator_reward_points.is_zero() {
333			return Ok(Some(T::WeightInfo::payout_stakers_alive_staked(0)).into())
334		}
335
336		// This is the fraction of the total reward that the validator and the
337		// nominators will get.
338		let validator_total_reward_part =
339			Perbill::from_rational(validator_reward_points, total_reward_points);
340
341		// This is how much validator + nominators are entitled to.
342		let validator_total_payout = validator_total_reward_part * era_payout;
343
344		let validator_commission = Eras::<T>::get_validator_commission(era, &ledger.stash);
345		// total commission validator takes across all nominator pages
346		let validator_total_commission_payout = validator_commission * validator_total_payout;
347
348		let validator_leftover_payout =
349			validator_total_payout.defensive_saturating_sub(validator_total_commission_payout);
350		// Now let's calculate how this is split to the validator.
351		let validator_exposure_part = Perbill::from_rational(exposure.own(), exposure.total());
352		let validator_staking_payout = validator_exposure_part * validator_leftover_payout;
353		let page_stake_part = Perbill::from_rational(exposure.page_total(), exposure.total());
354		// validator commission is paid out in fraction across pages proportional to the page stake.
355		let validator_commission_payout = page_stake_part * validator_total_commission_payout;
356
357		Self::deposit_event(Event::<T>::PayoutStarted {
358			era_index: era,
359			validator_stash: stash.clone(),
360			page,
361			next: Eras::<T>::get_next_claimable_page(era, &stash),
362		});
363
364		let mut total_imbalance = PositiveImbalanceOf::<T>::zero();
365		// We can now make total validator payout:
366		if let Some((imbalance, dest)) =
367			Self::make_payout(&stash, validator_staking_payout + validator_commission_payout)
368		{
369			Self::deposit_event(Event::<T>::Rewarded { stash, dest, amount: imbalance.peek() });
370			total_imbalance.subsume(imbalance);
371		}
372
373		// Track the number of payout ops to nominators. Note:
374		// `WeightInfo::payout_stakers_alive_staked` always assumes at least a validator is paid
375		// out, so we do not need to count their payout op.
376		let mut nominator_payout_count: u32 = 0;
377
378		// Lets now calculate how this is split to the nominators.
379		// Reward only the clipped exposures. Note this is not necessarily sorted.
380		for nominator in exposure.others().iter() {
381			let nominator_exposure_part = Perbill::from_rational(nominator.value, exposure.total());
382
383			let nominator_reward: BalanceOf<T> =
384				nominator_exposure_part * validator_leftover_payout;
385			// We can now make nominator payout:
386			if let Some((imbalance, dest)) = Self::make_payout(&nominator.who, nominator_reward) {
387				// Note: this logic does not count payouts for `RewardDestination::None`.
388				nominator_payout_count += 1;
389				let e = Event::<T>::Rewarded {
390					stash: nominator.who.clone(),
391					dest,
392					amount: imbalance.peek(),
393				};
394				Self::deposit_event(e);
395				total_imbalance.subsume(imbalance);
396			}
397		}
398
399		T::Reward::on_unbalanced(total_imbalance);
400		debug_assert!(nominator_payout_count <= T::MaxExposurePageSize::get());
401
402		Ok(Some(T::WeightInfo::payout_stakers_alive_staked(nominator_payout_count)).into())
403	}
404
405	/// Chill a stash account.
406	pub(crate) fn chill_stash(stash: &T::AccountId) {
407		let chilled_as_validator = Self::do_remove_validator(stash);
408		let chilled_as_nominator = Self::do_remove_nominator(stash);
409		if chilled_as_validator || chilled_as_nominator {
410			Self::deposit_event(Event::<T>::Chilled { stash: stash.clone() });
411		}
412	}
413
414	/// Actually make a payment to a staker. This uses the currency's reward function
415	/// to pay the right payee for the given staker account.
416	fn make_payout(
417		stash: &T::AccountId,
418		amount: BalanceOf<T>,
419	) -> Option<(PositiveImbalanceOf<T>, RewardDestination<T::AccountId>)> {
420		// noop if amount is zero
421		if amount.is_zero() {
422			return None
423		}
424		let dest = Self::payee(StakingAccount::Stash(stash.clone()))?;
425
426		let maybe_imbalance = match dest {
427			RewardDestination::Stash => asset::mint_into_existing::<T>(stash, amount),
428			RewardDestination::Staked => Self::ledger(Stash(stash.clone()))
429				.and_then(|mut ledger| {
430					ledger.active += amount;
431					ledger.total += amount;
432					let r = asset::mint_into_existing::<T>(stash, amount);
433
434					let _ = ledger
435						.update()
436						.defensive_proof("ledger fetched from storage, so it exists; qed.");
437
438					Ok(r)
439				})
440				.unwrap_or_default(),
441			RewardDestination::Account(ref dest_account) =>
442				Some(asset::mint_creating::<T>(&dest_account, amount)),
443			RewardDestination::None => None,
444			#[allow(deprecated)]
445			RewardDestination::Controller => Self::bonded(stash)
446					.map(|controller| {
447						defensive!("Paying out controller as reward destination which is deprecated and should be migrated.");
448						// This should never happen once payees with a `Controller` variant have been migrated.
449						// But if it does, just pay the controller account.
450						asset::mint_creating::<T>(&controller, amount)
451		}),
452		};
453		maybe_imbalance.map(|imbalance| (imbalance, dest))
454	}
455
456	/// Remove all associated data of a stash account from the staking system.
457	///
458	/// Assumes storage is upgraded before calling.
459	///
460	/// This is called:
461	/// - after a `withdraw_unbonded()` call that frees all of a stash's bonded balance.
462	/// - through `reap_stash()` if the balance has fallen to zero (through slashing).
463	pub(crate) fn kill_stash(stash: &T::AccountId) -> DispatchResult {
464		// removes controller from `Bonded` and staking ledger from `Ledger`, as well as reward
465		// setting of the stash in `Payee`.
466		StakingLedger::<T>::kill(&stash)?;
467
468		Self::do_remove_validator(&stash);
469		Self::do_remove_nominator(&stash);
470
471		Ok(())
472	}
473
474	#[cfg(test)]
475	pub(crate) fn reward_by_ids(validators_points: impl IntoIterator<Item = (T::AccountId, u32)>) {
476		Eras::<T>::reward_active_era(validators_points)
477	}
478
479	/// Helper to set a new `ForceEra` mode.
480	pub(crate) fn set_force_era(mode: Forcing) {
481		log!(info, "Setting force era mode {:?}.", mode);
482		ForceEra::<T>::put(mode);
483		Self::deposit_event(Event::<T>::ForceEra { mode });
484	}
485
486	#[cfg(feature = "runtime-benchmarks")]
487	pub fn add_era_stakers(
488		current_era: EraIndex,
489		stash: T::AccountId,
490		exposure: Exposure<T::AccountId, BalanceOf<T>>,
491	) {
492		Eras::<T>::upsert_exposure(current_era, &stash, exposure);
493	}
494
495	#[cfg(feature = "runtime-benchmarks")]
496	pub fn set_slash_reward_fraction(fraction: Perbill) {
497		SlashRewardFraction::<T>::put(fraction);
498	}
499
500	/// Get all the voters associated with `page` that are eligible for the npos election.
501	///
502	/// `bounds` can impose a cap on the number of voters returned per page.
503	///
504	/// Sets `MinimumActiveStake` to the minimum active nominator stake in the returned set of
505	/// nominators.
506	///
507	/// Note: in the context of the multi-page snapshot, we expect the *order* of `VoterList` and
508	/// `TargetList` not to change while the pages are being processed.
509	pub(crate) fn get_npos_voters(
510		bounds: DataProviderBounds,
511		status: &SnapshotStatus<T::AccountId>,
512	) -> Vec<VoterOf<Self>> {
513		let mut voters_size_tracker: StaticTracker<Self> = StaticTracker::default();
514
515		let page_len_prediction = {
516			let all_voter_count = T::VoterList::count();
517			bounds.count.unwrap_or(all_voter_count.into()).min(all_voter_count.into()).0
518		};
519
520		let mut all_voters = Vec::<_>::with_capacity(page_len_prediction as usize);
521
522		// cache a few things.
523		let weight_of = Self::weight_of_fn();
524
525		let mut voters_seen = 0u32;
526		let mut validators_taken = 0u32;
527		let mut nominators_taken = 0u32;
528		let mut min_active_stake = u64::MAX;
529
530		let mut sorted_voters = match status {
531			// start the snapshot processing from the beginning.
532			SnapshotStatus::Waiting => T::VoterList::iter(),
533			// snapshot continues, start from the last iterated voter in the list.
534			SnapshotStatus::Ongoing(account_id) => T::VoterList::iter_from(&account_id)
535				.defensive_unwrap_or(Box::new(vec![].into_iter())),
536			// all voters have been consumed already, return an empty iterator.
537			SnapshotStatus::Consumed => Box::new(vec![].into_iter()),
538		};
539
540		while all_voters.len() < page_len_prediction as usize &&
541			voters_seen < (NPOS_MAX_ITERATIONS_COEFFICIENT * page_len_prediction as u32)
542		{
543			let voter = match sorted_voters.next() {
544				Some(voter) => {
545					voters_seen.saturating_inc();
546					voter
547				},
548				None => break,
549			};
550
551			let voter_weight = weight_of(&voter);
552			// if voter weight is zero, do not consider this voter for the snapshot.
553			if voter_weight.is_zero() {
554				log!(debug, "voter's active balance is 0. skip this voter.");
555				continue
556			}
557
558			if let Some(Nominations { targets, .. }) = <Nominators<T>>::get(&voter) {
559				if !targets.is_empty() {
560					// Note on lazy nomination quota: we do not check the nomination quota of the
561					// voter at this point and accept all the current nominations. The nomination
562					// quota is only enforced at `nominate` time.
563
564					let voter = (voter, voter_weight, targets);
565					if voters_size_tracker.try_register_voter(&voter, &bounds).is_err() {
566						// no more space left for the election result, stop iterating.
567						Self::deposit_event(Event::<T>::SnapshotVotersSizeExceeded {
568							size: voters_size_tracker.size as u32,
569						});
570						break
571					}
572
573					all_voters.push(voter);
574					nominators_taken.saturating_inc();
575				} else {
576					defensive!("non-nominator fetched from voter list: {:?}", voter);
577					// technically should never happen, but not much we can do about it.
578				}
579				min_active_stake =
580					if voter_weight < min_active_stake { voter_weight } else { min_active_stake };
581			} else if Validators::<T>::contains_key(&voter) {
582				// if this voter is a validator:
583				let self_vote = (
584					voter.clone(),
585					voter_weight,
586					vec![voter.clone()]
587						.try_into()
588						.expect("`MaxVotesPerVoter` must be greater than or equal to 1"),
589				);
590
591				if voters_size_tracker.try_register_voter(&self_vote, &bounds).is_err() {
592					// no more space left for the election snapshot, stop iterating.
593					Self::deposit_event(Event::<T>::SnapshotVotersSizeExceeded {
594						size: voters_size_tracker.size as u32,
595					});
596					break
597				}
598				all_voters.push(self_vote);
599				validators_taken.saturating_inc();
600			} else {
601				// this can only happen if: 1. there a bug in the bags-list (or whatever is the
602				// sorted list) logic and the state of the two pallets is no longer compatible, or
603				// because the nominators is not decodable since they have more nomination than
604				// `T::NominationsQuota::get_quota`. The latter can rarely happen, and is not
605				// really an emergency or bug if it does.
606				defensive!(
607				    "invalid item in `VoterList`: {:?}, this nominator probably has too many nominations now",
608                    voter,
609                );
610			}
611		}
612
613		// all_voters should have not re-allocated.
614		debug_assert!(all_voters.capacity() == page_len_prediction as usize);
615
616		let min_active_stake: T::CurrencyBalance =
617			if all_voters.is_empty() { Zero::zero() } else { min_active_stake.into() };
618
619		MinimumActiveStake::<T>::put(min_active_stake);
620
621		all_voters
622	}
623
624	/// Get all the targets associated are eligible for the npos election.
625	///
626	/// The target snapshot is *always* single paged.
627	///
628	/// This function is self-weighing as [`DispatchClass::Mandatory`].
629	pub fn get_npos_targets(bounds: DataProviderBounds) -> Vec<T::AccountId> {
630		let mut targets_size_tracker: StaticTracker<Self> = StaticTracker::default();
631
632		let final_predicted_len = {
633			let all_target_count = T::TargetList::count();
634			bounds.count.unwrap_or(all_target_count.into()).min(all_target_count.into()).0
635		};
636
637		let mut all_targets = Vec::<T::AccountId>::with_capacity(final_predicted_len as usize);
638		let mut targets_seen = 0;
639
640		let mut targets_iter = T::TargetList::iter();
641		while all_targets.len() < final_predicted_len as usize &&
642			targets_seen < (NPOS_MAX_ITERATIONS_COEFFICIENT * final_predicted_len as u32)
643		{
644			let target = match targets_iter.next() {
645				Some(target) => {
646					targets_seen.saturating_inc();
647					target
648				},
649				None => break,
650			};
651
652			if targets_size_tracker.try_register_target(target.clone(), &bounds).is_err() {
653				// no more space left for the election snapshot, stop iterating.
654				log!(warn, "npos targets size exceeded, stopping iteration.");
655				Self::deposit_event(Event::<T>::SnapshotTargetsSizeExceeded {
656					size: targets_size_tracker.size as u32,
657				});
658				break
659			}
660
661			if Validators::<T>::contains_key(&target) {
662				all_targets.push(target);
663			}
664		}
665
666		log!(debug, "[bounds {:?}] generated {} npos targets", bounds, all_targets.len());
667
668		all_targets
669	}
670
671	/// This function will add a nominator to the `Nominators` storage map,
672	/// and `VoterList`.
673	///
674	/// If the nominator already exists, their nominations will be updated.
675	///
676	/// NOTE: you must ALWAYS use this function to add nominator or update their targets. Any access
677	/// to `Nominators` or `VoterList` outside of this function is almost certainly
678	/// wrong.
679	pub fn do_add_nominator(who: &T::AccountId, nominations: Nominations<T>) {
680		if !Nominators::<T>::contains_key(who) {
681			// maybe update sorted list.
682			let _ = T::VoterList::on_insert(who.clone(), Self::weight_of(who))
683				.defensive_unwrap_or_default();
684		}
685		Nominators::<T>::insert(who, nominations);
686
687		debug_assert_eq!(
688			Nominators::<T>::count() + Validators::<T>::count(),
689			T::VoterList::count()
690		);
691	}
692
693	/// This function will remove a nominator from the `Nominators` storage map,
694	/// and `VoterList`.
695	///
696	/// Returns true if `who` was removed from `Nominators`, otherwise false.
697	///
698	/// NOTE: you must ALWAYS use this function to remove a nominator from the system. Any access to
699	/// `Nominators` or `VoterList` outside of this function is almost certainly
700	/// wrong.
701	pub fn do_remove_nominator(who: &T::AccountId) -> bool {
702		let outcome = if Nominators::<T>::contains_key(who) {
703			Nominators::<T>::remove(who);
704			let _ = T::VoterList::on_remove(who);
705			true
706		} else {
707			false
708		};
709
710		debug_assert_eq!(
711			Nominators::<T>::count() + Validators::<T>::count(),
712			T::VoterList::count()
713		);
714
715		outcome
716	}
717
718	/// This function will add a validator to the `Validators` storage map.
719	///
720	/// If the validator already exists, their preferences will be updated.
721	///
722	/// NOTE: you must ALWAYS use this function to add a validator to the system. Any access to
723	/// `Validators` or `VoterList` outside of this function is almost certainly
724	/// wrong.
725	pub fn do_add_validator(who: &T::AccountId, prefs: ValidatorPrefs) {
726		if !Validators::<T>::contains_key(who) {
727			// maybe update sorted list.
728			let _ = T::VoterList::on_insert(who.clone(), Self::weight_of(who));
729		}
730		Validators::<T>::insert(who, prefs);
731
732		debug_assert_eq!(
733			Nominators::<T>::count() + Validators::<T>::count(),
734			T::VoterList::count()
735		);
736	}
737
738	/// This function will remove a validator from the `Validators` storage map.
739	///
740	/// Returns true if `who` was removed from `Validators`, otherwise false.
741	///
742	/// NOTE: you must ALWAYS use this function to remove a validator from the system. Any access to
743	/// `Validators` or `VoterList` outside of this function is almost certainly
744	/// wrong.
745	pub fn do_remove_validator(who: &T::AccountId) -> bool {
746		let outcome = if Validators::<T>::contains_key(who) {
747			Validators::<T>::remove(who);
748			let _ = T::VoterList::on_remove(who);
749			true
750		} else {
751			false
752		};
753
754		debug_assert_eq!(
755			Nominators::<T>::count() + Validators::<T>::count(),
756			T::VoterList::count()
757		);
758
759		outcome
760	}
761
762	/// Register some amount of weight directly with the system pallet.
763	///
764	/// This is always mandatory weight.
765	pub(crate) fn register_weight(weight: Weight) {
766		<frame_system::Pallet<T>>::register_extra_weight_unchecked(
767			weight,
768			DispatchClass::Mandatory,
769		);
770	}
771
772	/// Returns full exposure of a validator for a given era.
773	///
774	/// History note: This used to be a getter for old storage item `ErasStakers` deprecated in v14
775	/// and deleted in v17. Since this function is used in the codebase at various places, we kept
776	/// it as a custom getter that takes care of getting the full exposure of the validator in a
777	/// backward compatible way.
778	pub fn eras_stakers(
779		era: EraIndex,
780		account: &T::AccountId,
781	) -> Exposure<T::AccountId, BalanceOf<T>> {
782		Eras::<T>::get_full_exposure(era, account)
783	}
784
785	pub(super) fn do_migrate_currency(stash: &T::AccountId) -> DispatchResult {
786		if Self::is_virtual_staker(stash) {
787			return Self::do_migrate_virtual_staker(stash);
788		}
789
790		let ledger = Self::ledger(Stash(stash.clone()))?;
791		let staked: BalanceOf<T> = T::OldCurrency::balance_locked(STAKING_ID, stash).into();
792		ensure!(!staked.is_zero(), Error::<T>::AlreadyMigrated);
793		ensure!(ledger.total == staked, Error::<T>::BadState);
794
795		// remove old staking lock
796		T::OldCurrency::remove_lock(STAKING_ID, &stash);
797
798		// check if we can hold all stake.
799		let max_hold = asset::free_to_stake::<T>(&stash);
800		let force_withdraw = if max_hold >= staked {
801			// this means we can hold all stake. yay!
802			asset::update_stake::<T>(&stash, staked)?;
803			Zero::zero()
804		} else {
805			// if we are here, it means we cannot hold all user stake. We will do a force withdraw
806			// from ledger, but that's okay since anyways user do not have funds for it.
807			let force_withdraw = staked.saturating_sub(max_hold);
808
809			// we ignore if active is 0. It implies the locked amount is not actively staked. The
810			// account can still get away from potential slash but we can't do much better here.
811			StakingLedger {
812				total: max_hold,
813				active: ledger.active.saturating_sub(force_withdraw),
814				// we are not changing the stash, so we can keep the stash.
815				..ledger
816			}
817			.update()?;
818			force_withdraw
819		};
820
821		// Get rid of the extra consumer we used to have with OldCurrency.
822		frame_system::Pallet::<T>::dec_consumers(&stash);
823
824		Self::deposit_event(Event::<T>::CurrencyMigrated { stash: stash.clone(), force_withdraw });
825		Ok(())
826	}
827
828	fn do_migrate_virtual_staker(stash: &T::AccountId) -> DispatchResult {
829		// Funds for virtual stakers not managed/held by this pallet. We only need to clear
830		// the extra consumer we used to have with OldCurrency.
831		frame_system::Pallet::<T>::dec_consumers(&stash);
832
833		// The delegation system that manages the virtual staker needed to increment provider
834		// previously because of the consumer needed by this pallet. In reality, this stash
835		// is just a key for managing the ledger and the account does not need to hold any
836		// balance or exist. We decrement this provider.
837		let actual_providers = frame_system::Pallet::<T>::providers(stash);
838
839		let expected_providers =
840			// provider is expected to be 1 but someone can always transfer some free funds to
841			// these accounts, increasing the provider.
842			if asset::free_to_stake::<T>(&stash) >= asset::existential_deposit::<T>() {
843				2
844			} else {
845				1
846			};
847
848		// We should never have more than expected providers.
849		ensure!(actual_providers <= expected_providers, Error::<T>::BadState);
850
851		// if actual provider is less than expected, it is already migrated.
852		ensure!(actual_providers == expected_providers, Error::<T>::AlreadyMigrated);
853
854		// dec provider
855		let _ = frame_system::Pallet::<T>::dec_providers(&stash)?;
856
857		return Ok(())
858	}
859}
860
861impl<T: Config> Pallet<T> {
862	/// Returns the current nominations quota for nominators.
863	///
864	/// Used by the runtime API.
865	pub fn api_nominations_quota(balance: BalanceOf<T>) -> u32 {
866		T::NominationsQuota::get_quota(balance)
867	}
868
869	pub fn api_eras_stakers(
870		era: EraIndex,
871		account: T::AccountId,
872	) -> Exposure<T::AccountId, BalanceOf<T>> {
873		Self::eras_stakers(era, &account)
874	}
875
876	pub fn api_eras_stakers_page_count(era: EraIndex, account: T::AccountId) -> Page {
877		Eras::<T>::exposure_page_count(era, &account)
878	}
879
880	pub fn api_pending_rewards(era: EraIndex, account: T::AccountId) -> bool {
881		Eras::<T>::pending_rewards(era, &account)
882	}
883}
884
885impl<T: Config> ElectionDataProvider for Pallet<T> {
886	type AccountId = T::AccountId;
887	type BlockNumber = BlockNumberFor<T>;
888	type MaxVotesPerVoter = MaxNominationsOf<T>;
889
890	fn desired_targets() -> data_provider::Result<u32> {
891		Self::register_weight(T::DbWeight::get().reads(1));
892		Ok(ValidatorCount::<T>::get())
893	}
894
895	fn electing_voters(
896		bounds: DataProviderBounds,
897		page: PageIndex,
898	) -> data_provider::Result<Vec<VoterOf<Self>>> {
899		let mut status = VoterSnapshotStatus::<T>::get();
900		let voters = Self::get_npos_voters(bounds, &status);
901
902		// update the voter snapshot status.
903		match (page, &status) {
904			// last page, reset status for next round.
905			(0, _) => status = SnapshotStatus::Waiting,
906
907			(_, SnapshotStatus::Waiting) | (_, SnapshotStatus::Ongoing(_)) => {
908				let maybe_last = voters.last().map(|(x, _, _)| x).cloned();
909
910				if let Some(ref last) = maybe_last {
911					let has_next =
912						T::VoterList::iter_from(last).ok().and_then(|mut i| i.next()).is_some();
913					if has_next {
914						status = SnapshotStatus::Ongoing(last.clone());
915					} else {
916						status = SnapshotStatus::Consumed;
917					}
918				}
919			},
920			// do nothing.
921			(_, SnapshotStatus::Consumed) => (),
922		}
923
924		log!(
925			debug,
926			"[page {}, (next) status {:?}, bounds {:?}] generated {} npos voters",
927			page,
928			status,
929			bounds,
930			voters.len(),
931		);
932
933		match status {
934			SnapshotStatus::Ongoing(_) => T::VoterList::lock(),
935			_ => T::VoterList::unlock(),
936		}
937
938		VoterSnapshotStatus::<T>::put(status);
939		debug_assert!(!bounds.slice_exhausted(&voters));
940
941		Ok(voters)
942	}
943
944	fn electing_voters_stateless(
945		bounds: DataProviderBounds,
946	) -> data_provider::Result<Vec<VoterOf<Self>>> {
947		let voters = Self::get_npos_voters(bounds, &SnapshotStatus::Waiting);
948		log!(debug, "[stateless, bounds {:?}] generated {} npos voters", bounds, voters.len(),);
949		Ok(voters)
950	}
951
952	fn electable_targets(
953		bounds: DataProviderBounds,
954		page: PageIndex,
955	) -> data_provider::Result<Vec<T::AccountId>> {
956		if page > 0 {
957			log!(warn, "multi-page target snapshot not supported, returning page 0.");
958		}
959
960		let targets = Self::get_npos_targets(bounds);
961		// We can't handle this case yet -- return an error. WIP to improve handling this case in
962		// <https://github.com/paritytech/substrate/pull/13195>.
963		if bounds.exhausted(None, CountBound(targets.len() as u32).into()) {
964			return Err("Target snapshot too big")
965		}
966
967		debug_assert!(!bounds.slice_exhausted(&targets));
968
969		Ok(targets)
970	}
971
972	fn next_election_prediction(_: BlockNumberFor<T>) -> BlockNumberFor<T> {
973		debug_assert!(false, "this is deprecated and not used anymore");
974		sp_runtime::traits::Bounded::max_value()
975	}
976
977	#[cfg(feature = "runtime-benchmarks")]
978	fn fetch_page(page: PageIndex) {
979		session_rotation::EraElectionPlanner::<T>::do_elect_paged(page);
980	}
981
982	#[cfg(feature = "runtime-benchmarks")]
983	fn add_voter(
984		voter: T::AccountId,
985		weight: VoteWeight,
986		targets: BoundedVec<T::AccountId, Self::MaxVotesPerVoter>,
987	) {
988		let stake = <BalanceOf<T>>::try_from(weight).unwrap_or_else(|_| {
989			panic!("cannot convert a VoteWeight into BalanceOf, benchmark needs reconfiguring.")
990		});
991		<Bonded<T>>::insert(voter.clone(), voter.clone());
992		<Ledger<T>>::insert(voter.clone(), StakingLedger::<T>::new(voter.clone(), stake));
993
994		Self::do_add_nominator(&voter, Nominations { targets, submitted_in: 0, suppressed: false });
995	}
996
997	#[cfg(feature = "runtime-benchmarks")]
998	fn add_target(target: T::AccountId) {
999		let stake = (Self::min_validator_bond() + 1u32.into()) * 100u32.into();
1000		<Bonded<T>>::insert(target.clone(), target.clone());
1001		<Ledger<T>>::insert(target.clone(), StakingLedger::<T>::new(target.clone(), stake));
1002		Self::do_add_validator(
1003			&target,
1004			ValidatorPrefs { commission: Perbill::zero(), blocked: false },
1005		);
1006	}
1007
1008	#[cfg(feature = "runtime-benchmarks")]
1009	fn clear() {
1010		#[allow(deprecated)]
1011		<Bonded<T>>::remove_all(None);
1012		#[allow(deprecated)]
1013		<Ledger<T>>::remove_all(None);
1014		#[allow(deprecated)]
1015		<Validators<T>>::remove_all();
1016		#[allow(deprecated)]
1017		<Nominators<T>>::remove_all();
1018
1019		T::VoterList::unsafe_clear();
1020	}
1021
1022	#[cfg(feature = "runtime-benchmarks")]
1023	fn put_snapshot(
1024		voters: Vec<VoterOf<Self>>,
1025		targets: Vec<T::AccountId>,
1026		target_stake: Option<VoteWeight>,
1027	) {
1028		targets.into_iter().for_each(|v| {
1029			let stake: BalanceOf<T> = target_stake
1030				.and_then(|w| <BalanceOf<T>>::try_from(w).ok())
1031				.unwrap_or_else(|| Self::min_nominator_bond() * 100u32.into());
1032			<Bonded<T>>::insert(v.clone(), v.clone());
1033			<Ledger<T>>::insert(v.clone(), StakingLedger::<T>::new(v.clone(), stake));
1034			Self::do_add_validator(
1035				&v,
1036				ValidatorPrefs { commission: Perbill::zero(), blocked: false },
1037			);
1038		});
1039
1040		voters.into_iter().for_each(|(v, s, t)| {
1041			let stake = <BalanceOf<T>>::try_from(s).unwrap_or_else(|_| {
1042				panic!("cannot convert a VoteWeight into BalanceOf, benchmark needs reconfiguring.")
1043			});
1044			<Bonded<T>>::insert(v.clone(), v.clone());
1045			<Ledger<T>>::insert(v.clone(), StakingLedger::<T>::new(v.clone(), stake));
1046			Self::do_add_nominator(
1047				&v,
1048				Nominations { targets: t, submitted_in: 0, suppressed: false },
1049			);
1050		});
1051	}
1052
1053	#[cfg(feature = "runtime-benchmarks")]
1054	fn set_desired_targets(count: u32) {
1055		ValidatorCount::<T>::put(count);
1056	}
1057}
1058
1059impl<T: Config> rc_client::AHStakingInterface for Pallet<T> {
1060	type AccountId = T::AccountId;
1061	type MaxValidatorSet = T::MaxValidatorSet;
1062
1063	/// When we receive a session report from the relay chain, it kicks off the next session.
1064	///
1065	/// There are three special types of things we can do in a session:
1066	/// 1. Plan a new era: We do this one session before the expected era rotation.
1067	/// 2. Kick off election: We do this based on the [`Config::PlanningEraOffset`] configuration.
1068	/// 3. Activate Next Era: When we receive an activation timestamp in the session report, it
1069	/// implies a new validator set has been applied, and we must increment the active era to keep
1070	/// the systems in sync.
1071	fn on_relay_session_report(report: rc_client::SessionReport<Self::AccountId>) {
1072		log!(debug, "Received session report: {}", report,);
1073		let consumed_weight = T::WeightInfo::rc_on_session_report();
1074
1075		let rc_client::SessionReport {
1076			end_index,
1077			activation_timestamp,
1078			validator_points,
1079			leftover,
1080		} = report;
1081		debug_assert!(!leftover);
1082
1083		Eras::<T>::reward_active_era(validator_points.into_iter());
1084		session_rotation::Rotator::<T>::end_session(end_index, activation_timestamp);
1085		// NOTE: we might want to either return these weights so that they are registered in the
1086		// rc-client pallet, or directly benchmarked there, such that we can use them in the
1087		// "pre-dispatch" fashion. That said, since these are all `Mandatory` weights, it doesn't
1088		// make that big of a difference.
1089		Self::register_weight(consumed_weight);
1090	}
1091
1092	fn on_new_offences(
1093		slash_session: SessionIndex,
1094		offences: Vec<rc_client::Offence<T::AccountId>>,
1095	) {
1096		log!(debug, "๐Ÿฆน on_new_offences: {:?}", offences);
1097		let consumed_weight = T::WeightInfo::rc_on_offence(offences.len() as u32);
1098
1099		// Find the era to which offence belongs.
1100		let Some(active_era) = ActiveEra::<T>::get() else {
1101			log!(warn, "๐Ÿฆน on_new_offences: no active era; ignoring offence");
1102			return
1103		};
1104
1105		let active_era_start_session = Rotator::<T>::active_era_start_session_index();
1106
1107		// Fast path for active-era report - most likely.
1108		// `slash_session` cannot be in a future active era. It must be in `active_era` or before.
1109		let offence_era = if slash_session >= active_era_start_session {
1110			active_era.index
1111		} else {
1112			match BondedEras::<T>::get()
1113				.iter()
1114				// Reverse because it's more likely to find reports from recent eras.
1115				.rev()
1116				.find_map(|&(era, sesh)| if sesh <= slash_session { Some(era) } else { None })
1117			{
1118				Some(era) => era,
1119				None => {
1120					// defensive: this implies offence is for a discarded era, and should already be
1121					// filtered out.
1122					log!(warn, "๐Ÿฆน on_offence: no era found for slash_session; ignoring offence");
1123					return
1124				},
1125			}
1126		};
1127
1128		let invulnerables = Invulnerables::<T>::get();
1129
1130		for o in offences {
1131			let slash_fraction = o.slash_fraction;
1132			let validator: <T as frame_system::Config>::AccountId = o.offender.into();
1133			// Skip if the validator is invulnerable.
1134			if invulnerables.contains(&validator) {
1135				log!(debug, "๐Ÿฆน on_offence: {:?} is invulnerable; ignoring offence", validator);
1136				continue
1137			}
1138
1139			let Some(exposure_overview) = <ErasStakersOverview<T>>::get(&offence_era, &validator)
1140			else {
1141				// defensive: this implies offence is for a discarded era, and should already be
1142				// filtered out.
1143				log!(
1144					warn,
1145					"๐Ÿฆน on_offence: no exposure found for {:?} in era {}; ignoring offence",
1146					validator,
1147					offence_era
1148				);
1149				continue;
1150			};
1151
1152			Self::deposit_event(Event::<T>::OffenceReported {
1153				validator: validator.clone(),
1154				fraction: slash_fraction,
1155				offence_era,
1156			});
1157
1158			let prior_slash_fraction = ValidatorSlashInEra::<T>::get(offence_era, &validator)
1159				.map_or(Zero::zero(), |(f, _)| f);
1160
1161			if let Some(existing) = OffenceQueue::<T>::get(offence_era, &validator) {
1162				if slash_fraction.deconstruct() > existing.slash_fraction.deconstruct() {
1163					OffenceQueue::<T>::insert(
1164						offence_era,
1165						&validator,
1166						OffenceRecord {
1167							reporter: o.reporters.first().cloned(),
1168							reported_era: active_era.index,
1169							slash_fraction,
1170							..existing
1171						},
1172					);
1173
1174					// update the slash fraction in the `ValidatorSlashInEra` storage.
1175					ValidatorSlashInEra::<T>::insert(
1176						offence_era,
1177						&validator,
1178						(slash_fraction, exposure_overview.own),
1179					);
1180
1181					log!(
1182						debug,
1183						"๐Ÿฆน updated slash for {:?}: {:?} (prior: {:?})",
1184						validator,
1185						slash_fraction,
1186						prior_slash_fraction,
1187					);
1188				} else {
1189					log!(
1190						debug,
1191						"๐Ÿฆน ignored slash for {:?}: {:?} (existing prior is larger: {:?})",
1192						validator,
1193						slash_fraction,
1194						prior_slash_fraction,
1195					);
1196				}
1197			} else if slash_fraction.deconstruct() > prior_slash_fraction.deconstruct() {
1198				ValidatorSlashInEra::<T>::insert(
1199					offence_era,
1200					&validator,
1201					(slash_fraction, exposure_overview.own),
1202				);
1203
1204				OffenceQueue::<T>::insert(
1205					offence_era,
1206					&validator,
1207					OffenceRecord {
1208						reporter: o.reporters.first().cloned(),
1209						reported_era: active_era.index,
1210						// there are cases of validator with no exposure, hence 0 page, so we
1211						// saturate to avoid underflow.
1212						exposure_page: exposure_overview.page_count.saturating_sub(1),
1213						slash_fraction,
1214						prior_slash_fraction,
1215					},
1216				);
1217
1218				OffenceQueueEras::<T>::mutate(|q| {
1219					if let Some(eras) = q {
1220						log!(debug, "๐Ÿฆน inserting offence era {} into existing queue", offence_era);
1221						eras.binary_search(&offence_era).err().map(|idx| {
1222							eras.try_insert(idx, offence_era).defensive_proof(
1223								"Offence era must be present in the existing queue",
1224							)
1225						});
1226					} else {
1227						let mut eras = BoundedVec::default();
1228						log!(debug, "๐Ÿฆน inserting offence era {} into empty queue", offence_era);
1229						let _ = eras
1230							.try_push(offence_era)
1231							.defensive_proof("Failed to push offence era into empty queue");
1232						*q = Some(eras);
1233					}
1234				});
1235
1236				log!(
1237					debug,
1238					"๐Ÿฆน queued slash for {:?}: {:?} (prior: {:?})",
1239					validator,
1240					slash_fraction,
1241					prior_slash_fraction,
1242				);
1243			} else {
1244				log!(
1245					debug,
1246					"๐Ÿฆน ignored slash for {:?}: {:?} (already slashed in era with prior: {:?})",
1247					validator,
1248					slash_fraction,
1249					prior_slash_fraction,
1250				);
1251			}
1252		}
1253
1254		Self::register_weight(consumed_weight);
1255	}
1256}
1257
1258impl<T: Config> ScoreProvider<T::AccountId> for Pallet<T> {
1259	type Score = VoteWeight;
1260
1261	fn score(who: &T::AccountId) -> Option<Self::Score> {
1262		Self::ledger(Stash(who.clone()))
1263			.map(|l| l.active)
1264			.map(|a| {
1265				let issuance = asset::total_issuance::<T>();
1266				T::CurrencyToVote::to_vote(a, issuance)
1267			})
1268			.ok()
1269	}
1270
1271	#[cfg(feature = "runtime-benchmarks")]
1272	fn set_score_of(who: &T::AccountId, weight: Self::Score) {
1273		// this will clearly results in an inconsistent state, but it should not matter for a
1274		// benchmark.
1275		let active: BalanceOf<T> = weight.try_into().map_err(|_| ()).unwrap();
1276		let mut ledger = match Self::ledger(StakingAccount::Stash(who.clone())) {
1277			Ok(l) => l,
1278			Err(_) => StakingLedger::default_from(who.clone()),
1279		};
1280		ledger.active = active;
1281
1282		<Ledger<T>>::insert(who, ledger);
1283		<Bonded<T>>::insert(who, who);
1284
1285		// also, we play a trick to make sure that a issuance based-`CurrencyToVote` behaves well:
1286		// This will make sure that total issuance is zero, thus the currency to vote will be a 1-1
1287		// conversion.
1288		let imbalance = asset::burn::<T>(asset::total_issuance::<T>());
1289		// kinda ugly, but gets the job done. The fact that this works here is a HUGE exception.
1290		// Don't try this pattern in other places.
1291		core::mem::forget(imbalance);
1292	}
1293}
1294
1295/// A simple sorted list implementation that does not require any additional pallets. Note, this
1296/// does not provide validators in sorted order. If you desire nominators in a sorted order take
1297/// a look at [`pallet-bags-list`].
1298pub struct UseValidatorsMap<T>(core::marker::PhantomData<T>);
1299impl<T: Config> SortedListProvider<T::AccountId> for UseValidatorsMap<T> {
1300	type Score = BalanceOf<T>;
1301	type Error = ();
1302
1303	/// Returns iterator over voter list, which can have `take` called on it.
1304	fn iter() -> Box<dyn Iterator<Item = T::AccountId>> {
1305		Box::new(Validators::<T>::iter().map(|(v, _)| v))
1306	}
1307	fn iter_from(
1308		start: &T::AccountId,
1309	) -> Result<Box<dyn Iterator<Item = T::AccountId>>, Self::Error> {
1310		if Validators::<T>::contains_key(start) {
1311			let start_key = Validators::<T>::hashed_key_for(start);
1312			Ok(Box::new(Validators::<T>::iter_from(start_key).map(|(n, _)| n)))
1313		} else {
1314			Err(())
1315		}
1316	}
1317	fn lock() {}
1318	fn unlock() {}
1319	fn count() -> u32 {
1320		Validators::<T>::count()
1321	}
1322	fn contains(id: &T::AccountId) -> bool {
1323		Validators::<T>::contains_key(id)
1324	}
1325	fn on_insert(_: T::AccountId, _weight: Self::Score) -> Result<(), Self::Error> {
1326		// nothing to do on insert.
1327		Ok(())
1328	}
1329	fn get_score(id: &T::AccountId) -> Result<Self::Score, Self::Error> {
1330		Ok(Pallet::<T>::weight_of(id).into())
1331	}
1332	fn on_update(_: &T::AccountId, _weight: Self::Score) -> Result<(), Self::Error> {
1333		// nothing to do on update.
1334		Ok(())
1335	}
1336	fn on_remove(_: &T::AccountId) -> Result<(), Self::Error> {
1337		// nothing to do on remove.
1338		Ok(())
1339	}
1340	fn unsafe_regenerate(
1341		_: impl IntoIterator<Item = T::AccountId>,
1342		_: Box<dyn Fn(&T::AccountId) -> Option<Self::Score>>,
1343	) -> u32 {
1344		// nothing to do upon regenerate.
1345		0
1346	}
1347	#[cfg(feature = "try-runtime")]
1348	fn try_state() -> Result<(), TryRuntimeError> {
1349		Ok(())
1350	}
1351
1352	fn unsafe_clear() {
1353		#[allow(deprecated)]
1354		Validators::<T>::remove_all();
1355	}
1356
1357	#[cfg(feature = "runtime-benchmarks")]
1358	fn score_update_worst_case(_who: &T::AccountId, _is_increase: bool) -> Self::Score {
1359		unimplemented!()
1360	}
1361}
1362
1363/// A simple voter list implementation that does not require any additional pallets. Note, this
1364/// does not provided nominators in sorted ordered. If you desire nominators in a sorted order take
1365/// a look at [`pallet-bags-list].
1366pub struct UseNominatorsAndValidatorsMap<T>(core::marker::PhantomData<T>);
1367impl<T: Config> SortedListProvider<T::AccountId> for UseNominatorsAndValidatorsMap<T> {
1368	type Error = ();
1369	type Score = VoteWeight;
1370
1371	fn iter() -> Box<dyn Iterator<Item = T::AccountId>> {
1372		Box::new(
1373			Validators::<T>::iter()
1374				.map(|(v, _)| v)
1375				.chain(Nominators::<T>::iter().map(|(n, _)| n)),
1376		)
1377	}
1378	fn iter_from(
1379		start: &T::AccountId,
1380	) -> Result<Box<dyn Iterator<Item = T::AccountId>>, Self::Error> {
1381		if Validators::<T>::contains_key(start) {
1382			let start_key = Validators::<T>::hashed_key_for(start);
1383			Ok(Box::new(
1384				Validators::<T>::iter_from(start_key)
1385					.map(|(n, _)| n)
1386					.chain(Nominators::<T>::iter().map(|(x, _)| x)),
1387			))
1388		} else if Nominators::<T>::contains_key(start) {
1389			let start_key = Nominators::<T>::hashed_key_for(start);
1390			Ok(Box::new(Nominators::<T>::iter_from(start_key).map(|(n, _)| n)))
1391		} else {
1392			Err(())
1393		}
1394	}
1395	fn lock() {}
1396	fn unlock() {}
1397	fn count() -> u32 {
1398		Nominators::<T>::count().saturating_add(Validators::<T>::count())
1399	}
1400	fn contains(id: &T::AccountId) -> bool {
1401		Nominators::<T>::contains_key(id) || Validators::<T>::contains_key(id)
1402	}
1403	fn on_insert(_: T::AccountId, _weight: Self::Score) -> Result<(), Self::Error> {
1404		// nothing to do on insert.
1405		Ok(())
1406	}
1407	fn get_score(id: &T::AccountId) -> Result<Self::Score, Self::Error> {
1408		Ok(Pallet::<T>::weight_of(id))
1409	}
1410	fn on_update(_: &T::AccountId, _weight: Self::Score) -> Result<(), Self::Error> {
1411		// nothing to do on update.
1412		Ok(())
1413	}
1414	fn on_remove(_: &T::AccountId) -> Result<(), Self::Error> {
1415		// nothing to do on remove.
1416		Ok(())
1417	}
1418	fn unsafe_regenerate(
1419		_: impl IntoIterator<Item = T::AccountId>,
1420		_: Box<dyn Fn(&T::AccountId) -> Option<Self::Score>>,
1421	) -> u32 {
1422		// nothing to do upon regenerate.
1423		0
1424	}
1425
1426	#[cfg(feature = "try-runtime")]
1427	fn try_state() -> Result<(), TryRuntimeError> {
1428		Ok(())
1429	}
1430
1431	fn unsafe_clear() {
1432		// NOTE: Caller must ensure this doesn't lead to too many storage accesses. This is a
1433		// condition of SortedListProvider::unsafe_clear.
1434		#[allow(deprecated)]
1435		Nominators::<T>::remove_all();
1436		#[allow(deprecated)]
1437		Validators::<T>::remove_all();
1438	}
1439
1440	#[cfg(feature = "runtime-benchmarks")]
1441	fn score_update_worst_case(_who: &T::AccountId, _is_increase: bool) -> Self::Score {
1442		unimplemented!()
1443	}
1444}
1445
1446impl<T: Config> StakingInterface for Pallet<T> {
1447	type AccountId = T::AccountId;
1448	type Balance = BalanceOf<T>;
1449	type CurrencyToVote = T::CurrencyToVote;
1450
1451	fn minimum_nominator_bond() -> Self::Balance {
1452		Self::min_nominator_bond()
1453	}
1454
1455	fn minimum_validator_bond() -> Self::Balance {
1456		Self::min_validator_bond()
1457	}
1458
1459	fn stash_by_ctrl(controller: &Self::AccountId) -> Result<Self::AccountId, DispatchError> {
1460		Self::ledger(Controller(controller.clone()))
1461			.map(|l| l.stash)
1462			.map_err(|e| e.into())
1463	}
1464
1465	fn bonding_duration() -> EraIndex {
1466		T::BondingDuration::get()
1467	}
1468
1469	fn current_era() -> EraIndex {
1470		CurrentEra::<T>::get().unwrap_or(Zero::zero())
1471	}
1472
1473	fn stake(who: &Self::AccountId) -> Result<Stake<BalanceOf<T>>, DispatchError> {
1474		Self::ledger(Stash(who.clone()))
1475			.map(|l| Stake { total: l.total, active: l.active })
1476			.map_err(|e| e.into())
1477	}
1478
1479	fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult {
1480		Self::bond_extra(RawOrigin::Signed(who.clone()).into(), extra)
1481	}
1482
1483	fn unbond(who: &Self::AccountId, value: Self::Balance) -> DispatchResult {
1484		let ctrl = Self::bonded(who).ok_or(Error::<T>::NotStash)?;
1485		Self::unbond(RawOrigin::Signed(ctrl).into(), value)
1486			.map_err(|with_post| with_post.error)
1487			.map(|_| ())
1488	}
1489
1490	fn set_payee(stash: &Self::AccountId, reward_acc: &Self::AccountId) -> DispatchResult {
1491		// Since virtual stakers are not allowed to compound their rewards as this pallet does not
1492		// manage their locks, we do not allow reward account to be set same as stash. For
1493		// external pallets that manage the virtual bond, they can claim rewards and re-bond them.
1494		ensure!(
1495			!Self::is_virtual_staker(stash) || stash != reward_acc,
1496			Error::<T>::RewardDestinationRestricted
1497		);
1498
1499		let ledger = Self::ledger(Stash(stash.clone()))?;
1500		let _ = ledger
1501			.set_payee(RewardDestination::Account(reward_acc.clone()))
1502			.defensive_proof("ledger was retrieved from storage, thus its bonded; qed.")?;
1503
1504		Ok(())
1505	}
1506
1507	fn chill(who: &Self::AccountId) -> DispatchResult {
1508		// defensive-only: any account bonded via this interface has the stash set as the
1509		// controller, but we have to be sure. Same comment anywhere else that we read this.
1510		let ctrl = Self::bonded(who).ok_or(Error::<T>::NotStash)?;
1511		Self::chill(RawOrigin::Signed(ctrl).into())
1512	}
1513
1514	fn withdraw_unbonded(
1515		who: Self::AccountId,
1516		_num_slashing_spans: u32,
1517	) -> Result<bool, DispatchError> {
1518		let ctrl = Self::bonded(&who).ok_or(Error::<T>::NotStash)?;
1519		Self::withdraw_unbonded(RawOrigin::Signed(ctrl.clone()).into(), 0)
1520			.map(|_| !StakingLedger::<T>::is_bonded(StakingAccount::Controller(ctrl)))
1521			.map_err(|with_post| with_post.error)
1522	}
1523
1524	fn bond(
1525		who: &Self::AccountId,
1526		value: Self::Balance,
1527		payee: &Self::AccountId,
1528	) -> DispatchResult {
1529		Self::bond(
1530			RawOrigin::Signed(who.clone()).into(),
1531			value,
1532			RewardDestination::Account(payee.clone()),
1533		)
1534	}
1535
1536	fn nominate(who: &Self::AccountId, targets: Vec<Self::AccountId>) -> DispatchResult {
1537		let ctrl = Self::bonded(who).ok_or(Error::<T>::NotStash)?;
1538		let targets = targets.into_iter().map(T::Lookup::unlookup).collect::<Vec<_>>();
1539		Self::nominate(RawOrigin::Signed(ctrl).into(), targets)
1540	}
1541
1542	fn desired_validator_count() -> u32 {
1543		ValidatorCount::<T>::get()
1544	}
1545
1546	fn election_ongoing() -> bool {
1547		<T::ElectionProvider as ElectionProvider>::status().is_ok()
1548	}
1549
1550	fn force_unstake(who: Self::AccountId) -> sp_runtime::DispatchResult {
1551		Self::force_unstake(RawOrigin::Root.into(), who.clone(), 0)
1552	}
1553
1554	fn is_exposed_in_era(who: &Self::AccountId, era: &EraIndex) -> bool {
1555		ErasStakersPaged::<T>::iter_prefix((era,)).any(|((validator, _), exposure_page)| {
1556			validator == *who || exposure_page.others.iter().any(|i| i.who == *who)
1557		})
1558	}
1559
1560	fn status(
1561		who: &Self::AccountId,
1562	) -> Result<sp_staking::StakerStatus<Self::AccountId>, DispatchError> {
1563		if !StakingLedger::<T>::is_bonded(StakingAccount::Stash(who.clone())) {
1564			return Err(Error::<T>::NotStash.into())
1565		}
1566
1567		let is_validator = Validators::<T>::contains_key(&who);
1568		let is_nominator = Nominators::<T>::get(&who);
1569
1570		use sp_staking::StakerStatus;
1571		match (is_validator, is_nominator.is_some()) {
1572			(false, false) => Ok(StakerStatus::Idle),
1573			(true, false) => Ok(StakerStatus::Validator),
1574			(false, true) => Ok(StakerStatus::Nominator(
1575				is_nominator.expect("is checked above; qed").targets.into_inner(),
1576			)),
1577			(true, true) => {
1578				defensive!("cannot be both validators and nominator");
1579				Err(Error::<T>::BadState.into())
1580			},
1581		}
1582	}
1583
1584	/// Whether `who` is a virtual staker whose funds are managed by another pallet.
1585	///
1586	/// There is an assumption that, this account is keyless and managed by another pallet in the
1587	/// runtime. Hence, it can never sign its own transactions.
1588	fn is_virtual_staker(who: &T::AccountId) -> bool {
1589		frame_system::Pallet::<T>::account_nonce(who).is_zero() &&
1590			VirtualStakers::<T>::contains_key(who)
1591	}
1592
1593	fn slash_reward_fraction() -> Perbill {
1594		SlashRewardFraction::<T>::get()
1595	}
1596
1597	sp_staking::runtime_benchmarks_enabled! {
1598		fn nominations(who: &Self::AccountId) -> Option<Vec<T::AccountId>> {
1599			Nominators::<T>::get(who).map(|n| n.targets.into_inner())
1600		}
1601
1602		fn add_era_stakers(
1603			current_era: &EraIndex,
1604			stash: &T::AccountId,
1605			exposures: Vec<(Self::AccountId, Self::Balance)>,
1606		) {
1607			let others = exposures
1608				.iter()
1609				.map(|(who, value)| crate::IndividualExposure { who: who.clone(), value: *value })
1610				.collect::<Vec<_>>();
1611			let exposure = Exposure { total: Default::default(), own: Default::default(), others };
1612			Eras::<T>::upsert_exposure(*current_era, stash, exposure);
1613		}
1614
1615		fn set_current_era(era: EraIndex) {
1616			CurrentEra::<T>::put(era);
1617		}
1618
1619		fn max_exposure_page_size() -> Page {
1620			T::MaxExposurePageSize::get()
1621		}
1622	}
1623}
1624
1625impl<T: Config> sp_staking::StakingUnchecked for Pallet<T> {
1626	fn migrate_to_virtual_staker(who: &Self::AccountId) -> DispatchResult {
1627		asset::kill_stake::<T>(who)?;
1628		VirtualStakers::<T>::insert(who, ());
1629		Ok(())
1630	}
1631
1632	/// Virtually bonds `keyless_who` to `payee` with `value`.
1633	///
1634	/// The payee must not be the same as the `keyless_who`.
1635	fn virtual_bond(
1636		keyless_who: &Self::AccountId,
1637		value: Self::Balance,
1638		payee: &Self::AccountId,
1639	) -> DispatchResult {
1640		if StakingLedger::<T>::is_bonded(StakingAccount::Stash(keyless_who.clone())) {
1641			return Err(Error::<T>::AlreadyBonded.into())
1642		}
1643
1644		// check if payee not same as who.
1645		ensure!(keyless_who != payee, Error::<T>::RewardDestinationRestricted);
1646
1647		// mark who as a virtual staker.
1648		VirtualStakers::<T>::insert(keyless_who, ());
1649
1650		Self::deposit_event(Event::<T>::Bonded { stash: keyless_who.clone(), amount: value });
1651		let ledger = StakingLedger::<T>::new(keyless_who.clone(), value);
1652
1653		ledger.bond(RewardDestination::Account(payee.clone()))?;
1654
1655		Ok(())
1656	}
1657
1658	/// Only meant to be used in tests.
1659	#[cfg(feature = "runtime-benchmarks")]
1660	fn migrate_to_direct_staker(who: &Self::AccountId) {
1661		assert!(VirtualStakers::<T>::contains_key(who));
1662		let ledger = StakingLedger::<T>::get(Stash(who.clone())).unwrap();
1663		let _ = asset::update_stake::<T>(who, ledger.total)
1664			.expect("funds must be transferred to stash");
1665		VirtualStakers::<T>::remove(who);
1666	}
1667}
1668
1669#[cfg(any(test, feature = "try-runtime"))]
1670impl<T: Config> Pallet<T> {
1671	pub(crate) fn do_try_state(_now: BlockNumberFor<T>) -> Result<(), TryRuntimeError> {
1672		session_rotation::Rotator::<T>::do_try_state()?;
1673		session_rotation::Eras::<T>::do_try_state()?;
1674
1675		use frame_support::traits::fungible::Inspect;
1676		if T::CurrencyToVote::will_downscale(T::Currency::total_issuance()).map_or(false, |x| x) {
1677			log!(warn, "total issuance will cause T::CurrencyToVote to downscale -- report to maintainers.")
1678		}
1679
1680		Self::check_ledgers()?;
1681		Self::check_bonded_consistency()?;
1682		Self::check_payees()?;
1683		Self::check_paged_exposures()?;
1684		Self::check_count()?;
1685
1686		Ok(())
1687	}
1688
1689	/// Invariants:
1690	/// * A controller should not be associated with more than one ledger.
1691	/// * A bonded (stash, controller) pair should have only one associated ledger. I.e. if the
1692	///   ledger is bonded by stash, the controller account must not bond a different ledger.
1693	/// * A bonded (stash, controller) pair must have an associated ledger.
1694	///
1695	/// NOTE: these checks result in warnings only. Once
1696	/// <https://github.com/paritytech/polkadot-sdk/issues/3245> is resolved, turn warns into check
1697	/// failures.
1698	fn check_bonded_consistency() -> Result<(), TryRuntimeError> {
1699		use alloc::collections::btree_set::BTreeSet;
1700
1701		let mut count_controller_double = 0;
1702		let mut count_double = 0;
1703		let mut count_none = 0;
1704		// sanity check to ensure that each controller in Bonded storage is associated with only one
1705		// ledger.
1706		let mut controllers = BTreeSet::new();
1707
1708		for (stash, controller) in <Bonded<T>>::iter() {
1709			if !controllers.insert(controller.clone()) {
1710				count_controller_double += 1;
1711			}
1712
1713			match (<Ledger<T>>::get(&stash), <Ledger<T>>::get(&controller)) {
1714				(Some(_), Some(_)) =>
1715				// if stash == controller, it means that the ledger has migrated to
1716				// post-controller. If no migration happened, we expect that the (stash,
1717				// controller) pair has only one associated ledger.
1718					if stash != controller {
1719						count_double += 1;
1720					},
1721				(None, None) => {
1722					count_none += 1;
1723				},
1724				_ => {},
1725			};
1726		}
1727
1728		if count_controller_double != 0 {
1729			log!(
1730				warn,
1731				"a controller is associated with more than one ledger ({} occurrences)",
1732				count_controller_double
1733			);
1734		};
1735
1736		if count_double != 0 {
1737			log!(warn, "single tuple of (stash, controller) pair bonds more than one ledger ({} occurrences)", count_double);
1738		}
1739
1740		if count_none != 0 {
1741			log!(warn, "inconsistent bonded state: (stash, controller) pair missing associated ledger ({} occurrences)", count_none);
1742		}
1743
1744		Ok(())
1745	}
1746
1747	/// Invariants:
1748	/// * A bonded ledger should always have an assigned `Payee`.
1749	/// * The number of entries in `Payee` and of bonded staking ledgers *must* match.
1750	/// * The stash account in the ledger must match that of the bonded account.
1751	fn check_payees() -> Result<(), TryRuntimeError> {
1752		for (stash, _) in Bonded::<T>::iter() {
1753			ensure!(Payee::<T>::get(&stash).is_some(), "bonded ledger does not have payee set");
1754		}
1755
1756		ensure!(
1757			(Ledger::<T>::iter().count() == Payee::<T>::iter().count()) &&
1758				(Ledger::<T>::iter().count() == Bonded::<T>::iter().count()),
1759			"number of entries in payee storage items does not match the number of bonded ledgers",
1760		);
1761
1762		Ok(())
1763	}
1764
1765	/// Invariants:
1766	/// * Number of voters in `VoterList` match that of the number of Nominators and Validators in
1767	/// the system (validator is both voter and target).
1768	/// * Number of targets in `TargetList` matches the number of validators in the system.
1769	/// * Current validator count is bounded by the election provider's max winners.
1770	fn check_count() -> Result<(), TryRuntimeError> {
1771		ensure!(
1772			<T as Config>::VoterList::count() ==
1773				Nominators::<T>::count() + Validators::<T>::count(),
1774			"wrong external count"
1775		);
1776		ensure!(
1777			<T as Config>::TargetList::count() == Validators::<T>::count(),
1778			"wrong external count"
1779		);
1780		let max_validators_bound = crate::MaxWinnersOf::<T>::get();
1781		let max_winners_per_page_bound = crate::MaxWinnersPerPageOf::<T::ElectionProvider>::get();
1782		ensure!(
1783			max_validators_bound >= max_winners_per_page_bound,
1784			"max validators should be higher than per page bounds"
1785		);
1786		ensure!(ValidatorCount::<T>::get() <= max_validators_bound, Error::<T>::TooManyValidators);
1787		Ok(())
1788	}
1789
1790	/// Invariants:
1791	/// * Stake consistency: ledger.total == ledger.active + sum(ledger.unlocking).
1792	/// * The ledger's controller and stash matches the associated `Bonded` tuple.
1793	/// * Staking locked funds for every bonded stash (non virtual stakers) should be the same as
1794	/// its ledger's total.
1795	/// * For virtual stakers, locked funds should be zero and payee should be non-stash account.
1796	/// * Staking ledger and bond are not corrupted.
1797	fn check_ledgers() -> Result<(), TryRuntimeError> {
1798		Bonded::<T>::iter()
1799			.map(|(stash, ctrl)| {
1800				// ensure locks consistency.
1801				if VirtualStakers::<T>::contains_key(stash.clone()) {
1802					ensure!(
1803						asset::staked::<T>(&stash) == Zero::zero(),
1804						"virtual stakers should not have any staked balance"
1805					);
1806					ensure!(
1807						<Bonded<T>>::get(stash.clone()).unwrap() == stash.clone(),
1808						"stash and controller should be same"
1809					);
1810					ensure!(
1811						Ledger::<T>::get(stash.clone()).unwrap().stash == stash,
1812						"ledger corrupted for virtual staker"
1813					);
1814					ensure!(
1815						frame_system::Pallet::<T>::account_nonce(&stash).is_zero(),
1816						"virtual stakers are keyless and should not have any nonce"
1817					);
1818					let reward_destination = <Payee<T>>::get(stash.clone()).unwrap();
1819					if let RewardDestination::Account(payee) = reward_destination {
1820						ensure!(
1821							payee != stash.clone(),
1822							"reward destination should not be same as stash for virtual staker"
1823						);
1824					} else {
1825						return Err(DispatchError::Other(
1826							"reward destination must be of account variant for virtual staker",
1827						));
1828					}
1829				} else {
1830					let integrity = Self::inspect_bond_state(&stash);
1831					if integrity != Ok(LedgerIntegrityState::Ok) {
1832						// NOTE: not using defensive! since we test these cases and it panics them
1833						log!(
1834							error,
1835							"defensive: bonded stash {:?} has inconsistent ledger state: {:?}",
1836							stash,
1837							integrity
1838						);
1839					}
1840				}
1841
1842				Self::ensure_ledger_consistent(&ctrl)?;
1843				Self::ensure_ledger_role_and_min_bond(&ctrl)?;
1844				Ok(())
1845			})
1846			.collect::<Result<Vec<_>, _>>()?;
1847		Ok(())
1848	}
1849
1850	/// Invariants:
1851	/// * For each paged era exposed validator, check if the exposure total is sane (exposure.total
1852	/// = exposure.own + exposure.own).
1853	/// * Paged exposures metadata (`ErasStakersOverview`) matches the paged exposures state.
1854	fn check_paged_exposures() -> Result<(), TryRuntimeError> {
1855		use alloc::collections::btree_map::BTreeMap;
1856		use sp_staking::PagedExposureMetadata;
1857
1858		// Sanity check for the paged exposure of the active era.
1859		let mut exposures: BTreeMap<T::AccountId, PagedExposureMetadata<BalanceOf<T>>> =
1860			BTreeMap::new();
1861		let era = ActiveEra::<T>::get().unwrap().index;
1862		let accumulator_default = PagedExposureMetadata {
1863			total: Zero::zero(),
1864			own: Zero::zero(),
1865			nominator_count: 0,
1866			page_count: 0,
1867		};
1868
1869		ErasStakersPaged::<T>::iter_prefix((era,))
1870			.map(|((validator, _page), expo)| {
1871				ensure!(
1872					expo.page_total ==
1873						expo.others.iter().map(|e| e.value).fold(Zero::zero(), |acc, x| acc + x),
1874					"wrong total exposure for the page.",
1875				);
1876
1877				let metadata = exposures.get(&validator).unwrap_or(&accumulator_default);
1878				exposures.insert(
1879					validator,
1880					PagedExposureMetadata {
1881						total: metadata.total + expo.page_total,
1882						own: metadata.own,
1883						nominator_count: metadata.nominator_count + expo.others.len() as u32,
1884						page_count: metadata.page_count + 1,
1885					},
1886				);
1887
1888				Ok(())
1889			})
1890			.collect::<Result<(), TryRuntimeError>>()?;
1891
1892		exposures
1893			.iter()
1894			.map(|(validator, metadata)| {
1895				let actual_overview = ErasStakersOverview::<T>::get(era, validator);
1896
1897				ensure!(actual_overview.is_some(), "No overview found for a paged exposure");
1898				let actual_overview = actual_overview.unwrap();
1899
1900				ensure!(
1901					actual_overview.total == metadata.total + actual_overview.own,
1902					"Exposure metadata does not have correct total exposed stake."
1903				);
1904				ensure!(
1905					actual_overview.nominator_count == metadata.nominator_count,
1906					"Exposure metadata does not have correct count of nominators."
1907				);
1908				ensure!(
1909					actual_overview.page_count == metadata.page_count,
1910					"Exposure metadata does not have correct count of pages."
1911				);
1912
1913				Ok(())
1914			})
1915			.collect::<Result<(), TryRuntimeError>>()
1916	}
1917
1918	fn ensure_ledger_role_and_min_bond(ctrl: &T::AccountId) -> Result<(), TryRuntimeError> {
1919		let ledger = Self::ledger(StakingAccount::Controller(ctrl.clone()))?;
1920		let stash = ledger.stash;
1921
1922		let is_nominator = Nominators::<T>::contains_key(&stash);
1923		let is_validator = Validators::<T>::contains_key(&stash);
1924
1925		match (is_nominator, is_validator) {
1926			(false, false) => {
1927				if ledger.active < Self::min_chilled_bond() {
1928					log!(warn, "Chilled stash {:?} has less than minimum bond", stash);
1929				}
1930				// is chilled
1931			},
1932			(true, false) => {
1933				// Nominators must have a minimum bond.
1934				if ledger.active < Self::min_nominator_bond() {
1935					log!(warn, "Nominator {:?} has less than minimum bond", stash);
1936				}
1937			},
1938			(false, true) => {
1939				// Validators must have a minimum bond.
1940				if ledger.active < Self::min_validator_bond() {
1941					log!(warn, "Validator {:?} has less than minimum bond", stash);
1942				}
1943			},
1944			(true, true) => {
1945				ensure!(false, "Stash cannot be both nominator and validator");
1946			},
1947		}
1948		Ok(())
1949	}
1950
1951	fn ensure_ledger_consistent(ctrl: &T::AccountId) -> Result<(), TryRuntimeError> {
1952		// ensures ledger.total == ledger.active + sum(ledger.unlocking).
1953		let ledger = Self::ledger(StakingAccount::Controller(ctrl.clone()))?;
1954
1955		let real_total: BalanceOf<T> =
1956			ledger.unlocking.iter().fold(ledger.active, |a, c| a + c.value);
1957		ensure!(real_total == ledger.total, "ledger.total corrupt");
1958
1959		Ok(())
1960	}
1961}