1use crate::{
21 asset, slashing, weights::WeightInfo, AccountIdLookupOf, ActiveEraInfo, BalanceOf, EraPayout,
22 EraRewardPoints, ExposurePage, Forcing, LedgerIntegrityState, MaxNominationsOf,
23 NegativeImbalanceOf, Nominations, NominationsQuota, PositiveImbalanceOf, RewardDestination,
24 StakingLedger, UnappliedSlash, UnlockChunk, ValidatorPrefs,
25};
26use alloc::{format, vec::Vec};
27use codec::Codec;
28use frame_election_provider_support::{ElectionProvider, SortedListProvider, VoteWeight};
29use frame_support::{
30 assert_ok,
31 pallet_prelude::*,
32 traits::{
33 fungible::{
34 hold::{Balanced as FunHoldBalanced, Mutate as FunHoldMutate},
35 Mutate, Mutate as FunMutate,
36 },
37 Contains, Defensive, DefensiveSaturating, EnsureOrigin, Get, InspectLockableCurrency,
38 Nothing, OnUnbalanced,
39 },
40 weights::Weight,
41 BoundedBTreeSet, BoundedVec,
42};
43use frame_system::{ensure_root, ensure_signed, pallet_prelude::*};
44pub use impls::*;
45use rand::seq::SliceRandom;
46use rand_chacha::{
47 rand_core::{RngCore, SeedableRng},
48 ChaChaRng,
49};
50use sp_core::{sr25519::Pair as SrPair, Pair};
51use sp_runtime::{
52 traits::{StaticLookup, Zero},
53 ArithmeticError, Perbill, Percent,
54};
55use sp_staking::{
56 EraIndex, Page, SessionIndex,
57 StakingAccount::{self, Controller, Stash},
58 StakingInterface,
59};
60
61mod impls;
62
63#[frame_support::pallet]
64pub mod pallet {
65 use core::ops::Deref;
66
67 use super::*;
68 use crate::{session_rotation, PagedExposureMetadata, SnapshotStatus};
69 use codec::HasCompact;
70 use frame_election_provider_support::{ElectionDataProvider, PageIndex};
71 use frame_support::DefaultNoBound;
72
73 #[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
75 pub enum PruningStep {
76 ErasStakersPaged,
78 ErasStakersOverview,
80 ErasValidatorPrefs,
82 ClaimedRewards,
84 ErasValidatorReward,
86 ErasRewardPoints,
88 ErasTotalStake,
90 }
91
92 const STORAGE_VERSION: StorageVersion = StorageVersion::new(17);
94
95 #[pallet::pallet]
96 #[pallet::storage_version(STORAGE_VERSION)]
97 pub struct Pallet<T>(_);
98
99 #[derive(TypeInfo, Debug, Clone, Encode, Decode, DecodeWithMemTracking, PartialEq)]
101 pub enum ConfigOp<T: Default + Codec> {
102 Noop,
104 Set(T),
106 Remove,
108 }
109
110 #[pallet::config(with_default)]
111 pub trait Config: frame_system::Config {
112 #[pallet::no_default]
114 type OldCurrency: InspectLockableCurrency<
115 Self::AccountId,
116 Moment = BlockNumberFor<Self>,
117 Balance = Self::CurrencyBalance,
118 >;
119
120 #[pallet::no_default]
122 type Currency: FunHoldMutate<
123 Self::AccountId,
124 Reason = Self::RuntimeHoldReason,
125 Balance = Self::CurrencyBalance,
126 > + FunMutate<Self::AccountId, Balance = Self::CurrencyBalance>
127 + FunHoldBalanced<Self::AccountId, Balance = Self::CurrencyBalance>;
128
129 #[pallet::no_default_bounds]
131 type RuntimeHoldReason: From<HoldReason>;
132
133 type CurrencyBalance: sp_runtime::traits::AtLeast32BitUnsigned
136 + codec::FullCodec
137 + DecodeWithMemTracking
138 + HasCompact<Type: DecodeWithMemTracking>
139 + Copy
140 + MaybeSerializeDeserialize
141 + core::fmt::Debug
142 + Default
143 + From<u64>
144 + TypeInfo
145 + Send
146 + Sync
147 + MaxEncodedLen;
148
149 #[pallet::no_default_bounds]
156 type CurrencyToVote: sp_staking::currency_to_vote::CurrencyToVote<BalanceOf<Self>>;
157
158 #[pallet::no_default]
160 type ElectionProvider: ElectionProvider<
161 AccountId = Self::AccountId,
162 BlockNumber = BlockNumberFor<Self>,
163 DataProvider = Pallet<Self>,
165 >;
166
167 #[pallet::no_default_bounds]
169 type NominationsQuota: NominationsQuota<BalanceOf<Self>>;
170
171 #[pallet::constant]
185 type HistoryDepth: Get<u32>;
186
187 #[pallet::no_default_bounds]
190 type RewardRemainder: OnUnbalanced<NegativeImbalanceOf<Self>>;
191
192 #[pallet::no_default_bounds]
194 type Slash: OnUnbalanced<NegativeImbalanceOf<Self>>;
195
196 #[pallet::no_default_bounds]
200 type Reward: OnUnbalanced<PositiveImbalanceOf<Self>>;
201
202 #[pallet::constant]
204 type SessionsPerEra: Get<SessionIndex>;
205
206 #[pallet::constant]
221 type PlanningEraOffset: Get<SessionIndex>;
222
223 #[pallet::constant]
225 type BondingDuration: Get<EraIndex>;
226
227 #[pallet::constant]
232 type SlashDeferDuration: Get<EraIndex>;
233
234 #[pallet::no_default]
238 type AdminOrigin: EnsureOrigin<Self::RuntimeOrigin>;
239
240 #[pallet::no_default]
243 type EraPayout: EraPayout<BalanceOf<Self>>;
244
245 #[pallet::constant]
257 type MaxExposurePageSize: Get<u32>;
258
259 #[pallet::constant]
264 type MaxValidatorSet: Get<u32>;
265
266 #[pallet::no_default]
278 type VoterList: SortedListProvider<Self::AccountId, Score = VoteWeight>;
279
280 #[pallet::no_default]
301 type TargetList: SortedListProvider<Self::AccountId, Score = BalanceOf<Self>>;
302
303 #[pallet::constant]
314 type MaxUnlockingChunks: Get<u32>;
315
316 type MaxControllersInDeprecationBatch: Get<u32>;
318
319 #[pallet::no_default_bounds]
324 type EventListeners: sp_staking::OnStakingUpdate<Self::AccountId, BalanceOf<Self>>;
325
326 #[pallet::constant]
328 type MaxInvulnerables: Get<u32>;
329
330 #[pallet::constant]
339 type MaxEraDuration: Get<u64>;
340
341 #[pallet::constant]
347 type MaxPruningItems: Get<u32>;
348
349 #[pallet::no_default]
352 type RcClientInterface: pallet_staking_async_rc_client::RcClientInterface<
353 AccountId = Self::AccountId,
354 >;
355
356 #[pallet::no_default_bounds]
357 type Filter: Contains<Self::AccountId>;
362
363 type WeightInfo: WeightInfo;
365 }
366
367 #[pallet::composite_enum]
369 pub enum HoldReason {
370 #[codec(index = 0)]
372 Staking,
373 }
374
375 pub mod config_preludes {
377 use super::*;
378 use frame_support::{derive_impl, parameter_types, traits::ConstU32};
379 pub struct TestDefaultConfig;
380
381 #[derive_impl(frame_system::config_preludes::TestDefaultConfig, no_aggregated_types)]
382 impl frame_system::DefaultConfig for TestDefaultConfig {}
383
384 parameter_types! {
385 pub const SessionsPerEra: SessionIndex = 3;
386 pub const BondingDuration: EraIndex = 3;
387 pub const MaxPruningItems: u32 = 100;
388 }
389
390 #[frame_support::register_default_impl(TestDefaultConfig)]
391 impl DefaultConfig for TestDefaultConfig {
392 #[inject_runtime_type]
393 type RuntimeHoldReason = ();
394 type CurrencyBalance = u128;
395 type CurrencyToVote = ();
396 type NominationsQuota = crate::FixedNominationsQuota<16>;
397 type HistoryDepth = ConstU32<84>;
398 type RewardRemainder = ();
399 type Slash = ();
400 type Reward = ();
401 type SessionsPerEra = SessionsPerEra;
402 type BondingDuration = BondingDuration;
403 type PlanningEraOffset = ConstU32<1>;
404 type SlashDeferDuration = ();
405 type MaxExposurePageSize = ConstU32<64>;
406 type MaxUnlockingChunks = ConstU32<32>;
407 type MaxValidatorSet = ConstU32<100>;
408 type MaxControllersInDeprecationBatch = ConstU32<100>;
409 type MaxInvulnerables = ConstU32<20>;
410 type MaxEraDuration = ();
411 type MaxPruningItems = MaxPruningItems;
412 type EventListeners = ();
413 type Filter = Nothing;
414 type WeightInfo = ();
415 }
416 }
417
418 #[pallet::storage]
420 pub type ValidatorCount<T> = StorageValue<_, u32, ValueQuery>;
421
422 #[pallet::storage]
426 pub type Invulnerables<T: Config> =
427 StorageValue<_, BoundedVec<T::AccountId, T::MaxInvulnerables>, ValueQuery>;
428
429 #[pallet::storage]
433 pub type Bonded<T: Config> = StorageMap<_, Twox64Concat, T::AccountId, T::AccountId>;
434
435 #[pallet::storage]
437 pub type MinNominatorBond<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
438
439 #[pallet::storage]
441 pub type MinValidatorBond<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
442
443 #[pallet::storage]
445 pub type MinimumActiveStake<T> = StorageValue<_, BalanceOf<T>, ValueQuery>;
446
447 #[pallet::storage]
451 pub type MinCommission<T: Config> = StorageValue<_, Perbill, ValueQuery>;
452
453 #[pallet::storage]
458 pub type Ledger<T: Config> = StorageMap<_, Blake2_128Concat, T::AccountId, StakingLedger<T>>;
459
460 #[pallet::storage]
464 pub type Payee<T: Config> =
465 StorageMap<_, Twox64Concat, T::AccountId, RewardDestination<T::AccountId>, OptionQuery>;
466
467 #[pallet::storage]
471 pub type Validators<T: Config> =
472 CountedStorageMap<_, Twox64Concat, T::AccountId, ValidatorPrefs, ValueQuery>;
473
474 #[pallet::storage]
478 pub type MaxValidatorsCount<T> = StorageValue<_, u32, OptionQuery>;
479
480 #[pallet::storage]
500 pub type Nominators<T: Config> =
501 CountedStorageMap<_, Twox64Concat, T::AccountId, Nominations<T>>;
502
503 #[pallet::storage]
510 pub type VirtualStakers<T: Config> = CountedStorageMap<_, Twox64Concat, T::AccountId, ()>;
511
512 #[pallet::storage]
516 pub type MaxNominatorsCount<T> = StorageValue<_, u32, OptionQuery>;
517
518 #[pallet::storage]
525 pub type CurrentEra<T> = StorageValue<_, EraIndex>;
526
527 #[pallet::storage]
532 pub type ActiveEra<T> = StorageValue<_, ActiveEraInfo>;
533
534 pub struct BondedErasBound<T>(core::marker::PhantomData<T>);
536 impl<T: Config> Get<u32> for BondedErasBound<T> {
537 fn get() -> u32 {
538 T::BondingDuration::get().saturating_add(1)
539 }
540 }
541
542 #[pallet::storage]
547 pub type BondedEras<T: Config> =
548 StorageValue<_, BoundedVec<(EraIndex, SessionIndex), BondedErasBound<T>>, ValueQuery>;
549
550 #[pallet::storage]
565 pub type ErasStakersOverview<T: Config> = StorageDoubleMap<
566 _,
567 Twox64Concat,
568 EraIndex,
569 Twox64Concat,
570 T::AccountId,
571 PagedExposureMetadata<BalanceOf<T>>,
572 OptionQuery,
573 >;
574
575 #[derive(PartialEqNoBound, Encode, Decode, DebugNoBound, TypeInfo, DefaultNoBound)]
584 #[scale_info(skip_type_params(T))]
585 pub struct BoundedExposurePage<T: Config>(pub ExposurePage<T::AccountId, BalanceOf<T>>);
586 impl<T: Config> Deref for BoundedExposurePage<T> {
587 type Target = ExposurePage<T::AccountId, BalanceOf<T>>;
588
589 fn deref(&self) -> &Self::Target {
590 &self.0
591 }
592 }
593
594 impl<T: Config> core::ops::DerefMut for BoundedExposurePage<T> {
595 fn deref_mut(&mut self) -> &mut Self::Target {
596 &mut self.0
597 }
598 }
599
600 impl<T: Config> codec::MaxEncodedLen for BoundedExposurePage<T> {
601 fn max_encoded_len() -> usize {
602 let max_exposure_page_size = T::MaxExposurePageSize::get() as usize;
603 let individual_size =
604 T::AccountId::max_encoded_len() + BalanceOf::<T>::max_encoded_len();
605
606 BalanceOf::<T>::max_encoded_len() +
608 max_exposure_page_size.saturating_mul(individual_size)
610 }
611 }
612
613 impl<T: Config> From<ExposurePage<T::AccountId, BalanceOf<T>>> for BoundedExposurePage<T> {
614 fn from(value: ExposurePage<T::AccountId, BalanceOf<T>>) -> Self {
615 Self(value)
616 }
617 }
618
619 impl<T: Config> From<BoundedExposurePage<T>> for ExposurePage<T::AccountId, BalanceOf<T>> {
620 fn from(value: BoundedExposurePage<T>) -> Self {
621 value.0
622 }
623 }
624
625 impl<T: Config> codec::EncodeLike<BoundedExposurePage<T>>
626 for ExposurePage<T::AccountId, BalanceOf<T>>
627 {
628 }
629
630 #[pallet::storage]
637 pub type ErasStakersPaged<T: Config> = StorageNMap<
638 _,
639 (
640 NMapKey<Twox64Concat, EraIndex>,
641 NMapKey<Twox64Concat, T::AccountId>,
642 NMapKey<Twox64Concat, Page>,
643 ),
644 BoundedExposurePage<T>,
645 OptionQuery,
646 >;
647
648 pub struct ClaimedRewardsBound<T>(core::marker::PhantomData<T>);
649 impl<T: Config> Get<u32> for ClaimedRewardsBound<T> {
650 fn get() -> u32 {
651 let max_total_nominators_per_validator =
652 <T::ElectionProvider as ElectionProvider>::MaxBackersPerWinnerFinal::get();
653 let exposure_page_size = T::MaxExposurePageSize::get();
654 max_total_nominators_per_validator
655 .saturating_div(exposure_page_size)
656 .saturating_add(1)
657 }
658 }
659
660 #[pallet::storage]
667 pub type ClaimedRewards<T: Config> = StorageDoubleMap<
668 _,
669 Twox64Concat,
670 EraIndex,
671 Twox64Concat,
672 T::AccountId,
673 WeakBoundedVec<Page, ClaimedRewardsBound<T>>,
674 ValueQuery,
675 >;
676
677 #[pallet::storage]
684 pub type ErasValidatorPrefs<T: Config> = StorageDoubleMap<
685 _,
686 Twox64Concat,
687 EraIndex,
688 Twox64Concat,
689 T::AccountId,
690 ValidatorPrefs,
691 ValueQuery,
692 >;
693
694 #[pallet::storage]
698 pub type ErasValidatorReward<T: Config> = StorageMap<_, Twox64Concat, EraIndex, BalanceOf<T>>;
699
700 #[pallet::storage]
703 pub type ErasRewardPoints<T: Config> =
704 StorageMap<_, Twox64Concat, EraIndex, EraRewardPoints<T>, ValueQuery>;
705
706 #[pallet::storage]
709 pub type ErasTotalStake<T: Config> =
710 StorageMap<_, Twox64Concat, EraIndex, BalanceOf<T>, ValueQuery>;
711
712 #[pallet::storage]
714 pub type ForceEra<T> = StorageValue<_, Forcing, ValueQuery>;
715
716 #[pallet::storage]
720 pub type MaxStakedRewards<T> = StorageValue<_, Percent, OptionQuery>;
721
722 #[pallet::storage]
726 pub type SlashRewardFraction<T> = StorageValue<_, Perbill, ValueQuery>;
727
728 #[pallet::storage]
731 pub type CanceledSlashPayout<T: Config> = StorageValue<_, BalanceOf<T>, ValueQuery>;
732
733 #[pallet::storage]
744 pub type OffenceQueue<T: Config> = StorageDoubleMap<
745 _,
746 Twox64Concat,
747 EraIndex,
748 Twox64Concat,
749 T::AccountId,
750 slashing::OffenceRecord<T::AccountId>,
751 >;
752
753 #[pallet::storage]
765 pub type OffenceQueueEras<T: Config> = StorageValue<_, WeakBoundedVec<u32, T::BondingDuration>>;
766
767 #[pallet::storage]
780 pub type ProcessingOffence<T: Config> =
781 StorageValue<_, (EraIndex, T::AccountId, slashing::OffenceRecord<T::AccountId>)>;
782
783 #[pallet::storage]
785 pub type UnappliedSlashes<T: Config> = StorageDoubleMap<
786 _,
787 Twox64Concat,
788 EraIndex,
789 Twox64Concat,
790 (T::AccountId, Perbill, u32),
792 UnappliedSlash<T>,
793 OptionQuery,
794 >;
795
796 #[pallet::storage]
802 pub type CancelledSlashes<T: Config> = StorageMap<
803 _,
804 Twox64Concat,
805 EraIndex,
806 BoundedVec<(T::AccountId, Perbill), T::MaxValidatorSet>,
807 ValueQuery,
808 >;
809
810 #[pallet::storage]
813 pub type ValidatorSlashInEra<T: Config> = StorageDoubleMap<
814 _,
815 Twox64Concat,
816 EraIndex,
817 Twox64Concat,
818 T::AccountId,
819 (Perbill, BalanceOf<T>),
820 >;
821
822 #[pallet::storage]
826 pub type ChillThreshold<T: Config> = StorageValue<_, Percent, OptionQuery>;
827
828 #[pallet::storage]
833 pub type VoterSnapshotStatus<T: Config> =
834 StorageValue<_, SnapshotStatus<T::AccountId>, ValueQuery>;
835
836 #[pallet::storage]
843 pub type NextElectionPage<T: Config> = StorageValue<_, PageIndex, OptionQuery>;
844
845 #[pallet::storage]
847 pub type ElectableStashes<T: Config> =
848 StorageValue<_, BoundedBTreeSet<T::AccountId, T::MaxValidatorSet>, ValueQuery>;
849
850 #[pallet::storage]
852 pub type EraPruningState<T: Config> = StorageMap<_, Twox64Concat, EraIndex, PruningStep>;
853
854 #[pallet::genesis_config]
855 #[derive(frame_support::DefaultNoBound, frame_support::DebugNoBound)]
856 pub struct GenesisConfig<T: Config> {
857 pub validator_count: u32,
858 pub invulnerables: BoundedVec<T::AccountId, T::MaxInvulnerables>,
859 pub force_era: Forcing,
860 pub slash_reward_fraction: Perbill,
861 pub canceled_payout: BalanceOf<T>,
862 pub stakers: Vec<(T::AccountId, BalanceOf<T>, crate::StakerStatus<T::AccountId>)>,
863 pub min_nominator_bond: BalanceOf<T>,
864 pub min_validator_bond: BalanceOf<T>,
865 pub max_validator_count: Option<u32>,
866 pub max_nominator_count: Option<u32>,
867 pub dev_stakers: Option<(u32, u32)>,
874 pub active_era: (u32, u32, u64),
876 }
877
878 impl<T: Config> GenesisConfig<T> {
879 fn generate_endowed_bonded_account(derivation: &str, rng: &mut ChaChaRng) -> T::AccountId {
880 let pair: SrPair = Pair::from_string(&derivation, None)
881 .expect(&format!("Failed to parse derivation string: {derivation}"));
882 let who = T::AccountId::decode(&mut &pair.public().encode()[..])
883 .expect(&format!("Failed to decode public key from pair: {:?}", pair.public()));
884
885 let (min, max) = T::VoterList::range();
886 let stake = BalanceOf::<T>::from(rng.next_u64().min(max).max(min));
887 let two: BalanceOf<T> = 2u32.into();
888
889 assert_ok!(T::Currency::mint_into(&who, stake * two));
890 assert_ok!(<Pallet<T>>::bond(
891 T::RuntimeOrigin::from(Some(who.clone()).into()),
892 stake,
893 RewardDestination::Staked,
894 ));
895 who
896 }
897 }
898
899 #[pallet::genesis_build]
900 impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
901 fn build(&self) {
902 crate::log!(trace, "initializing with {:?}", self);
903 assert!(
904 self.validator_count <=
905 <T::ElectionProvider as ElectionProvider>::MaxWinnersPerPage::get() *
906 <T::ElectionProvider as ElectionProvider>::Pages::get(),
907 "validator count is too high, `ElectionProvider` can never fulfill this"
908 );
909 ValidatorCount::<T>::put(self.validator_count);
910
911 assert!(
912 self.invulnerables.len() as u32 <= T::MaxInvulnerables::get(),
913 "Too many invulnerable validators at genesis."
914 );
915 <Invulnerables<T>>::put(&self.invulnerables);
916
917 ForceEra::<T>::put(self.force_era);
918 CanceledSlashPayout::<T>::put(self.canceled_payout);
919 SlashRewardFraction::<T>::put(self.slash_reward_fraction);
920 MinNominatorBond::<T>::put(self.min_nominator_bond);
921 MinValidatorBond::<T>::put(self.min_validator_bond);
922 if let Some(x) = self.max_validator_count {
923 MaxValidatorsCount::<T>::put(x);
924 }
925 if let Some(x) = self.max_nominator_count {
926 MaxNominatorsCount::<T>::put(x);
927 }
928
929 for &(ref stash, balance, ref status) in &self.stakers {
931 match status {
932 crate::StakerStatus::Validator => {
933 crate::log!(
934 trace,
935 "inserting genesis validator: {:?} => {:?} => {:?}",
936 stash,
937 balance,
938 status
939 );
940 assert!(
941 asset::free_to_stake::<T>(stash) >= balance,
942 "Stash does not have enough balance to bond."
943 );
944 assert_ok!(<Pallet<T>>::bond(
945 T::RuntimeOrigin::from(Some(stash.clone()).into()),
946 balance,
947 RewardDestination::Staked,
948 ));
949 assert_ok!(<Pallet<T>>::validate(
950 T::RuntimeOrigin::from(Some(stash.clone()).into()),
951 Default::default(),
952 ));
953 },
954 crate::StakerStatus::Idle => {
955 crate::log!(
956 trace,
957 "inserting genesis idle staker: {:?} => {:?} => {:?}",
958 stash,
959 balance,
960 status
961 );
962 assert!(
963 asset::free_to_stake::<T>(stash) >= balance,
964 "Stash does not have enough balance to bond."
965 );
966 assert_ok!(<Pallet<T>>::bond(
967 T::RuntimeOrigin::from(Some(stash.clone()).into()),
968 balance,
969 RewardDestination::Staked,
970 ));
971 },
972 _ => {},
973 }
974 }
975
976 for &(ref stash, balance, ref status) in &self.stakers {
978 match status {
979 crate::StakerStatus::Nominator(votes) => {
980 crate::log!(
981 trace,
982 "inserting genesis nominator: {:?} => {:?} => {:?}",
983 stash,
984 balance,
985 status
986 );
987 assert!(
988 asset::free_to_stake::<T>(stash) >= balance,
989 "Stash does not have enough balance to bond."
990 );
991 assert_ok!(<Pallet<T>>::bond(
992 T::RuntimeOrigin::from(Some(stash.clone()).into()),
993 balance,
994 RewardDestination::Staked,
995 ));
996 assert_ok!(<Pallet<T>>::nominate(
997 T::RuntimeOrigin::from(Some(stash.clone()).into()),
998 votes.iter().map(|l| T::Lookup::unlookup(l.clone())).collect(),
999 ));
1000 },
1001 _ => {},
1002 }
1003 }
1004
1005 assert_eq!(
1007 T::VoterList::count(),
1008 Nominators::<T>::count() + Validators::<T>::count(),
1009 "not all genesis stakers were inserted into sorted list provider, something is wrong."
1010 );
1011
1012 if let Some((validators, nominators)) = self.dev_stakers {
1014 crate::log!(
1015 debug,
1016 "generating dev stakers: validators: {}, nominators: {}",
1017 validators,
1018 nominators
1019 );
1020 let base_derivation = "//staker//{}";
1021
1022 let mut rng =
1025 ChaChaRng::from_seed(base_derivation.using_encoded(sp_core::blake2_256));
1026
1027 (0..validators).for_each(|index| {
1028 let derivation = base_derivation.replace("{}", &format!("validator{}", index));
1029 let who = Self::generate_endowed_bonded_account(&derivation, &mut rng);
1030 assert_ok!(<Pallet<T>>::validate(
1031 T::RuntimeOrigin::from(Some(who.clone()).into()),
1032 Default::default(),
1033 ));
1034 });
1035
1036 let all_validators = Validators::<T>::iter_keys().collect::<Vec<_>>();
1039
1040 (0..nominators).for_each(|index| {
1041 let derivation = base_derivation.replace("{}", &format!("nominator{}", index));
1042 let who = Self::generate_endowed_bonded_account(&derivation, &mut rng);
1043
1044 let random_nominations = all_validators
1045 .choose_multiple(&mut rng, MaxNominationsOf::<T>::get() as usize)
1046 .map(|v| v.clone())
1047 .collect::<Vec<_>>();
1048
1049 assert_ok!(<Pallet<T>>::nominate(
1050 T::RuntimeOrigin::from(Some(who.clone()).into()),
1051 random_nominations.iter().map(|l| T::Lookup::unlookup(l.clone())).collect(),
1052 ));
1053 })
1054 }
1055
1056 let (active_era, session_index, timestamp) = self.active_era;
1057 ActiveEra::<T>::put(ActiveEraInfo { index: active_era, start: Some(timestamp) });
1058 CurrentEra::<T>::put(active_era);
1060 BondedEras::<T>::put(
1062 BoundedVec::<_, BondedErasBound<T>>::try_from(
1063 alloc::vec![(active_era, session_index)]
1064 )
1065 .expect("bound for BondedEras is BondingDuration + 1; can contain at least one element; qed")
1066 );
1067 }
1068 }
1069
1070 #[pallet::event]
1071 #[pallet::generate_deposit(pub fn deposit_event)]
1072 pub enum Event<T: Config> {
1073 EraPaid {
1076 era_index: EraIndex,
1077 validator_payout: BalanceOf<T>,
1078 remainder: BalanceOf<T>,
1079 },
1080 Rewarded {
1082 stash: T::AccountId,
1083 dest: RewardDestination<T::AccountId>,
1084 amount: BalanceOf<T>,
1085 },
1086 Slashed {
1088 staker: T::AccountId,
1089 amount: BalanceOf<T>,
1090 },
1091 OldSlashingReportDiscarded {
1094 session_index: SessionIndex,
1095 },
1096 Bonded {
1101 stash: T::AccountId,
1102 amount: BalanceOf<T>,
1103 },
1104 Unbonded {
1106 stash: T::AccountId,
1107 amount: BalanceOf<T>,
1108 },
1109 Withdrawn {
1112 stash: T::AccountId,
1113 amount: BalanceOf<T>,
1114 },
1115 StakerRemoved {
1118 stash: T::AccountId,
1119 },
1120 Kicked {
1122 nominator: T::AccountId,
1123 stash: T::AccountId,
1124 },
1125 Chilled {
1127 stash: T::AccountId,
1128 },
1129 PayoutStarted {
1131 era_index: EraIndex,
1132 validator_stash: T::AccountId,
1133 page: Page,
1134 next: Option<Page>,
1135 },
1136 ValidatorPrefsSet {
1138 stash: T::AccountId,
1139 prefs: ValidatorPrefs,
1140 },
1141 SnapshotVotersSizeExceeded {
1143 size: u32,
1144 },
1145 SnapshotTargetsSizeExceeded {
1147 size: u32,
1148 },
1149 ForceEra {
1150 mode: Forcing,
1151 },
1152 ControllerBatchDeprecated {
1154 failures: u32,
1155 },
1156 CurrencyMigrated {
1159 stash: T::AccountId,
1160 force_withdraw: BalanceOf<T>,
1161 },
1162 PagedElectionProceeded {
1172 page: PageIndex,
1173 result: Result<u32, u32>,
1174 },
1175 OffenceReported {
1178 offence_era: EraIndex,
1179 validator: T::AccountId,
1180 fraction: Perbill,
1181 },
1182 SlashComputed {
1184 offence_era: EraIndex,
1185 slash_era: EraIndex,
1186 offender: T::AccountId,
1187 page: u32,
1188 },
1189 SlashCancelled {
1191 slash_era: EraIndex,
1192 validator: T::AccountId,
1193 },
1194 SessionRotated {
1199 starting_session: SessionIndex,
1200 active_era: EraIndex,
1201 planned_era: EraIndex,
1202 },
1203 Unexpected(UnexpectedKind),
1206 OffenceTooOld {
1208 offence_era: EraIndex,
1209 validator: T::AccountId,
1210 fraction: Perbill,
1211 },
1212 EraPruned {
1214 index: EraIndex,
1215 },
1216 }
1217
1218 #[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, TypeInfo, RuntimeDebug)]
1224 pub enum UnexpectedKind {
1225 EraDurationBoundExceeded,
1227 UnknownValidatorActivation,
1229 }
1230
1231 #[pallet::error]
1232 #[derive(PartialEq)]
1233 pub enum Error<T> {
1234 NotController,
1236 NotStash,
1238 AlreadyBonded,
1240 AlreadyPaired,
1242 EmptyTargets,
1244 DuplicateIndex,
1246 InvalidSlashRecord,
1248 InsufficientBond,
1252 NoMoreChunks,
1254 NoUnlockChunk,
1256 FundedTarget,
1258 InvalidEraToReward,
1260 InvalidNumberOfNominations,
1262 AlreadyClaimed,
1264 InvalidPage,
1266 IncorrectHistoryDepth,
1268 BadState,
1270 TooManyTargets,
1272 BadTarget,
1274 CannotChillOther,
1276 TooManyNominators,
1279 TooManyValidators,
1282 CommissionTooLow,
1284 BoundNotMet,
1286 ControllerDeprecated,
1288 CannotRestoreLedger,
1290 RewardDestinationRestricted,
1292 NotEnoughFunds,
1294 VirtualStakerNotAllowed,
1296 CannotReapStash,
1298 AlreadyMigrated,
1300 EraNotStarted,
1302 Restricted,
1305 UnappliedSlashesInPreviousEra,
1308 EraNotPrunable,
1310 CancelledSlash,
1312 }
1313
1314 impl<T: Config> Pallet<T> {
1315 pub fn apply_unapplied_slashes(active_era: EraIndex) -> Weight {
1317 let mut slashes = UnappliedSlashes::<T>::iter_prefix(&active_era).take(1);
1318 if let Some((key, slash)) = slashes.next() {
1319 crate::log!(
1320 debug,
1321 "🦹 found slash {:?} scheduled to be executed in era {:?}",
1322 slash,
1323 active_era,
1324 );
1325
1326 if Self::check_slash_cancelled(active_era, &key.0, key.1) {
1328 crate::log!(
1329 debug,
1330 "🦹 slash for {:?} in era {:?} was cancelled, skipping",
1331 key.0,
1332 active_era,
1333 );
1334 } else {
1335 let offence_era = active_era.saturating_sub(T::SlashDeferDuration::get());
1336 slashing::apply_slash::<T>(slash, offence_era);
1337 }
1338
1339 UnappliedSlashes::<T>::remove(&active_era, &key);
1341
1342 if UnappliedSlashes::<T>::iter_prefix(&active_era).next().is_none() {
1344 CancelledSlashes::<T>::remove(&active_era);
1346 }
1347
1348 T::WeightInfo::apply_slash()
1349 } else {
1350 T::DbWeight::get().reads(1)
1352 }
1353 }
1354
1355 fn do_prune_era_step(era: EraIndex) -> Result<Weight, DispatchError> {
1357 let current_step = EraPruningState::<T>::get(era).ok_or(Error::<T>::EraNotPrunable)?;
1362
1363 let items_limit = T::MaxPruningItems::get().min(T::MaxValidatorSet::get());
1366
1367 let actual_weight = match current_step {
1368 PruningStep::ErasStakersPaged => {
1369 let result = ErasStakersPaged::<T>::clear_prefix((era,), items_limit, None);
1370 let items_deleted = result.backend as u32;
1371 result.maybe_cursor.is_none().then(|| {
1372 EraPruningState::<T>::insert(era, PruningStep::ErasStakersOverview)
1373 });
1374 T::WeightInfo::prune_era_stakers_paged(items_deleted)
1375 },
1376 PruningStep::ErasStakersOverview => {
1377 let result = ErasStakersOverview::<T>::clear_prefix(era, items_limit, None);
1378 let items_deleted = result.backend as u32;
1379 result.maybe_cursor.is_none().then(|| {
1380 EraPruningState::<T>::insert(era, PruningStep::ErasValidatorPrefs)
1381 });
1382 T::WeightInfo::prune_era_stakers_overview(items_deleted)
1383 },
1384 PruningStep::ErasValidatorPrefs => {
1385 let result = ErasValidatorPrefs::<T>::clear_prefix(era, items_limit, None);
1386 let items_deleted = result.backend as u32;
1387 result
1388 .maybe_cursor
1389 .is_none()
1390 .then(|| EraPruningState::<T>::insert(era, PruningStep::ClaimedRewards));
1391 T::WeightInfo::prune_era_validator_prefs(items_deleted)
1392 },
1393 PruningStep::ClaimedRewards => {
1394 let result = ClaimedRewards::<T>::clear_prefix(era, items_limit, None);
1395 let items_deleted = result.backend as u32;
1396 result.maybe_cursor.is_none().then(|| {
1397 EraPruningState::<T>::insert(era, PruningStep::ErasValidatorReward)
1398 });
1399 T::WeightInfo::prune_era_claimed_rewards(items_deleted)
1400 },
1401 PruningStep::ErasValidatorReward => {
1402 ErasValidatorReward::<T>::remove(era);
1403 EraPruningState::<T>::insert(era, PruningStep::ErasRewardPoints);
1404 T::WeightInfo::prune_era_validator_reward()
1405 },
1406 PruningStep::ErasRewardPoints => {
1407 ErasRewardPoints::<T>::remove(era);
1408 EraPruningState::<T>::insert(era, PruningStep::ErasTotalStake);
1409 T::WeightInfo::prune_era_reward_points()
1410 },
1411 PruningStep::ErasTotalStake => {
1412 ErasTotalStake::<T>::remove(era);
1413 EraPruningState::<T>::remove(era);
1415 T::WeightInfo::prune_era_total_stake()
1416 },
1417 };
1418
1419 if EraPruningState::<T>::get(era).is_none() {
1421 Self::deposit_event(Event::<T>::EraPruned { index: era });
1422 }
1423
1424 Ok(actual_weight)
1425 }
1426 }
1427
1428 #[pallet::hooks]
1429 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
1430 fn on_initialize(_now: BlockNumberFor<T>) -> Weight {
1431 let mut consumed_weight = slashing::process_offence::<T>();
1433
1434 consumed_weight.saturating_accrue(T::DbWeight::get().reads(1));
1436 if let Some(active_era) = ActiveEra::<T>::get() {
1437 let slash_weight = Self::apply_unapplied_slashes(active_era.index);
1438 consumed_weight.saturating_accrue(slash_weight);
1439 }
1440
1441 session_rotation::EraElectionPlanner::<T>::maybe_fetch_election_results();
1444 consumed_weight
1445 }
1446
1447 fn integrity_test() {
1448 assert_eq!(
1450 MaxNominationsOf::<T>::get(),
1451 <Self as ElectionDataProvider>::MaxVotesPerVoter::get()
1452 );
1453
1454 assert!(!MaxNominationsOf::<T>::get().is_zero());
1456
1457 assert!(
1458 T::SlashDeferDuration::get() < T::BondingDuration::get() || T::BondingDuration::get() == 0,
1459 "As per documentation, slash defer duration ({}) should be less than bonding duration ({}).",
1460 T::SlashDeferDuration::get(),
1461 T::BondingDuration::get(),
1462 );
1463
1464 assert!(
1466 T::MaxPruningItems::get() >= 100,
1467 "MaxPruningItems must be at least 100 for efficient pruning, got: {}",
1468 T::MaxPruningItems::get()
1469 );
1470 }
1471
1472 #[cfg(feature = "try-runtime")]
1473 fn try_state(n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
1474 Self::do_try_state(n)
1475 }
1476 }
1477
1478 #[pallet::call]
1479 impl<T: Config> Pallet<T> {
1480 #[pallet::call_index(0)]
1493 #[pallet::weight(T::WeightInfo::bond())]
1494 pub fn bond(
1495 origin: OriginFor<T>,
1496 #[pallet::compact] value: BalanceOf<T>,
1497 payee: RewardDestination<T::AccountId>,
1498 ) -> DispatchResult {
1499 let stash = ensure_signed(origin)?;
1500
1501 ensure!(!T::Filter::contains(&stash), Error::<T>::Restricted);
1502
1503 if StakingLedger::<T>::is_bonded(StakingAccount::Stash(stash.clone())) {
1504 return Err(Error::<T>::AlreadyBonded.into());
1505 }
1506
1507 if StakingLedger::<T>::is_bonded(StakingAccount::Controller(stash.clone())) {
1509 return Err(Error::<T>::AlreadyPaired.into());
1510 }
1511
1512 if value < Self::min_chilled_bond() {
1514 return Err(Error::<T>::InsufficientBond.into());
1515 }
1516
1517 let stash_balance = asset::free_to_stake::<T>(&stash);
1518 let value = value.min(stash_balance);
1519 Self::deposit_event(Event::<T>::Bonded { stash: stash.clone(), amount: value });
1520 let ledger = StakingLedger::<T>::new(stash.clone(), value);
1521
1522 ledger.bond(payee)?;
1525
1526 Ok(())
1527 }
1528
1529 #[pallet::call_index(1)]
1540 #[pallet::weight(T::WeightInfo::bond_extra())]
1541 pub fn bond_extra(
1542 origin: OriginFor<T>,
1543 #[pallet::compact] max_additional: BalanceOf<T>,
1544 ) -> DispatchResult {
1545 let stash = ensure_signed(origin)?;
1546 ensure!(!T::Filter::contains(&stash), Error::<T>::Restricted);
1547 Self::do_bond_extra(&stash, max_additional)
1548 }
1549
1550 #[pallet::call_index(2)]
1570 #[pallet::weight(
1571 T::WeightInfo::withdraw_unbonded_kill().saturating_add(T::WeightInfo::unbond()))
1572 ]
1573 pub fn unbond(
1574 origin: OriginFor<T>,
1575 #[pallet::compact] value: BalanceOf<T>,
1576 ) -> DispatchResultWithPostInfo {
1577 let controller = ensure_signed(origin)?;
1578 let unlocking =
1579 Self::ledger(Controller(controller.clone())).map(|l| l.unlocking.len())?;
1580
1581 let maybe_withdraw_weight = {
1584 if unlocking == T::MaxUnlockingChunks::get() as usize {
1585 Some(Self::do_withdraw_unbonded(&controller)?)
1586 } else {
1587 None
1588 }
1589 };
1590
1591 let mut ledger = Self::ledger(Controller(controller))?;
1594 let mut value = value.min(ledger.active);
1595 let stash = ledger.stash.clone();
1596
1597 ensure!(
1598 ledger.unlocking.len() < T::MaxUnlockingChunks::get() as usize,
1599 Error::<T>::NoMoreChunks,
1600 );
1601
1602 if !value.is_zero() {
1603 ledger.active -= value;
1604
1605 if ledger.active < asset::existential_deposit::<T>() {
1607 value += ledger.active;
1608 ledger.active = Zero::zero();
1609 }
1610
1611 let min_active_bond = if Nominators::<T>::contains_key(&stash) {
1612 Self::min_nominator_bond()
1613 } else if Validators::<T>::contains_key(&stash) {
1614 Self::min_validator_bond()
1615 } else {
1616 Zero::zero()
1618 };
1619
1620 ensure!(ledger.active >= min_active_bond, Error::<T>::InsufficientBond);
1623
1624 let era = session_rotation::Rotator::<T>::active_era()
1628 .saturating_add(T::BondingDuration::get());
1629 if let Some(chunk) = ledger.unlocking.last_mut().filter(|chunk| chunk.era == era) {
1630 chunk.value = chunk.value.defensive_saturating_add(value)
1634 } else {
1635 ledger
1636 .unlocking
1637 .try_push(UnlockChunk { value, era })
1638 .map_err(|_| Error::<T>::NoMoreChunks)?;
1639 };
1640 ledger.update()?;
1642
1643 if T::VoterList::contains(&stash) {
1645 let _ = T::VoterList::on_update(&stash, Self::weight_of(&stash));
1646 }
1647
1648 Self::deposit_event(Event::<T>::Unbonded { stash, amount: value });
1649 }
1650
1651 let actual_weight = if let Some(withdraw_weight) = maybe_withdraw_weight {
1652 Some(T::WeightInfo::unbond().saturating_add(withdraw_weight))
1653 } else {
1654 Some(T::WeightInfo::unbond())
1655 };
1656
1657 Ok(actual_weight.into())
1658 }
1659
1660 #[pallet::call_index(3)]
1680 #[pallet::weight(T::WeightInfo::withdraw_unbonded_kill())]
1681 pub fn withdraw_unbonded(
1682 origin: OriginFor<T>,
1683 _num_slashing_spans: u32,
1684 ) -> DispatchResultWithPostInfo {
1685 let controller = ensure_signed(origin)?;
1686
1687 let actual_weight = Self::do_withdraw_unbonded(&controller)?;
1688 Ok(Some(actual_weight).into())
1689 }
1690
1691 #[pallet::call_index(4)]
1697 #[pallet::weight(T::WeightInfo::validate())]
1698 pub fn validate(origin: OriginFor<T>, prefs: ValidatorPrefs) -> DispatchResult {
1699 let controller = ensure_signed(origin)?;
1700
1701 let ledger = Self::ledger(Controller(controller))?;
1702
1703 ensure!(ledger.active >= Self::min_validator_bond(), Error::<T>::InsufficientBond);
1704 let stash = &ledger.stash;
1705
1706 ensure!(prefs.commission >= MinCommission::<T>::get(), Error::<T>::CommissionTooLow);
1708
1709 if !Validators::<T>::contains_key(stash) {
1711 if let Some(max_validators) = MaxValidatorsCount::<T>::get() {
1715 ensure!(
1716 Validators::<T>::count() < max_validators,
1717 Error::<T>::TooManyValidators
1718 );
1719 }
1720 }
1721
1722 Self::do_remove_nominator(stash);
1723 Self::do_add_validator(stash, prefs.clone());
1724 Self::deposit_event(Event::<T>::ValidatorPrefsSet { stash: ledger.stash, prefs });
1725
1726 Ok(())
1727 }
1728
1729 #[pallet::call_index(5)]
1735 #[pallet::weight(T::WeightInfo::nominate(targets.len() as u32))]
1736 pub fn nominate(
1737 origin: OriginFor<T>,
1738 targets: Vec<AccountIdLookupOf<T>>,
1739 ) -> DispatchResult {
1740 let controller = ensure_signed(origin)?;
1741
1742 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()))?;
1743
1744 ensure!(ledger.active >= Self::min_nominator_bond(), Error::<T>::InsufficientBond);
1745 let stash = &ledger.stash;
1746
1747 if !Nominators::<T>::contains_key(stash) {
1749 if let Some(max_nominators) = MaxNominatorsCount::<T>::get() {
1753 ensure!(
1754 Nominators::<T>::count() < max_nominators,
1755 Error::<T>::TooManyNominators
1756 );
1757 }
1758 }
1759
1760 let mut targets = targets
1762 .into_iter()
1763 .map(|t| T::Lookup::lookup(t).map_err(DispatchError::from))
1764 .collect::<Result<Vec<_>, _>>()?;
1765 targets.sort();
1766 targets.dedup();
1767
1768 ensure!(!targets.is_empty(), Error::<T>::EmptyTargets);
1769 ensure!(
1770 targets.len() <= T::NominationsQuota::get_quota(ledger.active) as usize,
1771 Error::<T>::TooManyTargets
1772 );
1773
1774 let old = Nominators::<T>::get(stash).map_or_else(Vec::new, |x| x.targets.into_inner());
1775
1776 let targets: BoundedVec<_, _> = targets
1777 .into_iter()
1778 .map(|n| {
1779 if old.contains(&n) ||
1780 (Validators::<T>::contains_key(&n) && !Validators::<T>::get(&n).blocked)
1781 {
1782 Ok(n)
1783 } else {
1784 Err(Error::<T>::BadTarget.into())
1785 }
1786 })
1787 .collect::<Result<Vec<_>, DispatchError>>()?
1788 .try_into()
1789 .map_err(|_| Error::<T>::TooManyNominators)?;
1790
1791 let nominations = Nominations {
1792 targets,
1793 submitted_in: CurrentEra::<T>::get().unwrap_or(0),
1795 suppressed: false,
1796 };
1797
1798 Self::do_remove_validator(stash);
1799 Self::do_add_nominator(stash, nominations);
1800 Ok(())
1801 }
1802
1803 #[pallet::call_index(6)]
1814 #[pallet::weight(T::WeightInfo::chill())]
1815 pub fn chill(origin: OriginFor<T>) -> DispatchResult {
1816 let controller = ensure_signed(origin)?;
1817
1818 let ledger = Self::ledger(StakingAccount::Controller(controller))?;
1819
1820 Self::chill_stash(&ledger.stash);
1821 Ok(())
1822 }
1823
1824 #[pallet::call_index(7)]
1830 #[pallet::weight(T::WeightInfo::set_payee())]
1831 pub fn set_payee(
1832 origin: OriginFor<T>,
1833 payee: RewardDestination<T::AccountId>,
1834 ) -> DispatchResult {
1835 let controller = ensure_signed(origin)?;
1836 let ledger = Self::ledger(Controller(controller.clone()))?;
1837
1838 ensure!(
1839 (payee != {
1840 #[allow(deprecated)]
1841 RewardDestination::Controller
1842 }),
1843 Error::<T>::ControllerDeprecated
1844 );
1845
1846 let _ = ledger
1847 .set_payee(payee)
1848 .defensive_proof("ledger was retrieved from storage, thus it's bonded; qed.")?;
1849
1850 Ok(())
1851 }
1852
1853 #[pallet::call_index(8)]
1862 #[pallet::weight(T::WeightInfo::set_controller())]
1863 pub fn set_controller(origin: OriginFor<T>) -> DispatchResult {
1864 let stash = ensure_signed(origin)?;
1865
1866 Self::ledger(StakingAccount::Stash(stash.clone())).map(|ledger| {
1867 let controller = ledger.controller()
1868 .defensive_proof("Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.")
1869 .ok_or(Error::<T>::NotController)?;
1870
1871 if controller == stash {
1872 return Err(Error::<T>::AlreadyPaired.into())
1874 }
1875
1876 let _ = ledger.set_controller_to_stash()?;
1877 Ok(())
1878 })?
1879 }
1880
1881 #[pallet::call_index(9)]
1885 #[pallet::weight(T::WeightInfo::set_validator_count())]
1886 pub fn set_validator_count(
1887 origin: OriginFor<T>,
1888 #[pallet::compact] new: u32,
1889 ) -> DispatchResult {
1890 ensure_root(origin)?;
1891
1892 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
1893
1894 ValidatorCount::<T>::put(new);
1895 Ok(())
1896 }
1897
1898 #[pallet::call_index(10)]
1903 #[pallet::weight(T::WeightInfo::set_validator_count())]
1904 pub fn increase_validator_count(
1905 origin: OriginFor<T>,
1906 #[pallet::compact] additional: u32,
1907 ) -> DispatchResult {
1908 ensure_root(origin)?;
1909 let old = ValidatorCount::<T>::get();
1910 let new = old.checked_add(additional).ok_or(ArithmeticError::Overflow)?;
1911
1912 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
1913
1914 ValidatorCount::<T>::put(new);
1915 Ok(())
1916 }
1917
1918 #[pallet::call_index(11)]
1923 #[pallet::weight(T::WeightInfo::set_validator_count())]
1924 pub fn scale_validator_count(origin: OriginFor<T>, factor: Percent) -> DispatchResult {
1925 ensure_root(origin)?;
1926 let old = ValidatorCount::<T>::get();
1927 let new = old.checked_add(factor.mul_floor(old)).ok_or(ArithmeticError::Overflow)?;
1928
1929 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
1930
1931 ValidatorCount::<T>::put(new);
1932 Ok(())
1933 }
1934
1935 #[pallet::call_index(12)]
1945 #[pallet::weight(T::WeightInfo::force_no_eras())]
1946 pub fn force_no_eras(origin: OriginFor<T>) -> DispatchResult {
1947 ensure_root(origin)?;
1948 Self::set_force_era(Forcing::ForceNone);
1949 Ok(())
1950 }
1951
1952 #[pallet::call_index(13)]
1963 #[pallet::weight(T::WeightInfo::force_new_era())]
1964 pub fn force_new_era(origin: OriginFor<T>) -> DispatchResult {
1965 ensure_root(origin)?;
1966 Self::set_force_era(Forcing::ForceNew);
1967 Ok(())
1968 }
1969
1970 #[pallet::call_index(14)]
1974 #[pallet::weight(T::WeightInfo::set_invulnerables(invulnerables.len() as u32))]
1975 pub fn set_invulnerables(
1976 origin: OriginFor<T>,
1977 invulnerables: Vec<T::AccountId>,
1978 ) -> DispatchResult {
1979 ensure_root(origin)?;
1980 let invulnerables =
1981 BoundedVec::try_from(invulnerables).map_err(|_| Error::<T>::BoundNotMet)?;
1982 <Invulnerables<T>>::put(invulnerables);
1983 Ok(())
1984 }
1985
1986 #[pallet::call_index(15)]
1995 #[pallet::weight(T::WeightInfo::force_unstake())]
1996 pub fn force_unstake(
1997 origin: OriginFor<T>,
1998 stash: T::AccountId,
1999 _num_slashing_spans: u32,
2000 ) -> DispatchResult {
2001 ensure_root(origin)?;
2002
2003 Self::kill_stash(&stash)?;
2005
2006 Ok(())
2007 }
2008
2009 #[pallet::call_index(16)]
2019 #[pallet::weight(T::WeightInfo::force_new_era_always())]
2020 pub fn force_new_era_always(origin: OriginFor<T>) -> DispatchResult {
2021 ensure_root(origin)?;
2022 Self::set_force_era(Forcing::ForceAlways);
2023 Ok(())
2024 }
2025
2026 #[pallet::call_index(17)]
2038 #[pallet::weight(T::WeightInfo::cancel_deferred_slash(validator_slashes.len() as u32))]
2039 pub fn cancel_deferred_slash(
2040 origin: OriginFor<T>,
2041 era: EraIndex,
2042 validator_slashes: Vec<(T::AccountId, Perbill)>,
2043 ) -> DispatchResult {
2044 T::AdminOrigin::ensure_origin(origin)?;
2045 ensure!(!validator_slashes.is_empty(), Error::<T>::EmptyTargets);
2046
2047 let mut cancelled_slashes = CancelledSlashes::<T>::get(&era);
2049
2050 for (validator, slash_fraction) in validator_slashes {
2052 cancelled_slashes.retain(|(v, _)| v != &validator);
2057
2058 cancelled_slashes
2060 .try_push((validator.clone(), slash_fraction))
2061 .map_err(|_| Error::<T>::BoundNotMet)
2062 .defensive_proof("cancelled_slashes should have capacity for all validators")?;
2063
2064 Self::deposit_event(Event::<T>::SlashCancelled { slash_era: era, validator });
2065 }
2066
2067 CancelledSlashes::<T>::insert(&era, cancelled_slashes);
2069
2070 Ok(())
2071 }
2072
2073 #[pallet::call_index(18)]
2087 #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked(T::MaxExposurePageSize::get()))]
2088 pub fn payout_stakers(
2089 origin: OriginFor<T>,
2090 validator_stash: T::AccountId,
2091 era: EraIndex,
2092 ) -> DispatchResultWithPostInfo {
2093 ensure_signed(origin)?;
2094
2095 Self::do_payout_stakers(validator_stash, era)
2096 }
2097
2098 #[pallet::call_index(19)]
2102 #[pallet::weight(T::WeightInfo::rebond(T::MaxUnlockingChunks::get() as u32))]
2103 pub fn rebond(
2104 origin: OriginFor<T>,
2105 #[pallet::compact] value: BalanceOf<T>,
2106 ) -> DispatchResultWithPostInfo {
2107 let controller = ensure_signed(origin)?;
2108 let ledger = Self::ledger(Controller(controller))?;
2109
2110 ensure!(!T::Filter::contains(&ledger.stash), Error::<T>::Restricted);
2111 ensure!(!ledger.unlocking.is_empty(), Error::<T>::NoUnlockChunk);
2112
2113 let initial_unlocking = ledger.unlocking.len() as u32;
2114 let (ledger, rebonded_value) = ledger.rebond(value);
2115 ensure!(ledger.active >= Self::min_chilled_bond(), Error::<T>::InsufficientBond);
2117
2118 Self::deposit_event(Event::<T>::Bonded {
2119 stash: ledger.stash.clone(),
2120 amount: rebonded_value,
2121 });
2122
2123 let stash = ledger.stash.clone();
2124 let final_unlocking = ledger.unlocking.len();
2125
2126 ledger.update()?;
2128 if T::VoterList::contains(&stash) {
2129 let _ = T::VoterList::on_update(&stash, Self::weight_of(&stash));
2130 }
2131
2132 let removed_chunks = 1u32 .saturating_add(initial_unlocking)
2134 .saturating_sub(final_unlocking as u32);
2135 Ok(Some(T::WeightInfo::rebond(removed_chunks)).into())
2136 }
2137
2138 #[pallet::call_index(20)]
2157 #[pallet::weight(T::WeightInfo::reap_stash())]
2158 pub fn reap_stash(
2159 origin: OriginFor<T>,
2160 stash: T::AccountId,
2161 _num_slashing_spans: u32,
2162 ) -> DispatchResultWithPostInfo {
2163 let _ = ensure_signed(origin)?;
2164
2165 ensure!(!Self::is_virtual_staker(&stash), Error::<T>::VirtualStakerNotAllowed);
2167
2168 let min_chilled_bond = Self::min_chilled_bond();
2169 let origin_balance = asset::total_balance::<T>(&stash);
2170 let ledger_total =
2171 Self::ledger(Stash(stash.clone())).map(|l| l.total).unwrap_or_default();
2172 let reapable = origin_balance < min_chilled_bond ||
2173 origin_balance.is_zero() ||
2174 ledger_total < min_chilled_bond ||
2175 ledger_total.is_zero();
2176 ensure!(reapable, Error::<T>::FundedTarget);
2177
2178 Self::kill_stash(&stash)?;
2180
2181 Ok(Pays::No.into())
2182 }
2183
2184 #[pallet::call_index(21)]
2196 #[pallet::weight(T::WeightInfo::kick(who.len() as u32))]
2197 pub fn kick(origin: OriginFor<T>, who: Vec<AccountIdLookupOf<T>>) -> DispatchResult {
2198 let controller = ensure_signed(origin)?;
2199 let ledger = Self::ledger(Controller(controller))?;
2200 let stash = &ledger.stash;
2201
2202 for nom_stash in who
2203 .into_iter()
2204 .map(T::Lookup::lookup)
2205 .collect::<Result<Vec<T::AccountId>, _>>()?
2206 .into_iter()
2207 {
2208 Nominators::<T>::mutate(&nom_stash, |maybe_nom| {
2209 if let Some(ref mut nom) = maybe_nom {
2210 if let Some(pos) = nom.targets.iter().position(|v| v == stash) {
2211 nom.targets.swap_remove(pos);
2212 Self::deposit_event(Event::<T>::Kicked {
2213 nominator: nom_stash.clone(),
2214 stash: stash.clone(),
2215 });
2216 }
2217 }
2218 });
2219 }
2220
2221 Ok(())
2222 }
2223
2224 #[pallet::call_index(22)]
2244 #[pallet::weight(
2245 T::WeightInfo::set_staking_configs_all_set()
2246 .max(T::WeightInfo::set_staking_configs_all_remove())
2247 )]
2248 pub fn set_staking_configs(
2249 origin: OriginFor<T>,
2250 min_nominator_bond: ConfigOp<BalanceOf<T>>,
2251 min_validator_bond: ConfigOp<BalanceOf<T>>,
2252 max_nominator_count: ConfigOp<u32>,
2253 max_validator_count: ConfigOp<u32>,
2254 chill_threshold: ConfigOp<Percent>,
2255 min_commission: ConfigOp<Perbill>,
2256 max_staked_rewards: ConfigOp<Percent>,
2257 ) -> DispatchResult {
2258 ensure_root(origin)?;
2259
2260 macro_rules! config_op_exp {
2261 ($storage:ty, $op:ident) => {
2262 match $op {
2263 ConfigOp::Noop => (),
2264 ConfigOp::Set(v) => <$storage>::put(v),
2265 ConfigOp::Remove => <$storage>::kill(),
2266 }
2267 };
2268 }
2269
2270 config_op_exp!(MinNominatorBond<T>, min_nominator_bond);
2271 config_op_exp!(MinValidatorBond<T>, min_validator_bond);
2272 config_op_exp!(MaxNominatorsCount<T>, max_nominator_count);
2273 config_op_exp!(MaxValidatorsCount<T>, max_validator_count);
2274 config_op_exp!(ChillThreshold<T>, chill_threshold);
2275 config_op_exp!(MinCommission<T>, min_commission);
2276 config_op_exp!(MaxStakedRewards<T>, max_staked_rewards);
2277 Ok(())
2278 }
2279 #[pallet::call_index(23)]
2306 #[pallet::weight(T::WeightInfo::chill_other())]
2307 pub fn chill_other(origin: OriginFor<T>, stash: T::AccountId) -> DispatchResult {
2308 let caller = ensure_signed(origin)?;
2310 let ledger = Self::ledger(Stash(stash.clone()))?;
2311 let controller = ledger
2312 .controller()
2313 .defensive_proof(
2314 "Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.",
2315 )
2316 .ok_or(Error::<T>::NotController)?;
2317
2318 if Nominators::<T>::contains_key(&stash) && Nominators::<T>::get(&stash).is_none() {
2335 Self::chill_stash(&stash);
2336 return Ok(());
2337 }
2338
2339 if caller != controller {
2340 let threshold = ChillThreshold::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2341 let min_active_bond = if Nominators::<T>::contains_key(&stash) {
2342 let max_nominator_count =
2343 MaxNominatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2344 let current_nominator_count = Nominators::<T>::count();
2345 ensure!(
2346 threshold * max_nominator_count < current_nominator_count,
2347 Error::<T>::CannotChillOther
2348 );
2349 Self::min_nominator_bond()
2350 } else if Validators::<T>::contains_key(&stash) {
2351 let max_validator_count =
2352 MaxValidatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2353 let current_validator_count = Validators::<T>::count();
2354 ensure!(
2355 threshold * max_validator_count < current_validator_count,
2356 Error::<T>::CannotChillOther
2357 );
2358 Self::min_validator_bond()
2359 } else {
2360 Zero::zero()
2361 };
2362
2363 ensure!(ledger.active < min_active_bond, Error::<T>::CannotChillOther);
2364 }
2365
2366 Self::chill_stash(&stash);
2367 Ok(())
2368 }
2369
2370 #[pallet::call_index(24)]
2374 #[pallet::weight(T::WeightInfo::force_apply_min_commission())]
2375 pub fn force_apply_min_commission(
2376 origin: OriginFor<T>,
2377 validator_stash: T::AccountId,
2378 ) -> DispatchResult {
2379 ensure_signed(origin)?;
2380 let min_commission = MinCommission::<T>::get();
2381 Validators::<T>::try_mutate_exists(validator_stash, |maybe_prefs| {
2382 maybe_prefs
2383 .as_mut()
2384 .map(|prefs| {
2385 (prefs.commission < min_commission)
2386 .then(|| prefs.commission = min_commission)
2387 })
2388 .ok_or(Error::<T>::NotStash)
2389 })?;
2390 Ok(())
2391 }
2392
2393 #[pallet::call_index(25)]
2398 #[pallet::weight(T::WeightInfo::set_min_commission())]
2399 pub fn set_min_commission(origin: OriginFor<T>, new: Perbill) -> DispatchResult {
2400 T::AdminOrigin::ensure_origin(origin)?;
2401 MinCommission::<T>::put(new);
2402 Ok(())
2403 }
2404
2405 #[pallet::call_index(26)]
2423 #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked(T::MaxExposurePageSize::get()))]
2424 pub fn payout_stakers_by_page(
2425 origin: OriginFor<T>,
2426 validator_stash: T::AccountId,
2427 era: EraIndex,
2428 page: Page,
2429 ) -> DispatchResultWithPostInfo {
2430 ensure_signed(origin)?;
2431 Self::do_payout_stakers_by_page(validator_stash, era, page)
2432 }
2433
2434 #[pallet::call_index(27)]
2441 #[pallet::weight(T::WeightInfo::update_payee())]
2442 pub fn update_payee(
2443 origin: OriginFor<T>,
2444 controller: T::AccountId,
2445 ) -> DispatchResultWithPostInfo {
2446 let _ = ensure_signed(origin)?;
2447 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()))?;
2448
2449 ensure!(
2450 (Payee::<T>::get(&ledger.stash) == {
2451 #[allow(deprecated)]
2452 Some(RewardDestination::Controller)
2453 }),
2454 Error::<T>::NotController
2455 );
2456
2457 let _ = ledger
2458 .set_payee(RewardDestination::Account(controller))
2459 .defensive_proof("ledger should have been previously retrieved from storage.")?;
2460
2461 Ok(Pays::No.into())
2462 }
2463
2464 #[pallet::call_index(28)]
2472 #[pallet::weight(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32))]
2473 pub fn deprecate_controller_batch(
2474 origin: OriginFor<T>,
2475 controllers: BoundedVec<T::AccountId, T::MaxControllersInDeprecationBatch>,
2476 ) -> DispatchResultWithPostInfo {
2477 T::AdminOrigin::ensure_origin(origin)?;
2478
2479 let filtered_batch_with_ledger: Vec<_> = controllers
2481 .iter()
2482 .filter_map(|controller| {
2483 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()));
2484 ledger.ok().map_or(None, |ledger| {
2485 let payee_deprecated = Payee::<T>::get(&ledger.stash) == {
2488 #[allow(deprecated)]
2489 Some(RewardDestination::Controller)
2490 };
2491
2492 if ledger.stash != *controller && !payee_deprecated {
2493 Some(ledger)
2494 } else {
2495 None
2496 }
2497 })
2498 })
2499 .collect();
2500
2501 let mut failures = 0;
2503 for ledger in filtered_batch_with_ledger {
2504 let _ = ledger.clone().set_controller_to_stash().map_err(|_| failures += 1);
2505 }
2506 Self::deposit_event(Event::<T>::ControllerBatchDeprecated { failures });
2507
2508 Ok(Some(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32)).into())
2509 }
2510
2511 #[pallet::call_index(29)]
2523 #[pallet::weight(T::WeightInfo::restore_ledger())]
2524 pub fn restore_ledger(
2525 origin: OriginFor<T>,
2526 stash: T::AccountId,
2527 maybe_controller: Option<T::AccountId>,
2528 maybe_total: Option<BalanceOf<T>>,
2529 maybe_unlocking: Option<BoundedVec<UnlockChunk<BalanceOf<T>>, T::MaxUnlockingChunks>>,
2530 ) -> DispatchResult {
2531 T::AdminOrigin::ensure_origin(origin)?;
2532
2533 ensure!(!Self::is_virtual_staker(&stash), Error::<T>::VirtualStakerNotAllowed);
2535
2536 let current_lock = asset::staked::<T>(&stash);
2537 let stash_balance = asset::stakeable_balance::<T>(&stash);
2538
2539 let (new_controller, new_total) = match Self::inspect_bond_state(&stash) {
2540 Ok(LedgerIntegrityState::Corrupted) => {
2541 let new_controller = maybe_controller.unwrap_or(stash.clone());
2542
2543 let new_total = if let Some(total) = maybe_total {
2544 let new_total = total.min(stash_balance);
2545 asset::update_stake::<T>(&stash, new_total)?;
2547 new_total
2548 } else {
2549 current_lock
2550 };
2551
2552 Ok((new_controller, new_total))
2553 },
2554 Ok(LedgerIntegrityState::CorruptedKilled) => {
2555 if current_lock == Zero::zero() {
2556 ensure!(maybe_total.is_some(), Error::<T>::CannotRestoreLedger);
2560 Ok((
2561 stash.clone(),
2562 maybe_total.expect("total exists as per the check above; qed."),
2563 ))
2564 } else {
2565 Ok((stash.clone(), current_lock))
2566 }
2567 },
2568 Ok(LedgerIntegrityState::LockCorrupted) => {
2569 let new_total =
2572 maybe_total.ok_or(Error::<T>::CannotRestoreLedger)?.min(stash_balance);
2573 asset::update_stake::<T>(&stash, new_total)?;
2574
2575 Ok((stash.clone(), new_total))
2576 },
2577 Err(Error::<T>::BadState) => {
2578 asset::kill_stake::<T>(&stash)?;
2580 ensure!(
2581 Self::inspect_bond_state(&stash) == Err(Error::<T>::NotStash),
2582 Error::<T>::BadState
2583 );
2584
2585 return Ok(());
2586 },
2587 Ok(LedgerIntegrityState::Ok) | Err(_) => Err(Error::<T>::CannotRestoreLedger),
2588 }?;
2589
2590 Bonded::<T>::insert(&stash, &new_controller);
2592
2593 let mut ledger = StakingLedger::<T>::new(stash.clone(), new_total);
2595 ledger.controller = Some(new_controller);
2596 ledger.unlocking = maybe_unlocking.unwrap_or_default();
2597 ledger.update()?;
2598
2599 ensure!(
2600 Self::inspect_bond_state(&stash) == Ok(LedgerIntegrityState::Ok),
2601 Error::<T>::BadState
2602 );
2603 Ok(())
2604 }
2605
2606 #[pallet::call_index(30)]
2614 #[pallet::weight(T::WeightInfo::migrate_currency())]
2615 pub fn migrate_currency(
2616 origin: OriginFor<T>,
2617 stash: T::AccountId,
2618 ) -> DispatchResultWithPostInfo {
2619 let _ = ensure_signed(origin)?;
2620 Self::do_migrate_currency(&stash)?;
2621
2622 Ok(Pays::No.into())
2624 }
2625
2626 #[pallet::call_index(31)]
2660 #[pallet::weight(T::WeightInfo::apply_slash())]
2661 pub fn apply_slash(
2662 origin: OriginFor<T>,
2663 slash_era: EraIndex,
2664 slash_key: (T::AccountId, Perbill, u32),
2665 ) -> DispatchResultWithPostInfo {
2666 let _ = ensure_signed(origin)?;
2667 let active_era = ActiveEra::<T>::get().map(|a| a.index).unwrap_or_default();
2668 ensure!(slash_era <= active_era, Error::<T>::EraNotStarted);
2669
2670 ensure!(
2672 !Self::check_slash_cancelled(slash_era, &slash_key.0, slash_key.1),
2673 Error::<T>::CancelledSlash
2674 );
2675
2676 let unapplied_slash = UnappliedSlashes::<T>::take(&slash_era, &slash_key)
2677 .ok_or(Error::<T>::InvalidSlashRecord)?;
2678 slashing::apply_slash::<T>(unapplied_slash, slash_era);
2679
2680 Ok(Pays::No.into())
2681 }
2682
2683 #[pallet::call_index(32)]
2695 #[pallet::weight({
2697 let v = T::MaxValidatorSet::get();
2698 T::WeightInfo::prune_era_stakers_paged(v)
2699 .max(T::WeightInfo::prune_era_stakers_overview(v))
2700 .max(T::WeightInfo::prune_era_validator_prefs(v))
2701 .max(T::WeightInfo::prune_era_claimed_rewards(v))
2702 .max(T::WeightInfo::prune_era_validator_reward())
2703 .max(T::WeightInfo::prune_era_reward_points())
2704 .max(T::WeightInfo::prune_era_total_stake())
2705 })]
2706 pub fn prune_era_step(origin: OriginFor<T>, era: EraIndex) -> DispatchResultWithPostInfo {
2707 let _ = ensure_signed(origin)?;
2708
2709 let active_era = crate::session_rotation::Rotator::<T>::active_era();
2711 let history_depth = T::HistoryDepth::get();
2712 let earliest_prunable_era = active_era.saturating_sub(history_depth).saturating_sub(1);
2713 ensure!(era <= earliest_prunable_era, Error::<T>::EraNotPrunable);
2714
2715 let actual_weight = Self::do_prune_era_step(era)?;
2716
2717 Ok(frame_support::dispatch::PostDispatchInfo {
2718 actual_weight: Some(actual_weight),
2719 pays_fee: frame_support::dispatch::Pays::No,
2720 })
2721 }
2722 }
2723}