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}