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<_>>();
1037
1038 (0..nominators).for_each(|index| {
1039 let derivation = base_derivation.replace("{}", &format!("nominator{}", index));
1040 let who = Self::generate_endowed_bonded_account(&derivation, &mut rng);
1041
1042 let random_nominations = all_validators
1043 .choose_multiple(&mut rng, MaxNominationsOf::<T>::get() as usize)
1044 .map(|v| v.clone())
1045 .collect::<Vec<_>>();
1046
1047 assert_ok!(<Pallet<T>>::nominate(
1048 T::RuntimeOrigin::from(Some(who.clone()).into()),
1049 random_nominations.iter().map(|l| T::Lookup::unlookup(l.clone())).collect(),
1050 ));
1051 })
1052 }
1053
1054 let (active_era, session_index, timestamp) = self.active_era;
1055 ActiveEra::<T>::put(ActiveEraInfo { index: active_era, start: Some(timestamp) });
1056 CurrentEra::<T>::put(active_era);
1058 BondedEras::<T>::put(
1060 BoundedVec::<_, BondedErasBound<T>>::try_from(
1061 alloc::vec![(active_era, session_index)]
1062 )
1063 .expect("bound for BondedEras is BondingDuration + 1; can contain at least one element; qed")
1064 );
1065 }
1066 }
1067
1068 #[pallet::event]
1069 #[pallet::generate_deposit(pub fn deposit_event)]
1070 pub enum Event<T: Config> {
1071 EraPaid {
1074 era_index: EraIndex,
1075 validator_payout: BalanceOf<T>,
1076 remainder: BalanceOf<T>,
1077 },
1078 Rewarded {
1080 stash: T::AccountId,
1081 dest: RewardDestination<T::AccountId>,
1082 amount: BalanceOf<T>,
1083 },
1084 Slashed {
1086 staker: T::AccountId,
1087 amount: BalanceOf<T>,
1088 },
1089 OldSlashingReportDiscarded {
1092 session_index: SessionIndex,
1093 },
1094 Bonded {
1099 stash: T::AccountId,
1100 amount: BalanceOf<T>,
1101 },
1102 Unbonded {
1104 stash: T::AccountId,
1105 amount: BalanceOf<T>,
1106 },
1107 Withdrawn {
1110 stash: T::AccountId,
1111 amount: BalanceOf<T>,
1112 },
1113 StakerRemoved {
1116 stash: T::AccountId,
1117 },
1118 Kicked {
1120 nominator: T::AccountId,
1121 stash: T::AccountId,
1122 },
1123 Chilled {
1125 stash: T::AccountId,
1126 },
1127 PayoutStarted {
1129 era_index: EraIndex,
1130 validator_stash: T::AccountId,
1131 page: Page,
1132 next: Option<Page>,
1133 },
1134 ValidatorPrefsSet {
1136 stash: T::AccountId,
1137 prefs: ValidatorPrefs,
1138 },
1139 SnapshotVotersSizeExceeded {
1141 size: u32,
1142 },
1143 SnapshotTargetsSizeExceeded {
1145 size: u32,
1146 },
1147 ForceEra {
1148 mode: Forcing,
1149 },
1150 ControllerBatchDeprecated {
1152 failures: u32,
1153 },
1154 CurrencyMigrated {
1157 stash: T::AccountId,
1158 force_withdraw: BalanceOf<T>,
1159 },
1160 PagedElectionProceeded {
1170 page: PageIndex,
1171 result: Result<u32, u32>,
1172 },
1173 OffenceReported {
1176 offence_era: EraIndex,
1177 validator: T::AccountId,
1178 fraction: Perbill,
1179 },
1180 SlashComputed {
1182 offence_era: EraIndex,
1183 slash_era: EraIndex,
1184 offender: T::AccountId,
1185 page: u32,
1186 },
1187 SlashCancelled {
1189 slash_era: EraIndex,
1190 validator: T::AccountId,
1191 },
1192 SessionRotated {
1197 starting_session: SessionIndex,
1198 active_era: EraIndex,
1199 planned_era: EraIndex,
1200 },
1201 Unexpected(UnexpectedKind),
1204 OffenceTooOld {
1206 offence_era: EraIndex,
1207 validator: T::AccountId,
1208 fraction: Perbill,
1209 },
1210 EraPruned {
1212 index: EraIndex,
1213 },
1214 }
1215
1216 #[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, TypeInfo, RuntimeDebug)]
1222 pub enum UnexpectedKind {
1223 EraDurationBoundExceeded,
1225 UnknownValidatorActivation,
1227 }
1228
1229 #[pallet::error]
1230 #[derive(PartialEq)]
1231 pub enum Error<T> {
1232 NotController,
1234 NotStash,
1236 AlreadyBonded,
1238 AlreadyPaired,
1240 EmptyTargets,
1242 DuplicateIndex,
1244 InvalidSlashRecord,
1246 InsufficientBond,
1250 NoMoreChunks,
1252 NoUnlockChunk,
1254 FundedTarget,
1256 InvalidEraToReward,
1258 InvalidNumberOfNominations,
1260 AlreadyClaimed,
1262 InvalidPage,
1264 IncorrectHistoryDepth,
1266 BadState,
1268 TooManyTargets,
1270 BadTarget,
1272 CannotChillOther,
1274 TooManyNominators,
1277 TooManyValidators,
1280 CommissionTooLow,
1282 BoundNotMet,
1284 ControllerDeprecated,
1286 CannotRestoreLedger,
1288 RewardDestinationRestricted,
1290 NotEnoughFunds,
1292 VirtualStakerNotAllowed,
1294 CannotReapStash,
1296 AlreadyMigrated,
1298 EraNotStarted,
1300 Restricted,
1303 UnappliedSlashesInPreviousEra,
1306 EraNotPrunable,
1308 CancelledSlash,
1310 }
1311
1312 impl<T: Config> Pallet<T> {
1313 pub fn apply_unapplied_slashes(active_era: EraIndex) -> Weight {
1315 let mut slashes = UnappliedSlashes::<T>::iter_prefix(&active_era).take(1);
1316 if let Some((key, slash)) = slashes.next() {
1317 crate::log!(
1318 debug,
1319 "🦹 found slash {:?} scheduled to be executed in era {:?}",
1320 slash,
1321 active_era,
1322 );
1323
1324 if Self::check_slash_cancelled(active_era, &key.0, key.1) {
1326 crate::log!(
1327 debug,
1328 "🦹 slash for {:?} in era {:?} was cancelled, skipping",
1329 key.0,
1330 active_era,
1331 );
1332 } else {
1333 let offence_era = active_era.saturating_sub(T::SlashDeferDuration::get());
1334 slashing::apply_slash::<T>(slash, offence_era);
1335 }
1336
1337 UnappliedSlashes::<T>::remove(&active_era, &key);
1339
1340 if UnappliedSlashes::<T>::iter_prefix(&active_era).next().is_none() {
1342 CancelledSlashes::<T>::remove(&active_era);
1344 }
1345
1346 T::WeightInfo::apply_slash()
1347 } else {
1348 T::DbWeight::get().reads(1)
1350 }
1351 }
1352
1353 fn do_prune_era_step(era: EraIndex) -> Result<Weight, DispatchError> {
1355 let current_step = EraPruningState::<T>::get(era).ok_or(Error::<T>::EraNotPrunable)?;
1360
1361 let items_limit = T::MaxPruningItems::get().min(T::MaxValidatorSet::get());
1364
1365 let actual_weight = match current_step {
1366 PruningStep::ErasStakersPaged => {
1367 let result = ErasStakersPaged::<T>::clear_prefix((era,), items_limit, None);
1368 let items_deleted = result.backend as u32;
1369 result.maybe_cursor.is_none().then(|| {
1370 EraPruningState::<T>::insert(era, PruningStep::ErasStakersOverview)
1371 });
1372 T::WeightInfo::prune_era_stakers_paged(items_deleted)
1373 },
1374 PruningStep::ErasStakersOverview => {
1375 let result = ErasStakersOverview::<T>::clear_prefix(era, items_limit, None);
1376 let items_deleted = result.backend as u32;
1377 result.maybe_cursor.is_none().then(|| {
1378 EraPruningState::<T>::insert(era, PruningStep::ErasValidatorPrefs)
1379 });
1380 T::WeightInfo::prune_era_stakers_overview(items_deleted)
1381 },
1382 PruningStep::ErasValidatorPrefs => {
1383 let result = ErasValidatorPrefs::<T>::clear_prefix(era, items_limit, None);
1384 let items_deleted = result.backend as u32;
1385 result
1386 .maybe_cursor
1387 .is_none()
1388 .then(|| EraPruningState::<T>::insert(era, PruningStep::ClaimedRewards));
1389 T::WeightInfo::prune_era_validator_prefs(items_deleted)
1390 },
1391 PruningStep::ClaimedRewards => {
1392 let result = ClaimedRewards::<T>::clear_prefix(era, items_limit, None);
1393 let items_deleted = result.backend as u32;
1394 result.maybe_cursor.is_none().then(|| {
1395 EraPruningState::<T>::insert(era, PruningStep::ErasValidatorReward)
1396 });
1397 T::WeightInfo::prune_era_claimed_rewards(items_deleted)
1398 },
1399 PruningStep::ErasValidatorReward => {
1400 ErasValidatorReward::<T>::remove(era);
1401 EraPruningState::<T>::insert(era, PruningStep::ErasRewardPoints);
1402 T::WeightInfo::prune_era_validator_reward()
1403 },
1404 PruningStep::ErasRewardPoints => {
1405 ErasRewardPoints::<T>::remove(era);
1406 EraPruningState::<T>::insert(era, PruningStep::ErasTotalStake);
1407 T::WeightInfo::prune_era_reward_points()
1408 },
1409 PruningStep::ErasTotalStake => {
1410 ErasTotalStake::<T>::remove(era);
1411 EraPruningState::<T>::remove(era);
1413 T::WeightInfo::prune_era_total_stake()
1414 },
1415 };
1416
1417 if EraPruningState::<T>::get(era).is_none() {
1419 Self::deposit_event(Event::<T>::EraPruned { index: era });
1420 }
1421
1422 Ok(actual_weight)
1423 }
1424 }
1425
1426 #[pallet::hooks]
1427 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
1428 fn on_initialize(_now: BlockNumberFor<T>) -> Weight {
1429 let mut consumed_weight = slashing::process_offence::<T>();
1431
1432 consumed_weight.saturating_accrue(T::DbWeight::get().reads(1));
1434 if let Some(active_era) = ActiveEra::<T>::get() {
1435 let slash_weight = Self::apply_unapplied_slashes(active_era.index);
1436 consumed_weight.saturating_accrue(slash_weight);
1437 }
1438
1439 session_rotation::EraElectionPlanner::<T>::maybe_fetch_election_results();
1442 consumed_weight
1443 }
1444
1445 fn integrity_test() {
1446 assert_eq!(
1448 MaxNominationsOf::<T>::get(),
1449 <Self as ElectionDataProvider>::MaxVotesPerVoter::get()
1450 );
1451
1452 assert!(!MaxNominationsOf::<T>::get().is_zero());
1454
1455 assert!(
1456 T::SlashDeferDuration::get() < T::BondingDuration::get() || T::BondingDuration::get() == 0,
1457 "As per documentation, slash defer duration ({}) should be less than bonding duration ({}).",
1458 T::SlashDeferDuration::get(),
1459 T::BondingDuration::get(),
1460 );
1461
1462 assert!(
1464 T::MaxPruningItems::get() >= 100,
1465 "MaxPruningItems must be at least 100 for efficient pruning, got: {}",
1466 T::MaxPruningItems::get()
1467 );
1468 }
1469
1470 #[cfg(feature = "try-runtime")]
1471 fn try_state(n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
1472 Self::do_try_state(n)
1473 }
1474 }
1475
1476 #[pallet::call]
1477 impl<T: Config> Pallet<T> {
1478 #[pallet::call_index(0)]
1491 #[pallet::weight(T::WeightInfo::bond())]
1492 pub fn bond(
1493 origin: OriginFor<T>,
1494 #[pallet::compact] value: BalanceOf<T>,
1495 payee: RewardDestination<T::AccountId>,
1496 ) -> DispatchResult {
1497 let stash = ensure_signed(origin)?;
1498
1499 ensure!(!T::Filter::contains(&stash), Error::<T>::Restricted);
1500
1501 if StakingLedger::<T>::is_bonded(StakingAccount::Stash(stash.clone())) {
1502 return Err(Error::<T>::AlreadyBonded.into());
1503 }
1504
1505 if StakingLedger::<T>::is_bonded(StakingAccount::Controller(stash.clone())) {
1507 return Err(Error::<T>::AlreadyPaired.into());
1508 }
1509
1510 if value < Self::min_chilled_bond() {
1512 return Err(Error::<T>::InsufficientBond.into());
1513 }
1514
1515 let stash_balance = asset::free_to_stake::<T>(&stash);
1516 let value = value.min(stash_balance);
1517 Self::deposit_event(Event::<T>::Bonded { stash: stash.clone(), amount: value });
1518 let ledger = StakingLedger::<T>::new(stash.clone(), value);
1519
1520 ledger.bond(payee)?;
1523
1524 Ok(())
1525 }
1526
1527 #[pallet::call_index(1)]
1538 #[pallet::weight(T::WeightInfo::bond_extra())]
1539 pub fn bond_extra(
1540 origin: OriginFor<T>,
1541 #[pallet::compact] max_additional: BalanceOf<T>,
1542 ) -> DispatchResult {
1543 let stash = ensure_signed(origin)?;
1544 ensure!(!T::Filter::contains(&stash), Error::<T>::Restricted);
1545 Self::do_bond_extra(&stash, max_additional)
1546 }
1547
1548 #[pallet::call_index(2)]
1568 #[pallet::weight(
1569 T::WeightInfo::withdraw_unbonded_kill().saturating_add(T::WeightInfo::unbond()))
1570 ]
1571 pub fn unbond(
1572 origin: OriginFor<T>,
1573 #[pallet::compact] value: BalanceOf<T>,
1574 ) -> DispatchResultWithPostInfo {
1575 let controller = ensure_signed(origin)?;
1576 let unlocking =
1577 Self::ledger(Controller(controller.clone())).map(|l| l.unlocking.len())?;
1578
1579 let maybe_withdraw_weight = {
1582 if unlocking == T::MaxUnlockingChunks::get() as usize {
1583 Some(Self::do_withdraw_unbonded(&controller)?)
1584 } else {
1585 None
1586 }
1587 };
1588
1589 let mut ledger = Self::ledger(Controller(controller))?;
1592 let mut value = value.min(ledger.active);
1593 let stash = ledger.stash.clone();
1594
1595 ensure!(
1596 ledger.unlocking.len() < T::MaxUnlockingChunks::get() as usize,
1597 Error::<T>::NoMoreChunks,
1598 );
1599
1600 if !value.is_zero() {
1601 ledger.active -= value;
1602
1603 if ledger.active < asset::existential_deposit::<T>() {
1605 value += ledger.active;
1606 ledger.active = Zero::zero();
1607 }
1608
1609 let min_active_bond = if Nominators::<T>::contains_key(&stash) {
1610 Self::min_nominator_bond()
1611 } else if Validators::<T>::contains_key(&stash) {
1612 Self::min_validator_bond()
1613 } else {
1614 Zero::zero()
1616 };
1617
1618 ensure!(ledger.active >= min_active_bond, Error::<T>::InsufficientBond);
1621
1622 let era = session_rotation::Rotator::<T>::active_era()
1626 .saturating_add(T::BondingDuration::get());
1627 if let Some(chunk) = ledger.unlocking.last_mut().filter(|chunk| chunk.era == era) {
1628 chunk.value = chunk.value.defensive_saturating_add(value)
1632 } else {
1633 ledger
1634 .unlocking
1635 .try_push(UnlockChunk { value, era })
1636 .map_err(|_| Error::<T>::NoMoreChunks)?;
1637 };
1638 ledger.update()?;
1640
1641 if T::VoterList::contains(&stash) {
1643 let _ = T::VoterList::on_update(&stash, Self::weight_of(&stash));
1644 }
1645
1646 Self::deposit_event(Event::<T>::Unbonded { stash, amount: value });
1647 }
1648
1649 let actual_weight = if let Some(withdraw_weight) = maybe_withdraw_weight {
1650 Some(T::WeightInfo::unbond().saturating_add(withdraw_weight))
1651 } else {
1652 Some(T::WeightInfo::unbond())
1653 };
1654
1655 Ok(actual_weight.into())
1656 }
1657
1658 #[pallet::call_index(3)]
1678 #[pallet::weight(T::WeightInfo::withdraw_unbonded_kill())]
1679 pub fn withdraw_unbonded(
1680 origin: OriginFor<T>,
1681 _num_slashing_spans: u32,
1682 ) -> DispatchResultWithPostInfo {
1683 let controller = ensure_signed(origin)?;
1684
1685 let actual_weight = Self::do_withdraw_unbonded(&controller)?;
1686 Ok(Some(actual_weight).into())
1687 }
1688
1689 #[pallet::call_index(4)]
1695 #[pallet::weight(T::WeightInfo::validate())]
1696 pub fn validate(origin: OriginFor<T>, prefs: ValidatorPrefs) -> DispatchResult {
1697 let controller = ensure_signed(origin)?;
1698
1699 let ledger = Self::ledger(Controller(controller))?;
1700
1701 ensure!(ledger.active >= Self::min_validator_bond(), Error::<T>::InsufficientBond);
1702 let stash = &ledger.stash;
1703
1704 ensure!(prefs.commission >= MinCommission::<T>::get(), Error::<T>::CommissionTooLow);
1706
1707 if !Validators::<T>::contains_key(stash) {
1709 if let Some(max_validators) = MaxValidatorsCount::<T>::get() {
1713 ensure!(
1714 Validators::<T>::count() < max_validators,
1715 Error::<T>::TooManyValidators
1716 );
1717 }
1718 }
1719
1720 Self::do_remove_nominator(stash);
1721 Self::do_add_validator(stash, prefs.clone());
1722 Self::deposit_event(Event::<T>::ValidatorPrefsSet { stash: ledger.stash, prefs });
1723
1724 Ok(())
1725 }
1726
1727 #[pallet::call_index(5)]
1733 #[pallet::weight(T::WeightInfo::nominate(targets.len() as u32))]
1734 pub fn nominate(
1735 origin: OriginFor<T>,
1736 targets: Vec<AccountIdLookupOf<T>>,
1737 ) -> DispatchResult {
1738 let controller = ensure_signed(origin)?;
1739
1740 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()))?;
1741
1742 ensure!(ledger.active >= Self::min_nominator_bond(), Error::<T>::InsufficientBond);
1743 let stash = &ledger.stash;
1744
1745 if !Nominators::<T>::contains_key(stash) {
1747 if let Some(max_nominators) = MaxNominatorsCount::<T>::get() {
1751 ensure!(
1752 Nominators::<T>::count() < max_nominators,
1753 Error::<T>::TooManyNominators
1754 );
1755 }
1756 }
1757
1758 let mut targets = targets
1760 .into_iter()
1761 .map(|t| T::Lookup::lookup(t).map_err(DispatchError::from))
1762 .collect::<Result<Vec<_>, _>>()?;
1763 targets.sort();
1764 targets.dedup();
1765
1766 ensure!(!targets.is_empty(), Error::<T>::EmptyTargets);
1767 ensure!(
1768 targets.len() <= T::NominationsQuota::get_quota(ledger.active) as usize,
1769 Error::<T>::TooManyTargets
1770 );
1771
1772 let old = Nominators::<T>::get(stash).map_or_else(Vec::new, |x| x.targets.into_inner());
1773
1774 let targets: BoundedVec<_, _> = targets
1775 .into_iter()
1776 .map(|n| {
1777 if old.contains(&n) ||
1778 (Validators::<T>::contains_key(&n) && !Validators::<T>::get(&n).blocked)
1779 {
1780 Ok(n)
1781 } else {
1782 Err(Error::<T>::BadTarget.into())
1783 }
1784 })
1785 .collect::<Result<Vec<_>, DispatchError>>()?
1786 .try_into()
1787 .map_err(|_| Error::<T>::TooManyNominators)?;
1788
1789 let nominations = Nominations {
1790 targets,
1791 submitted_in: CurrentEra::<T>::get().unwrap_or(0),
1793 suppressed: false,
1794 };
1795
1796 Self::do_remove_validator(stash);
1797 Self::do_add_nominator(stash, nominations);
1798 Ok(())
1799 }
1800
1801 #[pallet::call_index(6)]
1812 #[pallet::weight(T::WeightInfo::chill())]
1813 pub fn chill(origin: OriginFor<T>) -> DispatchResult {
1814 let controller = ensure_signed(origin)?;
1815
1816 let ledger = Self::ledger(StakingAccount::Controller(controller))?;
1817
1818 Self::chill_stash(&ledger.stash);
1819 Ok(())
1820 }
1821
1822 #[pallet::call_index(7)]
1828 #[pallet::weight(T::WeightInfo::set_payee())]
1829 pub fn set_payee(
1830 origin: OriginFor<T>,
1831 payee: RewardDestination<T::AccountId>,
1832 ) -> DispatchResult {
1833 let controller = ensure_signed(origin)?;
1834 let ledger = Self::ledger(Controller(controller.clone()))?;
1835
1836 ensure!(
1837 (payee != {
1838 #[allow(deprecated)]
1839 RewardDestination::Controller
1840 }),
1841 Error::<T>::ControllerDeprecated
1842 );
1843
1844 let _ = ledger
1845 .set_payee(payee)
1846 .defensive_proof("ledger was retrieved from storage, thus it's bonded; qed.")?;
1847
1848 Ok(())
1849 }
1850
1851 #[pallet::call_index(8)]
1860 #[pallet::weight(T::WeightInfo::set_controller())]
1861 pub fn set_controller(origin: OriginFor<T>) -> DispatchResult {
1862 let stash = ensure_signed(origin)?;
1863
1864 Self::ledger(StakingAccount::Stash(stash.clone())).map(|ledger| {
1865 let controller = ledger.controller()
1866 .defensive_proof("Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.")
1867 .ok_or(Error::<T>::NotController)?;
1868
1869 if controller == stash {
1870 return Err(Error::<T>::AlreadyPaired.into())
1872 }
1873
1874 let _ = ledger.set_controller_to_stash()?;
1875 Ok(())
1876 })?
1877 }
1878
1879 #[pallet::call_index(9)]
1883 #[pallet::weight(T::WeightInfo::set_validator_count())]
1884 pub fn set_validator_count(
1885 origin: OriginFor<T>,
1886 #[pallet::compact] new: u32,
1887 ) -> DispatchResult {
1888 ensure_root(origin)?;
1889
1890 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
1891
1892 ValidatorCount::<T>::put(new);
1893 Ok(())
1894 }
1895
1896 #[pallet::call_index(10)]
1901 #[pallet::weight(T::WeightInfo::set_validator_count())]
1902 pub fn increase_validator_count(
1903 origin: OriginFor<T>,
1904 #[pallet::compact] additional: u32,
1905 ) -> DispatchResult {
1906 ensure_root(origin)?;
1907 let old = ValidatorCount::<T>::get();
1908 let new = old.checked_add(additional).ok_or(ArithmeticError::Overflow)?;
1909
1910 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
1911
1912 ValidatorCount::<T>::put(new);
1913 Ok(())
1914 }
1915
1916 #[pallet::call_index(11)]
1921 #[pallet::weight(T::WeightInfo::set_validator_count())]
1922 pub fn scale_validator_count(origin: OriginFor<T>, factor: Percent) -> DispatchResult {
1923 ensure_root(origin)?;
1924 let old = ValidatorCount::<T>::get();
1925 let new = old.checked_add(factor.mul_floor(old)).ok_or(ArithmeticError::Overflow)?;
1926
1927 ensure!(new <= T::MaxValidatorSet::get(), Error::<T>::TooManyValidators);
1928
1929 ValidatorCount::<T>::put(new);
1930 Ok(())
1931 }
1932
1933 #[pallet::call_index(12)]
1943 #[pallet::weight(T::WeightInfo::force_no_eras())]
1944 pub fn force_no_eras(origin: OriginFor<T>) -> DispatchResult {
1945 ensure_root(origin)?;
1946 Self::set_force_era(Forcing::ForceNone);
1947 Ok(())
1948 }
1949
1950 #[pallet::call_index(13)]
1961 #[pallet::weight(T::WeightInfo::force_new_era())]
1962 pub fn force_new_era(origin: OriginFor<T>) -> DispatchResult {
1963 ensure_root(origin)?;
1964 Self::set_force_era(Forcing::ForceNew);
1965 Ok(())
1966 }
1967
1968 #[pallet::call_index(14)]
1972 #[pallet::weight(T::WeightInfo::set_invulnerables(invulnerables.len() as u32))]
1973 pub fn set_invulnerables(
1974 origin: OriginFor<T>,
1975 invulnerables: Vec<T::AccountId>,
1976 ) -> DispatchResult {
1977 ensure_root(origin)?;
1978 let invulnerables =
1979 BoundedVec::try_from(invulnerables).map_err(|_| Error::<T>::BoundNotMet)?;
1980 <Invulnerables<T>>::put(invulnerables);
1981 Ok(())
1982 }
1983
1984 #[pallet::call_index(15)]
1993 #[pallet::weight(T::WeightInfo::force_unstake())]
1994 pub fn force_unstake(
1995 origin: OriginFor<T>,
1996 stash: T::AccountId,
1997 _num_slashing_spans: u32,
1998 ) -> DispatchResult {
1999 ensure_root(origin)?;
2000
2001 Self::kill_stash(&stash)?;
2003
2004 Ok(())
2005 }
2006
2007 #[pallet::call_index(16)]
2017 #[pallet::weight(T::WeightInfo::force_new_era_always())]
2018 pub fn force_new_era_always(origin: OriginFor<T>) -> DispatchResult {
2019 ensure_root(origin)?;
2020 Self::set_force_era(Forcing::ForceAlways);
2021 Ok(())
2022 }
2023
2024 #[pallet::call_index(17)]
2036 #[pallet::weight(T::WeightInfo::cancel_deferred_slash(validator_slashes.len() as u32))]
2037 pub fn cancel_deferred_slash(
2038 origin: OriginFor<T>,
2039 era: EraIndex,
2040 validator_slashes: Vec<(T::AccountId, Perbill)>,
2041 ) -> DispatchResult {
2042 T::AdminOrigin::ensure_origin(origin)?;
2043 ensure!(!validator_slashes.is_empty(), Error::<T>::EmptyTargets);
2044
2045 let mut cancelled_slashes = CancelledSlashes::<T>::get(&era);
2047
2048 for (validator, slash_fraction) in validator_slashes {
2050 cancelled_slashes.retain(|(v, _)| v != &validator);
2055
2056 cancelled_slashes
2058 .try_push((validator.clone(), slash_fraction))
2059 .map_err(|_| Error::<T>::BoundNotMet)
2060 .defensive_proof("cancelled_slashes should have capacity for all validators")?;
2061
2062 Self::deposit_event(Event::<T>::SlashCancelled { slash_era: era, validator });
2063 }
2064
2065 CancelledSlashes::<T>::insert(&era, cancelled_slashes);
2067
2068 Ok(())
2069 }
2070
2071 #[pallet::call_index(18)]
2085 #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked(T::MaxExposurePageSize::get()))]
2086 pub fn payout_stakers(
2087 origin: OriginFor<T>,
2088 validator_stash: T::AccountId,
2089 era: EraIndex,
2090 ) -> DispatchResultWithPostInfo {
2091 ensure_signed(origin)?;
2092
2093 Self::do_payout_stakers(validator_stash, era)
2094 }
2095
2096 #[pallet::call_index(19)]
2100 #[pallet::weight(T::WeightInfo::rebond(T::MaxUnlockingChunks::get() as u32))]
2101 pub fn rebond(
2102 origin: OriginFor<T>,
2103 #[pallet::compact] value: BalanceOf<T>,
2104 ) -> DispatchResultWithPostInfo {
2105 let controller = ensure_signed(origin)?;
2106 let ledger = Self::ledger(Controller(controller))?;
2107
2108 ensure!(!T::Filter::contains(&ledger.stash), Error::<T>::Restricted);
2109 ensure!(!ledger.unlocking.is_empty(), Error::<T>::NoUnlockChunk);
2110
2111 let initial_unlocking = ledger.unlocking.len() as u32;
2112 let (ledger, rebonded_value) = ledger.rebond(value);
2113 ensure!(ledger.active >= Self::min_chilled_bond(), Error::<T>::InsufficientBond);
2115
2116 Self::deposit_event(Event::<T>::Bonded {
2117 stash: ledger.stash.clone(),
2118 amount: rebonded_value,
2119 });
2120
2121 let stash = ledger.stash.clone();
2122 let final_unlocking = ledger.unlocking.len();
2123
2124 ledger.update()?;
2126 if T::VoterList::contains(&stash) {
2127 let _ = T::VoterList::on_update(&stash, Self::weight_of(&stash));
2128 }
2129
2130 let removed_chunks = 1u32 .saturating_add(initial_unlocking)
2132 .saturating_sub(final_unlocking as u32);
2133 Ok(Some(T::WeightInfo::rebond(removed_chunks)).into())
2134 }
2135
2136 #[pallet::call_index(20)]
2156 #[pallet::weight(T::WeightInfo::reap_stash())]
2157 pub fn reap_stash(
2158 origin: OriginFor<T>,
2159 stash: T::AccountId,
2160 _num_slashing_spans: u32,
2161 ) -> DispatchResultWithPostInfo {
2162 let _ = ensure_signed(origin)?;
2163
2164 ensure!(!Self::is_virtual_staker(&stash), Error::<T>::VirtualStakerNotAllowed);
2166
2167 let min_chilled_bond = Self::min_chilled_bond();
2168 let origin_balance = asset::total_balance::<T>(&stash);
2169 let ledger_total =
2170 Self::ledger(Stash(stash.clone())).map(|l| l.total).unwrap_or_default();
2171 let reapable = origin_balance < min_chilled_bond ||
2172 origin_balance.is_zero() ||
2173 ledger_total < min_chilled_bond ||
2174 ledger_total.is_zero();
2175 ensure!(reapable, Error::<T>::FundedTarget);
2176
2177 Self::kill_stash(&stash)?;
2179
2180 Ok(Pays::No.into())
2181 }
2182
2183 #[pallet::call_index(21)]
2195 #[pallet::weight(T::WeightInfo::kick(who.len() as u32))]
2196 pub fn kick(origin: OriginFor<T>, who: Vec<AccountIdLookupOf<T>>) -> DispatchResult {
2197 let controller = ensure_signed(origin)?;
2198 let ledger = Self::ledger(Controller(controller))?;
2199 let stash = &ledger.stash;
2200
2201 for nom_stash in who
2202 .into_iter()
2203 .map(T::Lookup::lookup)
2204 .collect::<Result<Vec<T::AccountId>, _>>()?
2205 .into_iter()
2206 {
2207 Nominators::<T>::mutate(&nom_stash, |maybe_nom| {
2208 if let Some(ref mut nom) = maybe_nom {
2209 if let Some(pos) = nom.targets.iter().position(|v| v == stash) {
2210 nom.targets.swap_remove(pos);
2211 Self::deposit_event(Event::<T>::Kicked {
2212 nominator: nom_stash.clone(),
2213 stash: stash.clone(),
2214 });
2215 }
2216 }
2217 });
2218 }
2219
2220 Ok(())
2221 }
2222
2223 #[pallet::call_index(22)]
2243 #[pallet::weight(
2244 T::WeightInfo::set_staking_configs_all_set()
2245 .max(T::WeightInfo::set_staking_configs_all_remove())
2246 )]
2247 pub fn set_staking_configs(
2248 origin: OriginFor<T>,
2249 min_nominator_bond: ConfigOp<BalanceOf<T>>,
2250 min_validator_bond: ConfigOp<BalanceOf<T>>,
2251 max_nominator_count: ConfigOp<u32>,
2252 max_validator_count: ConfigOp<u32>,
2253 chill_threshold: ConfigOp<Percent>,
2254 min_commission: ConfigOp<Perbill>,
2255 max_staked_rewards: ConfigOp<Percent>,
2256 ) -> DispatchResult {
2257 ensure_root(origin)?;
2258
2259 macro_rules! config_op_exp {
2260 ($storage:ty, $op:ident) => {
2261 match $op {
2262 ConfigOp::Noop => (),
2263 ConfigOp::Set(v) => <$storage>::put(v),
2264 ConfigOp::Remove => <$storage>::kill(),
2265 }
2266 };
2267 }
2268
2269 config_op_exp!(MinNominatorBond<T>, min_nominator_bond);
2270 config_op_exp!(MinValidatorBond<T>, min_validator_bond);
2271 config_op_exp!(MaxNominatorsCount<T>, max_nominator_count);
2272 config_op_exp!(MaxValidatorsCount<T>, max_validator_count);
2273 config_op_exp!(ChillThreshold<T>, chill_threshold);
2274 config_op_exp!(MinCommission<T>, min_commission);
2275 config_op_exp!(MaxStakedRewards<T>, max_staked_rewards);
2276 Ok(())
2277 }
2278 #[pallet::call_index(23)]
2305 #[pallet::weight(T::WeightInfo::chill_other())]
2306 pub fn chill_other(origin: OriginFor<T>, stash: T::AccountId) -> DispatchResult {
2307 let caller = ensure_signed(origin)?;
2309 let ledger = Self::ledger(Stash(stash.clone()))?;
2310 let controller = ledger
2311 .controller()
2312 .defensive_proof(
2313 "Ledger's controller field didn't exist. The controller should have been fetched using StakingLedger.",
2314 )
2315 .ok_or(Error::<T>::NotController)?;
2316
2317 if Nominators::<T>::contains_key(&stash) && Nominators::<T>::get(&stash).is_none() {
2334 Self::chill_stash(&stash);
2335 return Ok(());
2336 }
2337
2338 if caller != controller {
2339 let threshold = ChillThreshold::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2340 let min_active_bond = if Nominators::<T>::contains_key(&stash) {
2341 let max_nominator_count =
2342 MaxNominatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2343 let current_nominator_count = Nominators::<T>::count();
2344 ensure!(
2345 threshold * max_nominator_count < current_nominator_count,
2346 Error::<T>::CannotChillOther
2347 );
2348 Self::min_nominator_bond()
2349 } else if Validators::<T>::contains_key(&stash) {
2350 let max_validator_count =
2351 MaxValidatorsCount::<T>::get().ok_or(Error::<T>::CannotChillOther)?;
2352 let current_validator_count = Validators::<T>::count();
2353 ensure!(
2354 threshold * max_validator_count < current_validator_count,
2355 Error::<T>::CannotChillOther
2356 );
2357 Self::min_validator_bond()
2358 } else {
2359 Zero::zero()
2360 };
2361
2362 ensure!(ledger.active < min_active_bond, Error::<T>::CannotChillOther);
2363 }
2364
2365 Self::chill_stash(&stash);
2366 Ok(())
2367 }
2368
2369 #[pallet::call_index(24)]
2373 #[pallet::weight(T::WeightInfo::force_apply_min_commission())]
2374 pub fn force_apply_min_commission(
2375 origin: OriginFor<T>,
2376 validator_stash: T::AccountId,
2377 ) -> DispatchResult {
2378 ensure_signed(origin)?;
2379 let min_commission = MinCommission::<T>::get();
2380 Validators::<T>::try_mutate_exists(validator_stash, |maybe_prefs| {
2381 maybe_prefs
2382 .as_mut()
2383 .map(|prefs| {
2384 (prefs.commission < min_commission)
2385 .then(|| prefs.commission = min_commission)
2386 })
2387 .ok_or(Error::<T>::NotStash)
2388 })?;
2389 Ok(())
2390 }
2391
2392 #[pallet::call_index(25)]
2397 #[pallet::weight(T::WeightInfo::set_min_commission())]
2398 pub fn set_min_commission(origin: OriginFor<T>, new: Perbill) -> DispatchResult {
2399 T::AdminOrigin::ensure_origin(origin)?;
2400 MinCommission::<T>::put(new);
2401 Ok(())
2402 }
2403
2404 #[pallet::call_index(26)]
2422 #[pallet::weight(T::WeightInfo::payout_stakers_alive_staked(T::MaxExposurePageSize::get()))]
2423 pub fn payout_stakers_by_page(
2424 origin: OriginFor<T>,
2425 validator_stash: T::AccountId,
2426 era: EraIndex,
2427 page: Page,
2428 ) -> DispatchResultWithPostInfo {
2429 ensure_signed(origin)?;
2430 Self::do_payout_stakers_by_page(validator_stash, era, page)
2431 }
2432
2433 #[pallet::call_index(27)]
2440 #[pallet::weight(T::WeightInfo::update_payee())]
2441 pub fn update_payee(
2442 origin: OriginFor<T>,
2443 controller: T::AccountId,
2444 ) -> DispatchResultWithPostInfo {
2445 let _ = ensure_signed(origin)?;
2446 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()))?;
2447
2448 ensure!(
2449 (Payee::<T>::get(&ledger.stash) == {
2450 #[allow(deprecated)]
2451 Some(RewardDestination::Controller)
2452 }),
2453 Error::<T>::NotController
2454 );
2455
2456 let _ = ledger
2457 .set_payee(RewardDestination::Account(controller))
2458 .defensive_proof("ledger should have been previously retrieved from storage.")?;
2459
2460 Ok(Pays::No.into())
2461 }
2462
2463 #[pallet::call_index(28)]
2471 #[pallet::weight(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32))]
2472 pub fn deprecate_controller_batch(
2473 origin: OriginFor<T>,
2474 controllers: BoundedVec<T::AccountId, T::MaxControllersInDeprecationBatch>,
2475 ) -> DispatchResultWithPostInfo {
2476 T::AdminOrigin::ensure_origin(origin)?;
2477
2478 let filtered_batch_with_ledger: Vec<_> = controllers
2480 .iter()
2481 .filter_map(|controller| {
2482 let ledger = Self::ledger(StakingAccount::Controller(controller.clone()));
2483 ledger.ok().map_or(None, |ledger| {
2484 let payee_deprecated = Payee::<T>::get(&ledger.stash) == {
2487 #[allow(deprecated)]
2488 Some(RewardDestination::Controller)
2489 };
2490
2491 if ledger.stash != *controller && !payee_deprecated {
2492 Some(ledger)
2493 } else {
2494 None
2495 }
2496 })
2497 })
2498 .collect();
2499
2500 let mut failures = 0;
2502 for ledger in filtered_batch_with_ledger {
2503 let _ = ledger.clone().set_controller_to_stash().map_err(|_| failures += 1);
2504 }
2505 Self::deposit_event(Event::<T>::ControllerBatchDeprecated { failures });
2506
2507 Ok(Some(T::WeightInfo::deprecate_controller_batch(controllers.len() as u32)).into())
2508 }
2509
2510 #[pallet::call_index(29)]
2522 #[pallet::weight(T::WeightInfo::restore_ledger())]
2523 pub fn restore_ledger(
2524 origin: OriginFor<T>,
2525 stash: T::AccountId,
2526 maybe_controller: Option<T::AccountId>,
2527 maybe_total: Option<BalanceOf<T>>,
2528 maybe_unlocking: Option<BoundedVec<UnlockChunk<BalanceOf<T>>, T::MaxUnlockingChunks>>,
2529 ) -> DispatchResult {
2530 T::AdminOrigin::ensure_origin(origin)?;
2531
2532 ensure!(!Self::is_virtual_staker(&stash), Error::<T>::VirtualStakerNotAllowed);
2534
2535 let current_lock = asset::staked::<T>(&stash);
2536 let stash_balance = asset::stakeable_balance::<T>(&stash);
2537
2538 let (new_controller, new_total) = match Self::inspect_bond_state(&stash) {
2539 Ok(LedgerIntegrityState::Corrupted) => {
2540 let new_controller = maybe_controller.unwrap_or(stash.clone());
2541
2542 let new_total = if let Some(total) = maybe_total {
2543 let new_total = total.min(stash_balance);
2544 asset::update_stake::<T>(&stash, new_total)?;
2546 new_total
2547 } else {
2548 current_lock
2549 };
2550
2551 Ok((new_controller, new_total))
2552 },
2553 Ok(LedgerIntegrityState::CorruptedKilled) => {
2554 if current_lock == Zero::zero() {
2555 ensure!(maybe_total.is_some(), Error::<T>::CannotRestoreLedger);
2559 Ok((
2560 stash.clone(),
2561 maybe_total.expect("total exists as per the check above; qed."),
2562 ))
2563 } else {
2564 Ok((stash.clone(), current_lock))
2565 }
2566 },
2567 Ok(LedgerIntegrityState::LockCorrupted) => {
2568 let new_total =
2571 maybe_total.ok_or(Error::<T>::CannotRestoreLedger)?.min(stash_balance);
2572 asset::update_stake::<T>(&stash, new_total)?;
2573
2574 Ok((stash.clone(), new_total))
2575 },
2576 Err(Error::<T>::BadState) => {
2577 asset::kill_stake::<T>(&stash)?;
2579 ensure!(
2580 Self::inspect_bond_state(&stash) == Err(Error::<T>::NotStash),
2581 Error::<T>::BadState
2582 );
2583
2584 return Ok(());
2585 },
2586 Ok(LedgerIntegrityState::Ok) | Err(_) => Err(Error::<T>::CannotRestoreLedger),
2587 }?;
2588
2589 Bonded::<T>::insert(&stash, &new_controller);
2591
2592 let mut ledger = StakingLedger::<T>::new(stash.clone(), new_total);
2594 ledger.controller = Some(new_controller);
2595 ledger.unlocking = maybe_unlocking.unwrap_or_default();
2596 ledger.update()?;
2597
2598 ensure!(
2599 Self::inspect_bond_state(&stash) == Ok(LedgerIntegrityState::Ok),
2600 Error::<T>::BadState
2601 );
2602 Ok(())
2603 }
2604
2605 #[pallet::call_index(30)]
2613 #[pallet::weight(T::WeightInfo::migrate_currency())]
2614 pub fn migrate_currency(
2615 origin: OriginFor<T>,
2616 stash: T::AccountId,
2617 ) -> DispatchResultWithPostInfo {
2618 let _ = ensure_signed(origin)?;
2619 Self::do_migrate_currency(&stash)?;
2620
2621 Ok(Pays::No.into())
2623 }
2624
2625 #[pallet::call_index(31)]
2659 #[pallet::weight(T::WeightInfo::apply_slash())]
2660 pub fn apply_slash(
2661 origin: OriginFor<T>,
2662 slash_era: EraIndex,
2663 slash_key: (T::AccountId, Perbill, u32),
2664 ) -> DispatchResultWithPostInfo {
2665 let _ = ensure_signed(origin)?;
2666 let active_era = ActiveEra::<T>::get().map(|a| a.index).unwrap_or_default();
2667 ensure!(slash_era <= active_era, Error::<T>::EraNotStarted);
2668
2669 ensure!(
2671 !Self::check_slash_cancelled(slash_era, &slash_key.0, slash_key.1),
2672 Error::<T>::CancelledSlash
2673 );
2674
2675 let unapplied_slash = UnappliedSlashes::<T>::take(&slash_era, &slash_key)
2676 .ok_or(Error::<T>::InvalidSlashRecord)?;
2677 slashing::apply_slash::<T>(unapplied_slash, slash_era);
2678
2679 Ok(Pays::No.into())
2680 }
2681
2682 #[pallet::call_index(32)]
2694 #[pallet::weight({
2696 let v = T::MaxValidatorSet::get();
2697 T::WeightInfo::prune_era_stakers_paged(v)
2698 .max(T::WeightInfo::prune_era_stakers_overview(v))
2699 .max(T::WeightInfo::prune_era_validator_prefs(v))
2700 .max(T::WeightInfo::prune_era_claimed_rewards(v))
2701 .max(T::WeightInfo::prune_era_validator_reward())
2702 .max(T::WeightInfo::prune_era_reward_points())
2703 .max(T::WeightInfo::prune_era_total_stake())
2704 })]
2705 pub fn prune_era_step(origin: OriginFor<T>, era: EraIndex) -> DispatchResultWithPostInfo {
2706 let _ = ensure_signed(origin)?;
2707
2708 let active_era = crate::session_rotation::Rotator::<T>::active_era();
2710 let history_depth = T::HistoryDepth::get();
2711 let earliest_prunable_era = active_era.saturating_sub(history_depth).saturating_sub(1);
2712 ensure!(era <= earliest_prunable_era, Error::<T>::EraNotPrunable);
2713
2714 let actual_weight = Self::do_prune_era_step(era)?;
2715
2716 Ok(frame_support::dispatch::PostDispatchInfo {
2717 actual_weight: Some(actual_weight),
2718 pays_fee: frame_support::dispatch::Pays::No,
2719 })
2720 }
2721 }
2722}