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