1use frame_election_provider_support::{
21 bounds::{CountBound, SizeBound},
22 data_provider, BoundedSupportsOf, DataProviderBounds, ElectionDataProvider, ElectionProvider,
23 PageIndex, ScoreProvider, SortedListProvider, TryFromOtherBounds, VoteWeight, VoterOf,
24};
25use frame_support::{
26 defensive,
27 dispatch::WithPostDispatchInfo,
28 pallet_prelude::*,
29 traits::{
30 Defensive, DefensiveSaturating, EstimateNextNewSession, Get, Imbalance,
31 InspectLockableCurrency, Len, LockableCurrency, OnUnbalanced, RewardsReporter, TryCollect,
32 UnixTime,
33 },
34 weights::Weight,
35};
36use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin};
37use pallet_session::historical;
38use sp_runtime::{
39 traits::{
40 Bounded, CheckedAdd, Convert, One, SaturatedConversion, Saturating, StaticLookup, Zero,
41 },
42 ArithmeticError, DispatchResult, Perbill, Percent,
43};
44use sp_staking::{
45 currency_to_vote::CurrencyToVote,
46 offence::{OffenceDetails, OnOffenceHandler},
47 EraIndex, OnStakingUpdate, Page, SessionIndex, Stake,
48 StakingAccount::{self, Controller, Stash},
49 StakingInterface,
50};
51
52use crate::{
53 asset, election_size_tracker::StaticTracker, log, slashing, weights::WeightInfo, ActiveEraInfo,
54 BalanceOf, EraInfo, EraPayout, Exposure, Forcing, IndividualExposure, LedgerIntegrityState,
55 MaxNominationsOf, MaxWinnersOf, Nominations, NominationsQuota, PositiveImbalanceOf,
56 RewardDestination, SessionInterface, StakingLedger, UnlockChunk, ValidatorPrefs, STAKING_ID,
57};
58use alloc::{boxed::Box, vec, vec::Vec};
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
67const NPOS_MAX_ITERATIONS_COEFFICIENT: u32 = 2;
74
75impl<T: Config> Pallet<T> {
76 pub fn ledger(account: StakingAccount<T::AccountId>) -> Result<StakingLedger<T>, Error<T>> {
78 StakingLedger::<T>::get(account)
79 }
80
81 pub fn payee(account: StakingAccount<T::AccountId>) -> Option<RewardDestination<T::AccountId>> {
82 StakingLedger::<T>::reward_destination(account)
83 }
84
85 pub fn bonded(stash: &T::AccountId) -> Option<T::AccountId> {
87 StakingLedger::<T>::paired_account(Stash(stash.clone()))
88 }
89
90 pub(crate) fn inspect_bond_state(
97 stash: &T::AccountId,
98 ) -> Result<LedgerIntegrityState, Error<T>> {
99 let hold_or_lock = match asset::staked::<T>(&stash) {
100 x if x.is_zero() => {
101 let locked = T::OldCurrency::balance_locked(STAKING_ID, &stash).into();
102 locked
103 },
104 held => held,
105 };
106
107 let controller = <Bonded<T>>::get(stash).ok_or_else(|| {
108 if hold_or_lock == Zero::zero() {
109 Error::<T>::NotStash
110 } else {
111 Error::<T>::BadState
112 }
113 })?;
114
115 match Ledger::<T>::get(controller) {
116 Some(ledger) => {
117 if ledger.stash != *stash {
118 Ok(LedgerIntegrityState::Corrupted)
119 } else {
120 if hold_or_lock != ledger.total {
121 Ok(LedgerIntegrityState::LockCorrupted)
122 } else {
123 Ok(LedgerIntegrityState::Ok)
124 }
125 }
126 },
127 None => Ok(LedgerIntegrityState::CorruptedKilled),
128 }
129 }
130
131 pub fn slashable_balance_of(stash: &T::AccountId) -> BalanceOf<T> {
133 Self::ledger(Stash(stash.clone())).map(|l| l.active).unwrap_or_default()
135 }
136
137 pub fn slashable_balance_of_vote_weight(
139 stash: &T::AccountId,
140 issuance: BalanceOf<T>,
141 ) -> VoteWeight {
142 T::CurrencyToVote::to_vote(Self::slashable_balance_of(stash), issuance)
143 }
144
145 pub fn weight_of_fn() -> Box<dyn Fn(&T::AccountId) -> VoteWeight> {
150 let issuance = asset::total_issuance::<T>();
153 Box::new(move |who: &T::AccountId| -> VoteWeight {
154 Self::slashable_balance_of_vote_weight(who, issuance)
155 })
156 }
157
158 pub fn weight_of(who: &T::AccountId) -> VoteWeight {
160 let issuance = asset::total_issuance::<T>();
161 Self::slashable_balance_of_vote_weight(who, issuance)
162 }
163
164 pub(super) fn do_bond_extra(stash: &T::AccountId, additional: BalanceOf<T>) -> DispatchResult {
165 let mut ledger = Self::ledger(StakingAccount::Stash(stash.clone()))?;
166
167 let extra = if Self::is_virtual_staker(stash) {
170 additional
171 } else {
172 additional.min(asset::free_to_stake::<T>(stash))
174 };
175
176 ledger.total = ledger.total.checked_add(&extra).ok_or(ArithmeticError::Overflow)?;
177 ledger.active = ledger.active.checked_add(&extra).ok_or(ArithmeticError::Overflow)?;
178 ensure!(ledger.active >= asset::existential_deposit::<T>(), Error::<T>::InsufficientBond);
180
181 ledger.update()?;
183 if T::VoterList::contains(stash) {
185 let _ = T::VoterList::on_update(&stash, Self::weight_of(stash)).defensive();
186 }
187
188 Self::deposit_event(Event::<T>::Bonded { stash: stash.clone(), amount: extra });
189
190 Ok(())
191 }
192
193 pub(super) fn do_withdraw_unbonded(
194 controller: &T::AccountId,
195 num_slashing_spans: u32,
196 ) -> Result<Weight, DispatchError> {
197 let mut ledger = Self::ledger(Controller(controller.clone()))?;
198 let (stash, old_total) = (ledger.stash.clone(), ledger.total);
199 if let Some(current_era) = CurrentEra::<T>::get() {
200 ledger = ledger.consolidate_unlocked(current_era)
201 }
202 let new_total = ledger.total;
203
204 let ed = asset::existential_deposit::<T>();
205 let used_weight =
206 if ledger.unlocking.is_empty() && (ledger.active < ed || ledger.active.is_zero()) {
207 Self::kill_stash(&ledger.stash, num_slashing_spans)?;
211
212 T::WeightInfo::withdraw_unbonded_kill(num_slashing_spans)
213 } else {
214 ledger.update()?;
216
217 T::WeightInfo::withdraw_unbonded_update(num_slashing_spans)
219 };
220
221 if new_total < old_total {
224 let value = old_total.defensive_saturating_sub(new_total);
226 Self::deposit_event(Event::<T>::Withdrawn { stash, amount: value });
227
228 T::EventListeners::on_withdraw(controller, value);
230 }
231
232 Ok(used_weight)
233 }
234
235 pub(super) fn do_payout_stakers(
236 validator_stash: T::AccountId,
237 era: EraIndex,
238 ) -> DispatchResultWithPostInfo {
239 let controller = Self::bonded(&validator_stash).ok_or_else(|| {
240 Error::<T>::NotStash.with_weight(T::WeightInfo::payout_stakers_alive_staked(0))
241 })?;
242
243 let ledger = Self::ledger(StakingAccount::Controller(controller))?;
244 let page = EraInfo::<T>::get_next_claimable_page(era, &validator_stash, &ledger)
245 .ok_or_else(|| {
246 Error::<T>::AlreadyClaimed
247 .with_weight(T::WeightInfo::payout_stakers_alive_staked(0))
248 })?;
249
250 Self::do_payout_stakers_by_page(validator_stash, era, page)
251 }
252
253 pub(super) fn do_payout_stakers_by_page(
254 validator_stash: T::AccountId,
255 era: EraIndex,
256 page: Page,
257 ) -> DispatchResultWithPostInfo {
258 let current_era = CurrentEra::<T>::get().ok_or_else(|| {
260 Error::<T>::InvalidEraToReward
261 .with_weight(T::WeightInfo::payout_stakers_alive_staked(0))
262 })?;
263
264 let history_depth = T::HistoryDepth::get();
265 ensure!(
266 era <= current_era && era >= current_era.saturating_sub(history_depth),
267 Error::<T>::InvalidEraToReward
268 .with_weight(T::WeightInfo::payout_stakers_alive_staked(0))
269 );
270
271 ensure!(
272 page < EraInfo::<T>::get_page_count(era, &validator_stash),
273 Error::<T>::InvalidPage.with_weight(T::WeightInfo::payout_stakers_alive_staked(0))
274 );
275
276 let era_payout = <ErasValidatorReward<T>>::get(&era).ok_or_else(|| {
279 Error::<T>::InvalidEraToReward
280 .with_weight(T::WeightInfo::payout_stakers_alive_staked(0))
281 })?;
282
283 let account = StakingAccount::Stash(validator_stash.clone());
284 let mut ledger = Self::ledger(account.clone()).or_else(|_| {
285 if StakingLedger::<T>::is_bonded(account) {
286 Err(Error::<T>::NotController.into())
287 } else {
288 Err(Error::<T>::NotStash.with_weight(T::WeightInfo::payout_stakers_alive_staked(0)))
289 }
290 })?;
291
292 ledger
294 .legacy_claimed_rewards
295 .retain(|&x| x >= current_era.saturating_sub(history_depth));
296 ledger.clone().update()?;
297
298 let stash = ledger.stash.clone();
299
300 if EraInfo::<T>::is_rewards_claimed_with_legacy_fallback(era, &ledger, &stash, page) {
301 return Err(Error::<T>::AlreadyClaimed
302 .with_weight(T::WeightInfo::payout_stakers_alive_staked(0)));
303 } else {
304 EraInfo::<T>::set_rewards_as_claimed(era, &stash, page);
305 }
306
307 let exposure = EraInfo::<T>::get_paged_exposure(era, &stash, page).ok_or_else(|| {
308 Error::<T>::InvalidEraToReward
309 .with_weight(T::WeightInfo::payout_stakers_alive_staked(0))
310 })?;
311
312 let era_reward_points = <ErasRewardPoints<T>>::get(&era);
322 let total_reward_points = era_reward_points.total;
323 let validator_reward_points =
324 era_reward_points.individual.get(&stash).copied().unwrap_or_else(Zero::zero);
325
326 if validator_reward_points.is_zero() {
328 return Ok(Some(T::WeightInfo::payout_stakers_alive_staked(0)).into());
329 }
330
331 let validator_total_reward_part =
334 Perbill::from_rational(validator_reward_points, total_reward_points);
335
336 let validator_total_payout = validator_total_reward_part * era_payout;
338
339 let validator_commission = EraInfo::<T>::get_validator_commission(era, &ledger.stash);
340 let validator_total_commission_payout = validator_commission * validator_total_payout;
342
343 let validator_leftover_payout =
344 validator_total_payout.defensive_saturating_sub(validator_total_commission_payout);
345 let validator_exposure_part = Perbill::from_rational(exposure.own(), exposure.total());
347 let validator_staking_payout = validator_exposure_part * validator_leftover_payout;
348 let page_stake_part = Perbill::from_rational(exposure.page_total(), exposure.total());
349 let validator_commission_payout = page_stake_part * validator_total_commission_payout;
351
352 Self::deposit_event(Event::<T>::PayoutStarted {
353 era_index: era,
354 validator_stash: stash.clone(),
355 page,
356 next: EraInfo::<T>::get_next_claimable_page(era, &stash, &ledger),
357 });
358
359 let mut total_imbalance = PositiveImbalanceOf::<T>::zero();
360 if let Some((imbalance, dest)) =
362 Self::make_payout(&stash, validator_staking_payout + validator_commission_payout)
363 {
364 Self::deposit_event(Event::<T>::Rewarded { stash, dest, amount: imbalance.peek() });
365 total_imbalance.subsume(imbalance);
366 }
367
368 let mut nominator_payout_count: u32 = 0;
372
373 for nominator in exposure.others().iter() {
376 let nominator_exposure_part = Perbill::from_rational(nominator.value, exposure.total());
377
378 let nominator_reward: BalanceOf<T> =
379 nominator_exposure_part * validator_leftover_payout;
380 if let Some((imbalance, dest)) = Self::make_payout(&nominator.who, nominator_reward) {
382 nominator_payout_count += 1;
384 let e = Event::<T>::Rewarded {
385 stash: nominator.who.clone(),
386 dest,
387 amount: imbalance.peek(),
388 };
389 Self::deposit_event(e);
390 total_imbalance.subsume(imbalance);
391 }
392 }
393
394 T::Reward::on_unbalanced(total_imbalance);
395 debug_assert!(nominator_payout_count <= T::MaxExposurePageSize::get());
396
397 Ok(Some(T::WeightInfo::payout_stakers_alive_staked(nominator_payout_count)).into())
398 }
399
400 pub(crate) fn chill_stash(stash: &T::AccountId) {
402 let chilled_as_validator = Self::do_remove_validator(stash);
403 let chilled_as_nominator = Self::do_remove_nominator(stash);
404 if chilled_as_validator || chilled_as_nominator {
405 Self::deposit_event(Event::<T>::Chilled { stash: stash.clone() });
406 }
407 }
408
409 fn make_payout(
412 stash: &T::AccountId,
413 amount: BalanceOf<T>,
414 ) -> Option<(PositiveImbalanceOf<T>, RewardDestination<T::AccountId>)> {
415 if amount.is_zero() {
417 return None;
418 }
419 let dest = Self::payee(StakingAccount::Stash(stash.clone()))?;
420
421 let maybe_imbalance = match dest {
422 RewardDestination::Stash => asset::mint_into_existing::<T>(stash, amount),
423 RewardDestination::Staked => Self::ledger(Stash(stash.clone()))
424 .and_then(|mut ledger| {
425 ledger.active += amount;
426 ledger.total += amount;
427 let r = asset::mint_into_existing::<T>(stash, amount);
428
429 let _ = ledger
430 .update()
431 .defensive_proof("ledger fetched from storage, so it exists; qed.");
432
433 Ok(r)
434 })
435 .unwrap_or_default(),
436 RewardDestination::Account(ref dest_account) =>
437 Some(asset::mint_creating::<T>(&dest_account, amount)),
438 RewardDestination::None => None,
439 #[allow(deprecated)]
440 RewardDestination::Controller => Self::bonded(stash)
441 .map(|controller| {
442 defensive!("Paying out controller as reward destination which is deprecated and should be migrated.");
443 asset::mint_creating::<T>(&controller, amount)
446 }),
447 };
448 maybe_imbalance.map(|imbalance| (imbalance, dest))
449 }
450
451 fn new_session(
453 session_index: SessionIndex,
454 is_genesis: bool,
455 ) -> Option<BoundedVec<T::AccountId, MaxWinnersOf<T>>> {
456 if let Some(current_era) = CurrentEra::<T>::get() {
457 let current_era_start_session_index = ErasStartSessionIndex::<T>::get(current_era)
459 .unwrap_or_else(|| {
460 frame_support::print("Error: start_session_index must be set for current_era");
461 0
462 });
463
464 let era_length = session_index.saturating_sub(current_era_start_session_index); match ForceEra::<T>::get() {
467 Forcing::ForceNew => (),
469 Forcing::ForceAlways => (),
471 Forcing::NotForcing if era_length >= T::SessionsPerEra::get() => (),
473 _ => {
474 return None;
477 },
478 }
479
480 let maybe_new_era_validators = Self::try_trigger_new_era(session_index, is_genesis);
482 if maybe_new_era_validators.is_some() &&
483 matches!(ForceEra::<T>::get(), Forcing::ForceNew)
484 {
485 Self::set_force_era(Forcing::NotForcing);
486 }
487
488 maybe_new_era_validators
489 } else {
490 log!(debug, "Starting the first era.");
492 Self::try_trigger_new_era(session_index, is_genesis)
493 }
494 }
495
496 fn start_session(start_session: SessionIndex) {
498 let next_active_era = ActiveEra::<T>::get().map(|e| e.index + 1).unwrap_or(0);
499 if let Some(next_active_era_start_session_index) =
503 ErasStartSessionIndex::<T>::get(next_active_era)
504 {
505 if next_active_era_start_session_index == start_session {
506 Self::start_era(start_session);
507 } else if next_active_era_start_session_index < start_session {
508 frame_support::print("Warning: A session appears to have been skipped.");
511 Self::start_era(start_session);
512 }
513 }
514 }
515
516 fn end_session(session_index: SessionIndex) {
518 if let Some(active_era) = ActiveEra::<T>::get() {
519 if let Some(next_active_era_start_session_index) =
520 ErasStartSessionIndex::<T>::get(active_era.index + 1)
521 {
522 if next_active_era_start_session_index == session_index + 1 {
523 Self::end_era(active_era, session_index);
524 }
525 }
526 }
527 }
528
529 fn start_era(start_session: SessionIndex) {
534 let active_era = ActiveEra::<T>::mutate(|active_era| {
535 let new_index = active_era.as_ref().map(|info| info.index + 1).unwrap_or(0);
536 *active_era = Some(ActiveEraInfo {
537 index: new_index,
538 start: None,
540 });
541 new_index
542 });
543
544 let bonding_duration = T::BondingDuration::get();
545
546 BondedEras::<T>::mutate(|bonded| {
547 bonded.push((active_era, start_session));
548
549 if active_era > bonding_duration {
550 let first_kept = active_era.defensive_saturating_sub(bonding_duration);
551
552 let n_to_prune =
554 bonded.iter().take_while(|&&(era_idx, _)| era_idx < first_kept).count();
555
556 for (pruned_era, _) in bonded.drain(..n_to_prune) {
558 slashing::clear_era_metadata::<T>(pruned_era);
559 }
560
561 if let Some(&(_, first_session)) = bonded.first() {
562 T::SessionInterface::prune_historical_up_to(first_session);
563 }
564 }
565 });
566
567 Self::apply_unapplied_slashes(active_era);
568 }
569
570 fn end_era(active_era: ActiveEraInfo, _session_index: SessionIndex) {
572 if let Some(active_era_start) = active_era.start {
574 let now_as_millis_u64 = T::UnixTime::now().as_millis().saturated_into::<u64>();
575
576 let era_duration = (now_as_millis_u64.defensive_saturating_sub(active_era_start))
577 .saturated_into::<u64>();
578 let staked = ErasTotalStake::<T>::get(&active_era.index);
579 let issuance = asset::total_issuance::<T>();
580
581 let (validator_payout, remainder) =
582 T::EraPayout::era_payout(staked, issuance, era_duration);
583
584 let total_payout = validator_payout.saturating_add(remainder);
585 let max_staked_rewards =
586 MaxStakedRewards::<T>::get().unwrap_or(Percent::from_percent(100));
587
588 let validator_payout = validator_payout.min(max_staked_rewards * total_payout);
590 let remainder = total_payout.saturating_sub(validator_payout);
591
592 Self::deposit_event(Event::<T>::EraPaid {
593 era_index: active_era.index,
594 validator_payout,
595 remainder,
596 });
597
598 <ErasValidatorReward<T>>::insert(&active_era.index, validator_payout);
600 T::RewardRemainder::on_unbalanced(asset::issue::<T>(remainder));
601 }
602 }
603
604 pub fn trigger_new_era(
613 start_session_index: SessionIndex,
614 exposures: BoundedVec<
615 (T::AccountId, Exposure<T::AccountId, BalanceOf<T>>),
616 MaxWinnersOf<T>,
617 >,
618 ) -> BoundedVec<T::AccountId, MaxWinnersOf<T>> {
619 let new_planned_era = CurrentEra::<T>::mutate(|s| {
621 *s = Some(s.map(|s| s + 1).unwrap_or(0));
622 s.unwrap()
623 });
624 ErasStartSessionIndex::<T>::insert(&new_planned_era, &start_session_index);
625
626 if let Some(old_era) = new_planned_era.checked_sub(T::HistoryDepth::get() + 1) {
628 Self::clear_era_information(old_era);
629 }
630
631 Self::store_stakers_info(exposures, new_planned_era)
633 }
634
635 pub(crate) fn try_trigger_new_era(
642 start_session_index: SessionIndex,
643 is_genesis: bool,
644 ) -> Option<BoundedVec<T::AccountId, MaxWinnersOf<T>>> {
645 let election_result = if is_genesis {
646 let result = <T::GenesisElectionProvider>::elect(0)
648 .map_err(|e| {
649 log!(warn, "genesis election provider failed due to {:?}", e);
650 Self::deposit_event(Event::StakingElectionFailed);
651 })
652 .ok()?;
653
654 BoundedSupportsOf::<T::ElectionProvider>::try_from_other_bounds(result).ok()?
655 } else {
656 <T::ElectionProvider>::elect(0)
658 .map_err(|e| {
659 log!(warn, "election provider failed due to {:?}", e);
660 Self::deposit_event(Event::StakingElectionFailed);
661 })
662 .ok()?
663 };
664
665 let exposures = Self::collect_exposures(election_result);
666 if (exposures.len() as u32) < MinimumValidatorCount::<T>::get().max(1) {
667 match CurrentEra::<T>::get() {
669 Some(current_era) if current_era > 0 => log!(
670 warn,
671 "chain does not have enough staking candidates to operate for era {:?} ({} \
672 elected, minimum is {})",
673 CurrentEra::<T>::get().unwrap_or(0),
674 exposures.len(),
675 MinimumValidatorCount::<T>::get(),
676 ),
677 None => {
678 CurrentEra::<T>::put(0);
683 ErasStartSessionIndex::<T>::insert(&0, &start_session_index);
684 },
685 _ => (),
686 }
687
688 Self::deposit_event(Event::StakingElectionFailed);
689 return None;
690 }
691
692 Self::deposit_event(Event::StakersElected);
693 Some(Self::trigger_new_era(start_session_index, exposures))
694 }
695
696 pub fn store_stakers_info(
700 exposures: BoundedVec<
701 (T::AccountId, Exposure<T::AccountId, BalanceOf<T>>),
702 MaxWinnersOf<T>,
703 >,
704 new_planned_era: EraIndex,
705 ) -> BoundedVec<T::AccountId, MaxWinnersOf<T>> {
706 let mut total_stake: BalanceOf<T> = Zero::zero();
708 let mut elected_stashes = Vec::with_capacity(exposures.len());
709
710 exposures.into_iter().for_each(|(stash, exposure)| {
711 elected_stashes.push(stash.clone());
713 total_stake = total_stake.saturating_add(exposure.total);
715 EraInfo::<T>::set_exposure(new_planned_era, &stash, exposure);
717 });
718
719 let elected_stashes: BoundedVec<_, MaxWinnersOf<T>> = elected_stashes
720 .try_into()
721 .expect("elected_stashes.len() always equal to exposures.len(); qed");
722
723 EraInfo::<T>::set_total_stake(new_planned_era, total_stake);
724
725 for stash in &elected_stashes {
727 let pref = Validators::<T>::get(stash);
728 <ErasValidatorPrefs<T>>::insert(&new_planned_era, stash, pref);
729 }
730
731 if new_planned_era > 0 {
732 log!(
733 debug,
734 "new validator set of size {:?} has been processed for era {:?}",
735 elected_stashes.len(),
736 new_planned_era,
737 );
738 }
739
740 elected_stashes
741 }
742
743 fn collect_exposures(
746 supports: BoundedSupportsOf<T::ElectionProvider>,
747 ) -> BoundedVec<(T::AccountId, Exposure<T::AccountId, BalanceOf<T>>), MaxWinnersOf<T>> {
748 let total_issuance = asset::total_issuance::<T>();
749 let to_currency = |e: frame_election_provider_support::ExtendedBalance| {
750 T::CurrencyToVote::to_currency(e, total_issuance)
751 };
752
753 supports
754 .into_iter()
755 .map(|(validator, support)| {
756 let mut others = Vec::with_capacity(support.voters.len());
758 let mut own: BalanceOf<T> = Zero::zero();
759 let mut total: BalanceOf<T> = Zero::zero();
760 support
761 .voters
762 .into_iter()
763 .map(|(nominator, weight)| (nominator, to_currency(weight)))
764 .for_each(|(nominator, stake)| {
765 if nominator == validator {
766 own = own.saturating_add(stake);
767 } else {
768 others.push(IndividualExposure { who: nominator, value: stake });
769 }
770 total = total.saturating_add(stake);
771 });
772
773 let exposure = Exposure { own, others, total };
774 (validator, exposure)
775 })
776 .try_collect()
777 .expect("we only map through support vector which cannot change the size; qed")
778 }
779
780 pub(crate) fn kill_stash(stash: &T::AccountId, num_slashing_spans: u32) -> DispatchResult {
788 slashing::clear_stash_metadata::<T>(&stash, num_slashing_spans)?;
789
790 StakingLedger::<T>::kill(&stash)?;
793
794 Self::do_remove_validator(&stash);
795 Self::do_remove_nominator(&stash);
796
797 Ok(())
798 }
799
800 pub(crate) fn clear_era_information(era_index: EraIndex) {
802 let mut cursor = <ErasStakers<T>>::clear_prefix(era_index, u32::MAX, None);
805 debug_assert!(cursor.maybe_cursor.is_none());
806 cursor = <ErasStakersClipped<T>>::clear_prefix(era_index, u32::MAX, None);
807 debug_assert!(cursor.maybe_cursor.is_none());
808 cursor = <ErasValidatorPrefs<T>>::clear_prefix(era_index, u32::MAX, None);
809 debug_assert!(cursor.maybe_cursor.is_none());
810 cursor = <ClaimedRewards<T>>::clear_prefix(era_index, u32::MAX, None);
811 debug_assert!(cursor.maybe_cursor.is_none());
812 cursor = <ErasStakersPaged<T>>::clear_prefix((era_index,), u32::MAX, None);
813 debug_assert!(cursor.maybe_cursor.is_none());
814 cursor = <ErasStakersOverview<T>>::clear_prefix(era_index, u32::MAX, None);
815 debug_assert!(cursor.maybe_cursor.is_none());
816
817 <ErasValidatorReward<T>>::remove(era_index);
818 <ErasRewardPoints<T>>::remove(era_index);
819 <ErasTotalStake<T>>::remove(era_index);
820 ErasStartSessionIndex::<T>::remove(era_index);
821 }
822
823 fn apply_unapplied_slashes(active_era: EraIndex) {
825 let era_slashes = UnappliedSlashes::<T>::take(&active_era);
826 log!(
827 debug,
828 "found {} slashes scheduled to be executed in era {:?}",
829 era_slashes.len(),
830 active_era,
831 );
832 for slash in era_slashes {
833 let slash_era = active_era.saturating_sub(T::SlashDeferDuration::get());
834 slashing::apply_slash::<T>(slash, slash_era);
835 }
836 }
837
838 fn reward_by_ids(validators_points: impl IntoIterator<Item = (T::AccountId, u32)>) {
850 if let Some(active_era) = ActiveEra::<T>::get() {
851 <ErasRewardPoints<T>>::mutate(active_era.index, |era_rewards| {
852 for (validator, points) in validators_points.into_iter() {
853 *era_rewards.individual.entry(validator).or_default() += points;
854 era_rewards.total += points;
855 }
856 });
857 }
858 }
859
860 pub(crate) fn set_force_era(mode: Forcing) {
862 log!(info, "Setting force era mode {:?}.", mode);
863 ForceEra::<T>::put(mode);
864 Self::deposit_event(Event::<T>::ForceEra { mode });
865 }
866
867 #[cfg(feature = "runtime-benchmarks")]
868 pub fn add_era_stakers(
869 current_era: EraIndex,
870 stash: T::AccountId,
871 exposure: Exposure<T::AccountId, BalanceOf<T>>,
872 ) {
873 EraInfo::<T>::set_exposure(current_era, &stash, exposure);
874 }
875
876 #[cfg(feature = "runtime-benchmarks")]
877 pub fn set_slash_reward_fraction(fraction: Perbill) {
878 SlashRewardFraction::<T>::put(fraction);
879 }
880
881 pub fn get_npos_voters(bounds: DataProviderBounds) -> Vec<VoterOf<Self>> {
890 let mut voters_size_tracker: StaticTracker<Self> = StaticTracker::default();
891
892 let final_predicted_len = {
893 let all_voter_count = T::VoterList::count();
894 bounds.count.unwrap_or(all_voter_count.into()).min(all_voter_count.into()).0
895 };
896
897 let mut all_voters = Vec::<_>::with_capacity(final_predicted_len as usize);
898
899 let weight_of = Self::weight_of_fn();
901
902 let mut voters_seen = 0u32;
903 let mut validators_taken = 0u32;
904 let mut nominators_taken = 0u32;
905 let mut min_active_stake = u64::MAX;
906
907 let mut sorted_voters = T::VoterList::iter();
908 while all_voters.len() < final_predicted_len as usize &&
909 voters_seen < (NPOS_MAX_ITERATIONS_COEFFICIENT * final_predicted_len as u32)
910 {
911 let voter = match sorted_voters.next() {
912 Some(voter) => {
913 voters_seen.saturating_inc();
914 voter
915 },
916 None => break,
917 };
918
919 let voter_weight = weight_of(&voter);
920 if voter_weight.is_zero() {
922 log!(debug, "voter's active balance is 0. skip this voter.");
923 continue;
924 }
925
926 if let Some(Nominations { targets, .. }) = <Nominators<T>>::get(&voter) {
927 if !targets.is_empty() {
928 let voter = (voter, voter_weight, targets);
933 if voters_size_tracker.try_register_voter(&voter, &bounds).is_err() {
934 Self::deposit_event(Event::<T>::SnapshotVotersSizeExceeded {
936 size: voters_size_tracker.size as u32,
937 });
938 break;
939 }
940
941 all_voters.push(voter);
942 nominators_taken.saturating_inc();
943 } else {
944 }
946 min_active_stake =
947 if voter_weight < min_active_stake { voter_weight } else { min_active_stake };
948 } else if Validators::<T>::contains_key(&voter) {
949 let self_vote = (
951 voter.clone(),
952 voter_weight,
953 vec![voter.clone()]
954 .try_into()
955 .expect("`MaxVotesPerVoter` must be greater than or equal to 1"),
956 );
957
958 if voters_size_tracker.try_register_voter(&self_vote, &bounds).is_err() {
959 Self::deposit_event(Event::<T>::SnapshotVotersSizeExceeded {
961 size: voters_size_tracker.size as u32,
962 });
963 break;
964 }
965 all_voters.push(self_vote);
966 validators_taken.saturating_inc();
967 } else {
968 defensive!(
974 "DEFENSIVE: invalid item in `VoterList`: {:?}, this nominator probably has too many nominations now",
975 voter,
976 );
977 }
978 }
979
980 debug_assert!(all_voters.capacity() == final_predicted_len as usize);
982
983 Self::register_weight(T::WeightInfo::get_npos_voters(validators_taken, nominators_taken));
984
985 let min_active_stake: T::CurrencyBalance =
986 if all_voters.is_empty() { Zero::zero() } else { min_active_stake.into() };
987
988 MinimumActiveStake::<T>::put(min_active_stake);
989
990 log!(
991 debug,
992 "generated {} npos voters, {} from validators and {} nominators",
993 all_voters.len(),
994 validators_taken,
995 nominators_taken
996 );
997
998 all_voters
999 }
1000
1001 pub fn get_npos_targets(bounds: DataProviderBounds) -> Vec<T::AccountId> {
1005 let mut targets_size_tracker: StaticTracker<Self> = StaticTracker::default();
1006
1007 let final_predicted_len = {
1008 let all_target_count = T::TargetList::count();
1009 bounds.count.unwrap_or(all_target_count.into()).min(all_target_count.into()).0
1010 };
1011
1012 let mut all_targets = Vec::<T::AccountId>::with_capacity(final_predicted_len as usize);
1013 let mut targets_seen = 0;
1014
1015 let mut targets_iter = T::TargetList::iter();
1016 while all_targets.len() < final_predicted_len as usize &&
1017 targets_seen < (NPOS_MAX_ITERATIONS_COEFFICIENT * final_predicted_len as u32)
1018 {
1019 let target = match targets_iter.next() {
1020 Some(target) => {
1021 targets_seen.saturating_inc();
1022 target
1023 },
1024 None => break,
1025 };
1026
1027 if targets_size_tracker.try_register_target(target.clone(), &bounds).is_err() {
1028 Self::deposit_event(Event::<T>::SnapshotTargetsSizeExceeded {
1030 size: targets_size_tracker.size as u32,
1031 });
1032 break;
1033 }
1034
1035 if Validators::<T>::contains_key(&target) {
1036 all_targets.push(target);
1037 }
1038 }
1039
1040 Self::register_weight(T::WeightInfo::get_npos_targets(all_targets.len() as u32));
1041 log!(debug, "generated {} npos targets", all_targets.len());
1042
1043 all_targets
1044 }
1045
1046 pub fn do_add_nominator(who: &T::AccountId, nominations: Nominations<T>) {
1055 if !Nominators::<T>::contains_key(who) {
1056 let _ = T::VoterList::on_insert(who.clone(), Self::weight_of(who))
1058 .defensive_unwrap_or_default();
1059 }
1060 Nominators::<T>::insert(who, nominations);
1061
1062 debug_assert_eq!(
1063 Nominators::<T>::count() + Validators::<T>::count(),
1064 T::VoterList::count()
1065 );
1066 }
1067
1068 pub fn do_remove_nominator(who: &T::AccountId) -> bool {
1077 let outcome = if Nominators::<T>::contains_key(who) {
1078 Nominators::<T>::remove(who);
1079 let _ = T::VoterList::on_remove(who).defensive();
1080 true
1081 } else {
1082 false
1083 };
1084
1085 debug_assert_eq!(
1086 Nominators::<T>::count() + Validators::<T>::count(),
1087 T::VoterList::count()
1088 );
1089
1090 outcome
1091 }
1092
1093 pub fn do_add_validator(who: &T::AccountId, prefs: ValidatorPrefs) {
1101 if !Validators::<T>::contains_key(who) {
1102 let _ = T::VoterList::on_insert(who.clone(), Self::weight_of(who))
1104 .defensive_unwrap_or_default();
1105 }
1106 Validators::<T>::insert(who, prefs);
1107
1108 debug_assert_eq!(
1109 Nominators::<T>::count() + Validators::<T>::count(),
1110 T::VoterList::count()
1111 );
1112 }
1113
1114 pub fn do_remove_validator(who: &T::AccountId) -> bool {
1122 let outcome = if Validators::<T>::contains_key(who) {
1123 Validators::<T>::remove(who);
1124 let _ = T::VoterList::on_remove(who).defensive();
1125 true
1126 } else {
1127 false
1128 };
1129
1130 debug_assert_eq!(
1131 Nominators::<T>::count() + Validators::<T>::count(),
1132 T::VoterList::count()
1133 );
1134
1135 outcome
1136 }
1137
1138 fn register_weight(weight: Weight) {
1142 <frame_system::Pallet<T>>::register_extra_weight_unchecked(
1143 weight,
1144 DispatchClass::Mandatory,
1145 );
1146 }
1147
1148 pub fn eras_stakers(
1154 era: EraIndex,
1155 account: &T::AccountId,
1156 ) -> Exposure<T::AccountId, BalanceOf<T>> {
1157 EraInfo::<T>::get_full_exposure(era, account)
1158 }
1159
1160 pub(super) fn do_migrate_currency(stash: &T::AccountId) -> DispatchResult {
1161 if Self::is_virtual_staker(stash) {
1162 return Self::do_migrate_virtual_staker(stash);
1163 }
1164
1165 let locked: BalanceOf<T> = T::OldCurrency::balance_locked(STAKING_ID, stash).into();
1166 ensure!(!locked.is_zero(), Error::<T>::AlreadyMigrated);
1167
1168 T::OldCurrency::remove_lock(STAKING_ID, &stash);
1170
1171 frame_system::Pallet::<T>::dec_consumers(&stash);
1173
1174 let Ok(ledger) = Self::ledger(Stash(stash.clone())) else {
1175 return Ok(());
1177 };
1178
1179 let max_hold = asset::stakeable_balance::<T>(&stash);
1181 let force_withdraw = if max_hold >= ledger.total {
1182 asset::update_stake::<T>(&stash, ledger.total)?;
1184 Zero::zero()
1185 } else {
1186 let old_total = ledger.total;
1189 let updated_ledger = ledger.update_total_stake(max_hold);
1191
1192 let new_total = updated_ledger.total;
1194 debug_assert_eq!(new_total, max_hold);
1195
1196 updated_ledger.update()?;
1198
1199 old_total.defensive_saturating_sub(new_total)
1201 };
1202
1203 Self::deposit_event(Event::<T>::CurrencyMigrated { stash: stash.clone(), force_withdraw });
1204 Ok(())
1205 }
1206
1207 fn do_migrate_virtual_staker(stash: &T::AccountId) -> DispatchResult {
1211 let consumer_count = frame_system::Pallet::<T>::consumers(stash);
1212 ensure!(consumer_count > 0, Error::<T>::AlreadyMigrated);
1214
1215 ensure!(consumer_count <= 2, Error::<T>::BadState);
1219
1220 for _ in 0..consumer_count {
1222 frame_system::Pallet::<T>::dec_consumers(&stash);
1223 }
1224
1225 let actual_providers = frame_system::Pallet::<T>::providers(stash);
1227
1228 let expected_providers =
1229 if asset::free_to_stake::<T>(&stash) >= asset::existential_deposit::<T>() {
1233 2
1234 } else {
1235 1
1236 };
1237
1238 ensure!(actual_providers <= expected_providers, Error::<T>::BadState);
1240
1241 ensure!(actual_providers == expected_providers, Error::<T>::AlreadyMigrated);
1243
1244 frame_system::Pallet::<T>::dec_providers(&stash)?;
1245
1246 Ok(())
1247 }
1248
1249 pub fn on_offence(
1250 offenders: impl Iterator<Item = OffenceDetails<T::AccountId, T::AccountId>>,
1251 slash_fractions: &[Perbill],
1252 slash_session: SessionIndex,
1253 ) -> Weight {
1254 let reward_proportion = SlashRewardFraction::<T>::get();
1255 let mut consumed_weight = Weight::from_parts(0, 0);
1256 let mut add_db_reads_writes = |reads, writes| {
1257 consumed_weight += T::DbWeight::get().reads_writes(reads, writes);
1258 };
1259
1260 let active_era = {
1261 let active_era = ActiveEra::<T>::get();
1262 add_db_reads_writes(1, 0);
1263 if active_era.is_none() {
1264 log!(warn, "🦹 on_offence: Active era not set -- not processing offence");
1265 return consumed_weight;
1267 }
1268 active_era.expect("value checked not to be `None`; qed").index
1269 };
1270 let active_era_start_session_index = ErasStartSessionIndex::<T>::get(active_era)
1271 .unwrap_or_else(|| {
1272 log!(error, "🦹 on_offence: start_session_index must be set for current_era");
1273 0
1274 });
1275 add_db_reads_writes(1, 0);
1276
1277 let window_start = active_era.saturating_sub(T::BondingDuration::get());
1278
1279 let slash_era = if slash_session >= active_era_start_session_index {
1282 active_era
1283 } else {
1284 let eras = BondedEras::<T>::get();
1285 add_db_reads_writes(1, 0);
1286
1287 match eras.iter().rev().find(|&(_, sesh)| sesh <= &slash_session) {
1289 Some((slash_era, _)) => *slash_era,
1290 None => {
1292 log!(warn, "🦹 on_offence: bonded era not found");
1293 return consumed_weight;
1294 },
1295 }
1296 };
1297
1298 add_db_reads_writes(1, 1);
1299
1300 let slash_defer_duration = T::SlashDeferDuration::get();
1301
1302 let invulnerables = Invulnerables::<T>::get();
1303 add_db_reads_writes(1, 0);
1304
1305 for (details, slash_fraction) in offenders.zip(slash_fractions) {
1306 let stash = &details.offender;
1307 let exposure = Self::eras_stakers(slash_era, stash);
1308
1309 if invulnerables.contains(stash) {
1311 continue;
1312 }
1313
1314 Self::deposit_event(Event::<T>::SlashReported {
1315 validator: stash.clone(),
1316 fraction: *slash_fraction,
1317 slash_era,
1318 });
1319
1320 if slash_era == active_era {
1321 add_db_reads_writes(2, 2);
1324 T::SessionInterface::report_offence(
1325 stash.clone(),
1326 crate::OffenceSeverity(*slash_fraction),
1327 );
1328 }
1329
1330 let unapplied = slashing::compute_slash::<T>(slashing::SlashParams {
1331 stash,
1332 slash: *slash_fraction,
1333 exposure: &exposure,
1334 slash_era,
1335 window_start,
1336 now: active_era,
1337 reward_proportion,
1338 });
1339
1340 if let Some(mut unapplied) = unapplied {
1341 let nominators_len = unapplied.others.len() as u64;
1342 let reporters_len = details.reporters.len() as u64;
1343
1344 {
1345 let upper_bound = 1 + 2 ;
1346 let rw = upper_bound + nominators_len * upper_bound;
1347 add_db_reads_writes(rw, rw);
1348 }
1349 unapplied.reporters = details.reporters.clone();
1350 if slash_defer_duration == 0 {
1351 slashing::apply_slash::<T>(unapplied, slash_era);
1353 {
1354 let slash_cost = (6, 5);
1355 let reward_cost = (2, 2);
1356 add_db_reads_writes(
1357 (1 + nominators_len) * slash_cost.0 + reward_cost.0 * reporters_len,
1358 (1 + nominators_len) * slash_cost.1 + reward_cost.1 * reporters_len,
1359 );
1360 }
1361 } else {
1362 log!(
1364 debug,
1365 "deferring slash of {:?} happened in {:?} (reported in {:?}) to {:?}",
1366 slash_fraction,
1367 slash_era,
1368 active_era,
1369 slash_era + slash_defer_duration + 1,
1370 );
1371 UnappliedSlashes::<T>::mutate(
1372 slash_era.saturating_add(slash_defer_duration).saturating_add(One::one()),
1373 move |for_later| for_later.push(unapplied),
1374 );
1375 add_db_reads_writes(1, 1);
1376 }
1377 } else {
1378 add_db_reads_writes(4 , 5 )
1379 }
1380 }
1381
1382 consumed_weight
1383 }
1384
1385 pub(crate) fn do_unbond(
1387 controller: T::AccountId,
1388 value: BalanceOf<T>,
1389 ) -> Result<Option<Weight>, DispatchError> {
1390 let unlocking = Self::ledger(Controller(controller.clone())).map(|l| l.unlocking.len())?;
1391
1392 let maybe_withdraw_weight = {
1395 if unlocking == T::MaxUnlockingChunks::get() as usize {
1396 let real_num_slashing_spans =
1397 SlashingSpans::<T>::get(&controller).map_or(0, |s| s.iter().count());
1398 Some(Self::do_withdraw_unbonded(&controller, real_num_slashing_spans as u32)?)
1399 } else {
1400 None
1401 }
1402 };
1403
1404 let mut ledger = Self::ledger(Controller(controller))?;
1407 let mut value = value.min(ledger.active);
1408 let stash = ledger.stash.clone();
1409
1410 ensure!(
1411 ledger.unlocking.len() < T::MaxUnlockingChunks::get() as usize,
1412 Error::<T>::NoMoreChunks,
1413 );
1414
1415 if !value.is_zero() {
1416 ledger.active -= value;
1417
1418 if ledger.active < asset::existential_deposit::<T>() {
1420 value += ledger.active;
1421 ledger.active = Zero::zero();
1422 }
1423
1424 let min_active_bond = if Nominators::<T>::contains_key(&stash) {
1425 MinNominatorBond::<T>::get()
1426 } else if Validators::<T>::contains_key(&stash) {
1427 MinValidatorBond::<T>::get()
1428 } else {
1429 Zero::zero()
1430 };
1431
1432 ensure!(ledger.active >= min_active_bond, Error::<T>::InsufficientBond);
1435
1436 let era = CurrentEra::<T>::get()
1438 .unwrap_or(0)
1439 .defensive_saturating_add(T::BondingDuration::get());
1440 if let Some(chunk) = ledger.unlocking.last_mut().filter(|chunk| chunk.era == era) {
1441 chunk.value = chunk.value.defensive_saturating_add(value)
1445 } else {
1446 ledger
1447 .unlocking
1448 .try_push(UnlockChunk { value, era })
1449 .map_err(|_| Error::<T>::NoMoreChunks)?;
1450 };
1451 ledger.update()?;
1453
1454 if T::VoterList::contains(&stash) {
1456 let _ = T::VoterList::on_update(&stash, Self::weight_of(&stash)).defensive();
1457 }
1458
1459 Self::deposit_event(Event::<T>::Unbonded { stash, amount: value });
1460 }
1461
1462 let actual_weight = if let Some(withdraw_weight) = maybe_withdraw_weight {
1463 Some(T::WeightInfo::unbond().saturating_add(withdraw_weight))
1464 } else {
1465 Some(T::WeightInfo::unbond())
1466 };
1467
1468 Ok(actual_weight)
1469 }
1470}
1471
1472impl<T: Config> Pallet<T> {
1473 pub fn api_nominations_quota(balance: BalanceOf<T>) -> u32 {
1477 T::NominationsQuota::get_quota(balance)
1478 }
1479
1480 pub fn api_eras_stakers(
1481 era: EraIndex,
1482 account: T::AccountId,
1483 ) -> Exposure<T::AccountId, BalanceOf<T>> {
1484 Self::eras_stakers(era, &account)
1485 }
1486
1487 pub fn api_eras_stakers_page_count(era: EraIndex, account: T::AccountId) -> Page {
1488 EraInfo::<T>::get_page_count(era, &account)
1489 }
1490
1491 pub fn api_pending_rewards(era: EraIndex, account: T::AccountId) -> bool {
1492 EraInfo::<T>::pending_rewards(era, &account)
1493 }
1494}
1495
1496impl<T: Config> ElectionDataProvider for Pallet<T> {
1497 type AccountId = T::AccountId;
1498 type BlockNumber = BlockNumberFor<T>;
1499 type MaxVotesPerVoter = MaxNominationsOf<T>;
1500
1501 fn desired_targets() -> data_provider::Result<u32> {
1502 Self::register_weight(T::DbWeight::get().reads(1));
1503 Ok(ValidatorCount::<T>::get())
1504 }
1505
1506 fn electing_voters(
1507 bounds: DataProviderBounds,
1508 _page: PageIndex,
1509 ) -> data_provider::Result<Vec<VoterOf<Self>>> {
1510 let voters = Self::get_npos_voters(bounds);
1512
1513 debug_assert!(!bounds.exhausted(
1514 SizeBound(voters.encoded_size() as u32).into(),
1515 CountBound(voters.len() as u32).into()
1516 ));
1517
1518 Ok(voters)
1519 }
1520
1521 fn electable_targets(
1522 bounds: DataProviderBounds,
1523 _page: PageIndex,
1524 ) -> data_provider::Result<Vec<T::AccountId>> {
1525 let targets = Self::get_npos_targets(bounds);
1526
1527 if bounds.exhausted(None, CountBound(T::TargetList::count()).into()) {
1530 return Err("Target snapshot too big");
1531 }
1532
1533 debug_assert!(!bounds.exhausted(
1534 SizeBound(targets.encoded_size() as u32).into(),
1535 CountBound(targets.len() as u32).into()
1536 ));
1537
1538 Ok(targets)
1539 }
1540
1541 fn next_election_prediction(now: BlockNumberFor<T>) -> BlockNumberFor<T> {
1542 let current_era = CurrentEra::<T>::get().unwrap_or(0);
1543 let current_session = CurrentPlannedSession::<T>::get();
1544 let current_era_start_session_index =
1545 ErasStartSessionIndex::<T>::get(current_era).unwrap_or(0);
1546 let era_progress = current_session
1548 .saturating_sub(current_era_start_session_index)
1549 .min(T::SessionsPerEra::get());
1550
1551 let until_this_session_end = T::NextNewSession::estimate_next_new_session(now)
1552 .0
1553 .unwrap_or_default()
1554 .saturating_sub(now);
1555
1556 let session_length = T::NextNewSession::average_session_length();
1557
1558 let sessions_left: BlockNumberFor<T> = match ForceEra::<T>::get() {
1559 Forcing::ForceNone => Bounded::max_value(),
1560 Forcing::ForceNew | Forcing::ForceAlways => Zero::zero(),
1561 Forcing::NotForcing if era_progress >= T::SessionsPerEra::get() => Zero::zero(),
1562 Forcing::NotForcing => T::SessionsPerEra::get()
1563 .saturating_sub(era_progress)
1564 .saturating_sub(1)
1566 .into(),
1567 };
1568
1569 now.saturating_add(
1570 until_this_session_end.saturating_add(sessions_left.saturating_mul(session_length)),
1571 )
1572 }
1573
1574 #[cfg(feature = "runtime-benchmarks")]
1575 fn add_voter(
1576 voter: T::AccountId,
1577 weight: VoteWeight,
1578 targets: BoundedVec<T::AccountId, Self::MaxVotesPerVoter>,
1579 ) {
1580 let stake = <BalanceOf<T>>::try_from(weight).unwrap_or_else(|_| {
1581 panic!("cannot convert a VoteWeight into BalanceOf, benchmark needs reconfiguring.")
1582 });
1583 <Bonded<T>>::insert(voter.clone(), voter.clone());
1584 <Ledger<T>>::insert(voter.clone(), StakingLedger::<T>::new(voter.clone(), stake));
1585
1586 Self::do_add_nominator(&voter, Nominations { targets, submitted_in: 0, suppressed: false });
1587 }
1588
1589 #[cfg(feature = "runtime-benchmarks")]
1590 fn add_target(target: T::AccountId) {
1591 let stake = MinValidatorBond::<T>::get() * 100u32.into();
1592 <Bonded<T>>::insert(target.clone(), target.clone());
1593 <Ledger<T>>::insert(target.clone(), StakingLedger::<T>::new(target.clone(), stake));
1594 Self::do_add_validator(
1595 &target,
1596 ValidatorPrefs { commission: Perbill::zero(), blocked: false },
1597 );
1598 }
1599
1600 #[cfg(feature = "runtime-benchmarks")]
1601 fn clear() {
1602 #[allow(deprecated)]
1603 <Bonded<T>>::remove_all(None);
1604 #[allow(deprecated)]
1605 <Ledger<T>>::remove_all(None);
1606 #[allow(deprecated)]
1607 <Validators<T>>::remove_all();
1608 #[allow(deprecated)]
1609 <Nominators<T>>::remove_all();
1610
1611 T::VoterList::unsafe_clear();
1612 }
1613
1614 #[cfg(feature = "runtime-benchmarks")]
1615 fn put_snapshot(
1616 voters: Vec<VoterOf<Self>>,
1617 targets: Vec<T::AccountId>,
1618 target_stake: Option<VoteWeight>,
1619 ) {
1620 targets.into_iter().for_each(|v| {
1621 let stake: BalanceOf<T> = target_stake
1622 .and_then(|w| <BalanceOf<T>>::try_from(w).ok())
1623 .unwrap_or_else(|| MinNominatorBond::<T>::get() * 100u32.into());
1624 <Bonded<T>>::insert(v.clone(), v.clone());
1625 <Ledger<T>>::insert(v.clone(), StakingLedger::<T>::new(v.clone(), stake));
1626 Self::do_add_validator(
1627 &v,
1628 ValidatorPrefs { commission: Perbill::zero(), blocked: false },
1629 );
1630 });
1631
1632 voters.into_iter().for_each(|(v, s, t)| {
1633 let stake = <BalanceOf<T>>::try_from(s).unwrap_or_else(|_| {
1634 panic!("cannot convert a VoteWeight into BalanceOf, benchmark needs reconfiguring.")
1635 });
1636 <Bonded<T>>::insert(v.clone(), v.clone());
1637 <Ledger<T>>::insert(v.clone(), StakingLedger::<T>::new(v.clone(), stake));
1638 Self::do_add_nominator(
1639 &v,
1640 Nominations { targets: t, submitted_in: 0, suppressed: false },
1641 );
1642 });
1643 }
1644}
1645
1646impl<T: Config> pallet_session::SessionManager<T::AccountId> for Pallet<T> {
1652 fn new_session(new_index: SessionIndex) -> Option<Vec<T::AccountId>> {
1653 log!(trace, "planning new session {}", new_index);
1654 CurrentPlannedSession::<T>::put(new_index);
1655 Self::new_session(new_index, false).map(|v| v.into_inner())
1656 }
1657 fn new_session_genesis(new_index: SessionIndex) -> Option<Vec<T::AccountId>> {
1658 log!(trace, "planning new session {} at genesis", new_index);
1659 CurrentPlannedSession::<T>::put(new_index);
1660 Self::new_session(new_index, true).map(|v| v.into_inner())
1661 }
1662 fn start_session(start_index: SessionIndex) {
1663 log!(trace, "starting session {}", start_index);
1664 Self::start_session(start_index)
1665 }
1666 fn end_session(end_index: SessionIndex) {
1667 log!(trace, "ending session {}", end_index);
1668 Self::end_session(end_index)
1669 }
1670}
1671
1672impl<T: Config> historical::SessionManager<T::AccountId, Exposure<T::AccountId, BalanceOf<T>>>
1673 for Pallet<T>
1674{
1675 fn new_session(
1676 new_index: SessionIndex,
1677 ) -> Option<Vec<(T::AccountId, Exposure<T::AccountId, BalanceOf<T>>)>> {
1678 <Self as pallet_session::SessionManager<_>>::new_session(new_index).map(|validators| {
1679 validators
1680 .into_iter()
1681 .map(|v| {
1682 let exposure = Exposure::<T::AccountId, BalanceOf<T>>::default();
1683 (v, exposure)
1684 })
1685 .collect()
1686 })
1687 }
1688 fn new_session_genesis(
1689 new_index: SessionIndex,
1690 ) -> Option<Vec<(T::AccountId, Exposure<T::AccountId, BalanceOf<T>>)>> {
1691 <Self as pallet_session::SessionManager<_>>::new_session_genesis(new_index).map(
1692 |validators| {
1693 validators
1694 .into_iter()
1695 .map(|v| {
1696 let exposure = Exposure::<T::AccountId, BalanceOf<T>>::default();
1697 (v, exposure)
1698 })
1699 .collect()
1700 },
1701 )
1702 }
1703 fn start_session(start_index: SessionIndex) {
1704 <Self as pallet_session::SessionManager<_>>::start_session(start_index)
1705 }
1706 fn end_session(end_index: SessionIndex) {
1707 <Self as pallet_session::SessionManager<_>>::end_session(end_index)
1708 }
1709}
1710
1711impl<T: Config> historical::SessionManager<T::AccountId, ()> for Pallet<T> {
1712 fn new_session(new_index: SessionIndex) -> Option<Vec<(T::AccountId, ())>> {
1713 <Self as pallet_session::SessionManager<_>>::new_session(new_index)
1714 .map(|validators| validators.into_iter().map(|v| (v, ())).collect())
1715 }
1716 fn new_session_genesis(new_index: SessionIndex) -> Option<Vec<(T::AccountId, ())>> {
1717 <Self as pallet_session::SessionManager<_>>::new_session_genesis(new_index)
1718 .map(|validators| validators.into_iter().map(|v| (v, ())).collect())
1719 }
1720 fn start_session(start_index: SessionIndex) {
1721 <Self as pallet_session::SessionManager<_>>::start_session(start_index)
1722 }
1723 fn end_session(end_index: SessionIndex) {
1724 <Self as pallet_session::SessionManager<_>>::end_session(end_index)
1725 }
1726}
1727
1728impl<T> pallet_authorship::EventHandler<T::AccountId, BlockNumberFor<T>> for Pallet<T>
1731where
1732 T: Config + pallet_authorship::Config + pallet_session::Config,
1733{
1734 fn note_author(author: T::AccountId) {
1735 <Self as RewardsReporter<T::AccountId>>::reward_by_ids(vec![(author, 20)])
1736 }
1737}
1738
1739impl<T: Config>
1741 OnOffenceHandler<T::AccountId, pallet_session::historical::IdentificationTuple<T>, Weight>
1742 for Pallet<T>
1743where
1744 T: pallet_session::Config<ValidatorId = <T as frame_system::Config>::AccountId>,
1745 T: pallet_session::historical::Config,
1746 T::SessionHandler: pallet_session::SessionHandler<<T as frame_system::Config>::AccountId>,
1747 T::SessionManager: pallet_session::SessionManager<<T as frame_system::Config>::AccountId>,
1748 T::ValidatorIdOf: Convert<
1749 <T as frame_system::Config>::AccountId,
1750 Option<<T as frame_system::Config>::AccountId>,
1751 >,
1752{
1753 fn on_offence(
1754 offenders: &[OffenceDetails<
1755 T::AccountId,
1756 pallet_session::historical::IdentificationTuple<T>,
1757 >],
1758 slash_fractions: &[Perbill],
1759 slash_session: SessionIndex,
1760 ) -> Weight {
1761 log!(
1762 debug,
1763 "🦹 on_offence: offenders={:?}, slash_fractions={:?}, slash_session={}",
1764 offenders,
1765 slash_fractions,
1766 slash_session,
1767 );
1768
1769 let offenders = offenders.iter().map(|details| {
1771 let (ref offender, _) = details.offender;
1772 OffenceDetails { offender: offender.clone(), reporters: details.reporters.clone() }
1773 });
1774
1775 Self::on_offence(offenders, slash_fractions, slash_session)
1776 }
1777}
1778
1779impl<T: Config> ScoreProvider<T::AccountId> for Pallet<T> {
1780 type Score = VoteWeight;
1781
1782 fn score(who: &T::AccountId) -> Option<Self::Score> {
1783 Self::ledger(Stash(who.clone()))
1784 .map(|l| l.active)
1785 .map(|a| {
1786 let issuance = asset::total_issuance::<T>();
1787 T::CurrencyToVote::to_vote(a, issuance)
1788 })
1789 .ok()
1790 }
1791
1792 #[cfg(feature = "runtime-benchmarks")]
1793 fn set_score_of(who: &T::AccountId, weight: Self::Score) {
1794 let active: BalanceOf<T> = weight.try_into().map_err(|_| ()).unwrap();
1797 let mut ledger = match Self::ledger(StakingAccount::Stash(who.clone())) {
1798 Ok(l) => l,
1799 Err(_) => StakingLedger::default_from(who.clone()),
1800 };
1801 ledger.active = active;
1802
1803 <Ledger<T>>::insert(who, ledger);
1804 <Bonded<T>>::insert(who, who);
1805
1806 let imbalance = asset::burn::<T>(asset::total_issuance::<T>());
1810 core::mem::forget(imbalance);
1813 }
1814}
1815
1816pub struct UseValidatorsMap<T>(core::marker::PhantomData<T>);
1820impl<T: Config> SortedListProvider<T::AccountId> for UseValidatorsMap<T> {
1821 type Score = BalanceOf<T>;
1822 type Error = ();
1823
1824 fn iter() -> Box<dyn Iterator<Item = T::AccountId>> {
1826 Box::new(Validators::<T>::iter().map(|(v, _)| v))
1827 }
1828 fn iter_from(
1829 start: &T::AccountId,
1830 ) -> Result<Box<dyn Iterator<Item = T::AccountId>>, Self::Error> {
1831 if Validators::<T>::contains_key(start) {
1832 let start_key = Validators::<T>::hashed_key_for(start);
1833 Ok(Box::new(Validators::<T>::iter_from(start_key).map(|(n, _)| n)))
1834 } else {
1835 Err(())
1836 }
1837 }
1838 fn count() -> u32 {
1839 Validators::<T>::count()
1840 }
1841 fn contains(id: &T::AccountId) -> bool {
1842 Validators::<T>::contains_key(id)
1843 }
1844 fn on_insert(_: T::AccountId, _weight: Self::Score) -> Result<(), Self::Error> {
1845 Ok(())
1847 }
1848 fn get_score(id: &T::AccountId) -> Result<Self::Score, Self::Error> {
1849 Ok(Pallet::<T>::weight_of(id).into())
1850 }
1851 fn on_update(_: &T::AccountId, _weight: Self::Score) -> Result<(), Self::Error> {
1852 Ok(())
1854 }
1855 fn on_remove(_: &T::AccountId) -> Result<(), Self::Error> {
1856 Ok(())
1858 }
1859 fn unsafe_regenerate(
1860 _: impl IntoIterator<Item = T::AccountId>,
1861 _: Box<dyn Fn(&T::AccountId) -> Option<Self::Score>>,
1862 ) -> u32 {
1863 0
1865 }
1866 #[cfg(feature = "try-runtime")]
1867 fn try_state() -> Result<(), TryRuntimeError> {
1868 Ok(())
1869 }
1870
1871 fn unsafe_clear() {
1872 #[allow(deprecated)]
1873 Validators::<T>::remove_all();
1874 }
1875
1876 #[cfg(feature = "runtime-benchmarks")]
1877 fn score_update_worst_case(_who: &T::AccountId, _is_increase: bool) -> Self::Score {
1878 unimplemented!()
1879 }
1880
1881 fn lock() {}
1882
1883 fn unlock() {}
1884}
1885
1886pub struct UseNominatorsAndValidatorsMap<T>(core::marker::PhantomData<T>);
1890impl<T: Config> SortedListProvider<T::AccountId> for UseNominatorsAndValidatorsMap<T> {
1891 type Error = ();
1892 type Score = VoteWeight;
1893
1894 fn iter() -> Box<dyn Iterator<Item = T::AccountId>> {
1895 Box::new(
1896 Validators::<T>::iter()
1897 .map(|(v, _)| v)
1898 .chain(Nominators::<T>::iter().map(|(n, _)| n)),
1899 )
1900 }
1901 fn iter_from(
1902 start: &T::AccountId,
1903 ) -> Result<Box<dyn Iterator<Item = T::AccountId>>, Self::Error> {
1904 if Validators::<T>::contains_key(start) {
1905 let start_key = Validators::<T>::hashed_key_for(start);
1906 Ok(Box::new(
1907 Validators::<T>::iter_from(start_key)
1908 .map(|(n, _)| n)
1909 .chain(Nominators::<T>::iter().map(|(x, _)| x)),
1910 ))
1911 } else if Nominators::<T>::contains_key(start) {
1912 let start_key = Nominators::<T>::hashed_key_for(start);
1913 Ok(Box::new(Nominators::<T>::iter_from(start_key).map(|(n, _)| n)))
1914 } else {
1915 Err(())
1916 }
1917 }
1918 fn count() -> u32 {
1919 Nominators::<T>::count().saturating_add(Validators::<T>::count())
1920 }
1921 fn contains(id: &T::AccountId) -> bool {
1922 Nominators::<T>::contains_key(id) || Validators::<T>::contains_key(id)
1923 }
1924 fn on_insert(_: T::AccountId, _weight: Self::Score) -> Result<(), Self::Error> {
1925 Ok(())
1927 }
1928 fn get_score(id: &T::AccountId) -> Result<Self::Score, Self::Error> {
1929 Ok(Pallet::<T>::weight_of(id))
1930 }
1931 fn on_update(_: &T::AccountId, _weight: Self::Score) -> Result<(), Self::Error> {
1932 Ok(())
1934 }
1935 fn on_remove(_: &T::AccountId) -> Result<(), Self::Error> {
1936 Ok(())
1938 }
1939 fn unsafe_regenerate(
1940 _: impl IntoIterator<Item = T::AccountId>,
1941 _: Box<dyn Fn(&T::AccountId) -> Option<Self::Score>>,
1942 ) -> u32 {
1943 0
1945 }
1946
1947 #[cfg(feature = "try-runtime")]
1948 fn try_state() -> Result<(), TryRuntimeError> {
1949 Ok(())
1950 }
1951
1952 fn unsafe_clear() {
1953 #[allow(deprecated)]
1956 Nominators::<T>::remove_all();
1957 #[allow(deprecated)]
1958 Validators::<T>::remove_all();
1959 }
1960
1961 #[cfg(feature = "runtime-benchmarks")]
1962 fn score_update_worst_case(_who: &T::AccountId, _is_increase: bool) -> Self::Score {
1963 unimplemented!()
1964 }
1965
1966 fn lock() {}
1967
1968 fn unlock() {}
1969}
1970
1971impl<T: Config> StakingInterface for Pallet<T> {
1972 type AccountId = T::AccountId;
1973 type Balance = BalanceOf<T>;
1974 type CurrencyToVote = T::CurrencyToVote;
1975
1976 fn minimum_nominator_bond() -> Self::Balance {
1977 MinNominatorBond::<T>::get()
1978 }
1979
1980 fn minimum_validator_bond() -> Self::Balance {
1981 MinValidatorBond::<T>::get()
1982 }
1983
1984 fn stash_by_ctrl(controller: &Self::AccountId) -> Result<Self::AccountId, DispatchError> {
1985 Self::ledger(Controller(controller.clone()))
1986 .map(|l| l.stash)
1987 .map_err(|e| e.into())
1988 }
1989
1990 fn bonding_duration() -> EraIndex {
1991 T::BondingDuration::get()
1992 }
1993
1994 fn current_era() -> EraIndex {
1995 CurrentEra::<T>::get().unwrap_or(Zero::zero())
1996 }
1997
1998 fn stake(who: &Self::AccountId) -> Result<Stake<BalanceOf<T>>, DispatchError> {
1999 Self::ledger(Stash(who.clone()))
2000 .map(|l| Stake { total: l.total, active: l.active })
2001 .map_err(|e| e.into())
2002 }
2003
2004 fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult {
2005 Self::bond_extra(RawOrigin::Signed(who.clone()).into(), extra)
2006 }
2007
2008 fn unbond(who: &Self::AccountId, value: Self::Balance) -> DispatchResult {
2009 let ctrl = Self::bonded(who).ok_or(Error::<T>::NotStash)?;
2010 Self::unbond(RawOrigin::Signed(ctrl).into(), value)
2011 .map_err(|with_post| with_post.error)
2012 .map(|_| ())
2013 }
2014
2015 fn set_payee(stash: &Self::AccountId, reward_acc: &Self::AccountId) -> DispatchResult {
2016 ensure!(
2020 !Self::is_virtual_staker(stash) || stash != reward_acc,
2021 Error::<T>::RewardDestinationRestricted
2022 );
2023
2024 let ledger = Self::ledger(Stash(stash.clone()))?;
2025 ledger
2026 .set_payee(RewardDestination::Account(reward_acc.clone()))
2027 .defensive_proof("ledger was retrieved from storage, thus its bonded; qed.")?;
2028
2029 Ok(())
2030 }
2031
2032 fn chill(who: &Self::AccountId) -> DispatchResult {
2033 let ctrl = Self::bonded(who).ok_or(Error::<T>::NotStash)?;
2036 Self::chill(RawOrigin::Signed(ctrl).into())
2037 }
2038
2039 fn withdraw_unbonded(
2040 who: Self::AccountId,
2041 num_slashing_spans: u32,
2042 ) -> Result<bool, DispatchError> {
2043 let ctrl = Self::bonded(&who).ok_or(Error::<T>::NotStash)?;
2044 Self::withdraw_unbonded(RawOrigin::Signed(ctrl.clone()).into(), num_slashing_spans)
2045 .map(|_| !StakingLedger::<T>::is_bonded(StakingAccount::Controller(ctrl)))
2046 .map_err(|with_post| with_post.error)
2047 }
2048
2049 fn bond(
2050 who: &Self::AccountId,
2051 value: Self::Balance,
2052 payee: &Self::AccountId,
2053 ) -> DispatchResult {
2054 Self::bond(
2055 RawOrigin::Signed(who.clone()).into(),
2056 value,
2057 RewardDestination::Account(payee.clone()),
2058 )
2059 }
2060
2061 fn nominate(who: &Self::AccountId, targets: Vec<Self::AccountId>) -> DispatchResult {
2062 let ctrl = Self::bonded(who).ok_or(Error::<T>::NotStash)?;
2063 let targets = targets.into_iter().map(T::Lookup::unlookup).collect::<Vec<_>>();
2064 Self::nominate(RawOrigin::Signed(ctrl).into(), targets)
2065 }
2066
2067 fn desired_validator_count() -> u32 {
2068 ValidatorCount::<T>::get()
2069 }
2070
2071 fn election_ongoing() -> bool {
2072 T::ElectionProvider::status().is_ok()
2073 }
2074
2075 fn force_unstake(who: Self::AccountId) -> sp_runtime::DispatchResult {
2076 let num_slashing_spans =
2077 SlashingSpans::<T>::get(&who).map_or(0, |s| s.iter().count() as u32);
2078 Self::force_unstake(RawOrigin::Root.into(), who.clone(), num_slashing_spans)
2079 }
2080
2081 fn is_exposed_in_era(who: &Self::AccountId, era: &EraIndex) -> bool {
2082 ErasStakers::<T>::iter_prefix(era).any(|(validator, exposures)| {
2085 validator == *who || exposures.others.iter().any(|i| i.who == *who)
2086 })
2087 ||
2088 ErasStakersPaged::<T>::iter_prefix((era,)).any(|((validator, _), exposure_page)| {
2090 validator == *who || exposure_page.others.iter().any(|i| i.who == *who)
2091 })
2092 }
2093 fn status(
2094 who: &Self::AccountId,
2095 ) -> Result<sp_staking::StakerStatus<Self::AccountId>, DispatchError> {
2096 if !StakingLedger::<T>::is_bonded(StakingAccount::Stash(who.clone())) {
2097 return Err(Error::<T>::NotStash.into());
2098 }
2099
2100 let is_validator = Validators::<T>::contains_key(&who);
2101 let is_nominator = Nominators::<T>::get(&who);
2102
2103 use sp_staking::StakerStatus;
2104 match (is_validator, is_nominator.is_some()) {
2105 (false, false) => Ok(StakerStatus::Idle),
2106 (true, false) => Ok(StakerStatus::Validator),
2107 (false, true) => Ok(StakerStatus::Nominator(
2108 is_nominator.expect("is checked above; qed").targets.into_inner(),
2109 )),
2110 (true, true) => {
2111 defensive!("cannot be both validators and nominator");
2112 Err(Error::<T>::BadState.into())
2113 },
2114 }
2115 }
2116
2117 fn is_virtual_staker(who: &T::AccountId) -> bool {
2122 frame_system::Pallet::<T>::account_nonce(who).is_zero() &&
2123 VirtualStakers::<T>::contains_key(who)
2124 }
2125
2126 fn slash_reward_fraction() -> Perbill {
2127 SlashRewardFraction::<T>::get()
2128 }
2129
2130 sp_staking::runtime_benchmarks_enabled! {
2131 fn nominations(who: &Self::AccountId) -> Option<Vec<T::AccountId>> {
2132 Nominators::<T>::get(who).map(|n| n.targets.into_inner())
2133 }
2134
2135 fn add_era_stakers(
2136 current_era: &EraIndex,
2137 stash: &T::AccountId,
2138 exposures: Vec<(Self::AccountId, Self::Balance)>,
2139 ) {
2140 let others = exposures
2141 .iter()
2142 .map(|(who, value)| IndividualExposure { who: who.clone(), value: *value })
2143 .collect::<Vec<_>>();
2144 let exposure = Exposure { total: Default::default(), own: Default::default(), others };
2145 EraInfo::<T>::set_exposure(*current_era, stash, exposure);
2146 }
2147
2148 fn max_exposure_page_size() -> Page {
2149 T::MaxExposurePageSize::get()
2150 }
2151 }
2152
2153 sp_staking::std_or_benchmarks_enabled! {
2154 fn set_era(era: EraIndex) {
2155 ActiveEra::<T>::put(ActiveEraInfo { index: era, start: None });
2156 CurrentEra::<T>::put(era);
2157 }
2158 }
2159}
2160
2161impl<T: Config> sp_staking::StakingUnchecked for Pallet<T> {
2162 fn migrate_to_virtual_staker(who: &Self::AccountId) -> DispatchResult {
2163 asset::kill_stake::<T>(who)?;
2164 VirtualStakers::<T>::insert(who, ());
2165 Ok(())
2166 }
2167
2168 fn virtual_bond(
2172 keyless_who: &Self::AccountId,
2173 value: Self::Balance,
2174 payee: &Self::AccountId,
2175 ) -> DispatchResult {
2176 if StakingLedger::<T>::is_bonded(StakingAccount::Stash(keyless_who.clone())) {
2177 return Err(Error::<T>::AlreadyBonded.into());
2178 }
2179
2180 ensure!(keyless_who != payee, Error::<T>::RewardDestinationRestricted);
2182
2183 VirtualStakers::<T>::insert(keyless_who, ());
2185
2186 Self::deposit_event(Event::<T>::Bonded { stash: keyless_who.clone(), amount: value });
2187 let ledger = StakingLedger::<T>::new(keyless_who.clone(), value);
2188
2189 ledger.bond(RewardDestination::Account(payee.clone()))?;
2190
2191 Ok(())
2192 }
2193
2194 #[cfg(feature = "runtime-benchmarks")]
2196 fn migrate_to_direct_staker(who: &Self::AccountId) {
2197 assert!(VirtualStakers::<T>::contains_key(who));
2198 let ledger = StakingLedger::<T>::get(Stash(who.clone())).unwrap();
2199 let _ = asset::update_stake::<T>(who, ledger.total)
2200 .expect("funds must be transferred to stash");
2201 VirtualStakers::<T>::remove(who);
2202 }
2203}
2204
2205impl<T: Config> RewardsReporter<T::AccountId> for Pallet<T> {
2206 fn reward_by_ids(validators_points: impl IntoIterator<Item = (T::AccountId, u32)>) {
2207 Self::reward_by_ids(validators_points)
2208 }
2209}
2210
2211#[cfg(any(test, feature = "try-runtime"))]
2212impl<T: Config> Pallet<T> {
2213 pub(crate) fn do_try_state(_: BlockNumberFor<T>) -> Result<(), TryRuntimeError> {
2214 ensure!(
2215 T::VoterList::iter()
2216 .all(|x| <Nominators<T>>::contains_key(&x) || <Validators<T>>::contains_key(&x)),
2217 "VoterList contains non-staker"
2218 );
2219
2220 use frame_support::traits::fungible::Inspect;
2221 if T::CurrencyToVote::will_downscale(T::Currency::total_issuance()).map_or(false, |x| x) {
2222 log!(warn, "total issuance will cause T::CurrencyToVote to downscale -- report to maintainers.")
2223 }
2224
2225 Self::check_ledgers()?;
2226 Self::check_bonded_consistency()?;
2227 Self::check_payees()?;
2228 Self::check_nominators()?;
2229 Self::check_exposures()?;
2230 Self::check_paged_exposures()?;
2231 Self::check_count()
2232 }
2233
2234 fn check_bonded_consistency() -> Result<(), TryRuntimeError> {
2244 use alloc::collections::btree_set::BTreeSet;
2245
2246 let mut count_controller_double = 0;
2247 let mut count_double = 0;
2248 let mut count_none = 0;
2249 let mut controllers = BTreeSet::new();
2252
2253 for (stash, controller) in <Bonded<T>>::iter() {
2254 if !controllers.insert(controller.clone()) {
2255 count_controller_double += 1;
2256 }
2257
2258 match (<Ledger<T>>::get(&stash), <Ledger<T>>::get(&controller)) {
2259 (Some(_), Some(_)) =>
2260 {
2264 if stash != controller {
2265 count_double += 1;
2266 }
2267 },
2268 (None, None) => {
2269 count_none += 1;
2270 },
2271 _ => {},
2272 };
2273 }
2274
2275 if count_controller_double != 0 {
2276 log!(
2277 warn,
2278 "a controller is associated with more than one ledger ({} occurrences)",
2279 count_controller_double
2280 );
2281 };
2282
2283 if count_double != 0 {
2284 log!(warn, "single tuple of (stash, controller) pair bonds more than one ledger ({} occurrences)", count_double);
2285 }
2286
2287 if count_none != 0 {
2288 log!(warn, "inconsistent bonded state: (stash, controller) pair missing associated ledger ({} occurrences)", count_none);
2289 }
2290
2291 Ok(())
2292 }
2293
2294 fn check_payees() -> Result<(), TryRuntimeError> {
2299 for (stash, _) in Bonded::<T>::iter() {
2300 ensure!(Payee::<T>::get(&stash).is_some(), "bonded ledger does not have payee set");
2301 }
2302
2303 ensure!(
2304 (Ledger::<T>::iter().count() == Payee::<T>::iter().count()) &&
2305 (Ledger::<T>::iter().count() == Bonded::<T>::iter().count()),
2306 "number of entries in payee storage items does not match the number of bonded ledgers",
2307 );
2308
2309 Ok(())
2310 }
2311
2312 fn check_count() -> Result<(), TryRuntimeError> {
2318 ensure!(
2319 <T as Config>::VoterList::count() ==
2320 Nominators::<T>::count() + Validators::<T>::count(),
2321 "wrong external count"
2322 );
2323 ensure!(
2324 <T as Config>::TargetList::count() == Validators::<T>::count(),
2325 "wrong external count"
2326 );
2327
2328 let max_validators_bound = MaxWinnersOf::<T>::get();
2329 let max_winners_per_page_bound = crate::MaxWinnersPerPageOf::<T::ElectionProvider>::get();
2330
2331 ensure!(
2332 max_validators_bound >= max_winners_per_page_bound,
2333 "max validators should be higher than per page bounds"
2334 );
2335
2336 ensure!(ValidatorCount::<T>::get() <= max_validators_bound, Error::<T>::TooManyValidators);
2337 Ok(())
2338 }
2339
2340 fn check_ledgers() -> Result<(), TryRuntimeError> {
2348 Bonded::<T>::iter()
2349 .map(|(stash, ctrl)| {
2350 if VirtualStakers::<T>::contains_key(stash.clone()) {
2352 ensure!(
2353 asset::staked::<T>(&stash) == Zero::zero(),
2354 "virtual stakers should not have any staked balance"
2355 );
2356 ensure!(
2357 <Bonded<T>>::get(stash.clone()).unwrap() == stash.clone(),
2358 "stash and controller should be same"
2359 );
2360 ensure!(
2361 Ledger::<T>::get(stash.clone()).unwrap().stash == stash,
2362 "ledger corrupted for virtual staker"
2363 );
2364 ensure!(
2365 frame_system::Pallet::<T>::account_nonce(&stash).is_zero(),
2366 "virtual stakers are keyless and should not have any nonce"
2367 );
2368 let reward_destination = <Payee<T>>::get(stash.clone()).unwrap();
2369 if let RewardDestination::Account(payee) = reward_destination {
2370 ensure!(
2371 payee != stash.clone(),
2372 "reward destination should not be same as stash for virtual staker"
2373 );
2374 } else {
2375 return Err(DispatchError::Other(
2376 "reward destination must be of account variant for virtual staker",
2377 ));
2378 }
2379 } else {
2380 ensure!(
2381 Self::inspect_bond_state(&stash) == Ok(LedgerIntegrityState::Ok),
2382 "bond, ledger and/or staking hold inconsistent for a bonded stash."
2383 );
2384 }
2385
2386 Self::ensure_ledger_consistent(ctrl)
2388 })
2389 .collect::<Result<Vec<_>, _>>()?;
2390 Ok(())
2391 }
2392
2393 fn check_exposures() -> Result<(), TryRuntimeError> {
2397 let era = ActiveEra::<T>::get().unwrap().index;
2398 ErasStakers::<T>::iter_prefix_values(era)
2399 .map(|expo| {
2400 ensure!(
2401 expo.total ==
2402 expo.own +
2403 expo.others
2404 .iter()
2405 .map(|e| e.value)
2406 .fold(Zero::zero(), |acc, x| acc + x),
2407 "wrong total exposure.",
2408 );
2409 Ok(())
2410 })
2411 .collect::<Result<(), TryRuntimeError>>()
2412 }
2413
2414 fn check_paged_exposures() -> Result<(), TryRuntimeError> {
2419 use alloc::collections::btree_map::BTreeMap;
2420 use sp_staking::PagedExposureMetadata;
2421
2422 let mut exposures: BTreeMap<T::AccountId, PagedExposureMetadata<BalanceOf<T>>> =
2424 BTreeMap::new();
2425 let era = ActiveEra::<T>::get().unwrap().index;
2426 let accumulator_default = PagedExposureMetadata {
2427 total: Zero::zero(),
2428 own: Zero::zero(),
2429 nominator_count: 0,
2430 page_count: 0,
2431 };
2432
2433 ErasStakersPaged::<T>::iter_prefix((era,))
2434 .map(|((validator, _page), expo)| {
2435 ensure!(
2436 expo.page_total ==
2437 expo.others.iter().map(|e| e.value).fold(Zero::zero(), |acc, x| acc + x),
2438 "wrong total exposure for the page.",
2439 );
2440
2441 let metadata = exposures.get(&validator).unwrap_or(&accumulator_default);
2442 exposures.insert(
2443 validator,
2444 PagedExposureMetadata {
2445 total: metadata.total + expo.page_total,
2446 own: metadata.own,
2447 nominator_count: metadata.nominator_count + expo.others.len() as u32,
2448 page_count: metadata.page_count + 1,
2449 },
2450 );
2451
2452 Ok(())
2453 })
2454 .collect::<Result<(), TryRuntimeError>>()?;
2455
2456 exposures
2457 .iter()
2458 .map(|(validator, metadata)| {
2459 let actual_overview = ErasStakersOverview::<T>::get(era, validator);
2460
2461 ensure!(actual_overview.is_some(), "No overview found for a paged exposure");
2462 let actual_overview = actual_overview.unwrap();
2463
2464 ensure!(
2465 actual_overview.total == metadata.total + actual_overview.own,
2466 "Exposure metadata does not have correct total exposed stake."
2467 );
2468 ensure!(
2469 actual_overview.nominator_count == metadata.nominator_count,
2470 "Exposure metadata does not have correct count of nominators."
2471 );
2472 ensure!(
2473 actual_overview.page_count == metadata.page_count,
2474 "Exposure metadata does not have correct count of pages."
2475 );
2476
2477 Ok(())
2478 })
2479 .collect::<Result<(), TryRuntimeError>>()
2480 }
2481
2482 fn check_nominators() -> Result<(), TryRuntimeError> {
2485 let era = ActiveEra::<T>::get().unwrap().index;
2488
2489 let era_exposures = T::SessionInterface::validators()
2491 .iter()
2492 .map(|v| Self::eras_stakers(era, v))
2493 .collect::<Vec<_>>();
2494
2495 <Nominators<T>>::iter()
2496 .filter_map(
2497 |(nominator, nomination)| {
2498 if nomination.submitted_in < era {
2499 Some(nominator)
2500 } else {
2501 None
2502 }
2503 },
2504 )
2505 .map(|nominator| -> Result<(), TryRuntimeError> {
2506 Self::ensure_is_stash(&nominator)?;
2508 let mut sum_exposed = BalanceOf::<T>::zero();
2509 era_exposures
2510 .iter()
2511 .map(|e| -> Result<(), TryRuntimeError> {
2512 let individual =
2513 e.others.iter().filter(|e| e.who == nominator).collect::<Vec<_>>();
2514 let len = individual.len();
2515 match len {
2516 0 => { },
2517 1 => sum_exposed += individual[0].value,
2518 _ => {
2519 return Err(
2520 "nominator cannot back a validator more than once.".into()
2521 )
2522 },
2523 };
2524 Ok(())
2525 })
2526 .collect::<Result<Vec<_>, _>>()?;
2527
2528 if sum_exposed > Self::ledger(Stash(nominator.clone()))?.total {
2531 log!(
2533 warn,
2534 "nominator {:?} stake {:?} exceeds the sum_exposed of exposures {:?}.",
2535 nominator,
2536 Self::ledger(Stash(nominator.clone()))?.total,
2537 sum_exposed,
2538 );
2539 }
2540
2541 Ok(())
2542 })
2543 .collect::<Result<Vec<_>, _>>()?;
2544
2545 Ok(())
2546 }
2547
2548 fn ensure_is_stash(who: &T::AccountId) -> Result<(), &'static str> {
2549 ensure!(Self::bonded(who).is_some(), "Not a stash.");
2550 Ok(())
2551 }
2552
2553 fn ensure_ledger_consistent(ctrl: T::AccountId) -> Result<(), TryRuntimeError> {
2554 let ledger = Self::ledger(StakingAccount::Controller(ctrl.clone()))?;
2556
2557 let real_total: BalanceOf<T> =
2558 ledger.unlocking.iter().fold(ledger.active, |a, c| a + c.value);
2559 ensure!(real_total == ledger.total, "ledger.total corrupt");
2560
2561 Ok(())
2562 }
2563}