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::{traits::ConstBool, 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 SingleEntryCleanups,
91 ValidatorSlashInEra,
93 }
94
95 const STORAGE_VERSION: StorageVersion = StorageVersion::new(17);
97
98 #[pallet::pallet]
99 #[pallet::storage_version(STORAGE_VERSION)]
100 pub struct Pallet<T>(_);
101
102 #[derive(TypeInfo, Debug, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq)]
104 pub enum ConfigOp<T: Default + Codec> {
105 Noop,
107 Set(T),
109 Remove,
111 }
112
113 #[pallet::config(with_default)]
114 pub trait Config: frame_system::Config {
115 #[pallet::no_default]
117 type OldCurrency: InspectLockableCurrency<
118 Self::AccountId,
119 Moment = BlockNumberFor<Self>,
120 Balance = Self::CurrencyBalance,
121 >;
122
123 #[pallet::no_default]
125 type Currency: FunHoldMutate<
126 Self::AccountId,
127 Reason = Self::RuntimeHoldReason,
128 Balance = Self::CurrencyBalance,
129 > + FunMutate<Self::AccountId, Balance = Self::CurrencyBalance>
130 + FunHoldBalanced<Self::AccountId, Balance = Self::CurrencyBalance>;
131
132 #[pallet::no_default_bounds]
134 type RuntimeHoldReason: From<HoldReason>;
135
136 type CurrencyBalance: sp_runtime::traits::AtLeast32BitUnsigned
139 + codec::FullCodec
140 + DecodeWithMemTracking
141 + HasCompact<Type: DecodeWithMemTracking>
142 + Copy
143 + MaybeSerializeDeserialize
144 + core::fmt::Debug
145 + Default
146 + From<u64>
147 + TypeInfo
148 + Send
149 + Sync
150 + MaxEncodedLen;
151
152 #[pallet::no_default_bounds]
159 type CurrencyToVote: sp_staking::currency_to_vote::CurrencyToVote<BalanceOf<Self>>;
160
161 #[pallet::no_default]
163 type ElectionProvider: ElectionProvider<
164 AccountId = Self::AccountId,
165 BlockNumber = BlockNumberFor<Self>,
166 DataProvider = Pallet<Self>,
168 >;
169
170 #[pallet::no_default_bounds]
172 type NominationsQuota: NominationsQuota<BalanceOf<Self>>;
173
174 #[pallet::constant]
188 type HistoryDepth: Get<u32>;
189
190 #[pallet::no_default_bounds]
193 type RewardRemainder: OnUnbalanced<NegativeImbalanceOf<Self>>;
194
195 #[pallet::no_default_bounds]
197 type Slash: OnUnbalanced<NegativeImbalanceOf<Self>>;
198
199 #[pallet::no_default_bounds]
203 type Reward: OnUnbalanced<PositiveImbalanceOf<Self>>;
204
205 #[pallet::constant]
207 type SessionsPerEra: Get<SessionIndex>;
208
209 #[pallet::constant]
224 type PlanningEraOffset: Get<SessionIndex>;
225
226 #[pallet::constant]
232 type BondingDuration: Get<EraIndex>;
233
234 #[pallet::constant]
243 type NominatorFastUnbondDuration: Get<EraIndex>;
244
245 #[pallet::constant]
250 type SlashDeferDuration: Get<EraIndex>;
251
252 #[pallet::no_default]
256 type AdminOrigin: EnsureOrigin<Self::RuntimeOrigin>;
257
258 #[pallet::no_default]
261 type EraPayout: EraPayout<BalanceOf<Self>>;
262
263 #[pallet::constant]
275 type MaxExposurePageSize: Get<u32>;
276
277 #[pallet::constant]
282 type MaxValidatorSet: Get<u32>;
283
284 #[pallet::no_default]
296 type VoterList: SortedListProvider<Self::AccountId, Score = VoteWeight>;
297
298 #[pallet::no_default]
319 type TargetList: SortedListProvider<Self::AccountId, Score = BalanceOf<Self>>;
320
321 #[pallet::constant]
332 type MaxUnlockingChunks: Get<u32>;
333
334 type MaxControllersInDeprecationBatch: Get<u32>;
336
337 #[pallet::no_default_bounds]
342 type EventListeners: sp_staking::OnStakingUpdate<Self::AccountId, BalanceOf<Self>>;
343
344 #[pallet::constant]
353 type MaxEraDuration: Get<u64>;
354
355 #[pallet::constant]
361 type MaxPruningItems: Get<u32>;
362
363 #[pallet::no_default]
366 type RcClientInterface: pallet_staking_async_rc_client::RcClientInterface<
367 AccountId = Self::AccountId,
368 >;
369
370 #[pallet::no_default_bounds]
371 type Filter: Contains<Self::AccountId>;
376
377 type WeightInfo: WeightInfo;
379 }
380
381 #[pallet::composite_enum]
383 pub enum HoldReason {
384 #[codec(index = 0)]
386 Staking,
387 }
388
389 pub mod config_preludes {
391 use super::*;
392 use frame_support::{derive_impl, parameter_types, traits::ConstU32};
393 pub struct TestDefaultConfig;
394
395 #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
396 impl frame_system::DefaultConfig for TestDefaultConfig {}
397
398 parameter_types! {
399 pub const SessionsPerEra: SessionIndex = 3;
400 pub const BondingDuration: EraIndex = 3;
401 pub const NominatorFastUnbondDuration: EraIndex = 2;
402 pub const MaxPruningItems: u32 = 100;
403 }
404
405 #[frame_support::register_default_impl(TestDefaultConfig)]
406 impl DefaultConfig for TestDefaultConfig {
407 #[inject_runtime_type]
408 type RuntimeHoldReason = ();
409 type CurrencyBalance = u128;
410 type CurrencyToVote = ();
411 type NominationsQuota = crate::FixedNominationsQuota<16>;
412 type HistoryDepth = ConstU32<84>;
413 type RewardRemainder = ();
414 type Slash = ();
415 type Reward = ();
416 type SessionsPerEra = SessionsPerEra;
417 type BondingDuration = BondingDuration;
418 type NominatorFastUnbondDuration = NominatorFastUnbondDuration;
419 type PlanningEraOffset = ConstU32<1>;
420 type SlashDeferDuration = ();
421 type MaxExposurePageSize = ConstU32<64>;
422 type MaxUnlockingChunks = ConstU32<32>;
423 type MaxValidatorSet = ConstU32<100>;
424 type MaxControllersInDeprecationBatch = ConstU32<100>;
425 type MaxEraDuration = ();
426 type MaxPruningItems = MaxPruningItems;
427 type EventListeners = ();
428 type Filter = Nothing;
429 type WeightInfo = ();
430 }
431 }
432
433 #[pallet::storage]
435 pub type ValidatorCount<T> = StorageValue<_, u32, ValueQuery>;
436
437 #[pallet::storage]
441 pub type Bonded<T: Config> = StorageMap<_, Twox64Concat, T::AccountId, T::AccountId>;
442
443 #[pallet::storage]
445 pub type MinNominatorBond<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
446
447 #[pallet::storage]
449 pub type MinValidatorBond<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
450
451 #[pallet::storage]
453 pub type MinimumActiveStake<T> = StorageValue<_, BalanceOf<T>, ValueQuery>;
454
455 #[pallet::storage]
459 pub type MinCommission<T: Config> = StorageValue<_, Perbill, ValueQuery>;
460
461 #[pallet::storage]
469 pub type AreNominatorsSlashable<T: Config> = StorageValue<_, bool, ValueQuery, ConstBool<true>>;
470
471 #[pallet::storage]
479 pub type ErasNominatorsSlashable<T: Config> =
480 StorageMap<_, Twox64Concat, EraIndex, bool, OptionQuery>;
481
482 #[pallet::storage]
487 pub type Ledger<T: Config> = StorageMap<_, Blake2_128Concat, T::AccountId, StakingLedger<T>>;
488
489 #[pallet::storage]
493 pub type Payee<T: Config> =
494 StorageMap<_, Twox64Concat, T::AccountId, RewardDestination<T::AccountId>, OptionQuery>;
495
496 #[pallet::storage]
500 pub type Validators<T: Config> =
501 CountedStorageMap<_, Twox64Concat, T::AccountId, ValidatorPrefs, ValueQuery>;
502
503 #[pallet::storage]
507 pub type MaxValidatorsCount<T> = StorageValue<_, u32, OptionQuery>;
508
509 #[pallet::storage]
522 pub type LastValidatorEra<T: Config> = StorageMap<_, Twox64Concat, T::AccountId, EraIndex>;
523
524 #[pallet::storage]
544 pub type Nominators<T: Config> =
545 CountedStorageMap<_, Twox64Concat, T::AccountId, Nominations<T>>;
546
547 #[pallet::storage]
554 pub type VirtualStakers<T: Config> = CountedStorageMap<_, Twox64Concat, T::AccountId, ()>;
555
556 #[pallet::storage]
560 pub type MaxNominatorsCount<T> = StorageValue<_, u32, OptionQuery>;
561
562 #[pallet::storage]
569 pub type CurrentEra<T> = StorageValue<_, EraIndex>;
570
571 #[pallet::storage]
576 pub type ActiveEra<T> = StorageValue<_, ActiveEraInfo>;
577
578 pub struct BondedErasBound<T>(core::marker::PhantomData<T>);
580 impl<T: Config> Get<u32> for BondedErasBound<T> {
581 fn get() -> u32 {
582 T::BondingDuration::get().saturating_add(1)
583 }
584 }
585
586 #[pallet::storage]
591 pub type BondedEras<T: Config> =
592 StorageValue<_, BoundedVec<(EraIndex, SessionIndex), BondedErasBound<T>>, ValueQuery>;
593
594 #[pallet::storage]
609 pub type ErasStakersOverview<T: Config> = StorageDoubleMap<
610 _,
611 Twox64Concat,
612 EraIndex,
613 Twox64Concat,
614 T::AccountId,
615 PagedExposureMetadata<BalanceOf<T>>,
616 OptionQuery,
617 >;
618
619 #[derive(PartialEqNoBound, Encode, Decode, DebugNoBound, TypeInfo, DefaultNoBound)]
628 #[scale_info(skip_type_params(T))]
629 pub struct BoundedExposurePage<T: Config>(pub ExposurePage<T::AccountId, BalanceOf<T>>);
630 impl<T: Config> Deref for BoundedExposurePage<T> {
631 type Target = ExposurePage<T::AccountId, BalanceOf<T>>;
632
633 fn deref(&self) -> &Self::Target {
634 &self.0
635 }
636 }
637
638 impl<T: Config> core::ops::DerefMut for BoundedExposurePage<T> {
639 fn deref_mut(&mut self) -> &mut Self::Target {
640 &mut self.0
641 }
642 }
643
644 impl<T: Config> codec::MaxEncodedLen for BoundedExposurePage<T> {
645 fn max_encoded_len() -> usize {
646 let max_exposure_page_size = T::MaxExposurePageSize::get() as usize;
647 let individual_size =
648 T::AccountId::max_encoded_len() + BalanceOf::<T>::max_encoded_len();
649
650 BalanceOf::<T>::max_encoded_len() +
652 max_exposure_page_size.saturating_mul(individual_size)
654 }
655 }
656
657 impl<T: Config> From<ExposurePage<T::AccountId, BalanceOf<T>>> for BoundedExposurePage<T> {
658 fn from(value: ExposurePage<T::AccountId, BalanceOf<T>>) -> Self {
659 Self(value)
660 }
661 }
662
663 impl<T: Config> From<BoundedExposurePage<T>> for ExposurePage<T::AccountId, BalanceOf<T>> {
664 fn from(value: BoundedExposurePage<T>) -> Self {
665 value.0
666 }
667 }
668
669 impl<T: Config> codec::EncodeLike<BoundedExposurePage<T>>
670 for ExposurePage<T::AccountId, BalanceOf<T>>
671 {
672 }
673
674 #[pallet::storage]
681 pub type ErasStakersPaged<T: Config> = StorageNMap<
682 _,
683 (
684 NMapKey<Twox64Concat, EraIndex>,
685 NMapKey<Twox64Concat, T::AccountId>,
686 NMapKey<Twox64Concat, Page>,
687 ),
688 BoundedExposurePage<T>,
689 OptionQuery,
690 >;
691
692 pub struct ClaimedRewardsBound<T>(core::marker::PhantomData<T>);
693 impl<T: Config> Get<u32> for ClaimedRewardsBound<T> {
694 fn get() -> u32 {
695 let max_total_nominators_per_validator =
696 <T::ElectionProvider as ElectionProvider>::MaxBackersPerWinnerFinal::get();
697 let exposure_page_size = T::MaxExposurePageSize::get();
698 max_total_nominators_per_validator
699 .saturating_div(exposure_page_size)
700 .saturating_add(1)
701 }
702 }
703
704 #[pallet::storage]
711 pub type ClaimedRewards<T: Config> = StorageDoubleMap<
712 _,
713 Twox64Concat,
714 EraIndex,
715 Twox64Concat,
716 T::AccountId,
717 WeakBoundedVec<Page, ClaimedRewardsBound<T>>,
718 ValueQuery,
719 >;
720
721 #[pallet::storage]
728 pub type ErasValidatorPrefs<T: Config> = StorageDoubleMap<
729 _,
730 Twox64Concat,
731 EraIndex,
732 Twox64Concat,
733 T::AccountId,
734 ValidatorPrefs,
735 ValueQuery,
736 >;
737
738 #[pallet::storage]
742 pub type ErasValidatorReward<T: Config> = StorageMap<_, Twox64Concat, EraIndex, BalanceOf<T>>;
743
744 #[pallet::storage]
747 pub type ErasRewardPoints<T: Config> =
748 StorageMap<_, Twox64Concat, EraIndex, EraRewardPoints<T>, ValueQuery>;
749
750 #[pallet::storage]
753 pub type ErasTotalStake<T: Config> =
754 StorageMap<_, Twox64Concat, EraIndex, BalanceOf<T>, ValueQuery>;
755
756 #[pallet::storage]
758 pub type ForceEra<T> = StorageValue<_, Forcing, ValueQuery>;
759
760 #[pallet::storage]
764 pub type MaxStakedRewards<T> = StorageValue<_, Percent, OptionQuery>;
765
766 #[pallet::storage]
770 pub type SlashRewardFraction<T> = StorageValue<_, Perbill, ValueQuery>;
771
772 #[pallet::storage]
775 pub type CanceledSlashPayout<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
776
777 #[pallet::storage]
788 pub type OffenceQueue<T: Config> = StorageDoubleMap<
789 _,
790 Twox64Concat,
791 EraIndex,
792 Twox64Concat,
793 T::AccountId,
794 slashing::OffenceRecord<T::AccountId>,
795 >;
796
797 #[pallet::storage]
809 pub type OffenceQueueEras<T: Config> = StorageValue<_, WeakBoundedVec<u32, T::BondingDuration>>;
810
811 #[pallet::storage]
824 pub type ProcessingOffence<T: Config> =
825 StorageValue<_, (EraIndex, T::AccountId, slashing::OffenceRecord<T::AccountId>)>;
826
827 #[pallet::storage]
829 pub type UnappliedSlashes<T: Config> = StorageDoubleMap<
830 _,
831 Twox64Concat,
832 EraIndex,
833 Twox64Concat,
834 (T::AccountId, Perbill, u32),
836 UnappliedSlash<T>,
837 OptionQuery,
838 >;
839
840 #[pallet::storage]
846 pub type CancelledSlashes<T: Config> = StorageMap<
847 _,
848 Twox64Concat,
849 EraIndex,
850 BoundedVec<(T::AccountId, Perbill), T::MaxValidatorSet>,
851 ValueQuery,
852 >;
853
854 #[pallet::storage]
857 pub type ValidatorSlashInEra<T: Config> = StorageDoubleMap<
858 _,
859 Twox64Concat,
860 EraIndex,
861 Twox64Concat,
862 T::AccountId,
863 (Perbill, BalanceOf<T>),
864 >;
865
866 #[pallet::storage]
870 pub type ChillThreshold<T: Config> = StorageValue<_, Percent, OptionQuery>;
871
872 #[pallet::storage]
877 pub type VoterSnapshotStatus<T: Config> =
878 StorageValue<_, SnapshotStatus<T::AccountId>, ValueQuery>;
879
880 #[pallet::storage]
887 pub type NextElectionPage<T: Config> = StorageValue<_, PageIndex, OptionQuery>;
888
889 #[pallet::storage]
891 pub type ElectableStashes<T: Config> =
892 StorageValue<_, BoundedBTreeSet<T::AccountId, T::MaxValidatorSet>, ValueQuery>;
893
894 #[pallet::storage]
896 pub type EraPruningState<T: Config> = StorageMap<_, Twox64Concat, EraIndex, PruningStep>;
897
898 #[pallet::genesis_config]
899 #[derive(frame_support::DefaultNoBound, frame_support::DebugNoBound)]
900 pub struct GenesisConfig<T: Config> {
901 pub validator_count: u32,
902 pub force_era: Forcing,
903 pub slash_reward_fraction: Perbill,
904 pub canceled_payout: BalanceOf<T>,
905 pub stakers: Vec<(T::AccountId, BalanceOf<T>, crate::StakerStatus<T::AccountId>)>,
906 pub min_nominator_bond: BalanceOf<T>,
907 pub min_validator_bond: BalanceOf<T>,
908 pub max_validator_count: Option<u32>,
909 pub max_nominator_count: Option<u32>,
910 pub dev_stakers: Option<(u32, u32)>,
917 pub active_era: (u32, u32, u64),
919 }
920
921 impl<T: Config> GenesisConfig<T> {
922 fn generate_endowed_bonded_account(derivation: &str, rng: &mut ChaChaRng) -> T::AccountId {
923 let pair: SrPair = Pair::from_string(&derivation, None)
924 .expect(&format!("Failed to parse derivation string: {derivation}"));
925 let who = T::AccountId::decode(&mut &pair.public().encode()[..])
926 .expect(&format!("Failed to decode public key from pair: {:?}", pair.public()));
927
928 let (min, max) = T::VoterList::range();
929 let stake = BalanceOf::<T>::from(rng.next_u64().min(max).max(min));
930 let two: BalanceOf<T> = 2u32.into();
931
932 assert_ok!(T::Currency::mint_into(&who, stake * two));
933 assert_ok!(<Pallet<T>>::bond(
934 T::RuntimeOrigin::from(Some(who.clone()).into()),
935 stake,
936 RewardDestination::Staked,
937 ));
938 who
939 }
940 }
941
942 #[pallet::genesis_build]
943 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
944 fn build(&self) {
945 crate::log!(trace, "initializing with {:?}", self);
946 assert!(
947 self.validator_count <=
948 <T::ElectionProvider as ElectionProvider>::MaxWinnersPerPage::get() *
949 <T::ElectionProvider as ElectionProvider>::Pages::get(),
950 "validator count is too high, `ElectionProvider` can never fulfill this"
951 );
952 ValidatorCount::<T>::put(self.validator_count);
953
954 ForceEra::<T>::put(self.force_era);
955 CanceledSlashPayout::<T>::put(self.canceled_payout);
956 SlashRewardFraction::<T>::put(self.slash_reward_fraction);
957 MinNominatorBond::<T>::put(self.min_nominator_bond);
958 MinValidatorBond::<T>::put(self.min_validator_bond);
959 if let Some(x) = self.max_validator_count {
960 MaxValidatorsCount::<T>::put(x);
961 }
962 if let Some(x) = self.max_nominator_count {
963 MaxNominatorsCount::<T>::put(x);
964 }
965
966 for &(ref stash, balance, ref status) in &self.stakers {
968 match status {
969 crate::StakerStatus::Validator => {
970 crate::log!(
971 trace,
972 "inserting genesis validator: {:?} => {:?} => {:?}",
973 stash,
974 balance,
975 status
976 );
977 assert!(
978 asset::free_to_stake::<T>(stash) >= balance,
979 "Stash does not have enough balance to bond."
980 );
981 assert_ok!(<Pallet<T>>::bond(
982 T::RuntimeOrigin::from(Some(stash.clone()).into()),
983 balance,
984 RewardDestination::Staked,
985 ));
986 assert_ok!(<Pallet<T>>::validate(
987 T::RuntimeOrigin::from(Some(stash.clone()).into()),
988 Default::default(),
989 ));
990 },
991 crate::StakerStatus::Idle => {
992 crate::log!(
993 trace,
994 "inserting genesis idle staker: {:?} => {:?} => {:?}",
995 stash,
996 balance,
997 status
998 );
999 assert!(
1000 asset::free_to_stake::<T>(stash) >= balance,
1001 "Stash does not have enough balance to bond."
1002 );
1003 assert_ok!(<Pallet<T>>::bond(
1004 T::RuntimeOrigin::from(Some(stash.clone()).into()),
1005 balance,
1006 RewardDestination::Staked,
1007 ));
1008 },
1009 _ => {},
1010 }
1011 }
1012
1013 for &(ref stash, balance, ref status) in &self.stakers {
1015 match status {
1016 crate::StakerStatus::Nominator(votes) => {
1017 crate::log!(
1018 trace,
1019 "inserting genesis nominator: {:?} => {:?} => {:?}",
1020 stash,
1021 balance,
1022 status
1023 );
1024 assert!(
1025 asset::free_to_stake::<T>(stash) >= balance,
1026 "Stash does not have enough balance to bond."
1027 );
1028 assert_ok!(<Pallet<T>>::bond(
1029 T::RuntimeOrigin::from(Some(stash.clone()).into()),
1030 balance,
1031 RewardDestination::Staked,
1032 ));
1033 assert_ok!(<Pallet<T>>::nominate(
1034 T::RuntimeOrigin::from(Some(stash.clone()).into()),
1035 votes.iter().map(|l| T::Lookup::unlookup(l.clone())).collect(),
1036 ));
1037 },
1038 _ => {},
1039 }
1040 }
1041
1042 assert_eq!(
1044 T::VoterList::count(),
1045 Nominators::<T>::count() + Validators::<T>::count(),
1046 "not all genesis stakers were inserted into sorted list provider, something is wrong."
1047 );
1048
1049 if let Some((validators, nominators)) = self.dev_stakers {
1051 crate::log!(
1052 debug,
1053 "generating dev stakers: validators: {}, nominators: {}",
1054 validators,
1055 nominators
1056 );
1057 let base_derivation = "//staker//{}";
1058
1059 let mut rng =
1062 ChaChaRng::from_seed(base_derivation.using_encoded(sp_core::blake2_256));
1063
1064 (0..validators).for_each(|index| {
1065 let derivation = base_derivation.replace("{}", &format!("validator{}", index));
1066 let who = Self::generate_endowed_bonded_account(&derivation, &mut rng);
1067 assert_ok!(<Pallet<T>>::validate(
1068 T::RuntimeOrigin::from(Some(who.clone()).into()),
1069 Default::default(),
1070 ));
1071 });
1072
1073 let all_validators = Validators::<T>::iter_keys().collect::<Vec<_>>();
1076
1077 (0..nominators).for_each(|index| {
1078 let derivation = base_derivation.replace("{}", &format!("nominator{}", index));
1079 let who = Self::generate_endowed_bonded_account(&derivation, &mut rng);
1080
1081 let random_nominations = all_validators
1082 .choose_multiple(&mut rng, MaxNominationsOf::<T>::get() as usize)
1083 .map(|v| v.clone())
1084 .collect::<Vec<_>>();
1085
1086 assert_ok!(<Pallet<T>>::nominate(
1087 T::RuntimeOrigin::from(Some(who.clone()).into()),
1088 random_nominations.iter().map(|l| T::Lookup::unlookup(l.clone())).collect(),
1089 ));
1090 })
1091 }
1092
1093 let (active_era, session_index, timestamp) = self.active_era;
1094 ActiveEra::<T>::put(ActiveEraInfo { index: active_era, start: Some(timestamp) });
1095 CurrentEra::<T>::put(active_era);
1097 BondedEras::<T>::put(
1099 BoundedVec::<_, BondedErasBound<T>>::try_from(
1100 alloc::vec![(active_era, session_index)]
1101 )
1102 .expect("bound for BondedEras is BondingDuration + 1; can contain at least one element; qed")
1103 );
1104 }
1105 }
1106
1107 #[pallet::event]
1108 #[pallet::generate_deposit(pub fn deposit_event)]
1109 pub enum Event<T: Config> {
1110 EraPaid {
1113 era_index: EraIndex,
1114 validator_payout: BalanceOf<T>,
1115 remainder: BalanceOf<T>,
1116 },
1117 Rewarded {
1119 stash: T::AccountId,
1120 dest: RewardDestination<T::AccountId>,
1121 amount: BalanceOf<T>,
1122 },
1123 Slashed {
1125 staker: T::AccountId,
1126 amount: BalanceOf<T>,
1127 },
1128 OldSlashingReportDiscarded {
1131 session_index: SessionIndex,
1132 },
1133 Bonded {
1138 stash: T::AccountId,
1139 amount: BalanceOf<T>,
1140 },
1141 Unbonded {
1143 stash: T::AccountId,
1144 amount: BalanceOf<T>,
1145 },
1146 Withdrawn {
1149 stash: T::AccountId,
1150 amount: BalanceOf<T>,
1151 },
1152 StakerRemoved {
1155 stash: T::AccountId,
1156 },
1157 Kicked {
1159 nominator: T::AccountId,
1160 stash: T::AccountId,
1161 },
1162 Chilled {
1164 stash: T::AccountId,
1165 },
1166 PayoutStarted {
1168 era_index: EraIndex,
1169 validator_stash: T::AccountId,
1170 page: Page,
1171 next: Option<Page>,
1172 },
1173 ValidatorPrefsSet {
1175 stash: T::AccountId,
1176 prefs: ValidatorPrefs,
1177 },
1178 SnapshotVotersSizeExceeded {
1180 size: u32,
1181 },
1182 SnapshotTargetsSizeExceeded {
1184 size: u32,
1185 },
1186 ForceEra {
1187 mode: Forcing,
1188 },
1189 ControllerBatchDeprecated {
1191 failures: u32,
1192 },
1193 CurrencyMigrated {
1196 stash: T::AccountId,
1197 force_withdraw: BalanceOf<T>,
1198 },
1199 PagedElectionProceeded {
1209 page: PageIndex,
1210 result: Result<u32, u32>,
1211 },
1212 OffenceReported {
1215 offence_era: EraIndex,
1216 validator: T::AccountId,
1217 fraction: Perbill,
1218 },
1219 SlashComputed {
1221 offence_era: EraIndex,
1222 slash_era: EraIndex,
1223 offender: T::AccountId,
1224 page: u32,
1225 },
1226 SlashCancelled {
1228 slash_era: EraIndex,
1229 validator: T::AccountId,
1230 },
1231 SessionRotated {
1236 starting_session: SessionIndex,
1237 active_era: EraIndex,
1238 planned_era: EraIndex,
1239 },
1240 Unexpected(UnexpectedKind),
1243 OffenceTooOld {
1245 offence_era: EraIndex,
1246 validator: T::AccountId,
1247 fraction: Perbill,
1248 },
1249 EraPruned {
1251 index: EraIndex,
1252 },
1253 }
1254
1255 #[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, TypeInfo, Debug)]
1261 pub enum UnexpectedKind {
1262 EraDurationBoundExceeded,
1264 UnknownValidatorActivation,
1266 PagedElectionOutOfWeight { page: PageIndex, required: Weight, had: Weight },
1268 }
1269
1270 #[pallet::error]
1271 #[derive(PartialEq)]
1272 pub enum Error<T> {
1273 NotController,
1275 NotStash,
1277 AlreadyBonded,
1279 AlreadyPaired,
1281 EmptyTargets,
1283 DuplicateIndex,
1285 InvalidSlashRecord,
1287 InsufficientBond,
1291 NoMoreChunks,
1293 NoUnlockChunk,
1295 FundedTarget,
1297 InvalidEraToReward,
1299 InvalidNumberOfNominations,
1301 AlreadyClaimed,
1303 InvalidPage,
1305 IncorrectHistoryDepth,
1307 BadState,
1309 TooManyTargets,
1311 BadTarget,
1313 CannotChillOther,
1315 TooManyNominators,
1318 TooManyValidators,
1321 CommissionTooLow,
1323 BoundNotMet,
1325 ControllerDeprecated,
1327 CannotRestoreLedger,
1329 RewardDestinationRestricted,
1331 NotEnoughFunds,
1333 VirtualStakerNotAllowed,
1335 CannotReapStash,
1337 AlreadyMigrated,
1339 EraNotStarted,
1341 Restricted,
1344 UnappliedSlashesInPreviousEra,
1347 EraNotPrunable,
1349 CancelledSlash,
1351 }
1352
1353 impl<T: Config> Pallet<T> {
1354 pub fn apply_unapplied_slashes(active_era: EraIndex) -> Weight {
1356 let mut slashes = UnappliedSlashes::<T>::iter_prefix(&active_era).take(1);
1357 if let Some((key, slash)) = slashes.next() {
1358 crate::log!(
1359 debug,
1360 "🦹 found slash {:?} scheduled to be executed in era {:?}",
1361 slash,
1362 active_era,
1363 );
1364
1365 let nominators_slashed = slash.others.len() as u32;
1366
1367 if Self::check_slash_cancelled(active_era, &key.0, key.1) {
1369 crate::log!(
1370 debug,
1371 "🦹 slash for {:?} in era {:?} was cancelled, skipping",
1372 key.0,
1373 active_era,
1374 );
1375 } else {
1376 let offence_era = active_era.saturating_sub(T::SlashDeferDuration::get());
1377 slashing::apply_slash::<T>(slash, offence_era);
1378 }
1379
1380 UnappliedSlashes::<T>::remove(&active_era, &key);
1382
1383 if UnappliedSlashes::<T>::iter_prefix(&active_era).next().is_none() {
1385 CancelledSlashes::<T>::remove(&active_era);
1387 }
1388
1389 T::WeightInfo::apply_slash(nominators_slashed)
1390 } else {
1391 T::DbWeight::get().reads(1)
1393 }
1394 }
1395
1396 fn do_prune_era_step(era: EraIndex) -> Result<Weight, DispatchError> {
1398 let current_step = EraPruningState::<T>::get(era).ok_or(Error::<T>::EraNotPrunable)?;
1403
1404 let items_limit = T::MaxPruningItems::get().min(T::MaxValidatorSet::get());
1407
1408 let actual_weight = match current_step {
1409 PruningStep::ErasStakersPaged => {
1410 let result = ErasStakersPaged::<T>::clear_prefix((era,), items_limit, None);
1411 let items_deleted = result.backend as u32;
1412 result.maybe_cursor.is_none().then(|| {
1413 EraPruningState::<T>::insert(era, PruningStep::ErasStakersOverview)
1414 });
1415 T::WeightInfo::prune_era_stakers_paged(items_deleted)
1416 },
1417 PruningStep::ErasStakersOverview => {
1418 let result = ErasStakersOverview::<T>::clear_prefix(era, items_limit, None);
1419 let items_deleted = result.backend as u32;
1420 result.maybe_cursor.is_none().then(|| {
1421 EraPruningState::<T>::insert(era, PruningStep::ErasValidatorPrefs)
1422 });
1423 T::WeightInfo::prune_era_stakers_overview(items_deleted)
1424 },
1425 PruningStep::ErasValidatorPrefs => {
1426 let result = ErasValidatorPrefs::<T>::clear_prefix(era, items_limit, None);
1427 let items_deleted = result.backend as u32;
1428 result
1429 .maybe_cursor
1430 .is_none()
1431 .then(|| EraPruningState::<T>::insert(era, PruningStep::ClaimedRewards));
1432 T::WeightInfo::prune_era_validator_prefs(items_deleted)
1433 },
1434 PruningStep::ClaimedRewards => {
1435 let result = ClaimedRewards::<T>::clear_prefix(era, items_limit, None);
1436 let items_deleted = result.backend as u32;
1437 result.maybe_cursor.is_none().then(|| {
1438 EraPruningState::<T>::insert(era, PruningStep::ErasValidatorReward)
1439 });
1440 T::WeightInfo::prune_era_claimed_rewards(items_deleted)
1441 },
1442 PruningStep::ErasValidatorReward => {
1443 ErasValidatorReward::<T>::remove(era);
1444 EraPruningState::<T>::insert(era, PruningStep::ErasRewardPoints);
1445 T::WeightInfo::prune_era_validator_reward()
1446 },
1447 PruningStep::ErasRewardPoints => {
1448 ErasRewardPoints::<T>::remove(era);
1449 EraPruningState::<T>::insert(era, PruningStep::SingleEntryCleanups);
1450 T::WeightInfo::prune_era_reward_points()
1451 },
1452 PruningStep::SingleEntryCleanups => {
1453 ErasTotalStake::<T>::remove(era);
1454 ErasNominatorsSlashable::<T>::remove(era);
1456 EraPruningState::<T>::insert(era, PruningStep::ValidatorSlashInEra);
1457 T::WeightInfo::prune_era_single_entry_cleanups()
1458 },
1459 PruningStep::ValidatorSlashInEra => {
1460 let result = ValidatorSlashInEra::<T>::clear_prefix(era, items_limit, None);
1462 let items_deleted = result.backend as u32;
1463
1464 if result.maybe_cursor.is_none() {
1466 EraPruningState::<T>::remove(era);
1467 }
1468
1469 T::WeightInfo::prune_era_validator_slash_in_era(items_deleted)
1470 },
1471 };
1472
1473 if EraPruningState::<T>::get(era).is_none() {
1475 Self::deposit_event(Event::<T>::EraPruned { index: era });
1476 }
1477
1478 Ok(actual_weight)
1479 }
1480 }
1481
1482 #[pallet::hooks]
1483 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
1484 fn on_poll(_now: BlockNumberFor<T>, weight_meter: &mut WeightMeter) {
1485 let (weight, exec) = EraElectionPlanner::<T>::maybe_fetch_election_results();
1486 crate::log!(
1487 trace,
1488 "weight of fetching next election page is {:?}, have {:?}",
1489 weight,
1490 weight_meter.remaining()
1491 );
1492
1493 if weight_meter.can_consume(weight) {
1494 exec(weight_meter);
1495 } else {
1496 Self::deposit_event(Event::<T>::Unexpected(
1497 UnexpectedKind::PagedElectionOutOfWeight {
1498 page: NextElectionPage::<T>::get().unwrap_or(
1499 EraElectionPlanner::<T>::election_pages().defensive_saturating_sub(1),
1500 ),
1501 required: weight,
1502 had: weight_meter.remaining(),
1503 },
1504 ));
1505 }
1506 }
1507
1508 fn on_initialize(_now: BlockNumberFor<T>) -> Weight {
1509 let mut consumed_weight = slashing::process_offence_for_era::<T>();
1511
1512 consumed_weight.saturating_accrue(T::DbWeight::get().reads(1));
1514 if let Some(active_era) = ActiveEra::<T>::get() {
1515 let slash_weight = Self::apply_unapplied_slashes(active_era.index);
1516 consumed_weight.saturating_accrue(slash_weight);
1517 }
1518
1519 consumed_weight
1520 }
1521
1522 fn integrity_test() {
1523 assert_eq!(
1525 MaxNominationsOf::<T>::get(),
1526 <Self as ElectionDataProvider>::MaxVotesPerVoter::get()
1527 );
1528
1529 assert!(!MaxNominationsOf::<T>::get().is_zero());
1531
1532 assert!(
1533 T::SlashDeferDuration::get() < T::BondingDuration::get() || T::BondingDuration::get() == 0,
1534 "As per documentation, slash defer duration ({}) should be less than bonding duration ({}).",
1535 T::SlashDeferDuration::get(),
1536 T::BondingDuration::get(),
1537 );
1538
1539 assert!(
1541 T::NominatorFastUnbondDuration::get() <= T::BondingDuration::get(),
1542 "NominatorFastUnbondDuration ({}) must not exceed BondingDuration ({}).",
1543 T::NominatorFastUnbondDuration::get(),
1544 T::BondingDuration::get(),
1545 );
1546 assert!(
1548 T::MaxPruningItems::get() >= 100,
1549 "MaxPruningItems must be at least 100 for efficient pruning, got: {}",
1550 T::MaxPruningItems::get()
1551 );
1552 }
1553
1554 #[cfg(feature = "try-runtime")]
1555 fn try_state(n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
1556 Self::do_try_state(n)
1557 }
1558 }
1559
1560 #[pallet::call]
1561 impl<T: Config> Pallet<T> {
1562 #[pallet::call_index(0)]
1575 #[pallet::weight(T::WeightInfo::bond())]
1576 pub fn bond(
1577 origin: OriginFor<T>,
1578 #[pallet::compact] value: BalanceOf<T>,
1579 payee: RewardDestination<T::AccountId>,
1580 ) -> DispatchResult {
1581 let stash = ensure_signed(origin)?;
1582
1583 ensure!(!T::Filter::contains(&stash), Error::<T>::Restricted);
1584
1585 if StakingLedger::<T>::is_bonded(StakingAccount::Stash(stash.clone())) {
1586 return Err(Error::<T>::AlreadyBonded.into());
1587 }
1588
1589 if StakingLedger::<T>::is_bonded(StakingAccount::Controller(stash.clone())) {
1591 return Err(Error::<T>::AlreadyPaired.into());
1592 }
1593
1594 if value < Self::min_chilled_bond() {
1596 return Err(Error::<T>::InsufficientBond.into());
1597 }
1598
1599 let stash_balance = asset::free_to_stake::<T>(&stash);
1600 let value = value.min(stash_balance);
1601 Self::deposit_event(Event::<T>::Bonded { stash: stash.clone(), amount: value });
1602 let ledger = StakingLedger::<T>::new(stash.clone(), value);
1603
1604 ledger.bond(payee)?;
1607
1608 Ok(())
1609 }
1610
1611 #[pallet::call_index(1)]
1622 #[pallet::weight(T::WeightInfo::bond_extra())]
1623 pub fn bond_extra(
1624 origin: OriginFor<T>,
1625 #[pallet::compact] max_additional: BalanceOf<T>,
1626 ) -> DispatchResult {
1627 let stash = ensure_signed(origin)?;
1628 ensure!(!T::Filter::contains(&stash), Error::<T>::Restricted);
1629 Self::do_bond_extra(&stash, max_additional)
1630 }
1631
1632 #[pallet::call_index(2)]
1652 #[pallet::weight(
1653 T::WeightInfo::withdraw_unbonded_kill().saturating_add(T::WeightInfo::unbond()))
1654 ]
1655 pub fn unbond(
1656 origin: OriginFor<T>,
1657 #[pallet::compact] value: BalanceOf<T>,
1658 ) -> DispatchResultWithPostInfo {
1659 let controller = ensure_signed(origin)?;
1660 let unlocking =
1661 Self::ledger(Controller(controller.clone())).map(|l| l.unlocking.len())?;
1662
1663 let maybe_withdraw_weight = {
1666 if unlocking == T::MaxUnlockingChunks::get() as usize {
1667 Some(Self::do_withdraw_unbonded(&controller)?)
1668 } else {
1669 None
1670 }
1671 };
1672
1673 let mut ledger = Self::ledger(Controller(controller))?;
1676 let mut value = value.min(ledger.active);
1677 let stash = ledger.stash.clone();
1678
1679 let chill_weight = if value >= ledger.active {
1682 Self::chill_stash(&stash);
1683 T::WeightInfo::chill()
1684 } else {
1685 Weight::zero()
1686 };
1687
1688 ensure!(
1689 ledger.unlocking.len() < T::MaxUnlockingChunks::get() as usize,
1690 Error::<T>::NoMoreChunks,
1691 );
1692
1693 if !value.is_zero() {
1694 ledger.active -= value;
1695
1696 if ledger.active < asset::existential_deposit::<T>() {
1698 value += ledger.active;
1699 ledger.active = Zero::zero();
1700 }
1701
1702 let is_nominator = Nominators::<T>::contains_key(&stash);
1703
1704 let min_active_bond = if is_nominator {
1705 Self::min_nominator_bond()
1706 } else if Validators::<T>::contains_key(&stash) {
1707 Self::min_validator_bond()
1708 } else {
1709 Zero::zero()
1711 };
1712
1713 ensure!(ledger.active >= min_active_bond, Error::<T>::InsufficientBond);
1716
1717 let active_era = session_rotation::Rotator::<T>::active_era();
1723 let was_recent_validator = LastValidatorEra::<T>::get(&stash)
1724 .map(|last_era| active_era.saturating_sub(last_era) < T::BondingDuration::get())
1725 .unwrap_or(false);
1726
1727 let unbond_duration = if was_recent_validator {
1728 T::BondingDuration::get()
1730 } else {
1731 <Self as sp_staking::StakingInterface>::nominator_bonding_duration()
1733 };
1734
1735 let era =
1736 session_rotation::Rotator::<T>::active_era().saturating_add(unbond_duration);
1737 if let Some(chunk) = ledger.unlocking.last_mut().filter(|chunk| chunk.era == era) {
1738 chunk.value = chunk.value.defensive_saturating_add(value)
1742 } else {
1743 ledger
1744 .unlocking
1745 .try_push(UnlockChunk { value, era })
1746 .map_err(|_| Error::<T>::NoMoreChunks)?;
1747 };
1748 ledger.update()?;
1750
1751 if T::VoterList::contains(&stash) {
1753 let _ = T::VoterList::on_update(&stash, Self::weight_of(&stash));
1754 }
1755
1756 Self::deposit_event(Event::<T>::Unbonded { stash, amount: value });
1757 }
1758
1759 let actual_weight = if let Some(withdraw_weight) = maybe_withdraw_weight {
1760 Some(
1761 T::WeightInfo::unbond()
1762 .saturating_add(withdraw_weight)
1763 .saturating_add(chill_weight),
1764 )
1765 } else {
1766 Some(T::WeightInfo::unbond().saturating_add(chill_weight))
1767 };
1768
1769 Ok(actual_weight.into())
1770 }
1771
1772 #[pallet::call_index(3)]
1792 #[pallet::weight(T::WeightInfo::withdraw_unbonded_kill())]
1793 pub fn withdraw_unbonded(
1794 origin: OriginFor<T>,
1795 _num_slashing_spans: u32,
1796 ) -> DispatchResultWithPostInfo {
1797 let controller = ensure_signed(origin)?;
1798
1799 let actual_weight = Self::do_withdraw_unbonded(&controller)?;
1800 Ok(Some(actual_weight).into())
1801 }
1802
1803 #[pallet::call_index(4)]
1809 #[pallet::weight(T::WeightInfo::validate())]
1810 pub fn validate(origin: OriginFor<T>, prefs: ValidatorPrefs) -> DispatchResult {
1811 let controller = ensure_signed(origin)?;
1812
1813 let ledger = Self::ledger(Controller(controller))?;
1814
1815 ensure!(ledger.active >= Self::min_validator_bond(), Error::<T>::InsufficientBond);
1816 let stash = &ledger.stash;
1817
1818 ensure!(prefs.commission >= MinCommission::<T>::get(), Error::<T>::CommissionTooLow);
1820
1821 if !Validators::<T>::contains_key(stash) {
1823 if let Some(max_validators) = MaxValidatorsCount::<T>::get() {
1827 ensure!(
1828 Validators::<T>::count() < max_validators,
1829 Error::<T>::TooManyValidators
1830 );
1831 }
1832 }
1833
1834 Self::do_remove_nominator(stash);
1835 Self::do_add_validator(stash, prefs.clone());
1836 Self::deposit_event(Event::<T>::ValidatorPrefsSet { stash: ledger.stash, prefs });
1837
1838 Ok(())
1839 }
1840
1841 #[pallet::call_index(5)]
1847 #[pallet::weight(T::WeightInfo::nominate(targets.len() as u32))]
1848 pub fn nominate(
1849 origin: OriginFor<T>,
1850 targets: Vec<AccountIdLookupOf<T>>,
1851 ) -> DispatchResult {
1852 let controller = ensure_signed(origin)?;
1853
1854 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()))?;
1855
1856 ensure!(ledger.active >= Self::min_nominator_bond(), Error::<T>::InsufficientBond);
1857 let stash = &ledger.stash;
1858
1859 if !Nominators::<T>::contains_key(stash) {
1861 if let Some(max_nominators) = MaxNominatorsCount::<T>::get() {
1865 ensure!(
1866 Nominators::<T>::count() < max_nominators,
1867 Error::<T>::TooManyNominators
1868 );
1869 }
1870 }
1871
1872 let mut targets = targets
1874 .into_iter()
1875 .map(|t| T::Lookup::lookup(t).map_err(DispatchError::from))
1876 .collect::<Result<Vec<_>, _>>()?;
1877 targets.sort();
1878 targets.dedup();
1879
1880 ensure!(!targets.is_empty(), Error::<T>::EmptyTargets);
1881 ensure!(
1882 targets.len() <= T::NominationsQuota::get_quota(ledger.active) as usize,
1883 Error::<T>::TooManyTargets
1884 );
1885
1886 let old = Nominators::<T>::get(stash).map_or_else(Vec::new, |x| x.targets.into_inner());
1887
1888 let targets: BoundedVec<_, _> = targets
1889 .into_iter()
1890 .map(|n| {
1891 if old.contains(&n) ||
1892 (Validators::<T>::contains_key(&n) && !Validators::<T>::get(&n).blocked)
1893 {
1894 Ok(n)
1895 } else {
1896 Err(Error::<T>::BadTarget.into())
1897 }
1898 })
1899 .collect::<Result<Vec<_>, DispatchError>>()?
1900 .try_into()
1901 .map_err(|_| Error::<T>::TooManyNominators)?;
1902
1903 let nominations = Nominations {
1904 targets,
1905 submitted_in: CurrentEra::<T>::get().unwrap_or(0),
1907 suppressed: false,
1908 };
1909
1910 Self::do_remove_validator(stash);
1911 Self::do_add_nominator(stash, nominations);
1912 Ok(())
1913 }
1914
1915 #[pallet::call_index(6)]
1926 #[pallet::weight(T::WeightInfo::chill())]
1927 pub fn chill(origin: OriginFor<T>) -> DispatchResult {
1928 let controller = ensure_signed(origin)?;
1929
1930 let ledger = Self::ledger(StakingAccount::Controller(controller))?;
1931
1932 Self::chill_stash(&ledger.stash);
1933 Ok(())
1934 }
1935
1936 #[pallet::call_index(7)]
1942 #[pallet::weight(T::WeightInfo::set_payee())]
1943 pub fn set_payee(
1944 origin: OriginFor<T>,
1945 payee: RewardDestination<T::AccountId>,
1946 ) -> DispatchResult {
1947 let controller = ensure_signed(origin)?;
1948 let ledger = Self::ledger(Controller(controller.clone()))?;
1949
1950 ensure!(
1951 (payee != {
1952 #[allow(deprecated)]
1953 RewardDestination::Controller
1954 }),
1955 Error::<T>::ControllerDeprecated
1956 );
1957
1958 let _ = ledger
1959 .set_payee(payee)
1960 .defensive_proof("ledger was retrieved from storage, thus it's bonded; qed.")?;
1961
1962 Ok(())
1963 }
1964
1965 #[pallet::call_index(8)]
1974 #[pallet::weight(T::WeightInfo::set_controller())]
1975 pub fn set_controller(origin: OriginFor<T>) -> DispatchResult {
1976 let stash = ensure_signed(origin)?;
1977
1978 Self::ledger(StakingAccount::Stash(stash.clone())).map(|ledger| {
1979 let controller = ledger.controller()
1980 .defensive_proof("Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.")
1981 .ok_or(Error::<T>::NotController)?;
1982
1983 if controller == stash {
1984 return Err(Error::<T>::AlreadyPaired.into())
1986 }
1987
1988 let _ = ledger.set_controller_to_stash()?;
1989 Ok(())
1990 })?
1991 }
1992
1993 #[pallet::call_index(9)]
1997 #[pallet::weight(T::WeightInfo::set_validator_count())]
1998 pub fn set_validator_count(
1999 origin: OriginFor<T>,
2000 #[pallet::compact] new: u32,
2001 ) -> DispatchResult {
2002 ensure_root(origin)?;
2003
2004 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
2005
2006 ValidatorCount::<T>::put(new);
2007 Ok(())
2008 }
2009
2010 #[pallet::call_index(10)]
2015 #[pallet::weight(T::WeightInfo::set_validator_count())]
2016 pub fn increase_validator_count(
2017 origin: OriginFor<T>,
2018 #[pallet::compact] additional: u32,
2019 ) -> DispatchResult {
2020 ensure_root(origin)?;
2021 let old = ValidatorCount::<T>::get();
2022 let new = old.checked_add(additional).ok_or(ArithmeticError::Overflow)?;
2023
2024 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
2025
2026 ValidatorCount::<T>::put(new);
2027 Ok(())
2028 }
2029
2030 #[pallet::call_index(11)]
2035 #[pallet::weight(T::WeightInfo::set_validator_count())]
2036 pub fn scale_validator_count(origin: OriginFor<T>, factor: Percent) -> DispatchResult {
2037 ensure_root(origin)?;
2038 let old = ValidatorCount::<T>::get();
2039 let new = old.checked_add(factor.mul_floor(old)).ok_or(ArithmeticError::Overflow)?;
2040
2041 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
2042
2043 ValidatorCount::<T>::put(new);
2044 Ok(())
2045 }
2046
2047 #[pallet::call_index(12)]
2057 #[pallet::weight(T::WeightInfo::force_no_eras())]
2058 pub fn force_no_eras(origin: OriginFor<T>) -> DispatchResult {
2059 ensure_root(origin)?;
2060 Self::set_force_era(Forcing::ForceNone);
2061 Ok(())
2062 }
2063
2064 #[pallet::call_index(13)]
2075 #[pallet::weight(T::WeightInfo::force_new_era())]
2076 pub fn force_new_era(origin: OriginFor<T>) -> DispatchResult {
2077 ensure_root(origin)?;
2078 Self::set_force_era(Forcing::ForceNew);
2079 Ok(())
2080 }
2081
2082 #[pallet::call_index(15)]
2091 #[pallet::weight(T::WeightInfo::force_unstake())]
2092 pub fn force_unstake(
2093 origin: OriginFor<T>,
2094 stash: T::AccountId,
2095 _num_slashing_spans: u32,
2096 ) -> DispatchResult {
2097 ensure_root(origin)?;
2098
2099 Self::kill_stash(&stash)?;
2101
2102 Ok(())
2103 }
2104
2105 #[pallet::call_index(16)]
2115 #[pallet::weight(T::WeightInfo::force_new_era_always())]
2116 pub fn force_new_era_always(origin: OriginFor<T>) -> DispatchResult {
2117 ensure_root(origin)?;
2118 Self::set_force_era(Forcing::ForceAlways);
2119 Ok(())
2120 }
2121
2122 #[pallet::call_index(17)]
2134 #[pallet::weight(T::WeightInfo::cancel_deferred_slash(validator_slashes.len() as u32))]
2135 pub fn cancel_deferred_slash(
2136 origin: OriginFor<T>,
2137 era: EraIndex,
2138 validator_slashes: Vec<(T::AccountId, Perbill)>,
2139 ) -> DispatchResult {
2140 T::AdminOrigin::ensure_origin(origin)?;
2141 ensure!(!validator_slashes.is_empty(), Error::<T>::EmptyTargets);
2142
2143 let mut cancelled_slashes = CancelledSlashes::<T>::get(&era);
2145
2146 for (validator, slash_fraction) in validator_slashes {
2148 cancelled_slashes.retain(|(v, _)| v != &validator);
2153
2154 cancelled_slashes
2156 .try_push((validator.clone(), slash_fraction))
2157 .map_err(|_| Error::<T>::BoundNotMet)
2158 .defensive_proof("cancelled_slashes should have capacity for all validators")?;
2159
2160 Self::deposit_event(Event::<T>::SlashCancelled { slash_era: era, validator });
2161 }
2162
2163 CancelledSlashes::<T>::insert(&era, cancelled_slashes);
2165
2166 Ok(())
2167 }
2168
2169 #[pallet::call_index(18)]
2183 #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked(T::MaxExposurePageSize::get()))]
2184 pub fn payout_stakers(
2185 origin: OriginFor<T>,
2186 validator_stash: T::AccountId,
2187 era: EraIndex,
2188 ) -> DispatchResultWithPostInfo {
2189 ensure_signed(origin)?;
2190
2191 Self::do_payout_stakers(validator_stash, era)
2192 }
2193
2194 #[pallet::call_index(19)]
2198 #[pallet::weight(T::WeightInfo::rebond(T::MaxUnlockingChunks::get() as u32))]
2199 pub fn rebond(
2200 origin: OriginFor<T>,
2201 #[pallet::compact] value: BalanceOf<T>,
2202 ) -> DispatchResultWithPostInfo {
2203 let controller = ensure_signed(origin)?;
2204 let ledger = Self::ledger(Controller(controller))?;
2205
2206 ensure!(!T::Filter::contains(&ledger.stash), Error::<T>::Restricted);
2207 ensure!(!ledger.unlocking.is_empty(), Error::<T>::NoUnlockChunk);
2208
2209 let initial_unlocking = ledger.unlocking.len() as u32;
2210 let (ledger, rebonded_value) = ledger.rebond(value);
2211 ensure!(ledger.active >= Self::min_chilled_bond(), Error::<T>::InsufficientBond);
2213
2214 Self::deposit_event(Event::<T>::Bonded {
2215 stash: ledger.stash.clone(),
2216 amount: rebonded_value,
2217 });
2218
2219 let stash = ledger.stash.clone();
2220 let final_unlocking = ledger.unlocking.len();
2221
2222 ledger.update()?;
2224 if T::VoterList::contains(&stash) {
2225 let _ = T::VoterList::on_update(&stash, Self::weight_of(&stash));
2226 }
2227
2228 let removed_chunks = 1u32 .saturating_add(initial_unlocking)
2230 .saturating_sub(final_unlocking as u32);
2231 Ok(Some(T::WeightInfo::rebond(removed_chunks)).into())
2232 }
2233
2234 #[pallet::call_index(20)]
2253 #[pallet::weight(T::WeightInfo::reap_stash())]
2254 pub fn reap_stash(
2255 origin: OriginFor<T>,
2256 stash: T::AccountId,
2257 _num_slashing_spans: u32,
2258 ) -> DispatchResultWithPostInfo {
2259 let _ = ensure_signed(origin)?;
2260
2261 ensure!(!Self::is_virtual_staker(&stash), Error::<T>::VirtualStakerNotAllowed);
2263
2264 let min_chilled_bond = Self::min_chilled_bond();
2265 let origin_balance = asset::total_balance::<T>(&stash);
2266 let ledger_total =
2267 Self::ledger(Stash(stash.clone())).map(|l| l.total).unwrap_or_default();
2268 let reapable = origin_balance < min_chilled_bond ||
2269 origin_balance.is_zero() ||
2270 ledger_total < min_chilled_bond ||
2271 ledger_total.is_zero();
2272 ensure!(reapable, Error::<T>::FundedTarget);
2273
2274 Self::kill_stash(&stash)?;
2276
2277 Ok(Pays::No.into())
2278 }
2279
2280 #[pallet::call_index(21)]
2292 #[pallet::weight(T::WeightInfo::kick(who.len() as u32))]
2293 pub fn kick(origin: OriginFor<T>, who: Vec<AccountIdLookupOf<T>>) -> DispatchResult {
2294 let controller = ensure_signed(origin)?;
2295 let ledger = Self::ledger(Controller(controller))?;
2296 let stash = &ledger.stash;
2297
2298 for nom_stash in who
2299 .into_iter()
2300 .map(T::Lookup::lookup)
2301 .collect::<Result<Vec<T::AccountId>, _>>()?
2302 .into_iter()
2303 {
2304 Nominators::<T>::mutate(&nom_stash, |maybe_nom| {
2305 if let Some(ref mut nom) = maybe_nom {
2306 if let Some(pos) = nom.targets.iter().position(|v| v == stash) {
2307 nom.targets.swap_remove(pos);
2308 Self::deposit_event(Event::<T>::Kicked {
2309 nominator: nom_stash.clone(),
2310 stash: stash.clone(),
2311 });
2312 }
2313 }
2314 });
2315 }
2316
2317 Ok(())
2318 }
2319
2320 #[pallet::call_index(22)]
2340 #[pallet::weight(
2341 T::WeightInfo::set_staking_configs_all_set()
2342 .max(T::WeightInfo::set_staking_configs_all_remove())
2343 )]
2344 pub fn set_staking_configs(
2345 origin: OriginFor<T>,
2346 min_nominator_bond: ConfigOp<BalanceOf<T>>,
2347 min_validator_bond: ConfigOp<BalanceOf<T>>,
2348 max_nominator_count: ConfigOp<u32>,
2349 max_validator_count: ConfigOp<u32>,
2350 chill_threshold: ConfigOp<Percent>,
2351 min_commission: ConfigOp<Perbill>,
2352 max_staked_rewards: ConfigOp<Percent>,
2353 are_nominators_slashable: ConfigOp<bool>,
2354 ) -> DispatchResult {
2355 ensure_root(origin)?;
2356
2357 macro_rules! config_op_exp {
2358 ($storage:ty, $op:ident) => {
2359 match $op {
2360 ConfigOp::Noop => (),
2361 ConfigOp::Set(v) => <$storage>::put(v),
2362 ConfigOp::Remove => <$storage>::kill(),
2363 }
2364 };
2365 }
2366
2367 config_op_exp!(MinNominatorBond<T>, min_nominator_bond);
2368 config_op_exp!(MinValidatorBond<T>, min_validator_bond);
2369 config_op_exp!(MaxNominatorsCount<T>, max_nominator_count);
2370 config_op_exp!(MaxValidatorsCount<T>, max_validator_count);
2371 config_op_exp!(ChillThreshold<T>, chill_threshold);
2372 config_op_exp!(MinCommission<T>, min_commission);
2373 config_op_exp!(MaxStakedRewards<T>, max_staked_rewards);
2374 config_op_exp!(AreNominatorsSlashable<T>, are_nominators_slashable);
2375 Ok(())
2376 }
2377 #[pallet::call_index(23)]
2404 #[pallet::weight(T::WeightInfo::chill_other())]
2405 pub fn chill_other(origin: OriginFor<T>, stash: T::AccountId) -> DispatchResult {
2406 let caller = ensure_signed(origin)?;
2408 let ledger = Self::ledger(Stash(stash.clone()))?;
2409 let controller = ledger
2410 .controller()
2411 .defensive_proof(
2412 "Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.",
2413 )
2414 .ok_or(Error::<T>::NotController)?;
2415
2416 if Nominators::<T>::contains_key(&stash) && Nominators::<T>::get(&stash).is_none() {
2433 Self::chill_stash(&stash);
2434 return Ok(());
2435 }
2436
2437 if caller != controller {
2438 let threshold = ChillThreshold::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2439 let min_active_bond = if Nominators::<T>::contains_key(&stash) {
2440 let max_nominator_count =
2441 MaxNominatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2442 let current_nominator_count = Nominators::<T>::count();
2443 ensure!(
2444 threshold * max_nominator_count < current_nominator_count,
2445 Error::<T>::CannotChillOther
2446 );
2447 Self::min_nominator_bond()
2448 } else if Validators::<T>::contains_key(&stash) {
2449 let max_validator_count =
2450 MaxValidatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2451 let current_validator_count = Validators::<T>::count();
2452 ensure!(
2453 threshold * max_validator_count < current_validator_count,
2454 Error::<T>::CannotChillOther
2455 );
2456 Self::min_validator_bond()
2457 } else {
2458 Zero::zero()
2459 };
2460
2461 ensure!(ledger.active < min_active_bond, Error::<T>::CannotChillOther);
2462 }
2463
2464 Self::chill_stash(&stash);
2465 Ok(())
2466 }
2467
2468 #[pallet::call_index(24)]
2472 #[pallet::weight(T::WeightInfo::force_apply_min_commission())]
2473 pub fn force_apply_min_commission(
2474 origin: OriginFor<T>,
2475 validator_stash: T::AccountId,
2476 ) -> DispatchResult {
2477 ensure_signed(origin)?;
2478 let min_commission = MinCommission::<T>::get();
2479 Validators::<T>::try_mutate_exists(validator_stash, |maybe_prefs| {
2480 maybe_prefs
2481 .as_mut()
2482 .map(|prefs| {
2483 (prefs.commission < min_commission)
2484 .then(|| prefs.commission = min_commission)
2485 })
2486 .ok_or(Error::<T>::NotStash)
2487 })?;
2488 Ok(())
2489 }
2490
2491 #[pallet::call_index(25)]
2496 #[pallet::weight(T::WeightInfo::set_min_commission())]
2497 pub fn set_min_commission(origin: OriginFor<T>, new: Perbill) -> DispatchResult {
2498 T::AdminOrigin::ensure_origin(origin)?;
2499 MinCommission::<T>::put(new);
2500 Ok(())
2501 }
2502
2503 #[pallet::call_index(26)]
2521 #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked(T::MaxExposurePageSize::get()))]
2522 pub fn payout_stakers_by_page(
2523 origin: OriginFor<T>,
2524 validator_stash: T::AccountId,
2525 era: EraIndex,
2526 page: Page,
2527 ) -> DispatchResultWithPostInfo {
2528 ensure_signed(origin)?;
2529 Self::do_payout_stakers_by_page(validator_stash, era, page)
2530 }
2531
2532 #[pallet::call_index(27)]
2539 #[pallet::weight(T::WeightInfo::update_payee())]
2540 pub fn update_payee(
2541 origin: OriginFor<T>,
2542 controller: T::AccountId,
2543 ) -> DispatchResultWithPostInfo {
2544 let _ = ensure_signed(origin)?;
2545 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()))?;
2546
2547 ensure!(
2548 (Payee::<T>::get(&ledger.stash) == {
2549 #[allow(deprecated)]
2550 Some(RewardDestination::Controller)
2551 }),
2552 Error::<T>::NotController
2553 );
2554
2555 let _ = ledger
2556 .set_payee(RewardDestination::Account(controller))
2557 .defensive_proof("ledger should have been previously retrieved from storage.")?;
2558
2559 Ok(Pays::No.into())
2560 }
2561
2562 #[pallet::call_index(28)]
2570 #[pallet::weight(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32))]
2571 pub fn deprecate_controller_batch(
2572 origin: OriginFor<T>,
2573 controllers: BoundedVec<T::AccountId, T::MaxControllersInDeprecationBatch>,
2574 ) -> DispatchResultWithPostInfo {
2575 T::AdminOrigin::ensure_origin(origin)?;
2576
2577 let filtered_batch_with_ledger: Vec<_> = controllers
2579 .iter()
2580 .filter_map(|controller| {
2581 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()));
2582 ledger.ok().map_or(None, |ledger| {
2583 let payee_deprecated = Payee::<T>::get(&ledger.stash) == {
2586 #[allow(deprecated)]
2587 Some(RewardDestination::Controller)
2588 };
2589
2590 if ledger.stash != *controller && !payee_deprecated {
2591 Some(ledger)
2592 } else {
2593 None
2594 }
2595 })
2596 })
2597 .collect();
2598
2599 let mut failures = 0;
2601 for ledger in filtered_batch_with_ledger {
2602 let _ = ledger.clone().set_controller_to_stash().map_err(|_| failures += 1);
2603 }
2604 Self::deposit_event(Event::<T>::ControllerBatchDeprecated { failures });
2605
2606 Ok(Some(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32)).into())
2607 }
2608
2609 #[pallet::call_index(29)]
2621 #[pallet::weight(T::WeightInfo::restore_ledger())]
2622 pub fn restore_ledger(
2623 origin: OriginFor<T>,
2624 stash: T::AccountId,
2625 maybe_controller: Option<T::AccountId>,
2626 maybe_total: Option<BalanceOf<T>>,
2627 maybe_unlocking: Option<BoundedVec<UnlockChunk<BalanceOf<T>>, T::MaxUnlockingChunks>>,
2628 ) -> DispatchResult {
2629 T::AdminOrigin::ensure_origin(origin)?;
2630
2631 ensure!(!Self::is_virtual_staker(&stash), Error::<T>::VirtualStakerNotAllowed);
2633
2634 let current_lock = asset::staked::<T>(&stash);
2635 let stash_balance = asset::stakeable_balance::<T>(&stash);
2636
2637 let (new_controller, new_total) = match Self::inspect_bond_state(&stash) {
2638 Ok(LedgerIntegrityState::Corrupted) => {
2639 let new_controller = maybe_controller.unwrap_or(stash.clone());
2640
2641 let new_total = if let Some(total) = maybe_total {
2642 let new_total = total.min(stash_balance);
2643 asset::update_stake::<T>(&stash, new_total)?;
2645 new_total
2646 } else {
2647 current_lock
2648 };
2649
2650 Ok((new_controller, new_total))
2651 },
2652 Ok(LedgerIntegrityState::CorruptedKilled) => {
2653 if current_lock == Zero::zero() {
2654 ensure!(maybe_total.is_some(), Error::<T>::CannotRestoreLedger);
2658 Ok((
2659 stash.clone(),
2660 maybe_total.expect("total exists as per the check above; qed."),
2661 ))
2662 } else {
2663 Ok((stash.clone(), current_lock))
2664 }
2665 },
2666 Ok(LedgerIntegrityState::LockCorrupted) => {
2667 let new_total =
2670 maybe_total.ok_or(Error::<T>::CannotRestoreLedger)?.min(stash_balance);
2671 asset::update_stake::<T>(&stash, new_total)?;
2672
2673 Ok((stash.clone(), new_total))
2674 },
2675 Err(Error::<T>::BadState) => {
2676 asset::kill_stake::<T>(&stash)?;
2678 ensure!(
2679 Self::inspect_bond_state(&stash) == Err(Error::<T>::NotStash),
2680 Error::<T>::BadState
2681 );
2682
2683 return Ok(());
2684 },
2685 Ok(LedgerIntegrityState::Ok) | Err(_) => Err(Error::<T>::CannotRestoreLedger),
2686 }?;
2687
2688 Bonded::<T>::insert(&stash, &new_controller);
2690
2691 let mut ledger = StakingLedger::<T>::new(stash.clone(), new_total);
2693 ledger.controller = Some(new_controller);
2694 ledger.unlocking = maybe_unlocking.unwrap_or_default();
2695 ledger.update()?;
2696
2697 ensure!(
2698 Self::inspect_bond_state(&stash) == Ok(LedgerIntegrityState::Ok),
2699 Error::<T>::BadState
2700 );
2701 Ok(())
2702 }
2703
2704 #[pallet::call_index(30)]
2712 #[pallet::weight(T::WeightInfo::migrate_currency())]
2713 pub fn migrate_currency(
2714 origin: OriginFor<T>,
2715 stash: T::AccountId,
2716 ) -> DispatchResultWithPostInfo {
2717 let _ = ensure_signed(origin)?;
2718 Self::do_migrate_currency(&stash)?;
2719
2720 Ok(Pays::No.into())
2722 }
2723
2724 #[pallet::call_index(31)]
2758 #[pallet::weight(T::WeightInfo::apply_slash(T::MaxExposurePageSize::get()))]
2759 pub fn apply_slash(
2760 origin: OriginFor<T>,
2761 slash_era: EraIndex,
2762 slash_key: (T::AccountId, Perbill, u32),
2763 ) -> DispatchResultWithPostInfo {
2764 let _ = ensure_signed(origin)?;
2765 let active_era = ActiveEra::<T>::get().map(|a| a.index).unwrap_or_default();
2766 ensure!(slash_era <= active_era, Error::<T>::EraNotStarted);
2767
2768 ensure!(
2770 !Self::check_slash_cancelled(slash_era, &slash_key.0, slash_key.1),
2771 Error::<T>::CancelledSlash
2772 );
2773
2774 let unapplied_slash = UnappliedSlashes::<T>::take(&slash_era, &slash_key)
2775 .ok_or(Error::<T>::InvalidSlashRecord)?;
2776 slashing::apply_slash::<T>(unapplied_slash, slash_era);
2777
2778 Ok(Pays::No.into())
2779 }
2780
2781 #[pallet::call_index(32)]
2793 #[pallet::weight({
2795 let v = T::MaxValidatorSet::get();
2796 T::WeightInfo::prune_era_stakers_paged(v)
2797 .max(T::WeightInfo::prune_era_stakers_overview(v))
2798 .max(T::WeightInfo::prune_era_validator_prefs(v))
2799 .max(T::WeightInfo::prune_era_claimed_rewards(v))
2800 .max(T::WeightInfo::prune_era_validator_reward())
2801 .max(T::WeightInfo::prune_era_reward_points())
2802 .max(T::WeightInfo::prune_era_single_entry_cleanups())
2803 .max(T::WeightInfo::prune_era_validator_slash_in_era(v))
2804 })]
2805 pub fn prune_era_step(origin: OriginFor<T>, era: EraIndex) -> DispatchResultWithPostInfo {
2806 let _ = ensure_signed(origin)?;
2807
2808 let active_era = crate::session_rotation::Rotator::<T>::active_era();
2810 let history_depth = T::HistoryDepth::get();
2811 let earliest_prunable_era = active_era.saturating_sub(history_depth).saturating_sub(1);
2812 ensure!(era <= earliest_prunable_era, Error::<T>::EraNotPrunable);
2813
2814 let actual_weight = Self::do_prune_era_step(era)?;
2815
2816 Ok(frame_support::dispatch::PostDispatchInfo {
2817 actual_weight: Some(actual_weight),
2818 pays_fee: frame_support::dispatch::Pays::No,
2819 })
2820 }
2821 }
2822}