1use crate::*;
80use alloc::{boxed::Box, vec::Vec};
81use frame_election_provider_support::{BoundedSupportsOf, ElectionProvider, PageIndex};
82use frame_support::{
83 pallet_prelude::*,
84 traits::{Defensive, DefensiveMax, DefensiveSaturating, OnUnbalanced, TryCollect},
85 weights::WeightMeter,
86};
87use pallet_staking_async_rc_client::RcClientInterface;
88use sp_runtime::{Perbill, Percent, Saturating};
89use sp_staking::{
90 currency_to_vote::CurrencyToVote, Exposure, Page, PagedExposureMetadata, SessionIndex,
91};
92
93pub struct Eras<T: Config>(core::marker::PhantomData<T>);
105
106impl<T: Config> Eras<T> {
107 pub(crate) fn set_validator_prefs(era: EraIndex, stash: &T::AccountId, prefs: ValidatorPrefs) {
108 debug_assert_eq!(era, Rotator::<T>::planned_era(), "we only set prefs for planning era");
109 <ErasValidatorPrefs<T>>::insert(era, stash, prefs);
110 }
111
112 pub(crate) fn get_validator_prefs(era: EraIndex, stash: &T::AccountId) -> ValidatorPrefs {
113 <ErasValidatorPrefs<T>>::get(era, stash)
114 }
115
116 pub(crate) fn get_validator_commission(era: EraIndex, stash: &T::AccountId) -> Perbill {
118 Self::get_validator_prefs(era, stash).commission
119 }
120
121 pub(crate) fn pending_rewards(era: EraIndex, validator: &T::AccountId) -> bool {
123 <ErasStakersOverview<T>>::get(&era, validator)
124 .map(|overview| {
125 ClaimedRewards::<T>::get(era, validator).len() < overview.page_count as usize
126 })
127 .unwrap_or(false)
128 }
129
130 pub(crate) fn get_paged_exposure(
138 era: EraIndex,
139 validator: &T::AccountId,
140 page: Page,
141 ) -> Option<PagedExposure<T::AccountId, BalanceOf<T>>> {
142 let overview = <ErasStakersOverview<T>>::get(&era, validator)?;
143
144 let validator_stake = if page == 0 { overview.own } else { Zero::zero() };
146
147 let exposure_page = <ErasStakersPaged<T>>::get((era, validator, page)).unwrap_or_default();
150
151 Some(PagedExposure {
153 exposure_metadata: PagedExposureMetadata { own: validator_stake, ..overview },
154 exposure_page: exposure_page.into(),
155 })
156 }
157
158 pub(crate) fn get_full_exposure(
160 era: EraIndex,
161 validator: &T::AccountId,
162 ) -> Exposure<T::AccountId, BalanceOf<T>> {
163 let Some(overview) = <ErasStakersOverview<T>>::get(&era, validator) else {
164 return Exposure::default();
165 };
166
167 let mut others = Vec::with_capacity(overview.nominator_count as usize);
168 for page in 0..overview.page_count {
169 let nominators = <ErasStakersPaged<T>>::get((era, validator, page));
170 others.append(&mut nominators.map(|n| n.others.clone()).defensive_unwrap_or_default());
171 }
172
173 Exposure { total: overview.total, own: overview.own, others }
174 }
175
176 pub(crate) fn exposure_page_count(era: EraIndex, validator: &T::AccountId) -> Page {
180 <ErasStakersOverview<T>>::get(&era, validator)
181 .map(|overview| {
182 if overview.page_count == 0 && overview.own > Zero::zero() {
183 1
186 } else {
187 overview.page_count
188 }
189 })
190 .unwrap_or(1)
193 }
194
195 pub(crate) fn get_next_claimable_page(era: EraIndex, validator: &T::AccountId) -> Option<Page> {
197 let page_count = Self::exposure_page_count(era, validator);
199 let all_claimable_pages: Vec<Page> = (0..page_count).collect();
200 let claimed_pages = ClaimedRewards::<T>::get(era, validator);
201
202 all_claimable_pages.into_iter().find(|p| !claimed_pages.contains(p))
203 }
204
205 pub(crate) fn set_rewards_as_claimed(era: EraIndex, validator: &T::AccountId, page: Page) {
208 let mut claimed_pages = ClaimedRewards::<T>::get(era, validator).into_inner();
209
210 if claimed_pages.contains(&page) {
212 defensive!("Trying to set an already claimed reward");
213 return
215 }
216
217 claimed_pages.push(page);
219 ClaimedRewards::<T>::insert(
220 era,
221 validator,
222 WeakBoundedVec::<_, _>::force_from(claimed_pages, Some("set_rewards_as_claimed")),
223 );
224 }
225
226 pub fn upsert_exposure(
233 era: EraIndex,
234 validator: &T::AccountId,
235 mut exposure: Exposure<T::AccountId, BalanceOf<T>>,
236 ) {
237 let page_size = T::MaxExposurePageSize::get().defensive_max(1);
238 if cfg!(debug_assertions) && cfg!(not(feature = "runtime-benchmarks")) {
239 let expected_total = exposure
242 .others
243 .iter()
244 .map(|ie| ie.value)
245 .fold::<BalanceOf<T>, _>(Default::default(), |acc, x| acc + x)
246 .saturating_add(exposure.own);
247 debug_assert_eq!(expected_total, exposure.total, "exposure total must equal own + sum(others) for (era: {:?}, validator: {:?}, exposure: {:?})", era, validator, exposure);
248 }
249
250 if let Some(overview) = ErasStakersOverview::<T>::get(era, &validator) {
251 let last_page_idx = overview.page_count.saturating_sub(1);
253 let mut last_page =
254 ErasStakersPaged::<T>::get((era, validator, last_page_idx)).unwrap_or_default();
255 let last_page_empty_slots =
256 T::MaxExposurePageSize::get().saturating_sub(last_page.others.len() as u32);
257
258 let new_stake_added = exposure.total;
261 let new_nominators_added = exposure.others.len() as u32;
262 let mut updated_overview = overview
263 .update_with::<T::MaxExposurePageSize>(new_stake_added, new_nominators_added);
264
265 match (updated_overview.own.is_zero(), exposure.own.is_zero()) {
267 (true, false) => {
268 updated_overview.own = exposure.own;
271 },
272 (true, true) | (false, true) => {
273 },
275 (false, false) => {
276 debug_assert!(
277 false,
278 "validator own stake already set in overview for (era: {:?}, validator: {:?}, current overview: {:?}, new exposure: {:?})",
279 era,
280 validator,
281 updated_overview,
282 exposure,
283 );
284 defensive!("duplicate validator self stake in election");
285 },
286 };
287
288 ErasStakersOverview::<T>::insert(era, &validator, updated_overview);
289 exposure.total = exposure.total.saturating_sub(exposure.own);
301 exposure.own = Zero::zero();
302
303 let append_to_last_page = exposure.split_others(last_page_empty_slots);
307 let put_in_new_pages = exposure;
308
309 last_page.page_total = last_page.page_total.saturating_add(append_to_last_page.total);
313 last_page.others.extend(append_to_last_page.others);
314 ErasStakersPaged::<T>::insert((era, &validator, last_page_idx), last_page);
315
316 let (_unused_metadata, put_in_new_pages_chunks) =
319 put_in_new_pages.into_pages(page_size);
320
321 put_in_new_pages_chunks
322 .into_iter()
323 .enumerate()
324 .for_each(|(idx, paged_exposure)| {
325 let append_at =
326 (last_page_idx.saturating_add(1).saturating_add(idx as u32)) as Page;
327 <ErasStakersPaged<T>>::insert((era, &validator, append_at), paged_exposure);
328 });
329 } else {
330 let expected_page_count = exposure
332 .others
333 .len()
334 .defensive_saturating_add((page_size as usize).defensive_saturating_sub(1))
335 .saturating_div(page_size as usize);
336
337 let (exposure_metadata, exposure_pages) = exposure.into_pages(page_size);
340 defensive_assert!(exposure_pages.len() == expected_page_count, "unexpected page count");
341
342 ErasStakersOverview::<T>::insert(era, &validator, exposure_metadata);
344
345 exposure_pages.into_iter().enumerate().for_each(|(idx, paged_exposure)| {
347 let append_at = idx as Page;
348 <ErasStakersPaged<T>>::insert((era, &validator, append_at), paged_exposure);
349 });
350 };
351 }
352
353 pub(crate) fn set_validators_reward(era: EraIndex, amount: BalanceOf<T>) {
354 ErasValidatorReward::<T>::insert(era, amount);
355 }
356
357 pub(crate) fn get_validators_reward(era: EraIndex) -> Option<BalanceOf<T>> {
358 ErasValidatorReward::<T>::get(era)
359 }
360
361 pub(crate) fn add_total_stake(era: EraIndex, stake: BalanceOf<T>) {
363 <ErasTotalStake<T>>::mutate(era, |total_stake| {
364 *total_stake += stake;
365 });
366 }
367
368 pub(crate) fn is_rewards_claimed(era: EraIndex, validator: &T::AccountId, page: Page) -> bool {
370 ClaimedRewards::<T>::get(era, validator).contains(&page)
371 }
372
373 pub(crate) fn reward_active_era(
375 validators_points: impl IntoIterator<Item = (T::AccountId, u32)>,
376 ) {
377 if let Some(active_era) = ActiveEra::<T>::get() {
378 <ErasRewardPoints<T>>::mutate(active_era.index, |era_rewards| {
379 for (validator, points) in validators_points.into_iter() {
380 match era_rewards.individual.get_mut(&validator) {
381 Some(individual) => individual.saturating_accrue(points),
382 None => {
383 let _ =
386 era_rewards.individual.try_insert(validator, points).defensive();
387 },
388 }
389 era_rewards.total.saturating_accrue(points);
390 }
391 });
392 }
393 }
394
395 pub(crate) fn get_reward_points(era: EraIndex) -> EraRewardPoints<T> {
396 ErasRewardPoints::<T>::get(era)
397 }
398}
399
400#[cfg(any(feature = "try-runtime", test, feature = "runtime-benchmarks"))]
401#[allow(unused)]
402impl<T: Config> Eras<T> {
403 pub(crate) fn era_fully_present(era: EraIndex) -> Result<(), sp_runtime::TryRuntimeError> {
405 let e0 = ErasValidatorPrefs::<T>::iter_prefix_values(era).count() != 0;
407 let e1 = ErasStakersOverview::<T>::iter_prefix_values(era).count() != 0;
409 ensure!(e0 == e1, "ErasValidatorPrefs and ErasStakersOverview should be consistent");
410
411 let e2 = ErasTotalStake::<T>::contains_key(era);
413
414 let active_era = Rotator::<T>::active_era();
415 let e4 = if era.saturating_sub(1) > 0 &&
416 era.saturating_sub(1) > active_era.saturating_sub(T::HistoryDepth::get() + 1)
417 {
418 ErasValidatorReward::<T>::contains_key(era.saturating_sub(1))
422 } else {
423 e2
425 };
426
427 ensure!(e2 == e4, "era info presence not consistent");
428
429 if e2 {
430 Ok(())
431 } else {
432 Err("era presence mismatch".into())
433 }
434 }
435
436 pub(crate) fn era_pruning_in_progress(era: EraIndex) -> bool {
438 EraPruningState::<T>::contains_key(era)
439 }
440
441 pub(crate) fn era_absent_or_pruning(era: EraIndex) -> Result<(), sp_runtime::TryRuntimeError> {
443 if Self::era_pruning_in_progress(era) {
444 Ok(())
445 } else {
446 Self::era_absent(era)
447 }
448 }
449
450 pub(crate) fn era_absent(era: EraIndex) -> Result<(), sp_runtime::TryRuntimeError> {
453 let e0 = ErasValidatorPrefs::<T>::iter_prefix_values(era).count() != 0;
455 let e1 = ErasStakersPaged::<T>::iter_prefix_values((era,)).count() != 0;
456 let e2 = ErasStakersOverview::<T>::iter_prefix_values(era).count() != 0;
457
458 let e3 = ErasValidatorReward::<T>::contains_key(era);
461 let e4 = ErasTotalStake::<T>::contains_key(era);
462
463 let e6 = ClaimedRewards::<T>::iter_prefix_values(era).count() != 0;
465 let e7 = ErasRewardPoints::<T>::contains_key(era);
466
467 if !vec![e0, e1, e2, e3, e4, e6, e7].windows(2).all(|w| w[0] == w[1]) {
469 return Err("era info absence not consistent - partial pruning state".into());
470 }
471
472 if !e0 {
473 Ok(())
474 } else {
475 Err("era absence mismatch".into())
476 }
477 }
478
479 pub(crate) fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
480 let active_era = Rotator::<T>::active_era();
482 let oldest_present_era = active_era.saturating_sub(T::HistoryDepth::get()).max(1);
485
486 for e in oldest_present_era..=active_era {
487 Self::era_fully_present(e)?
488 }
489
490 ensure!(
493 (1..oldest_present_era).all(|e| Self::era_absent_or_pruning(e).is_ok()),
494 "All old eras must be either fully pruned or marked for pruning"
495 );
496
497 Ok(())
498 }
499}
500
501pub struct Rotator<T: Config>(core::marker::PhantomData<T>);
510
511impl<T: Config> Rotator<T> {
512 #[cfg(feature = "runtime-benchmarks")]
513 pub(crate) fn legacy_insta_plan_era() -> Vec<T::AccountId> {
514 Self::plan_new_era();
516 <<T as Config>::ElectionProvider as ElectionProvider>::asap();
518 let msp = <T::ElectionProvider as ElectionProvider>::msp();
521 let lsp = 0;
522 for p in (lsp..=msp).rev() {
523 EraElectionPlanner::<T>::do_elect_paged(p);
524 }
525
526 crate::ElectableStashes::<T>::take().into_iter().collect()
527 }
528
529 #[cfg(any(feature = "try-runtime", test))]
530 pub(crate) fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
531 let active_era = ActiveEra::<T>::get();
533 let planned_era = CurrentEra::<T>::get();
534
535 let bonded = BondedEras::<T>::get();
536
537 match (&active_era, &planned_era) {
538 (None, None) => {
539 ensure!(bonded.is_empty(), "BondedEras must be empty when ActiveEra is None");
541 },
542 (Some(active), Some(planned)) => {
543 ensure!(
545 *planned == active.index || *planned == active.index + 1,
546 "planned era is always equal or one more than active"
547 );
548
549 ensure!(
552 bonded.into_iter().map(|(era, _sess)| era).collect::<Vec<_>>() ==
553 (active.index.saturating_sub(T::BondingDuration::get())..=active.index)
554 .collect::<Vec<_>>(),
555 "BondedEras range incorrect"
556 );
557 },
558 _ => {
559 ensure!(false, "ActiveEra and CurrentEra must both be None or both be Some");
560 },
561 }
562
563 Ok(())
564 }
565
566 #[cfg(any(feature = "try-runtime", feature = "std", feature = "runtime-benchmarks", test))]
567 pub fn assert_election_ongoing() {
568 assert!(Self::is_planning().is_some(), "planning era must exist");
569 assert!(
570 T::ElectionProvider::status().is_ok(),
571 "Election provider must be in a good state during election"
572 );
573 }
574
575 pub fn planned_era() -> EraIndex {
583 CurrentEra::<T>::get().unwrap_or(0)
584 }
585
586 pub fn active_era() -> EraIndex {
587 ActiveEra::<T>::get().map(|a| a.index).defensive_unwrap_or(0)
588 }
589
590 pub fn is_planning() -> Option<EraIndex> {
594 let (active, planned) = (Self::active_era(), Self::planned_era());
595 if planned.defensive_saturating_sub(active) > 1 {
596 defensive!("planned era must always be equal or one more than active");
597 }
598
599 (planned > active).then_some(planned)
600 }
601
602 pub(crate) fn end_session(
604 end_index: SessionIndex,
605 activation_timestamp: Option<(u64, u32)>,
606 ) -> Weight {
607 let weight = T::WeightInfo::rc_on_session_report();
609
610 let Some(active_era) = ActiveEra::<T>::get() else {
611 defensive!("Active era must always be available.");
612 return weight;
613 };
614 let current_planned_era = Self::is_planning();
615 let starting = end_index + 1;
616 let planning = starting + 1;
618
619 log!(
620 info,
621 "Session: end {:?}, start {:?} (ts: {:?}), planning {:?}",
622 end_index,
623 starting,
624 activation_timestamp,
625 planning
626 );
627 log!(info, "Era: active {:?}, planned {:?}", active_era.index, current_planned_era);
628
629 match activation_timestamp {
630 Some((time, id)) if Some(id) == current_planned_era => {
631 Self::start_era(active_era, starting, time);
633 },
634 Some((_time, id)) => {
635 crate::log!(
637 warn,
638 "received wrong ID with activation timestamp. Got {}, expected {:?}",
639 id,
640 current_planned_era
641 );
642 Pallet::<T>::deposit_event(Event::Unexpected(
643 UnexpectedKind::UnknownValidatorActivation,
644 ));
645 },
646 None => (),
647 }
648
649 let should_plan_era = match ForceEra::<T>::get() {
651 Forcing::NotForcing => Self::is_plan_era_deadline(starting),
653 Forcing::ForceNew => {
655 ForceEra::<T>::put(Forcing::NotForcing);
656 true
657 },
658 Forcing::ForceAlways => true,
660 Forcing::ForceNone => false,
662 };
663
664 let has_pending_era = Self::is_planning().is_some();
667 match (should_plan_era, has_pending_era) {
668 (false, _) => {
669 },
671 (true, false) => {
672 Self::plan_new_era();
674 },
675 (true, true) => {
676 crate::log!(
679 debug,
680 "time to plan a new era {:?}, but waiting for the activation of the previous.",
681 current_planned_era
682 );
683 },
684 }
685
686 Pallet::<T>::deposit_event(Event::SessionRotated {
687 starting_session: starting,
688 active_era: Self::active_era(),
689 planned_era: Self::planned_era(),
690 });
691
692 weight
693 }
694
695 pub(crate) fn start_era(
696 ending_era: ActiveEraInfo,
697 starting_session: SessionIndex,
698 new_era_start_timestamp: u64,
699 ) {
700 debug_assert!(CurrentEra::<T>::get().unwrap_or(0) == ending_era.index + 1);
702
703 let starting_era = ending_era.index + 1;
704
705 Self::end_era(&ending_era, new_era_start_timestamp);
707
708 Self::start_era_inc_active_era(new_era_start_timestamp);
710 Self::start_era_update_bonded_eras(starting_era, starting_session);
711
712 EraElectionPlanner::<T>::cleanup();
714
715 if let Some(old_era) = starting_era.checked_sub(T::HistoryDepth::get() + 1) {
717 log!(debug, "Marking era {:?} for lazy pruning", old_era);
718 EraPruningState::<T>::insert(old_era, PruningStep::ErasStakersPaged);
719 }
720 }
721
722 fn start_era_inc_active_era(start_timestamp: u64) {
723 ActiveEra::<T>::mutate(|active_era| {
724 let new_index = active_era.as_ref().map(|info| info.index + 1).unwrap_or(0);
725 log!(
726 debug,
727 "starting active era {:?} with RC-provided timestamp {:?}",
728 new_index,
729 start_timestamp
730 );
731 *active_era = Some(ActiveEraInfo { index: new_index, start: Some(start_timestamp) });
732 });
733 }
734
735 pub fn active_era_start_session_index() -> SessionIndex {
739 Self::era_start_session_index(Self::active_era()).defensive_unwrap_or(0)
740 }
741
742 pub fn era_start_session_index(era: EraIndex) -> Option<SessionIndex> {
744 BondedEras::<T>::get()
745 .into_iter()
746 .rev()
747 .find_map(|(e, s)| if e == era { Some(s) } else { None })
748 }
749
750 fn start_era_update_bonded_eras(starting_era: EraIndex, start_session: SessionIndex) {
751 let bonding_duration = T::BondingDuration::get();
752
753 BondedEras::<T>::mutate(|bonded| {
754 if bonded.is_full() {
755 let (era_removed, _) = bonded.remove(0);
757 debug_assert!(
758 era_removed <= (starting_era.saturating_sub(bonding_duration)),
759 "should not delete an era that is not older than bonding duration"
760 );
761 slashing::clear_era_metadata::<T>(era_removed);
762 }
763
764 let _ = bonded.try_push((starting_era, start_session)).defensive();
766 });
767 }
768
769 fn end_era(ending_era: &ActiveEraInfo, new_era_start: u64) {
770 let previous_era_start = ending_era.start.defensive_unwrap_or(new_era_start);
771 let uncapped_era_duration = new_era_start.saturating_sub(previous_era_start);
772
773 let cap = T::MaxEraDuration::get();
775 let era_duration = if cap == 0 {
776 uncapped_era_duration
778 } else if uncapped_era_duration > cap {
779 Pallet::<T>::deposit_event(Event::Unexpected(UnexpectedKind::EraDurationBoundExceeded));
780
781 log!(
784 warn,
785 "capping era duration for era {:?} from {:?} to max allowed {:?}",
786 ending_era.index,
787 uncapped_era_duration,
788 cap
789 );
790 cap
791 } else {
792 uncapped_era_duration
793 };
794
795 Self::end_era_compute_payout(ending_era, era_duration);
796 }
797
798 fn end_era_compute_payout(ending_era: &ActiveEraInfo, era_duration: u64) {
799 let staked = ErasTotalStake::<T>::get(ending_era.index);
800 let issuance = asset::total_issuance::<T>();
801
802 log!(
803 debug,
804 "computing inflation for era {:?} with duration {:?}",
805 ending_era.index,
806 era_duration
807 );
808 let (validator_payout, remainder) =
809 T::EraPayout::era_payout(staked, issuance, era_duration);
810
811 let total_payout = validator_payout.saturating_add(remainder);
812 let max_staked_rewards = MaxStakedRewards::<T>::get().unwrap_or(Percent::from_percent(100));
813
814 let validator_payout = validator_payout.min(max_staked_rewards * total_payout);
816 let remainder = total_payout.saturating_sub(validator_payout);
817
818 Pallet::<T>::deposit_event(Event::<T>::EraPaid {
819 era_index: ending_era.index,
820 validator_payout,
821 remainder,
822 });
823
824 Eras::<T>::set_validators_reward(ending_era.index, validator_payout);
826 T::RewardRemainder::on_unbalanced(asset::issue::<T>(remainder));
827 }
828
829 fn plan_new_era() {
833 let _ = CurrentEra::<T>::try_mutate(|x| {
834 log!(info, "Planning new era: {:?}, sending election start signal", x.unwrap_or(0));
835 let could_start_election = EraElectionPlanner::<T>::plan_new_election();
836 *x = Some(x.unwrap_or(0) + 1);
837 could_start_election
838 });
839 }
840
841 fn is_plan_era_deadline(start_session: SessionIndex) -> bool {
843 let planning_era_offset = T::PlanningEraOffset::get().min(T::SessionsPerEra::get());
844 let target_plan_era_session = T::SessionsPerEra::get().saturating_sub(planning_era_offset);
846 let era_start_session = Self::active_era_start_session_index();
847
848 let session_progress = start_session.defensive_saturating_sub(era_start_session);
850
851 log!(
852 debug,
853 "Session progress within era: {:?}, target_plan_era_session: {:?}",
854 session_progress,
855 target_plan_era_session
856 );
857 session_progress >= target_plan_era_session
858 }
859}
860
861pub(crate) struct EraElectionPlanner<T: Config>(PhantomData<T>);
886impl<T: Config> EraElectionPlanner<T> {
887 pub(crate) fn cleanup() {
889 VoterSnapshotStatus::<T>::kill();
890 NextElectionPage::<T>::kill();
891 ElectableStashes::<T>::kill();
892 Pallet::<T>::register_weight(T::DbWeight::get().writes(3));
893 }
894
895 pub(crate) fn election_pages() -> u32 {
897 <<T as Config>::ElectionProvider as ElectionProvider>::Pages::get()
898 }
899
900 pub(crate) fn plan_new_election() -> Result<(), <T::ElectionProvider as ElectionProvider>::Error>
902 {
903 T::ElectionProvider::start()
904 .inspect_err(|e| log!(warn, "Election provider failed to start: {:?}", e))
905 }
906
907 pub(crate) fn maybe_fetch_election_results() -> (Weight, Box<dyn Fn(&mut WeightMeter)>) {
908 let Ok(Some(mut required_weight)) = T::ElectionProvider::status() else {
909 let weight = T::DbWeight::get().reads(1);
911 return (weight, Box::new(move |meter: &mut WeightMeter| meter.consume(weight)))
912 };
913 let exec = Box::new(move |meter: &mut WeightMeter| {
914 crate::log!(
915 debug,
916 "Election provider is ready, our status is {:?}",
917 NextElectionPage::<T>::get()
918 );
919
920 debug_assert!(
921 CurrentEra::<T>::get().unwrap_or(0) ==
922 ActiveEra::<T>::get().map_or(0, |a| a.index) + 1,
923 "Next era must be already planned."
924 );
925
926 let current_page = NextElectionPage::<T>::get()
927 .unwrap_or(Self::election_pages().defensive_saturating_sub(1));
928 let maybe_next_page = current_page.checked_sub(1);
929 crate::log!(debug, "fetching page {:?}, next {:?}", current_page, maybe_next_page);
930
931 Self::do_elect_paged(current_page);
932 NextElectionPage::<T>::set(maybe_next_page);
933
934 if maybe_next_page.is_none() {
935 let id = CurrentEra::<T>::get().defensive_unwrap_or(0);
936 let prune_up_to = Self::get_prune_up_to();
937 let rc_validators = ElectableStashes::<T>::take().into_iter().collect::<Vec<_>>();
938
939 crate::log!(
940 info,
941 "Sending new validator set of size {:?} to RC. ID: {:?}, prune_up_to: {:?}",
942 rc_validators.len(),
943 id,
944 prune_up_to
945 );
946 T::RcClientInterface::validator_set(rc_validators, id, prune_up_to);
947 }
948
949 meter.consume(required_weight)
951 });
952
953 required_weight.saturating_accrue(T::DbWeight::get().reads_writes(3, 2));
962
963 (required_weight, exec)
964 }
965
966 fn get_prune_up_to() -> Option<SessionIndex> {
969 let bonded_eras = BondedEras::<T>::get();
970
971 if bonded_eras.is_full() {
973 bonded_eras.first().map(|(_, first_session)| first_session.saturating_sub(1))
974 } else {
975 None
976 }
977 }
978
979 pub(crate) fn do_elect_paged(page: PageIndex) {
991 let election_result = T::ElectionProvider::elect(page);
992 match election_result {
993 Ok(supports) => {
994 let inner_processing_results = Self::do_elect_paged_inner(supports);
995 if let Err(not_included) = inner_processing_results {
996 defensive!(
997 "electable stashes exceeded limit, unexpected but election proceeds.\
998 {} stashes from election result discarded",
999 not_included
1000 );
1001 };
1002
1003 Pallet::<T>::deposit_event(Event::PagedElectionProceeded {
1004 page,
1005 result: inner_processing_results.map(|x| x as u32).map_err(|x| x as u32),
1006 });
1007 },
1008 Err(e) => {
1009 log!(warn, "election provider page failed due to {:?} (page: {})", e, page);
1010 Pallet::<T>::deposit_event(Event::PagedElectionProceeded { page, result: Err(0) });
1011 },
1012 }
1013 }
1014
1015 pub(crate) fn do_elect_paged_inner(
1021 mut supports: BoundedSupportsOf<T::ElectionProvider>,
1022 ) -> Result<usize, usize> {
1023 let planning_era = Rotator::<T>::planned_era();
1024
1025 match Self::add_electables(supports.iter().map(|(s, _)| s.clone())) {
1026 Ok(added) => {
1027 let exposures = Self::collect_exposures(supports);
1028 let _ = Self::store_stakers_info(exposures, planning_era);
1029 Ok(added)
1030 },
1031 Err(not_included_idx) => {
1032 let not_included = supports.len().saturating_sub(not_included_idx);
1033
1034 log!(
1035 warn,
1036 "not all winners fit within the electable stashes, excluding {:?} accounts from solution.",
1037 not_included,
1038 );
1039
1040 supports.truncate(not_included_idx);
1043 let exposures = Self::collect_exposures(supports);
1044 let _ = Self::store_stakers_info(exposures, planning_era);
1045
1046 Err(not_included)
1047 },
1048 }
1049 }
1050
1051 pub(crate) fn store_stakers_info(
1055 exposures: BoundedExposuresOf<T>,
1056 new_planned_era: EraIndex,
1057 ) -> BoundedVec<T::AccountId, MaxWinnersPerPageOf<T::ElectionProvider>> {
1058 let mut total_stake_page: BalanceOf<T> = Zero::zero();
1060 let mut elected_stashes_page = Vec::with_capacity(exposures.len());
1061 let mut total_backers = 0u32;
1062
1063 exposures.into_iter().for_each(|(stash, exposure)| {
1064 log!(
1065 trace,
1066 "storing exposure for stash {:?} with {:?} own-stake and {:?} backers",
1067 stash,
1068 exposure.own,
1069 exposure.others.len()
1070 );
1071 elected_stashes_page.push(stash.clone());
1073 total_stake_page = total_stake_page.saturating_add(exposure.total);
1075 total_backers += exposure.others.len() as u32;
1076 Eras::<T>::upsert_exposure(new_planned_era, &stash, exposure);
1078 });
1079
1080 let elected_stashes: BoundedVec<_, MaxWinnersPerPageOf<T::ElectionProvider>> =
1081 elected_stashes_page
1082 .try_into()
1083 .expect("both types are bounded by MaxWinnersPerPageOf; qed");
1084
1085 Eras::<T>::add_total_stake(new_planned_era, total_stake_page);
1087
1088 for stash in &elected_stashes {
1092 let pref = Validators::<T>::get(stash);
1093 Eras::<T>::set_validator_prefs(new_planned_era, stash, pref);
1094 }
1095
1096 log!(
1097 debug,
1098 "stored a page of stakers with {:?} validators and {:?} total backers for era {:?}",
1099 elected_stashes.len(),
1100 total_backers,
1101 new_planned_era,
1102 );
1103
1104 elected_stashes
1105 }
1106
1107 fn collect_exposures(
1113 supports: BoundedSupportsOf<T::ElectionProvider>,
1114 ) -> BoundedExposuresOf<T> {
1115 let total_issuance = asset::total_issuance::<T>();
1116 let to_currency = |e: frame_election_provider_support::ExtendedBalance| {
1117 T::CurrencyToVote::to_currency(e, total_issuance)
1118 };
1119
1120 supports
1121 .into_iter()
1122 .map(|(validator, support)| {
1123 let mut others = Vec::with_capacity(support.voters.len());
1125 let mut own: BalanceOf<T> = Zero::zero();
1126 let mut total: BalanceOf<T> = Zero::zero();
1127 support
1128 .voters
1129 .into_iter()
1130 .map(|(nominator, weight)| (nominator, to_currency(weight)))
1131 .for_each(|(nominator, stake)| {
1132 if nominator == validator {
1133 defensive_assert!(own == Zero::zero(), "own stake should be unique");
1134 own = own.saturating_add(stake);
1135 } else {
1136 others.push(IndividualExposure { who: nominator, value: stake });
1137 }
1138 total = total.saturating_add(stake);
1139 });
1140
1141 let exposure = Exposure { own, others, total };
1142 (validator, exposure)
1143 })
1144 .try_collect()
1145 .expect("we only map through support vector which cannot change the size; qed")
1146 }
1147
1148 pub(crate) fn add_electables(
1155 new_stashes: impl Iterator<Item = T::AccountId>,
1156 ) -> Result<usize, usize> {
1157 ElectableStashes::<T>::mutate(|electable| {
1158 let pre_size = electable.len();
1159
1160 for (idx, stash) in new_stashes.enumerate() {
1161 if electable.try_insert(stash).is_err() {
1162 return Err(idx);
1163 }
1164 }
1165
1166 Ok(electable.len() - pre_size)
1167 })
1168 }
1169}