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