Skip to main content

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