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