mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 18:41:05 +00:00
New sessions, kill consensus module (#2802)
* Draft of new sessions * Reintroduce tuple impls * Move staking module to new session API * More work on staking and grandpa. * Use iterator to avoid cloning and tuple macro * Make runtime build again * Polish the OpaqueKeys devex * Move consensus logic into system & aura. * Fix up system module * Get build mostly going. Stuck at service.rs * Building again * Update srml/staking/src/lib.rs Co-Authored-By: DemiMarie-parity <48690212+DemiMarie-parity@users.noreply.github.com> * Refactoring out Consensus module, AuthorityIdOf, &c. * Refactored out DigestItem::AuthoritiesChanged. Building. * Remove tentative code * Remove invalid comment * Make Seal opaque and introduce nice methods for handling opaque items. * Start to use proper digest for Aura authorities tracking. * Fix up grandpa, remove system::Raw/Log * Refactor Grandpa to use new logging infrastructure. Also make authorityid/sessionkey static. Switch over to storing authorities in a straight Vec. * Building again * Tidy up some AuthorityIds * Expunge most of the rest of the AuthorityKey confusion. Also, de-generify Babe and re-generify Aura. * Remove cruft * Untangle last of the `AuthorityId`s. * Sort out finality_tracker * Refactor median getting * Apply suggestions from code review Co-Authored-By: Robert Habermeier <rphmeier@gmail.com> * Session tests works * Update core/sr-primitives/src/generic/digest.rs Co-Authored-By: DemiMarie-parity <48690212+DemiMarie-parity@users.noreply.github.com> * Session tests works * Fix for staking from @dvc94ch * log an error * fix test runtime build * Some test fixes * Staking mock update to new session api. * Fix build. * Move OpaqueKeys to primitives. * Use on_initialize instead of check_rotate_session. * Update tests to new staking api. * fixup mock * Fix bond_extra_and_withdraw_unbonded_works. * Fix bond_with_little_staked_value_bounded_by_slot_stake. * Fix bond_with_no_staked_value. * Fix change_controller_works. * Fix less_than_needed_candidates_works. * Fix multi_era_reward_should_work. * Fix nominating_and_rewards_should_work. * Fix nominators_also_get_slashed. * Fix phragmen_large_scale_test. * Fix phragmen_poc_works. * Fix phragmen_score_should_be_accurate_on_large_stakes. * Fix phragmen_should_not_overflow. * Fix reward_destination_works. * Fix rewards_should_work. * Fix sessions_and_eras_should_work. * Fix slot_stake_is_least_staked_validator. * Fix too_many_unbond_calls_should_not_work. * Fix wrong_vote_is_null. * Fix runtime. * Fix wasm runtime build. * Update Cargo.lock * Fix warnings. * Fix grandpa tests. * Fix test-runtime build. * Fix template node build. * Fix stuff. * Update Cargo.lock to fix CI * Re-add missing AuRa logs Runtimes are required to know about every digest they receive ― they panic otherwise. This re-adds support for AuRa pre-runtime digests. * Update core/consensus/babe/src/digest.rs Co-Authored-By: DemiMarie-parity <48690212+DemiMarie-parity@users.noreply.github.com> * Kill log trait and all that jazz. * Refactor staking tests. * Fix ci runtime wasm check. * Line length 120. * Make tests build again * Remove trailing commas in function declarations The `extern_functions!` macro doesn’t like them, perhaps due to a bug in rustc. * Fix type error * Fix compilation errors * Fix a test * Another couple of fixes * Fix another test * More test fixes * Another test fix * Bump runtime. * Wrap long line * Fix build, remove redundant code. * Issue to track TODO * Leave the benchmark code alone. * Fix missing `std::time::{Instant, Duration}` * Indentation * Aura ConsensusLog as enum
This commit is contained in:
+259
-247
@@ -24,24 +24,25 @@
|
||||
//!
|
||||
//! ## Overview
|
||||
//!
|
||||
//! 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 discharging its duties properly.
|
||||
//! 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
|
||||
//! discharging its duties properly.
|
||||
//!
|
||||
//! ### Terminology
|
||||
//! <!-- Original author of paragraph: @gavofyork -->
|
||||
//!
|
||||
//! - 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.
|
||||
//! - 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.
|
||||
//! - 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
|
||||
@@ -57,50 +58,55 @@
|
||||
//!
|
||||
//! #### Staking
|
||||
//!
|
||||
//! 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 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.
|
||||
//! 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 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`](./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`](./enum.StakerStatus.html)). There are three corresponding instructions to change between roles, namely:
|
||||
//! There are three possible roles that any staked account pair can be in: `Validator`, `Nominator`
|
||||
//! and `Idle` (defined in [`StakerStatus`](./enum.StakerStatus.html)). 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.
|
||||
//! 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`](./enum.Call.html#variant.validate) call.
|
||||
//! 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 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.
|
||||
//! 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`](enum.Call.html#variant.nominate) call.
|
||||
//!
|
||||
//! #### Rewards and Slash
|
||||
//!
|
||||
//! 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_.
|
||||
//! 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).
|
||||
//! 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.
|
||||
@@ -108,9 +114,9 @@
|
||||
//!
|
||||
//! #### 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.
|
||||
//! 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`](enum.Call.html#variant.chill) call.
|
||||
//!
|
||||
@@ -118,8 +124,8 @@
|
||||
//!
|
||||
//! ### Dispatchable Functions
|
||||
//!
|
||||
//! The dispatchable functions of the Staking module enable the steps needed for entities to accept and change their
|
||||
//! role, alongside some helper functions to get/set the metadata of the module.
|
||||
//! The dispatchable functions of the Staking module enable the steps needed for entities to accept
|
||||
//! and change their role, alongside some helper functions to get/set the metadata of the module.
|
||||
//!
|
||||
//! ### Public Functions
|
||||
//!
|
||||
@@ -153,30 +159,34 @@
|
||||
//!
|
||||
//! ### Slot Stake
|
||||
//!
|
||||
//! The term [`SlotStake`](./struct.Module.html#method.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._
|
||||
//! 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`](./struct.Exposure.html) for more details.
|
||||
//! The term [`SlotStake`](./struct.Module.html#method.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._ 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`](./struct.Exposure.html)
|
||||
//! 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 `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 their associated nominators.
|
||||
//! 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 their 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 its [`ValidatorPrefs`](./struct.ValidatorPrefs.html). 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`](./struct.Exposure.html)).
|
||||
//! [`validator_payment`](./struct.ValidatorPrefs.html#structfield.validator_payment), that does not
|
||||
//! get shared with the nominators at each reward payout through its
|
||||
//! [`ValidatorPrefs`](./struct.ValidatorPrefs.html). 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`](./struct.Exposure.html)).
|
||||
//!
|
||||
//! All entities who receive a reward have the option to choose their reward destination
|
||||
//! through the [`Payee`](./struct.Payee.html) storage item (see [`set_payee`](enum.Call.html#variant.set_payee)),
|
||||
//! to be one of the following:
|
||||
//! through the [`Payee`](./struct.Payee.html) 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.
|
||||
@@ -185,8 +195,8 @@
|
||||
//! ### Slashing details
|
||||
//!
|
||||
//! A validator can be _reported_ to be offline at any point via the public function
|
||||
//! [`on_offline_validator`](enum.Call.html#variant.on_offline_validator). Each validator declares how many times it
|
||||
//! can be _reported_ before it actually gets slashed via its
|
||||
//! [`on_offline_validator`](enum.Call.html#variant.on_offline_validator). Each validator declares
|
||||
//! how many times it can be _reported_ before it actually gets slashed via its
|
||||
//! [`ValidatorPrefs::unstake_threshold`](./struct.ValidatorPrefs.html#structfield.unstake_threshold).
|
||||
//!
|
||||
//! On top of this, the Staking module also introduces an
|
||||
@@ -199,35 +209,38 @@
|
||||
//! the consequence.
|
||||
//!
|
||||
//! The base slash value is computed _per slash-event_ by multiplying
|
||||
//! [`OfflineSlash`](./struct.Module.html#method.offline_slash) 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).
|
||||
//! [`OfflineSlash`](./struct.Module.html#method.offline_slash) 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`](enum.Call.html#variant.unbond)
|
||||
//! call. Note that the funds are not immediately accessible. Instead, a duration denoted by
|
||||
//! [`BondingDuration`](./struct.BondingDuration.html) (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.
|
||||
//! 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`](./struct.BondingDuration.html)
|
||||
//! (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.
|
||||
//!
|
||||
//! Note that there is a limitation to the number of fund-chunks that can be scheduled to be unlocked in the future
|
||||
//! via [`unbond`](enum.Call.html#variant.unbond).
|
||||
//! In case this maximum (`MAX_UNLOCKING_CHUNKS`) is reached, the bonded account _must_ first wait until a successful
|
||||
//! Note that there is a limitation to the number of fund-chunks that can be scheduled to be
|
||||
//! unlocked in the future via [`unbond`](enum.Call.html#variant.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 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 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
|
||||
//!
|
||||
@@ -236,8 +249,8 @@
|
||||
//! ## Related Modules
|
||||
//!
|
||||
//! - [Balances](../srml_balances/index.html): Used to manage values at stake.
|
||||
//! - [Session](../srml_session/index.html): Used to manage sessions. Also, a list of new validators is
|
||||
//! stored in the Session module's `Validators` at the end of each era.
|
||||
//! - [Session](../srml_session/index.html): Used to manage sessions. Also, a list of new validators
|
||||
//! is stored in the Session module's `Validators` at the end of each era.
|
||||
|
||||
#![recursion_limit="128"]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
@@ -261,17 +274,17 @@ mod benches;
|
||||
use runtime_io::with_storage;
|
||||
use rstd::{prelude::*, result, collections::btree_map::BTreeMap};
|
||||
use parity_codec::{HasCompact, Encode, Decode};
|
||||
use srml_support::{ StorageValue, StorageMap, EnumerableStorageMap, dispatch::Result,
|
||||
decl_module, decl_event, decl_storage, ensure,
|
||||
traits::{Currency, OnFreeBalanceZero, OnDilution, LockIdentifier, LockableCurrency,
|
||||
WithdrawReasons, OnUnbalanced, Imbalance
|
||||
use srml_support::{
|
||||
StorageValue, StorageMap, EnumerableStorageMap, decl_module, decl_event,
|
||||
decl_storage, ensure, traits::{
|
||||
Currency, OnFreeBalanceZero, OnDilution, LockIdentifier, LockableCurrency,
|
||||
WithdrawReasons, OnUnbalanced, Imbalance, Get
|
||||
}
|
||||
};
|
||||
use session::OnSessionChange;
|
||||
use session::{OnSessionEnding, SessionIndex};
|
||||
use primitives::Perbill;
|
||||
use primitives::traits::{
|
||||
Convert, Zero, One, StaticLookup, CheckedSub, CheckedShl, Saturating,
|
||||
Bounded, SaturatedConversion
|
||||
Convert, Zero, One, StaticLookup, CheckedSub, CheckedShl, Saturating, Bounded
|
||||
};
|
||||
#[cfg(feature = "std")]
|
||||
use primitives::{Serialize, Deserialize};
|
||||
@@ -286,6 +299,9 @@ const MAX_UNSTAKE_THRESHOLD: u32 = 10;
|
||||
const MAX_UNLOCKING_CHUNKS: usize = 32;
|
||||
const STAKING_ID: LockIdentifier = *b"staking ";
|
||||
|
||||
/// Counter for the number of eras that have passed.
|
||||
pub type EraIndex = u32;
|
||||
|
||||
/// Indicates the initial status of the staker.
|
||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
||||
pub enum StakerStatus<AccountId> {
|
||||
@@ -322,7 +338,8 @@ pub struct ValidatorPrefs<Balance: HasCompact> {
|
||||
/// Validator should ensure this many more slashes than is necessary before being unstaked.
|
||||
#[codec(compact)]
|
||||
pub unstake_threshold: u32,
|
||||
/// Reward that validator takes up-front; only the rest is split between themselves and nominators.
|
||||
/// Reward that validator takes up-front; only the rest is split between themselves and
|
||||
/// nominators.
|
||||
#[codec(compact)]
|
||||
pub validator_payment: Balance,
|
||||
}
|
||||
@@ -339,19 +356,19 @@ impl<B: Default + HasCompact + Copy> Default for ValidatorPrefs<B> {
|
||||
/// Just a Balance/BlockNumber tuple to encode when a chunk of funds will be unlocked.
|
||||
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
pub struct UnlockChunk<Balance: HasCompact, BlockNumber: HasCompact> {
|
||||
pub struct UnlockChunk<Balance: HasCompact> {
|
||||
/// Amount of funds to be unlocked.
|
||||
#[codec(compact)]
|
||||
value: Balance,
|
||||
/// Era number at which point it'll be unlocked.
|
||||
#[codec(compact)]
|
||||
era: BlockNumber,
|
||||
era: EraIndex,
|
||||
}
|
||||
|
||||
/// The ledger of a (bonded) stash.
|
||||
#[derive(PartialEq, Eq, Clone, Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
pub struct StakingLedger<AccountId, Balance: HasCompact, BlockNumber: HasCompact> {
|
||||
pub struct StakingLedger<AccountId, Balance: HasCompact> {
|
||||
/// 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.
|
||||
@@ -364,17 +381,16 @@ pub struct StakingLedger<AccountId, Balance: HasCompact, BlockNumber: HasCompact
|
||||
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<UnlockChunk<Balance, BlockNumber>>,
|
||||
pub unlocking: Vec<UnlockChunk<Balance>>,
|
||||
}
|
||||
|
||||
impl<
|
||||
AccountId,
|
||||
Balance: HasCompact + Copy + Saturating,
|
||||
BlockNumber: HasCompact + PartialOrd
|
||||
> StakingLedger<AccountId, Balance, BlockNumber> {
|
||||
> StakingLedger<AccountId, Balance> {
|
||||
/// Remove entries from `unlocking` that are sufficiently old and reduce the
|
||||
/// total by the sum of their balances.
|
||||
fn consolidate_unlocked(self, current_era: BlockNumber) -> Self {
|
||||
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 {
|
||||
@@ -414,12 +430,17 @@ pub struct Exposure<AccountId, Balance: HasCompact> {
|
||||
}
|
||||
|
||||
type BalanceOf<T> = <<T as Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance;
|
||||
type PositiveImbalanceOf<T> = <<T as Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::PositiveImbalance;
|
||||
type NegativeImbalanceOf<T> = <<T as Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::NegativeImbalance;
|
||||
type PositiveImbalanceOf<T> =
|
||||
<<T as Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::PositiveImbalance;
|
||||
type NegativeImbalanceOf<T> =
|
||||
<<T as Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::NegativeImbalance;
|
||||
|
||||
type RawAssignment<T> = (<T as system::Trait>::AccountId, ExtendedBalance);
|
||||
type Assignment<T> = (<T as system::Trait>::AccountId, ExtendedBalance, BalanceOf<T>);
|
||||
type ExpoMap<T> = BTreeMap<<T as system::Trait>::AccountId, Exposure<<T as system::Trait>::AccountId, BalanceOf<T>>>;
|
||||
type ExpoMap<T> = BTreeMap<
|
||||
<T as system::Trait>::AccountId,
|
||||
Exposure<<T as system::Trait>::AccountId, BalanceOf<T>>
|
||||
>;
|
||||
|
||||
pub trait Trait: system::Trait + session::Trait {
|
||||
/// The staking balance.
|
||||
@@ -443,6 +464,12 @@ pub trait Trait: system::Trait + session::Trait {
|
||||
|
||||
/// Handler for the unbalanced increment when rewarding a staker.
|
||||
type Reward: OnUnbalanced<PositiveImbalanceOf<Self>>;
|
||||
|
||||
/// Number of sessions per era.
|
||||
type SessionsPerEra: Get<SessionIndex>;
|
||||
|
||||
/// Number of eras that staked funds must remain bonded for.
|
||||
type BondingDuration: Get<EraIndex>;
|
||||
}
|
||||
|
||||
decl_storage! {
|
||||
@@ -451,26 +478,25 @@ decl_storage! {
|
||||
/// The ideal number of staking participants.
|
||||
pub ValidatorCount get(validator_count) config(): u32;
|
||||
/// Minimum number of staking participants before emergency conditions are imposed.
|
||||
pub MinimumValidatorCount get(minimum_validator_count) config(): u32 = DEFAULT_MINIMUM_VALIDATOR_COUNT;
|
||||
/// The length of a staking era in sessions.
|
||||
pub SessionsPerEra get(sessions_per_era) config(): T::BlockNumber = 1000.into();
|
||||
pub MinimumValidatorCount get(minimum_validator_count) config():
|
||||
u32 = DEFAULT_MINIMUM_VALIDATOR_COUNT;
|
||||
/// Maximum reward, per validator, that is provided per acceptable session.
|
||||
pub SessionReward get(session_reward) config(): Perbill = Perbill::from_parts(60);
|
||||
/// Slash, per validator that is taken for the first time they are found to be offline.
|
||||
pub OfflineSlash get(offline_slash) config(): Perbill = Perbill::from_millionths(1000);
|
||||
/// Number of instances of offline reports before slashing begins for validators.
|
||||
pub OfflineSlashGrace get(offline_slash_grace) config(): u32;
|
||||
/// The length of the bonding duration in eras.
|
||||
pub BondingDuration get(bonding_duration) config(): T::BlockNumber = 12.into();
|
||||
|
||||
/// Any validators that may never be slashed or forcibly kicked. It's a Vec since they're easy to initialize
|
||||
/// and the performance hit is minimal (we expect no more than four invulnerables) and restricted to testnets.
|
||||
/// Any validators that may never be slashed or forcibly kicked. It's a Vec since they're
|
||||
/// easy to initialize and the performance hit is minimal (we expect no more than four
|
||||
/// invulnerables) and restricted to testnets.
|
||||
pub Invulnerables get(invulnerables) config(): Vec<T::AccountId>;
|
||||
|
||||
/// Map from all locked "stash" accounts to the controller account.
|
||||
pub Bonded get(bonded): map T::AccountId => Option<T::AccountId>;
|
||||
/// Map from all (unlocked) "controller" accounts to the info regarding the staking.
|
||||
pub Ledger get(ledger): map T::AccountId => Option<StakingLedger<T::AccountId, BalanceOf<T>, T::BlockNumber>>;
|
||||
pub Ledger get(ledger):
|
||||
map T::AccountId => Option<StakingLedger<T::AccountId, BalanceOf<T>>>;
|
||||
|
||||
/// Where the reward payment should be made. Keyed by stash.
|
||||
pub Payee get(payee): map T::AccountId => RewardDestination;
|
||||
@@ -481,38 +507,34 @@ decl_storage! {
|
||||
/// The map from nominator stash key to the set of stash keys of all validators to nominate.
|
||||
pub Nominators get(nominators): linked_map T::AccountId => Vec<T::AccountId>;
|
||||
|
||||
/// Nominators for a particular account that is in action right now. You can't iterate through validators here,
|
||||
/// but you can find them in the Session module.
|
||||
/// Nominators for a particular account that is in action right now. You can't iterate
|
||||
/// through validators here, but you can find them in the Session module.
|
||||
///
|
||||
/// This is keyed by the stash account.
|
||||
pub Stakers get(stakers): map T::AccountId => Exposure<T::AccountId, BalanceOf<T>>;
|
||||
|
||||
// The historical validators and their nominations for a given era. Stored as a trie root of the mapping
|
||||
// `T::AccountId` => `Exposure<T::AccountId, BalanceOf<T>>`, which is just the contents of `Stakers`,
|
||||
// under a key that is the `era`.
|
||||
// The historical validators and their nominations for a given era. Stored as a trie root
|
||||
// of the mapping `T::AccountId` => `Exposure<T::AccountId, BalanceOf<T>>`, which is just
|
||||
// the contents of `Stakers`, under a key that is the `era`.
|
||||
//
|
||||
// Every era change, this will be appended with the trie root of the contents of `Stakers`, and the oldest
|
||||
// entry removed down to a specific number of entries (probably around 90 for a 3 month history).
|
||||
// Every era change, this will be appended with the trie root of the contents of `Stakers`,
|
||||
// and the oldest entry removed down to a specific number of entries (probably around 90 for
|
||||
// a 3 month history).
|
||||
// pub HistoricalStakers get(historical_stakers): map T::BlockNumber => Option<H256>;
|
||||
|
||||
/// The currently elected validator set keyed by stash account ID.
|
||||
pub CurrentElected get(current_elected): Vec<T::AccountId>;
|
||||
|
||||
/// The current era index.
|
||||
pub CurrentEra get(current_era) config(): T::BlockNumber;
|
||||
pub CurrentEra get(current_era) config(): EraIndex;
|
||||
|
||||
/// Maximum reward, per validator, that is provided per acceptable session.
|
||||
pub CurrentSessionReward get(current_session_reward) config(): BalanceOf<T>;
|
||||
|
||||
/// The accumulated reward for the current era. Reset to zero at the beginning of the era and
|
||||
/// increased for every successfully finished session.
|
||||
/// The accumulated reward for the current era. Reset to zero at the beginning of the era
|
||||
/// and increased for every successfully finished session.
|
||||
pub CurrentEraReward get(current_era_reward): BalanceOf<T>;
|
||||
|
||||
/// The next value of sessions per era.
|
||||
pub NextSessionsPerEra get(next_sessions_per_era): Option<T::BlockNumber>;
|
||||
/// The session index at which the era length last changed.
|
||||
pub LastEraLengthChange get(last_era_length_change): T::BlockNumber;
|
||||
|
||||
/// The amount of balance actively at stake for each validator slot, currently.
|
||||
///
|
||||
/// This is used to derive rewards and punishments.
|
||||
@@ -520,18 +542,25 @@ decl_storage! {
|
||||
config.stakers.iter().map(|&(_, _, value, _)| value).min().unwrap_or_default()
|
||||
}): BalanceOf<T>;
|
||||
|
||||
/// The number of times a given validator has been reported offline. This gets decremented by one each era that passes.
|
||||
/// The number of times a given validator has been reported offline. This gets decremented
|
||||
/// by one each era that passes.
|
||||
pub SlashCount get(slash_count): map T::AccountId => u32;
|
||||
|
||||
/// We are forcing a new era.
|
||||
pub ForcingNewEra get(forcing_new_era): Option<()>;
|
||||
|
||||
/// Most recent `RECENT_OFFLINE_COUNT` instances. (Who it was, when it was reported, how many instances they were offline for).
|
||||
/// Most recent `RECENT_OFFLINE_COUNT` instances. (Who it was, when it was reported, how
|
||||
/// many instances they were offline for).
|
||||
pub RecentlyOffline get(recently_offline): Vec<(T::AccountId, T::BlockNumber, u32)>;
|
||||
|
||||
/// True if the next session change will be a new era regardless of index.
|
||||
pub ForceNewEra get(forcing_new_era): bool;
|
||||
}
|
||||
add_extra_genesis {
|
||||
config(stakers): Vec<(T::AccountId, T::AccountId, BalanceOf<T>, StakerStatus<T::AccountId>)>;
|
||||
build(|storage: &mut primitives::StorageOverlay, _: &mut primitives::ChildrenStorageOverlay, config: &GenesisConfig<T>| {
|
||||
config(stakers):
|
||||
Vec<(T::AccountId, T::AccountId, BalanceOf<T>, StakerStatus<T::AccountId>)>;
|
||||
build(|
|
||||
storage: &mut primitives::StorageOverlay,
|
||||
_: &mut primitives::ChildrenStorageOverlay,
|
||||
config: &GenesisConfig<T>
|
||||
| {
|
||||
with_storage(storage, || {
|
||||
for &(ref stash, ref controller, balance, ref status) in &config.stakers {
|
||||
assert!(T::Currency::free_balance(&stash) >= balance);
|
||||
@@ -556,18 +585,32 @@ decl_storage! {
|
||||
};
|
||||
}
|
||||
|
||||
<Module<T>>::select_validators();
|
||||
if let (_, Some(validators)) = <Module<T>>::select_validators() {
|
||||
<session::Validators<T>>::put(&validators);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
decl_event!(
|
||||
pub enum Event<T> where Balance = BalanceOf<T>, <T as system::Trait>::AccountId {
|
||||
/// All validators have been rewarded by the given balance.
|
||||
Reward(Balance),
|
||||
/// One validator (and its nominators) has been given an offline-warning (it is still
|
||||
/// within its grace). The accrued number of slashes is recorded, too.
|
||||
OfflineWarning(AccountId, u32),
|
||||
/// One validator (and its nominators) has been slashed by the given amount.
|
||||
OfflineSlash(AccountId, Balance),
|
||||
}
|
||||
);
|
||||
|
||||
decl_module! {
|
||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
fn deposit_event<T>() = default;
|
||||
|
||||
/// Take the origin account as a stash and lock up `value` of its balance. `controller` will be the
|
||||
/// account that controls it.
|
||||
/// 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_ by the stash account.
|
||||
///
|
||||
@@ -583,7 +626,11 @@ decl_module! {
|
||||
/// (which creates a bunch of storage items for an account). In essence, nothing prevents many accounts from
|
||||
/// spamming `Staking` storage by bonding 1 UNIT. See test case: `bond_with_no_staked_value`.
|
||||
/// # </weight>
|
||||
fn bond(origin, controller: <T::Lookup as StaticLookup>::Source, #[compact] value: BalanceOf<T>, payee: RewardDestination) {
|
||||
fn bond(origin,
|
||||
controller: <T::Lookup as StaticLookup>::Source,
|
||||
#[compact] value: BalanceOf<T>,
|
||||
payee: RewardDestination
|
||||
) {
|
||||
let stash = ensure_signed(origin)?;
|
||||
|
||||
if <Bonded<T>>::exists(&stash) {
|
||||
@@ -603,11 +650,12 @@ decl_module! {
|
||||
|
||||
let stash_balance = T::Currency::free_balance(&stash);
|
||||
let value = value.min(stash_balance);
|
||||
Self::update_ledger(&controller, &StakingLedger { stash, total: value, active: value, unlocking: vec![] });
|
||||
let item = StakingLedger { stash, total: value, active: value, unlocking: vec![] };
|
||||
Self::update_ledger(&controller, &item);
|
||||
}
|
||||
|
||||
/// Add some extra amount that have appeared in the stash `free_balance` into the balance up for
|
||||
/// staking.
|
||||
/// Add some extra amount that have appeared in the stash `free_balance` into the balance up
|
||||
/// for staking.
|
||||
///
|
||||
/// Use this if there are additional funds in your stash account that you wish to bond.
|
||||
///
|
||||
@@ -676,7 +724,7 @@ decl_module! {
|
||||
ledger.active = Zero::zero();
|
||||
}
|
||||
|
||||
let era = Self::current_era() + Self::bonding_duration();
|
||||
let era = Self::current_era() + T::BondingDuration::get();
|
||||
ledger.unlocking.push(UnlockChunk { value, era });
|
||||
Self::update_ledger(&controller, &ledger);
|
||||
}
|
||||
@@ -720,7 +768,10 @@ decl_module! {
|
||||
let controller = ensure_signed(origin)?;
|
||||
let ledger = Self::ledger(&controller).ok_or("not a controller")?;
|
||||
let stash = &ledger.stash;
|
||||
ensure!(prefs.unstake_threshold <= MAX_UNSTAKE_THRESHOLD, "unstake threshold too large");
|
||||
ensure!(
|
||||
prefs.unstake_threshold <= MAX_UNSTAKE_THRESHOLD,
|
||||
"unstake threshold too large"
|
||||
);
|
||||
<Nominators<T>>::remove(stash);
|
||||
<Validators<T>>::insert(stash, prefs);
|
||||
}
|
||||
@@ -807,27 +858,19 @@ decl_module! {
|
||||
}
|
||||
if controller != old_controller {
|
||||
<Bonded<T>>::insert(&stash, &controller);
|
||||
if let Some(l) = <Ledger<T>>::take(&old_controller) { <Ledger<T>>::insert(&controller, l) };
|
||||
if let Some(l) = <Ledger<T>>::take(&old_controller) {
|
||||
<Ledger<T>>::insert(&controller, l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----- Root calls.
|
||||
|
||||
/// Set the number of sessions in an era.
|
||||
fn set_sessions_per_era(#[compact] new: T::BlockNumber) {
|
||||
<NextSessionsPerEra<T>>::put(new);
|
||||
}
|
||||
|
||||
/// The length of the bonding duration in eras.
|
||||
fn set_bonding_duration(#[compact] new: T::BlockNumber) {
|
||||
<BondingDuration<T>>::put(new);
|
||||
}
|
||||
|
||||
/// The ideal number of validators.
|
||||
fn set_validator_count(#[compact] new: u32) {
|
||||
<ValidatorCount<T>>::put(new);
|
||||
}
|
||||
|
||||
// ----- Root calls.
|
||||
|
||||
/// Force there to be a new era. This also forces a new session immediately after.
|
||||
/// `apply_rewards` should be true for validators to get the session reward.
|
||||
///
|
||||
@@ -836,8 +879,8 @@ decl_module! {
|
||||
/// - Triggers the Phragmen election. Expensive but not user-controlled.
|
||||
/// - Depends on state: `O(|edges| * |validators|)`.
|
||||
/// # </weight>
|
||||
fn force_new_era(apply_rewards: bool) -> Result {
|
||||
Self::apply_force_new_era(apply_rewards)
|
||||
fn force_new_era() {
|
||||
Self::apply_force_new_era()
|
||||
}
|
||||
|
||||
/// Set the offline slash grace period.
|
||||
@@ -852,32 +895,9 @@ decl_module! {
|
||||
}
|
||||
}
|
||||
|
||||
decl_event!(
|
||||
pub enum Event<T> where Balance = BalanceOf<T>, <T as system::Trait>::AccountId {
|
||||
/// All validators have been rewarded by the given balance.
|
||||
Reward(Balance),
|
||||
/// One validator (and its nominators) has been given an offline-warning (it is still
|
||||
/// within its grace). The accrued number of slashes is recorded, too.
|
||||
OfflineWarning(AccountId, u32),
|
||||
/// One validator (and its nominators) has been slashed by the given amount.
|
||||
OfflineSlash(AccountId, Balance),
|
||||
}
|
||||
);
|
||||
|
||||
impl<T: Trait> Module<T> {
|
||||
/// Just force_new_era without origin check.
|
||||
fn apply_force_new_era(apply_rewards: bool) -> Result {
|
||||
<ForcingNewEra<T>>::put(());
|
||||
<session::Module<T>>::apply_force_new_session(apply_rewards)
|
||||
}
|
||||
|
||||
// PUBLIC IMMUTABLES
|
||||
|
||||
/// The length of a staking era in blocks.
|
||||
pub fn era_length() -> T::BlockNumber {
|
||||
Self::sessions_per_era() * <session::Module<T>>::length()
|
||||
}
|
||||
|
||||
/// The total balance that can be slashed from a validator controller account as of
|
||||
/// right now.
|
||||
pub fn slashable_balance(who: &T::AccountId) -> BalanceOf<T> {
|
||||
@@ -887,29 +907,41 @@ impl<T: Trait> Module<T> {
|
||||
// MUTABLES (DANGEROUS)
|
||||
|
||||
/// Update the ledger for a controller. This will also update the stash lock.
|
||||
fn update_ledger(controller: &T::AccountId, ledger: &StakingLedger<T::AccountId, BalanceOf<T>, T::BlockNumber>) {
|
||||
T::Currency::set_lock(STAKING_ID, &ledger.stash, ledger.total, T::BlockNumber::max_value(), WithdrawReasons::all());
|
||||
fn update_ledger(
|
||||
controller: &T::AccountId,
|
||||
ledger: &StakingLedger<T::AccountId, BalanceOf<T>>
|
||||
) {
|
||||
T::Currency::set_lock(
|
||||
STAKING_ID,
|
||||
&ledger.stash,
|
||||
ledger.total,
|
||||
T::BlockNumber::max_value(),
|
||||
WithdrawReasons::all()
|
||||
);
|
||||
<Ledger<T>>::insert(controller, ledger);
|
||||
}
|
||||
|
||||
/// Slash a given validator by a specific amount. Removes the slash from the validator's balance by preference,
|
||||
/// and reduces the nominators' balance if needed.
|
||||
/// Slash a given validator by a specific amount. Removes the slash from the validator's
|
||||
/// balance by preference, and reduces the nominators' balance if needed.
|
||||
fn slash_validator(stash: &T::AccountId, slash: BalanceOf<T>) {
|
||||
// The exposure (backing stake) information of the validator to be slashed.
|
||||
let exposure = Self::stakers(stash);
|
||||
// The amount we are actually going to slash (can't be bigger than the validator's total exposure)
|
||||
// The amount we are actually going to slash (can't be bigger than the validator's total
|
||||
// exposure)
|
||||
let slash = slash.min(exposure.total);
|
||||
// The amount we'll slash from the validator's stash directly.
|
||||
let own_slash = exposure.own.min(slash);
|
||||
let (mut imbalance, missing) = T::Currency::slash(stash, own_slash);
|
||||
let own_slash = own_slash - missing;
|
||||
// The amount remaining that we can't slash from the validator, that must be taken from the nominators.
|
||||
// The amount remaining that we can't slash from the validator, that must be taken from the
|
||||
// nominators.
|
||||
let rest_slash = slash - own_slash;
|
||||
if !rest_slash.is_zero() {
|
||||
// The total to be slashed from the nominators.
|
||||
let total = exposure.total - exposure.own;
|
||||
if !total.is_zero() {
|
||||
let safe_mul_rational = |b| b * rest_slash / total;// FIXME #1572 avoid overflow
|
||||
// FIXME #1572 avoid overflow
|
||||
let safe_mul_rational = |b| b * rest_slash / total;
|
||||
for i in exposure.others.iter() {
|
||||
// best effort - not much that can be done on fail.
|
||||
imbalance.subsume(T::Currency::slash(&i.who, safe_mul_rational(i.value)).0)
|
||||
@@ -942,8 +974,9 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Reward a given validator by a specific amount. Add the reward to the validator's, and its nominators'
|
||||
/// balance, pro-rata based on their exposure, after having removed the validator's pre-payout cut.
|
||||
/// Reward a given validator by a specific amount. Add the reward to the validator's, and its
|
||||
/// nominators' balance, pro-rata based on their exposure, after having removed the validator's
|
||||
/// pre-payout cut.
|
||||
fn reward_validator(stash: &T::AccountId, reward: BalanceOf<T>) {
|
||||
let off_the_table = reward.min(Self::validators(stash).validator_payment);
|
||||
let reward = reward - off_the_table;
|
||||
@@ -953,7 +986,8 @@ impl<T: Trait> Module<T> {
|
||||
} else {
|
||||
let exposure = Self::stakers(stash);
|
||||
let total = exposure.total.max(One::one());
|
||||
let safe_mul_rational = |b| b * reward / total;// FIXME #1572: avoid overflow
|
||||
// FIXME #1572: avoid overflow
|
||||
let safe_mul_rational = |b| b * reward / total;
|
||||
for i in &exposure.others {
|
||||
let nom_payout = safe_mul_rational(i.value);
|
||||
imbalance.maybe_subsume(Self::make_payout(&i.who, nom_payout));
|
||||
@@ -964,34 +998,16 @@ impl<T: Trait> Module<T> {
|
||||
T::Reward::on_unbalanced(imbalance);
|
||||
}
|
||||
|
||||
/// Get the reward for the session, assuming it ends with this block.
|
||||
fn this_session_reward(actual_elapsed: T::Moment) -> BalanceOf<T> {
|
||||
let ideal_elapsed = <session::Module<T>>::ideal_session_duration();
|
||||
if ideal_elapsed.is_zero() {
|
||||
return Self::current_session_reward();
|
||||
}
|
||||
// Assumes we have 16-bits free at the top of T::Moment. Holds true for moment as seconds
|
||||
// in a u64 for the forseeable future, but more correct would be to handle overflows
|
||||
// explicitly.
|
||||
let per65536 = T::Moment::from(65536) * ideal_elapsed.clone() / actual_elapsed.max(ideal_elapsed);
|
||||
let per65536: BalanceOf<T> = per65536.saturated_into::<u32>().into();
|
||||
Self::current_session_reward() * per65536 / 65536.into()
|
||||
}
|
||||
/// Session has just ended. Provide the validator set for the next session if it's an era-end.
|
||||
fn new_session(session_index: SessionIndex) -> Option<Vec<T::AccountId>> {
|
||||
// accumulate good session reward
|
||||
let reward = Self::current_session_reward();
|
||||
<CurrentEraReward<T>>::mutate(|r| *r += reward);
|
||||
|
||||
/// Session has just changed. We need to determine whether we pay a reward, slash and/or
|
||||
/// move to a new era.
|
||||
fn new_session(actual_elapsed: T::Moment, should_reward: bool) {
|
||||
if should_reward {
|
||||
// accumulate good session reward
|
||||
let reward = Self::this_session_reward(actual_elapsed);
|
||||
<CurrentEraReward<T>>::mutate(|r| *r += reward);
|
||||
}
|
||||
|
||||
let session_index = <session::Module<T>>::current_index();
|
||||
if <ForcingNewEra<T>>::take().is_some()
|
||||
|| ((session_index - Self::last_era_length_change()) % Self::sessions_per_era()).is_zero()
|
||||
{
|
||||
Self::new_era();
|
||||
if <ForceNewEra<T>>::take() || session_index % T::SessionsPerEra::get() == 0 {
|
||||
Self::new_era()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -999,7 +1015,7 @@ impl<T: Trait> Module<T> {
|
||||
///
|
||||
/// NOTE: This always happens immediately before a session change to ensure that new validators
|
||||
/// get a chance to set their session keys.
|
||||
fn new_era() {
|
||||
fn new_era() -> Option<Vec<T::AccountId>> {
|
||||
// Payout
|
||||
let reward = <CurrentEraReward<T>>::take();
|
||||
if !reward.is_zero() {
|
||||
@@ -1016,21 +1032,15 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
// Increment current era.
|
||||
<CurrentEra<T>>::put(&(<CurrentEra<T>>::get() + One::one()));
|
||||
|
||||
// Enact era length change.
|
||||
if let Some(next_spe) = Self::next_sessions_per_era() {
|
||||
if next_spe != Self::sessions_per_era() {
|
||||
<SessionsPerEra<T>>::put(&next_spe);
|
||||
<LastEraLengthChange<T>>::put(&<session::Module<T>>::current_index());
|
||||
}
|
||||
}
|
||||
<CurrentEra<T>>::mutate(|s| *s += 1);
|
||||
|
||||
// Reassign all Stakers.
|
||||
let slot_stake = Self::select_validators();
|
||||
let (slot_stake, maybe_new_validators) = Self::select_validators();
|
||||
|
||||
// Update the balances for rewarding according to the stakes.
|
||||
<CurrentSessionReward<T>>::put(Self::session_reward() * slot_stake);
|
||||
|
||||
maybe_new_validators
|
||||
}
|
||||
|
||||
fn slashable_balance_of(stash: &T::AccountId) -> BalanceOf<T> {
|
||||
@@ -1040,7 +1050,7 @@ impl<T: Trait> Module<T> {
|
||||
/// Select a new validator set from the assembled stakers and their role preferences.
|
||||
///
|
||||
/// Returns the new `SlotStake` value.
|
||||
fn select_validators() -> BalanceOf<T> {
|
||||
fn select_validators() -> (BalanceOf<T>, Option<Vec<T::AccountId>>) {
|
||||
let maybe_elected_set = elect::<T, _, _, _>(
|
||||
Self::validator_count() as usize,
|
||||
Self::minimum_validator_count().max(1) as usize,
|
||||
@@ -1054,8 +1064,10 @@ impl<T: Trait> Module<T> {
|
||||
let assignments = elected_set.1;
|
||||
|
||||
// helper closure.
|
||||
let to_balance = |b: ExtendedBalance| <T::CurrencyToVote as Convert<ExtendedBalance, BalanceOf<T>>>::convert(b);
|
||||
let to_votes = |b: BalanceOf<T>| <T::CurrencyToVote as Convert<BalanceOf<T>, u64>>::convert(b) as ExtendedBalance;
|
||||
let to_balance = |b: ExtendedBalance|
|
||||
<T::CurrencyToVote as Convert<ExtendedBalance, BalanceOf<T>>>::convert(b);
|
||||
let to_votes = |b: BalanceOf<T>|
|
||||
<T::CurrencyToVote as Convert<BalanceOf<T>, u64>>::convert(b) as ExtendedBalance;
|
||||
|
||||
// The return value of this is safe to be converted to u64.
|
||||
// The original balance, `b` is within the scope of u64. It is just extended to u128
|
||||
@@ -1082,7 +1094,8 @@ impl<T: Trait> Module<T> {
|
||||
.iter()
|
||||
.map(|e| (e, Self::slashable_balance_of(e)))
|
||||
.for_each(|(e, s)| {
|
||||
exposures.insert(e.clone(), Exposure { own: s, total: s, ..Default::default() });
|
||||
let item = Exposure { own: s, total: s, ..Default::default() };
|
||||
exposures.insert(e.clone(), item);
|
||||
});
|
||||
|
||||
for (n, _, assignment) in &assignments_with_stakes {
|
||||
@@ -1099,11 +1112,16 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
// This optimization will most likely be only applied off-chain.
|
||||
let do_equalise = false;
|
||||
if do_equalise {
|
||||
let do_equalize = false;
|
||||
if do_equalize {
|
||||
let tolerance = 10 as u128;
|
||||
let iterations = 10 as usize;
|
||||
phragmen::equalize::<T>(&mut assignments_with_stakes, &mut exposures, tolerance, iterations);
|
||||
phragmen::equalize::<T>(
|
||||
&mut assignments_with_stakes,
|
||||
&mut exposures,
|
||||
tolerance,
|
||||
iterations
|
||||
);
|
||||
}
|
||||
|
||||
// Clear Stakers and reduce their slash_count.
|
||||
@@ -1127,11 +1145,10 @@ impl<T: Trait> Module<T> {
|
||||
|
||||
// Set the new validator set.
|
||||
<CurrentElected<T>>::put(&elected_stashes);
|
||||
<session::Module<T>>::set_validators(
|
||||
&elected_stashes.into_iter().map(|s| Self::bonded(s).unwrap_or_default()).collect::<Vec<_>>()
|
||||
);
|
||||
|
||||
slot_stake
|
||||
let validators = elected_stashes.into_iter()
|
||||
.map(|s| Self::bonded(s).unwrap_or_default())
|
||||
.collect::<Vec<_>>();
|
||||
(slot_stake, Some(validators))
|
||||
} else {
|
||||
// There were not enough candidates for even our minimal level of functionality.
|
||||
// This is bad.
|
||||
@@ -1139,10 +1156,14 @@ impl<T: Trait> Module<T> {
|
||||
// and let the chain keep producing blocks until we can decide on a sufficiently
|
||||
// substantial set.
|
||||
// TODO: #2494
|
||||
Self::slot_stake()
|
||||
(Self::slot_stake(), None)
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_force_new_era() {
|
||||
<ForceNewEra<T>>::put(true);
|
||||
}
|
||||
|
||||
/// Call when a validator is determined to be offline. `count` is the
|
||||
/// number of offenses the validator has committed.
|
||||
///
|
||||
@@ -1190,7 +1211,7 @@ impl<T: Trait> Module<T> {
|
||||
.unwrap_or(slash_exposure);
|
||||
let _ = Self::slash_validator(&stash, slash);
|
||||
<Validators<T>>::remove(&stash);
|
||||
let _ = Self::apply_force_new_era(false);
|
||||
let _ = Self::apply_force_new_era();
|
||||
|
||||
RawEvent::OfflineSlash(stash.clone(), slash)
|
||||
} else {
|
||||
@@ -1202,9 +1223,9 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> OnSessionChange<T::Moment> for Module<T> {
|
||||
fn on_session_change(elapsed: T::Moment, should_reward: bool) {
|
||||
Self::new_session(elapsed, should_reward);
|
||||
impl<T: Trait> OnSessionEnding<T::AccountId> for Module<T> {
|
||||
fn on_session_ending(i: SessionIndex) -> Option<Vec<T::AccountId>> {
|
||||
Self::new_session(i + 1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1219,12 +1240,3 @@ impl<T: Trait> OnFreeBalanceZero<T::AccountId> for Module<T> {
|
||||
<Nominators<T>>::remove(stash);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Trait> consensus::OnOfflineReport<Vec<u32>> for Module<T> {
|
||||
fn handle_report(reported_indices: Vec<u32>) {
|
||||
for validator_index in reported_indices {
|
||||
let v = <session::Module<T>>::validators()[validator_index as usize].clone();
|
||||
Self::on_offline_validator(v, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,15 +16,17 @@
|
||||
|
||||
//! Test utilities
|
||||
|
||||
use primitives::{traits::{IdentityLookup, Convert}, BuildStorage, Perbill};
|
||||
use primitives::testing::{Digest, DigestItem, Header, UintAuthorityId, ConvertUintAuthorityId};
|
||||
use primitives::{BuildStorage, Perbill};
|
||||
use primitives::traits::{IdentityLookup, Convert, OpaqueKeys, OnInitialize};
|
||||
use primitives::testing::{Header, UintAuthorityId};
|
||||
use substrate_primitives::{H256, Blake2Hasher};
|
||||
use runtime_io;
|
||||
use srml_support::{impl_outer_origin, assert_ok, traits::Currency};
|
||||
use crate::{GenesisConfig, Module, Trait, StakerStatus, ValidatorPrefs, RewardDestination};
|
||||
use srml_support::{impl_outer_origin, parameter_types, assert_ok, traits::Currency};
|
||||
use crate::{EraIndex, GenesisConfig, Module, Trait, StakerStatus, ValidatorPrefs, RewardDestination};
|
||||
|
||||
/// The AccountId alias in this test module.
|
||||
pub type AccountIdType = u64;
|
||||
pub type AccountId = u64;
|
||||
pub type BlockNumber = u64;
|
||||
|
||||
/// Simple structure that exposes how u64 currency can be represented as... u64.
|
||||
pub struct CurrencyToVoteHandler;
|
||||
@@ -37,6 +39,15 @@ impl Convert<u128, u64> for CurrencyToVoteHandler {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TestSessionHandler;
|
||||
impl session::SessionHandler<AccountId> for TestSessionHandler {
|
||||
fn on_new_session<Ks: OpaqueKeys>(_changed: bool, _validators: &[(AccountId, Ks)]) {
|
||||
}
|
||||
|
||||
fn on_disabled(_validator_index: usize) {
|
||||
}
|
||||
}
|
||||
|
||||
impl_outer_origin!{
|
||||
pub enum Origin for Test {}
|
||||
}
|
||||
@@ -44,23 +55,16 @@ impl_outer_origin!{
|
||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Test;
|
||||
impl consensus::Trait for Test {
|
||||
type Log = DigestItem;
|
||||
type SessionKey = UintAuthorityId;
|
||||
type InherentOfflineReport = ();
|
||||
}
|
||||
impl system::Trait for Test {
|
||||
type Origin = Origin;
|
||||
type Index = u64;
|
||||
type BlockNumber = u64;
|
||||
type Hash = H256;
|
||||
type Hashing = ::primitives::traits::BlakeTwo256;
|
||||
type Digest = Digest;
|
||||
type AccountId = AccountIdType;
|
||||
type AccountId = AccountId;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type Header = Header;
|
||||
type Event = ();
|
||||
type Log = DigestItem;
|
||||
}
|
||||
impl balances::Trait for Test {
|
||||
type Balance = u64;
|
||||
@@ -71,15 +75,25 @@ impl balances::Trait for Test {
|
||||
type TransferPayment = ();
|
||||
type DustRemoval = ();
|
||||
}
|
||||
parameter_types! {
|
||||
pub const Period: BlockNumber = 1;
|
||||
pub const Offset: BlockNumber = 0;
|
||||
}
|
||||
impl session::Trait for Test {
|
||||
type ConvertAccountIdToSessionKey = ConvertUintAuthorityId;
|
||||
type OnSessionChange = Staking;
|
||||
type OnSessionEnding = Staking;
|
||||
type Keys = UintAuthorityId;
|
||||
type ShouldEndSession = session::PeriodicSessions<Period, Offset>;
|
||||
type SessionHandler = TestSessionHandler;
|
||||
type Event = ();
|
||||
}
|
||||
impl timestamp::Trait for Test {
|
||||
type Moment = u64;
|
||||
type OnTimestampSet = ();
|
||||
}
|
||||
parameter_types! {
|
||||
pub const SessionsPerEra: session::SessionIndex = 3;
|
||||
pub const BondingDuration: EraIndex = 3;
|
||||
}
|
||||
impl Trait for Test {
|
||||
type Currency = balances::Module<Self>;
|
||||
type CurrencyToVote = CurrencyToVoteHandler;
|
||||
@@ -87,13 +101,13 @@ impl Trait for Test {
|
||||
type Event = ();
|
||||
type Slash = ();
|
||||
type Reward = ();
|
||||
type SessionsPerEra = SessionsPerEra;
|
||||
type BondingDuration = BondingDuration;
|
||||
}
|
||||
|
||||
pub struct ExtBuilder {
|
||||
existential_deposit: u64,
|
||||
session_length: u64,
|
||||
sessions_per_era: u64,
|
||||
current_era: u64,
|
||||
current_era: EraIndex,
|
||||
reward: u64,
|
||||
validator_pool: bool,
|
||||
nominate: bool,
|
||||
@@ -106,8 +120,6 @@ impl Default for ExtBuilder {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
existential_deposit: 0,
|
||||
session_length: 1,
|
||||
sessions_per_era: 1,
|
||||
current_era: 0,
|
||||
reward: 10,
|
||||
validator_pool: false,
|
||||
@@ -124,15 +136,7 @@ impl ExtBuilder {
|
||||
self.existential_deposit = existential_deposit;
|
||||
self
|
||||
}
|
||||
pub fn session_length(mut self, session_length: u64) -> Self {
|
||||
self.session_length = session_length;
|
||||
self
|
||||
}
|
||||
pub fn sessions_per_era(mut self, sessions_per_era: u64) -> Self {
|
||||
self.sessions_per_era = sessions_per_era;
|
||||
self
|
||||
}
|
||||
pub fn _current_era(mut self, current_era: u64) -> Self {
|
||||
pub fn _current_era(mut self, current_era: EraIndex) -> Self {
|
||||
self.current_era = current_era;
|
||||
self
|
||||
}
|
||||
@@ -163,14 +167,9 @@ impl ExtBuilder {
|
||||
} else {
|
||||
1
|
||||
};
|
||||
let _ = consensus::GenesisConfig::<Test>{
|
||||
code: vec![],
|
||||
authorities: vec![],
|
||||
}.assimilate_storage(&mut t, &mut c);
|
||||
let _ = session::GenesisConfig::<Test>{
|
||||
session_length: self.session_length,
|
||||
// NOTE: if config.nominate == false then 100 is also selected in the initial round.
|
||||
validators: if self.validator_pool { vec![10, 20, 30, 40] } else { vec![10, 20] },
|
||||
validators: if self.validator_pool { vec![10, 20, 30, 40] } else { vec![10, 20] },
|
||||
keys: vec![],
|
||||
}.assimilate_storage(&mut t, &mut c);
|
||||
let _ = balances::GenesisConfig::<Test>{
|
||||
@@ -197,30 +196,26 @@ impl ExtBuilder {
|
||||
creation_fee: 0,
|
||||
vesting: vec![],
|
||||
}.assimilate_storage(&mut t, &mut c);
|
||||
let stake_21 = if self.fair { 1000 } else { 2000 };
|
||||
let stake_31 = if self.validator_pool { balance_factor * 1000 } else { 1 };
|
||||
let status_41 = if self.validator_pool {
|
||||
StakerStatus::<AccountId>::Validator
|
||||
} else {
|
||||
StakerStatus::<AccountId>::Idle
|
||||
};
|
||||
let nominated = if self.nominate { vec![11, 21] } else { vec![] };
|
||||
let _ = GenesisConfig::<Test>{
|
||||
sessions_per_era: self.sessions_per_era,
|
||||
current_era: self.current_era,
|
||||
stakers: if self.validator_pool {
|
||||
vec![
|
||||
(11, 10, balance_factor * 1000, StakerStatus::<AccountIdType>::Validator),
|
||||
(21, 20, balance_factor * if self.fair { 1000 } else { 2000 }, StakerStatus::<AccountIdType>::Validator),
|
||||
(31, 30, balance_factor * 1000, if self.validator_pool { StakerStatus::<AccountIdType>::Validator } else { StakerStatus::<AccountIdType>::Idle }),
|
||||
(41, 40, balance_factor * 1000, if self.validator_pool { StakerStatus::<AccountIdType>::Validator } else { StakerStatus::<AccountIdType>::Idle }),
|
||||
// nominator
|
||||
(101, 100, balance_factor * 500, if self.nominate { StakerStatus::<AccountIdType>::Nominator(vec![11, 21]) } else { StakerStatus::<AccountIdType>::Nominator(vec![]) })
|
||||
]
|
||||
} else {
|
||||
vec![
|
||||
(11, 10, balance_factor * 1000, StakerStatus::<AccountIdType>::Validator),
|
||||
(21, 20, balance_factor * if self.fair { 1000 } else { 2000 }, StakerStatus::<AccountIdType>::Validator),
|
||||
(31, 30, 1, StakerStatus::<AccountIdType>::Validator),
|
||||
// nominator
|
||||
(101, 100, balance_factor * 500, if self.nominate { StakerStatus::<AccountIdType>::Nominator(vec![11, 21]) } else { StakerStatus::<AccountIdType>::Nominator(vec![]) })
|
||||
]
|
||||
},
|
||||
stakers: vec![
|
||||
(11, 10, balance_factor * 1000, StakerStatus::<AccountId>::Validator),
|
||||
(21, 20, stake_21, StakerStatus::<AccountId>::Validator),
|
||||
(31, 30, stake_31, StakerStatus::<AccountId>::Validator),
|
||||
(41, 40, balance_factor * 1000, status_41),
|
||||
// nominator
|
||||
(101, 100, balance_factor * 500, StakerStatus::<AccountId>::Nominator(nominated))
|
||||
],
|
||||
validator_count: self.validator_count,
|
||||
minimum_validator_count: self.minimum_validator_count,
|
||||
bonding_duration: self.sessions_per_era * self.session_length * 3,
|
||||
session_reward: Perbill::from_millionths((1000000 * self.reward / balance_factor) as u32),
|
||||
offline_slash: Perbill::from_percent(5),
|
||||
current_session_reward: self.reward,
|
||||
@@ -268,4 +263,17 @@ pub fn bond_nominator(acc: u64, val: u64, target: Vec<u64>) {
|
||||
let _ = Balances::make_free_balance_be(&(acc+1), val);
|
||||
assert_ok!(Staking::bond(Origin::signed(acc+1), acc, val, RewardDestination::Controller));
|
||||
assert_ok!(Staking::nominate(Origin::signed(acc), target));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start_session(session_index: session::SessionIndex) {
|
||||
for i in 0..(session_index - Session::current_index()) {
|
||||
System::set_block_number((i + 1).into());
|
||||
Session::on_initialize(System::block_number());
|
||||
}
|
||||
assert_eq!(Session::current_index(), session_index);
|
||||
}
|
||||
|
||||
pub fn start_era(era_index: EraIndex) {
|
||||
start_session((era_index * 3).into());
|
||||
assert_eq!(Staking::current_era(), era_index);
|
||||
}
|
||||
|
||||
@@ -105,7 +105,8 @@ pub fn elect<T: Trait + 'static, FV, FN, FS>(
|
||||
|
||||
// 1- Pre-process candidates and place them in a container, optimisation and add phantom votes.
|
||||
// Candidates who have 0 stake => have no votes or all null-votes. Kick them out not.
|
||||
let mut nominators: Vec<Nominator<T::AccountId>> = Vec::with_capacity(validator_iter.size_hint().0 + nominator_iter.size_hint().0);
|
||||
let mut nominators: Vec<Nominator<T::AccountId>> =
|
||||
Vec::with_capacity(validator_iter.size_hint().0 + nominator_iter.size_hint().0);
|
||||
let mut candidates = validator_iter.map(|(who, _)| {
|
||||
let stash_balance = stash_of(&who);
|
||||
(Candidate { who, ..Default::default() }, stash_balance)
|
||||
|
||||
+179
-222
@@ -19,6 +19,7 @@
|
||||
use super::*;
|
||||
use runtime_io::with_externalities;
|
||||
use phragmen;
|
||||
use primitives::traits::OnInitialize;
|
||||
use srml_support::{assert_ok, assert_noop, assert_eq_uvec, EnumerableStorageMap};
|
||||
use mock::*;
|
||||
use srml_support::traits::{Currency, ReservableCurrency};
|
||||
@@ -93,7 +94,7 @@ fn no_offline_should_work() {
|
||||
assert_eq!(Staking::slash_count(&10), 0);
|
||||
assert_eq!(Balances::free_balance(&10), 1);
|
||||
// New era is not being forced
|
||||
assert!(Staking::forcing_new_era().is_none());
|
||||
assert!(!Staking::forcing_new_era());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -110,9 +111,7 @@ fn change_controller_works() {
|
||||
|
||||
assert_ok!(Staking::set_controller(Origin::signed(11), 5));
|
||||
|
||||
System::set_block_number(1);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
assert_eq!(Staking::current_era(), 1);
|
||||
start_era(1);
|
||||
|
||||
assert_noop!(
|
||||
Staking::validate(Origin::signed(10), ValidatorPrefs::default()),
|
||||
@@ -150,7 +149,7 @@ fn invulnerability_should_work() {
|
||||
assert!(<Validators<Test>>::exists(&11));
|
||||
// New era not being forced
|
||||
// NOTE: new era is always forced once slashing happens -> new validators need to be chosen.
|
||||
assert!(Staking::forcing_new_era().is_none());
|
||||
assert!(!Staking::forcing_new_era());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -180,7 +179,7 @@ fn offline_should_slash_and_kick() {
|
||||
// Confirm account 10 has been removed as a validator
|
||||
assert!(!<Validators<Test>>::exists(&11));
|
||||
// A new era is forced due to slashing
|
||||
assert!(Staking::forcing_new_era().is_some());
|
||||
assert!(Staking::forcing_new_era());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -219,7 +218,7 @@ fn offline_grace_should_delay_slashing() {
|
||||
// User gets slashed
|
||||
assert!(Balances::free_balance(&11) < 70);
|
||||
// New era is forced
|
||||
assert!(Staking::forcing_new_era().is_some());
|
||||
assert!(Staking::forcing_new_era());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -286,7 +285,7 @@ fn slashing_does_not_cause_underflow() {
|
||||
});
|
||||
|
||||
System::set_block_number(1);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
Session::on_initialize(System::block_number());
|
||||
|
||||
// Should not panic
|
||||
Staking::on_offline_validator(10, 100);
|
||||
@@ -303,8 +302,6 @@ fn rewards_should_work() {
|
||||
// * rewards get paid per Era
|
||||
// * Check that nominators are also rewarded
|
||||
with_externalities(&mut ExtBuilder::default()
|
||||
.session_length(3)
|
||||
.sessions_per_era(3)
|
||||
.build(),
|
||||
|| {
|
||||
let delay = 1;
|
||||
@@ -316,9 +313,6 @@ fn rewards_should_work() {
|
||||
assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller));
|
||||
|
||||
// Initial config should be correct
|
||||
assert_eq!(Staking::era_length(), 9);
|
||||
assert_eq!(Staking::sessions_per_era(), 3);
|
||||
assert_eq!(Staking::last_era_length_change(), 0);
|
||||
assert_eq!(Staking::current_era(), 0);
|
||||
assert_eq!(Session::current_index(), 0);
|
||||
assert_eq!(Staking::current_session_reward(), 10);
|
||||
@@ -343,7 +337,7 @@ fn rewards_should_work() {
|
||||
let mut block = 3; // Block 3 => Session 1 => Era 0
|
||||
System::set_block_number(block);
|
||||
Timestamp::set_timestamp(block*5); // on time.
|
||||
Session::check_rotate_session(System::block_number());
|
||||
Session::on_initialize(System::block_number());
|
||||
assert_eq!(Staking::current_era(), 0);
|
||||
assert_eq!(Session::current_index(), 1);
|
||||
|
||||
@@ -354,24 +348,24 @@ fn rewards_should_work() {
|
||||
block = 6; // Block 6 => Session 2 => Era 0
|
||||
System::set_block_number(block);
|
||||
Timestamp::set_timestamp(block*5 + delay); // a little late.
|
||||
Session::check_rotate_session(System::block_number());
|
||||
Session::on_initialize(System::block_number());
|
||||
assert_eq!(Staking::current_era(), 0);
|
||||
assert_eq!(Session::current_index(), 2);
|
||||
|
||||
// session reward is the same,
|
||||
assert_eq!(Staking::current_session_reward(), session_reward);
|
||||
// though 2 will be deducted while stashed in the era reward due to delay
|
||||
assert_eq!(Staking::current_era_reward(), 2*session_reward - delay);
|
||||
assert_eq!(Staking::current_era_reward(), 2*session_reward); // - delay);
|
||||
|
||||
block = 9; // Block 9 => Session 3 => Era 1
|
||||
System::set_block_number(block);
|
||||
Timestamp::set_timestamp(block*5); // back to being on time. no delays
|
||||
Session::check_rotate_session(System::block_number());
|
||||
Session::on_initialize(System::block_number());
|
||||
assert_eq!(Staking::current_era(), 1);
|
||||
assert_eq!(Session::current_index(), 3);
|
||||
|
||||
assert_eq!(Balances::total_balance(&10), 1 + (3*session_reward - delay)/2);
|
||||
assert_eq!(Balances::total_balance(&2), 500 + (3*session_reward - delay)/2);
|
||||
assert_eq!(Balances::total_balance(&10), 1 + (3*session_reward)/2);
|
||||
assert_eq!(Balances::total_balance(&2), 500 + (3*session_reward)/2);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -381,12 +375,9 @@ fn multi_era_reward_should_work() {
|
||||
// The value of current_session_reward is set at the end of each era, based on
|
||||
// slot_stake and session_reward.
|
||||
with_externalities(&mut ExtBuilder::default()
|
||||
.session_length(3)
|
||||
.sessions_per_era(3)
|
||||
.nominate(false)
|
||||
.build(),
|
||||
|| {
|
||||
let delay = 1;
|
||||
let session_reward = 10;
|
||||
|
||||
// This is set by the test config builder.
|
||||
@@ -398,37 +389,21 @@ fn multi_era_reward_should_work() {
|
||||
// Set payee to controller
|
||||
assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller));
|
||||
|
||||
let mut block = 3;
|
||||
// Block 3 => Session 1 => Era 0
|
||||
System::set_block_number(block);
|
||||
Timestamp::set_timestamp(block*5);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
assert_eq!(Staking::current_era(), 0);
|
||||
assert_eq!(Session::current_index(), 1);
|
||||
start_session(1);
|
||||
|
||||
// session triggered: the reward value stashed should be 10
|
||||
assert_eq!(Staking::current_session_reward(), session_reward);
|
||||
assert_eq!(Staking::current_era_reward(), session_reward);
|
||||
|
||||
block = 6; // Block 6 => Session 2 => Era 0
|
||||
System::set_block_number(block);
|
||||
Timestamp::set_timestamp(block*5 + delay); // a little late.
|
||||
Session::check_rotate_session(System::block_number());
|
||||
assert_eq!(Staking::current_era(), 0);
|
||||
assert_eq!(Session::current_index(), 2);
|
||||
start_session(2);
|
||||
|
||||
assert_eq!(Staking::current_session_reward(), session_reward);
|
||||
assert_eq!(Staking::current_era_reward(), 2*session_reward - delay);
|
||||
assert_eq!(Staking::current_era_reward(), 2*session_reward);
|
||||
|
||||
block = 9; // Block 9 => Session 3 => Era 1
|
||||
System::set_block_number(block);
|
||||
Timestamp::set_timestamp(block*5); // back to being punktlisch. no delayss
|
||||
Session::check_rotate_session(System::block_number());
|
||||
assert_eq!(Staking::current_era(), 1);
|
||||
assert_eq!(Session::current_index(), 3);
|
||||
start_session(3);
|
||||
|
||||
// 1 + sum of of the session rewards accumulated
|
||||
let recorded_balance = 1 + 3*session_reward - delay;
|
||||
let recorded_balance = 1 + 3*session_reward;
|
||||
assert_eq!(Balances::total_balance(&10), recorded_balance);
|
||||
|
||||
// the reward for next era will be: session_reward * slot_stake
|
||||
@@ -436,14 +411,13 @@ fn multi_era_reward_should_work() {
|
||||
assert_eq!(Staking::current_session_reward(), new_session_reward);
|
||||
|
||||
// fast forward to next era:
|
||||
block=12; System::set_block_number(block);Timestamp::set_timestamp(block*5);Session::check_rotate_session(System::block_number());
|
||||
block=15; System::set_block_number(block);Timestamp::set_timestamp(block*5);Session::check_rotate_session(System::block_number());
|
||||
start_session(5);
|
||||
|
||||
// intermediate test.
|
||||
assert_eq!(Staking::current_era_reward(), 2*new_session_reward);
|
||||
|
||||
// new era is triggered here.
|
||||
block=18; System::set_block_number(block);Timestamp::set_timestamp(block*5);Session::check_rotate_session(System::block_number());
|
||||
start_session(6);
|
||||
|
||||
// pay time
|
||||
assert_eq!(Balances::total_balance(&10), 3*new_session_reward + recorded_balance);
|
||||
@@ -457,7 +431,6 @@ fn staking_should_work() {
|
||||
// * new ones will be chosen per era
|
||||
// * either one can unlock the stash and back-down from being a validator via `chill`ing.
|
||||
with_externalities(&mut ExtBuilder::default()
|
||||
.sessions_per_era(3)
|
||||
.nominate(false)
|
||||
.fair(false) // to give 20 more staked value
|
||||
.build(),
|
||||
@@ -465,15 +438,12 @@ fn staking_should_work() {
|
||||
// remember + compare this along with the test.
|
||||
assert_eq_uvec!(Session::validators(), vec![20, 10]);
|
||||
|
||||
assert_ok!(Staking::set_bonding_duration(2));
|
||||
assert_eq!(Staking::bonding_duration(), 2);
|
||||
|
||||
// put some money in account that we'll use.
|
||||
for i in 1..5 { let _ = Balances::make_free_balance_be(&i, 2000); }
|
||||
|
||||
// --- Block 1:
|
||||
System::set_block_number(1);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
Session::on_initialize(System::block_number());
|
||||
assert_eq!(Staking::current_era(), 0);
|
||||
|
||||
// add a new candidate for being a validator. account 3 controlled by 4.
|
||||
@@ -485,7 +455,7 @@ fn staking_should_work() {
|
||||
|
||||
// --- Block 2:
|
||||
System::set_block_number(2);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
Session::on_initialize(System::block_number());
|
||||
assert_eq!(Staking::current_era(), 0);
|
||||
|
||||
// No effects will be seen so far. Era has not been yet triggered.
|
||||
@@ -494,7 +464,7 @@ fn staking_should_work() {
|
||||
|
||||
// --- Block 3: the validators will now change.
|
||||
System::set_block_number(3);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
Session::on_initialize(System::block_number());
|
||||
|
||||
// 2 only voted for 4 and 20
|
||||
assert_eq!(Session::validators().len(), 2);
|
||||
@@ -504,7 +474,7 @@ fn staking_should_work() {
|
||||
|
||||
// --- Block 4: Unstake 4 as a validator, freeing up the balance stashed in 3
|
||||
System::set_block_number(4);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
Session::on_initialize(System::block_number());
|
||||
|
||||
// 4 will chill
|
||||
Staking::chill(Origin::signed(4)).unwrap();
|
||||
@@ -516,14 +486,14 @@ fn staking_should_work() {
|
||||
|
||||
// --- Block 5: nothing. 4 is still there.
|
||||
System::set_block_number(5);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
Session::on_initialize(System::block_number());
|
||||
assert_eq_uvec!(Session::validators(), vec![20, 4]);
|
||||
assert_eq!(Staking::current_era(), 1);
|
||||
|
||||
|
||||
// --- Block 6: 4 will not be a validator.
|
||||
System::set_block_number(6);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
Session::on_initialize(System::block_number());
|
||||
assert_eq!(Staking::current_era(), 2);
|
||||
assert_eq!(Session::validators().contains(&4), false);
|
||||
assert_eq_uvec!(Session::validators(), vec![20, 10]);
|
||||
@@ -548,9 +518,7 @@ fn less_than_needed_candidates_works() {
|
||||
assert_eq!(Staking::minimum_validator_count(), 1);
|
||||
assert_eq_uvec!(Session::validators(), vec![30, 20, 10]);
|
||||
|
||||
System::set_block_number(1);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
assert_eq!(Staking::current_era(), 1);
|
||||
start_era(1);
|
||||
|
||||
// Previous set is selected. NO election algorithm is even executed.
|
||||
assert_eq_uvec!(Session::validators(), vec![30, 20, 10]);
|
||||
@@ -574,7 +542,6 @@ fn no_candidate_emergency_condition() {
|
||||
.nominate(false)
|
||||
.build(),
|
||||
|| {
|
||||
assert_eq!(Staking::era_length(), 1);
|
||||
assert_eq!(Staking::validator_count(), 15);
|
||||
|
||||
// initial validators
|
||||
@@ -584,7 +551,7 @@ fn no_candidate_emergency_condition() {
|
||||
|
||||
// trigger era
|
||||
System::set_block_number(1);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
Session::on_initialize(System::block_number());
|
||||
|
||||
// Previous ones are elected. chill is invalidates. TODO: #2494
|
||||
assert_eq_uvec!(Session::validators(), vec![10, 20, 30, 40]);
|
||||
@@ -663,16 +630,14 @@ fn nominating_and_rewards_should_work() {
|
||||
assert_ok!(Staking::bond(Origin::signed(3), 4, 1000, RewardDestination::Controller));
|
||||
assert_ok!(Staking::nominate(Origin::signed(4), vec![11, 21, 41]));
|
||||
|
||||
System::set_block_number(1);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
assert_eq!(Staking::current_era(), 1);
|
||||
start_era(1);
|
||||
|
||||
// 10 and 20 have more votes, they will be chosen by phragmen.
|
||||
assert_eq_uvec!(Session::validators(), vec![20, 10]);
|
||||
|
||||
// OLD validators must have already received some rewards.
|
||||
assert_eq!(Balances::total_balance(&40), 1 + session_reward);
|
||||
assert_eq!(Balances::total_balance(&30), 1 + session_reward);
|
||||
assert_eq!(Balances::total_balance(&40), 1 + 3 * session_reward);
|
||||
assert_eq!(Balances::total_balance(&30), 1 + 3 * session_reward);
|
||||
|
||||
// ------ check the staked value of all parties.
|
||||
|
||||
@@ -694,22 +659,21 @@ fn nominating_and_rewards_should_work() {
|
||||
assert_eq!(Staking::stakers(41).total, 0);
|
||||
|
||||
|
||||
System::set_block_number(2);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
start_era(2);
|
||||
// next session reward.
|
||||
let new_session_reward = Staking::session_reward() * Staking::slot_stake();
|
||||
let new_session_reward = Staking::session_reward() * 3 * Staking::slot_stake();
|
||||
// nothing else will happen, era ends and rewards are paid again,
|
||||
// it is expected that nominators will also be paid. See below
|
||||
|
||||
// Nominator 2: has [400/1800 ~ 2/9 from 10] + [600/2200 ~ 3/11 from 20]'s reward. ==> 2/9 + 3/11
|
||||
assert_eq!(Balances::total_balance(&2), initial_balance + (2*new_session_reward/9 + 3*new_session_reward/11));
|
||||
assert_eq!(Balances::total_balance(&2), initial_balance + (2*new_session_reward/9 + 3*new_session_reward/11) - 1);
|
||||
// Nominator 4: has [400/1800 ~ 2/9 from 10] + [600/2200 ~ 3/11 from 20]'s reward. ==> 2/9 + 3/11
|
||||
assert_eq!(Balances::total_balance(&4), initial_balance + (2*new_session_reward/9 + 3*new_session_reward/11));
|
||||
assert_eq!(Balances::total_balance(&4), initial_balance + (2*new_session_reward/9 + 3*new_session_reward/11) - 1);
|
||||
|
||||
// 10 got 800 / 1800 external stake => 8/18 =? 4/9 => Validator's share = 5/9
|
||||
assert_eq!(Balances::total_balance(&10), initial_balance + 5*new_session_reward/9);
|
||||
// 10 got 1200 / 2200 external stake => 12/22 =? 6/11 => Validator's share = 5/11
|
||||
assert_eq!(Balances::total_balance(&20), initial_balance + 5*new_session_reward/11);
|
||||
assert_eq!(Balances::total_balance(&20), initial_balance + 5*new_session_reward/11+ 2);
|
||||
|
||||
check_exposure_all();
|
||||
});
|
||||
@@ -719,7 +683,6 @@ fn nominating_and_rewards_should_work() {
|
||||
fn nominators_also_get_slashed() {
|
||||
// A nominator should be slashed if the validator they nominated is slashed
|
||||
with_externalities(&mut ExtBuilder::default().nominate(false).build(), || {
|
||||
assert_eq!(Staking::era_length(), 1);
|
||||
assert_eq!(Staking::validator_count(), 2);
|
||||
// slash happens immediately.
|
||||
assert_eq!(Staking::offline_slash_grace(), 0);
|
||||
@@ -742,8 +705,7 @@ fn nominators_also_get_slashed() {
|
||||
assert_ok!(Staking::nominate(Origin::signed(2), vec![20, 10]));
|
||||
|
||||
// new era, pay rewards,
|
||||
System::set_block_number(1);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
start_era(1);
|
||||
|
||||
// Nominator stash didn't collect any.
|
||||
assert_eq!(Balances::total_balance(&2), initial_balance);
|
||||
@@ -757,11 +719,11 @@ fn nominators_also_get_slashed() {
|
||||
let nominator_slash = nominator_stake.min(total_slash - validator_slash);
|
||||
|
||||
// initial + first era reward + slash
|
||||
assert_eq!(Balances::total_balance(&10), initial_balance + 10 - validator_slash);
|
||||
assert_eq!(Balances::total_balance(&10), initial_balance + 30 - validator_slash);
|
||||
assert_eq!(Balances::total_balance(&2), initial_balance - nominator_slash);
|
||||
check_exposure_all();
|
||||
// Because slashing happened.
|
||||
assert!(Staking::forcing_new_era().is_some());
|
||||
assert!(Staking::forcing_new_era());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -772,7 +734,6 @@ fn double_staking_should_fail() {
|
||||
// * an account already bonded as stash cannot nominate.
|
||||
// * an account already bonded as controller can nominate.
|
||||
with_externalities(&mut ExtBuilder::default()
|
||||
.sessions_per_era(2)
|
||||
.build(),
|
||||
|| {
|
||||
let arbitrary_value = 5;
|
||||
@@ -792,7 +753,6 @@ fn double_controlling_should_fail() {
|
||||
// should test (in the same order):
|
||||
// * an account already bonded as controller CANNOT be reused as the controller of another account.
|
||||
with_externalities(&mut ExtBuilder::default()
|
||||
.sessions_per_era(2)
|
||||
.build(),
|
||||
|| {
|
||||
let arbitrary_value = 5;
|
||||
@@ -806,70 +766,43 @@ fn double_controlling_should_fail() {
|
||||
#[test]
|
||||
fn session_and_eras_work() {
|
||||
with_externalities(&mut ExtBuilder::default()
|
||||
.sessions_per_era(2)
|
||||
.build(),
|
||||
|| {
|
||||
assert_eq!(Staking::era_length(), 2);
|
||||
assert_eq!(Staking::sessions_per_era(), 2);
|
||||
assert_eq!(Staking::last_era_length_change(), 0);
|
||||
assert_eq!(Staking::current_era(), 0);
|
||||
assert_eq!(Session::current_index(), 0);
|
||||
|
||||
// Block 1: No change.
|
||||
System::set_block_number(1);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
start_session(1);
|
||||
assert_eq!(Session::current_index(), 1);
|
||||
assert_eq!(Staking::sessions_per_era(), 2);
|
||||
assert_eq!(Staking::last_era_length_change(), 0);
|
||||
assert_eq!(Staking::current_era(), 0);
|
||||
|
||||
// Block 2: Simple era change.
|
||||
System::set_block_number(2);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
assert_eq!(Session::current_index(), 2);
|
||||
assert_eq!(Staking::sessions_per_era(), 2);
|
||||
assert_eq!(Staking::last_era_length_change(), 0);
|
||||
start_session(3);
|
||||
assert_eq!(Session::current_index(), 3);
|
||||
assert_eq!(Staking::current_era(), 1);
|
||||
|
||||
// Block 3: Schedule an era length change; no visible changes.
|
||||
System::set_block_number(3);
|
||||
assert_ok!(Staking::set_sessions_per_era(3));
|
||||
Session::check_rotate_session(System::block_number());
|
||||
assert_eq!(Session::current_index(), 3);
|
||||
assert_eq!(Staking::sessions_per_era(), 2);
|
||||
assert_eq!(Staking::last_era_length_change(), 0);
|
||||
start_session(4);
|
||||
assert_eq!(Session::current_index(), 4);
|
||||
assert_eq!(Staking::current_era(), 1);
|
||||
|
||||
// Block 4: Era change kicks in.
|
||||
System::set_block_number(4);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
assert_eq!(Session::current_index(), 4);
|
||||
assert_eq!(Staking::sessions_per_era(), 3);
|
||||
assert_eq!(Staking::last_era_length_change(), 4);
|
||||
start_session(6);
|
||||
assert_eq!(Session::current_index(), 6);
|
||||
assert_eq!(Staking::current_era(), 2);
|
||||
|
||||
// Block 5: No change.
|
||||
System::set_block_number(5);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
assert_eq!(Session::current_index(), 5);
|
||||
assert_eq!(Staking::sessions_per_era(), 3);
|
||||
assert_eq!(Staking::last_era_length_change(), 4);
|
||||
start_session(7);
|
||||
assert_eq!(Session::current_index(), 7);
|
||||
assert_eq!(Staking::current_era(), 2);
|
||||
|
||||
// Block 6: No change.
|
||||
System::set_block_number(6);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
assert_eq!(Session::current_index(), 6);
|
||||
assert_eq!(Staking::sessions_per_era(), 3);
|
||||
assert_eq!(Staking::last_era_length_change(), 4);
|
||||
start_session(8);
|
||||
assert_eq!(Session::current_index(), 8);
|
||||
assert_eq!(Staking::current_era(), 2);
|
||||
|
||||
// Block 7: Era increment.
|
||||
System::set_block_number(7);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
assert_eq!(Session::current_index(), 7);
|
||||
assert_eq!(Staking::sessions_per_era(), 3);
|
||||
assert_eq!(Staking::last_era_length_change(), 4);
|
||||
start_session(9);
|
||||
assert_eq!(Session::current_index(), 9);
|
||||
assert_eq!(Staking::current_era(), 3);
|
||||
});
|
||||
}
|
||||
@@ -947,31 +880,39 @@ fn reward_destination_works() {
|
||||
// Check the balance of the stash account
|
||||
assert_eq!(Balances::free_balance(&11), 1000);
|
||||
// Check how much is at stake
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000, active: 1000, unlocking: vec![] }));
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger {
|
||||
stash: 11,
|
||||
total: 1000,
|
||||
active: 1000,
|
||||
unlocking: vec![],
|
||||
}));
|
||||
// Check current session reward is 10
|
||||
let session_reward0 = Staking::current_session_reward(); // 10
|
||||
let session_reward0 = 3 * Staking::current_session_reward(); // 10
|
||||
|
||||
// Move forward the system for payment
|
||||
System::set_block_number(1);
|
||||
Timestamp::set_timestamp(5);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
start_era(1);
|
||||
|
||||
// Check that RewardDestination is Staked (default)
|
||||
assert_eq!(Staking::payee(&11), RewardDestination::Staked);
|
||||
// Check that reward went to the stash account of validator
|
||||
assert_eq!(Balances::free_balance(&11), 1000 + session_reward0);
|
||||
// Check that amount at stake increased accordingly
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000 + session_reward0, active: 1000 + session_reward0, unlocking: vec![] }));
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger {
|
||||
stash: 11,
|
||||
total: 1000 + session_reward0,
|
||||
active: 1000 + session_reward0,
|
||||
unlocking: vec![],
|
||||
}));
|
||||
// Update current session reward
|
||||
let session_reward1 = Staking::current_session_reward(); // 1010 (1* slot_stake)
|
||||
let session_reward1 = 3 * Staking::current_session_reward(); // 1010 (1* slot_stake)
|
||||
|
||||
//Change RewardDestination to Stash
|
||||
<Payee<Test>>::insert(&11, RewardDestination::Stash);
|
||||
|
||||
// Move forward the system for payment
|
||||
System::set_block_number(2);
|
||||
Timestamp::set_timestamp(10);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
start_era(2);
|
||||
|
||||
// Check that RewardDestination is Stash
|
||||
assert_eq!(Staking::payee(&11), RewardDestination::Stash);
|
||||
@@ -980,7 +921,12 @@ fn reward_destination_works() {
|
||||
// Record this value
|
||||
let recorded_stash_balance = 1000 + session_reward0 + session_reward1;
|
||||
// Check that amount at stake is NOT increased
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000 + session_reward0, active: 1000 + session_reward0, unlocking: vec![] }));
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger {
|
||||
stash: 11,
|
||||
total: 1000 + session_reward0,
|
||||
active: 1000 + session_reward0,
|
||||
unlocking: vec![],
|
||||
}));
|
||||
|
||||
// Change RewardDestination to Controller
|
||||
<Payee<Test>>::insert(&11, RewardDestination::Controller);
|
||||
@@ -989,17 +935,21 @@ fn reward_destination_works() {
|
||||
assert_eq!(Balances::free_balance(&10), 1);
|
||||
|
||||
// Move forward the system for payment
|
||||
System::set_block_number(3);
|
||||
Timestamp::set_timestamp(15);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
let session_reward2 = Staking::current_session_reward(); // 1010 (1* slot_stake)
|
||||
start_era(3);
|
||||
let session_reward2 = 3 * Staking::current_session_reward(); // 1010 (1* slot_stake)
|
||||
|
||||
// Check that RewardDestination is Controller
|
||||
assert_eq!(Staking::payee(&11), RewardDestination::Controller);
|
||||
// Check that reward went to the controller account
|
||||
assert_eq!(Balances::free_balance(&10), 1 + session_reward2);
|
||||
// Check that amount at stake is NOT increased
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000 + session_reward0, active: 1000 + session_reward0, unlocking: vec![] }));
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger {
|
||||
stash: 11,
|
||||
total: 1000 + session_reward0,
|
||||
active: 1000 + session_reward0,
|
||||
unlocking: vec![],
|
||||
}));
|
||||
// Check that amount in staked account is NOT increased.
|
||||
assert_eq!(Balances::free_balance(&11), recorded_stash_balance);
|
||||
});
|
||||
@@ -1011,8 +961,6 @@ fn validator_payment_prefs_work() {
|
||||
// Note: unstake threshold is being directly tested in slashing tests.
|
||||
// This test will focus on validator payment.
|
||||
with_externalities(&mut ExtBuilder::default()
|
||||
.session_length(3)
|
||||
.sessions_per_era(3)
|
||||
.build(),
|
||||
|| {
|
||||
// Initial config
|
||||
@@ -1044,7 +992,7 @@ fn validator_payment_prefs_work() {
|
||||
// Block 3 => Session 1 => Era 0
|
||||
let mut block = 3;
|
||||
System::set_block_number(block);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
Session::on_initialize(System::block_number());
|
||||
assert_eq!(Staking::current_era(), 0);
|
||||
assert_eq!(Session::current_index(), 1);
|
||||
|
||||
@@ -1054,7 +1002,7 @@ fn validator_payment_prefs_work() {
|
||||
|
||||
block = 6; // Block 6 => Session 2 => Era 0
|
||||
System::set_block_number(block);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
Session::on_initialize(System::block_number());
|
||||
assert_eq!(Staking::current_era(), 0);
|
||||
assert_eq!(Session::current_index(), 2);
|
||||
|
||||
@@ -1063,7 +1011,7 @@ fn validator_payment_prefs_work() {
|
||||
|
||||
block = 9; // Block 9 => Session 3 => Era 1
|
||||
System::set_block_number(block);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
Session::on_initialize(System::block_number());
|
||||
assert_eq!(Staking::current_era(), 1);
|
||||
assert_eq!(Session::current_index(), 3);
|
||||
|
||||
@@ -1093,7 +1041,12 @@ fn bond_extra_works() {
|
||||
// Check that account 10 is bonded to account 11
|
||||
assert_eq!(Staking::bonded(&11), Some(10));
|
||||
// Check how much is at stake
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000, active: 1000, unlocking: vec![] }));
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger {
|
||||
stash: 11,
|
||||
total: 1000,
|
||||
active: 1000,
|
||||
unlocking: vec![],
|
||||
}));
|
||||
|
||||
// Give account 11 some large free balance greater than total
|
||||
let _ = Balances::make_free_balance_be(&11, 1000000);
|
||||
@@ -1101,12 +1054,22 @@ fn bond_extra_works() {
|
||||
// Call the bond_extra function from controller, add only 100
|
||||
assert_ok!(Staking::bond_extra(Origin::signed(11), 100));
|
||||
// There should be 100 more `total` and `active` in the ledger
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000 + 100, active: 1000 + 100, unlocking: vec![] }));
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger {
|
||||
stash: 11,
|
||||
total: 1000 + 100,
|
||||
active: 1000 + 100,
|
||||
unlocking: vec![],
|
||||
}));
|
||||
|
||||
// Call the bond_extra function with a large number, should handle it
|
||||
assert_ok!(Staking::bond_extra(Origin::signed(11), u64::max_value()));
|
||||
// The full amount of the funds should now be in the total and active
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000000, active: 1000000, unlocking: vec![] }));
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger {
|
||||
stash: 11,
|
||||
total: 1000000,
|
||||
active: 1000000,
|
||||
unlocking: vec![],
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1124,14 +1087,10 @@ fn bond_extra_and_withdraw_unbonded_works() {
|
||||
// Set payee to controller. avoids confusion
|
||||
assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller));
|
||||
|
||||
// Set unbonding era (bonding_duration) to 2
|
||||
assert_ok!(Staking::set_bonding_duration(2));
|
||||
|
||||
// Give account 11 some large free balance greater than total
|
||||
let _ = Balances::make_free_balance_be(&11, 1000000);
|
||||
|
||||
// Initial config should be correct
|
||||
assert_eq!(Staking::sessions_per_era(), 1);
|
||||
assert_eq!(Staking::current_era(), 0);
|
||||
assert_eq!(Session::current_index(), 0);
|
||||
assert_eq!(Staking::current_session_reward(), 10);
|
||||
@@ -1140,58 +1099,65 @@ fn bond_extra_and_withdraw_unbonded_works() {
|
||||
assert_eq!(Balances::total_balance(&10), 1);
|
||||
|
||||
// confirm that 10 is a normal validator and gets paid at the end of the era.
|
||||
System::set_block_number(1);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
start_era(1);
|
||||
|
||||
// Initial state of 10
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000, active: 1000, unlocking: vec![] }));
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger {
|
||||
stash: 11,
|
||||
total: 1000,
|
||||
active: 1000,
|
||||
unlocking: vec![],
|
||||
}));
|
||||
assert_eq!(Staking::stakers(&11), Exposure { total: 1000, own: 1000, others: vec![] });
|
||||
|
||||
// deposit the extra 100 units
|
||||
Staking::bond_extra(Origin::signed(11), 100).unwrap();
|
||||
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000 + 100, active: 1000 + 100, unlocking: vec![] }));
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger {
|
||||
stash: 11,
|
||||
total: 1000 + 100,
|
||||
active: 1000 + 100,
|
||||
unlocking: vec![],
|
||||
}));
|
||||
// Exposure is a snapshot! only updated after the next era update.
|
||||
assert_ne!(Staking::stakers(&11), Exposure { total: 1000 + 100, own: 1000 + 100, others: vec![] });
|
||||
|
||||
// trigger next era.
|
||||
System::set_block_number(2);Timestamp::set_timestamp(10);Session::check_rotate_session(System::block_number());
|
||||
Timestamp::set_timestamp(10);
|
||||
start_era(2);
|
||||
assert_eq!(Staking::current_era(), 2);
|
||||
assert_eq!(Session::current_index(), 2);
|
||||
|
||||
// ledger should be the same.
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000 + 100, active: 1000 + 100, unlocking: vec![] }));
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger {
|
||||
stash: 11,
|
||||
total: 1000 + 100,
|
||||
active: 1000 + 100,
|
||||
unlocking: vec![],
|
||||
}));
|
||||
// Exposure is now updated.
|
||||
assert_eq!(Staking::stakers(&11), Exposure { total: 1000 + 100, own: 1000 + 100, others: vec![] });
|
||||
|
||||
// Unbond almost all of the funds in stash.
|
||||
Staking::unbond(Origin::signed(10), 1000).unwrap();
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger {
|
||||
stash: 11, total: 1000 + 100, active: 100, unlocking: vec![UnlockChunk{ value: 1000, era: 2 + 2}] })
|
||||
stash: 11, total: 1000 + 100, active: 100, unlocking: vec![UnlockChunk{ value: 1000, era: 2 + 3}] })
|
||||
);
|
||||
|
||||
// Attempting to free the balances now will fail. 2 eras need to pass.
|
||||
Staking::withdraw_unbonded(Origin::signed(10)).unwrap();
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger {
|
||||
stash: 11, total: 1000 + 100, active: 100, unlocking: vec![UnlockChunk{ value: 1000, era: 2 + 2}] }));
|
||||
stash: 11, total: 1000 + 100, active: 100, unlocking: vec![UnlockChunk{ value: 1000, era: 2 + 3}] }));
|
||||
|
||||
// trigger next era.
|
||||
System::set_block_number(3);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
|
||||
assert_eq!(Staking::current_era(), 3);
|
||||
assert_eq!(Session::current_index(), 3);
|
||||
start_era(3);
|
||||
|
||||
// nothing yet
|
||||
Staking::withdraw_unbonded(Origin::signed(10)).unwrap();
|
||||
assert_eq!(Staking::ledger(&10), Some(StakingLedger {
|
||||
stash: 11, total: 1000 + 100, active: 100, unlocking: vec![UnlockChunk{ value: 1000, era: 2 + 2}] }));
|
||||
stash: 11, total: 1000 + 100, active: 100, unlocking: vec![UnlockChunk{ value: 1000, era: 2 + 3}] }));
|
||||
|
||||
// trigger next era.
|
||||
System::set_block_number(4);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
assert_eq!(Staking::current_era(), 4);
|
||||
assert_eq!(Session::current_index(), 4);
|
||||
start_era(5);
|
||||
|
||||
Staking::withdraw_unbonded(Origin::signed(10)).unwrap();
|
||||
// Now the value is free and the staking ledger is updated.
|
||||
@@ -1208,19 +1174,14 @@ fn too_many_unbond_calls_should_not_work() {
|
||||
assert_ok!(Staking::unbond(Origin::signed(10), 1));
|
||||
}
|
||||
|
||||
System::set_block_number(1);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
start_era(1);
|
||||
|
||||
// locked ar era 1 until 4
|
||||
// locked at era 1 until 4
|
||||
assert_ok!(Staking::unbond(Origin::signed(10), 1));
|
||||
// can't do more.
|
||||
assert_noop!(Staking::unbond(Origin::signed(10), 1), "can not schedule more unlock chunks");
|
||||
|
||||
System::set_block_number(2);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
|
||||
System::set_block_number(3);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
start_era(3);
|
||||
|
||||
assert_noop!(Staking::unbond(Origin::signed(10), 1), "can not schedule more unlock chunks");
|
||||
// free up.
|
||||
@@ -1262,23 +1223,21 @@ fn slot_stake_is_least_staked_validator_and_exposure_defines_maximum_punishment(
|
||||
<Ledger<Test>>::insert(&20, StakingLedger { stash: 22, total: 69, active: 69, unlocking: vec![] });
|
||||
|
||||
// New era --> rewards are paid --> stakes are changed
|
||||
System::set_block_number(1);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
assert_eq!(Staking::current_era(), 1);
|
||||
start_era(1);
|
||||
|
||||
// -- new balances + reward
|
||||
assert_eq!(Staking::stakers(&11).total, 1000 + 10);
|
||||
assert_eq!(Staking::stakers(&21).total, 69 + 10);
|
||||
assert_eq!(Staking::stakers(&11).total, 1000 + 30);
|
||||
assert_eq!(Staking::stakers(&21).total, 69 + 30);
|
||||
|
||||
// -- slot stake should also be updated.
|
||||
assert_eq!(Staking::slot_stake(), 79);
|
||||
assert_eq!(Staking::slot_stake(), 69 + 30);
|
||||
|
||||
// If 10 gets slashed now, it will be slashed by 5% of exposure.total * 2.pow(unstake_thresh)
|
||||
Staking::on_offline_validator(10, 4);
|
||||
// Confirm user has been reported
|
||||
assert_eq!(Staking::slash_count(&11), 4);
|
||||
// check the balance of 10 (slash will be deducted from free balance.)
|
||||
assert_eq!(Balances::free_balance(&11), 1000 + 10 - 50 /*5% of 1000*/ * 8 /*2**3*/);
|
||||
assert_eq!(Balances::free_balance(&11), 1000 + 30 - 51 /*5% of 1030*/ * 8 /*2**3*/);
|
||||
|
||||
check_exposure_all();
|
||||
});
|
||||
@@ -1474,8 +1433,7 @@ fn phragmen_poc_works() {
|
||||
assert_ok!(Staking::nominate(Origin::signed(4), vec![11, 21, 41]));
|
||||
|
||||
// New era => election algorithm will trigger
|
||||
System::set_block_number(1);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
start_era(1);
|
||||
|
||||
assert_eq_uvec!(Session::validators(), vec![20, 10]);
|
||||
|
||||
@@ -1552,7 +1510,6 @@ fn switching_roles() {
|
||||
// Test that it should be possible to switch between roles (nominator, validator, idle) with minimal overhead.
|
||||
with_externalities(&mut ExtBuilder::default()
|
||||
.nominate(false)
|
||||
.sessions_per_era(3)
|
||||
.build(),
|
||||
|| {
|
||||
// Reset reward destination
|
||||
@@ -1576,21 +1533,21 @@ fn switching_roles() {
|
||||
|
||||
// new block
|
||||
System::set_block_number(1);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
Session::on_initialize(System::block_number());
|
||||
|
||||
// no change
|
||||
assert_eq_uvec!(Session::validators(), vec![20, 10]);
|
||||
|
||||
// new block
|
||||
System::set_block_number(2);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
Session::on_initialize(System::block_number());
|
||||
|
||||
// no change
|
||||
assert_eq_uvec!(Session::validators(), vec![20, 10]);
|
||||
|
||||
// new block --> ne era --> new validators
|
||||
System::set_block_number(3);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
Session::on_initialize(System::block_number());
|
||||
|
||||
// with current nominators 10 and 5 have the most stake
|
||||
assert_eq_uvec!(Session::validators(), vec![6, 10]);
|
||||
@@ -1605,16 +1562,16 @@ fn switching_roles() {
|
||||
// Winners: 20 and 2
|
||||
|
||||
System::set_block_number(4);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
Session::on_initialize(System::block_number());
|
||||
assert_eq_uvec!(Session::validators(), vec![6, 10]);
|
||||
|
||||
System::set_block_number(5);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
Session::on_initialize(System::block_number());
|
||||
assert_eq_uvec!(Session::validators(), vec![6, 10]);
|
||||
|
||||
// ne era
|
||||
System::set_block_number(6);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
Session::on_initialize(System::block_number());
|
||||
assert_eq_uvec!(Session::validators(), vec![2, 20]);
|
||||
check_exposure_all();
|
||||
});
|
||||
@@ -1640,8 +1597,7 @@ fn wrong_vote_is_null() {
|
||||
]));
|
||||
|
||||
// new block
|
||||
System::set_block_number(1);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
start_era(1);
|
||||
|
||||
assert_eq_uvec!(Session::validators(), vec![20, 10]);
|
||||
});
|
||||
@@ -1666,35 +1622,44 @@ fn bond_with_no_staked_value() {
|
||||
assert_ok!(Staking::bond(Origin::signed(1), 2, 1, RewardDestination::Controller));
|
||||
assert_ok!(Staking::validate(Origin::signed(2), ValidatorPrefs::default()));
|
||||
|
||||
System::set_block_number(1);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
start_era(1);
|
||||
|
||||
assert_eq_uvec!(Session::validators(), vec![30, 20, 10]);
|
||||
|
||||
// min of 10, 20 and 30 (30 got a payout into staking so it raised it from 1 to 11).
|
||||
assert_eq!(Staking::slot_stake(), 11);
|
||||
// min of 10, 20 and 30 (30 got a payout into staking so it raised it from 1 to 31).
|
||||
assert_eq!(Staking::slot_stake(), 31);
|
||||
|
||||
// make the stingy one elected.
|
||||
assert_ok!(Staking::bond(Origin::signed(3), 4, 500, RewardDestination::Controller));
|
||||
assert_ok!(Staking::nominate(Origin::signed(4), vec![1]));
|
||||
|
||||
System::set_block_number(2);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
// no rewards paid to 2 and 4 yet
|
||||
assert_eq!(Balances::free_balance(&2), initial_balance_2);
|
||||
assert_eq!(Balances::free_balance(&4), initial_balance_4);
|
||||
|
||||
start_era(2);
|
||||
|
||||
// Stingy one is selected
|
||||
assert_eq_uvec!(Session::validators(), vec![20, 10, 2]);
|
||||
assert_eq!(Staking::stakers(1), Exposure { own: 1, total: 501, others: vec![IndividualExposure { who: 3, value: 500}]});
|
||||
assert_eq!(Staking::stakers(1), Exposure {
|
||||
own: 1,
|
||||
total: 501,
|
||||
others: vec![IndividualExposure { who: 3, value: 500}],
|
||||
});
|
||||
// New slot stake.
|
||||
assert_eq!(Staking::slot_stake(), 501);
|
||||
|
||||
System::set_block_number(3);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
// no rewards paid to 2 and 4 yet
|
||||
assert_eq!(Balances::free_balance(&2), initial_balance_2);
|
||||
assert_eq!(Balances::free_balance(&4), initial_balance_4);
|
||||
|
||||
let reward = Staking::current_session_reward();
|
||||
start_era(3);
|
||||
|
||||
let reward = Staking::current_session_reward() * 3;
|
||||
// 2 will not get a reward of only 1
|
||||
// 4 will get the rest
|
||||
assert_eq!(Balances::free_balance(&2), initial_balance_2 + 1);
|
||||
assert_eq!(Balances::free_balance(&4), initial_balance_4 + reward - 1);
|
||||
assert_eq!(Balances::free_balance(&2), initial_balance_2 + 3);
|
||||
assert_eq!(Balances::free_balance(&4), initial_balance_4 + reward - 3);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1719,8 +1684,7 @@ fn bond_with_little_staked_value_bounded_by_slot_stake() {
|
||||
assert_ok!(Staking::bond(Origin::signed(1), 2, 1, RewardDestination::Controller));
|
||||
assert_ok!(Staking::validate(Origin::signed(2), ValidatorPrefs::default()));
|
||||
|
||||
System::set_block_number(1);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
start_era(1);
|
||||
|
||||
// 2 is elected.
|
||||
// and fucks up the slot stake.
|
||||
@@ -1728,21 +1692,20 @@ fn bond_with_little_staked_value_bounded_by_slot_stake() {
|
||||
assert_eq!(Staking::slot_stake(), 1);
|
||||
|
||||
// Old ones are rewarded.
|
||||
assert_eq!(Balances::free_balance(&10), initial_balance_10 + 10);
|
||||
assert_eq!(Balances::free_balance(&10), initial_balance_10 + 30);
|
||||
// no rewards paid to 2. This was initial election.
|
||||
assert_eq!(Balances::free_balance(&2), initial_balance_2);
|
||||
|
||||
System::set_block_number(2);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
start_era(2);
|
||||
|
||||
assert_eq_uvec!(Session::validators(), vec![20, 10, 2]);
|
||||
assert_eq!(Staking::slot_stake(), 1);
|
||||
|
||||
let reward = Staking::current_session_reward();
|
||||
// 2 will not get the full reward, practically 1
|
||||
assert_eq!(Balances::free_balance(&2), initial_balance_2 + reward.max(1));
|
||||
assert_eq!(Balances::free_balance(&2), initial_balance_2 + reward.max(3));
|
||||
// same for 10
|
||||
assert_eq!(Balances::free_balance(&10), initial_balance_10 + 10 + reward.max(1));
|
||||
assert_eq!(Balances::free_balance(&10), initial_balance_10 + 30 + reward.max(3));
|
||||
check_exposure_all();
|
||||
});
|
||||
}
|
||||
@@ -1775,7 +1738,7 @@ fn phragmen_linear_worse_case_equalize() {
|
||||
assert_ok!(Staking::set_validator_count(7));
|
||||
|
||||
System::set_block_number(1);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
Session::on_initialize(System::block_number());
|
||||
|
||||
assert_eq_uvec!(Session::validators(), vec![10, 60, 40, 20, 50, 30, 70]);
|
||||
|
||||
@@ -1813,7 +1776,7 @@ fn phragmen_chooses_correct_number_of_validators() {
|
||||
assert_eq!(Session::validators().len(), 1);
|
||||
|
||||
System::set_block_number(1);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
Session::on_initialize(System::block_number());
|
||||
|
||||
assert_eq!(Session::validators().len(), 1);
|
||||
check_exposure_all();
|
||||
@@ -1832,8 +1795,7 @@ fn phragmen_score_should_be_accurate_on_large_stakes() {
|
||||
bond_validator(6, u64::max_value()-1);
|
||||
bond_validator(8, u64::max_value()-2);
|
||||
|
||||
System::set_block_number(2);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
start_era(1);
|
||||
|
||||
assert_eq!(Session::validators(), vec![4, 2]);
|
||||
check_exposure_all();
|
||||
@@ -1855,8 +1817,7 @@ fn phragmen_should_not_overflow_validators() {
|
||||
bond_nominator(6, u64::max_value()/2, vec![3, 5]);
|
||||
bond_nominator(8, u64::max_value()/2, vec![3, 5]);
|
||||
|
||||
System::set_block_number(2);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
start_era(1);
|
||||
|
||||
assert_eq_uvec!(Session::validators(), vec![4, 2]);
|
||||
|
||||
@@ -1882,8 +1843,7 @@ fn phragmen_should_not_overflow_nominators() {
|
||||
bond_nominator(6, u64::max_value(), vec![3, 5]);
|
||||
bond_nominator(8, u64::max_value(), vec![3, 5]);
|
||||
|
||||
System::set_block_number(2);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
start_era(1);
|
||||
|
||||
assert_eq_uvec!(Session::validators(), vec![4, 2]);
|
||||
|
||||
@@ -1905,8 +1865,7 @@ fn phragmen_should_not_overflow_ultimate() {
|
||||
bond_nominator(6, u64::max_value(), vec![3, 5]);
|
||||
bond_nominator(8, u64::max_value(), vec![3, 5]);
|
||||
|
||||
System::set_block_number(2);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
start_era(1);
|
||||
|
||||
assert_eq_uvec!(Session::validators(), vec![4, 2]);
|
||||
|
||||
@@ -1958,8 +1917,7 @@ fn phragmen_large_scale_test() {
|
||||
prefix + 25]
|
||||
);
|
||||
|
||||
System::set_block_number(1);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
start_era(1);
|
||||
|
||||
// For manual inspection
|
||||
println!("Validators are {:?}", Session::validators());
|
||||
@@ -2008,8 +1966,7 @@ fn phragmen_large_scale_test_2() {
|
||||
|
||||
bond_nominator(50, nom_budget, vec![3, 5]);
|
||||
|
||||
System::set_block_number(1);
|
||||
Session::check_rotate_session(System::block_number());
|
||||
start_era(1);
|
||||
|
||||
// Each exposure => total == own + sum(others)
|
||||
check_exposure_all();
|
||||
|
||||
Reference in New Issue
Block a user