1use crate::{
21 asset, session_rotation::EraElectionPlanner, slashing, weights::WeightInfo, AccountIdLookupOf,
22 ActiveEraInfo, BalanceOf, EraPayout, EraRewardPoints, ExposurePage, Forcing,
23 LedgerIntegrityState, MaxNominationsOf, NegativeImbalanceOf, Nominations, NominationsQuota,
24 PositiveImbalanceOf, RewardDestination, StakingLedger, UnappliedSlash, UnlockChunk,
25 ValidatorPrefs,
26};
27use alloc::{format, vec::Vec};
28use codec::Codec;
29use frame_election_provider_support::{ElectionProvider, SortedListProvider, VoteWeight};
30use frame_support::{
31 assert_ok,
32 pallet_prelude::*,
33 traits::{
34 fungible::{
35 hold::{Balanced as FunHoldBalanced, Mutate as FunHoldMutate},
36 Mutate, Mutate as FunMutate,
37 },
38 Contains, Defensive, DefensiveSaturating, EnsureOrigin, Get, InspectLockableCurrency,
39 Nothing, OnUnbalanced,
40 },
41 weights::Weight,
42 BoundedBTreeSet, BoundedVec,
43};
44use frame_system::{ensure_root, ensure_signed, pallet_prelude::*};
45pub use impls::*;
46use rand::seq::SliceRandom;
47use rand_chacha::{
48 rand_core::{RngCore, SeedableRng},
49 ChaChaRng,
50};
51use sp_core::{sr25519::Pair as SrPair, Pair};
52use sp_runtime::{
53 traits::{StaticLookup, Zero},
54 ArithmeticError, Perbill, Percent,
55};
56use sp_staking::{
57 EraIndex, Page, SessionIndex,
58 StakingAccount::{self, Controller, Stash},
59 StakingInterface,
60};
61
62mod impls;
63
64#[frame_support::pallet]
65pub mod pallet {
66 use core::ops::Deref;
67
68 use super::*;
69 use crate::{session_rotation, PagedExposureMetadata, SnapshotStatus};
70 use codec::HasCompact;
71 use frame_election_provider_support::{ElectionDataProvider, PageIndex};
72 use frame_support::{weights::WeightMeter, DefaultNoBound};
73
74 #[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, Debug, TypeInfo, MaxEncodedLen)]
76 pub enum PruningStep {
77 ErasStakersPaged,
79 ErasStakersOverview,
81 ErasValidatorPrefs,
83 ClaimedRewards,
85 ErasValidatorReward,
87 ErasRewardPoints,
89 ErasTotalStake,
91 }
92
93 const STORAGE_VERSION: StorageVersion = StorageVersion::new(17);
95
96 #[pallet::pallet]
97 #[pallet::storage_version(STORAGE_VERSION)]
98 pub struct Pallet<T>(_);
99
100 #[derive(TypeInfo, Debug, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq)]
102 pub enum ConfigOp<T: Default + Codec> {
103 Noop,
105 Set(T),
107 Remove,
109 }
110
111 #[pallet::config(with_default)]
112 pub trait Config: frame_system::Config {
113 #[pallet::no_default]
115 type OldCurrency: InspectLockableCurrency<
116 Self::AccountId,
117 Moment = BlockNumberFor<Self>,
118 Balance = Self::CurrencyBalance,
119 >;
120
121 #[pallet::no_default]
123 type Currency: FunHoldMutate<
124 Self::AccountId,
125 Reason = Self::RuntimeHoldReason,
126 Balance = Self::CurrencyBalance,
127 > + FunMutate<Self::AccountId, Balance = Self::CurrencyBalance>
128 + FunHoldBalanced<Self::AccountId, Balance = Self::CurrencyBalance>;
129
130 #[pallet::no_default_bounds]
132 type RuntimeHoldReason: From<HoldReason>;
133
134 type CurrencyBalance: sp_runtime::traits::AtLeast32BitUnsigned
137 + codec::FullCodec
138 + DecodeWithMemTracking
139 + HasCompact<Type: DecodeWithMemTracking>
140 + Copy
141 + MaybeSerializeDeserialize
142 + core::fmt::Debug
143 + Default
144 + From<u64>
145 + TypeInfo
146 + Send
147 + Sync
148 + MaxEncodedLen;
149
150 #[pallet::no_default_bounds]
157 type CurrencyToVote: sp_staking::currency_to_vote::CurrencyToVote<BalanceOf<Self>>;
158
159 #[pallet::no_default]
161 type ElectionProvider: ElectionProvider<
162 AccountId = Self::AccountId,
163 BlockNumber = BlockNumberFor<Self>,
164 DataProvider = Pallet<Self>,
166 >;
167
168 #[pallet::no_default_bounds]
170 type NominationsQuota: NominationsQuota<BalanceOf<Self>>;
171
172 #[pallet::constant]
186 type HistoryDepth: Get<u32>;
187
188 #[pallet::no_default_bounds]
191 type RewardRemainder: OnUnbalanced<NegativeImbalanceOf<Self>>;
192
193 #[pallet::no_default_bounds]
195 type Slash: OnUnbalanced<NegativeImbalanceOf<Self>>;
196
197 #[pallet::no_default_bounds]
201 type Reward: OnUnbalanced<PositiveImbalanceOf<Self>>;
202
203 #[pallet::constant]
205 type SessionsPerEra: Get<SessionIndex>;
206
207 #[pallet::constant]
222 type PlanningEraOffset: Get<SessionIndex>;
223
224 #[pallet::constant]
226 type BondingDuration: Get<EraIndex>;
227
228 #[pallet::constant]
233 type SlashDeferDuration: Get<EraIndex>;
234
235 #[pallet::no_default]
239 type AdminOrigin: EnsureOrigin<Self::RuntimeOrigin>;
240
241 #[pallet::no_default]
244 type EraPayout: EraPayout<BalanceOf<Self>>;
245
246 #[pallet::constant]
258 type MaxExposurePageSize: Get<u32>;
259
260 #[pallet::constant]
265 type MaxValidatorSet: Get<u32>;
266
267 #[pallet::no_default]
279 type VoterList: SortedListProvider<Self::AccountId, Score = VoteWeight>;
280
281 #[pallet::no_default]
302 type TargetList: SortedListProvider<Self::AccountId, Score = BalanceOf<Self>>;
303
304 #[pallet::constant]
315 type MaxUnlockingChunks: Get<u32>;
316
317 type MaxControllersInDeprecationBatch: Get<u32>;
319
320 #[pallet::no_default_bounds]
325 type EventListeners: sp_staking::OnStakingUpdate<Self::AccountId, BalanceOf<Self>>;
326
327 #[pallet::constant]
336 type MaxEraDuration: Get<u64>;
337
338 #[pallet::constant]
344 type MaxPruningItems: Get<u32>;
345
346 #[pallet::no_default]
349 type RcClientInterface: pallet_staking_async_rc_client::RcClientInterface<
350 AccountId = Self::AccountId,
351 >;
352
353 #[pallet::no_default_bounds]
354 type Filter: Contains<Self::AccountId>;
359
360 type WeightInfo: WeightInfo;
362 }
363
364 #[pallet::composite_enum]
366 pub enum HoldReason {
367 #[codec(index = 0)]
369 Staking,
370 }
371
372 pub mod config_preludes {
374 use super::*;
375 use frame_support::{derive_impl, parameter_types, traits::ConstU32};
376 pub struct TestDefaultConfig;
377
378 #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
379 impl frame_system::DefaultConfig for TestDefaultConfig {}
380
381 parameter_types! {
382 pub const SessionsPerEra: SessionIndex = 3;
383 pub const BondingDuration: EraIndex = 3;
384 pub const MaxPruningItems: u32 = 100;
385 }
386
387 #[frame_support::register_default_impl(TestDefaultConfig)]
388 impl DefaultConfig for TestDefaultConfig {
389 #[inject_runtime_type]
390 type RuntimeHoldReason = ();
391 type CurrencyBalance = u128;
392 type CurrencyToVote = ();
393 type NominationsQuota = crate::FixedNominationsQuota<16>;
394 type HistoryDepth = ConstU32<84>;
395 type RewardRemainder = ();
396 type Slash = ();
397 type Reward = ();
398 type SessionsPerEra = SessionsPerEra;
399 type BondingDuration = BondingDuration;
400 type PlanningEraOffset = ConstU32<1>;
401 type SlashDeferDuration = ();
402 type MaxExposurePageSize = ConstU32<64>;
403 type MaxUnlockingChunks = ConstU32<32>;
404 type MaxValidatorSet = ConstU32<100>;
405 type MaxControllersInDeprecationBatch = ConstU32<100>;
406 type MaxEraDuration = ();
407 type MaxPruningItems = MaxPruningItems;
408 type EventListeners = ();
409 type Filter = Nothing;
410 type WeightInfo = ();
411 }
412 }
413
414 #[pallet::storage]
416 pub type ValidatorCount<T> = StorageValue<_, u32, ValueQuery>;
417
418 #[pallet::storage]
422 pub type Bonded<T: Config> = StorageMap<_, Twox64Concat, T::AccountId, T::AccountId>;
423
424 #[pallet::storage]
426 pub type MinNominatorBond<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
427
428 #[pallet::storage]
430 pub type MinValidatorBond<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
431
432 #[pallet::storage]
434 pub type MinimumActiveStake<T> = StorageValue<_, BalanceOf<T>, ValueQuery>;
435
436 #[pallet::storage]
440 pub type MinCommission<T: Config> = StorageValue<_, Perbill, ValueQuery>;
441
442 #[pallet::storage]
447 pub type Ledger<T: Config> = StorageMap<_, Blake2_128Concat, T::AccountId, StakingLedger<T>>;
448
449 #[pallet::storage]
453 pub type Payee<T: Config> =
454 StorageMap<_, Twox64Concat, T::AccountId, RewardDestination<T::AccountId>, OptionQuery>;
455
456 #[pallet::storage]
460 pub type Validators<T: Config> =
461 CountedStorageMap<_, Twox64Concat, T::AccountId, ValidatorPrefs, ValueQuery>;
462
463 #[pallet::storage]
467 pub type MaxValidatorsCount<T> = StorageValue<_, u32, OptionQuery>;
468
469 #[pallet::storage]
489 pub type Nominators<T: Config> =
490 CountedStorageMap<_, Twox64Concat, T::AccountId, Nominations<T>>;
491
492 #[pallet::storage]
499 pub type VirtualStakers<T: Config> = CountedStorageMap<_, Twox64Concat, T::AccountId, ()>;
500
501 #[pallet::storage]
505 pub type MaxNominatorsCount<T> = StorageValue<_, u32, OptionQuery>;
506
507 #[pallet::storage]
514 pub type CurrentEra<T> = StorageValue<_, EraIndex>;
515
516 #[pallet::storage]
521 pub type ActiveEra<T> = StorageValue<_, ActiveEraInfo>;
522
523 pub struct BondedErasBound<T>(core::marker::PhantomData<T>);
525 impl<T: Config> Get<u32> for BondedErasBound<T> {
526 fn get() -> u32 {
527 T::BondingDuration::get().saturating_add(1)
528 }
529 }
530
531 #[pallet::storage]
536 pub type BondedEras<T: Config> =
537 StorageValue<_, BoundedVec<(EraIndex, SessionIndex), BondedErasBound<T>>, ValueQuery>;
538
539 #[pallet::storage]
554 pub type ErasStakersOverview<T: Config> = StorageDoubleMap<
555 _,
556 Twox64Concat,
557 EraIndex,
558 Twox64Concat,
559 T::AccountId,
560 PagedExposureMetadata<BalanceOf<T>>,
561 OptionQuery,
562 >;
563
564 #[derive(PartialEqNoBound, Encode, Decode, DebugNoBound, TypeInfo, DefaultNoBound)]
573 #[scale_info(skip_type_params(T))]
574 pub struct BoundedExposurePage<T: Config>(pub ExposurePage<T::AccountId, BalanceOf<T>>);
575 impl<T: Config> Deref for BoundedExposurePage<T> {
576 type Target = ExposurePage<T::AccountId, BalanceOf<T>>;
577
578 fn deref(&self) -> &Self::Target {
579 &self.0
580 }
581 }
582
583 impl<T: Config> core::ops::DerefMut for BoundedExposurePage<T> {
584 fn deref_mut(&mut self) -> &mut Self::Target {
585 &mut self.0
586 }
587 }
588
589 impl<T: Config> codec::MaxEncodedLen for BoundedExposurePage<T> {
590 fn max_encoded_len() -> usize {
591 let max_exposure_page_size = T::MaxExposurePageSize::get() as usize;
592 let individual_size =
593 T::AccountId::max_encoded_len() + BalanceOf::<T>::max_encoded_len();
594
595 BalanceOf::<T>::max_encoded_len() +
597 max_exposure_page_size.saturating_mul(individual_size)
599 }
600 }
601
602 impl<T: Config> From<ExposurePage<T::AccountId, BalanceOf<T>>> for BoundedExposurePage<T> {
603 fn from(value: ExposurePage<T::AccountId, BalanceOf<T>>) -> Self {
604 Self(value)
605 }
606 }
607
608 impl<T: Config> From<BoundedExposurePage<T>> for ExposurePage<T::AccountId, BalanceOf<T>> {
609 fn from(value: BoundedExposurePage<T>) -> Self {
610 value.0
611 }
612 }
613
614 impl<T: Config> codec::EncodeLike<BoundedExposurePage<T>>
615 for ExposurePage<T::AccountId, BalanceOf<T>>
616 {
617 }
618
619 #[pallet::storage]
626 pub type ErasStakersPaged<T: Config> = StorageNMap<
627 _,
628 (
629 NMapKey<Twox64Concat, EraIndex>,
630 NMapKey<Twox64Concat, T::AccountId>,
631 NMapKey<Twox64Concat, Page>,
632 ),
633 BoundedExposurePage<T>,
634 OptionQuery,
635 >;
636
637 pub struct ClaimedRewardsBound<T>(core::marker::PhantomData<T>);
638 impl<T: Config> Get<u32> for ClaimedRewardsBound<T> {
639 fn get() -> u32 {
640 let max_total_nominators_per_validator =
641 <T::ElectionProvider as ElectionProvider>::MaxBackersPerWinnerFinal::get();
642 let exposure_page_size = T::MaxExposurePageSize::get();
643 max_total_nominators_per_validator
644 .saturating_div(exposure_page_size)
645 .saturating_add(1)
646 }
647 }
648
649 #[pallet::storage]
656 pub type ClaimedRewards<T: Config> = StorageDoubleMap<
657 _,
658 Twox64Concat,
659 EraIndex,
660 Twox64Concat,
661 T::AccountId,
662 WeakBoundedVec<Page, ClaimedRewardsBound<T>>,
663 ValueQuery,
664 >;
665
666 #[pallet::storage]
673 pub type ErasValidatorPrefs<T: Config> = StorageDoubleMap<
674 _,
675 Twox64Concat,
676 EraIndex,
677 Twox64Concat,
678 T::AccountId,
679 ValidatorPrefs,
680 ValueQuery,
681 >;
682
683 #[pallet::storage]
687 pub type ErasValidatorReward<T: Config> = StorageMap<_, Twox64Concat, EraIndex, BalanceOf<T>>;
688
689 #[pallet::storage]
692 pub type ErasRewardPoints<T: Config> =
693 StorageMap<_, Twox64Concat, EraIndex, EraRewardPoints<T>, ValueQuery>;
694
695 #[pallet::storage]
698 pub type ErasTotalStake<T: Config> =
699 StorageMap<_, Twox64Concat, EraIndex, BalanceOf<T>, ValueQuery>;
700
701 #[pallet::storage]
703 pub type ForceEra<T> = StorageValue<_, Forcing, ValueQuery>;
704
705 #[pallet::storage]
709 pub type MaxStakedRewards<T> = StorageValue<_, Percent, OptionQuery>;
710
711 #[pallet::storage]
715 pub type SlashRewardFraction<T> = StorageValue<_, Perbill, ValueQuery>;
716
717 #[pallet::storage]
720 pub type CanceledSlashPayout<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
721
722 #[pallet::storage]
733 pub type OffenceQueue<T: Config> = StorageDoubleMap<
734 _,
735 Twox64Concat,
736 EraIndex,
737 Twox64Concat,
738 T::AccountId,
739 slashing::OffenceRecord<T::AccountId>,
740 >;
741
742 #[pallet::storage]
754 pub type OffenceQueueEras<T: Config> = StorageValue<_, WeakBoundedVec<u32, T::BondingDuration>>;
755
756 #[pallet::storage]
769 pub type ProcessingOffence<T: Config> =
770 StorageValue<_, (EraIndex, T::AccountId, slashing::OffenceRecord<T::AccountId>)>;
771
772 #[pallet::storage]
774 pub type UnappliedSlashes<T: Config> = StorageDoubleMap<
775 _,
776 Twox64Concat,
777 EraIndex,
778 Twox64Concat,
779 (T::AccountId, Perbill, u32),
781 UnappliedSlash<T>,
782 OptionQuery,
783 >;
784
785 #[pallet::storage]
791 pub type CancelledSlashes<T: Config> = StorageMap<
792 _,
793 Twox64Concat,
794 EraIndex,
795 BoundedVec<(T::AccountId, Perbill), T::MaxValidatorSet>,
796 ValueQuery,
797 >;
798
799 #[pallet::storage]
802 pub type ValidatorSlashInEra<T: Config> = StorageDoubleMap<
803 _,
804 Twox64Concat,
805 EraIndex,
806 Twox64Concat,
807 T::AccountId,
808 (Perbill, BalanceOf<T>),
809 >;
810
811 #[pallet::storage]
815 pub type ChillThreshold<T: Config> = StorageValue<_, Percent, OptionQuery>;
816
817 #[pallet::storage]
822 pub type VoterSnapshotStatus<T: Config> =
823 StorageValue<_, SnapshotStatus<T::AccountId>, ValueQuery>;
824
825 #[pallet::storage]
832 pub type NextElectionPage<T: Config> = StorageValue<_, PageIndex, OptionQuery>;
833
834 #[pallet::storage]
836 pub type ElectableStashes<T: Config> =
837 StorageValue<_, BoundedBTreeSet<T::AccountId, T::MaxValidatorSet>, ValueQuery>;
838
839 #[pallet::storage]
841 pub type EraPruningState<T: Config> = StorageMap<_, Twox64Concat, EraIndex, PruningStep>;
842
843 #[pallet::genesis_config]
844 #[derive(frame_support::DefaultNoBound, frame_support::DebugNoBound)]
845 pub struct GenesisConfig<T: Config> {
846 pub validator_count: u32,
847 pub force_era: Forcing,
848 pub slash_reward_fraction: Perbill,
849 pub canceled_payout: BalanceOf<T>,
850 pub stakers: Vec<(T::AccountId, BalanceOf<T>, crate::StakerStatus<T::AccountId>)>,
851 pub min_nominator_bond: BalanceOf<T>,
852 pub min_validator_bond: BalanceOf<T>,
853 pub max_validator_count: Option<u32>,
854 pub max_nominator_count: Option<u32>,
855 pub dev_stakers: Option<(u32, u32)>,
862 pub active_era: (u32, u32, u64),
864 }
865
866 impl<T: Config> GenesisConfig<T> {
867 fn generate_endowed_bonded_account(derivation: &str, rng: &mut ChaChaRng) -> T::AccountId {
868 let pair: SrPair = Pair::from_string(&derivation, None)
869 .expect(&format!("Failed to parse derivation string: {derivation}"));
870 let who = T::AccountId::decode(&mut &pair.public().encode()[..])
871 .expect(&format!("Failed to decode public key from pair: {:?}", pair.public()));
872
873 let (min, max) = T::VoterList::range();
874 let stake = BalanceOf::<T>::from(rng.next_u64().min(max).max(min));
875 let two: BalanceOf<T> = 2u32.into();
876
877 assert_ok!(T::Currency::mint_into(&who, stake * two));
878 assert_ok!(<Pallet<T>>::bond(
879 T::RuntimeOrigin::from(Some(who.clone()).into()),
880 stake,
881 RewardDestination::Staked,
882 ));
883 who
884 }
885 }
886
887 #[pallet::genesis_build]
888 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
889 fn build(&self) {
890 crate::log!(trace, "initializing with {:?}", self);
891 assert!(
892 self.validator_count <=
893 <T::ElectionProvider as ElectionProvider>::MaxWinnersPerPage::get() *
894 <T::ElectionProvider as ElectionProvider>::Pages::get(),
895 "validator count is too high, `ElectionProvider` can never fulfill this"
896 );
897 ValidatorCount::<T>::put(self.validator_count);
898
899 ForceEra::<T>::put(self.force_era);
900 CanceledSlashPayout::<T>::put(self.canceled_payout);
901 SlashRewardFraction::<T>::put(self.slash_reward_fraction);
902 MinNominatorBond::<T>::put(self.min_nominator_bond);
903 MinValidatorBond::<T>::put(self.min_validator_bond);
904 if let Some(x) = self.max_validator_count {
905 MaxValidatorsCount::<T>::put(x);
906 }
907 if let Some(x) = self.max_nominator_count {
908 MaxNominatorsCount::<T>::put(x);
909 }
910
911 for &(ref stash, balance, ref status) in &self.stakers {
913 match status {
914 crate::StakerStatus::Validator => {
915 crate::log!(
916 trace,
917 "inserting genesis validator: {:?} => {:?} => {:?}",
918 stash,
919 balance,
920 status
921 );
922 assert!(
923 asset::free_to_stake::<T>(stash) >= balance,
924 "Stash does not have enough balance to bond."
925 );
926 assert_ok!(<Pallet<T>>::bond(
927 T::RuntimeOrigin::from(Some(stash.clone()).into()),
928 balance,
929 RewardDestination::Staked,
930 ));
931 assert_ok!(<Pallet<T>>::validate(
932 T::RuntimeOrigin::from(Some(stash.clone()).into()),
933 Default::default(),
934 ));
935 },
936 crate::StakerStatus::Idle => {
937 crate::log!(
938 trace,
939 "inserting genesis idle staker: {:?} => {:?} => {:?}",
940 stash,
941 balance,
942 status
943 );
944 assert!(
945 asset::free_to_stake::<T>(stash) >= balance,
946 "Stash does not have enough balance to bond."
947 );
948 assert_ok!(<Pallet<T>>::bond(
949 T::RuntimeOrigin::from(Some(stash.clone()).into()),
950 balance,
951 RewardDestination::Staked,
952 ));
953 },
954 _ => {},
955 }
956 }
957
958 for &(ref stash, balance, ref status) in &self.stakers {
960 match status {
961 crate::StakerStatus::Nominator(votes) => {
962 crate::log!(
963 trace,
964 "inserting genesis nominator: {:?} => {:?} => {:?}",
965 stash,
966 balance,
967 status
968 );
969 assert!(
970 asset::free_to_stake::<T>(stash) >= balance,
971 "Stash does not have enough balance to bond."
972 );
973 assert_ok!(<Pallet<T>>::bond(
974 T::RuntimeOrigin::from(Some(stash.clone()).into()),
975 balance,
976 RewardDestination::Staked,
977 ));
978 assert_ok!(<Pallet<T>>::nominate(
979 T::RuntimeOrigin::from(Some(stash.clone()).into()),
980 votes.iter().map(|l| T::Lookup::unlookup(l.clone())).collect(),
981 ));
982 },
983 _ => {},
984 }
985 }
986
987 assert_eq!(
989 T::VoterList::count(),
990 Nominators::<T>::count() + Validators::<T>::count(),
991 "not all genesis stakers were inserted into sorted list provider, something is wrong."
992 );
993
994 if let Some((validators, nominators)) = self.dev_stakers {
996 crate::log!(
997 debug,
998 "generating dev stakers: validators: {}, nominators: {}",
999 validators,
1000 nominators
1001 );
1002 let base_derivation = "//staker//{}";
1003
1004 let mut rng =
1007 ChaChaRng::from_seed(base_derivation.using_encoded(sp_core::blake2_256));
1008
1009 (0..validators).for_each(|index| {
1010 let derivation = base_derivation.replace("{}", &format!("validator{}", index));
1011 let who = Self::generate_endowed_bonded_account(&derivation, &mut rng);
1012 assert_ok!(<Pallet<T>>::validate(
1013 T::RuntimeOrigin::from(Some(who.clone()).into()),
1014 Default::default(),
1015 ));
1016 });
1017
1018 let all_validators = Validators::<T>::iter_keys().collect::<Vec<_>>();
1021
1022 (0..nominators).for_each(|index| {
1023 let derivation = base_derivation.replace("{}", &format!("nominator{}", index));
1024 let who = Self::generate_endowed_bonded_account(&derivation, &mut rng);
1025
1026 let random_nominations = all_validators
1027 .choose_multiple(&mut rng, MaxNominationsOf::<T>::get() as usize)
1028 .map(|v| v.clone())
1029 .collect::<Vec<_>>();
1030
1031 assert_ok!(<Pallet<T>>::nominate(
1032 T::RuntimeOrigin::from(Some(who.clone()).into()),
1033 random_nominations.iter().map(|l| T::Lookup::unlookup(l.clone())).collect(),
1034 ));
1035 })
1036 }
1037
1038 let (active_era, session_index, timestamp) = self.active_era;
1039 ActiveEra::<T>::put(ActiveEraInfo { index: active_era, start: Some(timestamp) });
1040 CurrentEra::<T>::put(active_era);
1042 BondedEras::<T>::put(
1044 BoundedVec::<_, BondedErasBound<T>>::try_from(
1045 alloc::vec![(active_era, session_index)]
1046 )
1047 .expect("bound for BondedEras is BondingDuration + 1; can contain at least one element; qed")
1048 );
1049 }
1050 }
1051
1052 #[pallet::event]
1053 #[pallet::generate_deposit(pub fn deposit_event)]
1054 pub enum Event<T: Config> {
1055 EraPaid {
1058 era_index: EraIndex,
1059 validator_payout: BalanceOf<T>,
1060 remainder: BalanceOf<T>,
1061 },
1062 Rewarded {
1064 stash: T::AccountId,
1065 dest: RewardDestination<T::AccountId>,
1066 amount: BalanceOf<T>,
1067 },
1068 Slashed {
1070 staker: T::AccountId,
1071 amount: BalanceOf<T>,
1072 },
1073 OldSlashingReportDiscarded {
1076 session_index: SessionIndex,
1077 },
1078 Bonded {
1083 stash: T::AccountId,
1084 amount: BalanceOf<T>,
1085 },
1086 Unbonded {
1088 stash: T::AccountId,
1089 amount: BalanceOf<T>,
1090 },
1091 Withdrawn {
1094 stash: T::AccountId,
1095 amount: BalanceOf<T>,
1096 },
1097 StakerRemoved {
1100 stash: T::AccountId,
1101 },
1102 Kicked {
1104 nominator: T::AccountId,
1105 stash: T::AccountId,
1106 },
1107 Chilled {
1109 stash: T::AccountId,
1110 },
1111 PayoutStarted {
1113 era_index: EraIndex,
1114 validator_stash: T::AccountId,
1115 page: Page,
1116 next: Option<Page>,
1117 },
1118 ValidatorPrefsSet {
1120 stash: T::AccountId,
1121 prefs: ValidatorPrefs,
1122 },
1123 SnapshotVotersSizeExceeded {
1125 size: u32,
1126 },
1127 SnapshotTargetsSizeExceeded {
1129 size: u32,
1130 },
1131 ForceEra {
1132 mode: Forcing,
1133 },
1134 ControllerBatchDeprecated {
1136 failures: u32,
1137 },
1138 CurrencyMigrated {
1141 stash: T::AccountId,
1142 force_withdraw: BalanceOf<T>,
1143 },
1144 PagedElectionProceeded {
1154 page: PageIndex,
1155 result: Result<u32, u32>,
1156 },
1157 OffenceReported {
1160 offence_era: EraIndex,
1161 validator: T::AccountId,
1162 fraction: Perbill,
1163 },
1164 SlashComputed {
1166 offence_era: EraIndex,
1167 slash_era: EraIndex,
1168 offender: T::AccountId,
1169 page: u32,
1170 },
1171 SlashCancelled {
1173 slash_era: EraIndex,
1174 validator: T::AccountId,
1175 },
1176 SessionRotated {
1181 starting_session: SessionIndex,
1182 active_era: EraIndex,
1183 planned_era: EraIndex,
1184 },
1185 Unexpected(UnexpectedKind),
1188 OffenceTooOld {
1190 offence_era: EraIndex,
1191 validator: T::AccountId,
1192 fraction: Perbill,
1193 },
1194 EraPruned {
1196 index: EraIndex,
1197 },
1198 }
1199
1200 #[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, TypeInfo, Debug)]
1206 pub enum UnexpectedKind {
1207 EraDurationBoundExceeded,
1209 UnknownValidatorActivation,
1211 PagedElectionOutOfWeight { page: PageIndex, required: Weight, had: Weight },
1213 }
1214
1215 #[pallet::error]
1216 #[derive(PartialEq)]
1217 pub enum Error<T> {
1218 NotController,
1220 NotStash,
1222 AlreadyBonded,
1224 AlreadyPaired,
1226 EmptyTargets,
1228 DuplicateIndex,
1230 InvalidSlashRecord,
1232 InsufficientBond,
1236 NoMoreChunks,
1238 NoUnlockChunk,
1240 FundedTarget,
1242 InvalidEraToReward,
1244 InvalidNumberOfNominations,
1246 AlreadyClaimed,
1248 InvalidPage,
1250 IncorrectHistoryDepth,
1252 BadState,
1254 TooManyTargets,
1256 BadTarget,
1258 CannotChillOther,
1260 TooManyNominators,
1263 TooManyValidators,
1266 CommissionTooLow,
1268 BoundNotMet,
1270 ControllerDeprecated,
1272 CannotRestoreLedger,
1274 RewardDestinationRestricted,
1276 NotEnoughFunds,
1278 VirtualStakerNotAllowed,
1280 CannotReapStash,
1282 AlreadyMigrated,
1284 EraNotStarted,
1286 Restricted,
1289 UnappliedSlashesInPreviousEra,
1292 EraNotPrunable,
1294 CancelledSlash,
1296 }
1297
1298 impl<T: Config> Pallet<T> {
1299 pub fn apply_unapplied_slashes(active_era: EraIndex) -> Weight {
1301 let mut slashes = UnappliedSlashes::<T>::iter_prefix(&active_era).take(1);
1302 if let Some((key, slash)) = slashes.next() {
1303 crate::log!(
1304 debug,
1305 "🦹 found slash {:?} scheduled to be executed in era {:?}",
1306 slash,
1307 active_era,
1308 );
1309
1310 if Self::check_slash_cancelled(active_era, &key.0, key.1) {
1312 crate::log!(
1313 debug,
1314 "🦹 slash for {:?} in era {:?} was cancelled, skipping",
1315 key.0,
1316 active_era,
1317 );
1318 } else {
1319 let offence_era = active_era.saturating_sub(T::SlashDeferDuration::get());
1320 slashing::apply_slash::<T>(slash, offence_era);
1321 }
1322
1323 UnappliedSlashes::<T>::remove(&active_era, &key);
1325
1326 if UnappliedSlashes::<T>::iter_prefix(&active_era).next().is_none() {
1328 CancelledSlashes::<T>::remove(&active_era);
1330 }
1331
1332 T::WeightInfo::apply_slash()
1333 } else {
1334 T::DbWeight::get().reads(1)
1336 }
1337 }
1338
1339 fn do_prune_era_step(era: EraIndex) -> Result<Weight, DispatchError> {
1341 let current_step = EraPruningState::<T>::get(era).ok_or(Error::<T>::EraNotPrunable)?;
1346
1347 let items_limit = T::MaxPruningItems::get().min(T::MaxValidatorSet::get());
1350
1351 let actual_weight = match current_step {
1352 PruningStep::ErasStakersPaged => {
1353 let result = ErasStakersPaged::<T>::clear_prefix((era,), items_limit, None);
1354 let items_deleted = result.backend as u32;
1355 result.maybe_cursor.is_none().then(|| {
1356 EraPruningState::<T>::insert(era, PruningStep::ErasStakersOverview)
1357 });
1358 T::WeightInfo::prune_era_stakers_paged(items_deleted)
1359 },
1360 PruningStep::ErasStakersOverview => {
1361 let result = ErasStakersOverview::<T>::clear_prefix(era, items_limit, None);
1362 let items_deleted = result.backend as u32;
1363 result.maybe_cursor.is_none().then(|| {
1364 EraPruningState::<T>::insert(era, PruningStep::ErasValidatorPrefs)
1365 });
1366 T::WeightInfo::prune_era_stakers_overview(items_deleted)
1367 },
1368 PruningStep::ErasValidatorPrefs => {
1369 let result = ErasValidatorPrefs::<T>::clear_prefix(era, items_limit, None);
1370 let items_deleted = result.backend as u32;
1371 result
1372 .maybe_cursor
1373 .is_none()
1374 .then(|| EraPruningState::<T>::insert(era, PruningStep::ClaimedRewards));
1375 T::WeightInfo::prune_era_validator_prefs(items_deleted)
1376 },
1377 PruningStep::ClaimedRewards => {
1378 let result = ClaimedRewards::<T>::clear_prefix(era, items_limit, None);
1379 let items_deleted = result.backend as u32;
1380 result.maybe_cursor.is_none().then(|| {
1381 EraPruningState::<T>::insert(era, PruningStep::ErasValidatorReward)
1382 });
1383 T::WeightInfo::prune_era_claimed_rewards(items_deleted)
1384 },
1385 PruningStep::ErasValidatorReward => {
1386 ErasValidatorReward::<T>::remove(era);
1387 EraPruningState::<T>::insert(era, PruningStep::ErasRewardPoints);
1388 T::WeightInfo::prune_era_validator_reward()
1389 },
1390 PruningStep::ErasRewardPoints => {
1391 ErasRewardPoints::<T>::remove(era);
1392 EraPruningState::<T>::insert(era, PruningStep::ErasTotalStake);
1393 T::WeightInfo::prune_era_reward_points()
1394 },
1395 PruningStep::ErasTotalStake => {
1396 ErasTotalStake::<T>::remove(era);
1397 EraPruningState::<T>::remove(era);
1399 T::WeightInfo::prune_era_total_stake()
1400 },
1401 };
1402
1403 if EraPruningState::<T>::get(era).is_none() {
1405 Self::deposit_event(Event::<T>::EraPruned { index: era });
1406 }
1407
1408 Ok(actual_weight)
1409 }
1410 }
1411
1412 #[pallet::hooks]
1413 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
1414 fn on_poll(_now: BlockNumberFor<T>, weight_meter: &mut WeightMeter) {
1415 let (weight, exec) = EraElectionPlanner::<T>::maybe_fetch_election_results();
1416 crate::log!(
1417 trace,
1418 "weight of fetching next election page is {:?}, have {:?}",
1419 weight,
1420 weight_meter.remaining()
1421 );
1422
1423 if weight_meter.can_consume(weight) {
1424 exec(weight_meter);
1425 } else {
1426 Self::deposit_event(Event::<T>::Unexpected(
1427 UnexpectedKind::PagedElectionOutOfWeight {
1428 page: NextElectionPage::<T>::get().unwrap_or(
1429 EraElectionPlanner::<T>::election_pages().defensive_saturating_sub(1),
1430 ),
1431 required: weight,
1432 had: weight_meter.remaining(),
1433 },
1434 ));
1435 }
1436 }
1437
1438 fn on_initialize(_now: BlockNumberFor<T>) -> Weight {
1439 let mut consumed_weight = slashing::process_offence::<T>();
1441
1442 consumed_weight.saturating_accrue(T::DbWeight::get().reads(1));
1444 if let Some(active_era) = ActiveEra::<T>::get() {
1445 let slash_weight = Self::apply_unapplied_slashes(active_era.index);
1446 consumed_weight.saturating_accrue(slash_weight);
1447 }
1448
1449 consumed_weight
1450 }
1451
1452 fn integrity_test() {
1453 assert_eq!(
1455 MaxNominationsOf::<T>::get(),
1456 <Self as ElectionDataProvider>::MaxVotesPerVoter::get()
1457 );
1458
1459 assert!(!MaxNominationsOf::<T>::get().is_zero());
1461
1462 assert!(
1463 T::SlashDeferDuration::get() < T::BondingDuration::get() || T::BondingDuration::get() == 0,
1464 "As per documentation, slash defer duration ({}) should be less than bonding duration ({}).",
1465 T::SlashDeferDuration::get(),
1466 T::BondingDuration::get(),
1467 );
1468
1469 assert!(
1471 T::MaxPruningItems::get() >= 100,
1472 "MaxPruningItems must be at least 100 for efficient pruning, got: {}",
1473 T::MaxPruningItems::get()
1474 );
1475 }
1476
1477 #[cfg(feature = "try-runtime")]
1478 fn try_state(n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
1479 Self::do_try_state(n)
1480 }
1481 }
1482
1483 #[pallet::call]
1484 impl<T: Config> Pallet<T> {
1485 #[pallet::call_index(0)]
1498 #[pallet::weight(T::WeightInfo::bond())]
1499 pub fn bond(
1500 origin: OriginFor<T>,
1501 #[pallet::compact] value: BalanceOf<T>,
1502 payee: RewardDestination<T::AccountId>,
1503 ) -> DispatchResult {
1504 let stash = ensure_signed(origin)?;
1505
1506 ensure!(!T::Filter::contains(&stash), Error::<T>::Restricted);
1507
1508 if StakingLedger::<T>::is_bonded(StakingAccount::Stash(stash.clone())) {
1509 return Err(Error::<T>::AlreadyBonded.into());
1510 }
1511
1512 if StakingLedger::<T>::is_bonded(StakingAccount::Controller(stash.clone())) {
1514 return Err(Error::<T>::AlreadyPaired.into());
1515 }
1516
1517 if value < Self::min_chilled_bond() {
1519 return Err(Error::<T>::InsufficientBond.into());
1520 }
1521
1522 let stash_balance = asset::free_to_stake::<T>(&stash);
1523 let value = value.min(stash_balance);
1524 Self::deposit_event(Event::<T>::Bonded { stash: stash.clone(), amount: value });
1525 let ledger = StakingLedger::<T>::new(stash.clone(), value);
1526
1527 ledger.bond(payee)?;
1530
1531 Ok(())
1532 }
1533
1534 #[pallet::call_index(1)]
1545 #[pallet::weight(T::WeightInfo::bond_extra())]
1546 pub fn bond_extra(
1547 origin: OriginFor<T>,
1548 #[pallet::compact] max_additional: BalanceOf<T>,
1549 ) -> DispatchResult {
1550 let stash = ensure_signed(origin)?;
1551 ensure!(!T::Filter::contains(&stash), Error::<T>::Restricted);
1552 Self::do_bond_extra(&stash, max_additional)
1553 }
1554
1555 #[pallet::call_index(2)]
1575 #[pallet::weight(
1576 T::WeightInfo::withdraw_unbonded_kill().saturating_add(T::WeightInfo::unbond()))
1577 ]
1578 pub fn unbond(
1579 origin: OriginFor<T>,
1580 #[pallet::compact] value: BalanceOf<T>,
1581 ) -> DispatchResultWithPostInfo {
1582 let controller = ensure_signed(origin)?;
1583 let unlocking =
1584 Self::ledger(Controller(controller.clone())).map(|l| l.unlocking.len())?;
1585
1586 let maybe_withdraw_weight = {
1589 if unlocking == T::MaxUnlockingChunks::get() as usize {
1590 Some(Self::do_withdraw_unbonded(&controller)?)
1591 } else {
1592 None
1593 }
1594 };
1595
1596 let mut ledger = Self::ledger(Controller(controller))?;
1599 let mut value = value.min(ledger.active);
1600 let stash = ledger.stash.clone();
1601
1602 ensure!(
1603 ledger.unlocking.len() < T::MaxUnlockingChunks::get() as usize,
1604 Error::<T>::NoMoreChunks,
1605 );
1606
1607 if !value.is_zero() {
1608 ledger.active -= value;
1609
1610 if ledger.active < asset::existential_deposit::<T>() {
1612 value += ledger.active;
1613 ledger.active = Zero::zero();
1614 }
1615
1616 let min_active_bond = if Nominators::<T>::contains_key(&stash) {
1617 Self::min_nominator_bond()
1618 } else if Validators::<T>::contains_key(&stash) {
1619 Self::min_validator_bond()
1620 } else {
1621 Zero::zero()
1623 };
1624
1625 ensure!(ledger.active >= min_active_bond, Error::<T>::InsufficientBond);
1628
1629 let era = session_rotation::Rotator::<T>::active_era()
1633 .saturating_add(T::BondingDuration::get());
1634 if let Some(chunk) = ledger.unlocking.last_mut().filter(|chunk| chunk.era == era) {
1635 chunk.value = chunk.value.defensive_saturating_add(value)
1639 } else {
1640 ledger
1641 .unlocking
1642 .try_push(UnlockChunk { value, era })
1643 .map_err(|_| Error::<T>::NoMoreChunks)?;
1644 };
1645 ledger.update()?;
1647
1648 if T::VoterList::contains(&stash) {
1650 let _ = T::VoterList::on_update(&stash, Self::weight_of(&stash));
1651 }
1652
1653 Self::deposit_event(Event::<T>::Unbonded { stash, amount: value });
1654 }
1655
1656 let actual_weight = if let Some(withdraw_weight) = maybe_withdraw_weight {
1657 Some(T::WeightInfo::unbond().saturating_add(withdraw_weight))
1658 } else {
1659 Some(T::WeightInfo::unbond())
1660 };
1661
1662 Ok(actual_weight.into())
1663 }
1664
1665 #[pallet::call_index(3)]
1685 #[pallet::weight(T::WeightInfo::withdraw_unbonded_kill())]
1686 pub fn withdraw_unbonded(
1687 origin: OriginFor<T>,
1688 _num_slashing_spans: u32,
1689 ) -> DispatchResultWithPostInfo {
1690 let controller = ensure_signed(origin)?;
1691
1692 let actual_weight = Self::do_withdraw_unbonded(&controller)?;
1693 Ok(Some(actual_weight).into())
1694 }
1695
1696 #[pallet::call_index(4)]
1702 #[pallet::weight(T::WeightInfo::validate())]
1703 pub fn validate(origin: OriginFor<T>, prefs: ValidatorPrefs) -> DispatchResult {
1704 let controller = ensure_signed(origin)?;
1705
1706 let ledger = Self::ledger(Controller(controller))?;
1707
1708 ensure!(ledger.active >= Self::min_validator_bond(), Error::<T>::InsufficientBond);
1709 let stash = &ledger.stash;
1710
1711 ensure!(prefs.commission >= MinCommission::<T>::get(), Error::<T>::CommissionTooLow);
1713
1714 if !Validators::<T>::contains_key(stash) {
1716 if let Some(max_validators) = MaxValidatorsCount::<T>::get() {
1720 ensure!(
1721 Validators::<T>::count() < max_validators,
1722 Error::<T>::TooManyValidators
1723 );
1724 }
1725 }
1726
1727 Self::do_remove_nominator(stash);
1728 Self::do_add_validator(stash, prefs.clone());
1729 Self::deposit_event(Event::<T>::ValidatorPrefsSet { stash: ledger.stash, prefs });
1730
1731 Ok(())
1732 }
1733
1734 #[pallet::call_index(5)]
1740 #[pallet::weight(T::WeightInfo::nominate(targets.len() as u32))]
1741 pub fn nominate(
1742 origin: OriginFor<T>,
1743 targets: Vec<AccountIdLookupOf<T>>,
1744 ) -> DispatchResult {
1745 let controller = ensure_signed(origin)?;
1746
1747 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()))?;
1748
1749 ensure!(ledger.active >= Self::min_nominator_bond(), Error::<T>::InsufficientBond);
1750 let stash = &ledger.stash;
1751
1752 if !Nominators::<T>::contains_key(stash) {
1754 if let Some(max_nominators) = MaxNominatorsCount::<T>::get() {
1758 ensure!(
1759 Nominators::<T>::count() < max_nominators,
1760 Error::<T>::TooManyNominators
1761 );
1762 }
1763 }
1764
1765 let mut targets = targets
1767 .into_iter()
1768 .map(|t| T::Lookup::lookup(t).map_err(DispatchError::from))
1769 .collect::<Result<Vec<_>, _>>()?;
1770 targets.sort();
1771 targets.dedup();
1772
1773 ensure!(!targets.is_empty(), Error::<T>::EmptyTargets);
1774 ensure!(
1775 targets.len() <= T::NominationsQuota::get_quota(ledger.active) as usize,
1776 Error::<T>::TooManyTargets
1777 );
1778
1779 let old = Nominators::<T>::get(stash).map_or_else(Vec::new, |x| x.targets.into_inner());
1780
1781 let targets: BoundedVec<_, _> = targets
1782 .into_iter()
1783 .map(|n| {
1784 if old.contains(&n) ||
1785 (Validators::<T>::contains_key(&n) && !Validators::<T>::get(&n).blocked)
1786 {
1787 Ok(n)
1788 } else {
1789 Err(Error::<T>::BadTarget.into())
1790 }
1791 })
1792 .collect::<Result<Vec<_>, DispatchError>>()?
1793 .try_into()
1794 .map_err(|_| Error::<T>::TooManyNominators)?;
1795
1796 let nominations = Nominations {
1797 targets,
1798 submitted_in: CurrentEra::<T>::get().unwrap_or(0),
1800 suppressed: false,
1801 };
1802
1803 Self::do_remove_validator(stash);
1804 Self::do_add_nominator(stash, nominations);
1805 Ok(())
1806 }
1807
1808 #[pallet::call_index(6)]
1819 #[pallet::weight(T::WeightInfo::chill())]
1820 pub fn chill(origin: OriginFor<T>) -> DispatchResult {
1821 let controller = ensure_signed(origin)?;
1822
1823 let ledger = Self::ledger(StakingAccount::Controller(controller))?;
1824
1825 Self::chill_stash(&ledger.stash);
1826 Ok(())
1827 }
1828
1829 #[pallet::call_index(7)]
1835 #[pallet::weight(T::WeightInfo::set_payee())]
1836 pub fn set_payee(
1837 origin: OriginFor<T>,
1838 payee: RewardDestination<T::AccountId>,
1839 ) -> DispatchResult {
1840 let controller = ensure_signed(origin)?;
1841 let ledger = Self::ledger(Controller(controller.clone()))?;
1842
1843 ensure!(
1844 (payee != {
1845 #[allow(deprecated)]
1846 RewardDestination::Controller
1847 }),
1848 Error::<T>::ControllerDeprecated
1849 );
1850
1851 let _ = ledger
1852 .set_payee(payee)
1853 .defensive_proof("ledger was retrieved from storage, thus it's bonded; qed.")?;
1854
1855 Ok(())
1856 }
1857
1858 #[pallet::call_index(8)]
1867 #[pallet::weight(T::WeightInfo::set_controller())]
1868 pub fn set_controller(origin: OriginFor<T>) -> DispatchResult {
1869 let stash = ensure_signed(origin)?;
1870
1871 Self::ledger(StakingAccount::Stash(stash.clone())).map(|ledger| {
1872 let controller = ledger.controller()
1873 .defensive_proof("Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.")
1874 .ok_or(Error::<T>::NotController)?;
1875
1876 if controller == stash {
1877 return Err(Error::<T>::AlreadyPaired.into())
1879 }
1880
1881 let _ = ledger.set_controller_to_stash()?;
1882 Ok(())
1883 })?
1884 }
1885
1886 #[pallet::call_index(9)]
1890 #[pallet::weight(T::WeightInfo::set_validator_count())]
1891 pub fn set_validator_count(
1892 origin: OriginFor<T>,
1893 #[pallet::compact] new: u32,
1894 ) -> DispatchResult {
1895 ensure_root(origin)?;
1896
1897 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
1898
1899 ValidatorCount::<T>::put(new);
1900 Ok(())
1901 }
1902
1903 #[pallet::call_index(10)]
1908 #[pallet::weight(T::WeightInfo::set_validator_count())]
1909 pub fn increase_validator_count(
1910 origin: OriginFor<T>,
1911 #[pallet::compact] additional: u32,
1912 ) -> DispatchResult {
1913 ensure_root(origin)?;
1914 let old = ValidatorCount::<T>::get();
1915 let new = old.checked_add(additional).ok_or(ArithmeticError::Overflow)?;
1916
1917 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
1918
1919 ValidatorCount::<T>::put(new);
1920 Ok(())
1921 }
1922
1923 #[pallet::call_index(11)]
1928 #[pallet::weight(T::WeightInfo::set_validator_count())]
1929 pub fn scale_validator_count(origin: OriginFor<T>, factor: Percent) -> DispatchResult {
1930 ensure_root(origin)?;
1931 let old = ValidatorCount::<T>::get();
1932 let new = old.checked_add(factor.mul_floor(old)).ok_or(ArithmeticError::Overflow)?;
1933
1934 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
1935
1936 ValidatorCount::<T>::put(new);
1937 Ok(())
1938 }
1939
1940 #[pallet::call_index(12)]
1950 #[pallet::weight(T::WeightInfo::force_no_eras())]
1951 pub fn force_no_eras(origin: OriginFor<T>) -> DispatchResult {
1952 ensure_root(origin)?;
1953 Self::set_force_era(Forcing::ForceNone);
1954 Ok(())
1955 }
1956
1957 #[pallet::call_index(13)]
1968 #[pallet::weight(T::WeightInfo::force_new_era())]
1969 pub fn force_new_era(origin: OriginFor<T>) -> DispatchResult {
1970 ensure_root(origin)?;
1971 Self::set_force_era(Forcing::ForceNew);
1972 Ok(())
1973 }
1974
1975 #[pallet::call_index(15)]
1984 #[pallet::weight(T::WeightInfo::force_unstake())]
1985 pub fn force_unstake(
1986 origin: OriginFor<T>,
1987 stash: T::AccountId,
1988 _num_slashing_spans: u32,
1989 ) -> DispatchResult {
1990 ensure_root(origin)?;
1991
1992 Self::kill_stash(&stash)?;
1994
1995 Ok(())
1996 }
1997
1998 #[pallet::call_index(16)]
2008 #[pallet::weight(T::WeightInfo::force_new_era_always())]
2009 pub fn force_new_era_always(origin: OriginFor<T>) -> DispatchResult {
2010 ensure_root(origin)?;
2011 Self::set_force_era(Forcing::ForceAlways);
2012 Ok(())
2013 }
2014
2015 #[pallet::call_index(17)]
2027 #[pallet::weight(T::WeightInfo::cancel_deferred_slash(validator_slashes.len() as u32))]
2028 pub fn cancel_deferred_slash(
2029 origin: OriginFor<T>,
2030 era: EraIndex,
2031 validator_slashes: Vec<(T::AccountId, Perbill)>,
2032 ) -> DispatchResult {
2033 T::AdminOrigin::ensure_origin(origin)?;
2034 ensure!(!validator_slashes.is_empty(), Error::<T>::EmptyTargets);
2035
2036 let mut cancelled_slashes = CancelledSlashes::<T>::get(&era);
2038
2039 for (validator, slash_fraction) in validator_slashes {
2041 cancelled_slashes.retain(|(v, _)| v != &validator);
2046
2047 cancelled_slashes
2049 .try_push((validator.clone(), slash_fraction))
2050 .map_err(|_| Error::<T>::BoundNotMet)
2051 .defensive_proof("cancelled_slashes should have capacity for all validators")?;
2052
2053 Self::deposit_event(Event::<T>::SlashCancelled { slash_era: era, validator });
2054 }
2055
2056 CancelledSlashes::<T>::insert(&era, cancelled_slashes);
2058
2059 Ok(())
2060 }
2061
2062 #[pallet::call_index(18)]
2076 #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked(T::MaxExposurePageSize::get()))]
2077 pub fn payout_stakers(
2078 origin: OriginFor<T>,
2079 validator_stash: T::AccountId,
2080 era: EraIndex,
2081 ) -> DispatchResultWithPostInfo {
2082 ensure_signed(origin)?;
2083
2084 Self::do_payout_stakers(validator_stash, era)
2085 }
2086
2087 #[pallet::call_index(19)]
2091 #[pallet::weight(T::WeightInfo::rebond(T::MaxUnlockingChunks::get() as u32))]
2092 pub fn rebond(
2093 origin: OriginFor<T>,
2094 #[pallet::compact] value: BalanceOf<T>,
2095 ) -> DispatchResultWithPostInfo {
2096 let controller = ensure_signed(origin)?;
2097 let ledger = Self::ledger(Controller(controller))?;
2098
2099 ensure!(!T::Filter::contains(&ledger.stash), Error::<T>::Restricted);
2100 ensure!(!ledger.unlocking.is_empty(), Error::<T>::NoUnlockChunk);
2101
2102 let initial_unlocking = ledger.unlocking.len() as u32;
2103 let (ledger, rebonded_value) = ledger.rebond(value);
2104 ensure!(ledger.active >= Self::min_chilled_bond(), Error::<T>::InsufficientBond);
2106
2107 Self::deposit_event(Event::<T>::Bonded {
2108 stash: ledger.stash.clone(),
2109 amount: rebonded_value,
2110 });
2111
2112 let stash = ledger.stash.clone();
2113 let final_unlocking = ledger.unlocking.len();
2114
2115 ledger.update()?;
2117 if T::VoterList::contains(&stash) {
2118 let _ = T::VoterList::on_update(&stash, Self::weight_of(&stash));
2119 }
2120
2121 let removed_chunks = 1u32 .saturating_add(initial_unlocking)
2123 .saturating_sub(final_unlocking as u32);
2124 Ok(Some(T::WeightInfo::rebond(removed_chunks)).into())
2125 }
2126
2127 #[pallet::call_index(20)]
2146 #[pallet::weight(T::WeightInfo::reap_stash())]
2147 pub fn reap_stash(
2148 origin: OriginFor<T>,
2149 stash: T::AccountId,
2150 _num_slashing_spans: u32,
2151 ) -> DispatchResultWithPostInfo {
2152 let _ = ensure_signed(origin)?;
2153
2154 ensure!(!Self::is_virtual_staker(&stash), Error::<T>::VirtualStakerNotAllowed);
2156
2157 let min_chilled_bond = Self::min_chilled_bond();
2158 let origin_balance = asset::total_balance::<T>(&stash);
2159 let ledger_total =
2160 Self::ledger(Stash(stash.clone())).map(|l| l.total).unwrap_or_default();
2161 let reapable = origin_balance < min_chilled_bond ||
2162 origin_balance.is_zero() ||
2163 ledger_total < min_chilled_bond ||
2164 ledger_total.is_zero();
2165 ensure!(reapable, Error::<T>::FundedTarget);
2166
2167 Self::kill_stash(&stash)?;
2169
2170 Ok(Pays::No.into())
2171 }
2172
2173 #[pallet::call_index(21)]
2185 #[pallet::weight(T::WeightInfo::kick(who.len() as u32))]
2186 pub fn kick(origin: OriginFor<T>, who: Vec<AccountIdLookupOf<T>>) -> DispatchResult {
2187 let controller = ensure_signed(origin)?;
2188 let ledger = Self::ledger(Controller(controller))?;
2189 let stash = &ledger.stash;
2190
2191 for nom_stash in who
2192 .into_iter()
2193 .map(T::Lookup::lookup)
2194 .collect::<Result<Vec<T::AccountId>, _>>()?
2195 .into_iter()
2196 {
2197 Nominators::<T>::mutate(&nom_stash, |maybe_nom| {
2198 if let Some(ref mut nom) = maybe_nom {
2199 if let Some(pos) = nom.targets.iter().position(|v| v == stash) {
2200 nom.targets.swap_remove(pos);
2201 Self::deposit_event(Event::<T>::Kicked {
2202 nominator: nom_stash.clone(),
2203 stash: stash.clone(),
2204 });
2205 }
2206 }
2207 });
2208 }
2209
2210 Ok(())
2211 }
2212
2213 #[pallet::call_index(22)]
2233 #[pallet::weight(
2234 T::WeightInfo::set_staking_configs_all_set()
2235 .max(T::WeightInfo::set_staking_configs_all_remove())
2236 )]
2237 pub fn set_staking_configs(
2238 origin: OriginFor<T>,
2239 min_nominator_bond: ConfigOp<BalanceOf<T>>,
2240 min_validator_bond: ConfigOp<BalanceOf<T>>,
2241 max_nominator_count: ConfigOp<u32>,
2242 max_validator_count: ConfigOp<u32>,
2243 chill_threshold: ConfigOp<Percent>,
2244 min_commission: ConfigOp<Perbill>,
2245 max_staked_rewards: ConfigOp<Percent>,
2246 ) -> DispatchResult {
2247 ensure_root(origin)?;
2248
2249 macro_rules! config_op_exp {
2250 ($storage:ty, $op:ident) => {
2251 match $op {
2252 ConfigOp::Noop => (),
2253 ConfigOp::Set(v) => <$storage>::put(v),
2254 ConfigOp::Remove => <$storage>::kill(),
2255 }
2256 };
2257 }
2258
2259 config_op_exp!(MinNominatorBond<T>, min_nominator_bond);
2260 config_op_exp!(MinValidatorBond<T>, min_validator_bond);
2261 config_op_exp!(MaxNominatorsCount<T>, max_nominator_count);
2262 config_op_exp!(MaxValidatorsCount<T>, max_validator_count);
2263 config_op_exp!(ChillThreshold<T>, chill_threshold);
2264 config_op_exp!(MinCommission<T>, min_commission);
2265 config_op_exp!(MaxStakedRewards<T>, max_staked_rewards);
2266 Ok(())
2267 }
2268 #[pallet::call_index(23)]
2295 #[pallet::weight(T::WeightInfo::chill_other())]
2296 pub fn chill_other(origin: OriginFor<T>, stash: T::AccountId) -> DispatchResult {
2297 let caller = ensure_signed(origin)?;
2299 let ledger = Self::ledger(Stash(stash.clone()))?;
2300 let controller = ledger
2301 .controller()
2302 .defensive_proof(
2303 "Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.",
2304 )
2305 .ok_or(Error::<T>::NotController)?;
2306
2307 if Nominators::<T>::contains_key(&stash) && Nominators::<T>::get(&stash).is_none() {
2324 Self::chill_stash(&stash);
2325 return Ok(());
2326 }
2327
2328 if caller != controller {
2329 let threshold = ChillThreshold::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2330 let min_active_bond = if Nominators::<T>::contains_key(&stash) {
2331 let max_nominator_count =
2332 MaxNominatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2333 let current_nominator_count = Nominators::<T>::count();
2334 ensure!(
2335 threshold * max_nominator_count < current_nominator_count,
2336 Error::<T>::CannotChillOther
2337 );
2338 Self::min_nominator_bond()
2339 } else if Validators::<T>::contains_key(&stash) {
2340 let max_validator_count =
2341 MaxValidatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2342 let current_validator_count = Validators::<T>::count();
2343 ensure!(
2344 threshold * max_validator_count < current_validator_count,
2345 Error::<T>::CannotChillOther
2346 );
2347 Self::min_validator_bond()
2348 } else {
2349 Zero::zero()
2350 };
2351
2352 ensure!(ledger.active < min_active_bond, Error::<T>::CannotChillOther);
2353 }
2354
2355 Self::chill_stash(&stash);
2356 Ok(())
2357 }
2358
2359 #[pallet::call_index(24)]
2363 #[pallet::weight(T::WeightInfo::force_apply_min_commission())]
2364 pub fn force_apply_min_commission(
2365 origin: OriginFor<T>,
2366 validator_stash: T::AccountId,
2367 ) -> DispatchResult {
2368 ensure_signed(origin)?;
2369 let min_commission = MinCommission::<T>::get();
2370 Validators::<T>::try_mutate_exists(validator_stash, |maybe_prefs| {
2371 maybe_prefs
2372 .as_mut()
2373 .map(|prefs| {
2374 (prefs.commission < min_commission)
2375 .then(|| prefs.commission = min_commission)
2376 })
2377 .ok_or(Error::<T>::NotStash)
2378 })?;
2379 Ok(())
2380 }
2381
2382 #[pallet::call_index(25)]
2387 #[pallet::weight(T::WeightInfo::set_min_commission())]
2388 pub fn set_min_commission(origin: OriginFor<T>, new: Perbill) -> DispatchResult {
2389 T::AdminOrigin::ensure_origin(origin)?;
2390 MinCommission::<T>::put(new);
2391 Ok(())
2392 }
2393
2394 #[pallet::call_index(26)]
2412 #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked(T::MaxExposurePageSize::get()))]
2413 pub fn payout_stakers_by_page(
2414 origin: OriginFor<T>,
2415 validator_stash: T::AccountId,
2416 era: EraIndex,
2417 page: Page,
2418 ) -> DispatchResultWithPostInfo {
2419 ensure_signed(origin)?;
2420 Self::do_payout_stakers_by_page(validator_stash, era, page)
2421 }
2422
2423 #[pallet::call_index(27)]
2430 #[pallet::weight(T::WeightInfo::update_payee())]
2431 pub fn update_payee(
2432 origin: OriginFor<T>,
2433 controller: T::AccountId,
2434 ) -> DispatchResultWithPostInfo {
2435 let _ = ensure_signed(origin)?;
2436 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()))?;
2437
2438 ensure!(
2439 (Payee::<T>::get(&ledger.stash) == {
2440 #[allow(deprecated)]
2441 Some(RewardDestination::Controller)
2442 }),
2443 Error::<T>::NotController
2444 );
2445
2446 let _ = ledger
2447 .set_payee(RewardDestination::Account(controller))
2448 .defensive_proof("ledger should have been previously retrieved from storage.")?;
2449
2450 Ok(Pays::No.into())
2451 }
2452
2453 #[pallet::call_index(28)]
2461 #[pallet::weight(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32))]
2462 pub fn deprecate_controller_batch(
2463 origin: OriginFor<T>,
2464 controllers: BoundedVec<T::AccountId, T::MaxControllersInDeprecationBatch>,
2465 ) -> DispatchResultWithPostInfo {
2466 T::AdminOrigin::ensure_origin(origin)?;
2467
2468 let filtered_batch_with_ledger: Vec<_> = controllers
2470 .iter()
2471 .filter_map(|controller| {
2472 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()));
2473 ledger.ok().map_or(None, |ledger| {
2474 let payee_deprecated = Payee::<T>::get(&ledger.stash) == {
2477 #[allow(deprecated)]
2478 Some(RewardDestination::Controller)
2479 };
2480
2481 if ledger.stash != *controller && !payee_deprecated {
2482 Some(ledger)
2483 } else {
2484 None
2485 }
2486 })
2487 })
2488 .collect();
2489
2490 let mut failures = 0;
2492 for ledger in filtered_batch_with_ledger {
2493 let _ = ledger.clone().set_controller_to_stash().map_err(|_| failures += 1);
2494 }
2495 Self::deposit_event(Event::<T>::ControllerBatchDeprecated { failures });
2496
2497 Ok(Some(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32)).into())
2498 }
2499
2500 #[pallet::call_index(29)]
2512 #[pallet::weight(T::WeightInfo::restore_ledger())]
2513 pub fn restore_ledger(
2514 origin: OriginFor<T>,
2515 stash: T::AccountId,
2516 maybe_controller: Option<T::AccountId>,
2517 maybe_total: Option<BalanceOf<T>>,
2518 maybe_unlocking: Option<BoundedVec<UnlockChunk<BalanceOf<T>>, T::MaxUnlockingChunks>>,
2519 ) -> DispatchResult {
2520 T::AdminOrigin::ensure_origin(origin)?;
2521
2522 ensure!(!Self::is_virtual_staker(&stash), Error::<T>::VirtualStakerNotAllowed);
2524
2525 let current_lock = asset::staked::<T>(&stash);
2526 let stash_balance = asset::stakeable_balance::<T>(&stash);
2527
2528 let (new_controller, new_total) = match Self::inspect_bond_state(&stash) {
2529 Ok(LedgerIntegrityState::Corrupted) => {
2530 let new_controller = maybe_controller.unwrap_or(stash.clone());
2531
2532 let new_total = if let Some(total) = maybe_total {
2533 let new_total = total.min(stash_balance);
2534 asset::update_stake::<T>(&stash, new_total)?;
2536 new_total
2537 } else {
2538 current_lock
2539 };
2540
2541 Ok((new_controller, new_total))
2542 },
2543 Ok(LedgerIntegrityState::CorruptedKilled) => {
2544 if current_lock == Zero::zero() {
2545 ensure!(maybe_total.is_some(), Error::<T>::CannotRestoreLedger);
2549 Ok((
2550 stash.clone(),
2551 maybe_total.expect("total exists as per the check above; qed."),
2552 ))
2553 } else {
2554 Ok((stash.clone(), current_lock))
2555 }
2556 },
2557 Ok(LedgerIntegrityState::LockCorrupted) => {
2558 let new_total =
2561 maybe_total.ok_or(Error::<T>::CannotRestoreLedger)?.min(stash_balance);
2562 asset::update_stake::<T>(&stash, new_total)?;
2563
2564 Ok((stash.clone(), new_total))
2565 },
2566 Err(Error::<T>::BadState) => {
2567 asset::kill_stake::<T>(&stash)?;
2569 ensure!(
2570 Self::inspect_bond_state(&stash) == Err(Error::<T>::NotStash),
2571 Error::<T>::BadState
2572 );
2573
2574 return Ok(());
2575 },
2576 Ok(LedgerIntegrityState::Ok) | Err(_) => Err(Error::<T>::CannotRestoreLedger),
2577 }?;
2578
2579 Bonded::<T>::insert(&stash, &new_controller);
2581
2582 let mut ledger = StakingLedger::<T>::new(stash.clone(), new_total);
2584 ledger.controller = Some(new_controller);
2585 ledger.unlocking = maybe_unlocking.unwrap_or_default();
2586 ledger.update()?;
2587
2588 ensure!(
2589 Self::inspect_bond_state(&stash) == Ok(LedgerIntegrityState::Ok),
2590 Error::<T>::BadState
2591 );
2592 Ok(())
2593 }
2594
2595 #[pallet::call_index(30)]
2603 #[pallet::weight(T::WeightInfo::migrate_currency())]
2604 pub fn migrate_currency(
2605 origin: OriginFor<T>,
2606 stash: T::AccountId,
2607 ) -> DispatchResultWithPostInfo {
2608 let _ = ensure_signed(origin)?;
2609 Self::do_migrate_currency(&stash)?;
2610
2611 Ok(Pays::No.into())
2613 }
2614
2615 #[pallet::call_index(31)]
2649 #[pallet::weight(T::WeightInfo::apply_slash())]
2650 pub fn apply_slash(
2651 origin: OriginFor<T>,
2652 slash_era: EraIndex,
2653 slash_key: (T::AccountId, Perbill, u32),
2654 ) -> DispatchResultWithPostInfo {
2655 let _ = ensure_signed(origin)?;
2656 let active_era = ActiveEra::<T>::get().map(|a| a.index).unwrap_or_default();
2657 ensure!(slash_era <= active_era, Error::<T>::EraNotStarted);
2658
2659 ensure!(
2661 !Self::check_slash_cancelled(slash_era, &slash_key.0, slash_key.1),
2662 Error::<T>::CancelledSlash
2663 );
2664
2665 let unapplied_slash = UnappliedSlashes::<T>::take(&slash_era, &slash_key)
2666 .ok_or(Error::<T>::InvalidSlashRecord)?;
2667 slashing::apply_slash::<T>(unapplied_slash, slash_era);
2668
2669 Ok(Pays::No.into())
2670 }
2671
2672 #[pallet::call_index(32)]
2684 #[pallet::weight({
2686 let v = T::MaxValidatorSet::get();
2687 T::WeightInfo::prune_era_stakers_paged(v)
2688 .max(T::WeightInfo::prune_era_stakers_overview(v))
2689 .max(T::WeightInfo::prune_era_validator_prefs(v))
2690 .max(T::WeightInfo::prune_era_claimed_rewards(v))
2691 .max(T::WeightInfo::prune_era_validator_reward())
2692 .max(T::WeightInfo::prune_era_reward_points())
2693 .max(T::WeightInfo::prune_era_total_stake())
2694 })]
2695 pub fn prune_era_step(origin: OriginFor<T>, era: EraIndex) -> DispatchResultWithPostInfo {
2696 let _ = ensure_signed(origin)?;
2697
2698 let active_era = crate::session_rotation::Rotator::<T>::active_era();
2700 let history_depth = T::HistoryDepth::get();
2701 let earliest_prunable_era = active_era.saturating_sub(history_depth).saturating_sub(1);
2702 ensure!(era <= earliest_prunable_era, Error::<T>::EraNotPrunable);
2703
2704 let actual_weight = Self::do_prune_era_step(era)?;
2705
2706 Ok(frame_support::dispatch::PostDispatchInfo {
2707 actual_weight: Some(actual_weight),
2708 pays_fee: frame_support::dispatch::Pays::No,
2709 })
2710 }
2711 }
2712}