1use frame_election_provider_support::{
21 bounds::{CountBound, SizeBound},
22 data_provider, BoundedSupportsOf, DataProviderBounds, ElectionDataProvider, ElectionProvider,
23 ScoreProvider, SortedListProvider, 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, TryCollect, UnixTime,
32 },
33 weights::Weight,
34};
35use frame_system::{pallet_prelude::BlockNumberFor, RawOrigin};
36use pallet_session::historical;
37use sp_runtime::{
38 traits::{
39 Bounded, CheckedAdd, Convert, One, SaturatedConversion, Saturating, StaticLookup, Zero,
40 },
41 ArithmeticError, DispatchResult, Perbill, Percent,
42};
43use sp_staking::{
44 currency_to_vote::CurrencyToVote,
45 offence::{OffenceDetails, OnOffenceHandler},
46 EraIndex, OnStakingUpdate, Page, SessionIndex, Stake,
47 StakingAccount::{self, Controller, Stash},
48 StakingInterface,
49};
50
51use crate::{
52 asset, election_size_tracker::StaticTracker, log, slashing, weights::WeightInfo, ActiveEraInfo,
53 BalanceOf, EraInfo, EraPayout, Exposure, Forcing, IndividualExposure, LedgerIntegrityState,
54 MaxNominationsOf, MaxWinnersOf, Nominations, NominationsQuota, PositiveImbalanceOf,
55 RewardDestination, SessionInterface, StakingLedger, ValidatorPrefs, STAKING_ID,
56};
57use alloc::{boxed::Box, vec, vec::Vec};
58
59use super::pallet::*;
60
61#[cfg(feature = "try-runtime")]
62use frame_support::ensure;
63#[cfg(any(test, feature = "try-runtime"))]
64use sp_runtime::TryRuntimeError;
65
66const NPOS_MAX_ITERATIONS_COEFFICIENT: u32 = 2;
73
74impl<T: Config> Pallet<T> {
75 pub fn ledger(account: StakingAccount<T::AccountId>) -> Result<StakingLedger<T>, Error<T>> {
77 StakingLedger::<T>::get(account)
78 }
79
80 pub fn payee(account: StakingAccount<T::AccountId>) -> Option<RewardDestination<T::AccountId>> {
81 StakingLedger::<T>::reward_destination(account)
82 }
83
84 pub fn bonded(stash: &T::AccountId) -> Option<T::AccountId> {
86 StakingLedger::<T>::paired_account(Stash(stash.clone()))
87 }
88
89 pub(crate) fn inspect_bond_state(
96 stash: &T::AccountId,
97 ) -> Result<LedgerIntegrityState, Error<T>> {
98 let hold_or_lock = match asset::staked::<T>(&stash) {
99 x if x.is_zero() => {
100 let locked = T::OldCurrency::balance_locked(STAKING_ID, &stash).into();
101 locked
102 },
103 held => held,
104 };
105
106 let controller = <Bonded<T>>::get(stash).ok_or_else(|| {
107 if hold_or_lock == Zero::zero() {
108 Error::<T>::NotStash
109 } else {
110 Error::<T>::BadState
111 }
112 })?;
113
114 match Ledger::<T>::get(controller) {
115 Some(ledger) =>
116 if ledger.stash != *stash {
117 Ok(LedgerIntegrityState::Corrupted)
118 } else {
119 if hold_or_lock != ledger.total {
120 Ok(LedgerIntegrityState::LockCorrupted)
121 } else {
122 Ok(LedgerIntegrityState::Ok)
123 }
124 },
125 None => Ok(LedgerIntegrityState::CorruptedKilled),
126 }
127 }
128
129 pub fn slashable_balance_of(stash: &T::AccountId) -> BalanceOf<T> {
131 Self::ledger(Stash(stash.clone())).map(|l| l.active).unwrap_or_default()
133 }
134
135 pub fn slashable_balance_of_vote_weight(
137 stash: &T::AccountId,
138 issuance: BalanceOf<T>,
139 ) -> VoteWeight {
140 T::CurrencyToVote::to_vote(Self::slashable_balance_of(stash), issuance)
141 }
142
143 pub fn weight_of_fn() -> Box<dyn Fn(&T::AccountId) -> VoteWeight> {
148 let issuance = asset::total_issuance::<T>();
151 Box::new(move |who: &T::AccountId| -> VoteWeight {
152 Self::slashable_balance_of_vote_weight(who, issuance)
153 })
154 }
155
156 pub fn weight_of(who: &T::AccountId) -> VoteWeight {
158 let issuance = asset::total_issuance::<T>();
159 Self::slashable_balance_of_vote_weight(who, issuance)
160 }
161
162 pub(super) fn do_bond_extra(stash: &T::AccountId, additional: BalanceOf<T>) -> DispatchResult {
163 let mut ledger = Self::ledger(StakingAccount::Stash(stash.clone()))?;
164
165 let extra = if Self::is_virtual_staker(stash) {
168 additional
169 } else {
170 additional.min(asset::free_to_stake::<T>(stash))
172 };
173
174 ledger.total = ledger.total.checked_add(&extra).ok_or(ArithmeticError::Overflow)?;
175 ledger.active = ledger.active.checked_add(&extra).ok_or(ArithmeticError::Overflow)?;
176 ensure!(ledger.active >= asset::existential_deposit::<T>(), Error::<T>::InsufficientBond);
178
179 ledger.update()?;
181 if T::VoterList::contains(stash) {
183 let _ = T::VoterList::on_update(&stash, Self::weight_of(stash)).defensive();
184 }
185
186 Self::deposit_event(Event::<T>::Bonded { stash: stash.clone(), amount: extra });
187
188 Ok(())
189 }
190
191 pub(super) fn do_withdraw_unbonded(
192 controller: &T::AccountId,
193 num_slashing_spans: u32,
194 ) -> Result<Weight, DispatchError> {
195 let mut ledger = Self::ledger(Controller(controller.clone()))?;
196 let (stash, old_total) = (ledger.stash.clone(), ledger.total);
197 if let Some(current_era) = CurrentEra::<T>::get() {
198 ledger = ledger.consolidate_unlocked(current_era)
199 }
200 let new_total = ledger.total;
201
202 let ed = asset::existential_deposit::<T>();
203 let used_weight =
204 if ledger.unlocking.is_empty() && (ledger.active < ed || ledger.active.is_zero()) {
205 Self::kill_stash(&ledger.stash, num_slashing_spans)?;
209
210 T::WeightInfo::withdraw_unbonded_kill(num_slashing_spans)
211 } else {
212 ledger.update()?;
214
215 T::WeightInfo::withdraw_unbonded_update(num_slashing_spans)
217 };
218
219 if new_total < old_total {
222 let value = old_total.defensive_saturating_sub(new_total);
224 Self::deposit_event(Event::<T>::Withdrawn { stash, amount: value });
225
226 T::EventListeners::on_withdraw(controller, value);
228 }
229
230 Ok(used_weight)
231 }
232
233 pub(super) fn do_payout_stakers(
234 validator_stash: T::AccountId,
235 era: EraIndex,
236 ) -> DispatchResultWithPostInfo {
237 let controller = Self::bonded(&validator_stash).ok_or_else(|| {
238 Error::<T>::NotStash.with_weight(T::WeightInfo::payout_stakers_alive_staked(0))
239 })?;
240
241 let ledger = Self::ledger(StakingAccount::Controller(controller))?;
242 let page = EraInfo::<T>::get_next_claimable_page(era, &validator_stash, &ledger)
243 .ok_or_else(|| {
244 Error::<T>::AlreadyClaimed
245 .with_weight(T::WeightInfo::payout_stakers_alive_staked(0))
246 })?;
247
248 Self::do_payout_stakers_by_page(validator_stash, era, page)
249 }
250
251 pub(super) fn do_payout_stakers_by_page(
252 validator_stash: T::AccountId,
253 era: EraIndex,
254 page: Page,
255 ) -> DispatchResultWithPostInfo {
256 let current_era = CurrentEra::<T>::get().ok_or_else(|| {
258 Error::<T>::InvalidEraToReward
259 .with_weight(T::WeightInfo::payout_stakers_alive_staked(0))
260 })?;
261
262 let history_depth = T::HistoryDepth::get();
263 ensure!(
264 era <= current_era && era >= current_era.saturating_sub(history_depth),
265 Error::<T>::InvalidEraToReward
266 .with_weight(T::WeightInfo::payout_stakers_alive_staked(0))
267 );
268
269 ensure!(
270 page < EraInfo::<T>::get_page_count(era, &validator_stash),
271 Error::<T>::InvalidPage.with_weight(T::WeightInfo::payout_stakers_alive_staked(0))
272 );
273
274 let era_payout = <ErasValidatorReward<T>>::get(&era).ok_or_else(|| {
277 Error::<T>::InvalidEraToReward
278 .with_weight(T::WeightInfo::payout_stakers_alive_staked(0))
279 })?;
280
281 let account = StakingAccount::Stash(validator_stash.clone());
282 let mut ledger = Self::ledger(account.clone()).or_else(|_| {
283 if StakingLedger::<T>::is_bonded(account) {
284 Err(Error::<T>::NotController.into())
285 } else {
286 Err(Error::<T>::NotStash.with_weight(T::WeightInfo::payout_stakers_alive_staked(0)))
287 }
288 })?;
289
290 ledger
292 .legacy_claimed_rewards
293 .retain(|&x| x >= current_era.saturating_sub(history_depth));
294 ledger.clone().update()?;
295
296 let stash = ledger.stash.clone();
297
298 if EraInfo::<T>::is_rewards_claimed_with_legacy_fallback(era, &ledger, &stash, page) {
299 return Err(Error::<T>::AlreadyClaimed
300 .with_weight(T::WeightInfo::payout_stakers_alive_staked(0)))
301 } else {
302 EraInfo::<T>::set_rewards_as_claimed(era, &stash, page);
303 }
304
305 let exposure = EraInfo::<T>::get_paged_exposure(era, &stash, page).ok_or_else(|| {
306 Error::<T>::InvalidEraToReward
307 .with_weight(T::WeightInfo::payout_stakers_alive_staked(0))
308 })?;
309
310 let era_reward_points = <ErasRewardPoints<T>>::get(&era);
320 let total_reward_points = era_reward_points.total;
321 let validator_reward_points =
322 era_reward_points.individual.get(&stash).copied().unwrap_or_else(Zero::zero);
323
324 if validator_reward_points.is_zero() {
326 return Ok(Some(T::WeightInfo::payout_stakers_alive_staked(0)).into())
327 }
328
329 let validator_total_reward_part =
332 Perbill::from_rational(validator_reward_points, total_reward_points);
333
334 let validator_total_payout = validator_total_reward_part * era_payout;
336
337 let validator_commission = EraInfo::<T>::get_validator_commission(era, &ledger.stash);
338 let validator_total_commission_payout = validator_commission * validator_total_payout;
340
341 let validator_leftover_payout =
342 validator_total_payout.defensive_saturating_sub(validator_total_commission_payout);
343 let validator_exposure_part = Perbill::from_rational(exposure.own(), exposure.total());
345 let validator_staking_payout = validator_exposure_part * validator_leftover_payout;
346 let page_stake_part = Perbill::from_rational(exposure.page_total(), exposure.total());
347 let validator_commission_payout = page_stake_part * validator_total_commission_payout;
349
350 Self::deposit_event(Event::<T>::PayoutStarted {
351 era_index: era,
352 validator_stash: stash.clone(),
353 page,
354 next: EraInfo::<T>::get_next_claimable_page(era, &stash, &ledger),
355 });
356
357 let mut total_imbalance = PositiveImbalanceOf::<T>::zero();
358 if let Some((imbalance, dest)) =
360 Self::make_payout(&stash, validator_staking_payout + validator_commission_payout)
361 {
362 Self::deposit_event(Event::<T>::Rewarded { stash, dest, amount: imbalance.peek() });
363 total_imbalance.subsume(imbalance);
364 }
365
366 let mut nominator_payout_count: u32 = 0;
370
371 for nominator in exposure.others().iter() {
374 let nominator_exposure_part = Perbill::from_rational(nominator.value, exposure.total());
375
376 let nominator_reward: BalanceOf<T> =
377 nominator_exposure_part * validator_leftover_payout;
378 if let Some((imbalance, dest)) = Self::make_payout(&nominator.who, nominator_reward) {
380 nominator_payout_count += 1;
382 let e = Event::<T>::Rewarded {
383 stash: nominator.who.clone(),
384 dest,
385 amount: imbalance.peek(),
386 };
387 Self::deposit_event(e);
388 total_imbalance.subsume(imbalance);
389 }
390 }
391
392 T::Reward::on_unbalanced(total_imbalance);
393 debug_assert!(nominator_payout_count <= T::MaxExposurePageSize::get());
394
395 Ok(Some(T::WeightInfo::payout_stakers_alive_staked(nominator_payout_count)).into())
396 }
397
398 pub(crate) fn chill_stash(stash: &T::AccountId) {
400 let chilled_as_validator = Self::do_remove_validator(stash);
401 let chilled_as_nominator = Self::do_remove_nominator(stash);
402 if chilled_as_validator || chilled_as_nominator {
403 Self::deposit_event(Event::<T>::Chilled { stash: stash.clone() });
404 }
405 }
406
407 fn make_payout(
410 stash: &T::AccountId,
411 amount: BalanceOf<T>,
412 ) -> Option<(PositiveImbalanceOf<T>, RewardDestination<T::AccountId>)> {
413 if amount.is_zero() {
415 return None
416 }
417 let dest = Self::payee(StakingAccount::Stash(stash.clone()))?;
418
419 let maybe_imbalance = match dest {
420 RewardDestination::Stash => asset::mint_into_existing::<T>(stash, amount),
421 RewardDestination::Staked => Self::ledger(Stash(stash.clone()))
422 .and_then(|mut ledger| {
423 ledger.active += amount;
424 ledger.total += amount;
425 let r = asset::mint_into_existing::<T>(stash, amount);
426
427 let _ = ledger
428 .update()
429 .defensive_proof("ledger fetched from storage, so it exists; qed.");
430
431 Ok(r)
432 })
433 .unwrap_or_default(),
434 RewardDestination::Account(ref dest_account) =>
435 Some(asset::mint_creating::<T>(&dest_account, amount)),
436 RewardDestination::None => None,
437 #[allow(deprecated)]
438 RewardDestination::Controller => Self::bonded(stash)
439 .map(|controller| {
440 defensive!("Paying out controller as reward destination which is deprecated and should be migrated.");
441 asset::mint_creating::<T>(&controller, amount)
444 }),
445 };
446 maybe_imbalance.map(|imbalance| (imbalance, dest))
447 }
448
449 fn new_session(
451 session_index: SessionIndex,
452 is_genesis: bool,
453 ) -> Option<BoundedVec<T::AccountId, MaxWinnersOf<T>>> {
454 if let Some(current_era) = CurrentEra::<T>::get() {
455 let current_era_start_session_index = ErasStartSessionIndex::<T>::get(current_era)
457 .unwrap_or_else(|| {
458 frame_support::print("Error: start_session_index must be set for current_era");
459 0
460 });
461
462 let era_length = session_index.saturating_sub(current_era_start_session_index); match ForceEra::<T>::get() {
465 Forcing::ForceNew => (),
467 Forcing::ForceAlways => (),
469 Forcing::NotForcing if era_length >= T::SessionsPerEra::get() => (),
471 _ => {
472 return None
475 },
476 }
477
478 let maybe_new_era_validators = Self::try_trigger_new_era(session_index, is_genesis);
480 if maybe_new_era_validators.is_some() &&
481 matches!(ForceEra::<T>::get(), Forcing::ForceNew)
482 {
483 Self::set_force_era(Forcing::NotForcing);
484 }
485
486 maybe_new_era_validators
487 } else {
488 log!(debug, "Starting the first era.");
490 Self::try_trigger_new_era(session_index, is_genesis)
491 }
492 }
493
494 fn start_session(start_session: SessionIndex) {
496 let next_active_era = ActiveEra::<T>::get().map(|e| e.index + 1).unwrap_or(0);
497 if let Some(next_active_era_start_session_index) =
501 ErasStartSessionIndex::<T>::get(next_active_era)
502 {
503 if next_active_era_start_session_index == start_session {
504 Self::start_era(start_session);
505 } else if next_active_era_start_session_index < start_session {
506 frame_support::print("Warning: A session appears to have been skipped.");
509 Self::start_era(start_session);
510 }
511 }
512 }
513
514 fn end_session(session_index: SessionIndex) {
516 if let Some(active_era) = ActiveEra::<T>::get() {
517 if let Some(next_active_era_start_session_index) =
518 ErasStartSessionIndex::<T>::get(active_era.index + 1)
519 {
520 if next_active_era_start_session_index == session_index + 1 {
521 Self::end_era(active_era, session_index);
522 }
523 }
524 }
525 }
526
527 fn start_era(start_session: SessionIndex) {
532 let active_era = ActiveEra::<T>::mutate(|active_era| {
533 let new_index = active_era.as_ref().map(|info| info.index + 1).unwrap_or(0);
534 *active_era = Some(ActiveEraInfo {
535 index: new_index,
536 start: None,
538 });
539 new_index
540 });
541
542 let bonding_duration = T::BondingDuration::get();
543
544 BondedEras::<T>::mutate(|bonded| {
545 bonded.push((active_era, start_session));
546
547 if active_era > bonding_duration {
548 let first_kept = active_era.defensive_saturating_sub(bonding_duration);
549
550 let n_to_prune =
552 bonded.iter().take_while(|&&(era_idx, _)| era_idx < first_kept).count();
553
554 for (pruned_era, _) in bonded.drain(..n_to_prune) {
556 slashing::clear_era_metadata::<T>(pruned_era);
557 }
558
559 if let Some(&(_, first_session)) = bonded.first() {
560 T::SessionInterface::prune_historical_up_to(first_session);
561 }
562 }
563 });
564
565 Self::apply_unapplied_slashes(active_era);
566 }
567
568 fn end_era(active_era: ActiveEraInfo, _session_index: SessionIndex) {
570 if let Some(active_era_start) = active_era.start {
572 let now_as_millis_u64 = T::UnixTime::now().as_millis().saturated_into::<u64>();
573
574 let era_duration = (now_as_millis_u64.defensive_saturating_sub(active_era_start))
575 .saturated_into::<u64>();
576 let staked = ErasTotalStake::<T>::get(&active_era.index);
577 let issuance = asset::total_issuance::<T>();
578
579 let (validator_payout, remainder) =
580 T::EraPayout::era_payout(staked, issuance, era_duration);
581
582 let total_payout = validator_payout.saturating_add(remainder);
583 let max_staked_rewards =
584 MaxStakedRewards::<T>::get().unwrap_or(Percent::from_percent(100));
585
586 let validator_payout = validator_payout.min(max_staked_rewards * total_payout);
588 let remainder = total_payout.saturating_sub(validator_payout);
589
590 Self::deposit_event(Event::<T>::EraPaid {
591 era_index: active_era.index,
592 validator_payout,
593 remainder,
594 });
595
596 <ErasValidatorReward<T>>::insert(&active_era.index, validator_payout);
598 T::RewardRemainder::on_unbalanced(asset::issue::<T>(remainder));
599 }
600 }
601
602 pub fn trigger_new_era(
611 start_session_index: SessionIndex,
612 exposures: BoundedVec<
613 (T::AccountId, Exposure<T::AccountId, BalanceOf<T>>),
614 MaxWinnersOf<T>,
615 >,
616 ) -> BoundedVec<T::AccountId, MaxWinnersOf<T>> {
617 let new_planned_era = CurrentEra::<T>::mutate(|s| {
619 *s = Some(s.map(|s| s + 1).unwrap_or(0));
620 s.unwrap()
621 });
622 ErasStartSessionIndex::<T>::insert(&new_planned_era, &start_session_index);
623
624 if let Some(old_era) = new_planned_era.checked_sub(T::HistoryDepth::get() + 1) {
626 Self::clear_era_information(old_era);
627 }
628
629 Self::store_stakers_info(exposures, new_planned_era)
631 }
632
633 pub(crate) fn try_trigger_new_era(
640 start_session_index: SessionIndex,
641 is_genesis: bool,
642 ) -> Option<BoundedVec<T::AccountId, MaxWinnersOf<T>>> {
643 let election_result: BoundedVec<_, MaxWinnersOf<T>> = if is_genesis {
644 let result = <T::GenesisElectionProvider>::elect().map_err(|e| {
645 log!(warn, "genesis election provider failed due to {:?}", e);
646 Self::deposit_event(Event::StakingElectionFailed);
647 });
648
649 result
650 .ok()?
651 .into_inner()
652 .try_into()
653 .defensive_unwrap_or_default()
655 } else {
656 let result = <T::ElectionProvider>::elect().map_err(|e| {
657 log!(warn, "election provider failed due to {:?}", e);
658 Self::deposit_event(Event::StakingElectionFailed);
659 });
660 result.ok()?
661 };
662
663 let exposures = Self::collect_exposures(election_result);
664 if (exposures.len() as u32) < MinimumValidatorCount::<T>::get().max(1) {
665 match CurrentEra::<T>::get() {
667 Some(current_era) if current_era > 0 => log!(
668 warn,
669 "chain does not have enough staking candidates to operate for era {:?} ({} \
670 elected, minimum is {})",
671 CurrentEra::<T>::get().unwrap_or(0),
672 exposures.len(),
673 MinimumValidatorCount::<T>::get(),
674 ),
675 None => {
676 CurrentEra::<T>::put(0);
681 ErasStartSessionIndex::<T>::insert(&0, &start_session_index);
682 },
683 _ => (),
684 }
685
686 Self::deposit_event(Event::StakingElectionFailed);
687 return None
688 }
689
690 Self::deposit_event(Event::StakersElected);
691 Some(Self::trigger_new_era(start_session_index, exposures))
692 }
693
694 pub fn store_stakers_info(
698 exposures: BoundedVec<
699 (T::AccountId, Exposure<T::AccountId, BalanceOf<T>>),
700 MaxWinnersOf<T>,
701 >,
702 new_planned_era: EraIndex,
703 ) -> BoundedVec<T::AccountId, MaxWinnersOf<T>> {
704 let mut total_stake: BalanceOf<T> = Zero::zero();
706 let mut elected_stashes = Vec::with_capacity(exposures.len());
707
708 exposures.into_iter().for_each(|(stash, exposure)| {
709 elected_stashes.push(stash.clone());
711 total_stake = total_stake.saturating_add(exposure.total);
713 EraInfo::<T>::set_exposure(new_planned_era, &stash, exposure);
715 });
716
717 let elected_stashes: BoundedVec<_, MaxWinnersOf<T>> = elected_stashes
718 .try_into()
719 .expect("elected_stashes.len() always equal to exposures.len(); qed");
720
721 EraInfo::<T>::set_total_stake(new_planned_era, total_stake);
722
723 for stash in &elected_stashes {
725 let pref = Validators::<T>::get(stash);
726 <ErasValidatorPrefs<T>>::insert(&new_planned_era, stash, pref);
727 }
728
729 if new_planned_era > 0 {
730 log!(
731 debug,
732 "new validator set of size {:?} has been processed for era {:?}",
733 elected_stashes.len(),
734 new_planned_era,
735 );
736 }
737
738 elected_stashes
739 }
740
741 fn collect_exposures(
744 supports: BoundedSupportsOf<T::ElectionProvider>,
745 ) -> BoundedVec<(T::AccountId, Exposure<T::AccountId, BalanceOf<T>>), MaxWinnersOf<T>> {
746 let total_issuance = asset::total_issuance::<T>();
747 let to_currency = |e: frame_election_provider_support::ExtendedBalance| {
748 T::CurrencyToVote::to_currency(e, total_issuance)
749 };
750
751 supports
752 .into_iter()
753 .map(|(validator, support)| {
754 let mut others = Vec::with_capacity(support.voters.len());
756 let mut own: BalanceOf<T> = Zero::zero();
757 let mut total: BalanceOf<T> = Zero::zero();
758 support
759 .voters
760 .into_iter()
761 .map(|(nominator, weight)| (nominator, to_currency(weight)))
762 .for_each(|(nominator, stake)| {
763 if nominator == validator {
764 own = own.saturating_add(stake);
765 } else {
766 others.push(IndividualExposure { who: nominator, value: stake });
767 }
768 total = total.saturating_add(stake);
769 });
770
771 let exposure = Exposure { own, others, total };
772 (validator, exposure)
773 })
774 .try_collect()
775 .expect("we only map through support vector which cannot change the size; qed")
776 }
777
778 pub(crate) fn kill_stash(stash: &T::AccountId, num_slashing_spans: u32) -> DispatchResult {
786 slashing::clear_stash_metadata::<T>(&stash, num_slashing_spans)?;
787
788 StakingLedger::<T>::kill(&stash)?;
791
792 Self::do_remove_validator(&stash);
793 Self::do_remove_nominator(&stash);
794
795 Ok(())
796 }
797
798 pub(crate) fn clear_era_information(era_index: EraIndex) {
800 let mut cursor = <ErasStakers<T>>::clear_prefix(era_index, u32::MAX, None);
803 debug_assert!(cursor.maybe_cursor.is_none());
804 cursor = <ErasStakersClipped<T>>::clear_prefix(era_index, u32::MAX, None);
805 debug_assert!(cursor.maybe_cursor.is_none());
806 cursor = <ErasValidatorPrefs<T>>::clear_prefix(era_index, u32::MAX, None);
807 debug_assert!(cursor.maybe_cursor.is_none());
808 cursor = <ClaimedRewards<T>>::clear_prefix(era_index, u32::MAX, None);
809 debug_assert!(cursor.maybe_cursor.is_none());
810 cursor = <ErasStakersPaged<T>>::clear_prefix((era_index,), u32::MAX, None);
811 debug_assert!(cursor.maybe_cursor.is_none());
812 cursor = <ErasStakersOverview<T>>::clear_prefix(era_index, u32::MAX, None);
813 debug_assert!(cursor.maybe_cursor.is_none());
814
815 <ErasValidatorReward<T>>::remove(era_index);
816 <ErasRewardPoints<T>>::remove(era_index);
817 <ErasTotalStake<T>>::remove(era_index);
818 ErasStartSessionIndex::<T>::remove(era_index);
819 }
820
821 fn apply_unapplied_slashes(active_era: EraIndex) {
823 let era_slashes = UnappliedSlashes::<T>::take(&active_era);
824 log!(
825 debug,
826 "found {} slashes scheduled to be executed in era {:?}",
827 era_slashes.len(),
828 active_era,
829 );
830 for slash in era_slashes {
831 let slash_era = active_era.saturating_sub(T::SlashDeferDuration::get());
832 slashing::apply_slash::<T>(slash, slash_era);
833 }
834 }
835
836 pub fn reward_by_ids(validators_points: impl IntoIterator<Item = (T::AccountId, u32)>) {
848 if let Some(active_era) = ActiveEra::<T>::get() {
849 <ErasRewardPoints<T>>::mutate(active_era.index, |era_rewards| {
850 for (validator, points) in validators_points.into_iter() {
851 *era_rewards.individual.entry(validator).or_default() += points;
852 era_rewards.total += points;
853 }
854 });
855 }
856 }
857
858 pub(crate) fn set_force_era(mode: Forcing) {
860 log!(info, "Setting force era mode {:?}.", mode);
861 ForceEra::<T>::put(mode);
862 Self::deposit_event(Event::<T>::ForceEra { mode });
863 }
864
865 #[cfg(feature = "runtime-benchmarks")]
866 pub fn add_era_stakers(
867 current_era: EraIndex,
868 stash: T::AccountId,
869 exposure: Exposure<T::AccountId, BalanceOf<T>>,
870 ) {
871 EraInfo::<T>::set_exposure(current_era, &stash, exposure);
872 }
873
874 #[cfg(feature = "runtime-benchmarks")]
875 pub fn set_slash_reward_fraction(fraction: Perbill) {
876 SlashRewardFraction::<T>::put(fraction);
877 }
878
879 pub fn get_npos_voters(bounds: DataProviderBounds) -> Vec<VoterOf<Self>> {
888 let mut voters_size_tracker: StaticTracker<Self> = StaticTracker::default();
889
890 let final_predicted_len = {
891 let all_voter_count = T::VoterList::count();
892 bounds.count.unwrap_or(all_voter_count.into()).min(all_voter_count.into()).0
893 };
894
895 let mut all_voters = Vec::<_>::with_capacity(final_predicted_len as usize);
896
897 let weight_of = Self::weight_of_fn();
899
900 let mut voters_seen = 0u32;
901 let mut validators_taken = 0u32;
902 let mut nominators_taken = 0u32;
903 let mut min_active_stake = u64::MAX;
904
905 let mut sorted_voters = T::VoterList::iter();
906 while all_voters.len() < final_predicted_len as usize &&
907 voters_seen < (NPOS_MAX_ITERATIONS_COEFFICIENT * final_predicted_len as u32)
908 {
909 let voter = match sorted_voters.next() {
910 Some(voter) => {
911 voters_seen.saturating_inc();
912 voter
913 },
914 None => break,
915 };
916
917 let voter_weight = weight_of(&voter);
918 if voter_weight.is_zero() {
920 log!(debug, "voter's active balance is 0. skip this voter.");
921 continue
922 }
923
924 if let Some(Nominations { targets, .. }) = <Nominators<T>>::get(&voter) {
925 if !targets.is_empty() {
926 let voter = (voter, voter_weight, targets);
931 if voters_size_tracker.try_register_voter(&voter, &bounds).is_err() {
932 Self::deposit_event(Event::<T>::SnapshotVotersSizeExceeded {
934 size: voters_size_tracker.size as u32,
935 });
936 break
937 }
938
939 all_voters.push(voter);
940 nominators_taken.saturating_inc();
941 } else {
942 }
944 min_active_stake =
945 if voter_weight < min_active_stake { voter_weight } else { min_active_stake };
946 } else if Validators::<T>::contains_key(&voter) {
947 let self_vote = (
949 voter.clone(),
950 voter_weight,
951 vec![voter.clone()]
952 .try_into()
953 .expect("`MaxVotesPerVoter` must be greater than or equal to 1"),
954 );
955
956 if voters_size_tracker.try_register_voter(&self_vote, &bounds).is_err() {
957 Self::deposit_event(Event::<T>::SnapshotVotersSizeExceeded {
959 size: voters_size_tracker.size as u32,
960 });
961 break
962 }
963 all_voters.push(self_vote);
964 validators_taken.saturating_inc();
965 } else {
966 defensive!(
972 "DEFENSIVE: invalid item in `VoterList`: {:?}, this nominator probably has too many nominations now",
973 voter,
974 );
975 }
976 }
977
978 debug_assert!(all_voters.capacity() == final_predicted_len as usize);
980
981 Self::register_weight(T::WeightInfo::get_npos_voters(validators_taken, nominators_taken));
982
983 let min_active_stake: T::CurrencyBalance =
984 if all_voters.is_empty() { Zero::zero() } else { min_active_stake.into() };
985
986 MinimumActiveStake::<T>::put(min_active_stake);
987
988 log!(
989 debug,
990 "generated {} npos voters, {} from validators and {} nominators",
991 all_voters.len(),
992 validators_taken,
993 nominators_taken
994 );
995
996 all_voters
997 }
998
999 pub fn get_npos_targets(bounds: DataProviderBounds) -> Vec<T::AccountId> {
1003 let mut targets_size_tracker: StaticTracker<Self> = StaticTracker::default();
1004
1005 let final_predicted_len = {
1006 let all_target_count = T::TargetList::count();
1007 bounds.count.unwrap_or(all_target_count.into()).min(all_target_count.into()).0
1008 };
1009
1010 let mut all_targets = Vec::<T::AccountId>::with_capacity(final_predicted_len as usize);
1011 let mut targets_seen = 0;
1012
1013 let mut targets_iter = T::TargetList::iter();
1014 while all_targets.len() < final_predicted_len as usize &&
1015 targets_seen < (NPOS_MAX_ITERATIONS_COEFFICIENT * final_predicted_len as u32)
1016 {
1017 let target = match targets_iter.next() {
1018 Some(target) => {
1019 targets_seen.saturating_inc();
1020 target
1021 },
1022 None => break,
1023 };
1024
1025 if targets_size_tracker.try_register_target(target.clone(), &bounds).is_err() {
1026 Self::deposit_event(Event::<T>::SnapshotTargetsSizeExceeded {
1028 size: targets_size_tracker.size as u32,
1029 });
1030 break
1031 }
1032
1033 if Validators::<T>::contains_key(&target) {
1034 all_targets.push(target);
1035 }
1036 }
1037
1038 Self::register_weight(T::WeightInfo::get_npos_targets(all_targets.len() as u32));
1039 log!(debug, "generated {} npos targets", all_targets.len());
1040
1041 all_targets
1042 }
1043
1044 pub fn do_add_nominator(who: &T::AccountId, nominations: Nominations<T>) {
1053 if !Nominators::<T>::contains_key(who) {
1054 let _ = T::VoterList::on_insert(who.clone(), Self::weight_of(who))
1056 .defensive_unwrap_or_default();
1057 }
1058 Nominators::<T>::insert(who, nominations);
1059
1060 debug_assert_eq!(
1061 Nominators::<T>::count() + Validators::<T>::count(),
1062 T::VoterList::count()
1063 );
1064 }
1065
1066 pub fn do_remove_nominator(who: &T::AccountId) -> bool {
1075 let outcome = if Nominators::<T>::contains_key(who) {
1076 Nominators::<T>::remove(who);
1077 let _ = T::VoterList::on_remove(who).defensive();
1078 true
1079 } else {
1080 false
1081 };
1082
1083 debug_assert_eq!(
1084 Nominators::<T>::count() + Validators::<T>::count(),
1085 T::VoterList::count()
1086 );
1087
1088 outcome
1089 }
1090
1091 pub fn do_add_validator(who: &T::AccountId, prefs: ValidatorPrefs) {
1099 if !Validators::<T>::contains_key(who) {
1100 let _ = T::VoterList::on_insert(who.clone(), Self::weight_of(who))
1102 .defensive_unwrap_or_default();
1103 }
1104 Validators::<T>::insert(who, prefs);
1105
1106 debug_assert_eq!(
1107 Nominators::<T>::count() + Validators::<T>::count(),
1108 T::VoterList::count()
1109 );
1110 }
1111
1112 pub fn do_remove_validator(who: &T::AccountId) -> bool {
1120 let outcome = if Validators::<T>::contains_key(who) {
1121 Validators::<T>::remove(who);
1122 let _ = T::VoterList::on_remove(who).defensive();
1123 true
1124 } else {
1125 false
1126 };
1127
1128 debug_assert_eq!(
1129 Nominators::<T>::count() + Validators::<T>::count(),
1130 T::VoterList::count()
1131 );
1132
1133 outcome
1134 }
1135
1136 fn register_weight(weight: Weight) {
1140 <frame_system::Pallet<T>>::register_extra_weight_unchecked(
1141 weight,
1142 DispatchClass::Mandatory,
1143 );
1144 }
1145
1146 pub fn eras_stakers(
1152 era: EraIndex,
1153 account: &T::AccountId,
1154 ) -> Exposure<T::AccountId, BalanceOf<T>> {
1155 EraInfo::<T>::get_full_exposure(era, account)
1156 }
1157
1158 pub(super) fn do_migrate_currency(stash: &T::AccountId) -> DispatchResult {
1159 if Self::is_virtual_staker(stash) {
1160 return Self::do_migrate_virtual_staker(stash);
1161 }
1162
1163 let locked: BalanceOf<T> = T::OldCurrency::balance_locked(STAKING_ID, stash).into();
1164 ensure!(!locked.is_zero(), Error::<T>::AlreadyMigrated);
1165
1166 T::OldCurrency::remove_lock(STAKING_ID, &stash);
1168
1169 frame_system::Pallet::<T>::dec_consumers(&stash);
1171
1172 let Ok(ledger) = Self::ledger(Stash(stash.clone())) else {
1173 return Ok(());
1175 };
1176
1177 let max_hold = asset::stakeable_balance::<T>(&stash);
1179 let force_withdraw = if max_hold >= ledger.total {
1180 asset::update_stake::<T>(&stash, ledger.total)?;
1182 Zero::zero()
1183 } else {
1184 let old_total = ledger.total;
1187 let updated_ledger = ledger.update_total_stake(max_hold);
1189
1190 let new_total = updated_ledger.total;
1192 debug_assert_eq!(new_total, max_hold);
1193
1194 updated_ledger.update()?;
1196
1197 old_total.defensive_saturating_sub(new_total)
1199 };
1200
1201 Self::deposit_event(Event::<T>::CurrencyMigrated { stash: stash.clone(), force_withdraw });
1202 Ok(())
1203 }
1204
1205 fn do_migrate_virtual_staker(stash: &T::AccountId) -> DispatchResult {
1209 let consumer_count = frame_system::Pallet::<T>::consumers(stash);
1210 ensure!(consumer_count > 0, Error::<T>::AlreadyMigrated);
1212
1213 ensure!(consumer_count <= 2, Error::<T>::BadState);
1217
1218 for _ in 0..consumer_count {
1220 frame_system::Pallet::<T>::dec_consumers(&stash);
1221 }
1222
1223 let actual_providers = frame_system::Pallet::<T>::providers(stash);
1225
1226 let expected_providers =
1227 if asset::free_to_stake::<T>(&stash) >= asset::existential_deposit::<T>() {
1231 2
1232 } else {
1233 1
1234 };
1235
1236 ensure!(actual_providers <= expected_providers, Error::<T>::BadState);
1238
1239 ensure!(actual_providers == expected_providers, Error::<T>::AlreadyMigrated);
1241
1242 let _ = frame_system::Pallet::<T>::dec_providers(&stash)?;
1243
1244 Ok(())
1245 }
1246
1247 pub fn on_offence(
1248 offenders: impl Iterator<Item = OffenceDetails<T::AccountId, T::AccountId>>,
1249 slash_fractions: &[Perbill],
1250 slash_session: SessionIndex,
1251 ) -> Weight {
1252 let reward_proportion = SlashRewardFraction::<T>::get();
1253 let mut consumed_weight = Weight::from_parts(0, 0);
1254 let mut add_db_reads_writes = |reads, writes| {
1255 consumed_weight += T::DbWeight::get().reads_writes(reads, writes);
1256 };
1257
1258 let active_era = {
1259 let active_era = ActiveEra::<T>::get();
1260 add_db_reads_writes(1, 0);
1261 if active_era.is_none() {
1262 return consumed_weight
1264 }
1265 active_era.expect("value checked not to be `None`; qed").index
1266 };
1267 let active_era_start_session_index = ErasStartSessionIndex::<T>::get(active_era)
1268 .unwrap_or_else(|| {
1269 frame_support::print("Error: start_session_index must be set for current_era");
1270 0
1271 });
1272 add_db_reads_writes(1, 0);
1273
1274 let window_start = active_era.saturating_sub(T::BondingDuration::get());
1275
1276 let slash_era = if slash_session >= active_era_start_session_index {
1279 active_era
1280 } else {
1281 let eras = BondedEras::<T>::get();
1282 add_db_reads_writes(1, 0);
1283
1284 match eras.iter().rev().find(|&(_, sesh)| sesh <= &slash_session) {
1286 Some((slash_era, _)) => *slash_era,
1287 None => return consumed_weight,
1289 }
1290 };
1291
1292 add_db_reads_writes(1, 1);
1293
1294 let slash_defer_duration = T::SlashDeferDuration::get();
1295
1296 let invulnerables = Invulnerables::<T>::get();
1297 add_db_reads_writes(1, 0);
1298
1299 for (details, slash_fraction) in offenders.zip(slash_fractions) {
1300 let stash = &details.offender;
1301 let exposure = Self::eras_stakers(slash_era, stash);
1302
1303 if invulnerables.contains(stash) {
1305 continue
1306 }
1307
1308 Self::deposit_event(Event::<T>::SlashReported {
1309 validator: stash.clone(),
1310 fraction: *slash_fraction,
1311 slash_era,
1312 });
1313
1314 if slash_era == active_era {
1315 add_db_reads_writes(2, 2);
1318 T::SessionInterface::report_offence(
1319 stash.clone(),
1320 crate::OffenceSeverity(*slash_fraction),
1321 );
1322 }
1323
1324 let unapplied = slashing::compute_slash::<T>(slashing::SlashParams {
1325 stash,
1326 slash: *slash_fraction,
1327 exposure: &exposure,
1328 slash_era,
1329 window_start,
1330 now: active_era,
1331 reward_proportion,
1332 });
1333
1334 if let Some(mut unapplied) = unapplied {
1335 let nominators_len = unapplied.others.len() as u64;
1336 let reporters_len = details.reporters.len() as u64;
1337
1338 {
1339 let upper_bound = 1 + 2 ;
1340 let rw = upper_bound + nominators_len * upper_bound;
1341 add_db_reads_writes(rw, rw);
1342 }
1343 unapplied.reporters = details.reporters.clone();
1344 if slash_defer_duration == 0 {
1345 slashing::apply_slash::<T>(unapplied, slash_era);
1347 {
1348 let slash_cost = (6, 5);
1349 let reward_cost = (2, 2);
1350 add_db_reads_writes(
1351 (1 + nominators_len) * slash_cost.0 + reward_cost.0 * reporters_len,
1352 (1 + nominators_len) * slash_cost.1 + reward_cost.1 * reporters_len,
1353 );
1354 }
1355 } else {
1356 log!(
1358 debug,
1359 "deferring slash of {:?}% happened in {:?} (reported in {:?}) to {:?}",
1360 slash_fraction,
1361 slash_era,
1362 active_era,
1363 slash_era + slash_defer_duration + 1,
1364 );
1365 UnappliedSlashes::<T>::mutate(
1366 slash_era.saturating_add(slash_defer_duration).saturating_add(One::one()),
1367 move |for_later| for_later.push(unapplied),
1368 );
1369 add_db_reads_writes(1, 1);
1370 }
1371 } else {
1372 add_db_reads_writes(4 , 5 )
1373 }
1374 }
1375
1376 consumed_weight
1377 }
1378}
1379
1380impl<T: Config> Pallet<T> {
1381 pub fn api_nominations_quota(balance: BalanceOf<T>) -> u32 {
1385 T::NominationsQuota::get_quota(balance)
1386 }
1387
1388 pub fn api_eras_stakers(
1389 era: EraIndex,
1390 account: T::AccountId,
1391 ) -> Exposure<T::AccountId, BalanceOf<T>> {
1392 Self::eras_stakers(era, &account)
1393 }
1394
1395 pub fn api_eras_stakers_page_count(era: EraIndex, account: T::AccountId) -> Page {
1396 EraInfo::<T>::get_page_count(era, &account)
1397 }
1398
1399 pub fn api_pending_rewards(era: EraIndex, account: T::AccountId) -> bool {
1400 EraInfo::<T>::pending_rewards(era, &account)
1401 }
1402}
1403
1404impl<T: Config> ElectionDataProvider for Pallet<T> {
1405 type AccountId = T::AccountId;
1406 type BlockNumber = BlockNumberFor<T>;
1407 type MaxVotesPerVoter = MaxNominationsOf<T>;
1408
1409 fn desired_targets() -> data_provider::Result<u32> {
1410 Self::register_weight(T::DbWeight::get().reads(1));
1411 Ok(ValidatorCount::<T>::get())
1412 }
1413
1414 fn electing_voters(bounds: DataProviderBounds) -> data_provider::Result<Vec<VoterOf<Self>>> {
1415 let voters = Self::get_npos_voters(bounds);
1417
1418 debug_assert!(!bounds.exhausted(
1419 SizeBound(voters.encoded_size() as u32).into(),
1420 CountBound(voters.len() as u32).into()
1421 ));
1422
1423 Ok(voters)
1424 }
1425
1426 fn electable_targets(bounds: DataProviderBounds) -> data_provider::Result<Vec<T::AccountId>> {
1427 let targets = Self::get_npos_targets(bounds);
1428
1429 if bounds.exhausted(None, CountBound(T::TargetList::count() as u32).into()) {
1432 return Err("Target snapshot too big")
1433 }
1434
1435 debug_assert!(!bounds.exhausted(
1436 SizeBound(targets.encoded_size() as u32).into(),
1437 CountBound(targets.len() as u32).into()
1438 ));
1439
1440 Ok(targets)
1441 }
1442
1443 fn next_election_prediction(now: BlockNumberFor<T>) -> BlockNumberFor<T> {
1444 let current_era = CurrentEra::<T>::get().unwrap_or(0);
1445 let current_session = CurrentPlannedSession::<T>::get();
1446 let current_era_start_session_index =
1447 ErasStartSessionIndex::<T>::get(current_era).unwrap_or(0);
1448 let era_progress = current_session
1450 .saturating_sub(current_era_start_session_index)
1451 .min(T::SessionsPerEra::get());
1452
1453 let until_this_session_end = T::NextNewSession::estimate_next_new_session(now)
1454 .0
1455 .unwrap_or_default()
1456 .saturating_sub(now);
1457
1458 let session_length = T::NextNewSession::average_session_length();
1459
1460 let sessions_left: BlockNumberFor<T> = match ForceEra::<T>::get() {
1461 Forcing::ForceNone => Bounded::max_value(),
1462 Forcing::ForceNew | Forcing::ForceAlways => Zero::zero(),
1463 Forcing::NotForcing if era_progress >= T::SessionsPerEra::get() => Zero::zero(),
1464 Forcing::NotForcing => T::SessionsPerEra::get()
1465 .saturating_sub(era_progress)
1466 .saturating_sub(1)
1468 .into(),
1469 };
1470
1471 now.saturating_add(
1472 until_this_session_end.saturating_add(sessions_left.saturating_mul(session_length)),
1473 )
1474 }
1475
1476 #[cfg(feature = "runtime-benchmarks")]
1477 fn add_voter(
1478 voter: T::AccountId,
1479 weight: VoteWeight,
1480 targets: BoundedVec<T::AccountId, Self::MaxVotesPerVoter>,
1481 ) {
1482 let stake = <BalanceOf<T>>::try_from(weight).unwrap_or_else(|_| {
1483 panic!("cannot convert a VoteWeight into BalanceOf, benchmark needs reconfiguring.")
1484 });
1485 <Bonded<T>>::insert(voter.clone(), voter.clone());
1486 <Ledger<T>>::insert(voter.clone(), StakingLedger::<T>::new(voter.clone(), stake));
1487
1488 Self::do_add_nominator(&voter, Nominations { targets, submitted_in: 0, suppressed: false });
1489 }
1490
1491 #[cfg(feature = "runtime-benchmarks")]
1492 fn add_target(target: T::AccountId) {
1493 let stake = MinValidatorBond::<T>::get() * 100u32.into();
1494 <Bonded<T>>::insert(target.clone(), target.clone());
1495 <Ledger<T>>::insert(target.clone(), StakingLedger::<T>::new(target.clone(), stake));
1496 Self::do_add_validator(
1497 &target,
1498 ValidatorPrefs { commission: Perbill::zero(), blocked: false },
1499 );
1500 }
1501
1502 #[cfg(feature = "runtime-benchmarks")]
1503 fn clear() {
1504 #[allow(deprecated)]
1505 <Bonded<T>>::remove_all(None);
1506 #[allow(deprecated)]
1507 <Ledger<T>>::remove_all(None);
1508 #[allow(deprecated)]
1509 <Validators<T>>::remove_all();
1510 #[allow(deprecated)]
1511 <Nominators<T>>::remove_all();
1512
1513 T::VoterList::unsafe_clear();
1514 }
1515
1516 #[cfg(feature = "runtime-benchmarks")]
1517 fn put_snapshot(
1518 voters: Vec<VoterOf<Self>>,
1519 targets: Vec<T::AccountId>,
1520 target_stake: Option<VoteWeight>,
1521 ) {
1522 targets.into_iter().for_each(|v| {
1523 let stake: BalanceOf<T> = target_stake
1524 .and_then(|w| <BalanceOf<T>>::try_from(w).ok())
1525 .unwrap_or_else(|| MinNominatorBond::<T>::get() * 100u32.into());
1526 <Bonded<T>>::insert(v.clone(), v.clone());
1527 <Ledger<T>>::insert(v.clone(), StakingLedger::<T>::new(v.clone(), stake));
1528 Self::do_add_validator(
1529 &v,
1530 ValidatorPrefs { commission: Perbill::zero(), blocked: false },
1531 );
1532 });
1533
1534 voters.into_iter().for_each(|(v, s, t)| {
1535 let stake = <BalanceOf<T>>::try_from(s).unwrap_or_else(|_| {
1536 panic!("cannot convert a VoteWeight into BalanceOf, benchmark needs reconfiguring.")
1537 });
1538 <Bonded<T>>::insert(v.clone(), v.clone());
1539 <Ledger<T>>::insert(v.clone(), StakingLedger::<T>::new(v.clone(), stake));
1540 Self::do_add_nominator(
1541 &v,
1542 Nominations { targets: t, submitted_in: 0, suppressed: false },
1543 );
1544 });
1545 }
1546}
1547
1548impl<T: Config> pallet_session::SessionManager<T::AccountId> for Pallet<T> {
1554 fn new_session(new_index: SessionIndex) -> Option<Vec<T::AccountId>> {
1555 log!(trace, "planning new session {}", new_index);
1556 CurrentPlannedSession::<T>::put(new_index);
1557 Self::new_session(new_index, false).map(|v| v.into_inner())
1558 }
1559 fn new_session_genesis(new_index: SessionIndex) -> Option<Vec<T::AccountId>> {
1560 log!(trace, "planning new session {} at genesis", new_index);
1561 CurrentPlannedSession::<T>::put(new_index);
1562 Self::new_session(new_index, true).map(|v| v.into_inner())
1563 }
1564 fn start_session(start_index: SessionIndex) {
1565 log!(trace, "starting session {}", start_index);
1566 Self::start_session(start_index)
1567 }
1568 fn end_session(end_index: SessionIndex) {
1569 log!(trace, "ending session {}", end_index);
1570 Self::end_session(end_index)
1571 }
1572}
1573
1574impl<T: Config> historical::SessionManager<T::AccountId, Exposure<T::AccountId, BalanceOf<T>>>
1575 for Pallet<T>
1576{
1577 fn new_session(
1578 new_index: SessionIndex,
1579 ) -> Option<Vec<(T::AccountId, Exposure<T::AccountId, BalanceOf<T>>)>> {
1580 <Self as pallet_session::SessionManager<_>>::new_session(new_index).map(|validators| {
1581 let current_era = CurrentEra::<T>::get()
1582 .unwrap_or(0);
1584
1585 validators
1586 .into_iter()
1587 .map(|v| {
1588 let exposure = Self::eras_stakers(current_era, &v);
1589 (v, exposure)
1590 })
1591 .collect()
1592 })
1593 }
1594 fn new_session_genesis(
1595 new_index: SessionIndex,
1596 ) -> Option<Vec<(T::AccountId, Exposure<T::AccountId, BalanceOf<T>>)>> {
1597 <Self as pallet_session::SessionManager<_>>::new_session_genesis(new_index).map(
1598 |validators| {
1599 let current_era = CurrentEra::<T>::get()
1600 .unwrap_or(0);
1602
1603 validators
1604 .into_iter()
1605 .map(|v| {
1606 let exposure = Self::eras_stakers(current_era, &v);
1607 (v, exposure)
1608 })
1609 .collect()
1610 },
1611 )
1612 }
1613 fn start_session(start_index: SessionIndex) {
1614 <Self as pallet_session::SessionManager<_>>::start_session(start_index)
1615 }
1616 fn end_session(end_index: SessionIndex) {
1617 <Self as pallet_session::SessionManager<_>>::end_session(end_index)
1618 }
1619}
1620
1621impl<T: Config> historical::SessionManager<T::AccountId, ()> for Pallet<T> {
1622 fn new_session(new_index: SessionIndex) -> Option<Vec<(T::AccountId, ())>> {
1623 <Self as pallet_session::SessionManager<_>>::new_session(new_index)
1624 .map(|validators| validators.into_iter().map(|v| (v, ())).collect())
1625 }
1626 fn new_session_genesis(new_index: SessionIndex) -> Option<Vec<(T::AccountId, ())>> {
1627 <Self as pallet_session::SessionManager<_>>::new_session_genesis(new_index)
1628 .map(|validators| validators.into_iter().map(|v| (v, ())).collect())
1629 }
1630 fn start_session(start_index: SessionIndex) {
1631 <Self as pallet_session::SessionManager<_>>::start_session(start_index)
1632 }
1633 fn end_session(end_index: SessionIndex) {
1634 <Self as pallet_session::SessionManager<_>>::end_session(end_index)
1635 }
1636}
1637
1638impl<T> pallet_authorship::EventHandler<T::AccountId, BlockNumberFor<T>> for Pallet<T>
1641where
1642 T: Config + pallet_authorship::Config + pallet_session::Config,
1643{
1644 fn note_author(author: T::AccountId) {
1645 Self::reward_by_ids(vec![(author, 20)])
1646 }
1647}
1648
1649impl<T: Config>
1651 OnOffenceHandler<T::AccountId, pallet_session::historical::IdentificationTuple<T>, Weight>
1652 for Pallet<T>
1653where
1654 T: pallet_session::Config<ValidatorId = <T as frame_system::Config>::AccountId>,
1655 T: pallet_session::historical::Config,
1656 T::SessionHandler: pallet_session::SessionHandler<<T as frame_system::Config>::AccountId>,
1657 T::SessionManager: pallet_session::SessionManager<<T as frame_system::Config>::AccountId>,
1658 T::ValidatorIdOf: Convert<
1659 <T as frame_system::Config>::AccountId,
1660 Option<<T as frame_system::Config>::AccountId>,
1661 >,
1662{
1663 fn on_offence(
1664 offenders: &[OffenceDetails<
1665 T::AccountId,
1666 pallet_session::historical::IdentificationTuple<T>,
1667 >],
1668 slash_fractions: &[Perbill],
1669 slash_session: SessionIndex,
1670 ) -> Weight {
1671 log!(
1672 debug,
1673 "🦹 on_offence: offenders={:?}, slash_fractions={:?}, slash_session={}",
1674 offenders,
1675 slash_fractions,
1676 slash_session,
1677 );
1678
1679 let offenders = offenders.iter().map(|details| {
1681 let (ref offender, _) = details.offender;
1682 OffenceDetails { offender: offender.clone(), reporters: details.reporters.clone() }
1683 });
1684
1685 Self::on_offence(offenders, slash_fractions, slash_session)
1686 }
1687}
1688
1689impl<T: Config> ScoreProvider<T::AccountId> for Pallet<T> {
1690 type Score = VoteWeight;
1691
1692 fn score(who: &T::AccountId) -> Self::Score {
1693 Self::weight_of(who)
1694 }
1695
1696 #[cfg(feature = "runtime-benchmarks")]
1697 fn set_score_of(who: &T::AccountId, weight: Self::Score) {
1698 let active: BalanceOf<T> = weight.try_into().map_err(|_| ()).unwrap();
1701 let mut ledger = match Self::ledger(StakingAccount::Stash(who.clone())) {
1702 Ok(l) => l,
1703 Err(_) => StakingLedger::default_from(who.clone()),
1704 };
1705 ledger.active = active;
1706
1707 <Ledger<T>>::insert(who, ledger);
1708 <Bonded<T>>::insert(who, who);
1709
1710 let imbalance = asset::burn::<T>(asset::total_issuance::<T>());
1714 core::mem::forget(imbalance);
1717 }
1718}
1719
1720pub struct UseValidatorsMap<T>(core::marker::PhantomData<T>);
1724impl<T: Config> SortedListProvider<T::AccountId> for UseValidatorsMap<T> {
1725 type Score = BalanceOf<T>;
1726 type Error = ();
1727
1728 fn iter() -> Box<dyn Iterator<Item = T::AccountId>> {
1730 Box::new(Validators::<T>::iter().map(|(v, _)| v))
1731 }
1732 fn iter_from(
1733 start: &T::AccountId,
1734 ) -> Result<Box<dyn Iterator<Item = T::AccountId>>, Self::Error> {
1735 if Validators::<T>::contains_key(start) {
1736 let start_key = Validators::<T>::hashed_key_for(start);
1737 Ok(Box::new(Validators::<T>::iter_from(start_key).map(|(n, _)| n)))
1738 } else {
1739 Err(())
1740 }
1741 }
1742 fn count() -> u32 {
1743 Validators::<T>::count()
1744 }
1745 fn contains(id: &T::AccountId) -> bool {
1746 Validators::<T>::contains_key(id)
1747 }
1748 fn on_insert(_: T::AccountId, _weight: Self::Score) -> Result<(), Self::Error> {
1749 Ok(())
1751 }
1752 fn get_score(id: &T::AccountId) -> Result<Self::Score, Self::Error> {
1753 Ok(Pallet::<T>::weight_of(id).into())
1754 }
1755 fn on_update(_: &T::AccountId, _weight: Self::Score) -> Result<(), Self::Error> {
1756 Ok(())
1758 }
1759 fn on_remove(_: &T::AccountId) -> Result<(), Self::Error> {
1760 Ok(())
1762 }
1763 fn unsafe_regenerate(
1764 _: impl IntoIterator<Item = T::AccountId>,
1765 _: Box<dyn Fn(&T::AccountId) -> Self::Score>,
1766 ) -> u32 {
1767 0
1769 }
1770 #[cfg(feature = "try-runtime")]
1771 fn try_state() -> Result<(), TryRuntimeError> {
1772 Ok(())
1773 }
1774
1775 fn unsafe_clear() {
1776 #[allow(deprecated)]
1777 Validators::<T>::remove_all();
1778 }
1779
1780 #[cfg(feature = "runtime-benchmarks")]
1781 fn score_update_worst_case(_who: &T::AccountId, _is_increase: bool) -> Self::Score {
1782 unimplemented!()
1783 }
1784}
1785
1786pub struct UseNominatorsAndValidatorsMap<T>(core::marker::PhantomData<T>);
1790impl<T: Config> SortedListProvider<T::AccountId> for UseNominatorsAndValidatorsMap<T> {
1791 type Error = ();
1792 type Score = VoteWeight;
1793
1794 fn iter() -> Box<dyn Iterator<Item = T::AccountId>> {
1795 Box::new(
1796 Validators::<T>::iter()
1797 .map(|(v, _)| v)
1798 .chain(Nominators::<T>::iter().map(|(n, _)| n)),
1799 )
1800 }
1801 fn iter_from(
1802 start: &T::AccountId,
1803 ) -> Result<Box<dyn Iterator<Item = T::AccountId>>, Self::Error> {
1804 if Validators::<T>::contains_key(start) {
1805 let start_key = Validators::<T>::hashed_key_for(start);
1806 Ok(Box::new(
1807 Validators::<T>::iter_from(start_key)
1808 .map(|(n, _)| n)
1809 .chain(Nominators::<T>::iter().map(|(x, _)| x)),
1810 ))
1811 } else if Nominators::<T>::contains_key(start) {
1812 let start_key = Nominators::<T>::hashed_key_for(start);
1813 Ok(Box::new(Nominators::<T>::iter_from(start_key).map(|(n, _)| n)))
1814 } else {
1815 Err(())
1816 }
1817 }
1818 fn count() -> u32 {
1819 Nominators::<T>::count().saturating_add(Validators::<T>::count())
1820 }
1821 fn contains(id: &T::AccountId) -> bool {
1822 Nominators::<T>::contains_key(id) || Validators::<T>::contains_key(id)
1823 }
1824 fn on_insert(_: T::AccountId, _weight: Self::Score) -> Result<(), Self::Error> {
1825 Ok(())
1827 }
1828 fn get_score(id: &T::AccountId) -> Result<Self::Score, Self::Error> {
1829 Ok(Pallet::<T>::weight_of(id))
1830 }
1831 fn on_update(_: &T::AccountId, _weight: Self::Score) -> Result<(), Self::Error> {
1832 Ok(())
1834 }
1835 fn on_remove(_: &T::AccountId) -> Result<(), Self::Error> {
1836 Ok(())
1838 }
1839 fn unsafe_regenerate(
1840 _: impl IntoIterator<Item = T::AccountId>,
1841 _: Box<dyn Fn(&T::AccountId) -> Self::Score>,
1842 ) -> u32 {
1843 0
1845 }
1846
1847 #[cfg(feature = "try-runtime")]
1848 fn try_state() -> Result<(), TryRuntimeError> {
1849 Ok(())
1850 }
1851
1852 fn unsafe_clear() {
1853 #[allow(deprecated)]
1856 Nominators::<T>::remove_all();
1857 #[allow(deprecated)]
1858 Validators::<T>::remove_all();
1859 }
1860
1861 #[cfg(feature = "runtime-benchmarks")]
1862 fn score_update_worst_case(_who: &T::AccountId, _is_increase: bool) -> Self::Score {
1863 unimplemented!()
1864 }
1865}
1866
1867impl<T: Config> StakingInterface for Pallet<T> {
1868 type AccountId = T::AccountId;
1869 type Balance = BalanceOf<T>;
1870 type CurrencyToVote = T::CurrencyToVote;
1871
1872 fn minimum_nominator_bond() -> Self::Balance {
1873 MinNominatorBond::<T>::get()
1874 }
1875
1876 fn minimum_validator_bond() -> Self::Balance {
1877 MinValidatorBond::<T>::get()
1878 }
1879
1880 fn stash_by_ctrl(controller: &Self::AccountId) -> Result<Self::AccountId, DispatchError> {
1881 Self::ledger(Controller(controller.clone()))
1882 .map(|l| l.stash)
1883 .map_err(|e| e.into())
1884 }
1885
1886 fn bonding_duration() -> EraIndex {
1887 T::BondingDuration::get()
1888 }
1889
1890 fn current_era() -> EraIndex {
1891 CurrentEra::<T>::get().unwrap_or(Zero::zero())
1892 }
1893
1894 fn stake(who: &Self::AccountId) -> Result<Stake<BalanceOf<T>>, DispatchError> {
1895 Self::ledger(Stash(who.clone()))
1896 .map(|l| Stake { total: l.total, active: l.active })
1897 .map_err(|e| e.into())
1898 }
1899
1900 fn bond_extra(who: &Self::AccountId, extra: Self::Balance) -> DispatchResult {
1901 Self::bond_extra(RawOrigin::Signed(who.clone()).into(), extra)
1902 }
1903
1904 fn unbond(who: &Self::AccountId, value: Self::Balance) -> DispatchResult {
1905 let ctrl = Self::bonded(who).ok_or(Error::<T>::NotStash)?;
1906 Self::unbond(RawOrigin::Signed(ctrl).into(), value)
1907 .map_err(|with_post| with_post.error)
1908 .map(|_| ())
1909 }
1910
1911 fn set_payee(stash: &Self::AccountId, reward_acc: &Self::AccountId) -> DispatchResult {
1912 ensure!(
1916 !Self::is_virtual_staker(stash) || stash != reward_acc,
1917 Error::<T>::RewardDestinationRestricted
1918 );
1919
1920 let ledger = Self::ledger(Stash(stash.clone()))?;
1921 let _ = ledger
1922 .set_payee(RewardDestination::Account(reward_acc.clone()))
1923 .defensive_proof("ledger was retrieved from storage, thus its bonded; qed.")?;
1924
1925 Ok(())
1926 }
1927
1928 fn chill(who: &Self::AccountId) -> DispatchResult {
1929 let ctrl = Self::bonded(who).ok_or(Error::<T>::NotStash)?;
1932 Self::chill(RawOrigin::Signed(ctrl).into())
1933 }
1934
1935 fn withdraw_unbonded(
1936 who: Self::AccountId,
1937 num_slashing_spans: u32,
1938 ) -> Result<bool, DispatchError> {
1939 let ctrl = Self::bonded(&who).ok_or(Error::<T>::NotStash)?;
1940 Self::withdraw_unbonded(RawOrigin::Signed(ctrl.clone()).into(), num_slashing_spans)
1941 .map(|_| !StakingLedger::<T>::is_bonded(StakingAccount::Controller(ctrl)))
1942 .map_err(|with_post| with_post.error)
1943 }
1944
1945 fn bond(
1946 who: &Self::AccountId,
1947 value: Self::Balance,
1948 payee: &Self::AccountId,
1949 ) -> DispatchResult {
1950 Self::bond(
1951 RawOrigin::Signed(who.clone()).into(),
1952 value,
1953 RewardDestination::Account(payee.clone()),
1954 )
1955 }
1956
1957 fn nominate(who: &Self::AccountId, targets: Vec<Self::AccountId>) -> DispatchResult {
1958 let ctrl = Self::bonded(who).ok_or(Error::<T>::NotStash)?;
1959 let targets = targets.into_iter().map(T::Lookup::unlookup).collect::<Vec<_>>();
1960 Self::nominate(RawOrigin::Signed(ctrl).into(), targets)
1961 }
1962
1963 fn desired_validator_count() -> u32 {
1964 ValidatorCount::<T>::get()
1965 }
1966
1967 fn election_ongoing() -> bool {
1968 T::ElectionProvider::ongoing()
1969 }
1970
1971 fn force_unstake(who: Self::AccountId) -> sp_runtime::DispatchResult {
1972 let num_slashing_spans =
1973 SlashingSpans::<T>::get(&who).map_or(0, |s| s.iter().count() as u32);
1974 Self::force_unstake(RawOrigin::Root.into(), who.clone(), num_slashing_spans)
1975 }
1976
1977 fn is_exposed_in_era(who: &Self::AccountId, era: &EraIndex) -> bool {
1978 ErasStakers::<T>::iter_prefix(era).any(|(validator, exposures)| {
1981 validator == *who || exposures.others.iter().any(|i| i.who == *who)
1982 })
1983 ||
1984 ErasStakersPaged::<T>::iter_prefix((era,)).any(|((validator, _), exposure_page)| {
1986 validator == *who || exposure_page.others.iter().any(|i| i.who == *who)
1987 })
1988 }
1989 fn status(
1990 who: &Self::AccountId,
1991 ) -> Result<sp_staking::StakerStatus<Self::AccountId>, DispatchError> {
1992 if !StakingLedger::<T>::is_bonded(StakingAccount::Stash(who.clone())) {
1993 return Err(Error::<T>::NotStash.into())
1994 }
1995
1996 let is_validator = Validators::<T>::contains_key(&who);
1997 let is_nominator = Nominators::<T>::get(&who);
1998
1999 use sp_staking::StakerStatus;
2000 match (is_validator, is_nominator.is_some()) {
2001 (false, false) => Ok(StakerStatus::Idle),
2002 (true, false) => Ok(StakerStatus::Validator),
2003 (false, true) => Ok(StakerStatus::Nominator(
2004 is_nominator.expect("is checked above; qed").targets.into_inner(),
2005 )),
2006 (true, true) => {
2007 defensive!("cannot be both validators and nominator");
2008 Err(Error::<T>::BadState.into())
2009 },
2010 }
2011 }
2012
2013 fn is_virtual_staker(who: &T::AccountId) -> bool {
2018 frame_system::Pallet::<T>::account_nonce(who).is_zero() &&
2019 VirtualStakers::<T>::contains_key(who)
2020 }
2021
2022 fn slash_reward_fraction() -> Perbill {
2023 SlashRewardFraction::<T>::get()
2024 }
2025
2026 sp_staking::runtime_benchmarks_enabled! {
2027 fn nominations(who: &Self::AccountId) -> Option<Vec<T::AccountId>> {
2028 Nominators::<T>::get(who).map(|n| n.targets.into_inner())
2029 }
2030
2031 fn add_era_stakers(
2032 current_era: &EraIndex,
2033 stash: &T::AccountId,
2034 exposures: Vec<(Self::AccountId, Self::Balance)>,
2035 ) {
2036 let others = exposures
2037 .iter()
2038 .map(|(who, value)| IndividualExposure { who: who.clone(), value: *value })
2039 .collect::<Vec<_>>();
2040 let exposure = Exposure { total: Default::default(), own: Default::default(), others };
2041 EraInfo::<T>::set_exposure(*current_era, stash, exposure);
2042 }
2043
2044 fn set_current_era(era: EraIndex) {
2045 CurrentEra::<T>::put(era);
2046 }
2047
2048 fn max_exposure_page_size() -> Page {
2049 T::MaxExposurePageSize::get()
2050 }
2051 }
2052}
2053
2054impl<T: Config> sp_staking::StakingUnchecked for Pallet<T> {
2055 fn migrate_to_virtual_staker(who: &Self::AccountId) -> DispatchResult {
2056 asset::kill_stake::<T>(who)?;
2057 VirtualStakers::<T>::insert(who, ());
2058 Ok(())
2059 }
2060
2061 fn virtual_bond(
2065 keyless_who: &Self::AccountId,
2066 value: Self::Balance,
2067 payee: &Self::AccountId,
2068 ) -> DispatchResult {
2069 if StakingLedger::<T>::is_bonded(StakingAccount::Stash(keyless_who.clone())) {
2070 return Err(Error::<T>::AlreadyBonded.into())
2071 }
2072
2073 ensure!(keyless_who != payee, Error::<T>::RewardDestinationRestricted);
2075
2076 VirtualStakers::<T>::insert(keyless_who, ());
2078
2079 Self::deposit_event(Event::<T>::Bonded { stash: keyless_who.clone(), amount: value });
2080 let ledger = StakingLedger::<T>::new(keyless_who.clone(), value);
2081
2082 ledger.bond(RewardDestination::Account(payee.clone()))?;
2083
2084 Ok(())
2085 }
2086
2087 #[cfg(feature = "runtime-benchmarks")]
2089 fn migrate_to_direct_staker(who: &Self::AccountId) {
2090 assert!(VirtualStakers::<T>::contains_key(who));
2091 let ledger = StakingLedger::<T>::get(Stash(who.clone())).unwrap();
2092 let _ = asset::update_stake::<T>(who, ledger.total)
2093 .expect("funds must be transferred to stash");
2094 VirtualStakers::<T>::remove(who);
2095 }
2096}
2097
2098#[cfg(any(test, feature = "try-runtime"))]
2099impl<T: Config> Pallet<T> {
2100 pub(crate) fn do_try_state(_: BlockNumberFor<T>) -> Result<(), TryRuntimeError> {
2101 ensure!(
2102 T::VoterList::iter()
2103 .all(|x| <Nominators<T>>::contains_key(&x) || <Validators<T>>::contains_key(&x)),
2104 "VoterList contains non-staker"
2105 );
2106
2107 Self::check_ledgers()?;
2108 Self::check_bonded_consistency()?;
2109 Self::check_payees()?;
2110 Self::check_nominators()?;
2111 Self::check_exposures()?;
2112 Self::check_paged_exposures()?;
2113 Self::check_count()
2114 }
2115
2116 fn check_bonded_consistency() -> Result<(), TryRuntimeError> {
2126 use alloc::collections::btree_set::BTreeSet;
2127
2128 let mut count_controller_double = 0;
2129 let mut count_double = 0;
2130 let mut count_none = 0;
2131 let mut controllers = BTreeSet::new();
2134
2135 for (stash, controller) in <Bonded<T>>::iter() {
2136 if !controllers.insert(controller.clone()) {
2137 count_controller_double += 1;
2138 }
2139
2140 match (<Ledger<T>>::get(&stash), <Ledger<T>>::get(&controller)) {
2141 (Some(_), Some(_)) =>
2142 if stash != controller {
2146 count_double += 1;
2147 },
2148 (None, None) => {
2149 count_none += 1;
2150 },
2151 _ => {},
2152 };
2153 }
2154
2155 if count_controller_double != 0 {
2156 log!(
2157 warn,
2158 "a controller is associated with more than one ledger ({} occurrences)",
2159 count_controller_double
2160 );
2161 };
2162
2163 if count_double != 0 {
2164 log!(warn, "single tuple of (stash, controller) pair bonds more than one ledger ({} occurrences)", count_double);
2165 }
2166
2167 if count_none != 0 {
2168 log!(warn, "inconsistent bonded state: (stash, controller) pair missing associated ledger ({} occurrences)", count_none);
2169 }
2170
2171 Ok(())
2172 }
2173
2174 fn check_payees() -> Result<(), TryRuntimeError> {
2179 for (stash, _) in Bonded::<T>::iter() {
2180 ensure!(Payee::<T>::get(&stash).is_some(), "bonded ledger does not have payee set");
2181 }
2182
2183 ensure!(
2184 (Ledger::<T>::iter().count() == Payee::<T>::iter().count()) &&
2185 (Ledger::<T>::iter().count() == Bonded::<T>::iter().count()),
2186 "number of entries in payee storage items does not match the number of bonded ledgers",
2187 );
2188
2189 Ok(())
2190 }
2191
2192 fn check_count() -> Result<(), TryRuntimeError> {
2198 ensure!(
2199 <T as Config>::VoterList::count() ==
2200 Nominators::<T>::count() + Validators::<T>::count(),
2201 "wrong external count"
2202 );
2203 ensure!(
2204 <T as Config>::TargetList::count() == Validators::<T>::count(),
2205 "wrong external count"
2206 );
2207 ensure!(
2208 ValidatorCount::<T>::get() <=
2209 <T::ElectionProvider as frame_election_provider_support::ElectionProviderBase>::MaxWinners::get(),
2210 Error::<T>::TooManyValidators
2211 );
2212 Ok(())
2213 }
2214
2215 fn check_ledgers() -> Result<(), TryRuntimeError> {
2223 Bonded::<T>::iter()
2224 .map(|(stash, ctrl)| {
2225 if VirtualStakers::<T>::contains_key(stash.clone()) {
2227 ensure!(
2228 asset::staked::<T>(&stash) == Zero::zero(),
2229 "virtual stakers should not have any staked balance"
2230 );
2231 ensure!(
2232 <Bonded<T>>::get(stash.clone()).unwrap() == stash.clone(),
2233 "stash and controller should be same"
2234 );
2235 ensure!(
2236 Ledger::<T>::get(stash.clone()).unwrap().stash == stash,
2237 "ledger corrupted for virtual staker"
2238 );
2239 ensure!(
2240 frame_system::Pallet::<T>::account_nonce(&stash).is_zero(),
2241 "virtual stakers are keyless and should not have any nonce"
2242 );
2243 let reward_destination = <Payee<T>>::get(stash.clone()).unwrap();
2244 if let RewardDestination::Account(payee) = reward_destination {
2245 ensure!(
2246 payee != stash.clone(),
2247 "reward destination should not be same as stash for virtual staker"
2248 );
2249 } else {
2250 return Err(DispatchError::Other(
2251 "reward destination must be of account variant for virtual staker",
2252 ));
2253 }
2254 } else {
2255 ensure!(
2256 Self::inspect_bond_state(&stash) == Ok(LedgerIntegrityState::Ok),
2257 "bond, ledger and/or staking hold inconsistent for a bonded stash."
2258 );
2259 }
2260
2261 Self::ensure_ledger_consistent(ctrl)
2263 })
2264 .collect::<Result<Vec<_>, _>>()?;
2265 Ok(())
2266 }
2267
2268 fn check_exposures() -> Result<(), TryRuntimeError> {
2272 let era = ActiveEra::<T>::get().unwrap().index;
2273 ErasStakers::<T>::iter_prefix_values(era)
2274 .map(|expo| {
2275 ensure!(
2276 expo.total ==
2277 expo.own +
2278 expo.others
2279 .iter()
2280 .map(|e| e.value)
2281 .fold(Zero::zero(), |acc, x| acc + x),
2282 "wrong total exposure.",
2283 );
2284 Ok(())
2285 })
2286 .collect::<Result<(), TryRuntimeError>>()
2287 }
2288
2289 fn check_paged_exposures() -> Result<(), TryRuntimeError> {
2294 use alloc::collections::btree_map::BTreeMap;
2295 use sp_staking::PagedExposureMetadata;
2296
2297 let mut exposures: BTreeMap<T::AccountId, PagedExposureMetadata<BalanceOf<T>>> =
2299 BTreeMap::new();
2300 let era = ActiveEra::<T>::get().unwrap().index;
2301 let accumulator_default = PagedExposureMetadata {
2302 total: Zero::zero(),
2303 own: Zero::zero(),
2304 nominator_count: 0,
2305 page_count: 0,
2306 };
2307
2308 ErasStakersPaged::<T>::iter_prefix((era,))
2309 .map(|((validator, _page), expo)| {
2310 ensure!(
2311 expo.page_total ==
2312 expo.others.iter().map(|e| e.value).fold(Zero::zero(), |acc, x| acc + x),
2313 "wrong total exposure for the page.",
2314 );
2315
2316 let metadata = exposures.get(&validator).unwrap_or(&accumulator_default);
2317 exposures.insert(
2318 validator,
2319 PagedExposureMetadata {
2320 total: metadata.total + expo.page_total,
2321 own: metadata.own,
2322 nominator_count: metadata.nominator_count + expo.others.len() as u32,
2323 page_count: metadata.page_count + 1,
2324 },
2325 );
2326
2327 Ok(())
2328 })
2329 .collect::<Result<(), TryRuntimeError>>()?;
2330
2331 exposures
2332 .iter()
2333 .map(|(validator, metadata)| {
2334 let actual_overview = ErasStakersOverview::<T>::get(era, validator);
2335
2336 ensure!(actual_overview.is_some(), "No overview found for a paged exposure");
2337 let actual_overview = actual_overview.unwrap();
2338
2339 ensure!(
2340 actual_overview.total == metadata.total + actual_overview.own,
2341 "Exposure metadata does not have correct total exposed stake."
2342 );
2343 ensure!(
2344 actual_overview.nominator_count == metadata.nominator_count,
2345 "Exposure metadata does not have correct count of nominators."
2346 );
2347 ensure!(
2348 actual_overview.page_count == metadata.page_count,
2349 "Exposure metadata does not have correct count of pages."
2350 );
2351
2352 Ok(())
2353 })
2354 .collect::<Result<(), TryRuntimeError>>()
2355 }
2356
2357 fn check_nominators() -> Result<(), TryRuntimeError> {
2360 let era = ActiveEra::<T>::get().unwrap().index;
2363
2364 let era_exposures = T::SessionInterface::validators()
2366 .iter()
2367 .map(|v| Self::eras_stakers(era, v))
2368 .collect::<Vec<_>>();
2369
2370 <Nominators<T>>::iter()
2371 .filter_map(
2372 |(nominator, nomination)| {
2373 if nomination.submitted_in < era {
2374 Some(nominator)
2375 } else {
2376 None
2377 }
2378 },
2379 )
2380 .map(|nominator| -> Result<(), TryRuntimeError> {
2381 Self::ensure_is_stash(&nominator)?;
2383 let mut sum_exposed = BalanceOf::<T>::zero();
2384 era_exposures
2385 .iter()
2386 .map(|e| -> Result<(), TryRuntimeError> {
2387 let individual =
2388 e.others.iter().filter(|e| e.who == nominator).collect::<Vec<_>>();
2389 let len = individual.len();
2390 match len {
2391 0 => { },
2392 1 => sum_exposed += individual[0].value,
2393 _ =>
2394 return Err(
2395 "nominator cannot back a validator more than once.".into()
2396 ),
2397 };
2398 Ok(())
2399 })
2400 .collect::<Result<Vec<_>, _>>()?;
2401
2402 if sum_exposed > Self::ledger(Stash(nominator.clone()))?.total {
2405 log!(
2407 warn,
2408 "nominator {:?} stake {:?} exceeds the sum_exposed of exposures {:?}.",
2409 nominator,
2410 Self::ledger(Stash(nominator.clone()))?.total,
2411 sum_exposed,
2412 );
2413 }
2414
2415 Ok(())
2416 })
2417 .collect::<Result<Vec<_>, _>>()?;
2418
2419 Ok(())
2420 }
2421
2422 fn ensure_is_stash(who: &T::AccountId) -> Result<(), &'static str> {
2423 ensure!(Self::bonded(who).is_some(), "Not a stash.");
2424 Ok(())
2425 }
2426
2427 fn ensure_ledger_consistent(ctrl: T::AccountId) -> Result<(), TryRuntimeError> {
2428 let ledger = Self::ledger(StakingAccount::Controller(ctrl.clone()))?;
2430
2431 let real_total: BalanceOf<T> =
2432 ledger.unlocking.iter().fold(ledger.active, |a, c| a + c.value);
2433 ensure!(real_total == ledger.total, "ledger.total corrupt");
2434
2435 Ok(())
2436 }
2437}