diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index fe67e1bec2..e2a20ece4d 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -3159,6 +3159,7 @@ dependencies = [ "frame-system", "num-format", "pallet-staking", + "sp-staking", ] [[package]] @@ -6528,6 +6529,7 @@ dependencies = [ "sp-io", "sp-npos-elections", "sp-runtime", + "sp-staking", "sp-std", "sp-tracing", "substrate-test-utils", @@ -11507,6 +11509,7 @@ dependencies = [ name = "sp-staking" version = "4.0.0-dev" dependencies = [ + "impl-trait-for-tuples", "parity-scale-codec", "scale-info", "serde", diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index e1434f0504..058f43dad0 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -38,7 +38,7 @@ use frame_support::{ tokens::{nonfungibles_v2::Inspect, GetSalary, PayFromAccount}, AsEnsureOriginWithArg, ConstBool, ConstU128, ConstU16, ConstU32, Currency, EitherOfDiverse, EqualPrivilegeOnly, Everything, Imbalance, InstanceFilter, KeyOwnerProofSystem, - LockIdentifier, Nothing, OnUnbalanced, U128CurrencyToVote, WithdrawReasons, + LockIdentifier, Nothing, OnUnbalanced, WithdrawReasons, }, weights::{ constants::{ @@ -575,7 +575,7 @@ impl pallet_staking::Config for Runtime { type Currency = Balances; type CurrencyBalance = Balance; type UnixTime = Timestamp; - type CurrencyToVote = U128CurrencyToVote; + type CurrencyToVote = sp_staking::currency_to_vote::U128CurrencyToVote; type RewardRemainder = Treasury; type RuntimeEvent = RuntimeEvent; type Slash = Treasury; // send the slashed funds to the treasury. @@ -600,7 +600,7 @@ impl pallet_staking::Config for Runtime { type TargetList = pallet_staking::UseValidatorsMap; type MaxUnlockingChunks = ConstU32<32>; type HistoryDepth = HistoryDepth; - type OnStakerSlash = NominationPools; + type EventListeners = NominationPools; type WeightInfo = pallet_staking::weights::SubstrateWeight; type BenchmarkingConfig = StakingBenchmarkingConfig; } @@ -1047,7 +1047,7 @@ impl pallet_elections_phragmen::Config for Runtime { // NOTE: this implies that council's genesis members cannot be set directly and must come from // this module. type InitializeMembers = Council; - type CurrencyToVote = U128CurrencyToVote; + type CurrencyToVote = sp_staking::currency_to_vote::U128CurrencyToVote; type CandidacyBond = CandidacyBond; type VotingBondBase = VotingBondBase; type VotingBondFactor = VotingBondFactor; diff --git a/substrate/frame/babe/src/mock.rs b/substrate/frame/babe/src/mock.rs index c0ccc9a8ac..ccc24050d5 100644 --- a/substrate/frame/babe/src/mock.rs +++ b/substrate/frame/babe/src/mock.rs @@ -182,7 +182,7 @@ impl onchain::Config for OnChainSeqPhragmen { impl pallet_staking::Config for Test { type MaxNominations = ConstU32<16>; type RewardRemainder = (); - type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; + type CurrencyToVote = (); type RuntimeEvent = RuntimeEvent; type Currency = Balances; type CurrencyBalance = ::Balance; @@ -204,7 +204,7 @@ impl pallet_staking::Config for Test { type TargetList = pallet_staking::UseValidatorsMap; type MaxUnlockingChunks = ConstU32<32>; type HistoryDepth = ConstU32<84>; - type OnStakerSlash = (); + type EventListeners = (); type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; type WeightInfo = (); } diff --git a/substrate/frame/beefy/src/mock.rs b/substrate/frame/beefy/src/mock.rs index 7edf4d3397..270097e346 100644 --- a/substrate/frame/beefy/src/mock.rs +++ b/substrate/frame/beefy/src/mock.rs @@ -206,7 +206,7 @@ impl onchain::Config for OnChainSeqPhragmen { impl pallet_staking::Config for Test { type MaxNominations = ConstU32<16>; type RewardRemainder = (); - type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; + type CurrencyToVote = (); type RuntimeEvent = RuntimeEvent; type Currency = Balances; type CurrencyBalance = ::Balance; @@ -228,7 +228,7 @@ impl pallet_staking::Config for Test { type TargetList = pallet_staking::UseValidatorsMap; type MaxUnlockingChunks = ConstU32<32>; type HistoryDepth = ConstU32<84>; - type OnStakerSlash = (); + type EventListeners = (); type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; type WeightInfo = (); } diff --git a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs index da7ccf6dce..8c641d6137 100644 --- a/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs +++ b/substrate/frame/election-provider-multi-phase/test-staking-e2e/src/mock.rs @@ -265,7 +265,7 @@ impl pallet_staking::Config for Runtime { type Currency = Balances; type CurrencyBalance = Balance; type UnixTime = Timestamp; - type CurrencyToVote = traits::SaturatingCurrencyToVote; + type CurrencyToVote = (); type RewardRemainder = (); type RuntimeEvent = RuntimeEvent; type Slash = (); // burn slashes @@ -285,7 +285,7 @@ impl pallet_staking::Config for Runtime { type TargetList = pallet_staking::UseValidatorsMap; type MaxUnlockingChunks = ConstU32<32>; type HistoryDepth = HistoryDepth; - type OnStakerSlash = (); + type EventListeners = (); type WeightInfo = pallet_staking::weights::SubstrateWeight; type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; } diff --git a/substrate/frame/elections-phragmen/Cargo.toml b/substrate/frame/elections-phragmen/Cargo.toml index 20ca29fff6..9b26e1e367 100644 --- a/substrate/frame/elections-phragmen/Cargo.toml +++ b/substrate/frame/elections-phragmen/Cargo.toml @@ -26,6 +26,7 @@ sp-io = { version = "23.0.0", default-features = false, path = "../../primitives sp-npos-elections = { version = "4.0.0-dev", default-features = false, path = "../../primitives/npos-elections" } sp-runtime = { version = "24.0.0", default-features = false, path = "../../primitives/runtime" } sp-std = { version = "8.0.0", default-features = false, path = "../../primitives/std" } +sp-staking = { default-features = false, path = "../../primitives/staking" } [dev-dependencies] pallet-balances = { version = "4.0.0-dev", path = "../balances" } diff --git a/substrate/frame/elections-phragmen/src/lib.rs b/substrate/frame/elections-phragmen/src/lib.rs index ee8bcc1de5..7a4fb61970 100644 --- a/substrate/frame/elections-phragmen/src/lib.rs +++ b/substrate/frame/elections-phragmen/src/lib.rs @@ -101,9 +101,9 @@ use codec::{Decode, Encode}; use frame_support::{ traits::{ - defensive_prelude::*, ChangeMembers, Contains, ContainsLengthBound, Currency, - CurrencyToVote, Get, InitializeMembers, LockIdentifier, LockableCurrency, OnUnbalanced, - ReservableCurrency, SortedMembers, WithdrawReasons, + defensive_prelude::*, ChangeMembers, Contains, ContainsLengthBound, Currency, Get, + InitializeMembers, LockIdentifier, LockableCurrency, OnUnbalanced, ReservableCurrency, + SortedMembers, WithdrawReasons, }, weights::Weight, }; @@ -113,6 +113,7 @@ use sp_runtime::{ traits::{Saturating, StaticLookup, Zero}, DispatchError, Perbill, RuntimeDebug, }; +use sp_staking::currency_to_vote::CurrencyToVote; use sp_std::{cmp::Ordering, prelude::*}; #[cfg(any(feature = "try-runtime", test))] @@ -1424,7 +1425,7 @@ mod tests { type PalletId = ElectionsPhragmenPalletId; type RuntimeEvent = RuntimeEvent; type Currency = Balances; - type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; + type CurrencyToVote = (); type ChangeMembers = TestChangeMembers; type InitializeMembers = (); type CandidacyBond = CandidacyBond; diff --git a/substrate/frame/fast-unstake/src/mock.rs b/substrate/frame/fast-unstake/src/mock.rs index 101ad90881..ecdbad9ff7 100644 --- a/substrate/frame/fast-unstake/src/mock.rs +++ b/substrate/frame/fast-unstake/src/mock.rs @@ -137,7 +137,7 @@ impl pallet_staking::Config for Runtime { type Currency = Balances; type CurrencyBalance = Balance; type UnixTime = pallet_timestamp::Pallet; - type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; + type CurrencyToVote = (); type RewardRemainder = (); type RuntimeEvent = RuntimeEvent; type Slash = (); @@ -157,7 +157,7 @@ impl pallet_staking::Config for Runtime { type VoterList = pallet_staking::UseNominatorsAndValidatorsMap; type TargetList = pallet_staking::UseValidatorsMap; type MaxUnlockingChunks = ConstU32<32>; - type OnStakerSlash = (); + type EventListeners = (); type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; type WeightInfo = (); } diff --git a/substrate/frame/grandpa/src/mock.rs b/substrate/frame/grandpa/src/mock.rs index df012ab9dc..3a13385d35 100644 --- a/substrate/frame/grandpa/src/mock.rs +++ b/substrate/frame/grandpa/src/mock.rs @@ -187,7 +187,7 @@ impl onchain::Config for OnChainSeqPhragmen { impl pallet_staking::Config for Test { type MaxNominations = ConstU32<16>; type RewardRemainder = (); - type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; + type CurrencyToVote = (); type RuntimeEvent = RuntimeEvent; type Currency = Balances; type CurrencyBalance = ::Balance; @@ -209,7 +209,7 @@ impl pallet_staking::Config for Test { type TargetList = pallet_staking::UseValidatorsMap; type MaxUnlockingChunks = ConstU32<32>; type HistoryDepth = ConstU32<84>; - type OnStakerSlash = (); + type EventListeners = (); type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; type WeightInfo = (); } diff --git a/substrate/frame/nomination-pools/benchmarking/src/mock.rs b/substrate/frame/nomination-pools/benchmarking/src/mock.rs index d94c63d1bf..62655690a2 100644 --- a/substrate/frame/nomination-pools/benchmarking/src/mock.rs +++ b/substrate/frame/nomination-pools/benchmarking/src/mock.rs @@ -99,7 +99,7 @@ impl pallet_staking::Config for Runtime { type Currency = Balances; type CurrencyBalance = Balance; type UnixTime = pallet_timestamp::Pallet; - type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; + type CurrencyToVote = (); type RewardRemainder = (); type RuntimeEvent = RuntimeEvent; type Slash = (); @@ -120,7 +120,7 @@ impl pallet_staking::Config for Runtime { type TargetList = pallet_staking::UseValidatorsMap; type MaxUnlockingChunks = ConstU32<32>; type HistoryDepth = ConstU32<84>; - type OnStakerSlash = Pools; + type EventListeners = Pools; type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; type WeightInfo = (); } diff --git a/substrate/frame/nomination-pools/src/lib.rs b/substrate/frame/nomination-pools/src/lib.rs index 8518ca58ba..a356ba529a 100644 --- a/substrate/frame/nomination-pools/src/lib.rs +++ b/substrate/frame/nomination-pools/src/lib.rs @@ -326,7 +326,7 @@ //! //! This section assumes that the slash computation is executed by //! `pallet_staking::StakingLedger::slash`, which passes the information to this pallet via -//! [`sp_staking::OnStakerSlash::on_slash`]. +//! [`sp_staking::OnStakingUpdate::on_slash`]. //! //! Unbonding pools need to be slashed to ensure all nominators whom where in the bonded pool while //! it was backing a validator that equivocated are punished. Without these measures a member could @@ -341,10 +341,6 @@ //! in addition to the unbonding pools. For maintenance simplicity these are not implemented. //! Related: //! -//! **Relevant methods:** -//! -//! * [`Pallet::on_slash`] -//! //! ### Limitations //! //! * PoolMembers cannot vote with their staked funds because they are transferred into the pools @@ -375,7 +371,7 @@ use sp_runtime::{ }, FixedPointNumber, Perbill, }; -use sp_staking::{EraIndex, OnStakerSlash, StakingInterface}; +use sp_staking::{EraIndex, StakingInterface}; use sp_std::{collections::btree_map::BTreeMap, fmt::Debug, ops::Div, vec::Vec}; #[cfg(any(feature = "try-runtime", feature = "fuzzing", test, debug_assertions))] @@ -3265,7 +3261,7 @@ impl Pallet { } } -impl OnStakerSlash> for Pallet { +impl sp_staking::OnStakingUpdate> for Pallet { fn on_slash( pool_account: &T::AccountId, // Bonded balance is always read directly from staking, therefore we don't need to update diff --git a/substrate/frame/nomination-pools/src/mock.rs b/substrate/frame/nomination-pools/src/mock.rs index f0b73bbea2..0a40460349 100644 --- a/substrate/frame/nomination-pools/src/mock.rs +++ b/substrate/frame/nomination-pools/src/mock.rs @@ -47,6 +47,7 @@ impl StakingMock { impl sp_staking::StakingInterface for StakingMock { type Balance = Balance; type AccountId = AccountId; + type CurrencyToVote = (); fn minimum_nominator_bond() -> Self::Balance { StakingMinBond::get() diff --git a/substrate/frame/nomination-pools/test-staking/src/mock.rs b/substrate/frame/nomination-pools/test-staking/src/mock.rs index a9c6450856..6d73615c80 100644 --- a/substrate/frame/nomination-pools/test-staking/src/mock.rs +++ b/substrate/frame/nomination-pools/test-staking/src/mock.rs @@ -113,7 +113,7 @@ impl pallet_staking::Config for Runtime { type Currency = Balances; type CurrencyBalance = Balance; type UnixTime = pallet_timestamp::Pallet; - type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; + type CurrencyToVote = (); type RewardRemainder = (); type RuntimeEvent = RuntimeEvent; type Slash = (); @@ -134,7 +134,7 @@ impl pallet_staking::Config for Runtime { type TargetList = pallet_staking::UseValidatorsMap; type MaxUnlockingChunks = ConstU32<32>; type HistoryDepth = ConstU32<84>; - type OnStakerSlash = Pools; + type EventListeners = Pools; type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; type WeightInfo = (); } diff --git a/substrate/frame/offences/benchmarking/src/mock.rs b/substrate/frame/offences/benchmarking/src/mock.rs index 6bb3b9d7f4..d6c1f95117 100644 --- a/substrate/frame/offences/benchmarking/src/mock.rs +++ b/substrate/frame/offences/benchmarking/src/mock.rs @@ -162,7 +162,7 @@ impl pallet_staking::Config for Test { type Currency = Balances; type CurrencyBalance = ::Balance; type UnixTime = pallet_timestamp::Pallet; - type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; + type CurrencyToVote = (); type RewardRemainder = (); type RuntimeEvent = RuntimeEvent; type Slash = (); @@ -182,7 +182,7 @@ impl pallet_staking::Config for Test { type TargetList = pallet_staking::UseValidatorsMap; type MaxUnlockingChunks = ConstU32<32>; type HistoryDepth = ConstU32<84>; - type OnStakerSlash = (); + type EventListeners = (); type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; type WeightInfo = (); } diff --git a/substrate/frame/root-offences/src/mock.rs b/substrate/frame/root-offences/src/mock.rs index 8c48e34e4e..65fd788e05 100644 --- a/substrate/frame/root-offences/src/mock.rs +++ b/substrate/frame/root-offences/src/mock.rs @@ -149,17 +149,6 @@ impl onchain::Config for OnChainSeqPhragmen { type TargetsBound = ConstU32<{ u32::MAX }>; } -pub struct OnStakerSlashMock(core::marker::PhantomData); -impl sp_staking::OnStakerSlash for OnStakerSlashMock { - fn on_slash( - _pool_account: &AccountId, - slashed_bonded: Balance, - slashed_chunks: &BTreeMap, - ) { - LedgerSlashPerEra::set((slashed_bonded, slashed_chunks.clone())); - } -} - parameter_types! { pub const RewardCurve: &'static PiecewiseLinear<'static> = &REWARD_CURVE; pub static Offset: BlockNumber = 0; @@ -176,7 +165,7 @@ impl pallet_staking::Config for Test { type Currency = Balances; type CurrencyBalance = ::Balance; type UnixTime = Timestamp; - type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; + type CurrencyToVote = (); type RewardRemainder = (); type RuntimeEvent = RuntimeEvent; type Slash = (); @@ -196,7 +185,7 @@ impl pallet_staking::Config for Test { type MaxUnlockingChunks = ConstU32<32>; type HistoryDepth = ConstU32<84>; type VoterList = pallet_staking::UseNominatorsAndValidatorsMap; - type OnStakerSlash = OnStakerSlashMock; + type EventListeners = (); type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; type WeightInfo = (); } diff --git a/substrate/frame/session/benchmarking/src/mock.rs b/substrate/frame/session/benchmarking/src/mock.rs index 3b027492e0..d4866a4607 100644 --- a/substrate/frame/session/benchmarking/src/mock.rs +++ b/substrate/frame/session/benchmarking/src/mock.rs @@ -164,7 +164,7 @@ impl pallet_staking::Config for Test { type Currency = Balances; type CurrencyBalance = ::Balance; type UnixTime = pallet_timestamp::Pallet; - type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; + type CurrencyToVote = (); type RewardRemainder = (); type RuntimeEvent = RuntimeEvent; type Slash = (); @@ -184,7 +184,7 @@ impl pallet_staking::Config for Test { type HistoryDepth = ConstU32<84>; type VoterList = pallet_staking::UseNominatorsAndValidatorsMap; type TargetList = pallet_staking::UseValidatorsMap; - type OnStakerSlash = (); + type EventListeners = (); type BenchmarkingConfig = pallet_staking::TestBenchmarkingConfig; type WeightInfo = (); } diff --git a/substrate/frame/staking/src/benchmarking.rs b/substrate/frame/staking/src/benchmarking.rs index 53589ecfe4..8d6eb74590 100644 --- a/substrate/frame/staking/src/benchmarking.rs +++ b/substrate/frame/staking/src/benchmarking.rs @@ -26,13 +26,13 @@ use frame_election_provider_support::SortedListProvider; use frame_support::{ dispatch::UnfilteredDispatchable, pallet_prelude::*, - traits::{Currency, CurrencyToVote, Get, Imbalance}, + traits::{Currency, Get, Imbalance}, }; use sp_runtime::{ traits::{Bounded, One, StaticLookup, TrailingZeroInput, Zero}, Perbill, Percent, }; -use sp_staking::SessionIndex; +use sp_staking::{currency_to_vote::CurrencyToVote, SessionIndex}; use sp_std::prelude::*; pub use frame_benchmarking::v1::{ diff --git a/substrate/frame/staking/src/lib.rs b/substrate/frame/staking/src/lib.rs index 344c4ecb4f..99622a64f8 100644 --- a/substrate/frame/staking/src/lib.rs +++ b/substrate/frame/staking/src/lib.rs @@ -314,7 +314,7 @@ use sp_runtime::{ pub use sp_staking::StakerStatus; use sp_staking::{ offence::{Offence, OffenceError, ReportOffence}, - EraIndex, SessionIndex, + EraIndex, OnStakingUpdate, SessionIndex, }; use sp_std::{collections::btree_map::BTreeMap, prelude::*}; pub use weights::WeightInfo; @@ -549,7 +549,7 @@ impl StakingLedger { /// /// `slash_era` is the era in which the slash (which is being enacted now) actually happened. /// - /// This calls `Config::OnStakerSlash::on_slash` with information as to how the slash was + /// This calls `Config::OnStakingUpdate::on_slash` with information as to how the slash was /// applied. pub fn slash( &mut self, @@ -562,7 +562,6 @@ impl StakingLedger { } use sp_runtime::PerThing as _; - use sp_staking::OnStakerSlash as _; let mut remaining_slash = slash_amount; let pre_slash_total = self.total; @@ -667,7 +666,7 @@ impl StakingLedger { // clean unlocking chunks that are set to zero. self.unlocking.retain(|c| !c.value.is_zero()); - T::OnStakerSlash::on_slash(&self.stash, self.active, &slashed_unlocking); + T::EventListeners::on_slash(&self.stash, self.active, &slashed_unlocking); pre_slash_total.saturating_sub(self.total) } } diff --git a/substrate/frame/staking/src/mock.rs b/substrate/frame/staking/src/mock.rs index f9af9f5003..19529ca087 100644 --- a/substrate/frame/staking/src/mock.rs +++ b/substrate/frame/staking/src/mock.rs @@ -236,7 +236,6 @@ parameter_types! { pub static HistoryDepth: u32 = 80; pub static MaxUnlockingChunks: u32 = 32; pub static RewardOnUnbalanceWasCalled: bool = false; - pub static LedgerSlashPerEra: (BalanceOf, BTreeMap>) = (Zero::zero(), BTreeMap::new()); pub static MaxWinners: u32 = 100; } @@ -268,8 +267,14 @@ impl OnUnbalanced> for MockReward { } } -pub struct OnStakerSlashMock(core::marker::PhantomData); -impl sp_staking::OnStakerSlash for OnStakerSlashMock { +parameter_types! { + pub static LedgerSlashPerEra: + (BalanceOf, BTreeMap>) = + (Zero::zero(), BTreeMap::new()); +} + +pub struct EventListenerMock; +impl OnStakingUpdate for EventListenerMock { fn on_slash( _pool_account: &AccountId, slashed_bonded: Balance, @@ -284,7 +289,7 @@ impl crate::pallet::pallet::Config for Test { type Currency = Balances; type CurrencyBalance = ::Balance; type UnixTime = Timestamp; - type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote; + type CurrencyToVote = (); type RewardRemainder = RewardRemainderMock; type RuntimeEvent = RuntimeEvent; type Slash = (); @@ -305,7 +310,7 @@ impl crate::pallet::pallet::Config for Test { type TargetList = UseValidatorsMap; type MaxUnlockingChunks = MaxUnlockingChunks; type HistoryDepth = HistoryDepth; - type OnStakerSlash = OnStakerSlashMock; + type EventListeners = EventListenerMock; type BenchmarkingConfig = TestBenchmarkingConfig; type WeightInfo = (); } diff --git a/substrate/frame/staking/src/pallet/impls.rs b/substrate/frame/staking/src/pallet/impls.rs index 44d5674f2f..5166608a97 100644 --- a/substrate/frame/staking/src/pallet/impls.rs +++ b/substrate/frame/staking/src/pallet/impls.rs @@ -26,8 +26,8 @@ use frame_support::{ dispatch::WithPostDispatchInfo, pallet_prelude::*, traits::{ - Currency, CurrencyToVote, Defensive, DefensiveResult, EstimateNextNewSession, Get, - Imbalance, LockableCurrency, OnUnbalanced, TryCollect, UnixTime, WithdrawReasons, + Currency, Defensive, DefensiveResult, EstimateNextNewSession, Get, Imbalance, + LockableCurrency, OnUnbalanced, TryCollect, UnixTime, WithdrawReasons, }, weights::Weight, }; @@ -38,6 +38,7 @@ use sp_runtime::{ Perbill, }; use sp_staking::{ + currency_to_vote::CurrencyToVote, offence::{DisableStrategy, OffenceDetails, OnOffenceHandler}, EraIndex, SessionIndex, Stake, StakingInterface, }; @@ -1568,10 +1569,10 @@ impl SortedListProvider for UseNominatorsAndValidatorsM } } -// NOTE: in this entire impl block, the assumption is that `who` is a stash account. impl StakingInterface for Pallet { type AccountId = T::AccountId; type Balance = BalanceOf; + type CurrencyToVote = T::CurrencyToVote; fn minimum_nominator_bond() -> Self::Balance { MinNominatorBond::::get() diff --git a/substrate/frame/staking/src/pallet/mod.rs b/substrate/frame/staking/src/pallet/mod.rs index bff51312e7..0163d6543f 100644 --- a/substrate/frame/staking/src/pallet/mod.rs +++ b/substrate/frame/staking/src/pallet/mod.rs @@ -24,7 +24,7 @@ use frame_support::{ dispatch::Codec, pallet_prelude::*, traits::{ - Currency, CurrencyToVote, Defensive, DefensiveResult, DefensiveSaturating, EnsureOrigin, + Currency, Defensive, DefensiveResult, DefensiveSaturating, EnsureOrigin, EstimateNextNewSession, Get, LockIdentifier, LockableCurrency, OnUnbalanced, TryCollect, UnixTime, }, @@ -113,7 +113,7 @@ pub mod pallet { /// in 128. /// Consequently, the backward convert is used convert the u128s from sp-elections back to a /// [`BalanceOf`]. - type CurrencyToVote: CurrencyToVote>; + type CurrencyToVote: sp_staking::currency_to_vote::CurrencyToVote>; /// Something that provides the election functionality. type ElectionProvider: ElectionProvider< @@ -261,9 +261,11 @@ pub mod pallet { #[pallet::constant] type MaxUnlockingChunks: Get; - /// A hook called when any staker is slashed. Mostly likely this can be a no-op unless - /// other pallets exist that are affected by slashing per-staker. - type OnStakerSlash: sp_staking::OnStakerSlash>; + /// Something that listens to staking updates and performs actions based on the data it + /// receives. + /// + /// WARNING: this only reports slashing events for the time being. + type EventListeners: sp_staking::OnStakingUpdate>; /// Some parameters of the benchmarking. type BenchmarkingConfig: BenchmarkingConfig; diff --git a/substrate/frame/support/src/traits.rs b/substrate/frame/support/src/traits.rs index 5b34a77df4..f34a0b5d87 100644 --- a/substrate/frame/support/src/traits.rs +++ b/substrate/frame/support/src/traits.rs @@ -102,10 +102,7 @@ pub use dispatch::{ }; mod voting; -pub use voting::{ - ClassCountOf, CurrencyToVote, PollStatus, Polling, SaturatingCurrencyToVote, - U128CurrencyToVote, VoteTally, -}; +pub use voting::{ClassCountOf, PollStatus, Polling, VoteTally}; mod preimages; pub use preimages::{Bounded, BoundedInline, FetchResult, Hash, QueryPreimage, StorePreimage}; diff --git a/substrate/frame/support/src/traits/voting.rs b/substrate/frame/support/src/traits/voting.rs index caec472785..4201b8d48d 100644 --- a/substrate/frame/support/src/traits/voting.rs +++ b/substrate/frame/support/src/traits/voting.rs @@ -20,81 +20,10 @@ use crate::dispatch::{DispatchError, Parameter}; use codec::{HasCompact, MaxEncodedLen}; -use sp_arithmetic::{ - traits::{SaturatedConversion, UniqueSaturatedFrom, UniqueSaturatedInto}, - Perbill, -}; +use sp_arithmetic::Perbill; use sp_runtime::traits::Member; use sp_std::prelude::*; -/// A trait similar to `Convert` to convert values from `B` an abstract balance type -/// into u64 and back from u128. (This conversion is used in election and other places where complex -/// calculation over balance type is needed) -/// -/// Total issuance of the currency is passed in, but an implementation of this trait may or may not -/// use it. -/// -/// # WARNING -/// -/// the total issuance being passed in implies that the implementation must be aware of the fact -/// that its values can affect the outcome. This implies that if the vote value is dependent on the -/// total issuance, it should never ber written to storage for later re-use. -pub trait CurrencyToVote { - /// Convert balance to u64. - fn to_vote(value: B, issuance: B) -> u64; - - /// Convert u128 to balance. - fn to_currency(value: u128, issuance: B) -> B; -} - -/// An implementation of `CurrencyToVote` tailored for chain's that have a balance type of u128. -/// -/// The factor is the `(total_issuance / u64::MAX).max(1)`, represented as u64. Let's look at the -/// important cases: -/// -/// If the chain's total issuance is less than u64::MAX, this will always be 1, which means that -/// the factor will not have any effect. In this case, any account's balance is also less. Thus, -/// both of the conversions are basically an `as`; Any balance can fit in u64. -/// -/// If the chain's total issuance is more than 2*u64::MAX, then a factor might be multiplied and -/// divided upon conversion. -pub struct U128CurrencyToVote; - -impl U128CurrencyToVote { - fn factor(issuance: u128) -> u128 { - (issuance / u64::MAX as u128).max(1) - } -} - -impl CurrencyToVote for U128CurrencyToVote { - fn to_vote(value: u128, issuance: u128) -> u64 { - (value / Self::factor(issuance)).saturated_into() - } - - fn to_currency(value: u128, issuance: u128) -> u128 { - value.saturating_mul(Self::factor(issuance)) - } -} - -/// A naive implementation of `CurrencyConvert` that simply saturates all conversions. -/// -/// # Warning -/// -/// This is designed to be used mostly for testing. Use with care, and think about the consequences. -pub struct SaturatingCurrencyToVote; - -impl + UniqueSaturatedFrom> CurrencyToVote - for SaturatingCurrencyToVote -{ - fn to_vote(value: B, _: B) -> u64 { - value.unique_saturated_into() - } - - fn to_currency(value: u128, _: B) -> B { - B::unique_saturated_from(value) - } -} - pub trait VoteTally { fn new(_: Class) -> Self; fn ayes(&self, class: Class) -> Votes; diff --git a/substrate/primitives/staking/Cargo.toml b/substrate/primitives/staking/Cargo.toml index 0191a34491..c6aee3cbcd 100644 --- a/substrate/primitives/staking/Cargo.toml +++ b/substrate/primitives/staking/Cargo.toml @@ -16,9 +16,11 @@ targets = ["x86_64-unknown-linux-gnu"] serde = { version = "1.0.163", default-features = false, features = ["derive", "alloc"], optional = true } codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive"] } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } -sp-core = { version = "21.0.0", default-features = false, path = "../core" } -sp-runtime = { version = "24.0.0", default-features = false, path = "../runtime" } -sp-std = { version = "8.0.0", default-features = false, path = "../std" } +impl-trait-for-tuples = "0.2.2" + +sp-core = { default-features = false, path = "../core" } +sp-runtime = { default-features = false, path = "../runtime" } +sp-std = { default-features = false, path = "../std" } [features] default = ["std"] diff --git a/substrate/primitives/staking/src/currency_to_vote.rs b/substrate/primitives/staking/src/currency_to_vote.rs new file mode 100644 index 0000000000..556e5bd210 --- /dev/null +++ b/substrate/primitives/staking/src/currency_to_vote.rs @@ -0,0 +1,101 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use sp_runtime::{ + traits::{UniqueSaturatedFrom, UniqueSaturatedInto}, + SaturatedConversion, +}; + +/// A trait similar to `Convert` to convert values from `B` an abstract balance type +/// into u64 and back from u128. (This conversion is used in election and other places where complex +/// calculation over balance type is needed) +/// +/// Total issuance of the currency is passed in, but an implementation of this trait may or may not +/// use it. +/// +/// # WARNING +/// +/// the total issuance being passed in implies that the implementation must be aware of the fact +/// that its values can affect the outcome. This implies that if the vote value is dependent on the +/// total issuance, it should never ber written to storage for later re-use. +pub trait CurrencyToVote { + /// Convert balance to u64. + fn to_vote(value: B, issuance: B) -> u64; + + /// Convert u128 to balance. + fn to_currency(value: u128, issuance: B) -> B; +} + +/// An implementation of `CurrencyToVote` tailored for chain's that have a balance type of u128. +/// +/// The factor is the `(total_issuance / u64::MAX).max(1)`, represented as u64. Let's look at the +/// important cases: +/// +/// If the chain's total issuance is less than u64::MAX, this will always be 1, which means that +/// the factor will not have any effect. In this case, any account's balance is also less. Thus, +/// both of the conversions are basically an `as`; Any balance can fit in u64. +/// +/// If the chain's total issuance is more than 2*u64::MAX, then a factor might be multiplied and +/// divided upon conversion. +pub struct U128CurrencyToVote; + +impl U128CurrencyToVote { + fn factor(issuance: u128) -> u128 { + (issuance / u64::MAX as u128).max(1) + } +} + +impl CurrencyToVote for U128CurrencyToVote { + fn to_vote(value: u128, issuance: u128) -> u64 { + (value / Self::factor(issuance)).saturated_into() + } + + fn to_currency(value: u128, issuance: u128) -> u128 { + value.saturating_mul(Self::factor(issuance)) + } +} + +/// A naive implementation of `CurrencyConvert` that simply saturates all conversions. +/// +/// # Warning +/// +/// This is designed to be used mostly for testing. Use with care, and think about the consequences. +pub struct SaturatingCurrencyToVote; + +impl + UniqueSaturatedFrom> CurrencyToVote + for SaturatingCurrencyToVote +{ + fn to_vote(value: B, _: B) -> u64 { + value.unique_saturated_into() + } + + fn to_currency(value: u128, _: B) -> B { + B::unique_saturated_from(value) + } +} + +#[cfg(feature = "std")] +impl + UniqueSaturatedFrom> CurrencyToVote for () { + fn to_vote(value: B, issuance: B) -> u64 { + SaturatingCurrencyToVote::to_vote(value, issuance) + } + + /// Convert u128 to balance. + fn to_currency(value: u128, issuance: B) -> B { + SaturatingCurrencyToVote::to_currency(value, issuance) + } +} diff --git a/substrate/primitives/staking/src/lib.rs b/substrate/primitives/staking/src/lib.rs index 2fd5c6acc6..1621af164b 100644 --- a/substrate/primitives/staking/src/lib.rs +++ b/substrate/primitives/staking/src/lib.rs @@ -20,13 +20,17 @@ //! A crate which contains primitives that are useful for implementation that uses staking //! approaches in general. Definitions related to sessions, slashing, etc go here. +use crate::currency_to_vote::CurrencyToVote; +use codec::{FullCodec, MaxEncodedLen}; use scale_info::TypeInfo; use sp_core::RuntimeDebug; -use sp_runtime::{DispatchError, DispatchResult}; -use sp_std::{collections::btree_map::BTreeMap, vec::Vec}; +use sp_runtime::{DispatchError, DispatchResult, Saturating}; +use sp_std::{collections::btree_map::BTreeMap, ops::Sub, vec::Vec}; pub mod offence; +pub mod currency_to_vote; + /// Simple index type with which we can count sessions. pub type SessionIndex = u32; @@ -45,32 +49,9 @@ pub enum StakerStatus { Nominator(Vec), } -/// Trait describing something that implements a hook for any operations to perform when a staker is -/// slashed. -pub trait OnStakerSlash { - /// A hook for any operations to perform when a staker is slashed. - /// - /// # Arguments - /// - /// * `stash` - The stash of the staker whom the slash was applied to. - /// * `slashed_active` - The new bonded balance of the staker after the slash was applied. - /// * `slashed_unlocking` - A map of slashed eras, and the balance of that unlocking chunk after - /// the slash is applied. Any era not present in the map is not affected at all. - fn on_slash( - stash: &AccountId, - slashed_active: Balance, - slashed_unlocking: &BTreeMap, - ); -} - -impl OnStakerSlash for () { - fn on_slash(_: &AccountId, _: Balance, _: &BTreeMap) { - // Nothing to do here - } -} - /// A struct that reflects stake that an account has in the staking system. Provides a set of /// methods to operate on it's properties. Aimed at making `StakingInterface` more concise. +#[derive(RuntimeDebug, Clone, Copy, Eq, PartialEq, Default)] pub struct Stake { /// The total stake that `stash` has in the staking system. This includes the /// `active` stake, and any funds currently in the process of unbonding via @@ -87,16 +68,88 @@ pub struct Stake { pub active: Balance, } +/// A generic staking event listener. +/// +/// Note that the interface is designed in a way that the events are fired post-action, so any +/// pre-action data that is needed needs to be passed to interface methods. The rest of the data can +/// be retrieved by using `StakingInterface`. +#[impl_trait_for_tuples::impl_for_tuples(10)] +pub trait OnStakingUpdate { + /// Fired when the stake amount of someone updates. + /// + /// This is effectively any changes to the bond amount, such as bonding more funds, and + /// unbonding. + fn on_stake_update(_who: &AccountId, _prev_stake: Option>) {} + + /// Fired when someone sets their intention to nominate. + /// + /// This should never be fired for existing nominators. + fn on_nominator_add(_who: &AccountId) {} + + /// Fired when an existing nominator updates their nominations. + /// + /// Note that this is not fired when a nominator changes their stake. For that, + /// `on_stake_update` should be used, followed by querying whether `who` was a validator or a + /// nominator. + fn on_nominator_update(_who: &AccountId, _prev_nominations: Vec) {} + + /// Fired when someone removes their intention to nominate, either due to chill or validating. + /// + /// The set of nominations at the time of removal is provided as it can no longer be fetched in + /// any way. + fn on_nominator_remove(_who: &AccountId, _nominations: Vec) {} + + /// Fired when someone sets their intention to validate. + /// + /// Note validator preference changes are not communicated, but could be added if needed. + fn on_validator_add(_who: &AccountId) {} + + /// Fired when an existing validator updates their preferences. + /// + /// Note validator preference changes are not communicated, but could be added if needed. + fn on_validator_update(_who: &AccountId) {} + + /// Fired when someone removes their intention to validate, either due to chill or nominating. + fn on_validator_remove(_who: &AccountId) {} + + /// Fired when someone is fully unstaked. + fn on_unstake(_who: &AccountId) {} + + /// Fired when a staker is slashed. + /// + /// * `stash` - The stash of the staker whom the slash was applied to. + /// * `slashed_active` - The new bonded balance of the staker after the slash was applied. + /// * `slashed_unlocking` - A map of slashed eras, and the balance of that unlocking chunk after + /// the slash is applied. Any era not present in the map is not affected at all. + fn on_slash( + _stash: &AccountId, + _slashed_active: Balance, + _slashed_unlocking: &BTreeMap, + ) { + } +} + /// A generic representation of a staking implementation. /// /// This interface uses the terminology of NPoS, but it is aims to be generic enough to cover other /// implementations as well. pub trait StakingInterface { /// Balance type used by the staking system. - type Balance: PartialEq; + type Balance: Sub + + Ord + + PartialEq + + Default + + Copy + + MaxEncodedLen + + FullCodec + + TypeInfo + + Saturating; - /// AccountId type used by the staking system - type AccountId; + /// AccountId type used by the staking system. + type AccountId: Clone + sp_std::fmt::Debug; + + /// Means of converting Currency to VoteWeight. + type CurrencyToVote: CurrencyToVote; /// The minimum amount required to bond in order to set nomination intentions. This does not /// necessarily mean the nomination will be counted in an election, but instead just enough to @@ -195,8 +248,12 @@ pub trait StakingInterface { /// Return the status of the given staker, `None` if not staked at all. fn status(who: &Self::AccountId) -> Result, DispatchError>; + /// Checks whether or not this is a validator account. + fn is_validator(who: &Self::AccountId) -> bool { + Self::status(who).map(|s| matches!(s, StakerStatus::Validator)).unwrap_or(false) + } + /// Get the nominations of a stash, if they are a nominator, `None` otherwise. - #[cfg(feature = "runtime-benchmarks")] fn nominations(who: &Self::AccountId) -> Option> { match Self::status(who) { Ok(StakerStatus::Nominator(t)) => Some(t), diff --git a/substrate/utils/frame/generate-bags/Cargo.toml b/substrate/utils/frame/generate-bags/Cargo.toml index e47c838440..e8d7d51ead 100644 --- a/substrate/utils/frame/generate-bags/Cargo.toml +++ b/substrate/utils/frame/generate-bags/Cargo.toml @@ -14,6 +14,7 @@ frame-support = { version = "4.0.0-dev", path = "../../../frame/support" } frame-election-provider-support = { version = "4.0.0-dev", path = "../../../frame/election-provider-support" } frame-system = { version = "4.0.0-dev", path = "../../../frame/system" } pallet-staking = { version = "4.0.0-dev", path = "../../../frame/staking" } +sp-staking = { version = "4.0.0-dev", path = "../../../primitives/staking" } # third party chrono = { version = "0.4.19" } diff --git a/substrate/utils/frame/generate-bags/src/lib.rs b/substrate/utils/frame/generate-bags/src/lib.rs index fda78692cb..923017261a 100644 --- a/substrate/utils/frame/generate-bags/src/lib.rs +++ b/substrate/utils/frame/generate-bags/src/lib.rs @@ -18,8 +18,7 @@ //! Support code to ease the process of generating bag thresholds. //! //! NOTE: this assume the runtime implements [`pallet_staking::Config`], as it requires an -//! implementation of the traits [`frame_support::traits::Currency`] and -//! [`frame_support::traits::CurrencyToVote`]. +//! implementation of the traits [`frame_support::traits::Currency`] and `CurrencyToVote`. //! //! The process of adding bags to a runtime requires only four steps. //! @@ -70,7 +69,7 @@ fn existential_weight( total_issuance: u128, minimum_balance: u128, ) -> VoteWeight { - use frame_support::traits::CurrencyToVote; + use sp_staking::currency_to_vote::CurrencyToVote; T::CurrencyToVote::to_vote( minimum_balance