mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-19 04:11:09 +00:00
Extended Balance Type for Staking's Election (#2134)
* First draft of extended balance type * Test cleanup. * Update staking docs. * Add a good failing test case for quintill * Bring back saturating. * Some final fixes * A few more. * Update wasm; Bump spec; * Re-bump. * Custom lossy conversion from currency to vote * remove print * Fix reverse conversion issue. * void. Re-trigger ci.
This commit is contained in:
@@ -15,12 +15,13 @@
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! # Staking Module
|
||||
//!
|
||||
//! <!-- Original author of paragraph: @gavofyork -->
|
||||
//! The staking module is the means by which a set of network maintainers (known as "authorities" in some contexts and "validators" in others)
|
||||
//!
|
||||
//! The staking module 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
|
||||
//! normal operation but are held at pain of _slash_ (expropriation) should the staked maintainer be found not to be
|
||||
//! discharging their duties properly.
|
||||
//!
|
||||
//! You can start using the Staking module by implementing the staking [`Trait`].
|
||||
//!
|
||||
//! ## Overview
|
||||
@@ -34,12 +35,13 @@
|
||||
//! - Stash account: The account holding an owner's funds used for staking.
|
||||
//! - Controller account: The account which 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 their funds ([reference](#references)).
|
||||
//! - Slash: The punishment of a staker by reducing their funds.
|
||||
//!
|
||||
//! ### Goals
|
||||
//! <!-- Original author of paragraph: @gavofyork -->
|
||||
//!
|
||||
//! The staking system in Substrate NPoS is designed to achieve three goals:
|
||||
//!
|
||||
//! - It should be possible to stake funds that are controlled by a cold wallet.
|
||||
//! - It should be possible to withdraw some, or deposit more, funds without interrupting the role of an entity.
|
||||
//! - It should be possible to switch between roles (nominator, validator, idle) with minimal overhead.
|
||||
@@ -48,59 +50,62 @@
|
||||
//!
|
||||
//! #### Staking
|
||||
//!
|
||||
//! Almost any interaction with the staking module requires at least one account to become **bonded**, also known as
|
||||
//! being a **staker**. For this, all that it is needed is a secondary _**stash account**_ which will hold the staked funds.
|
||||
//! Henceforth, the former account that initiated the interest is called the **controller** and the latter, holding the
|
||||
//! funds, is named the **stash**. Also, note that this implies that entering the staking process requires an _account
|
||||
//! pair_, one to take the role of the controller and one to be the frozen stash account (any value locked in
|
||||
//! stash cannot be used, hence called _frozen_). This process in the public API is mostly referred to as _bonding_ via
|
||||
//! the `bond()` function.
|
||||
//! Almost any interaction with the staking module requires a process of _**bonding**_ (also known as
|
||||
//! being a _staker_). To become *bonded* a fund-holding account known as the _stash account_, which holds some of 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.
|
||||
//!
|
||||
//! Any account pair successfully placed at stake can accept three possible roles, namely: `validate`, `nominate` or
|
||||
//! simply `chill`. Note that during the process of accepting these roles, the _controller_ account is always responsible
|
||||
//! for declaring interest and the _stash_ account stays untouched, without directly interacting in any operation.
|
||||
//! An account pair can become bonded using the [`bond`](./enum.Call.html#variant.bond) call.
|
||||
//!
|
||||
//! Stash accounts can change their associated controller using the [`set_controller`](./enum.Call.html#variant.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`](./enum.Call.html#variant.validate), [`nominate`](./enum.Call.html#variant.nominate) and [`chill`](./enum.Call.html#variant.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 via the `validate()` call.
|
||||
//! 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`](./enum.Call.html#variant.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 _immediately_, meaning that its
|
||||
//! votes will be taken into account at the next election round. As mentioned above, a nominator must also place some
|
||||
//! funds in a stash account, essentially indicating the _weight_ of its vote. In some sense, the nominator bets on the
|
||||
//! honesty of a set of validators by voting for them, with the goal of having a share of the reward granted to them.
|
||||
//! Any rewards given to a validator is shared among that validator and all of the nominators that voted for it. The
|
||||
//! same logic applies to the slash of a validator; if a validator misbehaves all of its nominators also get slashed.
|
||||
//! 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.
|
||||
//! because the nominators will also lose funds if they vote poorly.
|
||||
//!
|
||||
//! An account can become a nominator via the [`nominate`](enum.Call.html#variant.nominate) call.
|
||||
//!
|
||||
//! #### Rewards and Slash
|
||||
//!
|
||||
//! The **reward and slashing** procedure are the core of the staking module, attempting to _embrace valid behavior_
|
||||
//! while _punishing any misbehavior or lack of availability_. Slashing can occur at any point in time, once
|
||||
//! misbehavior is reported. One such misbehavior is a validator being detected as offline more than a certain number of
|
||||
//! times. Once slashing is determined, a value is deducted from the balance of the validator and all the nominators who
|
||||
//! voted for this validator. Same rules apply to the rewards in the sense of being shared among a validator and its
|
||||
//! associated nominators.
|
||||
//! The **reward and slashing** procedure is the core of the staking module, attempting to _embrace valid behavior_
|
||||
//! while _punishing any misbehavior or lack of availability_.
|
||||
//!
|
||||
//! 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).
|
||||
//!
|
||||
//! 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 (again, both effects apply at the beginning of the next era). An account can
|
||||
//! step back via the `chill()` call.
|
||||
//! be a candidate for the next election.
|
||||
//!
|
||||
//! An account can step back via the [`chill`](enum.Call.html#variant.chill) call.
|
||||
//!
|
||||
//! ## Interface
|
||||
//!
|
||||
//! ### Types
|
||||
//!
|
||||
//! - `Currency`: Used as the measurement means of staking and funds management.
|
||||
//!
|
||||
//! ### Dispatchable
|
||||
//!
|
||||
//! The Dispatchable functions of the staking module enable the steps needed for entities to accept and change their
|
||||
@@ -110,7 +115,7 @@
|
||||
//!
|
||||
//! ### Public
|
||||
//! The staking module contains many public storage items and (im)mutable functions. Please refer to the [struct list](#structs)
|
||||
//! below and the [`Module`](https://crates.parity.io/srml_staking/struct.Module.html) struct definition for more details.
|
||||
//! below and the [`Module`] struct definition for more details.
|
||||
//!
|
||||
//! ## Usage
|
||||
//!
|
||||
@@ -134,8 +139,6 @@
|
||||
//! Staking::validate(Origin::signed(4), ValidatorPrefs::default());
|
||||
//! ```
|
||||
//!
|
||||
//! Note that, as mentioned, the stash account is transparent in such calls and only the controller initiates the function calls.
|
||||
//!
|
||||
//! Similarly, to state desire in nominating:
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
@@ -149,59 +152,96 @@
|
||||
//! Staking::chill(Origin::signed(4));
|
||||
//! ```
|
||||
//!
|
||||
//! You can find the equivalent of the above calls in your [Substrate UI](https://substrate-ui.parity.io).
|
||||
//! ### Snippet: Reporting Misbehavior
|
||||
//!
|
||||
//! ```
|
||||
//! use srml_support::{decl_module, dispatch::Result};
|
||||
//! use system::ensure_signed;
|
||||
//! use srml_staking::{self as staking};
|
||||
//!
|
||||
//! pub trait Trait: staking::Trait {}
|
||||
//!
|
||||
//! decl_module! {
|
||||
//! pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
//! /// Report whoever calls this function as offline once.
|
||||
//! pub fn report_sender(origin) -> Result {
|
||||
//! let reported = ensure_signed(origin)?;
|
||||
//! <staking::Module<T>>::on_offline_validator(reported, 1);
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//! # fn main() { }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Implementation Details
|
||||
//!
|
||||
//! ### Slot Stake
|
||||
//!
|
||||
//! The term `slot_stake` will be used throughout this section. It refers to a value calculated at the end of each era,
|
||||
//! containing the _minimum value at stake among all validators._
|
||||
//! The term [`SlotStake`] will be used throughout this section. It refers to a value calculated at the end of each era,
|
||||
//! containing the _minimum value at stake among all validators._ Note that a validator's value at stake might be a combination of
|
||||
//! The validator's own stake and the votes it received. See [`Exposure`] for more details.
|
||||
//!
|
||||
//! ### Reward Calculation
|
||||
//!
|
||||
//! - Rewards are recorded **per-session** and paid **per-era**. The value of the reward for each session is calculated at
|
||||
//! the end of the session based on the timeliness of the session, then accumulated to be paid later. The value of
|
||||
//! the new _per-session-reward_ is calculated at the end of each era by multiplying `slot_stake` and a configuration
|
||||
//! storage item named `SessionReward`.
|
||||
//! - Once a new era is triggered, rewards are paid to the validators and the associated nominators.
|
||||
//! - The validator can declare an amount, named `validator_payment`, that does not get shared with the nominators at
|
||||
//! each reward payout through their `ValidatorPrefs`. This value gets deducted from the total reward that can be paid.
|
||||
//! The remaining portion is split among the validator and all of the nominators who had a vote for this validator,
|
||||
//! proportional to their staked value.
|
||||
//! - All entities who receive a reward have the option to choose their reward destination, through the `Payee` storage item (see `set_payee()`), to be one of the following:
|
||||
//! - Controller account.
|
||||
//! Rewards are recorded **per-session** and paid **per-era**. The value of the reward for each session is calculated at
|
||||
//! the end of the session based on the timeliness of the session, then accumulated to be paid later. The value of
|
||||
//! the new _per-session-reward_ is calculated at the end of each era by multiplying [`SlotStake`] and [`SessionReward`]
|
||||
//! (`SessionReward` is the multiplication factor, represented by a number between 0 and 1).
|
||||
//! Once a new era is triggered, rewards are paid to the validators and the associated nominators.
|
||||
//!
|
||||
//! The validator can declare an amount, named [`validator_payment`](./struct.ValidatorPrefs.html#structfield.validator_payment), that does not get shared with the nominators at
|
||||
//! each reward payout through their [`ValidatorPrefs`]. This value gets deducted from the total reward that can be paid.
|
||||
//! The remaining portion is split among the validator and all of the nominators that nominated the validator,
|
||||
//! proportional to the value staked behind this validator
|
||||
//! (_i.e._ dividing the [`own`](./struct.Exposure.html#structfield.own) or [`others`](./struct.Exposure.html#structfield.others) by [`total`](./struct.Exposure.html#structfield.total) in [`Exposure`]).
|
||||
//!
|
||||
//! All entities who receive a reward have the option to choose their reward destination,
|
||||
//! through the [`Payee`] storage item (see [`set_payee`](enum.Call.html#variant.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.
|
||||
//!
|
||||
//! ### Slashing details
|
||||
//!
|
||||
//! - A validator can be _reported_ to be offline at any point via `on_offline_validator` public function.
|
||||
//! - Each validator declares how many times it can be _reported_ before it actually gets slashed via the
|
||||
//! `unstake_threshold` in `ValidatorPrefs`. On top of this, the module also introduces an `OfflineSlashGrace`,
|
||||
//! which applies to all validators and prevents them from getting immediately slashed.
|
||||
//! - Similar to the reward value, the slash value is updated at the end of each era by multiplying `slot_stake` and a
|
||||
//! configuration storage item, `OfflineSlash`.
|
||||
//! - Once a validator has been reported a sufficient number of times, the actual value that gets deducted from that
|
||||
//! validator, and every single nominator that voted for it is calculated by multiplying the result of the above point
|
||||
//! by `2.pow(unstake_threshold)`.
|
||||
//! - If the previous overflows, then `slot_stake` is used.
|
||||
//! - If the previous is more than what the validator/nominator has in stake, all of its stake is slashed (`.max(total_stake)`).
|
||||
//! A validator can be _reported_ to be offline at any point via [`on_offline_validator`](enum.Call.html#variant.on_offline_validator) public function.
|
||||
//! Each validator declares how many times it can be _reported_ before it actually gets slashed via their
|
||||
//! `unstake_threshold` in [`ValidatorPrefs`].
|
||||
//!
|
||||
//! On top of this, staking module also introduces an [`OfflineSlashGrace`], which applies to all validators and prevents
|
||||
//! them from getting immediately slashed.
|
||||
//!
|
||||
//! Essentially, a validator gets slashed once they have been reported more than [`OfflineSlashGrace`] + [`unstake_threshold`](./struct.ValidatorPrefs.html#structfield.unstake_threshold) times.
|
||||
//! Getting slashed due to offline report always leads to being _unstaked_ (_i.e._ removed as a validator candidate) as the consequence.
|
||||
//!
|
||||
//! The base slash value is computed _per slash-event_ by multiplying [`OfflineSlash`] and the `total` [`Exposure`]. This value
|
||||
//! is then multiplied by `2.pow(unstake_threshold)` to obtain the final slash value.
|
||||
//! All individual accounts' punishments are capped at their total stake (NOTE: This cap should never come into force in a correctly implemented, non-corrupted, well-configured system).
|
||||
//!
|
||||
//! ### 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. Note that the funds
|
||||
//! are not immediately accessible, instead, a duration denoted by `BondingDuration` (in number of eras) must pass until the funds can actually be removed.
|
||||
//! - To actually remove the funds, once the bonding duration is over, `withdraw_unbonded()` can be used.
|
||||
//! - As opposed to the above, additional funds can be added to the stash account via the `bond_extra()` transaction call.
|
||||
//! The controller account can free a portion (or all) of the funds using the [`unbond`](enum.Call.html#variant.unbond) call.
|
||||
//! Note that the funds are not immediately accessible. Instead, a duration denoted by [`BondingDuration`] (in number of eras)
|
||||
//! must pass until the funds can actually be removed. Once the [`BondingDuration`] is over the [`withdraw_unbonded`]((enum.Call.html#variant.withdraw_unbonded)) call can be used
|
||||
//! to actually withdraw the funds.
|
||||
//!
|
||||
//! ### Election algorithm details.
|
||||
//!### 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.
|
||||
//!
|
||||
//! 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).
|
||||
//!
|
||||
//! ## GenesisConfig
|
||||
//!
|
||||
//! See the [`GensisConfig`] for a list of attributes that can be provided.
|
||||
//! See the [`GenesisConfig`] for a list of attributes that can be provided.
|
||||
//!
|
||||
//! ## Related Modules
|
||||
//!
|
||||
@@ -209,10 +249,6 @@
|
||||
//! - [**Sessions**](https://crates.parity.io/srml_session/index.html): Used to manage sessions. Also, a list of new validators is also stored in the sessions module's `Validators` at the end of each era.
|
||||
//! - [**System**](https://crates.parity.io/srml_system/index.html): Used to obtain block number and time, among other details.
|
||||
//!
|
||||
//! # References
|
||||
//!
|
||||
//! 1. This document is written as a more verbose version of the original [Staking.md](../Staking.md) file. Some sections, are taken directly from the aforementioned document.
|
||||
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
@@ -224,11 +260,11 @@ use srml_support::{StorageValue, StorageMap, EnumerableStorageMap, dispatch::Res
|
||||
use srml_support::{decl_module, decl_event, decl_storage, ensure};
|
||||
use srml_support::traits::{
|
||||
Currency, OnFreeBalanceZero, OnDilution, LockIdentifier, LockableCurrency, WithdrawReasons,
|
||||
OnUnbalanced, Imbalance
|
||||
OnUnbalanced, Imbalance,
|
||||
};
|
||||
use session::OnSessionChange;
|
||||
use primitives::Perbill;
|
||||
use primitives::traits::{Zero, One, As, StaticLookup, CheckedSub, Saturating, Bounded};
|
||||
use primitives::traits::{Convert, Zero, One, As, StaticLookup, CheckedSub, Saturating, Bounded};
|
||||
#[cfg(feature = "std")]
|
||||
use primitives::{Serialize, Deserialize};
|
||||
use system::ensure_signed;
|
||||
@@ -249,7 +285,7 @@ const MAX_UNSTAKE_THRESHOLD: u32 = 10;
|
||||
pub enum StakerStatus<AccountId> {
|
||||
/// Chilling.
|
||||
Idle,
|
||||
/// Declared state in validating or already participating in it.
|
||||
/// Declared desire in validating or already participating in it.
|
||||
Validator,
|
||||
/// Nominating for a group of other stakers.
|
||||
Nominator(Vec<AccountId>),
|
||||
@@ -381,6 +417,10 @@ pub trait Trait: system::Trait + session::Trait {
|
||||
Currency<Self::AccountId> +
|
||||
LockableCurrency<Self::AccountId, Moment=Self::BlockNumber>;
|
||||
|
||||
/// Convert a balance into a number used for election calculation.
|
||||
/// This must fit into a `u64` but is allowed to be sensibly lossy.
|
||||
type CurrencyToVote: Convert<BalanceOf<Self>, u64> + Convert<u128, BalanceOf<Self>>;
|
||||
|
||||
/// Some tokens minted.
|
||||
type OnRewardMinted: OnDilution<BalanceOf<Self>>;
|
||||
|
||||
@@ -520,7 +560,7 @@ decl_module! {
|
||||
/// Take the origin account as a stash and lock up `value` of its balance. `controller` will be the
|
||||
/// account that controls it.
|
||||
///
|
||||
/// The dispatch origin for this call must be _Signed_.
|
||||
/// The dispatch origin for this call must be _Signed_ by the stash account.
|
||||
fn bond(origin, controller: <T::Lookup as StaticLookup>::Source, #[compact] value: BalanceOf<T>, payee: RewardDestination) {
|
||||
let stash = ensure_signed(origin)?;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user