// This file is part of Substrate. // Copyright (C) 2017-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. //! # Staking Pallet //! //! The Staking pallet is used to manage funds at stake by network maintainers. //! //! - [`Config`] //! - [`Call`] //! - [`Pallet`] //! //! ## Overview //! //! The Staking pallet is the means by which a set of network maintainers (known as _authorities_ in //! some contexts and _validators_ in others) are chosen based upon those who voluntarily place //! funds under deposit. Under deposit, those funds are rewarded under normal operation but are held //! at pain of _slash_ (expropriation) should the staked maintainer be found not to be discharging //! its duties properly. //! //! ### Terminology //! //! //! - Staking: The process of locking up funds for some time, placing them at risk of slashing //! (loss) in order to become a rewarded maintainer of the network. //! - Validating: The process of running a node to actively maintain the network, either by //! producing blocks or guaranteeing finality of the chain. //! - Nominating: The process of placing staked funds behind one or more validators in order to //! share in any reward, and punishment, they take. //! - Stash account: The account holding an owner's funds used for staking. //! - Controller account: The account that controls an owner's funds for staking. //! - Era: A (whole) number of sessions, which is the period that the validator set (and each //! validator's active nominator set) is recalculated and where rewards are paid out. //! - Slash: The punishment of a staker by reducing its funds. //! //! ### Goals //! //! //! The staking system in Substrate NPoS is designed to make the following possible: //! //! - Stake funds that are controlled by a cold wallet. //! - Withdraw some, or deposit more, funds without interrupting the role of an entity. //! - Switch between roles (nominator, validator, idle) with minimal overhead. //! //! ### Scenarios //! //! #### Staking //! //! Almost any interaction with the Staking pallet requires a process of _**bonding**_ (also known //! as being a _staker_). To become *bonded*, a fund-holding register known as the _stash account_, //! which holds some or all of the funds that become frozen in place as part of the staking process, //! is paired with an active **controller** account, which issues instructions on how they shall be //! used. //! //! An account pair can become bonded using the [`bond`](Call::bond) call. //! //! Stash accounts can change their associated controller using the //! [`set_controller`](Call::set_controller) call. //! //! There are three possible roles that any staked account pair can be in: `Validator`, `Nominator` //! and `Idle` (defined in [`StakerStatus`]). There are three //! corresponding instructions to change between roles, namely: //! [`validate`](Call::validate), //! [`nominate`](Call::nominate), and [`chill`](Call::chill). //! //! #### Validating //! //! A **validator** takes the role of either validating blocks or ensuring their finality, //! maintaining the veracity of the network. A validator should avoid both any sort of malicious //! misbehavior and going offline. Bonded accounts that state interest in being a validator do NOT //! get immediately chosen as a validator. Instead, they are declared as a _candidate_ and they //! _might_ get elected at the _next era_ as a validator. The result of the election is determined //! by nominators and their votes. //! //! An account can become a validator candidate via the //! [`validate`](Call::validate) call. //! //! #### Nomination //! //! A **nominator** does not take any _direct_ role in maintaining the network, instead, it votes on //! a set of validators to be elected. Once interest in nomination is stated by an account, it //! takes effect at the next election round. The funds in the nominator's stash account indicate the //! _weight_ of its vote. Both the rewards and any punishment that a validator earns are shared //! between the validator and its nominators. This rule incentivizes the nominators to NOT vote for //! the misbehaving/offline validators as much as possible, simply because the nominators will also //! lose funds if they vote poorly. //! //! An account can become a nominator via the [`nominate`](Call::nominate) call. //! //! #### Voting //! //! Staking is closely related to elections; actual validators are chosen from among all potential //! validators via election by the potential validators and nominators. To reduce use of the phrase //! "potential validators and nominators", we often use the term **voters**, who are simply //! the union of potential validators and nominators. //! //! #### Rewards and Slash //! //! The **reward and slashing** procedure is the core of the Staking pallet, attempting to _embrace //! valid behavior_ while _punishing any misbehavior or lack of availability_. //! //! Rewards must be claimed for each era before it gets too old by `$HISTORY_DEPTH` using the //! `payout_stakers` call. Any account can call `payout_stakers`, which pays the reward to the //! validator as well as its nominators. Only the [`Config::MaxNominatorRewardedPerValidator`] //! biggest stakers can claim their reward. This is to limit the i/o cost to mutate storage for each //! nominator's account. //! //! Slashing can occur at any point in time, once misbehavior is reported. Once slashing is //! determined, a value is deducted from the balance of the validator and all the nominators who //! voted for this validator (values are deducted from the _stash_ account of the slashed entity). //! //! Slashing logic is further described in the documentation of the `slashing` pallet. //! //! Similar to slashing, rewards are also shared among a validator and its associated nominators. //! Yet, the reward funds are not always transferred to the stash account and can be configured. See //! [Reward Calculation](#reward-calculation) for more details. //! //! #### Chilling //! //! Finally, any of the roles above can choose to step back temporarily and just chill for a while. //! This means that if they are a nominator, they will not be considered as voters anymore and if //! they are validators, they will no longer be a candidate for the next election. //! //! An account can step back via the [`chill`](Call::chill) call. //! //! ### Session managing //! //! The pallet implement the trait `SessionManager`. Which is the only API to query new validator //! set and allowing these validator set to be rewarded once their era is ended. //! //! ## Interface //! //! ### Dispatchable Functions //! //! The dispatchable functions of the Staking pallet enable the steps needed for entities to accept //! and change their role, alongside some helper functions to get/set the metadata of the pallet. //! //! ### Public Functions //! //! The Staking pallet contains many public storage items and (im)mutable functions. //! //! ## Usage //! //! ### Example: Rewarding a validator by id. //! //! ``` //! use pallet_staking::{self as staking}; //! //! #[frame_support::pallet] //! pub mod pallet { //! use super::*; //! use frame_support::pallet_prelude::*; //! use frame_system::pallet_prelude::*; //! //! #[pallet::pallet] //! pub struct Pallet(_); //! //! #[pallet::config] //! pub trait Config: frame_system::Config + staking::Config {} //! //! #[pallet::call] //! impl Pallet { //! /// Reward a validator. //! #[pallet::weight(0)] //! pub fn reward_myself(origin: OriginFor) -> DispatchResult { //! let reported = ensure_signed(origin)?; //! >::reward_by_ids(vec![(reported, 10)]); //! Ok(()) //! } //! } //! } //! # fn main() { } //! ``` //! //! ## Implementation Details //! //! ### Era payout //! //! The era payout is computed using yearly inflation curve defined at //! [`Config::EraPayout`] as such: //! //! ```nocompile //! staker_payout = yearly_inflation(npos_token_staked / total_tokens) * total_tokens / era_per_year //! ``` //! This payout is used to reward stakers as defined in next section //! //! ```nocompile //! remaining_payout = max_yearly_inflation * total_tokens / era_per_year - staker_payout //! ``` //! The remaining reward is send to the configurable end-point //! [`Config::RewardRemainder`]. //! //! ### Reward Calculation //! //! Validators and nominators are rewarded at the end of each era. The total reward of an era is //! calculated using the era duration and the staking rate (the total amount of tokens staked by //! nominators and validators, divided by the total token supply). It aims to incentivize toward a //! defined staking rate. The full specification can be found //! [here](https://research.web3.foundation/en/latest/polkadot/Token%20Economics.html#inflation-model). //! //! Total reward is split among validators and their nominators depending on the number of points //! they received during the era. Points are added to a validator using //! [`reward_by_ids`](Pallet::reward_by_ids). //! //! [`Pallet`] implements //! [`pallet_authorship::EventHandler`] to add reward //! points to block producer and block producer of referenced uncles. //! //! The validator and its nominator split their reward as following: //! //! The validator can declare an amount, named [`commission`](ValidatorPrefs::commission), that does //! not get shared with the nominators at each reward payout through its [`ValidatorPrefs`]. This //! value gets deducted from the total reward that is paid to the validator and its nominators. The //! remaining portion is split pro rata among the validator and the top //! [`Config::MaxNominatorRewardedPerValidator`] nominators that nominated the validator, //! proportional to the value staked behind the validator (_i.e._ dividing the //! [`own`](Exposure::own) or [`others`](Exposure::others) by [`total`](Exposure::total) in //! [`Exposure`]). Note that the pro rata division of rewards uses the total exposure behind the //! validator, *not* just the exposure of the validator and the top //! [`Config::MaxNominatorRewardedPerValidator`] nominators. //! //! All entities who receive a reward have the option to choose their reward destination through the //! [`Payee`] storage item (see //! [`set_payee`](Call::set_payee)), to be one of the following: //! //! - Controller account, (obviously) not increasing the staked value. //! - Stash account, not increasing the staked value. //! - Stash account, also increasing the staked value. //! //! ### Additional Fund Management Operations //! //! Any funds already placed into stash can be the target of the following operations: //! //! The controller account can free a portion (or all) of the funds using the //! [`unbond`](Call::unbond) call. Note that the funds are not immediately //! accessible. Instead, a duration denoted by //! [`Config::BondingDuration`] (in number of eras) must //! pass until the funds can actually be removed. Once the `BondingDuration` is over, the //! [`withdraw_unbonded`](Call::withdraw_unbonded) call can be used to actually //! withdraw the funds. //! //! Note that there is a limitation to the number of fund-chunks that can be scheduled to be //! unlocked in the future via [`unbond`](Call::unbond). In case this maximum //! (`MAX_UNLOCKING_CHUNKS`) is reached, the bonded account _must_ first wait until a successful //! call to `withdraw_unbonded` to remove some of the chunks. //! //! ### Election Algorithm //! //! The current election algorithm is implemented based on Phragmén. The reference implementation //! can be found [here](https://github.com/w3f/consensus/tree/master/NPoS). //! //! The election algorithm, aside from electing the validators with the most stake value and votes, //! tries to divide the nominator votes among candidates in an equal manner. To further assure this, //! an optional post-processing can be applied that iteratively normalizes the nominator staked //! values until the total difference among votes of a particular nominator are less than a //! threshold. //! //! ## GenesisConfig //! //! The Staking pallet depends on the [`GenesisConfig`]. The //! `GenesisConfig` is optional and allow to set some initial stakers. //! //! ## Related Modules //! //! - [Balances](../pallet_balances/index.html): Used to manage values at stake. //! - [Session](../pallet_session/index.html): Used to manage sessions. Also, a list of new //! validators is stored in the Session pallet's `Validators` at the end of each era. #![cfg_attr(not(feature = "std"), no_std)] #![recursion_limit = "256"] #[cfg(feature = "runtime-benchmarks")] pub mod benchmarking; #[cfg(any(feature = "runtime-benchmarks", test))] pub mod testing_utils; #[cfg(test)] pub(crate) mod mock; #[cfg(test)] mod tests; pub mod inflation; pub mod migrations; pub mod slashing; pub mod weights; mod pallet; use codec::{Decode, Encode, HasCompact}; use frame_support::{ traits::{ConstU32, Currency, Get}, weights::Weight, BoundedVec, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound, }; use scale_info::TypeInfo; use sp_runtime::{ curve::PiecewiseLinear, traits::{AtLeast32BitUnsigned, Convert, Saturating, Zero}, Perbill, RuntimeDebug, }; use sp_staking::{ offence::{Offence, OffenceError, ReportOffence}, EraIndex, SessionIndex, }; use sp_std::{collections::btree_map::BTreeMap, convert::From, prelude::*}; pub use weights::WeightInfo; pub use pallet::{pallet::*, *}; pub(crate) const LOG_TARGET: &'static str = "runtime::staking"; // syntactic sugar for logging. #[macro_export] macro_rules! log { ($level:tt, $patter:expr $(, $values:expr)* $(,)?) => { log::$level!( target: crate::LOG_TARGET, concat!("[{:?}] 💸 ", $patter), >::block_number() $(, $values)* ) }; } /// Counter for the number of "reward" points earned by a given validator. pub type RewardPoint = u32; /// The balance type of this pallet. pub type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; type PositiveImbalanceOf = <::Currency as Currency< ::AccountId, >>::PositiveImbalance; type NegativeImbalanceOf = <::Currency as Currency< ::AccountId, >>::NegativeImbalance; /// Information regarding the active era (era in used in session). #[derive(Encode, Decode, RuntimeDebug, TypeInfo)] pub struct ActiveEraInfo { /// Index of era. pub index: EraIndex, /// Moment of start expressed as millisecond from `$UNIX_EPOCH`. /// /// Start can be none if start hasn't been set for the era yet, /// Start is set on the first on_finalize of the era to guarantee usage of `Time`. start: Option, } /// Reward points of an era. Used to split era total payout between validators. /// /// This points will be used to reward validators and their respective nominators. #[derive(PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct EraRewardPoints { /// Total number of points. Equals the sum of reward points for each validator. total: RewardPoint, /// The reward points earned by a given validator. individual: BTreeMap, } impl Default for EraRewardPoints { fn default() -> Self { EraRewardPoints { total: Default::default(), individual: BTreeMap::new() } } } /// Indicates the initial status of the staker. #[derive(RuntimeDebug, TypeInfo)] #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize, Clone))] pub enum StakerStatus { /// Chilling. Idle, /// Declared desire in validating or already participating in it. Validator, /// Nominating for a group of other stakers. Nominator(Vec), } /// A destination account for payment. #[derive(PartialEq, Eq, Copy, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] pub enum RewardDestination { /// Pay into the stash account, increasing the amount at stake accordingly. Staked, /// Pay into the stash account, not increasing the amount at stake. Stash, /// Pay into the controller account. Controller, /// Pay into a specified account. Account(AccountId), /// Receive no reward. None, } impl Default for RewardDestination { fn default() -> Self { RewardDestination::Staked } } /// Preference of what happens regarding validation. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo, Default)] pub struct ValidatorPrefs { /// Reward that validator takes up-front; only the rest is split between themselves and /// nominators. #[codec(compact)] pub commission: Perbill, /// Whether or not this validator is accepting more nominations. If `true`, then no nominator /// who is not already nominating this validator may nominate them. By default, validators /// are accepting nominations. pub blocked: bool, } /// Just a Balance/BlockNumber tuple to encode when a chunk of funds will be unlocked. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct UnlockChunk { /// Amount of funds to be unlocked. #[codec(compact)] value: Balance, /// Era number at which point it'll be unlocked. #[codec(compact)] era: EraIndex, } /// The ledger of a (bonded) stash. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct StakingLedger { /// The stash account whose balance is actually locked and at stake. pub stash: AccountId, /// The total amount of the stash's balance that we are currently accounting for. /// It's just `active` plus all the `unlocking` balances. #[codec(compact)] pub total: Balance, /// The total amount of the stash's balance that will be at stake in any forthcoming /// rounds. #[codec(compact)] pub active: Balance, /// Any balance that is becoming free, which may eventually be transferred out /// of the stash (assuming it doesn't get slashed first). pub unlocking: Vec>, /// List of eras for which the stakers behind a validator have claimed rewards. Only updated /// for validators. pub claimed_rewards: Vec, } impl StakingLedger { /// Initializes the default object using the given `validator`. pub fn default_from(stash: AccountId) -> Self { Self { stash, total: Zero::zero(), active: Zero::zero(), unlocking: vec![], claimed_rewards: vec![], } } /// Remove entries from `unlocking` that are sufficiently old and reduce the /// total by the sum of their balances. fn consolidate_unlocked(self, current_era: EraIndex) -> Self { let mut total = self.total; let unlocking = self .unlocking .into_iter() .filter(|chunk| { if chunk.era > current_era { true } else { total = total.saturating_sub(chunk.value); false } }) .collect(); Self { stash: self.stash, total, active: self.active, unlocking, claimed_rewards: self.claimed_rewards, } } /// Re-bond funds that were scheduled for unlocking. /// /// Returns the updated ledger, and the amount actually rebonded. fn rebond(mut self, value: Balance) -> (Self, Balance) { let mut unlocking_balance: Balance = Zero::zero(); while let Some(last) = self.unlocking.last_mut() { if unlocking_balance + last.value <= value { unlocking_balance += last.value; self.active += last.value; self.unlocking.pop(); } else { let diff = value - unlocking_balance; unlocking_balance += diff; self.active += diff; last.value -= diff; } if unlocking_balance >= value { break } } (self, unlocking_balance) } } impl StakingLedger where Balance: AtLeast32BitUnsigned + Saturating + Copy, { /// Slash the validator for a given amount of balance. This can grow the value /// of the slash in the case that the validator has less than `minimum_balance` /// active funds. Returns the amount of funds actually slashed. /// /// Slashes from `active` funds first, and then `unlocking`, starting with the /// chunks that are closest to unlocking. fn slash(&mut self, mut value: Balance, minimum_balance: Balance) -> Balance { let pre_total = self.total; let total = &mut self.total; let active = &mut self.active; let slash_out_of = |total_remaining: &mut Balance, target: &mut Balance, value: &mut Balance| { let mut slash_from_target = (*value).min(*target); if !slash_from_target.is_zero() { *target -= slash_from_target; // Don't leave a dust balance in the staking system. if *target <= minimum_balance { slash_from_target += *target; *value += sp_std::mem::replace(target, Zero::zero()); } *total_remaining = total_remaining.saturating_sub(slash_from_target); *value -= slash_from_target; } }; slash_out_of(total, active, &mut value); let i = self .unlocking .iter_mut() .map(|chunk| { slash_out_of(total, &mut chunk.value, &mut value); chunk.value }) .take_while(|value| value.is_zero()) // Take all fully-consumed chunks out. .count(); // Kill all drained chunks. let _ = self.unlocking.drain(..i); pre_total.saturating_sub(*total) } } /// A record of the nominations made by a specific account. #[derive(PartialEqNoBound, EqNoBound, Clone, Encode, Decode, RuntimeDebugNoBound, TypeInfo)] #[codec(mel_bound())] #[scale_info(skip_type_params(T))] pub struct Nominations { /// The targets of nomination. pub targets: BoundedVec, /// The era the nominations were submitted. /// /// Except for initial nominations which are considered submitted at era 0. pub submitted_in: EraIndex, /// Whether the nominations have been suppressed. This can happen due to slashing of the /// validators, or other events that might invalidate the nomination. /// /// NOTE: this for future proofing and is thus far not used. pub suppressed: bool, } /// The amount of exposure (to slashing) than an individual nominator has. #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct IndividualExposure { /// The stash account of the nominator in question. pub who: AccountId, /// Amount of funds exposed. #[codec(compact)] pub value: Balance, } /// A snapshot of the stake backing a single validator in the system. #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct Exposure { /// The total balance backing this validator. #[codec(compact)] pub total: Balance, /// The validator's own stash that is exposed. #[codec(compact)] pub own: Balance, /// The portions of nominators stashes that are exposed. pub others: Vec>, } impl Default for Exposure { fn default() -> Self { Self { total: Default::default(), own: Default::default(), others: vec![] } } } /// A pending slash record. The value of the slash has been computed but not applied yet, /// rather deferred for several eras. #[derive(Encode, Decode, RuntimeDebug, TypeInfo)] pub struct UnappliedSlash { /// The stash ID of the offending validator. validator: AccountId, /// The validator's own slash. own: Balance, /// All other slashed stakers and amounts. others: Vec<(AccountId, Balance)>, /// Reporters of the offence; bounty payout recipients. reporters: Vec, /// The amount of payout. payout: Balance, } impl UnappliedSlash { /// Initializes the default object using the given `validator`. pub fn default_from(validator: AccountId) -> Self { Self { validator, own: Zero::zero(), others: vec![], reporters: vec![], payout: Zero::zero(), } } } /// Means for interacting with a specialized version of the `session` trait. /// /// This is needed because `Staking` sets the `ValidatorIdOf` of the `pallet_session::Config` pub trait SessionInterface { /// Disable the validator at the given index, returns `false` if the validator was already /// disabled or the index is out of bounds. fn disable_validator(validator_index: u32) -> bool; /// Get the validators from session. fn validators() -> Vec; /// Prune historical session tries up to but not including the given index. fn prune_historical_up_to(up_to: SessionIndex); } impl SessionInterface<::AccountId> for T where T: pallet_session::Config::AccountId>, T: pallet_session::historical::Config< FullIdentification = Exposure<::AccountId, BalanceOf>, FullIdentificationOf = ExposureOf, >, T::SessionHandler: pallet_session::SessionHandler<::AccountId>, T::SessionManager: pallet_session::SessionManager<::AccountId>, T::ValidatorIdOf: Convert< ::AccountId, Option<::AccountId>, >, { fn disable_validator(validator_index: u32) -> bool { >::disable_index(validator_index) } fn validators() -> Vec<::AccountId> { >::validators() } fn prune_historical_up_to(up_to: SessionIndex) { >::prune_up_to(up_to); } } /// Handler for determining how much of a balance should be paid out on the current era. pub trait EraPayout { /// Determine the payout for this era. /// /// Returns the amount to be paid to stakers in this era, as well as whatever else should be /// paid out ("the rest"). fn era_payout( total_staked: Balance, total_issuance: Balance, era_duration_millis: u64, ) -> (Balance, Balance); } impl EraPayout for () { fn era_payout( _total_staked: Balance, _total_issuance: Balance, _era_duration_millis: u64, ) -> (Balance, Balance) { (Default::default(), Default::default()) } } /// Adaptor to turn a `PiecewiseLinear` curve definition into an `EraPayout` impl, used for /// backwards compatibility. pub struct ConvertCurve(sp_std::marker::PhantomData); impl>> EraPayout for ConvertCurve { fn era_payout( total_staked: Balance, total_issuance: Balance, era_duration_millis: u64, ) -> (Balance, Balance) { let (validator_payout, max_payout) = inflation::compute_total_payout( &T::get(), total_staked, total_issuance, // Duration of era; more than u64::MAX is rewarded as u64::MAX. era_duration_millis, ); let rest = max_payout.saturating_sub(validator_payout.clone()); (validator_payout, rest) } } /// Mode of era-forcing. #[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] #[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub enum Forcing { /// Not forcing anything - just let whatever happen. NotForcing, /// Force a new era, then reset to `NotForcing` as soon as it is done. /// Note that this will force to trigger an election until a new era is triggered, if the /// election failed, the next session end will trigger a new election again, until success. ForceNew, /// Avoid a new era indefinitely. ForceNone, /// Force a new era at the end of all sessions indefinitely. ForceAlways, } impl Default for Forcing { fn default() -> Self { Forcing::NotForcing } } // A value placed in storage that represents the current version of the Staking storage. This value // is used by the `on_runtime_upgrade` logic to determine whether we run storage migration logic. // This should match directly with the semantic versions of the Rust crate. #[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)] enum Releases { V1_0_0Ancient, V2_0_0, V3_0_0, V4_0_0, V5_0_0, // blockable validators. V6_0_0, // removal of all storage associated with offchain phragmen. V7_0_0, // keep track of number of nominators / validators in map V8_0_0, // populate `SortedListProvider`. } impl Default for Releases { fn default() -> Self { Releases::V8_0_0 } } /// A `Convert` implementation that finds the stash of the given controller account, /// if any. pub struct StashOf(sp_std::marker::PhantomData); impl Convert> for StashOf { fn convert(controller: T::AccountId) -> Option { >::ledger(&controller).map(|l| l.stash) } } /// A typed conversion from stash account ID to the active exposure of nominators /// on that account. /// /// Active exposure is the exposure of the validator set currently validating, i.e. in /// `active_era`. It can differ from the latest planned exposure in `current_era`. pub struct ExposureOf(sp_std::marker::PhantomData); impl Convert>>> for ExposureOf { fn convert(validator: T::AccountId) -> Option>> { >::active_era() .map(|active_era| >::eras_stakers(active_era.index, &validator)) } } /// Filter historical offences out and only allow those from the bonding period. pub struct FilterHistoricalOffences { _inner: sp_std::marker::PhantomData<(T, R)>, } impl ReportOffence for FilterHistoricalOffences, R> where T: Config, R: ReportOffence, O: Offence, { fn report_offence(reporters: Vec, offence: O) -> Result<(), OffenceError> { // Disallow any slashing from before the current bonding period. let offence_session = offence.session_index(); let bonded_eras = BondedEras::::get(); if bonded_eras.first().filter(|(_, start)| offence_session >= *start).is_some() { R::report_offence(reporters, offence) } else { >::deposit_event(Event::::OldSlashingReportDiscarded(offence_session)); Ok(()) } } fn is_known_offence(offenders: &[Offender], time_slot: &O::TimeSlot) -> bool { R::is_known_offence(offenders, time_slot) } } /// Configurations of the benchmarking of the pallet. pub trait BenchmarkingConfig { /// The maximum number of validators to use. type MaxValidators: Get; /// The maximum number of nominators to use. type MaxNominators: Get; } /// A mock benchmarking config for pallet-staking. /// /// Should only be used for testing. #[cfg(feature = "std")] pub struct TestBenchmarkingConfig; #[cfg(feature = "std")] impl BenchmarkingConfig for TestBenchmarkingConfig { type MaxValidators = ConstU32<100>; type MaxNominators = ConstU32<100>; }