1use crate::*;
80use alloc::vec::Vec;
81use frame_election_provider_support::{BoundedSupportsOf, ElectionProvider, PageIndex};
82use frame_support::{
83 pallet_prelude::*,
84 traits::{Defensive, DefensiveMax, DefensiveSaturating, OnUnbalanced, TryCollect},
85};
86use sp_runtime::{Perbill, Percent, Saturating};
87use sp_staking::{
88 currency_to_vote::CurrencyToVote, Exposure, Page, PagedExposureMetadata, SessionIndex,
89};
90
91pub struct Eras<T: Config>(core::marker::PhantomData<T>);
103
104impl<T: Config> Eras<T> {
105 pub(crate) fn set_validator_prefs(era: EraIndex, stash: &T::AccountId, prefs: ValidatorPrefs) {
106 debug_assert_eq!(era, Rotator::<T>::planned_era(), "we only set prefs for planning era");
107 <ErasValidatorPrefs<T>>::insert(era, stash, prefs);
108 }
109
110 pub(crate) fn get_validator_prefs(era: EraIndex, stash: &T::AccountId) -> ValidatorPrefs {
111 <ErasValidatorPrefs<T>>::get(era, stash)
112 }
113
114 pub(crate) fn get_validator_commission(era: EraIndex, stash: &T::AccountId) -> Perbill {
116 Self::get_validator_prefs(era, stash).commission
117 }
118
119 pub(crate) fn pending_rewards(era: EraIndex, validator: &T::AccountId) -> bool {
121 <ErasStakersOverview<T>>::get(&era, validator)
122 .map(|overview| {
123 ClaimedRewards::<T>::get(era, validator).len() < overview.page_count as usize
124 })
125 .unwrap_or(false)
126 }
127
128 pub(crate) fn get_paged_exposure(
133 era: EraIndex,
134 validator: &T::AccountId,
135 page: Page,
136 ) -> Option<PagedExposure<T::AccountId, BalanceOf<T>>> {
137 let overview = <ErasStakersOverview<T>>::get(&era, validator)?;
138
139 let validator_stake = if page == 0 { overview.own } else { Zero::zero() };
141
142 let exposure_page = <ErasStakersPaged<T>>::get((era, validator, page)).unwrap_or_default();
145
146 Some(PagedExposure {
148 exposure_metadata: PagedExposureMetadata { own: validator_stake, ..overview },
149 exposure_page: exposure_page.into(),
150 })
151 }
152
153 pub(crate) fn get_full_exposure(
155 era: EraIndex,
156 validator: &T::AccountId,
157 ) -> Exposure<T::AccountId, BalanceOf<T>> {
158 let Some(overview) = <ErasStakersOverview<T>>::get(&era, validator) else {
159 return Exposure::default();
160 };
161
162 let mut others = Vec::with_capacity(overview.nominator_count as usize);
163 for page in 0..overview.page_count {
164 let nominators = <ErasStakersPaged<T>>::get((era, validator, page));
165 others.append(&mut nominators.map(|n| n.others.clone()).defensive_unwrap_or_default());
166 }
167
168 Exposure { total: overview.total, own: overview.own, others }
169 }
170
171 pub(crate) fn exposure_page_count(era: EraIndex, validator: &T::AccountId) -> Page {
175 <ErasStakersOverview<T>>::get(&era, validator)
176 .map(|overview| {
177 if overview.page_count == 0 && overview.own > Zero::zero() {
178 1
181 } else {
182 overview.page_count
183 }
184 })
185 .unwrap_or(1)
188 }
189
190 pub(crate) fn get_next_claimable_page(era: EraIndex, validator: &T::AccountId) -> Option<Page> {
192 let page_count = Self::exposure_page_count(era, validator);
194 let all_claimable_pages: Vec<Page> = (0..page_count).collect();
195 let claimed_pages = ClaimedRewards::<T>::get(era, validator);
196
197 all_claimable_pages.into_iter().find(|p| !claimed_pages.contains(p))
198 }
199
200 pub(crate) fn set_rewards_as_claimed(era: EraIndex, validator: &T::AccountId, page: Page) {
203 let mut claimed_pages = ClaimedRewards::<T>::get(era, validator).into_inner();
204
205 if claimed_pages.contains(&page) {
207 defensive!("Trying to set an already claimed reward");
208 return
210 }
211
212 claimed_pages.push(page);
214 ClaimedRewards::<T>::insert(
215 era,
216 validator,
217 WeakBoundedVec::<_, _>::force_from(claimed_pages, Some("set_rewards_as_claimed")),
218 );
219 }
220
221 pub fn upsert_exposure(
228 era: EraIndex,
229 validator: &T::AccountId,
230 mut exposure: Exposure<T::AccountId, BalanceOf<T>>,
231 ) {
232 let page_size = T::MaxExposurePageSize::get().defensive_max(1);
233
234 if let Some(stored_overview) = ErasStakersOverview::<T>::get(era, &validator) {
235 let last_page_idx = stored_overview.page_count.saturating_sub(1);
236
237 let mut last_page =
238 ErasStakersPaged::<T>::get((era, validator, last_page_idx)).unwrap_or_default();
239 let last_page_empty_slots =
240 T::MaxExposurePageSize::get().saturating_sub(last_page.others.len() as u32);
241
242 let exposures_append = exposure.split_others(last_page_empty_slots);
246
247 ErasStakersOverview::<T>::mutate(era, &validator, |stored| {
248 let new_metadata =
252 stored.defensive_unwrap_or_default().update_with::<T::MaxExposurePageSize>(
253 [&exposures_append, &exposure]
254 .iter()
255 .fold(Default::default(), |total, expo| {
256 total.saturating_add(expo.total.saturating_sub(expo.own))
257 }),
258 [&exposures_append, &exposure]
259 .iter()
260 .fold(Default::default(), |count, expo| {
261 count.saturating_add(expo.others.len() as u32)
262 }),
263 );
264 *stored = new_metadata.into();
265 });
266
267 last_page.page_total = last_page
269 .page_total
270 .saturating_add(exposures_append.total)
271 .saturating_sub(exposures_append.own);
272 last_page.others.extend(exposures_append.others);
273 ErasStakersPaged::<T>::insert((era, &validator, last_page_idx), last_page);
274
275 let (_, exposure_pages) = exposure.into_pages(page_size);
278
279 exposure_pages.into_iter().enumerate().for_each(|(idx, paged_exposure)| {
280 let append_at =
281 (last_page_idx.saturating_add(1).saturating_add(idx as u32)) as Page;
282 <ErasStakersPaged<T>>::insert((era, &validator, append_at), paged_exposure);
283 });
284 } else {
285 let expected_page_count = exposure
287 .others
288 .len()
289 .defensive_saturating_add((page_size as usize).defensive_saturating_sub(1))
290 .saturating_div(page_size as usize);
291
292 let (exposure_metadata, exposure_pages) = exposure.into_pages(page_size);
295 defensive_assert!(exposure_pages.len() == expected_page_count, "unexpected page count");
296
297 ErasStakersOverview::<T>::insert(era, &validator, exposure_metadata);
299
300 exposure_pages.into_iter().enumerate().for_each(|(idx, paged_exposure)| {
302 let append_at = idx as Page;
303 <ErasStakersPaged<T>>::insert((era, &validator, append_at), paged_exposure);
304 });
305 };
306 }
307
308 pub(crate) fn set_validators_reward(era: EraIndex, amount: BalanceOf<T>) {
309 ErasValidatorReward::<T>::insert(era, amount);
310 }
311
312 pub(crate) fn get_validators_reward(era: EraIndex) -> Option<BalanceOf<T>> {
313 ErasValidatorReward::<T>::get(era)
314 }
315
316 pub(crate) fn add_total_stake(era: EraIndex, stake: BalanceOf<T>) {
318 <ErasTotalStake<T>>::mutate(era, |total_stake| {
319 *total_stake += stake;
320 });
321 }
322
323 pub(crate) fn is_rewards_claimed(era: EraIndex, validator: &T::AccountId, page: Page) -> bool {
325 ClaimedRewards::<T>::get(era, validator).contains(&page)
326 }
327
328 pub(crate) fn reward_active_era(
330 validators_points: impl IntoIterator<Item = (T::AccountId, u32)>,
331 ) {
332 if let Some(active_era) = ActiveEra::<T>::get() {
333 <ErasRewardPoints<T>>::mutate(active_era.index, |era_rewards| {
334 for (validator, points) in validators_points.into_iter() {
335 match era_rewards.individual.get_mut(&validator) {
336 Some(individual) => individual.saturating_accrue(points),
337 None => {
338 let _ =
341 era_rewards.individual.try_insert(validator, points).defensive();
342 },
343 }
344 era_rewards.total.saturating_accrue(points);
345 }
346 });
347 }
348 }
349
350 pub(crate) fn get_reward_points(era: EraIndex) -> EraRewardPoints<T> {
351 ErasRewardPoints::<T>::get(era)
352 }
353}
354
355#[cfg(any(feature = "try-runtime", test, feature = "runtime-benchmarks"))]
356#[allow(unused)]
357impl<T: Config> Eras<T> {
358 pub(crate) fn era_fully_present(era: EraIndex) -> Result<(), sp_runtime::TryRuntimeError> {
360 let e0 = ErasValidatorPrefs::<T>::iter_prefix_values(era).count() != 0;
362 let e1 = ErasStakersOverview::<T>::iter_prefix_values(era).count() != 0;
364 ensure!(e0 == e1, "ErasValidatorPrefs and ErasStakersOverview should be consistent");
365
366 let e2 = ErasTotalStake::<T>::contains_key(era);
368
369 let active_era = Rotator::<T>::active_era();
370 let e4 = if era.saturating_sub(1) > 0 &&
371 era.saturating_sub(1) > active_era.saturating_sub(T::HistoryDepth::get() + 1)
372 {
373 ErasValidatorReward::<T>::contains_key(era.saturating_sub(1))
377 } else {
378 e2
380 };
381
382 ensure!(e2 == e4, "era info presence not consistent");
383
384 if e2 {
385 Ok(())
386 } else {
387 Err("era presence mismatch".into())
388 }
389 }
390
391 pub(crate) fn era_pruning_in_progress(era: EraIndex) -> bool {
393 EraPruningState::<T>::contains_key(era)
394 }
395
396 pub(crate) fn era_absent_or_pruning(era: EraIndex) -> Result<(), sp_runtime::TryRuntimeError> {
398 if Self::era_pruning_in_progress(era) {
399 Ok(())
400 } else {
401 Self::era_absent(era)
402 }
403 }
404
405 pub(crate) fn era_absent(era: EraIndex) -> Result<(), sp_runtime::TryRuntimeError> {
408 let e0 = ErasValidatorPrefs::<T>::iter_prefix_values(era).count() != 0;
410 let e1 = ErasStakersPaged::<T>::iter_prefix_values((era,)).count() != 0;
411 let e2 = ErasStakersOverview::<T>::iter_prefix_values(era).count() != 0;
412
413 let e3 = ErasValidatorReward::<T>::contains_key(era);
416 let e4 = ErasTotalStake::<T>::contains_key(era);
417
418 let e6 = ClaimedRewards::<T>::iter_prefix_values(era).count() != 0;
420 let e7 = ErasRewardPoints::<T>::contains_key(era);
421
422 if !vec![e0, e1, e2, e3, e4, e6, e7].windows(2).all(|w| w[0] == w[1]) {
424 return Err("era info absence not consistent - partial pruning state".into());
425 }
426
427 if !e0 {
428 Ok(())
429 } else {
430 Err("era absence mismatch".into())
431 }
432 }
433
434 pub(crate) fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
435 let active_era = Rotator::<T>::active_era();
437 let oldest_present_era = active_era.saturating_sub(T::HistoryDepth::get()).max(1);
440
441 for e in oldest_present_era..=active_era {
442 Self::era_fully_present(e)?
443 }
444
445 ensure!(
448 (1..oldest_present_era).all(|e| Self::era_absent_or_pruning(e).is_ok()),
449 "All old eras must be either fully pruned or marked for pruning"
450 );
451
452 Ok(())
453 }
454}
455
456pub struct Rotator<T: Config>(core::marker::PhantomData<T>);
465
466impl<T: Config> Rotator<T> {
467 #[cfg(feature = "runtime-benchmarks")]
468 pub(crate) fn legacy_insta_plan_era() -> Vec<T::AccountId> {
469 Self::plan_new_era();
471 <<T as Config>::ElectionProvider as ElectionProvider>::asap();
473 let msp = <T::ElectionProvider as ElectionProvider>::msp();
476 let lsp = 0;
477 for p in (lsp..=msp).rev() {
478 EraElectionPlanner::<T>::do_elect_paged(p);
479 }
480
481 crate::ElectableStashes::<T>::take().into_iter().collect()
482 }
483
484 #[cfg(any(feature = "try-runtime", test))]
485 pub(crate) fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
486 let active_era = ActiveEra::<T>::get();
488 let planned_era = CurrentEra::<T>::get();
489
490 let bonded = BondedEras::<T>::get();
491
492 match (&active_era, &planned_era) {
493 (None, None) => {
494 ensure!(bonded.is_empty(), "BondedEras must be empty when ActiveEra is None");
496 },
497 (Some(active), Some(planned)) => {
498 ensure!(
500 *planned == active.index || *planned == active.index + 1,
501 "planned era is always equal or one more than active"
502 );
503
504 ensure!(
507 bonded.into_iter().map(|(era, _sess)| era).collect::<Vec<_>>() ==
508 (active.index.saturating_sub(T::BondingDuration::get())..=active.index)
509 .collect::<Vec<_>>(),
510 "BondedEras range incorrect"
511 );
512 },
513 _ => {
514 ensure!(false, "ActiveEra and CurrentEra must both be None or both be Some");
515 },
516 }
517
518 Ok(())
519 }
520
521 #[cfg(any(feature = "try-runtime", feature = "std", feature = "runtime-benchmarks", test))]
522 pub fn assert_election_ongoing() {
523 assert!(Self::is_planning().is_some(), "planning era must exist");
524 assert!(
525 T::ElectionProvider::status().is_ok(),
526 "Election provider must be in a good state during election"
527 );
528 }
529
530 pub fn planned_era() -> EraIndex {
538 CurrentEra::<T>::get().unwrap_or(0)
539 }
540
541 pub fn active_era() -> EraIndex {
542 ActiveEra::<T>::get().map(|a| a.index).defensive_unwrap_or(0)
543 }
544
545 pub fn is_planning() -> Option<EraIndex> {
549 let (active, planned) = (Self::active_era(), Self::planned_era());
550 if planned.defensive_saturating_sub(active) > 1 {
551 defensive!("planned era must always be equal or one more than active");
552 }
553
554 (planned > active).then_some(planned)
555 }
556
557 pub(crate) fn end_session(
559 end_index: SessionIndex,
560 activation_timestamp: Option<(u64, u32)>,
561 ) -> Weight {
562 let weight = T::WeightInfo::rc_on_session_report();
564
565 let Some(active_era) = ActiveEra::<T>::get() else {
566 defensive!("Active era must always be available.");
567 return weight;
568 };
569 let current_planned_era = Self::is_planning();
570 let starting = end_index + 1;
571 let planning = starting + 1;
573
574 log!(
575 info,
576 "Session: end {:?}, start {:?} (ts: {:?}), planning {:?}",
577 end_index,
578 starting,
579 activation_timestamp,
580 planning
581 );
582 log!(info, "Era: active {:?}, planned {:?}", active_era.index, current_planned_era);
583
584 match activation_timestamp {
585 Some((time, id)) if Some(id) == current_planned_era => {
586 Self::start_era(active_era, starting, time);
588 },
589 Some((_time, id)) => {
590 crate::log!(
592 warn,
593 "received wrong ID with activation timestamp. Got {}, expected {:?}",
594 id,
595 current_planned_era
596 );
597 Pallet::<T>::deposit_event(Event::Unexpected(
598 UnexpectedKind::UnknownValidatorActivation,
599 ));
600 },
601 None => (),
602 }
603
604 let should_plan_era = match ForceEra::<T>::get() {
606 Forcing::NotForcing => Self::is_plan_era_deadline(starting),
608 Forcing::ForceNew => {
610 ForceEra::<T>::put(Forcing::NotForcing);
611 true
612 },
613 Forcing::ForceAlways => true,
615 Forcing::ForceNone => false,
617 };
618
619 let has_pending_era = Self::is_planning().is_some();
622 match (should_plan_era, has_pending_era) {
623 (false, _) => {
624 },
626 (true, false) => {
627 Self::plan_new_era();
629 },
630 (true, true) => {
631 crate::log!(
634 debug,
635 "time to plan a new era {:?}, but waiting for the activation of the previous.",
636 current_planned_era
637 );
638 },
639 }
640
641 Pallet::<T>::deposit_event(Event::SessionRotated {
642 starting_session: starting,
643 active_era: Self::active_era(),
644 planned_era: Self::planned_era(),
645 });
646
647 weight
648 }
649
650 pub(crate) fn start_era(
651 ending_era: ActiveEraInfo,
652 starting_session: SessionIndex,
653 new_era_start_timestamp: u64,
654 ) {
655 debug_assert!(CurrentEra::<T>::get().unwrap_or(0) == ending_era.index + 1);
657
658 let starting_era = ending_era.index + 1;
659
660 Self::end_era(&ending_era, new_era_start_timestamp);
662
663 Self::start_era_inc_active_era(new_era_start_timestamp);
665 Self::start_era_update_bonded_eras(starting_era, starting_session);
666
667 EraElectionPlanner::<T>::cleanup();
669
670 if let Some(old_era) = starting_era.checked_sub(T::HistoryDepth::get() + 1) {
672 log!(debug, "Marking era {:?} for lazy pruning", old_era);
673 EraPruningState::<T>::insert(old_era, PruningStep::ErasStakersPaged);
674 }
675 }
676
677 fn start_era_inc_active_era(start_timestamp: u64) {
678 ActiveEra::<T>::mutate(|active_era| {
679 let new_index = active_era.as_ref().map(|info| info.index + 1).unwrap_or(0);
680 log!(
681 debug,
682 "starting active era {:?} with RC-provided timestamp {:?}",
683 new_index,
684 start_timestamp
685 );
686 *active_era = Some(ActiveEraInfo { index: new_index, start: Some(start_timestamp) });
687 });
688 }
689
690 pub fn active_era_start_session_index() -> SessionIndex {
694 Self::era_start_session_index(Self::active_era()).defensive_unwrap_or(0)
695 }
696
697 pub fn era_start_session_index(era: EraIndex) -> Option<SessionIndex> {
699 BondedEras::<T>::get()
700 .into_iter()
701 .rev()
702 .find_map(|(e, s)| if e == era { Some(s) } else { None })
703 }
704
705 fn start_era_update_bonded_eras(starting_era: EraIndex, start_session: SessionIndex) {
706 let bonding_duration = T::BondingDuration::get();
707
708 BondedEras::<T>::mutate(|bonded| {
709 if bonded.is_full() {
710 let (era_removed, _) = bonded.remove(0);
712 debug_assert!(
713 era_removed <= (starting_era.saturating_sub(bonding_duration)),
714 "should not delete an era that is not older than bonding duration"
715 );
716 slashing::clear_era_metadata::<T>(era_removed);
717 }
718
719 let _ = bonded.try_push((starting_era, start_session)).defensive();
721 });
722 }
723
724 fn end_era(ending_era: &ActiveEraInfo, new_era_start: u64) {
725 let previous_era_start = ending_era.start.defensive_unwrap_or(new_era_start);
726 let uncapped_era_duration = new_era_start.saturating_sub(previous_era_start);
727
728 let cap = T::MaxEraDuration::get();
730 let era_duration = if cap == 0 {
731 uncapped_era_duration
733 } else if uncapped_era_duration > cap {
734 Pallet::<T>::deposit_event(Event::Unexpected(UnexpectedKind::EraDurationBoundExceeded));
735
736 log!(
739 warn,
740 "capping era duration for era {:?} from {:?} to max allowed {:?}",
741 ending_era.index,
742 uncapped_era_duration,
743 cap
744 );
745 cap
746 } else {
747 uncapped_era_duration
748 };
749
750 Self::end_era_compute_payout(ending_era, era_duration);
751 }
752
753 fn end_era_compute_payout(ending_era: &ActiveEraInfo, era_duration: u64) {
754 let staked = ErasTotalStake::<T>::get(ending_era.index);
755 let issuance = asset::total_issuance::<T>();
756
757 log!(
758 debug,
759 "computing inflation for era {:?} with duration {:?}",
760 ending_era.index,
761 era_duration
762 );
763 let (validator_payout, remainder) =
764 T::EraPayout::era_payout(staked, issuance, era_duration);
765
766 let total_payout = validator_payout.saturating_add(remainder);
767 let max_staked_rewards = MaxStakedRewards::<T>::get().unwrap_or(Percent::from_percent(100));
768
769 let validator_payout = validator_payout.min(max_staked_rewards * total_payout);
771 let remainder = total_payout.saturating_sub(validator_payout);
772
773 Pallet::<T>::deposit_event(Event::<T>::EraPaid {
774 era_index: ending_era.index,
775 validator_payout,
776 remainder,
777 });
778
779 Eras::<T>::set_validators_reward(ending_era.index, validator_payout);
781 T::RewardRemainder::on_unbalanced(asset::issue::<T>(remainder));
782 }
783
784 fn plan_new_era() {
788 let _ = CurrentEra::<T>::try_mutate(|x| {
789 log!(info, "Planning new era: {:?}, sending election start signal", x.unwrap_or(0));
790 let could_start_election = EraElectionPlanner::<T>::plan_new_election();
791 *x = Some(x.unwrap_or(0) + 1);
792 could_start_election
793 });
794 }
795
796 fn is_plan_era_deadline(start_session: SessionIndex) -> bool {
798 let planning_era_offset = T::PlanningEraOffset::get().min(T::SessionsPerEra::get());
799 let target_plan_era_session = T::SessionsPerEra::get().saturating_sub(planning_era_offset);
801 let era_start_session = Self::active_era_start_session_index();
802
803 let session_progress = start_session.defensive_saturating_sub(era_start_session);
805
806 log!(
807 debug,
808 "Session progress within era: {:?}, target_plan_era_session: {:?}",
809 session_progress,
810 target_plan_era_session
811 );
812 session_progress >= target_plan_era_session
813 }
814}
815
816pub(crate) struct EraElectionPlanner<T: Config>(PhantomData<T>);
841impl<T: Config> EraElectionPlanner<T> {
842 pub(crate) fn cleanup() {
844 VoterSnapshotStatus::<T>::kill();
845 NextElectionPage::<T>::kill();
846 ElectableStashes::<T>::kill();
847 Pallet::<T>::register_weight(T::DbWeight::get().writes(3));
848 }
849
850 pub(crate) fn election_pages() -> u32 {
852 <<T as Config>::ElectionProvider as ElectionProvider>::Pages::get()
853 }
854
855 pub(crate) fn plan_new_election() -> Result<(), <T::ElectionProvider as ElectionProvider>::Error>
857 {
858 T::ElectionProvider::start()
859 .inspect_err(|e| log!(warn, "Election provider failed to start: {:?}", e))
860 }
861
862 pub(crate) fn maybe_fetch_election_results() {
864 if let Ok(true) = T::ElectionProvider::status() {
865 crate::log!(
866 debug,
867 "Election provider is ready, our status is {:?}",
868 NextElectionPage::<T>::get()
869 );
870
871 debug_assert!(
872 CurrentEra::<T>::get().unwrap_or(0) ==
873 ActiveEra::<T>::get().map_or(0, |a| a.index) + 1,
874 "Next era must be already planned."
875 );
876
877 let current_page = NextElectionPage::<T>::get()
878 .unwrap_or(Self::election_pages().defensive_saturating_sub(1));
879 let maybe_next_page = current_page.checked_sub(1);
880 crate::log!(debug, "fetching page {:?}, next {:?}", current_page, maybe_next_page);
881
882 Self::do_elect_paged(current_page);
883 NextElectionPage::<T>::set(maybe_next_page);
884
885 if maybe_next_page.is_none() {
888 use pallet_staking_async_rc_client::RcClientInterface;
889 let id = CurrentEra::<T>::get().defensive_unwrap_or(0);
890 let prune_up_to = Self::get_prune_up_to();
891 let rc_validators = ElectableStashes::<T>::take().into_iter().collect::<Vec<_>>();
892
893 crate::log!(
894 info,
895 "Sending new validator set of size {:?} to RC. ID: {:?}, prune_up_to: {:?}",
896 rc_validators.len(),
897 id,
898 prune_up_to
899 );
900
901 T::RcClientInterface::validator_set(rc_validators, id, prune_up_to);
902 }
903 }
904 }
905
906 fn get_prune_up_to() -> Option<SessionIndex> {
909 let bonded_eras = BondedEras::<T>::get();
910
911 if bonded_eras.is_full() {
913 bonded_eras.first().map(|(_, first_session)| first_session.saturating_sub(1))
914 } else {
915 None
916 }
917 }
918
919 pub(crate) fn do_elect_paged(page: PageIndex) {
931 let election_result = T::ElectionProvider::elect(page);
932 match election_result {
933 Ok(supports) => {
934 let inner_processing_results = Self::do_elect_paged_inner(supports);
935 if let Err(not_included) = inner_processing_results {
936 defensive!(
937 "electable stashes exceeded limit, unexpected but election proceeds.\
938 {} stashes from election result discarded",
939 not_included
940 );
941 };
942
943 Pallet::<T>::deposit_event(Event::PagedElectionProceeded {
944 page,
945 result: inner_processing_results.map(|x| x as u32).map_err(|x| x as u32),
946 });
947 },
948 Err(e) => {
949 log!(warn, "election provider page failed due to {:?} (page: {})", e, page);
950 Pallet::<T>::deposit_event(Event::PagedElectionProceeded { page, result: Err(0) });
951 },
952 }
953 }
954
955 pub(crate) fn do_elect_paged_inner(
961 mut supports: BoundedSupportsOf<T::ElectionProvider>,
962 ) -> Result<usize, usize> {
963 let planning_era = Rotator::<T>::planned_era();
964
965 match Self::add_electables(supports.iter().map(|(s, _)| s.clone())) {
966 Ok(added) => {
967 let exposures = Self::collect_exposures(supports);
968 let _ = Self::store_stakers_info(exposures, planning_era);
969 Ok(added)
970 },
971 Err(not_included_idx) => {
972 let not_included = supports.len().saturating_sub(not_included_idx);
973
974 log!(
975 warn,
976 "not all winners fit within the electable stashes, excluding {:?} accounts from solution.",
977 not_included,
978 );
979
980 supports.truncate(not_included_idx);
983 let exposures = Self::collect_exposures(supports);
984 let _ = Self::store_stakers_info(exposures, planning_era);
985
986 Err(not_included)
987 },
988 }
989 }
990
991 pub(crate) fn store_stakers_info(
995 exposures: BoundedExposuresOf<T>,
996 new_planned_era: EraIndex,
997 ) -> BoundedVec<T::AccountId, MaxWinnersPerPageOf<T::ElectionProvider>> {
998 let mut total_stake_page: BalanceOf<T> = Zero::zero();
1000 let mut elected_stashes_page = Vec::with_capacity(exposures.len());
1001 let mut total_backers = 0u32;
1002
1003 exposures.into_iter().for_each(|(stash, exposure)| {
1004 log!(
1005 trace,
1006 "stored exposure for stash {:?} and {:?} backers",
1007 stash,
1008 exposure.others.len()
1009 );
1010 elected_stashes_page.push(stash.clone());
1012 total_stake_page = total_stake_page.saturating_add(exposure.total);
1014 total_backers += exposure.others.len() as u32;
1016 Eras::<T>::upsert_exposure(new_planned_era, &stash, exposure);
1017 });
1018
1019 let elected_stashes: BoundedVec<_, MaxWinnersPerPageOf<T::ElectionProvider>> =
1020 elected_stashes_page
1021 .try_into()
1022 .expect("both types are bounded by MaxWinnersPerPageOf; qed");
1023
1024 Eras::<T>::add_total_stake(new_planned_era, total_stake_page);
1026
1027 for stash in &elected_stashes {
1029 let pref = Validators::<T>::get(stash);
1030 Eras::<T>::set_validator_prefs(new_planned_era, stash, pref);
1031 }
1032
1033 log!(
1034 debug,
1035 "stored a page of stakers with {:?} validators and {:?} total backers for era {:?}",
1036 elected_stashes.len(),
1037 total_backers,
1038 new_planned_era,
1039 );
1040
1041 elected_stashes
1042 }
1043
1044 fn collect_exposures(
1050 supports: BoundedSupportsOf<T::ElectionProvider>,
1051 ) -> BoundedExposuresOf<T> {
1052 let total_issuance = asset::total_issuance::<T>();
1053 let to_currency = |e: frame_election_provider_support::ExtendedBalance| {
1054 T::CurrencyToVote::to_currency(e, total_issuance)
1055 };
1056
1057 supports
1058 .into_iter()
1059 .map(|(validator, support)| {
1060 let mut others = Vec::with_capacity(support.voters.len());
1062 let mut own: BalanceOf<T> = Zero::zero();
1063 let mut total: BalanceOf<T> = Zero::zero();
1064 support
1065 .voters
1066 .into_iter()
1067 .map(|(nominator, weight)| (nominator, to_currency(weight)))
1068 .for_each(|(nominator, stake)| {
1069 if nominator == validator {
1070 defensive_assert!(own == Zero::zero(), "own stake should be unique");
1071 own = own.saturating_add(stake);
1072 } else {
1073 others.push(IndividualExposure { who: nominator, value: stake });
1074 }
1075 total = total.saturating_add(stake);
1076 });
1077
1078 let exposure = Exposure { own, others, total };
1079 (validator, exposure)
1080 })
1081 .try_collect()
1082 .expect("we only map through support vector which cannot change the size; qed")
1083 }
1084
1085 pub(crate) fn add_electables(
1092 new_stashes: impl Iterator<Item = T::AccountId>,
1093 ) -> Result<usize, usize> {
1094 ElectableStashes::<T>::mutate(|electable| {
1095 let pre_size = electable.len();
1096
1097 for (idx, stash) in new_stashes.enumerate() {
1098 if electable.try_insert(stash).is_err() {
1099 return Err(idx);
1100 }
1101 }
1102
1103 Ok(electable.len() - pre_size)
1104 })
1105 }
1106}