pallet_staking/lib.rs
1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! # Staking Pallet
19//!
20//! The Staking pallet is used to manage funds at stake by network maintainers.
21//!
22//! - [`Config`]
23//! - [`Call`]
24//! - [`Pallet`]
25//!
26//! ## Overview
27//!
28//! The Staking pallet is the means by which a set of network maintainers (known as _authorities_ in
29//! some contexts and _validators_ in others) are chosen based upon those who voluntarily place
30//! funds under deposit. Under deposit, those funds are rewarded under normal operation but are held
31//! at pain of _slash_ (expropriation) should the staked maintainer be found not to be discharging
32//! its duties properly.
33//!
34//! ### Terminology
35//! <!-- Original author of paragraph: @gavofyork -->
36//!
37//! - Staking: The process of locking up funds for some time, placing them at risk of slashing
38//! (loss) in order to become a rewarded maintainer of the network.
39//! - Validating: The process of running a node to actively maintain the network, either by
40//! producing blocks or guaranteeing finality of the chain.
41//! - Nominating: The process of placing staked funds behind one or more validators in order to
42//! share in any reward, and punishment, they take.
43//! - Stash account: The account holding an owner's funds used for staking.
44//! - Controller account (being deprecated): The account that controls an owner's funds for staking.
45//! - Era: A (whole) number of sessions, which is the period that the validator set (and each
46//! validator's active nominator set) is recalculated and where rewards are paid out.
47//! - Slash: The punishment of a staker by reducing its funds.
48//!
49//! ### Goals
50//! <!-- Original author of paragraph: @gavofyork -->
51//!
52//! The staking system in Substrate NPoS is designed to make the following possible:
53//!
54//! - Stake funds that are controlled by a cold wallet.
55//! - Withdraw some, or deposit more, funds without interrupting the role of an entity.
56//! - Switch between roles (nominator, validator, idle) with minimal overhead.
57//!
58//! ### Scenarios
59//!
60//! #### Staking
61//!
62//! Almost any interaction with the Staking pallet requires a process of _**bonding**_ (also known
63//! as being a _staker_). To become *bonded*, a fund-holding register known as the _stash account_,
64//! which holds some or all of the funds that become frozen in place as part of the staking process.
65//! The controller account, which this pallet now assigns the stash account to, issues instructions
66//! on how funds shall be used.
67//!
68//! An account can become a bonded stash account using the [`bond`](Call::bond) call.
69//!
70//! In the event stash accounts registered a unique controller account before the controller account
71//! deprecation, they can update their associated controller back to the stash account using the
72//! [`set_controller`](Call::set_controller) call.
73//!
74//! There are three possible roles that any staked account pair can be in: `Validator`, `Nominator`
75//! and `Idle` (defined in [`StakerStatus`]). There are three corresponding instructions to change
76//! between roles, namely: [`validate`](Call::validate), [`nominate`](Call::nominate), and
77//! [`chill`](Call::chill).
78//!
79//! #### Validating
80//!
81//! A **validator** takes the role of either validating blocks or ensuring their finality,
82//! maintaining the veracity of the network. A validator should avoid both any sort of malicious
83//! misbehavior and going offline. Bonded accounts that state interest in being a validator do NOT
84//! get immediately chosen as a validator. Instead, they are declared as a _candidate_ and they
85//! _might_ get elected at the _next era_ as a validator. The result of the election is determined
86//! by nominators and their votes.
87//!
88//! An account can become a validator candidate via the [`validate`](Call::validate) call.
89//!
90//! #### Nomination
91//!
92//! A **nominator** does not take any _direct_ role in maintaining the network, instead, it votes on
93//! a set of validators to be elected. Once interest in nomination is stated by an account, it takes
94//! effect at the next election round. The funds in the nominator's stash account indicate the
95//! _weight_ of its vote. Both the rewards and any punishment that a validator earns are shared
96//! between the validator and its nominators. This rule incentivizes the nominators to NOT vote for
97//! the misbehaving/offline validators as much as possible, simply because the nominators will also
98//! lose funds if they vote poorly.
99//!
100//! An account can become a nominator via the [`nominate`](Call::nominate) call.
101//!
102//! #### Voting
103//!
104//! Staking is closely related to elections; actual validators are chosen from among all potential
105//! validators via election by the potential validators and nominators. To reduce use of the phrase
106//! "potential validators and nominators", we often use the term **voters**, who are simply the
107//! union of potential validators and nominators.
108//!
109//! #### Rewards and Slash
110//!
111//! The **reward and slashing** procedure is the core of the Staking pallet, attempting to _embrace
112//! valid behavior_ while _punishing any misbehavior or lack of availability_.
113//!
114//! Rewards must be claimed for each era before it gets too old by
115//! [`HistoryDepth`](`Config::HistoryDepth`) using the `payout_stakers` call. Any account can call
116//! `payout_stakers`, which pays the reward to the validator as well as its nominators. Only
117//! [`Config::MaxExposurePageSize`] nominator rewards can be claimed in a single call. When the
118//! number of nominators exceeds [`Config::MaxExposurePageSize`], then the exposed nominators are
119//! stored in multiple pages, with each page containing up to [`Config::MaxExposurePageSize`]
120//! nominators. To pay out all nominators, `payout_stakers` must be called once for each available
121//! page. Paging exists to limit the i/o cost to mutate storage for each nominator's account.
122//!
123//! Slashing can occur at any point in time, once misbehavior is reported. Once slashing is
124//! determined, a value is deducted from the balance of the validator and all the nominators who
125//! voted for this validator (values are deducted from the _stash_ account of the slashed entity).
126//!
127//! Slashing logic is further described in the documentation of the `slashing` pallet.
128//!
129//! Similar to slashing, rewards are also shared among a validator and its associated nominators.
130//! Yet, the reward funds are not always transferred to the stash account and can be configured. See
131//! [Reward Calculation](#reward-calculation) for more details.
132//!
133//! #### Chilling
134//!
135//! Finally, any of the roles above can choose to step back temporarily and just chill for a while.
136//! This means that if they are a nominator, they will not be considered as voters anymore and if
137//! they are validators, they will no longer be a candidate for the next election.
138//!
139//! An account can step back via the [`chill`](Call::chill) call.
140//!
141//! ### Session managing
142//!
143//! The pallet implement the trait `SessionManager`. Which is the only API to query new validator
144//! set and allowing these validator set to be rewarded once their era is ended.
145//!
146//! ## Interface
147//!
148//! ### Dispatchable Functions
149//!
150//! The dispatchable functions of the Staking pallet enable the steps needed for entities to accept
151//! and change their role, alongside some helper functions to get/set the metadata of the pallet.
152//!
153//! ### Public Functions
154//!
155//! The Staking pallet contains many public storage items and (im)mutable functions.
156//!
157//! ## Usage
158//!
159//! ### Example: Rewarding a validator by id.
160//!
161//! ```
162//! use pallet_staking::{self as staking};
163//! use frame_support::traits::RewardsReporter;
164//!
165//! #[frame_support::pallet(dev_mode)]
166//! pub mod pallet {
167//! use super::*;
168//! use frame_support::pallet_prelude::*;
169//! use frame_system::pallet_prelude::*;
170//! # use frame_support::traits::RewardsReporter;
171//!
172//! #[pallet::pallet]
173//! pub struct Pallet<T>(_);
174//!
175//! #[pallet::config]
176//! pub trait Config: frame_system::Config + staking::Config {}
177//!
178//! #[pallet::call]
179//! impl<T: Config> Pallet<T> {
180//! /// Reward a validator.
181//! #[pallet::weight(0)]
182//! pub fn reward_myself(origin: OriginFor<T>) -> DispatchResult {
183//! let reported = ensure_signed(origin)?;
184//! <staking::Pallet<T>>::reward_by_ids(vec![(reported, 10)]);
185//! Ok(())
186//! }
187//! }
188//! }
189//! # fn main() { }
190//! ```
191//!
192//! ## Implementation Details
193//!
194//! ### Era payout
195//!
196//! The era payout is computed using yearly inflation curve defined at [`Config::EraPayout`] as
197//! such:
198//!
199//! ```nocompile
200//! staker_payout = yearly_inflation(npos_token_staked / total_tokens) * total_tokens / era_per_year
201//! ```
202//! This payout is used to reward stakers as defined in next section
203//!
204//! ```nocompile
205//! remaining_payout = max_yearly_inflation * total_tokens / era_per_year - staker_payout
206//! ```
207//!
208//! Note, however, that it is possible to set a cap on the total `staker_payout` for the era through
209//! the `MaxStakersRewards` storage type. The `era_payout` implementor must ensure that the
210//! `max_payout = remaining_payout + (staker_payout * max_stakers_rewards)`. The excess payout that
211//! is not allocated for stakers is the era remaining reward.
212//!
213//! The remaining reward is send to the configurable end-point [`Config::RewardRemainder`].
214//!
215//! ### Reward Calculation
216//!
217//! Validators and nominators are rewarded at the end of each era. The total reward of an era is
218//! calculated using the era duration and the staking rate (the total amount of tokens staked by
219//! nominators and validators, divided by the total token supply). It aims to incentivize toward a
220//! defined staking rate. The full specification can be found
221//! [here](https://research.web3.foundation/en/latest/polkadot/Token%20Economics.html#inflation-model).
222//!
223//! Total reward is split among validators and their nominators depending on the number of points
224//! they received during the era. Points are added to a validator using the method
225//! [`frame_support::traits::RewardsReporter::reward_by_ids`] implemented by the [`Pallet`].
226//!
227//! [`Pallet`] implements [`pallet_authorship::EventHandler`] to add reward points to block producer
228//! and block producer of referenced uncles.
229//!
230//! The validator and its nominator split their reward as following:
231//!
232//! The validator can declare an amount, named [`commission`](ValidatorPrefs::commission), that does
233//! not get shared with the nominators at each reward payout through its [`ValidatorPrefs`]. This
234//! value gets deducted from the total reward that is paid to the validator and its nominators. The
235//! remaining portion is split pro rata among the validator and the nominators that nominated the
236//! validator, proportional to the value staked behind the validator (_i.e._ dividing the
237//! [`own`](Exposure::own) or [`others`](Exposure::others) by [`total`](Exposure::total) in
238//! [`Exposure`]). Note that payouts are made in pages with each page capped at
239//! [`Config::MaxExposurePageSize`] nominators. The distribution of nominators across pages may be
240//! unsorted. The total commission is paid out proportionally across pages based on the total stake
241//! of the page.
242//!
243//! All entities who receive a reward have the option to choose their reward destination through the
244//! [`Payee`] storage item (see [`set_payee`](Call::set_payee)), to be one of the following:
245//!
246//! - Stash account, not increasing the staked value.
247//! - Stash account, also increasing the staked value.
248//! - Any other account, sent as free balance.
249//!
250//! ### Additional Fund Management Operations
251//!
252//! Any funds already placed into stash can be the target of the following operations:
253//!
254//! The controller account can free a portion (or all) of the funds using the
255//! [`unbond`](Call::unbond) call. Note that the funds are not immediately accessible. Instead, a
256//! duration denoted by [`Config::BondingDuration`] (in number of eras) must pass until the funds
257//! can actually be removed. Once the `BondingDuration` is over, the
258//! [`withdraw_unbonded`](Call::withdraw_unbonded) call can be used to actually withdraw the funds.
259//!
260//! Note that there is a limitation to the number of fund-chunks that can be scheduled to be
261//! unlocked in the future via [`unbond`](Call::unbond). In case this maximum
262//! (`MAX_UNLOCKING_CHUNKS`) is reached, the bonded account _must_ first wait until a successful
263//! call to `withdraw_unbonded` to remove some of the chunks.
264//!
265//! ### Election Algorithm
266//!
267//! The current election algorithm is implemented based on Phragmén. The reference implementation
268//! can be found [here](https://github.com/w3f/consensus/tree/master/NPoS).
269//!
270//! The election algorithm, aside from electing the validators with the most stake value and votes,
271//! tries to divide the nominator votes among candidates in an equal manner. To further assure this,
272//! an optional post-processing can be applied that iteratively normalizes the nominator staked
273//! values until the total difference among votes of a particular nominator are less than a
274//! threshold.
275//!
276//! ## GenesisConfig
277//!
278//! The Staking pallet depends on the [`GenesisConfig`]. The `GenesisConfig` is optional and allow
279//! to set some initial stakers.
280//!
281//! ## Related Modules
282//!
283//! - [Balances](../pallet_balances/index.html): Used to manage values at stake.
284//! - [Session](../pallet_session/index.html): Used to manage sessions. Also, a list of new
285//! validators is stored in the Session pallet's `Validators` at the end of each era.
286
287#![cfg_attr(not(feature = "std"), no_std)]
288#![recursion_limit = "256"]
289
290#[cfg(feature = "runtime-benchmarks")]
291pub mod benchmarking;
292#[cfg(any(feature = "runtime-benchmarks", test))]
293pub mod testing_utils;
294
295#[cfg(test)]
296pub(crate) mod mock;
297#[cfg(test)]
298mod tests;
299
300pub mod asset;
301pub mod election_size_tracker;
302pub mod inflation;
303pub mod ledger;
304pub mod migrations;
305pub mod slashing;
306pub mod weights;
307
308mod pallet;
309
310extern crate alloc;
311
312use alloc::{collections::btree_map::BTreeMap, vec, vec::Vec};
313use codec::{Decode, DecodeWithMemTracking, Encode, HasCompact, MaxEncodedLen};
314use frame_election_provider_support::ElectionProvider;
315use frame_support::{
316 defensive, defensive_assert,
317 traits::{
318 tokens::fungible::{Credit, Debt},
319 ConstU32, Contains, Defensive, DefensiveMax, DefensiveSaturating, Get, LockIdentifier,
320 },
321 weights::Weight,
322 BoundedVec, CloneNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound,
323};
324use scale_info::TypeInfo;
325use sp_runtime::{
326 curve::PiecewiseLinear,
327 traits::{AtLeast32BitUnsigned, Convert, StaticLookup, Zero},
328 Perbill, Perquintill, Rounding, RuntimeDebug, Saturating,
329};
330use sp_staking::{
331 offence::{Offence, OffenceError, OffenceSeverity, ReportOffence},
332 EraIndex, ExposurePage, OnStakingUpdate, Page, PagedExposureMetadata, SessionIndex,
333};
334pub use sp_staking::{Exposure, IndividualExposure, StakerStatus};
335pub use weights::WeightInfo;
336
337pub use pallet::{pallet::*, UseNominatorsAndValidatorsMap, UseValidatorsMap};
338
339pub(crate) const STAKING_ID: LockIdentifier = *b"staking ";
340pub(crate) const LOG_TARGET: &str = "runtime::staking";
341
342// syntactic sugar for logging.
343#[macro_export]
344macro_rules! log {
345 ($level:tt, $patter:expr $(, $values:expr)* $(,)?) => {
346 log::$level!(
347 target: crate::LOG_TARGET,
348 concat!("[{:?}] 💸 ", $patter), <frame_system::Pallet<T>>::block_number() $(, $values)*
349 )
350 };
351}
352
353/// Alias for the maximum number of winners (aka. active validators), as defined in by this pallet's
354/// config.
355pub type MaxWinnersOf<T> = <T as Config>::MaxValidatorSet;
356
357/// Alias for the maximum number of winners per page, as expected by the election provider.
358pub type MaxWinnersPerPageOf<P> = <P as ElectionProvider>::MaxWinnersPerPage;
359
360/// Maximum number of nominations per nominator.
361pub type MaxNominationsOf<T> =
362 <<T as Config>::NominationsQuota as NominationsQuota<BalanceOf<T>>>::MaxNominations;
363
364/// Counter for the number of "reward" points earned by a given validator.
365pub type RewardPoint = u32;
366
367/// The balance type of this pallet.
368pub type BalanceOf<T> = <T as Config>::CurrencyBalance;
369
370type PositiveImbalanceOf<T> = Debt<<T as frame_system::Config>::AccountId, <T as Config>::Currency>;
371pub type NegativeImbalanceOf<T> =
372 Credit<<T as frame_system::Config>::AccountId, <T as Config>::Currency>;
373
374type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source;
375
376/// Information regarding the active era (era in used in session).
377#[derive(
378 Encode,
379 Decode,
380 DecodeWithMemTracking,
381 Clone,
382 RuntimeDebug,
383 TypeInfo,
384 MaxEncodedLen,
385 PartialEq,
386 Eq,
387)]
388pub struct ActiveEraInfo {
389 /// Index of era.
390 pub index: EraIndex,
391 /// Moment of start expressed as millisecond from `$UNIX_EPOCH`.
392 ///
393 /// Start can be none if start hasn't been set for the era yet,
394 /// Start is set on the first on_finalize of the era to guarantee usage of `Time`.
395 pub start: Option<u64>,
396}
397
398/// Reward points of an era. Used to split era total payout between validators.
399///
400/// This points will be used to reward validators and their respective nominators.
401#[derive(Encode, Decode, DecodeWithMemTracking, RuntimeDebug, TypeInfo, Clone, PartialEq, Eq)]
402pub struct EraRewardPoints<AccountId: Ord> {
403 /// Total number of points. Equals the sum of reward points for each validator.
404 pub total: RewardPoint,
405 /// The reward points earned by a given validator.
406 pub individual: BTreeMap<AccountId, RewardPoint>,
407}
408
409impl<AccountId: Ord> Default for EraRewardPoints<AccountId> {
410 fn default() -> Self {
411 EraRewardPoints { total: Default::default(), individual: BTreeMap::new() }
412 }
413}
414
415/// A destination account for payment.
416#[derive(
417 PartialEq,
418 Eq,
419 Copy,
420 Clone,
421 Encode,
422 Decode,
423 DecodeWithMemTracking,
424 RuntimeDebug,
425 TypeInfo,
426 MaxEncodedLen,
427)]
428pub enum RewardDestination<AccountId> {
429 /// Pay into the stash account, increasing the amount at stake accordingly.
430 Staked,
431 /// Pay into the stash account, not increasing the amount at stake.
432 Stash,
433 #[deprecated(
434 note = "`Controller` will be removed after January 2024. Use `Account(controller)` instead."
435 )]
436 Controller,
437 /// Pay into a specified account.
438 Account(AccountId),
439 /// Receive no reward.
440 None,
441}
442
443/// Preference of what happens regarding validation.
444#[derive(
445 PartialEq,
446 Eq,
447 Clone,
448 Encode,
449 Decode,
450 DecodeWithMemTracking,
451 RuntimeDebug,
452 TypeInfo,
453 Default,
454 MaxEncodedLen,
455)]
456pub struct ValidatorPrefs {
457 /// Reward that validator takes up-front; only the rest is split between themselves and
458 /// nominators.
459 #[codec(compact)]
460 pub commission: Perbill,
461 /// Whether or not this validator is accepting more nominations. If `true`, then no nominator
462 /// who is not already nominating this validator may nominate them. By default, validators
463 /// are accepting nominations.
464 pub blocked: bool,
465}
466
467/// Just a Balance/BlockNumber tuple to encode when a chunk of funds will be unlocked.
468#[derive(
469 PartialEq,
470 Eq,
471 Clone,
472 Encode,
473 Decode,
474 DecodeWithMemTracking,
475 RuntimeDebug,
476 TypeInfo,
477 MaxEncodedLen,
478)]
479pub struct UnlockChunk<Balance: HasCompact + MaxEncodedLen> {
480 /// Amount of funds to be unlocked.
481 #[codec(compact)]
482 pub value: Balance,
483 /// Era number at which point it'll be unlocked.
484 #[codec(compact)]
485 pub era: EraIndex,
486}
487
488/// The ledger of a (bonded) stash.
489///
490/// Note: All the reads and mutations to the [`Ledger`], [`Bonded`] and [`Payee`] storage items
491/// *MUST* be performed through the methods exposed by this struct, to ensure the consistency of
492/// ledger's data and corresponding staking lock
493///
494/// TODO: move struct definition and full implementation into `/src/ledger.rs`. Currently
495/// leaving here to enforce a clean PR diff, given how critical this logic is. Tracking issue
496/// <https://github.com/paritytech/substrate/issues/14749>.
497#[derive(
498 PartialEqNoBound,
499 EqNoBound,
500 CloneNoBound,
501 Encode,
502 Decode,
503 DecodeWithMemTracking,
504 RuntimeDebugNoBound,
505 TypeInfo,
506 MaxEncodedLen,
507)]
508#[scale_info(skip_type_params(T))]
509pub struct StakingLedger<T: Config> {
510 /// The stash account whose balance is actually locked and at stake.
511 pub stash: T::AccountId,
512
513 /// The total amount of the stash's balance that we are currently accounting for.
514 /// It's just `active` plus all the `unlocking` balances.
515 #[codec(compact)]
516 pub total: BalanceOf<T>,
517
518 /// The total amount of the stash's balance that will be at stake in any forthcoming
519 /// rounds.
520 #[codec(compact)]
521 pub active: BalanceOf<T>,
522
523 /// Any balance that is becoming free, which may eventually be transferred out of the stash
524 /// (assuming it doesn't get slashed first). It is assumed that this will be treated as a first
525 /// in, first out queue where the new (higher value) eras get pushed on the back.
526 pub unlocking: BoundedVec<UnlockChunk<BalanceOf<T>>, T::MaxUnlockingChunks>,
527
528 /// List of eras for which the stakers behind a validator have claimed rewards. Only updated
529 /// for validators.
530 ///
531 /// This is deprecated as of V14 in favor of `T::ClaimedRewards` and will be removed in future.
532 /// Refer to issue <https://github.com/paritytech/polkadot-sdk/issues/433>
533 pub legacy_claimed_rewards: BoundedVec<EraIndex, T::HistoryDepth>,
534
535 /// The controller associated with this ledger's stash.
536 ///
537 /// This is not stored on-chain, and is only bundled when the ledger is read from storage.
538 /// Use [`Self::controller()`] function to get the controller associated with the ledger.
539 #[codec(skip)]
540 pub controller: Option<T::AccountId>,
541}
542
543/// State of a ledger with regards with its data and metadata integrity.
544#[derive(PartialEq, Debug)]
545enum LedgerIntegrityState {
546 /// Ledger, bond and corresponding staking lock is OK.
547 Ok,
548 /// Ledger and/or bond is corrupted. This means that the bond has a ledger with a different
549 /// stash than the bonded stash.
550 Corrupted,
551 /// Ledger was corrupted and it has been killed.
552 CorruptedKilled,
553 /// Ledger and bond are OK, however the ledger's stash lock is out of sync.
554 LockCorrupted,
555}
556
557impl<T: Config> StakingLedger<T> {
558 /// Remove entries from `unlocking` that are sufficiently old and reduce the
559 /// total by the sum of their balances.
560 fn consolidate_unlocked(self, current_era: EraIndex) -> Self {
561 let mut total = self.total;
562 let unlocking: BoundedVec<_, _> = self
563 .unlocking
564 .into_iter()
565 .filter(|chunk| {
566 if chunk.era > current_era {
567 true
568 } else {
569 total = total.saturating_sub(chunk.value);
570 false
571 }
572 })
573 .collect::<Vec<_>>()
574 .try_into()
575 .expect(
576 "filtering items from a bounded vec always leaves length less than bounds. qed",
577 );
578
579 Self {
580 stash: self.stash,
581 total,
582 active: self.active,
583 unlocking,
584 legacy_claimed_rewards: self.legacy_claimed_rewards,
585 controller: self.controller,
586 }
587 }
588
589 /// Sets ledger total to the `new_total`.
590 ///
591 /// Removes entries from `unlocking` upto `amount` starting from the oldest first.
592 fn update_total_stake(mut self, new_total: BalanceOf<T>) -> Self {
593 let old_total = self.total;
594 self.total = new_total;
595 debug_assert!(
596 new_total <= old_total,
597 "new_total {:?} must be <= old_total {:?}",
598 new_total,
599 old_total
600 );
601
602 let to_withdraw = old_total.defensive_saturating_sub(new_total);
603 // accumulator to keep track of how much is withdrawn.
604 // First we take out from active.
605 let mut withdrawn = BalanceOf::<T>::zero();
606
607 // first we try to remove stake from active
608 if self.active >= to_withdraw {
609 self.active -= to_withdraw;
610 return self
611 } else {
612 withdrawn += self.active;
613 self.active = BalanceOf::<T>::zero();
614 }
615
616 // start removing from the oldest chunk.
617 while let Some(last) = self.unlocking.last_mut() {
618 if withdrawn.defensive_saturating_add(last.value) <= to_withdraw {
619 withdrawn += last.value;
620 self.unlocking.pop();
621 } else {
622 let diff = to_withdraw.defensive_saturating_sub(withdrawn);
623 withdrawn += diff;
624 last.value -= diff;
625 }
626
627 if withdrawn >= to_withdraw {
628 break
629 }
630 }
631
632 self
633 }
634
635 /// Re-bond funds that were scheduled for unlocking.
636 ///
637 /// Returns the updated ledger, and the amount actually rebonded.
638 fn rebond(mut self, value: BalanceOf<T>) -> (Self, BalanceOf<T>) {
639 let mut unlocking_balance = BalanceOf::<T>::zero();
640
641 while let Some(last) = self.unlocking.last_mut() {
642 if unlocking_balance.defensive_saturating_add(last.value) <= value {
643 unlocking_balance += last.value;
644 self.active += last.value;
645 self.unlocking.pop();
646 } else {
647 let diff = value.defensive_saturating_sub(unlocking_balance);
648
649 unlocking_balance += diff;
650 self.active += diff;
651 last.value -= diff;
652 }
653
654 if unlocking_balance >= value {
655 break
656 }
657 }
658
659 (self, unlocking_balance)
660 }
661
662 /// Slash the staker for a given amount of balance.
663 ///
664 /// This implements a proportional slashing system, whereby we set our preference to slash as
665 /// such:
666 ///
667 /// - If any unlocking chunks exist that are scheduled to be unlocked at `slash_era +
668 /// bonding_duration` and onwards, the slash is divided equally between the active ledger and
669 /// the unlocking chunks.
670 /// - If no such chunks exist, then only the active balance is slashed.
671 ///
672 /// Note that the above is only a *preference*. If for any reason the active ledger, with or
673 /// without some portion of the unlocking chunks that are more justified to be slashed are not
674 /// enough, then the slashing will continue and will consume as much of the active and unlocking
675 /// chunks as needed.
676 ///
677 /// This will never slash more than the given amount. If any of the chunks become dusted, the
678 /// last chunk is slashed slightly less to compensate. Returns the amount of funds actually
679 /// slashed.
680 ///
681 /// `slash_era` is the era in which the slash (which is being enacted now) actually happened.
682 ///
683 /// This calls `Config::OnStakingUpdate::on_slash` with information as to how the slash was
684 /// applied.
685 pub fn slash(
686 &mut self,
687 slash_amount: BalanceOf<T>,
688 minimum_balance: BalanceOf<T>,
689 slash_era: EraIndex,
690 ) -> BalanceOf<T> {
691 if slash_amount.is_zero() {
692 return Zero::zero()
693 }
694
695 use sp_runtime::PerThing as _;
696 let mut remaining_slash = slash_amount;
697 let pre_slash_total = self.total;
698
699 // for a `slash_era = x`, any chunk that is scheduled to be unlocked at era `x + 28`
700 // (assuming 28 is the bonding duration) onwards should be slashed.
701 let slashable_chunks_start = slash_era.saturating_add(T::BondingDuration::get());
702
703 // `Some(ratio)` if this is proportional, with `ratio`, `None` otherwise. In both cases, we
704 // slash first the active chunk, and then `slash_chunks_priority`.
705 let (maybe_proportional, slash_chunks_priority) = {
706 if let Some(first_slashable_index) =
707 self.unlocking.iter().position(|c| c.era >= slashable_chunks_start)
708 {
709 // If there exists a chunk who's after the first_slashable_start, then this is a
710 // proportional slash, because we want to slash active and these chunks
711 // proportionally.
712
713 // The indices of the first chunk after the slash up through the most recent chunk.
714 // (The most recent chunk is at greatest from this era)
715 let affected_indices = first_slashable_index..self.unlocking.len();
716 let unbonding_affected_balance =
717 affected_indices.clone().fold(BalanceOf::<T>::zero(), |sum, i| {
718 if let Some(chunk) = self.unlocking.get(i).defensive() {
719 sum.saturating_add(chunk.value)
720 } else {
721 sum
722 }
723 });
724 let affected_balance = self.active.saturating_add(unbonding_affected_balance);
725 let ratio = Perquintill::from_rational_with_rounding(
726 slash_amount,
727 affected_balance,
728 Rounding::Up,
729 )
730 .unwrap_or_else(|_| Perquintill::one());
731 (
732 Some(ratio),
733 affected_indices.chain((0..first_slashable_index).rev()).collect::<Vec<_>>(),
734 )
735 } else {
736 // We just slash from the last chunk to the most recent one, if need be.
737 (None, (0..self.unlocking.len()).rev().collect::<Vec<_>>())
738 }
739 };
740
741 // Helper to update `target` and the ledgers total after accounting for slashing `target`.
742 log!(
743 debug,
744 "slashing {:?} for era {:?} out of {:?}, priority: {:?}, proportional = {:?}",
745 slash_amount,
746 slash_era,
747 self,
748 slash_chunks_priority,
749 maybe_proportional,
750 );
751
752 let mut slash_out_of = |target: &mut BalanceOf<T>, slash_remaining: &mut BalanceOf<T>| {
753 let mut slash_from_target = if let Some(ratio) = maybe_proportional {
754 ratio.mul_ceil(*target)
755 } else {
756 *slash_remaining
757 }
758 // this is the total that that the slash target has. We can't slash more than
759 // this anyhow!
760 .min(*target)
761 // this is the total amount that we would have wanted to slash
762 // non-proportionally, a proportional slash should never exceed this either!
763 .min(*slash_remaining);
764
765 // slash out from *target exactly `slash_from_target`.
766 *target = *target - slash_from_target;
767 if *target < minimum_balance {
768 // Slash the rest of the target if it's dust. This might cause the last chunk to be
769 // slightly under-slashed, by at most `MaxUnlockingChunks * ED`, which is not a big
770 // deal.
771 slash_from_target =
772 core::mem::replace(target, Zero::zero()).saturating_add(slash_from_target)
773 }
774
775 self.total = self.total.saturating_sub(slash_from_target);
776 *slash_remaining = slash_remaining.saturating_sub(slash_from_target);
777 };
778
779 // If this is *not* a proportional slash, the active will always wiped to 0.
780 slash_out_of(&mut self.active, &mut remaining_slash);
781
782 let mut slashed_unlocking = BTreeMap::<_, _>::new();
783 for i in slash_chunks_priority {
784 if remaining_slash.is_zero() {
785 break
786 }
787
788 if let Some(chunk) = self.unlocking.get_mut(i).defensive() {
789 slash_out_of(&mut chunk.value, &mut remaining_slash);
790 // write the new slashed value of this chunk to the map.
791 slashed_unlocking.insert(chunk.era, chunk.value);
792 } else {
793 break
794 }
795 }
796
797 // clean unlocking chunks that are set to zero.
798 self.unlocking.retain(|c| !c.value.is_zero());
799
800 let final_slashed_amount = pre_slash_total.saturating_sub(self.total);
801 T::EventListeners::on_slash(
802 &self.stash,
803 self.active,
804 &slashed_unlocking,
805 final_slashed_amount,
806 );
807 final_slashed_amount
808 }
809}
810
811/// A record of the nominations made by a specific account.
812#[derive(
813 PartialEqNoBound,
814 EqNoBound,
815 Clone,
816 Encode,
817 Decode,
818 DecodeWithMemTracking,
819 RuntimeDebugNoBound,
820 TypeInfo,
821 MaxEncodedLen,
822)]
823#[codec(mel_bound())]
824#[scale_info(skip_type_params(T))]
825pub struct Nominations<T: Config> {
826 /// The targets of nomination.
827 pub targets: BoundedVec<T::AccountId, MaxNominationsOf<T>>,
828 /// The era the nominations were submitted.
829 ///
830 /// Except for initial nominations which are considered submitted at era 0.
831 pub submitted_in: EraIndex,
832 /// Whether the nominations have been suppressed. This can happen due to slashing of the
833 /// validators, or other events that might invalidate the nomination.
834 ///
835 /// NOTE: this for future proofing and is thus far not used.
836 pub suppressed: bool,
837}
838
839/// Facade struct to encapsulate `PagedExposureMetadata` and a single page of `ExposurePage`.
840///
841/// This is useful where we need to take into account the validator's own stake and total exposure
842/// in consideration, in addition to the individual nominators backing them.
843#[derive(Encode, Decode, RuntimeDebug, TypeInfo, PartialEq, Eq)]
844pub struct PagedExposure<AccountId, Balance: HasCompact + codec::MaxEncodedLen> {
845 exposure_metadata: PagedExposureMetadata<Balance>,
846 exposure_page: ExposurePage<AccountId, Balance>,
847}
848
849impl<AccountId, Balance: HasCompact + Copy + AtLeast32BitUnsigned + codec::MaxEncodedLen>
850 PagedExposure<AccountId, Balance>
851{
852 /// Create a new instance of `PagedExposure` from legacy clipped exposures.
853 pub fn from_clipped(exposure: Exposure<AccountId, Balance>) -> Self {
854 Self {
855 exposure_metadata: PagedExposureMetadata {
856 total: exposure.total,
857 own: exposure.own,
858 nominator_count: exposure.others.len() as u32,
859 page_count: 1,
860 },
861 exposure_page: ExposurePage { page_total: exposure.total, others: exposure.others },
862 }
863 }
864
865 /// Returns total exposure of this validator across pages
866 pub fn total(&self) -> Balance {
867 self.exposure_metadata.total
868 }
869
870 /// Returns total exposure of this validator for the current page
871 pub fn page_total(&self) -> Balance {
872 self.exposure_page.page_total + self.exposure_metadata.own
873 }
874
875 /// Returns validator's own stake that is exposed
876 pub fn own(&self) -> Balance {
877 self.exposure_metadata.own
878 }
879
880 /// Returns the portions of nominators stashes that are exposed in this page.
881 pub fn others(&self) -> &Vec<IndividualExposure<AccountId, Balance>> {
882 &self.exposure_page.others
883 }
884}
885
886/// A pending slash record. The value of the slash has been computed but not applied yet,
887/// rather deferred for several eras.
888#[derive(Encode, Decode, RuntimeDebug, TypeInfo, PartialEq, Eq, Clone, DecodeWithMemTracking)]
889pub struct UnappliedSlash<AccountId, Balance: HasCompact> {
890 /// The stash ID of the offending validator.
891 pub validator: AccountId,
892 /// The validator's own slash.
893 pub own: Balance,
894 /// All other slashed stakers and amounts.
895 pub others: Vec<(AccountId, Balance)>,
896 /// Reporters of the offence; bounty payout recipients.
897 pub reporters: Vec<AccountId>,
898 /// The amount of payout.
899 pub payout: Balance,
900}
901
902impl<AccountId, Balance: HasCompact + Zero> UnappliedSlash<AccountId, Balance> {
903 /// Initializes the default object using the given `validator`.
904 pub fn default_from(validator: AccountId) -> Self {
905 Self {
906 validator,
907 own: Zero::zero(),
908 others: vec![],
909 reporters: vec![],
910 payout: Zero::zero(),
911 }
912 }
913}
914
915/// Something that defines the maximum number of nominations per nominator based on a curve.
916///
917/// The method `curve` implements the nomination quota curve and should not be used directly.
918/// However, `get_quota` returns the bounded maximum number of nominations based on `fn curve` and
919/// the nominator's balance.
920pub trait NominationsQuota<Balance> {
921 /// Strict maximum number of nominations that caps the nominations curve. This value can be
922 /// used as the upper bound of the number of votes per nominator.
923 type MaxNominations: Get<u32>;
924
925 /// Returns the voter's nomination quota within reasonable bounds [`min`, `max`], where `min`
926 /// is 1 and `max` is `Self::MaxNominations`.
927 fn get_quota(balance: Balance) -> u32 {
928 Self::curve(balance).clamp(1, Self::MaxNominations::get())
929 }
930
931 /// Returns the voter's nomination quota based on its balance and a curve.
932 fn curve(balance: Balance) -> u32;
933}
934
935/// A nomination quota that allows up to MAX nominations for all validators.
936pub struct FixedNominationsQuota<const MAX: u32>;
937impl<Balance, const MAX: u32> NominationsQuota<Balance> for FixedNominationsQuota<MAX> {
938 type MaxNominations = ConstU32<MAX>;
939
940 fn curve(_: Balance) -> u32 {
941 MAX
942 }
943}
944
945/// Means for interacting with a specialized version of the `session` trait.
946///
947/// This is needed because `Staking` sets the `ValidatorIdOf` of the `pallet_session::Config`
948pub trait SessionInterface<AccountId> {
949 /// Report an offending validator.
950 fn report_offence(validator: AccountId, severity: OffenceSeverity);
951 /// Get the validators from session.
952 fn validators() -> Vec<AccountId>;
953 /// Prune historical session tries up to but not including the given index.
954 fn prune_historical_up_to(up_to: SessionIndex);
955}
956
957impl<T: Config> SessionInterface<<T as frame_system::Config>::AccountId> for T
958where
959 T: pallet_session::Config<ValidatorId = <T as frame_system::Config>::AccountId>,
960 T: pallet_session::historical::Config,
961 T::SessionHandler: pallet_session::SessionHandler<<T as frame_system::Config>::AccountId>,
962 T::SessionManager: pallet_session::SessionManager<<T as frame_system::Config>::AccountId>,
963 T::ValidatorIdOf: Convert<
964 <T as frame_system::Config>::AccountId,
965 Option<<T as frame_system::Config>::AccountId>,
966 >,
967{
968 fn report_offence(
969 validator: <T as frame_system::Config>::AccountId,
970 severity: OffenceSeverity,
971 ) {
972 <pallet_session::Pallet<T>>::report_offence(validator, severity)
973 }
974
975 fn validators() -> Vec<<T as frame_system::Config>::AccountId> {
976 <pallet_session::Pallet<T>>::validators()
977 }
978
979 fn prune_historical_up_to(up_to: SessionIndex) {
980 <pallet_session::historical::Pallet<T>>::prune_up_to(up_to);
981 }
982}
983
984impl<AccountId> SessionInterface<AccountId> for () {
985 fn report_offence(_validator: AccountId, _severity: OffenceSeverity) {
986 ()
987 }
988 fn validators() -> Vec<AccountId> {
989 Vec::new()
990 }
991 fn prune_historical_up_to(_: SessionIndex) {
992 ()
993 }
994}
995
996/// Handler for determining how much of a balance should be paid out on the current era.
997pub trait EraPayout<Balance> {
998 /// Determine the payout for this era.
999 ///
1000 /// Returns the amount to be paid to stakers in this era, as well as whatever else should be
1001 /// paid out ("the rest").
1002 fn era_payout(
1003 total_staked: Balance,
1004 total_issuance: Balance,
1005 era_duration_millis: u64,
1006 ) -> (Balance, Balance);
1007}
1008
1009impl<Balance: Default> EraPayout<Balance> for () {
1010 fn era_payout(
1011 _total_staked: Balance,
1012 _total_issuance: Balance,
1013 _era_duration_millis: u64,
1014 ) -> (Balance, Balance) {
1015 (Default::default(), Default::default())
1016 }
1017}
1018
1019/// Adaptor to turn a `PiecewiseLinear` curve definition into an `EraPayout` impl, used for
1020/// backwards compatibility.
1021pub struct ConvertCurve<T>(core::marker::PhantomData<T>);
1022impl<Balance, T> EraPayout<Balance> for ConvertCurve<T>
1023where
1024 Balance: AtLeast32BitUnsigned + Clone + Copy,
1025 T: Get<&'static PiecewiseLinear<'static>>,
1026{
1027 fn era_payout(
1028 total_staked: Balance,
1029 total_issuance: Balance,
1030 era_duration_millis: u64,
1031 ) -> (Balance, Balance) {
1032 let (validator_payout, max_payout) = inflation::compute_total_payout(
1033 T::get(),
1034 total_staked,
1035 total_issuance,
1036 // Duration of era; more than u64::MAX is rewarded as u64::MAX.
1037 era_duration_millis,
1038 );
1039 let rest = max_payout.saturating_sub(validator_payout);
1040 (validator_payout, rest)
1041 }
1042}
1043
1044/// Mode of era-forcing.
1045#[derive(
1046 Copy,
1047 Clone,
1048 PartialEq,
1049 Eq,
1050 Encode,
1051 Decode,
1052 DecodeWithMemTracking,
1053 RuntimeDebug,
1054 TypeInfo,
1055 MaxEncodedLen,
1056 serde::Serialize,
1057 serde::Deserialize,
1058)]
1059pub enum Forcing {
1060 /// Not forcing anything - just let whatever happen.
1061 NotForcing,
1062 /// Force a new era, then reset to `NotForcing` as soon as it is done.
1063 /// Note that this will force to trigger an election until a new era is triggered, if the
1064 /// election failed, the next session end will trigger a new election again, until success.
1065 ForceNew,
1066 /// Avoid a new era indefinitely.
1067 ForceNone,
1068 /// Force a new era at the end of all sessions indefinitely.
1069 ForceAlways,
1070}
1071
1072impl Default for Forcing {
1073 fn default() -> Self {
1074 Forcing::NotForcing
1075 }
1076}
1077
1078/// A typed conversion from stash account ID to the active exposure of nominators
1079/// on that account.
1080///
1081/// Active exposure is the exposure of the validator set currently validating, i.e. in
1082/// `active_era`. It can differ from the latest planned exposure in `current_era`.
1083#[deprecated(note = "Use `DefaultExposureOf` instead")]
1084pub struct ExposureOf<T>(core::marker::PhantomData<T>);
1085
1086#[allow(deprecated)]
1087impl<T: Config> Convert<T::AccountId, Option<Exposure<T::AccountId, BalanceOf<T>>>>
1088 for ExposureOf<T>
1089{
1090 fn convert(validator: T::AccountId) -> Option<Exposure<T::AccountId, BalanceOf<T>>> {
1091 ActiveEra::<T>::get()
1092 .map(|active_era| <Pallet<T>>::eras_stakers(active_era.index, &validator))
1093 }
1094}
1095
1096/// Identify a validator with their default exposure.
1097///
1098/// This type should not be used in a fresh runtime, instead use [`UnitIdentificationOf`].
1099///
1100/// In the past, a type called [`ExposureOf`] used to return the full exposure of a validator to
1101/// identify their exposure. This type is kept, marked as deprecated, for backwards compatibility of
1102/// external SDK users, but is no longer used in this repo.
1103///
1104/// In the new model, we don't need to identify a validator with their full exposure anymore, and
1105/// therefore [`UnitIdentificationOf`] is perfectly fine. Yet, for runtimes that used to work with
1106/// [`ExposureOf`], we need to be able to decode old identification data, possibly stored in the
1107/// historical session pallet in older blocks. Therefore, this type is a good compromise, allowing
1108/// old exposure identifications to be decoded, and returning a few zero bytes
1109/// (`Exposure::default`) for any new identification request.
1110///
1111/// A typical usage of this type is:
1112///
1113/// ```ignore
1114/// impl pallet_session::historical::Config for Runtime {
1115/// type FullIdentification = sp_staking::Exposure<AccountId, Balance>;
1116/// type IdentificationOf = pallet_staking::DefaultExposureOf<Self>
1117/// }
1118/// ```
1119pub struct DefaultExposureOf<T>(core::marker::PhantomData<T>);
1120
1121impl<T: Config> Convert<T::AccountId, Option<Exposure<T::AccountId, BalanceOf<T>>>>
1122 for DefaultExposureOf<T>
1123{
1124 fn convert(validator: T::AccountId) -> Option<Exposure<T::AccountId, BalanceOf<T>>> {
1125 T::SessionInterface::validators()
1126 .contains(&validator)
1127 .then_some(Default::default())
1128 }
1129}
1130
1131/// An identification type that signifies the existence of a validator by returning `Some(())`, and
1132/// `None` otherwise. Also see the documentation of [`DefaultExposureOf`] for more info.
1133///
1134/// ```ignore
1135/// impl pallet_session::historical::Config for Runtime {
1136/// type FullIdentification = ();
1137/// type IdentificationOf = pallet_staking::UnitIdentificationOf<Self>
1138/// }
1139/// ```
1140pub struct UnitIdentificationOf<T>(core::marker::PhantomData<T>);
1141impl<T: Config> Convert<T::AccountId, Option<()>> for UnitIdentificationOf<T> {
1142 fn convert(validator: T::AccountId) -> Option<()> {
1143 DefaultExposureOf::<T>::convert(validator).map(|_default_exposure| ())
1144 }
1145}
1146
1147/// Filter historical offences out and only allow those from the bonding period.
1148pub struct FilterHistoricalOffences<T, R> {
1149 _inner: core::marker::PhantomData<(T, R)>,
1150}
1151
1152impl<T, Reporter, Offender, R, O> ReportOffence<Reporter, Offender, O>
1153 for FilterHistoricalOffences<Pallet<T>, R>
1154where
1155 T: Config,
1156 R: ReportOffence<Reporter, Offender, O>,
1157 O: Offence<Offender>,
1158{
1159 fn report_offence(reporters: Vec<Reporter>, offence: O) -> Result<(), OffenceError> {
1160 // Disallow any slashing from before the current bonding period.
1161 let offence_session = offence.session_index();
1162 let bonded_eras = BondedEras::<T>::get();
1163
1164 if bonded_eras.first().filter(|(_, start)| offence_session >= *start).is_some() {
1165 R::report_offence(reporters, offence)
1166 } else {
1167 <Pallet<T>>::deposit_event(Event::<T>::OldSlashingReportDiscarded {
1168 session_index: offence_session,
1169 });
1170 Ok(())
1171 }
1172 }
1173
1174 fn is_known_offence(offenders: &[Offender], time_slot: &O::TimeSlot) -> bool {
1175 R::is_known_offence(offenders, time_slot)
1176 }
1177}
1178
1179/// Wrapper struct for Era related information. It is not a pure encapsulation as these storage
1180/// items can be accessed directly but nevertheless, its recommended to use `EraInfo` where we
1181/// can and add more functions to it as needed.
1182pub struct EraInfo<T>(core::marker::PhantomData<T>);
1183impl<T: Config> EraInfo<T> {
1184 /// Returns true if validator has one or more page of era rewards not claimed yet.
1185 // Also looks at legacy storage that can be cleaned up after #433.
1186 pub fn pending_rewards(era: EraIndex, validator: &T::AccountId) -> bool {
1187 let page_count = if let Some(overview) = <ErasStakersOverview<T>>::get(&era, validator) {
1188 overview.page_count
1189 } else {
1190 if <ErasStakers<T>>::contains_key(era, validator) {
1191 // this means non paged exposure, and we treat them as single paged.
1192 1
1193 } else {
1194 // if no exposure, then no rewards to claim.
1195 return false
1196 }
1197 };
1198
1199 // check if era is marked claimed in legacy storage.
1200 if <Ledger<T>>::get(validator)
1201 .map(|l| l.legacy_claimed_rewards.contains(&era))
1202 .unwrap_or_default()
1203 {
1204 return false
1205 }
1206
1207 ClaimedRewards::<T>::get(era, validator).len() < page_count as usize
1208 }
1209
1210 /// Temporary function which looks at both (1) passed param `T::StakingLedger` for legacy
1211 /// non-paged rewards, and (2) `T::ClaimedRewards` for paged rewards. This function can be
1212 /// removed once `T::HistoryDepth` eras have passed and none of the older non-paged rewards
1213 /// are relevant/claimable.
1214 // Refer tracker issue for cleanup: https://github.com/paritytech/polkadot-sdk/issues/433
1215 pub(crate) fn is_rewards_claimed_with_legacy_fallback(
1216 era: EraIndex,
1217 ledger: &StakingLedger<T>,
1218 validator: &T::AccountId,
1219 page: Page,
1220 ) -> bool {
1221 ledger.legacy_claimed_rewards.binary_search(&era).is_ok() ||
1222 Self::is_rewards_claimed(era, validator, page)
1223 }
1224
1225 /// Check if the rewards for the given era and page index have been claimed.
1226 ///
1227 /// This is only used for paged rewards. Once older non-paged rewards are no longer
1228 /// relevant, `is_rewards_claimed_with_legacy_fallback` can be removed and this function can
1229 /// be made public.
1230 fn is_rewards_claimed(era: EraIndex, validator: &T::AccountId, page: Page) -> bool {
1231 ClaimedRewards::<T>::get(era, validator).contains(&page)
1232 }
1233
1234 /// Get exposure for a validator at a given era and page.
1235 ///
1236 /// This builds a paged exposure from `PagedExposureMetadata` and `ExposurePage` of the
1237 /// validator. For older non-paged exposure, it returns the clipped exposure directly.
1238 pub fn get_paged_exposure(
1239 era: EraIndex,
1240 validator: &T::AccountId,
1241 page: Page,
1242 ) -> Option<PagedExposure<T::AccountId, BalanceOf<T>>> {
1243 let overview = <ErasStakersOverview<T>>::get(&era, validator);
1244
1245 // return clipped exposure if page zero and paged exposure does not exist
1246 // exists for backward compatibility and can be removed as part of #13034
1247 if overview.is_none() && page == 0 {
1248 return Some(PagedExposure::from_clipped(<ErasStakersClipped<T>>::get(era, validator)))
1249 }
1250
1251 // no exposure for this validator
1252 if overview.is_none() {
1253 return None
1254 }
1255
1256 let overview = overview.expect("checked above; qed");
1257
1258 // validator stake is added only in page zero
1259 let validator_stake = if page == 0 { overview.own } else { Zero::zero() };
1260
1261 // since overview is present, paged exposure will always be present except when a
1262 // validator has only own stake and no nominator stake.
1263 let exposure_page = <ErasStakersPaged<T>>::get((era, validator, page)).unwrap_or_default();
1264
1265 // build the exposure
1266 Some(PagedExposure {
1267 exposure_metadata: PagedExposureMetadata { own: validator_stake, ..overview },
1268 exposure_page,
1269 })
1270 }
1271
1272 /// Get full exposure of the validator at a given era.
1273 pub fn get_full_exposure(
1274 era: EraIndex,
1275 validator: &T::AccountId,
1276 ) -> Exposure<T::AccountId, BalanceOf<T>> {
1277 let overview = <ErasStakersOverview<T>>::get(&era, validator);
1278
1279 if overview.is_none() {
1280 return ErasStakers::<T>::get(era, validator)
1281 }
1282
1283 let overview = overview.expect("checked above; qed");
1284
1285 let mut others = Vec::with_capacity(overview.nominator_count as usize);
1286 for page in 0..overview.page_count {
1287 let nominators = <ErasStakersPaged<T>>::get((era, validator, page));
1288 others.append(&mut nominators.map(|n| n.others).defensive_unwrap_or_default());
1289 }
1290
1291 Exposure { total: overview.total, own: overview.own, others }
1292 }
1293
1294 /// Returns the number of pages of exposure a validator has for the given era.
1295 ///
1296 /// For eras where paged exposure does not exist, this returns 1 to keep backward compatibility.
1297 pub(crate) fn get_page_count(era: EraIndex, validator: &T::AccountId) -> Page {
1298 <ErasStakersOverview<T>>::get(&era, validator)
1299 .map(|overview| {
1300 if overview.page_count == 0 && overview.own > Zero::zero() {
1301 // Even though there are no nominator pages, there is still validator's own
1302 // stake exposed which needs to be paid out in a page.
1303 1
1304 } else {
1305 overview.page_count
1306 }
1307 })
1308 // Always returns 1 page for older non-paged exposure.
1309 // FIXME: Can be cleaned up with issue #13034.
1310 .unwrap_or(1)
1311 }
1312
1313 /// Returns the next page that can be claimed or `None` if nothing to claim.
1314 pub(crate) fn get_next_claimable_page(
1315 era: EraIndex,
1316 validator: &T::AccountId,
1317 ledger: &StakingLedger<T>,
1318 ) -> Option<Page> {
1319 if Self::is_non_paged_exposure(era, validator) {
1320 return match ledger.legacy_claimed_rewards.binary_search(&era) {
1321 // already claimed
1322 Ok(_) => None,
1323 // Non-paged exposure is considered as a single page
1324 Err(_) => Some(0),
1325 }
1326 }
1327
1328 // Find next claimable page of paged exposure.
1329 let page_count = Self::get_page_count(era, validator);
1330 let all_claimable_pages: Vec<Page> = (0..page_count).collect();
1331 let claimed_pages = ClaimedRewards::<T>::get(era, validator);
1332
1333 all_claimable_pages.into_iter().find(|p| !claimed_pages.contains(p))
1334 }
1335
1336 /// Checks if exposure is paged or not.
1337 fn is_non_paged_exposure(era: EraIndex, validator: &T::AccountId) -> bool {
1338 <ErasStakersClipped<T>>::contains_key(&era, validator)
1339 }
1340
1341 /// Returns validator commission for this era and page.
1342 pub(crate) fn get_validator_commission(
1343 era: EraIndex,
1344 validator_stash: &T::AccountId,
1345 ) -> Perbill {
1346 <ErasValidatorPrefs<T>>::get(&era, validator_stash).commission
1347 }
1348
1349 /// Creates an entry to track validator reward has been claimed for a given era and page.
1350 /// Noop if already claimed.
1351 pub(crate) fn set_rewards_as_claimed(era: EraIndex, validator: &T::AccountId, page: Page) {
1352 let mut claimed_pages = ClaimedRewards::<T>::get(era, validator);
1353
1354 // this should never be called if the reward has already been claimed
1355 if claimed_pages.contains(&page) {
1356 defensive!("Trying to set an already claimed reward");
1357 // nevertheless don't do anything since the page already exist in claimed rewards.
1358 return
1359 }
1360
1361 // add page to claimed entries
1362 claimed_pages.push(page);
1363 ClaimedRewards::<T>::insert(era, validator, claimed_pages);
1364 }
1365
1366 /// Store exposure for elected validators at start of an era.
1367 pub fn set_exposure(
1368 era: EraIndex,
1369 validator: &T::AccountId,
1370 exposure: Exposure<T::AccountId, BalanceOf<T>>,
1371 ) {
1372 let page_size = T::MaxExposurePageSize::get().defensive_max(1);
1373
1374 let nominator_count = exposure.others.len();
1375 // expected page count is the number of nominators divided by the page size, rounded up.
1376 let expected_page_count = nominator_count
1377 .defensive_saturating_add((page_size as usize).defensive_saturating_sub(1))
1378 .saturating_div(page_size as usize);
1379
1380 let (exposure_metadata, exposure_pages) = exposure.into_pages(page_size);
1381 defensive_assert!(exposure_pages.len() == expected_page_count, "unexpected page count");
1382
1383 <ErasStakersOverview<T>>::insert(era, &validator, &exposure_metadata);
1384 exposure_pages.iter().enumerate().for_each(|(page, paged_exposure)| {
1385 <ErasStakersPaged<T>>::insert((era, &validator, page as Page), &paged_exposure);
1386 });
1387 }
1388
1389 /// Store total exposure for all the elected validators in the era.
1390 pub(crate) fn set_total_stake(era: EraIndex, total_stake: BalanceOf<T>) {
1391 <ErasTotalStake<T>>::insert(era, total_stake);
1392 }
1393}
1394
1395/// A utility struct that provides a way to check if a given account is a staker.
1396///
1397/// This struct implements the `Contains` trait, allowing it to determine whether
1398/// a particular account is currently staking by checking if the account exists in
1399/// the staking ledger.
1400pub struct AllStakers<T: Config>(core::marker::PhantomData<T>);
1401
1402impl<T: Config> Contains<T::AccountId> for AllStakers<T> {
1403 /// Checks if the given account ID corresponds to a staker.
1404 ///
1405 /// # Returns
1406 /// - `true` if the account has an entry in the staking ledger (indicating it is staking).
1407 /// - `false` otherwise.
1408 fn contains(account: &T::AccountId) -> bool {
1409 Ledger::<T>::contains_key(account)
1410 }
1411}
1412
1413/// Configurations of the benchmarking of the pallet.
1414pub trait BenchmarkingConfig {
1415 /// The maximum number of validators to use.
1416 type MaxValidators: Get<u32>;
1417 /// The maximum number of nominators to use.
1418 type MaxNominators: Get<u32>;
1419}
1420
1421/// A mock benchmarking config for pallet-staking.
1422///
1423/// Should only be used for testing.
1424#[cfg(feature = "std")]
1425pub struct TestBenchmarkingConfig;
1426
1427#[cfg(feature = "std")]
1428impl BenchmarkingConfig for TestBenchmarkingConfig {
1429 type MaxValidators = frame_support::traits::ConstU32<100>;
1430 type MaxNominators = frame_support::traits::ConstU32<100>;
1431}