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