From aa83f220a497467dc316b96510d33fff69fbf91a Mon Sep 17 00:00:00 2001 From: kianenigma Date: Sat, 9 Mar 2019 14:59:38 +0100 Subject: [PATCH 01/54] initial doc for the staking module --- srml/staking/README.adoc | 120 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 srml/staking/README.adoc diff --git a/srml/staking/README.adoc b/srml/staking/README.adoc new file mode 100644 index 0000000000000..7662b82a19d08 --- /dev/null +++ b/srml/staking/README.adoc @@ -0,0 +1,120 @@ +# Staking Module + +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”__ should they be found not to bee discharging their duties properly. + + +## Overview + +### Terminology + +- **Staking**: The process of locking up funds for some time, placing them at risk of slashing (loss) in order to become a rewarded maintainer of the network. +- **Validating**: The process of running a node to actively maintain the network, either by producing blocks or guaranteeing finality of the chain. (see [here]() for the details of how block production and finalization are separated) +- **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 which controls an owner's funds for staking. +- **Era**: A (whole) number of sessions which is the period that the validator set (and each validator's active nominator set) is recalculated and where rewards are paid out. +- **Slash**: The punishment of a staker by reducing their funds. + +### Scenarios + +Almost any interaction with the staking module requires at least one account to become **bonded**, also known as being a **staker**. For this, all that it is needed is a secondary _**stash account**_ which will hold the staked funds. Henceforth, the former account that initiated the interest is called the **controller** and the later, holding the funds, is named the **stash**. Also, note that this implies that entering the staking process requires an _account pair_, one of which to take the role of the controller and one to be the frozen stash account (any value locked in stash cannot be used, hence called _frozen_). This process in the public API is mostly referred to as _bonding_ via the [`bond()`](#todo) function. + +Any account pair successfully placed at stake can accept three possible roles, namely: `validate`, `nominate` or simply `chill`. Note that during the process of accepting these roles, the _controller_ account is always responsible for declaring interest and the _stash_ account stays untouched, without directly interacting in any operation. + +A **validator** takes the role of either validating blocks or ensuring their finality, maintaining the veracity of the network in other words. A validator should avoid both any sort of malicious misbehavior and going offline. Unlike nominating, 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 election is determined by nominators and their votes. An account can become a validator via the [`validate()`](#todo) call. + +A **nominator** does not take any _direct_ role in maintaining the network, instead, it votes on a set of validators to be elected. Once interest in nomination is stated by an account, it takes effect _immediately_, meaning that their votes will be taken into account at the next election round. As mentioned above, a nominator must also place some fund in a stash account, essentially indicating the _weight_ of their vote. In some sense, the nominator bets on the honesty of a set of validators by voting for them, with the goal of having a share at the reward granted to them. Any rewards given to a validator is shared among that validator and all of the nominators that voted for it. The same logic applies to the slash of a validator; if a validator misbehaves all of its nominators also get slashed. 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()`](#todo) call. + +The **reward and slashing** procedure are the core of the staking module, attempting to _embrace valid behavior_ while _punishing any misbehavior or lack of availability_. Slashing can occur at any point in time, once a misbehavior is reported. One such misbehavior is a validator to be detected as offline more than a certain number of times. Once slashing is determined, a value is deducted from the balance of validator and all the nominators who voted for this validator. Same rules apply to the rewards in the sense of being shared among validator and its associated nominators. + +Finally, any of the roles above can choose to temporarily step back and just chill for a while. This means that if they are a nominator, they will not be considered as voters anymore and if they are validators, they will no longer be a candidate for the next election (again, both effects apply at the beginning of the next era). An account can step back via the [`chill()`](#todo) call. + +## Public Interface + +The staking module contains many public storage items and (im)mutable functions. Please refer to the [rustdoc](#todo) to see the full list. + +## Usage Example + +### Bonding and Accepting Roles + +An arbitrary account pair, given that the associated stash has the required funds, can become stakers via the following call: + +``` +// bond account 3 as stash +// account 4 as controller +// with stash value 1500 units +// while the rewards get transferred to the controller account. +Staking::bond(Origin::signed(3), 4, 1500, RewardDestination::Controller); +``` + +To state desire in becoming a validator: + +``` +// controller account 4 states desire for validation with the given preferences. +Staking::validate(Origin::signed(4), ValidatorPrefs::default()); +``` + +Note that, as mentioned, the stash account is transparent in such calls and only the controller initiates the function calls. + +Similarly, to state desire in nominating: + +``` +// controller account 4 nominates for account 10 and 20. +Staking::nominate(Origin::signed(4), vec![20, 10]); +``` + +Finally, account 4 can withdraw from any of the above roles via + +``` +Staking::chill(Origin::signed(4)); +``` + +### ??? + +TODO: What else could be a usage example here interesting for the user? Do we have any? + +## Implementation Details + +### Slot Stake + +The term `slot_stake` will be used throughout this section. It refers to a value calculated at the end of each era, containing the _minimum value at stake among all validators._ + +### Reward Calculation + + - Rewards are recorded **per-session** and paid **per-era**. The value of reward for each session is calculated at the end of the session based on the timeliness of the session, then accumulated to be paid later. The value of the new _per-session-reward_ is calculated at the end of each era by multiplying `slot_stake` and a configuration storage named [`SessionReward`](#todo). + - Once a new era is triggered, rewards are paid to the validators and the associated nominators. + - The validator can declare an amount that does not get shared with the nominators at each reward payout through their [`ValidatorPrefs`](#todo). This value gets deducted from the total reward that can be paid. The remaining portion is split among the validator and all of the nominators who had a vote for this validator, proportional to their staked value. + - All entities who receive a reward have the option to choose their reward destination, through the [`Payee`](#todo) storage, to be one of the following: + - Controller account. + - Stash account, not increasing the staked value. + - Stash account, also increasing the staked value. + +### Slashing details + +- A validator can be _reported_ to be offline at any point via [`on_offline_validator`](#todo) public function. +- Each validator declares how many times they can be _reported_ before it actually gets slashed via the `unstake_threshold` in [`ValidatorPrefs`](#todo). On top of this, the module also introduces a `OfflineSlashGrace`, which applies to all validators and prevents them from getting immediately slashed. +- Similar to the reward value, the slash value is updated at the end of each era by multiplying `slot_stake` and a configuration storage item, [`OfflineSlash`](#todo). +- Once a validator has been reported a sufficient amount of times, the actual value that gets deducted from that validator, and every single nominator that voted for it calculated by multiplying the result of the above point by `2.pow(unstake_threshold)`. + - If the previous overflow, then `slot_stake` is used. + - If the previous is more than what the validator/nominator has in stake, all of their stake is slashed (`.max(total_stake)` in other words). + +### Election algorithm details. + +Current election algorithm is implemented based on Phragmen. The reference implementation can be found [here](https://github.com/w3f/consensus/tree/master/NPoS). + +## Extensibility + +// Details that the user can modify or customize to make their own + + +## Dependencies + +### GenesisConfig + +See [`chain_spec.rs`](#todo) for a list of attributed that can be provided. + +### Related Modules + +- [Balances](#todo): Used to manage values at stake. +- [Sessions](#todo): Used to manage sessions. Also, a list of new validators is also stored in the sessions module's [`Validators`](#todo) at the end of each era. +- [System](#todo): Used to obtain block number and time, among other details. From b9d09bb7d751bc01480d813b1e1ac20eb1296362 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Sat, 9 Mar 2019 15:04:47 +0100 Subject: [PATCH 02/54] Remove md style links. --- srml/staking/README.adoc | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/srml/staking/README.adoc b/srml/staking/README.adoc index 7662b82a19d08..931ff8c4cb5c2 100644 --- a/srml/staking/README.adoc +++ b/srml/staking/README.adoc @@ -17,21 +17,21 @@ The staking module is the means by which a set of network maintainers (known as ### Scenarios -Almost any interaction with the staking module requires at least one account to become **bonded**, also known as being a **staker**. For this, all that it is needed is a secondary _**stash account**_ which will hold the staked funds. Henceforth, the former account that initiated the interest is called the **controller** and the later, holding the funds, is named the **stash**. Also, note that this implies that entering the staking process requires an _account pair_, one of which to take the role of the controller and one to be the frozen stash account (any value locked in stash cannot be used, hence called _frozen_). This process in the public API is mostly referred to as _bonding_ via the [`bond()`](#todo) function. +Almost any interaction with the staking module requires at least one account to become **bonded**, also known as being a **staker**. For this, all that it is needed is a secondary _**stash account**_ which will hold the staked funds. Henceforth, the former account that initiated the interest is called the **controller** and the later, holding the funds, is named the **stash**. Also, note that this implies that entering the staking process requires an _account pair_, one of which to take the role of the controller and one to be the frozen stash account (any value locked in stash cannot be used, hence called _frozen_). This process in the public API is mostly referred to as _bonding_ via the `bond()` function. Any account pair successfully placed at stake can accept three possible roles, namely: `validate`, `nominate` or simply `chill`. Note that during the process of accepting these roles, the _controller_ account is always responsible for declaring interest and the _stash_ account stays untouched, without directly interacting in any operation. -A **validator** takes the role of either validating blocks or ensuring their finality, maintaining the veracity of the network in other words. A validator should avoid both any sort of malicious misbehavior and going offline. Unlike nominating, 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 election is determined by nominators and their votes. An account can become a validator via the [`validate()`](#todo) call. +A **validator** takes the role of either validating blocks or ensuring their finality, maintaining the veracity of the network in other words. A validator should avoid both any sort of malicious misbehavior and going offline. Unlike nominating, 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 election is determined by nominators and their votes. An account can become a validator via the `validate()` call. -A **nominator** does not take any _direct_ role in maintaining the network, instead, it votes on a set of validators to be elected. Once interest in nomination is stated by an account, it takes effect _immediately_, meaning that their votes will be taken into account at the next election round. As mentioned above, a nominator must also place some fund in a stash account, essentially indicating the _weight_ of their vote. In some sense, the nominator bets on the honesty of a set of validators by voting for them, with the goal of having a share at the reward granted to them. Any rewards given to a validator is shared among that validator and all of the nominators that voted for it. The same logic applies to the slash of a validator; if a validator misbehaves all of its nominators also get slashed. 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()`](#todo) call. +A **nominator** does not take any _direct_ role in maintaining the network, instead, it votes on a set of validators to be elected. Once interest in nomination is stated by an account, it takes effect _immediately_, meaning that their votes will be taken into account at the next election round. As mentioned above, a nominator must also place some fund in a stash account, essentially indicating the _weight_ of their vote. In some sense, the nominator bets on the honesty of a set of validators by voting for them, with the goal of having a share at the reward granted to them. Any rewards given to a validator is shared among that validator and all of the nominators that voted for it. The same logic applies to the slash of a validator; if a validator misbehaves all of its nominators also get slashed. This rule incentivizes the nominators to NOT vote for the misbehaving/offline validators as much as possible, simply because the nominators will also lose funds if they vote poorly. An account can become a nominator via the `nominate()` call. The **reward and slashing** procedure are the core of the staking module, attempting to _embrace valid behavior_ while _punishing any misbehavior or lack of availability_. Slashing can occur at any point in time, once a misbehavior is reported. One such misbehavior is a validator to be detected as offline more than a certain number of times. Once slashing is determined, a value is deducted from the balance of validator and all the nominators who voted for this validator. Same rules apply to the rewards in the sense of being shared among validator and its associated nominators. -Finally, any of the roles above can choose to temporarily step back and just chill for a while. This means that if they are a nominator, they will not be considered as voters anymore and if they are validators, they will no longer be a candidate for the next election (again, both effects apply at the beginning of the next era). An account can step back via the [`chill()`](#todo) call. +Finally, any of the roles above can choose to temporarily step back and just chill for a while. This means that if they are a nominator, they will not be considered as voters anymore and if they are validators, they will no longer be a candidate for the next election (again, both effects apply at the beginning of the next era). An account can step back via the [`chill()` call. ## Public Interface -The staking module contains many public storage items and (im)mutable functions. Please refer to the [rustdoc](#todo) to see the full list. +The staking module contains many public storage items and (im)mutable functions. Please refer to the rustdoc to see the full list. ## Usage Example @@ -69,8 +69,6 @@ Finally, account 4 can withdraw from any of the above roles via Staking::chill(Origin::signed(4)); ``` -### ??? - TODO: What else could be a usage example here interesting for the user? Do we have any? ## Implementation Details @@ -81,19 +79,19 @@ The term `slot_stake` will be used throughout this section. It refers to a value ### Reward Calculation - - Rewards are recorded **per-session** and paid **per-era**. The value of reward for each session is calculated at the end of the session based on the timeliness of the session, then accumulated to be paid later. The value of the new _per-session-reward_ is calculated at the end of each era by multiplying `slot_stake` and a configuration storage named [`SessionReward`](#todo). + - Rewards are recorded **per-session** and paid **per-era**. The value of reward for each session is calculated at the end of the session based on the timeliness of the session, then accumulated to be paid later. The value of the new _per-session-reward_ is calculated at the end of each era by multiplying `slot_stake` and a configuration storage named `SessionReward`. - Once a new era is triggered, rewards are paid to the validators and the associated nominators. - - The validator can declare an amount that does not get shared with the nominators at each reward payout through their [`ValidatorPrefs`](#todo). This value gets deducted from the total reward that can be paid. The remaining portion is split among the validator and all of the nominators who had a vote for this validator, proportional to their staked value. - - All entities who receive a reward have the option to choose their reward destination, through the [`Payee`](#todo) storage, to be one of the following: + - The validator can declare an amount that does not get shared with the nominators at each reward payout through their `ValidatorPrefs`. This value gets deducted from the total reward that can be paid. The remaining portion is split among the validator and all of the nominators who had a vote for this validator, proportional to their staked value. + - All entities who receive a reward have the option to choose their reward destination, through the `Payee` storage, to be one of the following: - Controller account. - Stash account, not increasing the staked value. - Stash account, also increasing the staked value. ### Slashing details -- A validator can be _reported_ to be offline at any point via [`on_offline_validator`](#todo) public function. -- Each validator declares how many times they can be _reported_ before it actually gets slashed via the `unstake_threshold` in [`ValidatorPrefs`](#todo). On top of this, the module also introduces a `OfflineSlashGrace`, which applies to all validators and prevents them from getting immediately slashed. -- Similar to the reward value, the slash value is updated at the end of each era by multiplying `slot_stake` and a configuration storage item, [`OfflineSlash`](#todo). +- A validator can be _reported_ to be offline at any point via `on_offline_validator` public function. +- Each validator declares how many times they can be _reported_ before it actually gets slashed via the `unstake_threshold` in `ValidatorPrefs`. On top of this, the module also introduces a `OfflineSlashGrace`, which applies to all validators and prevents them from getting immediately slashed. +- Similar to the reward value, the slash value is updated at the end of each era by multiplying `slot_stake` and a configuration storage item, `OfflineSlash`. - Once a validator has been reported a sufficient amount of times, the actual value that gets deducted from that validator, and every single nominator that voted for it calculated by multiplying the result of the above point by `2.pow(unstake_threshold)`. - If the previous overflow, then `slot_stake` is used. - If the previous is more than what the validator/nominator has in stake, all of their stake is slashed (`.max(total_stake)` in other words). @@ -111,10 +109,10 @@ Current election algorithm is implemented based on Phragmen. The reference imple ### GenesisConfig -See [`chain_spec.rs`](#todo) for a list of attributed that can be provided. +See `chain_spec.rs` for a list of attributed that can be provided. ### Related Modules -- [Balances](#todo): Used to manage values at stake. -- [Sessions](#todo): Used to manage sessions. Also, a list of new validators is also stored in the sessions module's [`Validators`](#todo) at the end of each era. -- [System](#todo): Used to obtain block number and time, among other details. +- Balances: Used to manage values at stake. +- Sessions: Used to manage sessions. Also, a list of new validators is also stored in the sessions module's `Validators` at the end of each era. +- System: Used to obtain block number and time, among other details. From 7df2b19ff20e97f22213f69e32b6922b42fd3dcc Mon Sep 17 00:00:00 2001 From: kianenigma Date: Sat, 9 Mar 2019 15:07:12 +0100 Subject: [PATCH 03/54] Remove todos. --- srml/staking/README.adoc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/srml/staking/README.adoc b/srml/staking/README.adoc index 931ff8c4cb5c2..0ef8d4b90876a 100644 --- a/srml/staking/README.adoc +++ b/srml/staking/README.adoc @@ -8,7 +8,7 @@ The staking module is the means by which a set of network maintainers (known as ### Terminology - **Staking**: The process of locking up funds for some time, placing them at risk of slashing (loss) in order to become a rewarded maintainer of the network. -- **Validating**: The process of running a node to actively maintain the network, either by producing blocks or guaranteeing finality of the chain. (see [here]() for the details of how block production and finalization are separated) +- **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 which controls an owner's funds for staking. @@ -69,7 +69,6 @@ Finally, account 4 can withdraw from any of the above roles via Staking::chill(Origin::signed(4)); ``` -TODO: What else could be a usage example here interesting for the user? Do we have any? ## Implementation Details @@ -104,7 +103,6 @@ Current election algorithm is implemented based on Phragmen. The reference imple // Details that the user can modify or customize to make their own - ## Dependencies ### GenesisConfig From 28b959b5675a2f7208b142cf65b23b4d298550ec Mon Sep 17 00:00:00 2001 From: kianenigma Date: Sat, 9 Mar 2019 15:11:53 +0100 Subject: [PATCH 04/54] Add rust code types --- srml/staking/README.adoc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/srml/staking/README.adoc b/srml/staking/README.adoc index 0ef8d4b90876a..fc25f5a704909 100644 --- a/srml/staking/README.adoc +++ b/srml/staking/README.adoc @@ -39,7 +39,7 @@ The staking module contains many public storage items and (im)mutable functions. An arbitrary account pair, given that the associated stash has the required funds, can become stakers via the following call: -``` +```rust // bond account 3 as stash // account 4 as controller // with stash value 1500 units @@ -49,7 +49,7 @@ Staking::bond(Origin::signed(3), 4, 1500, RewardDestination::Controller); To state desire in becoming a validator: -``` +```rust // controller account 4 states desire for validation with the given preferences. Staking::validate(Origin::signed(4), ValidatorPrefs::default()); ``` @@ -58,14 +58,14 @@ Note that, as mentioned, the stash account is transparent in such calls and only Similarly, to state desire in nominating: -``` +```rust // controller account 4 nominates for account 10 and 20. Staking::nominate(Origin::signed(4), vec![20, 10]); ``` Finally, account 4 can withdraw from any of the above roles via -``` +```rust Staking::chill(Origin::signed(4)); ``` From 91be68b9a7d029b33331c58a89a7884e57366533 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 11 Mar 2019 16:10:27 +0100 Subject: [PATCH 05/54] Rename and fix review notes. --- srml/staking/README.adoc | 116 --------------------------------------- 1 file changed, 116 deletions(-) delete mode 100644 srml/staking/README.adoc diff --git a/srml/staking/README.adoc b/srml/staking/README.adoc deleted file mode 100644 index fc25f5a704909..0000000000000 --- a/srml/staking/README.adoc +++ /dev/null @@ -1,116 +0,0 @@ -# Staking Module - -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”__ should they be found not to bee discharging their duties properly. - - -## Overview - -### Terminology - -- **Staking**: The process of locking up funds for some time, placing them at risk of slashing (loss) in order to become a rewarded maintainer of the network. -- **Validating**: The process of running a node to actively maintain the network, either by producing blocks or guaranteeing finality of the chain. -- **Nominating**: The process of placing staked funds behind one or more validators in order to share in any reward and punishment, they take. -- **Stash account**: The account holding an owner's funds used for staking. -- **Controller account**: The account which controls an owner's funds for staking. -- **Era**: A (whole) number of sessions which is the period that the validator set (and each validator's active nominator set) is recalculated and where rewards are paid out. -- **Slash**: The punishment of a staker by reducing their funds. - -### Scenarios - -Almost any interaction with the staking module requires at least one account to become **bonded**, also known as being a **staker**. For this, all that it is needed is a secondary _**stash account**_ which will hold the staked funds. Henceforth, the former account that initiated the interest is called the **controller** and the later, holding the funds, is named the **stash**. Also, note that this implies that entering the staking process requires an _account pair_, one of which to take the role of the controller and one to be the frozen stash account (any value locked in stash cannot be used, hence called _frozen_). This process in the public API is mostly referred to as _bonding_ via the `bond()` function. - -Any account pair successfully placed at stake can accept three possible roles, namely: `validate`, `nominate` or simply `chill`. Note that during the process of accepting these roles, the _controller_ account is always responsible for declaring interest and the _stash_ account stays untouched, without directly interacting in any operation. - -A **validator** takes the role of either validating blocks or ensuring their finality, maintaining the veracity of the network in other words. A validator should avoid both any sort of malicious misbehavior and going offline. Unlike nominating, 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 election is determined by nominators and their votes. An account can become a validator via the `validate()` call. - -A **nominator** does not take any _direct_ role in maintaining the network, instead, it votes on a set of validators to be elected. Once interest in nomination is stated by an account, it takes effect _immediately_, meaning that their votes will be taken into account at the next election round. As mentioned above, a nominator must also place some fund in a stash account, essentially indicating the _weight_ of their vote. In some sense, the nominator bets on the honesty of a set of validators by voting for them, with the goal of having a share at the reward granted to them. Any rewards given to a validator is shared among that validator and all of the nominators that voted for it. The same logic applies to the slash of a validator; if a validator misbehaves all of its nominators also get slashed. This rule incentivizes the nominators to NOT vote for the misbehaving/offline validators as much as possible, simply because the nominators will also lose funds if they vote poorly. An account can become a nominator via the `nominate()` call. - -The **reward and slashing** procedure are the core of the staking module, attempting to _embrace valid behavior_ while _punishing any misbehavior or lack of availability_. Slashing can occur at any point in time, once a misbehavior is reported. One such misbehavior is a validator to be detected as offline more than a certain number of times. Once slashing is determined, a value is deducted from the balance of validator and all the nominators who voted for this validator. Same rules apply to the rewards in the sense of being shared among validator and its associated nominators. - -Finally, any of the roles above can choose to temporarily step back and just chill for a while. This means that if they are a nominator, they will not be considered as voters anymore and if they are validators, they will no longer be a candidate for the next election (again, both effects apply at the beginning of the next era). An account can step back via the [`chill()` call. - -## Public Interface - -The staking module contains many public storage items and (im)mutable functions. Please refer to the rustdoc to see the full list. - -## Usage Example - -### Bonding and Accepting Roles - -An arbitrary account pair, given that the associated stash has the required funds, can become stakers via the following call: - -```rust -// bond account 3 as stash -// account 4 as controller -// with stash value 1500 units -// while the rewards get transferred to the controller account. -Staking::bond(Origin::signed(3), 4, 1500, RewardDestination::Controller); -``` - -To state desire in becoming a validator: - -```rust -// controller account 4 states desire for validation with the given preferences. -Staking::validate(Origin::signed(4), ValidatorPrefs::default()); -``` - -Note that, as mentioned, the stash account is transparent in such calls and only the controller initiates the function calls. - -Similarly, to state desire in nominating: - -```rust -// controller account 4 nominates for account 10 and 20. -Staking::nominate(Origin::signed(4), vec![20, 10]); -``` - -Finally, account 4 can withdraw from any of the above roles via - -```rust -Staking::chill(Origin::signed(4)); -``` - - -## Implementation Details - -### Slot Stake - -The term `slot_stake` will be used throughout this section. It refers to a value calculated at the end of each era, containing the _minimum value at stake among all validators._ - -### Reward Calculation - - - Rewards are recorded **per-session** and paid **per-era**. The value of reward for each session is calculated at the end of the session based on the timeliness of the session, then accumulated to be paid later. The value of the new _per-session-reward_ is calculated at the end of each era by multiplying `slot_stake` and a configuration storage named `SessionReward`. - - Once a new era is triggered, rewards are paid to the validators and the associated nominators. - - The validator can declare an amount that does not get shared with the nominators at each reward payout through their `ValidatorPrefs`. This value gets deducted from the total reward that can be paid. The remaining portion is split among the validator and all of the nominators who had a vote for this validator, proportional to their staked value. - - All entities who receive a reward have the option to choose their reward destination, through the `Payee` storage, to be one of the following: - - Controller account. - - Stash account, not increasing the staked value. - - Stash account, also increasing the staked value. - -### Slashing details - -- A validator can be _reported_ to be offline at any point via `on_offline_validator` public function. -- Each validator declares how many times they can be _reported_ before it actually gets slashed via the `unstake_threshold` in `ValidatorPrefs`. On top of this, the module also introduces a `OfflineSlashGrace`, which applies to all validators and prevents them from getting immediately slashed. -- Similar to the reward value, the slash value is updated at the end of each era by multiplying `slot_stake` and a configuration storage item, `OfflineSlash`. -- Once a validator has been reported a sufficient amount of times, the actual value that gets deducted from that validator, and every single nominator that voted for it calculated by multiplying the result of the above point by `2.pow(unstake_threshold)`. - - If the previous overflow, then `slot_stake` is used. - - If the previous is more than what the validator/nominator has in stake, all of their stake is slashed (`.max(total_stake)` in other words). - -### Election algorithm details. - -Current election algorithm is implemented based on Phragmen. The reference implementation can be found [here](https://github.com/w3f/consensus/tree/master/NPoS). - -## Extensibility - -// Details that the user can modify or customize to make their own - -## Dependencies - -### GenesisConfig - -See `chain_spec.rs` for a list of attributed that can be provided. - -### Related Modules - -- Balances: Used to manage values at stake. -- Sessions: Used to manage sessions. Also, a list of new validators is also stored in the sessions module's `Validators` at the end of each era. -- System: Used to obtain block number and time, among other details. From 5912ff59c4f7ac3dde384bc78ee0872cde1f5626 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 11 Mar 2019 16:11:13 +0100 Subject: [PATCH 06/54] Add new md file --- srml/staking/README.md | 139 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 srml/staking/README.md diff --git a/srml/staking/README.md b/srml/staking/README.md new file mode 100644 index 0000000000000..45d706ae7009a --- /dev/null +++ b/srml/staking/README.md @@ -0,0 +1,139 @@ +# Staking Module + + +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 they be found not to bee discharhing their duties properly[[1](#references)]. + +### Overview + +### Terminology + + +- Staking: The process of locking up funds for some time, placing them at risk of slashing (loss) in order to become a rewarded maintainer of the network. +- Validating: The process of running a node to actively maintain the network, either by producing blocks or guaranteeing finality of the chain. +- Nominating: The process of placing staked funds behind one or more validators in order to share in any reward, and punishment, they take. +- Stash account: The account holding an owner's funds used for staking. +- Controller account: The account which controls am owner's funds for staking. +- Era: A (whole) number of sessions which is the period that the validator set (and each validator's active nominator set) is recalculated and where rewards are paid out. +- Slash: The punishment of a staker by reducing their funds. + +### Goals + + +The staking system in Substrate NPoS is designed to achieve three goals: +- It should be possible to stake funds that are controlled by a cold wallet. +- It should be possible to withdraw some, or deposit more, funds without interrupting the role of t. +- It should be possible to switch between roles (nominator, validator, idle) with minimal overhead. + +### Scenarios + +#### Staking + +Almost any interaction with the staking module requires at least one account to become **bonded**, also known as being a **staker**. For this, all that it is needed is a secondary _**stash account**_ which will hold the staked funds. Henceforth, the former account that initiated the interest is called the **controller** and the later, holding the funds, is named the **stash**. Also, note that this implies that entering the staking process requires an _account pair_, one of which to take the role of the controller and one to be the frozen stash account (any value locked in stash cannot be used, hence called _frozen_). This process in the public API is mostly referred to as _bonding_ via the `bond()` function. + +Any account pair successfully placed at stake can accept three possible roles, namely: `validate`, `nominate` or simply `chill`. Note that during the process of accepting these roles, the _controller_ account is always responsible for declaring interest and the _stash_ account stays untouched, without directly interacting in any operation. + +#### Validating + +A **validator** takes the role of either validating blocks or ensuring their finality, maintaining the veracity of the network in other words. A validator should avoid both any sort of malicious misbehavior and going offline. Unlike nominating, 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 election is determined by nominators and their votes. An account can become a validator via the `validate()` call. + +#### Nomination + +A **nominator** does not take any _direct_ role in maintaining the network, instead, it votes on a set of validators to be elected. Once interest in nomination is stated by an account, it takes effect _immediately_, meaning that their votes will be taken into account at the next election round. As mentioned above, a nominator must also place some fund in a stash account, essentially indicating the _weight_ of their vote. In some sense, the nominator bets on the honesty of a set of validators by voting for them, with the goal of having a share at the reward granted to them. Any rewards given to a validator is shared among that validator and all of the nominators that voted for it. The same logic applies to the slash of a validator; if a validator misbehaves all of its nominators also get slashed. This rule incentivizes the nominators to NOT vote for the misbehaving/offline validators as much as possible, simply because the nominators will also lose funds if they vote poorly. An account can become a nominator via the `nominate()` call. + +#### Rewards and Slash + +The **reward and slashing** procedure are the core of the staking module, attempting to _embrace valid behavior_ while _punishing any misbehavior or lack of availability_. Slashing can occur at any point in time, once a misbehavior is reported. One such misbehavior is a validator to be detected as offline more than a certain number of times. Once slashing is determined, a value is deducted from the balance of validator and all the nominators who voted for this validator. Same rules apply to the rewards in the sense of being shared among validator and its associated nominators. + +Finally, any of the roles above can choose to temporarily step back and just chill for a while. This means that if they are a nominator, they will not be considered as voters anymore and if they are validators, they will no longer be a candidate for the next election (again, both effects apply at the beginning of the next era). An account can step back via the `chill()` call. + +## Public Interface + +The staking module contains many public storage items and (im)mutable, and dispatchable, functions. Please refer to the `Module` struct definition for more details. + +## Usage Example + +### Bonding and Accepting Roles + +An arbitrary account pair, given that the associated stash has the required funds, can become stakers via the following call: + +```rust +// bond account 3 as stash +// account 4 as controller +// with stash value 1500 units +// while the rewards get transferred to the controller account. +Staking::bond(Origin::signed(3), 4, 1500, RewardDestination::Controller); +``` + +To state desire in becoming a validator: + +```rust +// controller account 4 states desire for validation with the given preferences. +Staking::validate(Origin::signed(4), ValidatorPrefs::default()); +``` + +Note that, as mentioned, the stash account is transparent in such calls and only the controller initiates the function calls. + +Similarly, to state desire in nominating: + +```rust +// controller account 4 nominates for account 10 and 20. +Staking::nominate(Origin::signed(4), vec![20, 10]); +``` + +Finally, account 4 can withdraw from any of the above roles via + +```rust +Staking::chill(Origin::signed(4)); +``` + + +## Implementation Details + +### Slot Stake + +The term `slot_stake` will be used throughout this section. It refers to a value calculated at the end of each era, containing the _minimum value at stake among all validators._ + +### Reward Calculation + + - Rewards are recorded **per-session** and paid **per-era**. The value of reward for each session is calculated at the end of the session based on the timeliness of the session, then accumulated to be paid later. The value of the new _per-session-reward_ is calculated at the end of each era by multiplying `slot_stake` and a configuration storage named `SessionReward`. + - Once a new era is triggered, rewards are paid to the validators and the associated nominators. + - The validator can declare an amount, named `validator_payment`, that does not get shared with the nominators at each reward payout through their `ValidatorPrefs`. This value gets deducted from the total reward that can be paid. The remaining portion is split among the validator and all of the nominators who had a vote for this validator, proportional to their staked value. + - All entities who receive a reward have the option to choose their reward destination, through the `Payee` storage (see `set_payee()`), to be one of the following: + - Controller account. + - Stash account, not increasing the staked value. + - Stash account, also increasing the staked value. + +### Slashing details + +- A validator can be _reported_ to be offline at any point via `on_offline_validator` public function. +- Each validator declares how many times they can be _reported_ before it actually gets slashed via the `unstake_threshold` in `ValidatorPrefs`. On top of this, the module also introduces a `OfflineSlashGrace`, which applies to all validators and prevents them from getting immediately slashed. +- Similar to the reward value, the slash value is updated at the end of each era by multiplying `slot_stake` and a configuration storage item, `OfflineSlash`. +- Once a validator has been reported a sufficient amount of times, the actual value that gets deducted from that validator, and every single nominator that voted for it calculated by multiplying the result of the above point by `2.pow(unstake_threshold)`. + - If the previous overflow, then `slot_stake` is used. + - If the previous is more than what the validator/nominator has in stake, all of their stake is slashed (`.max(total_stake)` in other words). + +### Additional Fund Management Operations + +Any funds already placed into stash can be the target of the following operations: + +- The controller account can free an portion (or all) of the funds using the `unbond()` call. Note that the funds are not immediately accessible, instead, a duration denoted by `BondingDuration` number of eras must pass until the funds can be actually removed. +- To actually remove the funds, once the bonding duration is over, the `withdraw_unbonded()` can be used. +- As opposed to the above, additional funds can be added to the stash account via the `bond_extra()` transaction call. + +### Election algorithm details. + +Current election algorithm is implemented based on Phragmén. The reference implementation can be found [here](https://github.com/w3f/consensus/tree/master/NPoS). + +## GenesisConfig + +See the [`GensisConfig`](https://crates.parity.io/srml_staking/struct.GenesisConfig.html) for a list of attributed that can be provided. + +## Related Modules + +- [**Balances**](https://crates.parity.io/srml_balances/index.html): Used to manage values at stake. +- [**Sessions**](https://crates.parity.io/srml_session/index.html): Used to manage sessions. Also, a list of new validators is also stored in the sessions module's `Validators` at the end of each era. +- [**System**](https://crates.parity.io/srml_system/index.html): Used to obtain block number and time, among other details. + +# References + +1. This document is written as a more verbose version of the original [Staking.md]() file. Some sections, (denoted by `[1]`) are taken directly from the aforementioned document. \ No newline at end of file From 64ca546bfed1e5ae62bae883365616677a74bf0b Mon Sep 17 00:00:00 2001 From: Kian Peymani Date: Mon, 11 Mar 2019 16:17:03 +0100 Subject: [PATCH 07/54] Final touches. --- srml/staking/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/srml/staking/README.md b/srml/staking/README.md index 45d706ae7009a..589d881e1ac86 100644 --- a/srml/staking/README.md +++ b/srml/staking/README.md @@ -1,7 +1,7 @@ # Staking Module -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 they be found not to bee discharhing their duties properly[[1](#references)]. +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 they be found not to bee discharhing their duties properly [[1](#references)]. ### Overview @@ -34,7 +34,7 @@ Any account pair successfully placed at stake can accept three possible roles, n #### Validating -A **validator** takes the role of either validating blocks or ensuring their finality, maintaining the veracity of the network in other words. A validator should avoid both any sort of malicious misbehavior and going offline. Unlike nominating, 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 election is determined by nominators and their votes. An account can become a validator via the `validate()` call. +A **validator** takes the role of either validating blocks or ensuring their finality, maintaining the veracity of the network in other words. 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 election is determined by nominators and their votes. An account can become a validator via the `validate()` call. #### Nomination @@ -48,7 +48,7 @@ Finally, any of the roles above can choose to temporarily step back and just chi ## Public Interface -The staking module contains many public storage items and (im)mutable, and dispatchable, functions. Please refer to the `Module` struct definition for more details. +The staking module contains many public storage items and (im)mutable, and dispatchable, functions. Please refer to the [`Module`](https://crates.parity.io/srml_staking/struct.Module.html) struct definition for more details. ## Usage Example @@ -136,4 +136,4 @@ See the [`GensisConfig`](https://crates.parity.io/srml_staking/struct.GenesisCon # References -1. This document is written as a more verbose version of the original [Staking.md]() file. Some sections, (denoted by `[1]`) are taken directly from the aforementioned document. \ No newline at end of file +1. This document is written as a more verbose version of the original [Staking.md](./Staking.md) file. Some sections, (denoted by `[1]`) are taken directly from the aforementioned document. From a8ba78e502341ed598c5da7c9a250772c1c18821 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 11 Mar 2019 18:29:37 +0100 Subject: [PATCH 08/54] Migrate compleatly to rustdoc --- srml/staking/README.md | 139 --------------------------------------- srml/staking/src/lib.rs | 141 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 140 insertions(+), 140 deletions(-) delete mode 100644 srml/staking/README.md diff --git a/srml/staking/README.md b/srml/staking/README.md deleted file mode 100644 index 589d881e1ac86..0000000000000 --- a/srml/staking/README.md +++ /dev/null @@ -1,139 +0,0 @@ -# Staking Module - - -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 they be found not to bee discharhing their duties properly [[1](#references)]. - -### Overview - -### Terminology - - -- Staking: The process of locking up funds for some time, placing them at risk of slashing (loss) in order to become a rewarded maintainer of the network. -- Validating: The process of running a node to actively maintain the network, either by producing blocks or guaranteeing finality of the chain. -- Nominating: The process of placing staked funds behind one or more validators in order to share in any reward, and punishment, they take. -- Stash account: The account holding an owner's funds used for staking. -- Controller account: The account which controls am owner's funds for staking. -- Era: A (whole) number of sessions which is the period that the validator set (and each validator's active nominator set) is recalculated and where rewards are paid out. -- Slash: The punishment of a staker by reducing their funds. - -### Goals - - -The staking system in Substrate NPoS is designed to achieve three goals: -- It should be possible to stake funds that are controlled by a cold wallet. -- It should be possible to withdraw some, or deposit more, funds without interrupting the role of t. -- It should be possible to switch between roles (nominator, validator, idle) with minimal overhead. - -### Scenarios - -#### Staking - -Almost any interaction with the staking module requires at least one account to become **bonded**, also known as being a **staker**. For this, all that it is needed is a secondary _**stash account**_ which will hold the staked funds. Henceforth, the former account that initiated the interest is called the **controller** and the later, holding the funds, is named the **stash**. Also, note that this implies that entering the staking process requires an _account pair_, one of which to take the role of the controller and one to be the frozen stash account (any value locked in stash cannot be used, hence called _frozen_). This process in the public API is mostly referred to as _bonding_ via the `bond()` function. - -Any account pair successfully placed at stake can accept three possible roles, namely: `validate`, `nominate` or simply `chill`. Note that during the process of accepting these roles, the _controller_ account is always responsible for declaring interest and the _stash_ account stays untouched, without directly interacting in any operation. - -#### Validating - -A **validator** takes the role of either validating blocks or ensuring their finality, maintaining the veracity of the network in other words. 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 election is determined by nominators and their votes. An account can become a validator via the `validate()` call. - -#### Nomination - -A **nominator** does not take any _direct_ role in maintaining the network, instead, it votes on a set of validators to be elected. Once interest in nomination is stated by an account, it takes effect _immediately_, meaning that their votes will be taken into account at the next election round. As mentioned above, a nominator must also place some fund in a stash account, essentially indicating the _weight_ of their vote. In some sense, the nominator bets on the honesty of a set of validators by voting for them, with the goal of having a share at the reward granted to them. Any rewards given to a validator is shared among that validator and all of the nominators that voted for it. The same logic applies to the slash of a validator; if a validator misbehaves all of its nominators also get slashed. This rule incentivizes the nominators to NOT vote for the misbehaving/offline validators as much as possible, simply because the nominators will also lose funds if they vote poorly. An account can become a nominator via the `nominate()` call. - -#### Rewards and Slash - -The **reward and slashing** procedure are the core of the staking module, attempting to _embrace valid behavior_ while _punishing any misbehavior or lack of availability_. Slashing can occur at any point in time, once a misbehavior is reported. One such misbehavior is a validator to be detected as offline more than a certain number of times. Once slashing is determined, a value is deducted from the balance of validator and all the nominators who voted for this validator. Same rules apply to the rewards in the sense of being shared among validator and its associated nominators. - -Finally, any of the roles above can choose to temporarily step back and just chill for a while. This means that if they are a nominator, they will not be considered as voters anymore and if they are validators, they will no longer be a candidate for the next election (again, both effects apply at the beginning of the next era). An account can step back via the `chill()` call. - -## Public Interface - -The staking module contains many public storage items and (im)mutable, and dispatchable, functions. Please refer to the [`Module`](https://crates.parity.io/srml_staking/struct.Module.html) struct definition for more details. - -## Usage Example - -### Bonding and Accepting Roles - -An arbitrary account pair, given that the associated stash has the required funds, can become stakers via the following call: - -```rust -// bond account 3 as stash -// account 4 as controller -// with stash value 1500 units -// while the rewards get transferred to the controller account. -Staking::bond(Origin::signed(3), 4, 1500, RewardDestination::Controller); -``` - -To state desire in becoming a validator: - -```rust -// controller account 4 states desire for validation with the given preferences. -Staking::validate(Origin::signed(4), ValidatorPrefs::default()); -``` - -Note that, as mentioned, the stash account is transparent in such calls and only the controller initiates the function calls. - -Similarly, to state desire in nominating: - -```rust -// controller account 4 nominates for account 10 and 20. -Staking::nominate(Origin::signed(4), vec![20, 10]); -``` - -Finally, account 4 can withdraw from any of the above roles via - -```rust -Staking::chill(Origin::signed(4)); -``` - - -## Implementation Details - -### Slot Stake - -The term `slot_stake` will be used throughout this section. It refers to a value calculated at the end of each era, containing the _minimum value at stake among all validators._ - -### Reward Calculation - - - Rewards are recorded **per-session** and paid **per-era**. The value of reward for each session is calculated at the end of the session based on the timeliness of the session, then accumulated to be paid later. The value of the new _per-session-reward_ is calculated at the end of each era by multiplying `slot_stake` and a configuration storage named `SessionReward`. - - Once a new era is triggered, rewards are paid to the validators and the associated nominators. - - The validator can declare an amount, named `validator_payment`, that does not get shared with the nominators at each reward payout through their `ValidatorPrefs`. This value gets deducted from the total reward that can be paid. The remaining portion is split among the validator and all of the nominators who had a vote for this validator, proportional to their staked value. - - All entities who receive a reward have the option to choose their reward destination, through the `Payee` storage (see `set_payee()`), to be one of the following: - - Controller account. - - Stash account, not increasing the staked value. - - Stash account, also increasing the staked value. - -### Slashing details - -- A validator can be _reported_ to be offline at any point via `on_offline_validator` public function. -- Each validator declares how many times they can be _reported_ before it actually gets slashed via the `unstake_threshold` in `ValidatorPrefs`. On top of this, the module also introduces a `OfflineSlashGrace`, which applies to all validators and prevents them from getting immediately slashed. -- Similar to the reward value, the slash value is updated at the end of each era by multiplying `slot_stake` and a configuration storage item, `OfflineSlash`. -- Once a validator has been reported a sufficient amount of times, the actual value that gets deducted from that validator, and every single nominator that voted for it calculated by multiplying the result of the above point by `2.pow(unstake_threshold)`. - - If the previous overflow, then `slot_stake` is used. - - If the previous is more than what the validator/nominator has in stake, all of their stake is slashed (`.max(total_stake)` in other words). - -### Additional Fund Management Operations - -Any funds already placed into stash can be the target of the following operations: - -- The controller account can free an portion (or all) of the funds using the `unbond()` call. Note that the funds are not immediately accessible, instead, a duration denoted by `BondingDuration` number of eras must pass until the funds can be actually removed. -- To actually remove the funds, once the bonding duration is over, the `withdraw_unbonded()` can be used. -- As opposed to the above, additional funds can be added to the stash account via the `bond_extra()` transaction call. - -### Election algorithm details. - -Current election algorithm is implemented based on Phragmén. The reference implementation can be found [here](https://github.com/w3f/consensus/tree/master/NPoS). - -## GenesisConfig - -See the [`GensisConfig`](https://crates.parity.io/srml_staking/struct.GenesisConfig.html) for a list of attributed that can be provided. - -## Related Modules - -- [**Balances**](https://crates.parity.io/srml_balances/index.html): Used to manage values at stake. -- [**Sessions**](https://crates.parity.io/srml_session/index.html): Used to manage sessions. Also, a list of new validators is also stored in the sessions module's `Validators` at the end of each era. -- [**System**](https://crates.parity.io/srml_system/index.html): Used to obtain block number and time, among other details. - -# References - -1. This document is written as a more verbose version of the original [Staking.md](./Staking.md) file. Some sections, (denoted by `[1]`) are taken directly from the aforementioned document. diff --git a/srml/staking/src/lib.rs b/srml/staking/src/lib.rs index b96cb8bbf630a..b038fb550efc5 100644 --- a/srml/staking/src/lib.rs +++ b/srml/staking/src/lib.rs @@ -16,7 +16,146 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! Staking manager: Periodically determines the best set of validators. +//! # Staking Module +//! +//! +//! 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 they be found not to bee discharhing their duties properly [[1](#references)]. +//! +//! ### Overview +//! +//! ### Terminology +//! +//! +//! - Staking: The process of locking up funds for some time, placing them at risk of slashing (loss) in order to become a rewarded maintainer of the network. +//! - Validating: The process of running a node to actively maintain the network, either by producing blocks or guaranteeing finality of the chain. +//! - Nominating: The process of placing staked funds behind one or more validators in order to share in any reward, and punishment, they take. +//! - Stash account: The account holding an owner's funds used for staking. +//! - Controller account: The account which controls am owner's funds for staking. +//! - Era: A (whole) number of sessions which is the period that the validator set (and each validator's active nominator set) is recalculated and where rewards are paid out. +//! - Slash: The punishment of a staker by reducing their funds. +//! +//! ### Goals +//! +//! +//! The staking system in Substrate NPoS is designed to achieve three goals: +//! - It should be possible to stake funds that are controlled by a cold wallet. +//! - It should be possible to withdraw some, or deposit more, funds without interrupting the role of t. +//! - It should be possible to switch between roles (nominator, validator, idle) with minimal overhead. +//! +//! ### Scenarios +//! +//! #### Staking +//! +//! Almost any interaction with the staking module requires at least one account to become **bonded**, also known as being a **staker**. For this, all that it is needed is a secondary _**stash account**_ which will hold the staked funds. Henceforth, the former account that initiated the interest is called the **controller** and the later, holding the funds, is named the **stash**. Also, note that this implies that entering the staking process requires an _account pair_, one of which to take the role of the controller and one to be the frozen stash account (any value locked in stash cannot be used, hence called _frozen_). This process in the public API is mostly referred to as _bonding_ via the `bond()` function. +//! +//! Any account pair successfully placed at stake can accept three possible roles, namely: `validate`, `nominate` or simply `chill`. Note that during the process of accepting these roles, the _controller_ account is always responsible for declaring interest and the _stash_ account stays untouched, without directly interacting in any operation. +//! +//! #### Validating +//! +//! A **validator** takes the role of either validating blocks or ensuring their finality, maintaining the veracity of the network in other words. 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 election is determined by nominators and their votes. An account can become a validator via the `validate()` call. +//! +//! #### Nomination +//! +//! A **nominator** does not take any _direct_ role in maintaining the network, instead, it votes on a set of validators to be elected. Once interest in nomination is stated by an account, it takes effect _immediately_, meaning that their votes will be taken into account at the next election round. As mentioned above, a nominator must also place some fund in a stash account, essentially indicating the _weight_ of their vote. In some sense, the nominator bets on the honesty of a set of validators by voting for them, with the goal of having a share at the reward granted to them. Any rewards given to a validator is shared among that validator and all of the nominators that voted for it. The same logic applies to the slash of a validator; if a validator misbehaves all of its nominators also get slashed. This rule incentivizes the nominators to NOT vote for the misbehaving/offline validators as much as possible, simply because the nominators will also lose funds if they vote poorly. An account can become a nominator via the `nominate()` call. +//! +//! #### Rewards and Slash +//! +//! The **reward and slashing** procedure are the core of the staking module, attempting to _embrace valid behavior_ while _punishing any misbehavior or lack of availability_. Slashing can occur at any point in time, once a misbehavior is reported. One such misbehavior is a validator to be detected as offline more than a certain number of times. Once slashing is determined, a value is deducted from the balance of validator and all the nominators who voted for this validator. Same rules apply to the rewards in the sense of being shared among validator and its associated nominators. +//! +//! Finally, any of the roles above can choose to temporarily step back and just chill for a while. This means that if they are a nominator, they will not be considered as voters anymore and if they are validators, they will no longer be a candidate for the next election (again, both effects apply at the beginning of the next era). An account can step back via the `chill()` call. +//! +//! ## Public Interface +//! +//! The staking module contains many public storage items and (im)mutable, and dispatchable, functions. Please refer to the [`Module`](https://crates.parity.io/srml_staking/struct.Module.html) struct definition for more details. +//! +//! ## Usage Example +//! +//! ### Bonding and Accepting Roles +//! +//! An arbitrary account pair, given that the associated stash has the required funds, can become stakers via the following call: +//! +//! ```rust +//! // bond account 3 as stash +//! // account 4 as controller +//! // with stash value 1500 units +//! // while the rewards get transferred to the controller account. +//! Staking::bond(Origin::signed(3), 4, 1500, RewardDestination::Controller); +//! ``` +//! +//! To state desire in becoming a validator: +//! +//! ```rust +//! // controller account 4 states desire for validation with the given preferences. +//! Staking::validate(Origin::signed(4), ValidatorPrefs::default()); +//! ``` +//! +//! Note that, as mentioned, the stash account is transparent in such calls and only the controller initiates the function calls. +//! +//! Similarly, to state desire in nominating: +//! +//! ```rust +//! // controller account 4 nominates for account 10 and 20. +//! Staking::nominate(Origin::signed(4), vec![20, 10]); +//! ``` +//! +//! Finally, account 4 can withdraw from any of the above roles via +//! +//! ```rust +//! Staking::chill(Origin::signed(4)); +//! ``` +//! +//! +//! ## Implementation Details +//! +//! ### Slot Stake +//! +//! The term `slot_stake` will be used throughout this section. It refers to a value calculated at the end of each era, containing the _minimum value at stake among all validators._ +//! +//! ### Reward Calculation +//! +//! - Rewards are recorded **per-session** and paid **per-era**. The value of reward for each session is calculated at the end of the session based on the timeliness of the session, then accumulated to be paid later. The value of the new _per-session-reward_ is calculated at the end of each era by multiplying `slot_stake` and a configuration storage named `SessionReward`. +//! - Once a new era is triggered, rewards are paid to the validators and the associated nominators. +//! - The validator can declare an amount, named `validator_payment`, that does not get shared with the nominators at each reward payout through their `ValidatorPrefs`. This value gets deducted from the total reward that can be paid. The remaining portion is split among the validator and all of the nominators who had a vote for this validator, proportional to their staked value. +//! - All entities who receive a reward have the option to choose their reward destination, through the `Payee` storage (see `set_payee()`), to be one of the following: +//! - Controller account. +//! - Stash account, not increasing the staked value. +//! - Stash account, also increasing the staked value. +//! +//! ### Slashing details +//! +//! - A validator can be _reported_ to be offline at any point via `on_offline_validator` public function. +//! - Each validator declares how many times they can be _reported_ before it actually gets slashed via the `unstake_threshold` in `ValidatorPrefs`. On top of this, the module also introduces a `OfflineSlashGrace`, which applies to all validators and prevents them from getting immediately slashed. +//! - Similar to the reward value, the slash value is updated at the end of each era by multiplying `slot_stake` and a configuration storage item, `OfflineSlash`. +//! - Once a validator has been reported a sufficient amount of times, the actual value that gets deducted from that validator, and every single nominator that voted for it calculated by multiplying the result of the above point by `2.pow(unstake_threshold)`. +//! - If the previous overflow, then `slot_stake` is used. +//! - If the previous is more than what the validator/nominator has in stake, all of their stake is slashed (`.max(total_stake)` in other words). +//! +//! ### Additional Fund Management Operations +//! +//! Any funds already placed into stash can be the target of the following operations: +//! +//! - The controller account can free an portion (or all) of the funds using the `unbond()` call. Note that the funds are not immediately accessible, instead, a duration denoted by `BondingDuration` number of eras must pass until the funds can be actually removed. +//! - To actually remove the funds, once the bonding duration is over, the `withdraw_unbonded()` can be used. +//! - As opposed to the above, additional funds can be added to the stash account via the `bond_extra()` transaction call. +//! +//! ### Election algorithm details. +//! +//! Current election algorithm is implemented based on Phragmén. The reference implementation can be found [here](https://github.com/w3f/consensus/tree/master/NPoS). +//! +//! ## GenesisConfig +//! +//! See the [`GensisConfig`](https://crates.parity.io/srml_staking/struct.GenesisConfig.html) for a list of attributed that can be provided. +//! +//! ## Related Modules +//! +//! - [**Balances**](https://crates.parity.io/srml_balances/index.html): Used to manage values at stake. +//! - [**Sessions**](https://crates.parity.io/srml_session/index.html): Used to manage sessions. Also, a list of new validators is also stored in the sessions module's `Validators` at the end of each era. +//! - [**System**](https://crates.parity.io/srml_system/index.html): Used to obtain block number and time, among other details. +//! +//! # References +//! +//! 1. This document is written as a more verbose version of the original [Staking.md](./Staking.md) file. Some sections, (denoted by `[1]`) are taken directly from the aforementioned document. + #![cfg_attr(not(feature = "std"), no_std)] From 982d7e80426189983f561fe7879a3cfbfd4435cf Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 11 Mar 2019 18:29:58 +0100 Subject: [PATCH 09/54] Update link --- srml/staking/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srml/staking/src/lib.rs b/srml/staking/src/lib.rs index b038fb550efc5..764b63cb82de2 100644 --- a/srml/staking/src/lib.rs +++ b/srml/staking/src/lib.rs @@ -154,7 +154,7 @@ //! //! # References //! -//! 1. This document is written as a more verbose version of the original [Staking.md](./Staking.md) file. Some sections, (denoted by `[1]`) are taken directly from the aforementioned document. +//! 1. This document is written as a more verbose version of the original [Staking.md](../Staking.md) file. Some sections, (denoted by `[1]`) are taken directly from the aforementioned document. #![cfg_attr(not(feature = "std"), no_std)] From 4a572a28e8c6c4cd69d779969ebd424b2d431403 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 11 Mar 2019 18:43:12 +0100 Subject: [PATCH 10/54] Fix heading --- srml/staking/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srml/staking/src/lib.rs b/srml/staking/src/lib.rs index 764b63cb82de2..5c2056c179bbb 100644 --- a/srml/staking/src/lib.rs +++ b/srml/staking/src/lib.rs @@ -21,7 +21,7 @@ //! //! 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 they be found not to bee discharhing their duties properly [[1](#references)]. //! -//! ### Overview +//! ## Overview //! //! ### Terminology //! From 89682a64d4b99fa98c33c85200bab8d244c0873c Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 12 Mar 2019 16:06:07 +0100 Subject: [PATCH 11/54] Final touches wrt the new template. --- srml/staking/src/lib.rs | 51 ++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/srml/staking/src/lib.rs b/srml/staking/src/lib.rs index b311cba1fcf81..c417955c49b59 100644 --- a/srml/staking/src/lib.rs +++ b/srml/staking/src/lib.rs @@ -19,7 +19,7 @@ //! # Staking Module //! //! -//! 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 they be found not to bee discharhing their duties properly [[1](#references)]. +//! 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 they be found not to bee discharhing their duties properly. //! //! ## Overview //! @@ -32,7 +32,7 @@ //! - Stash account: The account holding an owner's funds used for staking. //! - Controller account: The account which controls am owner's funds for staking. //! - Era: A (whole) number of sessions which is the period that the validator set (and each validator's active nominator set) is recalculated and where rewards are paid out. -//! - Slash: The punishment of a staker by reducing their funds. +//! - Slash: The punishment of a staker by reducing their funds ([reference](#references)). //! //! ### Goals //! @@ -64,17 +64,30 @@ //! //! Finally, any of the roles above can choose to temporarily step back and just chill for a while. This means that if they are a nominator, they will not be considered as voters anymore and if they are validators, they will no longer be a candidate for the next election (again, both effects apply at the beginning of the next era). An account can step back via the `chill()` call. //! -//! ## Public Interface +//! ## Interface //! -//! The staking module contains many public storage items and (im)mutable, and dispatchable, functions. Please refer to the [`Module`](https://crates.parity.io/srml_staking/struct.Module.html) struct definition for more details. +//! ### Types //! -//! ## Usage Example +//! - `Currency`: Used as the measurement means of staking and funds management. +//! +//! ### Dispatchable //! -//! ### Bonding and Accepting Roles +//! The Dispatchable functions of the staking module enable the steps needed for entities to accept and and change their role, alongside some helper funcitons to get/set the metadata of the module. +//! +//! Please refer to the [`Call`](https://crates.parity.io/srml_staking/enum.Call.html) enum and its associated functions for a detailed list of dispatchable functions. +//! +//! ### Public +//! The staking module contains many public storage items and (im)mutable functions. Please refer to the [struct list](#structs) below and the [`Module`](https://crates.parity.io/srml_staking/struct.Module.html) struct definition for more details. +//! +//! ## Usage +//! +//! ### Prerequisites +//! +//! ### Snippet: Bonding and Accepting Roles //! //! An arbitrary account pair, given that the associated stash has the required funds, can become stakers via the following call: //! -//! ```rust +//! ```rust,ignore //! // bond account 3 as stash //! // account 4 as controller //! // with stash value 1500 units @@ -84,7 +97,7 @@ //! //! To state desire in becoming a validator: //! -//! ```rust +//! ```rust,ignore //! // controller account 4 states desire for validation with the given preferences. //! Staking::validate(Origin::signed(4), ValidatorPrefs::default()); //! ``` @@ -93,18 +106,17 @@ //! //! Similarly, to state desire in nominating: //! -//! ```rust +//! ```rust,ignore //! // controller account 4 nominates for account 10 and 20. //! Staking::nominate(Origin::signed(4), vec![20, 10]); //! ``` //! //! Finally, account 4 can withdraw from any of the above roles via //! -//! ```rust +//! ```rust,ignore //! Staking::chill(Origin::signed(4)); //! ``` //! -//! //! ## Implementation Details //! //! ### Slot Stake @@ -154,7 +166,7 @@ //! //! # References //! -//! 1. This document is written as a more verbose version of the original [Staking.md](../Staking.md) file. Some sections, (denoted by `[1]`) are taken directly from the aforementioned document. +//! 1. This document is written as a more verbose version of the original [Staking.md](../Staking.md) file. Some sections, are taken directly from the aforementioned document. #![cfg_attr(not(feature = "std"), no_std)] @@ -424,6 +436,7 @@ decl_module! { /// Take the origin account as a stash and lock up `value` of its balance. `controller` will be the /// account that controls it. + /// The dispatch origin for this call must be _Signed_. fn bond(origin, controller: ::Source, #[compact] value: BalanceOf, payee: RewardDestination) { let stash = ensure_signed(origin)?; @@ -449,7 +462,7 @@ decl_module! { /// /// Use this if there are additional funds in your stash account that you wish to bond. /// - /// NOTE: This call must be made by the controller, not the stash. + /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. fn bond_extra(origin, max_additional: BalanceOf) { let controller = ensure_signed(origin)?; let mut ledger = Self::ledger(&controller).ok_or("not a controller")?; @@ -470,7 +483,7 @@ decl_module! { /// Once the unlock period is done, you can call `withdraw_unbonded` to actually move /// the funds out of management ready for transfer. /// - /// NOTE: This call must be made by the controller, not the stash. + /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. /// /// See also [`Call::withdraw_unbonded`]. fn unbond(origin, #[compact] value: BalanceOf) { @@ -500,7 +513,7 @@ decl_module! { /// This essentially frees up that balance to be used by the stash account to do /// whatever it wants. /// - /// NOTE: This call must be made by the controller, not the stash. + /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. /// /// See also [`Call::unbond`]. fn withdraw_unbonded(origin) { @@ -514,7 +527,7 @@ decl_module! { /// /// Effects will be felt at the beginning of the next era. /// - /// NOTE: This call must be made by the controller, not the stash. + /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. fn validate(origin, prefs: ValidatorPrefs>) { let controller = ensure_signed(origin)?; let _ledger = Self::ledger(&controller).ok_or("not a controller")?; @@ -527,7 +540,7 @@ decl_module! { /// /// Effects will be felt at the beginning of the next era. /// - /// NOTE: This call must be made by the controller, not the stash. + /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. fn nominate(origin, targets: Vec<::Source>) { let controller = ensure_signed(origin)?; let _ledger = Self::ledger(&controller).ok_or("not a controller")?; @@ -545,7 +558,7 @@ decl_module! { /// /// Effects will be felt at the beginning of the next era. /// - /// NOTE: This call must be made by the controller, not the stash. + /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. fn chill(origin) { let controller = ensure_signed(origin)?; let _ledger = Self::ledger(&controller).ok_or("not a controller")?; @@ -557,7 +570,7 @@ decl_module! { /// /// Effects will be felt at the beginning of the next era. /// - /// NOTE: This call must be made by the controller, not the stash. + /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. fn set_payee(origin, payee: RewardDestination) { let controller = ensure_signed(origin)?; let _ledger = Self::ledger(&controller).ok_or("not a controller")?; From 956ec9634c822986b0dff12e2d248528faa9b761 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 12 Mar 2019 16:11:34 +0100 Subject: [PATCH 12/54] Remove empty prereq. --- srml/staking/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/srml/staking/src/lib.rs b/srml/staking/src/lib.rs index c417955c49b59..f28bed03e463d 100644 --- a/srml/staking/src/lib.rs +++ b/srml/staking/src/lib.rs @@ -81,7 +81,6 @@ //! //! ## Usage //! -//! ### Prerequisites //! //! ### Snippet: Bonding and Accepting Roles //! From 2879e1d6713398801a1294f0ce999ebf3bb5995d Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 12 Mar 2019 16:58:28 +0100 Subject: [PATCH 13/54] Fix more reviews --- srml/staking/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/srml/staking/src/lib.rs b/srml/staking/src/lib.rs index f28bed03e463d..68b62b2ede549 100644 --- a/srml/staking/src/lib.rs +++ b/srml/staking/src/lib.rs @@ -74,7 +74,7 @@ //! //! The Dispatchable functions of the staking module enable the steps needed for entities to accept and and change their role, alongside some helper funcitons to get/set the metadata of the module. //! -//! Please refer to the [`Call`](https://crates.parity.io/srml_staking/enum.Call.html) enum and its associated functions for a detailed list of dispatchable functions. +//! Please refer to the [`Call`] enum and its associated variants for a detailed list of dispatchable functions. //! //! ### Public //! The staking module contains many public storage items and (im)mutable functions. Please refer to the [struct list](#structs) below and the [`Module`](https://crates.parity.io/srml_staking/struct.Module.html) struct definition for more details. @@ -155,7 +155,7 @@ //! //! ## GenesisConfig //! -//! See the [`GensisConfig`](https://crates.parity.io/srml_staking/struct.GenesisConfig.html) for a list of attributed that can be provided. +//! See the [`GensisConfig`] for a list of attributed that can be provided. //! //! ## Related Modules //! From 105723b1be449f4e72d634bd9735227369508719 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 12 Mar 2019 18:47:48 +0100 Subject: [PATCH 14/54] Some final nits. --- srml/staking/src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/srml/staking/src/lib.rs b/srml/staking/src/lib.rs index 68b62b2ede549..ada23a39d5968 100644 --- a/srml/staking/src/lib.rs +++ b/srml/staking/src/lib.rs @@ -19,7 +19,7 @@ //! # Staking Module //! //! -//! 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 they be found not to bee discharhing their 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 they be found not to bee discharging their duties properly. //! //! ## Overview //! @@ -39,14 +39,14 @@ //! //! The staking system in Substrate NPoS is designed to achieve three goals: //! - It should be possible to stake funds that are controlled by a cold wallet. -//! - It should be possible to withdraw some, or deposit more, funds without interrupting the role of t. +//! - It should be possible to withdraw some, or deposit more, funds without interrupting the role of an entity. //! - It should be possible to switch between roles (nominator, validator, idle) with minimal overhead. //! //! ### Scenarios //! //! #### Staking //! -//! Almost any interaction with the staking module requires at least one account to become **bonded**, also known as being a **staker**. For this, all that it is needed is a secondary _**stash account**_ which will hold the staked funds. Henceforth, the former account that initiated the interest is called the **controller** and the later, holding the funds, is named the **stash**. Also, note that this implies that entering the staking process requires an _account pair_, one of which to take the role of the controller and one to be the frozen stash account (any value locked in stash cannot be used, hence called _frozen_). This process in the public API is mostly referred to as _bonding_ via the `bond()` function. +//! Almost any interaction with the staking module requires at least one account to become **bonded**, also known as being a **staker**. For this, all that it is needed is a secondary _**stash account**_ which will hold the staked funds. Henceforth, the former account that initiated the interest is called the **controller** and the latter, holding the funds, is named the **stash**. Also, note that this implies that entering the staking process requires an _account pair_, one of which to take the role of the controller and one to be the frozen stash account (any value locked in stash cannot be used, hence called _frozen_). This process in the public API is mostly referred to as _bonding_ via the `bond()` function. //! //! Any account pair successfully placed at stake can accept three possible roles, namely: `validate`, `nominate` or simply `chill`. Note that during the process of accepting these roles, the _controller_ account is always responsible for declaring interest and the _stash_ account stays untouched, without directly interacting in any operation. //! @@ -56,7 +56,7 @@ //! //! #### Nomination //! -//! A **nominator** does not take any _direct_ role in maintaining the network, instead, it votes on a set of validators to be elected. Once interest in nomination is stated by an account, it takes effect _immediately_, meaning that their votes will be taken into account at the next election round. As mentioned above, a nominator must also place some fund in a stash account, essentially indicating the _weight_ of their vote. In some sense, the nominator bets on the honesty of a set of validators by voting for them, with the goal of having a share at the reward granted to them. Any rewards given to a validator is shared among that validator and all of the nominators that voted for it. The same logic applies to the slash of a validator; if a validator misbehaves all of its nominators also get slashed. This rule incentivizes the nominators to NOT vote for the misbehaving/offline validators as much as possible, simply because the nominators will also lose funds if they vote poorly. An account can become a nominator via the `nominate()` call. +//! A **nominator** does not take any _direct_ role in maintaining the network, instead, it votes on a set of validators to be elected. Once interest in nomination is stated by an account, it takes effect _immediately_, meaning that their votes will be taken into account at the next election round. As mentioned above, a nominator must also place some fund in a stash account, essentially indicating the _weight_ of their vote. In some sense, the nominator bets on the honesty of a set of validators by voting for them, with the goal of having a share of the reward granted to them. Any rewards given to a validator is shared among that validator and all of the nominators that voted for it. The same logic applies to the slash of a validator; if a validator misbehaves all of its nominators also get slashed. This rule incentivizes the nominators to NOT vote for the misbehaving/offline validators as much as possible, simply because the nominators will also lose funds if they vote poorly. An account can become a nominator via the `nominate()` call. //! //! #### Rewards and Slash //! @@ -72,7 +72,7 @@ //! //! ### Dispatchable //! -//! The Dispatchable functions of the staking module enable the steps needed for entities to accept and and change their role, alongside some helper funcitons 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. //! //! Please refer to the [`Call`] enum and its associated variants for a detailed list of dispatchable functions. //! @@ -137,7 +137,7 @@ //! - A validator can be _reported_ to be offline at any point via `on_offline_validator` public function. //! - Each validator declares how many times they can be _reported_ before it actually gets slashed via the `unstake_threshold` in `ValidatorPrefs`. On top of this, the module also introduces a `OfflineSlashGrace`, which applies to all validators and prevents them from getting immediately slashed. //! - Similar to the reward value, the slash value is updated at the end of each era by multiplying `slot_stake` and a configuration storage item, `OfflineSlash`. -//! - Once a validator has been reported a sufficient amount of times, the actual value that gets deducted from that validator, and every single nominator that voted for it calculated by multiplying the result of the above point by `2.pow(unstake_threshold)`. +//! - Once a validator has been reported a sufficient amount of times, the actual value that gets deducted from that validator, and every single nominator that voted for it is calculated by multiplying the result of the above point by `2.pow(unstake_threshold)`. //! - If the previous overflow, then `slot_stake` is used. //! - If the previous is more than what the validator/nominator has in stake, all of their stake is slashed (`.max(total_stake)` in other words). //! From 5567859b677636dcab85b758ef5c3555f4c00b8d Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 14 Mar 2019 14:05:47 +0100 Subject: [PATCH 15/54] Fix some side issues. --- node/executor/src/lib.rs | 16 +--------------- srml/staking/src/lib.rs | 2 +- srml/staking/src/phragmen.rs | 34 +++++++++++++++------------------- 3 files changed, 17 insertions(+), 35 deletions(-) diff --git a/node/executor/src/lib.rs b/node/executor/src/lib.rs index 84b2de336ed58..cc5992a514555 100644 --- a/node/executor/src/lib.rs +++ b/node/executor/src/lib.rs @@ -445,13 +445,7 @@ mod tests { ] ); - // let mut digest = generic::Digest::::default(); - // digest.push(Log::from(::grandpa::RawLog::AuthoritiesChangeSignal(0, vec![ - // (Keyring::Charlie.to_raw_public().into(), 1), - // (Keyring::Bob.to_raw_public().into(), 1), - // (Keyring::Alice.to_raw_public().into(), 1), - // ]))); - let digest = generic::Digest::::default(); // TODO test this + let digest = generic::Digest::::default(); assert_eq!(Header::decode(&mut &block2.0[..]).unwrap().digest, digest); (block1, block2) @@ -584,14 +578,6 @@ mod tests { phase: Phase::Finalization, event: Event::session(session::RawEvent::NewSession(1)) }, - // EventRecord { // TODO: this might be wrong. - // phase: Phase::Finalization, - // event: Event::grandpa(::grandpa::RawEvent::NewAuthorities(vec![ - // (Keyring::Charlie.to_raw_public().into(), 1), - // (Keyring::Bob.to_raw_public().into(), 1), - // (Keyring::Alice.to_raw_public().into(), 1), - // ])), - // }, EventRecord { phase: Phase::Finalization, event: Event::treasury(treasury::RawEvent::Spending(0)) diff --git a/srml/staking/src/lib.rs b/srml/staking/src/lib.rs index bd354e0bf10e2..0311fdf618c2e 100644 --- a/srml/staking/src/lib.rs +++ b/srml/staking/src/lib.rs @@ -811,7 +811,7 @@ impl Module { /// Select a new validator set from the assembled stakers and their role preferences. /// - /// @returns the new SlotStake value. + /// returns the new SlotStake value. fn select_validators() -> BalanceOf { // Map of (would-be) validator account to amount of stake backing it. diff --git a/srml/staking/src/phragmen.rs b/srml/staking/src/phragmen.rs index bdaed1fee9760..d67612755bc0d 100644 --- a/srml/staking/src/phragmen.rs +++ b/srml/staking/src/phragmen.rs @@ -69,25 +69,25 @@ pub struct Vote { /// /// Reference implementation: https://github.com/w3f/consensus /// -/// @returns a vector of elected candidates +/// returns a vector of elected candidates pub fn elect( get_rounds: FR, get_validators: FV, get_nominators: FN, stash_of: FS, minimum_validator_count: usize, - ) -> Vec>> where - FR: Fn() -> usize, - FV: Fn() -> Box>) - >>, - FN: Fn() -> Box) - >>, - FS: Fn(T::AccountId) -> BalanceOf, +) -> Vec>> where + FR: Fn() -> usize, + FV: Fn() -> Box>) + >>, + FN: Fn() -> Box) + >>, + FS: Fn(T::AccountId) -> BalanceOf, { let rounds = get_rounds(); - let mut elected_candidates = vec![]; + let mut elected_candidates; // 1- Pre-process candidates and place them in a container let mut candidates = get_validators().map(|(who, _)| { @@ -130,6 +130,7 @@ pub fn elect( // 4- If we have more candidates then needed, run Phragmén. if candidates.len() > rounds { + elected_candidates = Vec::with_capacity(rounds); // Main election loop for _round in 0..rounds { // Loop 1: initialize score @@ -177,7 +178,6 @@ pub fn elect( } elected_candidates.push(winner); - } // end of all rounds // 4.1- Update backing stake of candidates and nominators @@ -185,15 +185,11 @@ pub fn elect( for v in &mut n.nominees { // if the target of this vote is among the winners, otherwise let go. if let Some(c) = elected_candidates.iter_mut().find(|c| c.who == v.who) { - v.backing_stake = as As>::sa( - n.stake.as_() - * *v.load - / *n.load - ); + v.backing_stake = as As>::sa(n.stake.as_() * *v.load / *n.load); c.exposure.total += v.backing_stake; // Update IndividualExposure of those who nominated and their vote won c.exposure.others.push( - IndividualExposure {who: n.who.clone(), value: v.backing_stake } + IndividualExposure { who: n.who.clone(), value: v.backing_stake } ); } } @@ -208,7 +204,7 @@ pub fn elect( if let Some(c) = elected_candidates.iter_mut().find(|c| c.who == v.who) { c.exposure.total += n.stake; c.exposure.others.push( - IndividualExposure {who: n.who.clone(), value: n.stake } + IndividualExposure { who: n.who.clone(), value: n.stake } ); } } From e739361fccf59f4119b4c41e1336e8f482a845b0 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 15 Mar 2019 10:56:03 +0100 Subject: [PATCH 16/54] Fix another set of reviews --- srml/staking/src/lib.rs | 101 ++++++++++++++++++++++++++--------- srml/staking/src/phragmen.rs | 2 +- 2 files changed, 76 insertions(+), 27 deletions(-) diff --git a/srml/staking/src/lib.rs b/srml/staking/src/lib.rs index 0311fdf618c2e..f5f5f7a8f1d03 100644 --- a/srml/staking/src/lib.rs +++ b/srml/staking/src/lib.rs @@ -19,7 +19,11 @@ //! # Staking Module //! //! -//! 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 they be found not to bee discharging their 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 their duties properly. +//! You can start using the Staking module by implementing the staking [`Trait`]. //! //! ## Overview //! @@ -30,8 +34,8 @@ //! - 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 which controls am 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. +//! - Controller account: The account which controls an owner's funds for staking. +//! - Era: A (whole) number of sessions, which is the period that the validator set (and each validator's active nominator set) is recalculated and where rewards are paid out. //! - Slash: The punishment of a staker by reducing their funds ([reference](#references)). //! //! ### Goals @@ -46,23 +50,52 @@ //! //! #### Staking //! -//! Almost any interaction with the staking module requires at least one account to become **bonded**, also known as being a **staker**. For this, all that it is needed is a secondary _**stash account**_ which will hold the staked funds. Henceforth, the former account that initiated the interest is called the **controller** and the latter, holding the funds, is named the **stash**. Also, note that this implies that entering the staking process requires an _account pair_, one of which to take the role of the controller and one to be the frozen stash account (any value locked in stash cannot be used, hence called _frozen_). This process in the public API is mostly referred to as _bonding_ via the `bond()` function. +//! Almost any interaction with the staking module requires at least one account to become **bonded**, also known as +//! being a **staker**. For this, all that it is needed is a secondary _**stash account**_ which will hold the staked funds. +//! Henceforth, the former account that initiated the interest is called the **controller** and the latter, holding the +//! funds, is named the **stash**. Also, note that this implies that entering the staking process requires an _account +//! pair_, one to take the role of the controller and one to be the frozen stash account (any value locked in +//! stash cannot be used, hence called _frozen_). This process in the public API is mostly referred to as _bonding_ via +//! the `bond()` function. //! -//! Any account pair successfully placed at stake can accept three possible roles, namely: `validate`, `nominate` or simply `chill`. Note that during the process of accepting these roles, the _controller_ account is always responsible for declaring interest and the _stash_ account stays untouched, without directly interacting in any operation. +//! Any account pair successfully placed at stake can accept three possible roles, namely: `validate`, `nominate` or +//! simply `chill`. Note that during the process of accepting these roles, the _controller_ account is always responsible +//! for declaring interest and the _stash_ account stays untouched, without directly interacting in any operation. //! //! #### Validating //! -//! A **validator** takes the role of either validating blocks or ensuring their finality, maintaining the veracity of the network in other words. 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 election is determined by nominators and their votes. An account can become a validator via the `validate()` call. +//! A **validator** takes the role of either validating blocks or ensuring their finality, maintaining the veracity of +//! the network. A validator should avoid both any sort of malicious misbehavior and going offline. +//! Bonded accounts that state interest in being a validator do NOT get immediately chosen as a validator. Instead, they +//! are declared as a _candidate_ and they _might_ get elected at the _next **era**_ as a validator. The result of the +//! election is determined by nominators and their votes. An account can become a validator via the `validate()` call. //! //! #### Nomination //! -//! A **nominator** does not take any _direct_ role in maintaining the network, instead, it votes on a set of validators to be elected. Once interest in nomination is stated by an account, it takes effect _immediately_, meaning that their votes will be taken into account at the next election round. As mentioned above, a nominator must also place some fund in a stash account, essentially indicating the _weight_ of their vote. In some sense, the nominator bets on the honesty of a set of validators by voting for them, with the goal of having a share of the reward granted to them. Any rewards given to a validator is shared among that validator and all of the nominators that voted for it. The same logic applies to the slash of a validator; if a validator misbehaves all of its nominators also get slashed. This rule incentivizes the nominators to NOT vote for the misbehaving/offline validators as much as possible, simply because the nominators will also lose funds if they vote poorly. An account can become a nominator via the `nominate()` call. +//! A **nominator** does not take any _direct_ role in maintaining the network, instead, it votes on a set of validators +//! to be elected. Once interest in nomination is stated by an account, it takes effect _immediately_, meaning that its +//! votes will be taken into account at the next election round. As mentioned above, a nominator must also place some +//! funds in a stash account, essentially indicating the _weight_ of its vote. In some sense, the nominator bets on the +//! honesty of a set of validators by voting for them, with the goal of having a share of the reward granted to them. +//! Any rewards given to a validator is shared among that validator and all of the nominators that voted for it. The +//! same logic applies to the slash of a validator; if a validator misbehaves all of its nominators also get slashed. +//! This rule incentivizes the nominators to NOT vote for the misbehaving/offline validators as much as possible, simply +//! because the nominators will also lose funds if they vote poorly. An account can become a nominator via the +//! `nominate()` call. //! //! #### Rewards and Slash //! -//! The **reward and slashing** procedure are the core of the staking module, attempting to _embrace valid behavior_ while _punishing any misbehavior or lack of availability_. Slashing can occur at any point in time, once a misbehavior is reported. One such misbehavior is a validator to be detected as offline more than a certain number of times. Once slashing is determined, a value is deducted from the balance of validator and all the nominators who voted for this validator. Same rules apply to the rewards in the sense of being shared among validator and its associated nominators. +//! The **reward and slashing** procedure are the core of the staking module, attempting to _embrace valid behavior_ +//! while _punishing any misbehavior or lack of availability_. Slashing can occur at any point in time, once +//! misbehavior is reported. One such misbehavior is a validator being detected as offline more than a certain number of +//! times. Once slashing is determined, a value is deducted from the balance of the validator and all the nominators who +//! voted for this validator. Same rules apply to the rewards in the sense of being shared among a validator and its +//! associated nominators. //! -//! Finally, any of the roles above can choose to temporarily step back and just chill for a while. This means that if they are a nominator, they will not be considered as voters anymore and if they are validators, they will no longer be a candidate for the next election (again, both effects apply at the beginning of the next era). An account can step back via the `chill()` call. +//! Finally, any of the roles above can choose to step back temporarily and just chill for a while. This means that if +//! they are a nominator, they will not be considered as voters anymore and if they are validators, they will no longer +//! be a candidate for the next election (again, both effects apply at the beginning of the next era). An account can +//! step back via the `chill()` call. //! //! ## Interface //! @@ -72,12 +105,14 @@ //! //! ### Dispatchable //! -//! 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. //! //! Please refer to the [`Call`] enum and its associated variants for a detailed list of dispatchable functions. //! //! ### Public -//! The staking module contains many public storage items and (im)mutable functions. Please refer to the [struct list](#structs) below and the [`Module`](https://crates.parity.io/srml_staking/struct.Module.html) struct definition for more details. +//! The staking module contains many public storage items and (im)mutable functions. Please refer to the [struct list](#structs) +//! below and the [`Module`](https://crates.parity.io/srml_staking/struct.Module.html) struct definition for more details. //! //! ## Usage //! @@ -94,7 +129,7 @@ //! Staking::bond(Origin::signed(3), 4, 1500, RewardDestination::Controller); //! ``` //! -//! To state desire in becoming a validator: +//! To state desire to become a validator: //! //! ```rust,ignore //! // controller account 4 states desire for validation with the given preferences. @@ -120,14 +155,21 @@ //! //! ### Slot Stake //! -//! The term `slot_stake` will be used throughout this section. It refers to a value calculated at the end of each era, containing the _minimum value at stake among all validators._ +//! The term `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._ //! //! ### Reward Calculation //! -//! - Rewards are recorded **per-session** and paid **per-era**. The value of reward for each session is calculated at the end of the session based on the timeliness of the session, then accumulated to be paid later. The value of the new _per-session-reward_ is calculated at the end of each era by multiplying `slot_stake` and a configuration storage named `SessionReward`. +//! - Rewards are recorded **per-session** and paid **per-era**. The value of the reward for each session is calculated at +//! the end of the session based on the timeliness of the session, then accumulated to be paid later. The value of +//! the new _per-session-reward_ is calculated at the end of each era by multiplying `slot_stake` and a configuration +//! storage item named `SessionReward`. //! - Once a new era is triggered, rewards are paid to the validators and the associated nominators. -//! - The validator can declare an amount, named `validator_payment`, that does not get shared with the nominators at each reward payout through their `ValidatorPrefs`. This value gets deducted from the total reward that can be paid. The remaining portion is split among the validator and all of the nominators who had a vote for this validator, proportional to their staked value. -//! - All entities who receive a reward have the option to choose their reward destination, through the `Payee` storage (see `set_payee()`), to be one of the following: +//! - The validator can declare an amount, named `validator_payment`, that does not get shared with the nominators at +//! each reward payout through their `ValidatorPrefs`. This value gets deducted from the total reward that can be paid. +//! The remaining portion is split among the validator and all of the nominators who had a vote for this validator, +//! proportional to their staked value. +//! - All entities who receive a reward have the option to choose their reward destination, through the `Payee` storage item (see `set_payee()`), to be one of the following: //! - Controller account. //! - Stash account, not increasing the staked value. //! - Stash account, also increasing the staked value. @@ -135,27 +177,33 @@ //! ### Slashing details //! //! - A validator can be _reported_ to be offline at any point via `on_offline_validator` public function. -//! - Each validator declares how many times they can be _reported_ before it actually gets slashed via the `unstake_threshold` in `ValidatorPrefs`. On top of this, the module also introduces a `OfflineSlashGrace`, which applies to all validators and prevents them from getting immediately slashed. -//! - Similar to the reward value, the slash value is updated at the end of each era by multiplying `slot_stake` and a configuration storage item, `OfflineSlash`. -//! - Once a validator has been reported a sufficient amount of times, the actual value that gets deducted from that validator, and every single nominator that voted for it is calculated by multiplying the result of the above point by `2.pow(unstake_threshold)`. -//! - If the previous overflow, then `slot_stake` is used. -//! - If the previous is more than what the validator/nominator has in stake, all of their stake is slashed (`.max(total_stake)` in other words). +//! - Each validator declares how many times it can be _reported_ before it actually gets slashed via the +//! `unstake_threshold` in `ValidatorPrefs`. On top of this, the module also introduces an `OfflineSlashGrace`, +//! which applies to all validators and prevents them from getting immediately slashed. +//! - Similar to the reward value, the slash value is updated at the end of each era by multiplying `slot_stake` and a +//! configuration storage item, `OfflineSlash`. +//! - Once a validator has been reported a sufficient number of times, the actual value that gets deducted from that +//! validator, and every single nominator that voted for it is calculated by multiplying the result of the above point +//! by `2.pow(unstake_threshold)`. +//! - If the previous overflows, then `slot_stake` is used. +//! - If the previous is more than what the validator/nominator has in stake, all of its stake is slashed (`.max(total_stake)`). //! //! ### Additional Fund Management Operations //! //! Any funds already placed into stash can be the target of the following operations: //! -//! - The controller account can free an portion (or all) of the funds using the `unbond()` call. Note that the funds are not immediately accessible, instead, a duration denoted by `BondingDuration` number of eras must pass until the funds can be actually removed. -//! - To actually remove the funds, once the bonding duration is over, the `withdraw_unbonded()` can be used. +//! - The controller account can free a portion (or all) of the funds using the `unbond()` call. Note that the funds +//! are not immediately accessible, instead, a duration denoted by `BondingDuration` (in number of eras) must pass until the funds can actually be removed. +//! - To actually remove the funds, once the bonding duration is over, `withdraw_unbonded()` can be used. //! - As opposed to the above, additional funds can be added to the stash account via the `bond_extra()` transaction call. //! //! ### Election algorithm details. //! -//! 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 current election algorithm is implemented based on Phragmén. The reference implementation can be found [here](https://github.com/w3f/consensus/tree/master/NPoS). //! //! ## GenesisConfig //! -//! See the [`GensisConfig`] for a list of attributed that can be provided. +//! See the [`GensisConfig`] for a list of attributes that can be provided. //! //! ## Related Modules //! @@ -460,6 +508,7 @@ decl_module! { /// Take the origin account as a stash and lock up `value` of its balance. `controller` will be the /// account that controls it. + /// /// The dispatch origin for this call must be _Signed_. fn bond(origin, controller: ::Source, #[compact] value: BalanceOf, payee: RewardDestination) { let stash = ensure_signed(origin)?; @@ -811,7 +860,7 @@ impl Module { /// Select a new validator set from the assembled stakers and their role preferences. /// - /// returns the new SlotStake value. + /// Returns the new SlotStake value. fn select_validators() -> BalanceOf { // Map of (would-be) validator account to amount of stake backing it. diff --git a/srml/staking/src/phragmen.rs b/srml/staking/src/phragmen.rs index d67612755bc0d..6211557ad525a 100644 --- a/srml/staking/src/phragmen.rs +++ b/srml/staking/src/phragmen.rs @@ -69,7 +69,7 @@ pub struct Vote { /// /// Reference implementation: https://github.com/w3f/consensus /// -/// returns a vector of elected candidates +/// Returns a vector of elected candidates pub fn elect( get_rounds: FR, get_validators: FV, From 4eb568ccb22782ae0517c5d8687c5c58b87bac9c Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 15 Mar 2019 12:19:49 +0100 Subject: [PATCH 17/54] Fix + stabilize leftover reivews. --- srml/staking/src/lib.rs | 13 +++++++++---- srml/staking/src/mock.rs | 22 +++++++++++++++------ srml/staking/src/phragmen.rs | 37 +++++++++++++----------------------- srml/staking/src/tests.rs | 8 +++----- 4 files changed, 41 insertions(+), 39 deletions(-) diff --git a/srml/staking/src/lib.rs b/srml/staking/src/lib.rs index 4650bff981d77..4b75f1f192167 100644 --- a/srml/staking/src/lib.rs +++ b/srml/staking/src/lib.rs @@ -31,7 +31,7 @@ use srml_support::traits::{ LockIdentifier, LockableCurrency, WithdrawReasons }; use session::OnSessionChange; -use primitives::{Perbill}; +use primitives::Perbill; use primitives::traits::{Zero, One, As, StaticLookup, Saturating, Bounded}; #[cfg(feature = "std")] use primitives::{Serialize, Deserialize}; @@ -48,7 +48,11 @@ const MAX_UNSTAKE_THRESHOLD: u32 = 10; // Indicates the initial status of the staker #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] -pub enum StakerStatus { Idle, Validator, Nominator(Vec), } +pub enum StakerStatus { + Idle, + Validator, + Nominator(Vec), +} /// A destination account for payment. #[derive(PartialEq, Eq, Copy, Clone, Encode, Decode)] @@ -166,7 +170,7 @@ pub struct Exposure { pub others: Vec>, } -type BalanceOf = <::Currency as ArithmeticType>::Type; +type BalanceOf = <::Currency as ArithmeticType>::Type; pub trait Trait: system::Trait + session::Trait { /// The staking balance. @@ -277,6 +281,7 @@ decl_storage! { build(|storage: &mut primitives::StorageOverlay, _: &mut primitives::ChildrenStorageOverlay, config: &GenesisConfig| { with_storage(storage, || { for &(ref stash, ref controller, balance, ref status) in &config.stakers { + assert!(T::Currency::free_balance(&stash) >= balance); let _ = >::bond( T::Origin::from(Some(stash.clone()).into()), T::Lookup::unlookup(controller.clone()), @@ -483,7 +488,7 @@ decl_module! { } } -/// An event in this module. +// An event in this module. decl_event!( pub enum Event where Balance = BalanceOf, ::AccountId { /// All validators have been rewarded by the given balance. diff --git a/srml/staking/src/mock.rs b/srml/staking/src/mock.rs index 6a76f350efbea..e91b6015cff44 100644 --- a/srml/staking/src/mock.rs +++ b/srml/staking/src/mock.rs @@ -176,21 +176,31 @@ impl ExtBuilder { (11, balance_factor * 1000), (20, balance_factor), (21, balance_factor * 2000), + (30, balance_factor), + (31, balance_factor * 3000), + (40, balance_factor), + (41, balance_factor * 4000), (100, 2000 * balance_factor), (101, 2000 * balance_factor), ] } else { vec![ - (1, 10 * balance_factor), (2, 20 * balance_factor), - (3, 300 * balance_factor), (4, 400 * balance_factor) + (1, 10 * balance_factor), + (2, 20 * balance_factor), + (3, 300 * balance_factor), + (4, 400 * balance_factor), ] } } else { vec![ - (10, balance_factor), (11, balance_factor * 10), - (20, balance_factor), (21, balance_factor * 20), - (30, balance_factor), (31, balance_factor * 30), - (40, balance_factor), (41, balance_factor * 40) + (10, balance_factor), + (11, balance_factor * 10), + (20, balance_factor), + (21, balance_factor * 20), + (30, balance_factor), + (31, balance_factor * 30), + (40, balance_factor), + (41, balance_factor * 40), ] }, existential_deposit: self.existential_deposit, diff --git a/srml/staking/src/phragmen.rs b/srml/staking/src/phragmen.rs index bdaed1fee9760..188c78f944e5b 100644 --- a/srml/staking/src/phragmen.rs +++ b/srml/staking/src/phragmen.rs @@ -1,4 +1,4 @@ -// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// Copyright 2019 Parity Technologies (UK) Ltd. // This file is part of Substrate. // Substrate is free software: you can redistribute it and/or modify @@ -24,7 +24,7 @@ use crate::{Exposure, BalanceOf, Trait, ValidatorPrefs, IndividualExposure}; // Wrapper around validation candidates some metadata. #[derive(Clone, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[cfg_attr(feature = "std", derive(Debug, Default))] pub struct Candidate { // The validator's account pub who: AccountId, @@ -53,9 +53,10 @@ pub struct Nominations { } // Wrapper around a nominator vote and the load of that vote. +// // Referred to as 'edge' in the Phragmén reference implementation. #[derive(Clone, Encode, Decode)] -#[cfg_attr(feature = "std", derive(Debug))] +#[cfg_attr(feature = "std", derive(Debug, Default))] pub struct Vote { // Account being voted for who: AccountId, @@ -94,9 +95,8 @@ pub fn elect( let stash_balance = stash_of(who.clone()); Candidate { who, - approval_stake: BalanceOf::::zero(), - score: Perquintill::zero(), exposure: Exposure { total: stash_balance, own: stash_balance, others: vec![] }, + ..Default::default() } }).collect::>>>(); @@ -116,7 +116,7 @@ pub fn elect( Nominations { who, nominees: nominees.into_iter() - .map(|n| Vote {who: n, load: Perquintill::zero(), backing_stake: BalanceOf::::zero()}) + .map(|n| Vote { who: n, ..Default::default() }) .collect::>>>(), stake: nominator_stake, load : Perquintill::zero(), @@ -135,23 +135,16 @@ pub fn elect( // Loop 1: initialize score for nominaotion in &nominations { for vote in &nominaotion.nominees { - let candidate = &vote.who; - if let Some(c) = candidates.iter_mut().find(|i| i.who == *candidate) { - let approval_stake = c.approval_stake; - c.score = Perquintill::from_xth(approval_stake.as_()); + if let Some(c) = candidates.iter_mut().find(|i| i.who == vote.who) { + c.score = Perquintill::from_xth(c.approval_stake.as_()); } } } // Loop 2: increment score. - for nominaotion in &nominations { - for vote in &nominaotion.nominees { - let candidate = &vote.who; - if let Some(c) = candidates.iter_mut().find(|i| i.who == *candidate) { - let approval_stake = c.approval_stake; - let temp = - nominaotion.stake.as_() - * *nominaotion.load - / approval_stake.as_(); + for nomination in &nominations { + for vote in &nomination.nominees { + if let Some(c) = candidates.iter_mut().find(|i| i.who == vote.who) { + let temp = nomination.stake.as_() * *nomination.load / c.approval_stake.as_(); c.score = Perquintill::from_quintillionths(*c.score + temp); } } @@ -166,11 +159,7 @@ pub fn elect( for n in &mut nominations { for v in &mut n.nominees { if v.who == winner.who { - v.load = - Perquintill::from_quintillionths( - *winner.score - - *n.load - ); + v.load = Perquintill::from_quintillionths(*winner.score - *n.load); n.load = winner.score; } } diff --git a/srml/staking/src/tests.rs b/srml/staking/src/tests.rs index 7921d7f313027..1bcbe7d7e610d 100644 --- a/srml/staking/src/tests.rs +++ b/srml/staking/src/tests.rs @@ -761,7 +761,6 @@ fn double_staking_should_fail() { fn session_and_eras_work() { with_externalities(&mut ExtBuilder::default() .sessions_per_era(2) - .reward(10) .build(), || { assert_eq!(Staking::era_length(), 2); @@ -1066,7 +1065,6 @@ fn bond_extra_and_withdraw_unbonded_works() { // * it can unbond a portion of its funds from the stash account. // * Once the unbonding period is done, it can actually take the funds out of the stash. with_externalities(&mut ExtBuilder::default() - .reward(10) // it is the default, just for verbosity .nominate(false) .build(), || { @@ -1407,8 +1405,8 @@ fn phragmen_poc_works() { // This is only because 30 has been bonded on the fly, exposures are stored at the very end of the era. // 35 is the point, not 'own' Exposure. - assert_eq!(Staking::stakers(30).own, 0); - assert_eq!(Staking::stakers(30).total, 0 + 35); + assert_eq!(Staking::stakers(30).own, 1000); + assert_eq!(Staking::stakers(30).total, 1000 + 35); // same as above. +25 is the point assert_eq!(Staking::stakers(20).own, 2010); assert_eq!(Staking::stakers(20).total, 2010 + 25); @@ -1482,7 +1480,7 @@ fn phragmen_election_works() { assert_eq!(winner_10.exposure.others[0].value, 21); assert_eq!(winner_10.exposure.others[1].value, 5); - assert_eq!(winner_30.exposure.total, 23); + assert_eq!(winner_30.exposure.total, 1000 + 23); assert_eq!(winner_30.score, Perquintill::from_quintillionths(42222222222222222)); assert_eq!(winner_30.exposure.others[0].value, 23); }) From 9b026cc50ac18c929d1fde0cc32100c12c83397b Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 15 Mar 2019 12:25:19 +0100 Subject: [PATCH 18/54] Remove unused test parameters --- srml/staking/src/mock.rs | 59 +++++++++------------------------------- 1 file changed, 13 insertions(+), 46 deletions(-) diff --git a/srml/staking/src/mock.rs b/srml/staking/src/mock.rs index e91b6015cff44..e3dc95664c350 100644 --- a/srml/staking/src/mock.rs +++ b/srml/staking/src/mock.rs @@ -79,7 +79,6 @@ pub struct ExtBuilder { session_length: u64, sessions_per_era: u64, current_era: u64, - monied: bool, reward: u64, validator_pool: bool, nominate: bool, @@ -94,7 +93,6 @@ impl Default for ExtBuilder { session_length: 1, sessions_per_era: 1, current_era: 0, - monied: true, reward: 10, validator_pool: false, nominate: true, @@ -121,16 +119,7 @@ impl ExtBuilder { self.current_era = current_era; self } - pub fn _monied(mut self, monied: bool) -> Self { - self.monied = monied; - self - } - pub fn reward(mut self, reward: u64) -> Self { - self.reward = reward; - self - } pub fn validator_pool(mut self, validator_pool: bool) -> Self { - // NOTE: this should only be set to true with monied = false. self.validator_pool = validator_pool; self } @@ -165,44 +154,22 @@ impl ExtBuilder { keys: vec![], }.assimilate_storage(&mut t, &mut c); let _ = balances::GenesisConfig::{ - balances: if self.monied { - if self.reward > 0 { - vec![ - (1, 10 * balance_factor), - (2, 20 * balance_factor), - (3, 300 * balance_factor), - (4, 400 * balance_factor), - (10, balance_factor), - (11, balance_factor * 1000), - (20, balance_factor), - (21, balance_factor * 2000), - (30, balance_factor), - (31, balance_factor * 3000), - (40, balance_factor), - (41, balance_factor * 4000), - (100, 2000 * balance_factor), - (101, 2000 * balance_factor), - ] - } else { - vec![ - (1, 10 * balance_factor), - (2, 20 * balance_factor), - (3, 300 * balance_factor), - (4, 400 * balance_factor), - ] - } - } else { - vec![ + balances: vec![ + (1, 10 * balance_factor), + (2, 20 * balance_factor), + (3, 300 * balance_factor), + (4, 400 * balance_factor), (10, balance_factor), - (11, balance_factor * 10), + (11, balance_factor * 1000), (20, balance_factor), - (21, balance_factor * 20), + (21, balance_factor * 2000), (30, balance_factor), - (31, balance_factor * 30), + (31, balance_factor * 3000), (40, balance_factor), - (41, balance_factor * 40), - ] - }, + (41, balance_factor * 4000), + (100, 2000 * balance_factor), + (101, 2000 * balance_factor), + ], existential_deposit: self.existential_deposit, transfer_fee: 0, creation_fee: 0, @@ -232,7 +199,7 @@ impl ExtBuilder { 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: if self.monied { Perbill::from_percent(40) } else { Perbill::zero() }, + offline_slash: Perbill::from_percent(40), current_session_reward: self.reward, current_offline_slash: 20, offline_slash_grace: 0, From a8075c74233e6b6347f756e76a89657e7182397c Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 15 Mar 2019 12:26:17 +0100 Subject: [PATCH 19/54] Fix typo. --- srml/staking/src/phragmen.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/srml/staking/src/phragmen.rs b/srml/staking/src/phragmen.rs index 188c78f944e5b..9017ed9549d59 100644 --- a/srml/staking/src/phragmen.rs +++ b/srml/staking/src/phragmen.rs @@ -133,8 +133,8 @@ pub fn elect( // Main election loop for _round in 0..rounds { // Loop 1: initialize score - for nominaotion in &nominations { - for vote in &nominaotion.nominees { + for nomination in &nominations { + for vote in &nomination.nominees { if let Some(c) = candidates.iter_mut().find(|i| i.who == vote.who) { c.score = Perquintill::from_xth(c.approval_stake.as_()); } From a33205ba8440325cc2191177979f40a71c5575c3 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Fri, 15 Mar 2019 14:33:44 +0100 Subject: [PATCH 20/54] Merge redundant loops --- srml/staking/src/lib.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/srml/staking/src/lib.rs b/srml/staking/src/lib.rs index 4b75f1f192167..38ed6cfb3da83 100644 --- a/srml/staking/src/lib.rs +++ b/srml/staking/src/lib.rs @@ -682,14 +682,6 @@ impl Module { min_validator_count ); - // Figure out the minimum stake behind a slot. - let slot_stake = elected_candidates - .iter() - .min_by_key(|c| c.exposure.total) - .map(|c| c.exposure.total) - .unwrap_or_default(); - >::put(&slot_stake); - // Clear Stakers and reduce their slash_count. for v in >::validators().iter() { >::remove(v); @@ -699,10 +691,13 @@ impl Module { } } - // Populate Stakers. + // Populate Stakers and figure out the minimum stake behind a slot. + let mut slot_stake = elected_candidates[0].exposure.total; for candidate in &elected_candidates { + if candidate.exposure.total < slot_stake { slot_stake = candidate.exposure.total; } >::insert(candidate.who.clone(), candidate.exposure.clone()); } + >::put(&slot_stake); // Set the new validator set. >::set_validators( From cfb39b92e75b2f47d231d2421cfb4e18f5cb6f2a Mon Sep 17 00:00:00 2001 From: kianenigma Date: Sat, 16 Mar 2019 10:59:54 +0100 Subject: [PATCH 21/54] Adds phantom self-vote --- srml/staking/src/lib.rs | 7 +++++-- srml/staking/src/phragmen.rs | 24 ++++++++++++++++++------ srml/staking/src/tests.rs | 2 +- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/srml/staking/src/lib.rs b/srml/staking/src/lib.rs index b9ead1f12785d..08135a4db8798 100644 --- a/srml/staking/src/lib.rs +++ b/srml/staking/src/lib.rs @@ -246,9 +246,12 @@ const MAX_UNSTAKE_THRESHOLD: u32 = 10; // Indicates the initial status of the staker #[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))] -pub enum StakerStatus { +pub enum StakerStatus { + // Chilling. Idle, + // Declared state in validating or already participating in it. Validator, + // Nominating for a group of other stakers. Nominator(Vec), } @@ -872,7 +875,7 @@ impl Module { let rounds = || >::get() as usize; let validators = || >::enumerate(); let nominators = || >::enumerate(); - let stash_of = |w| Self::stash_balance(&w); + let stash_of = |w: &T::AccountId| -> BalanceOf { Self::stash_balance(w) }; let min_validator_count = Self::minimum_validator_count() as usize; let elected_candidates = phragmen::elect::( rounds, diff --git a/srml/staking/src/phragmen.rs b/srml/staking/src/phragmen.rs index 5769192bd99cb..02380fd2c35b0 100644 --- a/srml/staking/src/phragmen.rs +++ b/srml/staking/src/phragmen.rs @@ -85,14 +85,14 @@ pub fn elect( FN: Fn() -> Box) >>, - FS: Fn(T::AccountId) -> BalanceOf, + for <'r> FS: Fn(&'r T::AccountId) -> BalanceOf, { let rounds = get_rounds(); let mut elected_candidates; // 1- Pre-process candidates and place them in a container let mut candidates = get_validators().map(|(who, _)| { - let stash_balance = stash_of(who.clone()); + let stash_balance = stash_of(&who); Candidate { who, exposure: Exposure { total: stash_balance, own: stash_balance, others: vec![] }, @@ -106,7 +106,7 @@ pub fn elect( // 2- Collect the nominators with the associated votes. // Also collect approval stake along the way. let mut nominations = get_nominators().map(|(who, nominees)| { - let nominator_stake = stash_of(who.clone()); + let nominator_stake = stash_of(&who); for n in &nominees { candidates.iter_mut().filter(|i| i.who == *n).for_each(|c| { c.approval_stake += nominator_stake; @@ -119,9 +119,19 @@ pub fn elect( .map(|n| Vote { who: n, ..Default::default() }) .collect::>>>(), stake: nominator_stake, - load : Perquintill::zero(), + load: Perquintill::zero(), } }).collect::>>>(); + + // 2.1- Add self-vote + candidates.iter().for_each(|v| { + nominations.push(Nominations { + who: v.who.clone(), + nominees: vec![Vote { who: v.who.clone(), ..Default::default() }], + stake: v.exposure.total, + load: Perquintill::zero(), + }) + }); // 3- optimization: // Candidates who have 0 stake => have no votes or all null-votes. Kick them out not. @@ -171,9 +181,10 @@ pub fn elect( // 4.1- Update backing stake of candidates and nominators for n in &mut nominations { + let nominator = n.who.clone(); for v in &mut n.nominees { // if the target of this vote is among the winners, otherwise let go. - if let Some(c) = elected_candidates.iter_mut().find(|c| c.who == v.who) { + if let Some(c) = elected_candidates.iter_mut().find(|c| c.who == v.who && c.who != nominator) { v.backing_stake = as As>::sa(n.stake.as_() * *v.load / *n.load); c.exposure.total += v.backing_stake; // Update IndividualExposure of those who nominated and their vote won @@ -189,8 +200,9 @@ pub fn elect( elected_candidates = candidates; // `Exposure.others` still needs an update for n in &mut nominations { + let nominator = n.who.clone(); for v in &mut n.nominees { - if let Some(c) = elected_candidates.iter_mut().find(|c| c.who == v.who) { + if let Some(c) = elected_candidates.iter_mut().find(|c| c.who == v.who && c.who != nominator) { c.exposure.total += n.stake; c.exposure.others.push( IndividualExposure { who: n.who.clone(), value: n.stake } diff --git a/srml/staking/src/tests.rs b/srml/staking/src/tests.rs index 1bcbe7d7e610d..bff1043839780 100644 --- a/srml/staking/src/tests.rs +++ b/srml/staking/src/tests.rs @@ -1447,7 +1447,7 @@ fn phragmen_election_works() { let rounds = || 2 as usize; let validators = || >::enumerate(); let nominators = || >::enumerate(); - let stash_of = |w| Staking::stash_balance(&w); + let stash_of = |w: &u64| -> u64 { Staking::stash_balance(w) }; let min_validator_count = Staking::minimum_validator_count() as usize; let winners = phragmen::elect::( From a4c298c466253dc9ee28c0edd72ce4bcd1feeb84 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Sun, 17 Mar 2019 16:14:57 +0100 Subject: [PATCH 22/54] Fix broken tests. --- srml/staking/src/mock.rs | 18 +- srml/staking/src/phragmen.rs | 32 +-- srml/staking/src/tests.rs | 415 +++++++++++++++++++---------------- 3 files changed, 256 insertions(+), 209 deletions(-) diff --git a/srml/staking/src/mock.rs b/srml/staking/src/mock.rs index e3dc95664c350..a639ffd5299b2 100644 --- a/srml/staking/src/mock.rs +++ b/srml/staking/src/mock.rs @@ -84,6 +84,7 @@ pub struct ExtBuilder { nominate: bool, validator_count: u32, minimum_validator_count: u32, + fare: bool, } impl Default for ExtBuilder { @@ -98,6 +99,7 @@ impl Default for ExtBuilder { nominate: true, validator_count: 2, minimum_validator_count: 0, + fare: true } } } @@ -136,6 +138,10 @@ impl ExtBuilder { self.minimum_validator_count = count; self } + pub fn fare(mut self, is_fare: bool) -> Self { + self.fare = is_fare; + self + } pub fn build(self) -> runtime_io::TestExternalities { let (mut t, mut c) = system::GenesisConfig::::default().build_storage().unwrap(); let balance_factor = if self.existential_deposit > 0 { @@ -164,9 +170,9 @@ impl ExtBuilder { (20, balance_factor), (21, balance_factor * 2000), (30, balance_factor), - (31, balance_factor * 3000), + (31, balance_factor * 2000), (40, balance_factor), - (41, balance_factor * 4000), + (41, balance_factor * 2000), (100, 2000 * balance_factor), (101, 2000 * balance_factor), ], @@ -181,16 +187,16 @@ impl ExtBuilder { stakers: if self.validator_pool { vec![ (11, 10, balance_factor * 1000, StakerStatus::::Validator), - (21, 20, balance_factor * 2000, StakerStatus::::Validator), - (31, 30, balance_factor * 3000, if self.validator_pool { StakerStatus::::Validator } else { StakerStatus::::Idle }), - (41, 40, balance_factor * 4000, if self.validator_pool { StakerStatus::::Validator } else { StakerStatus::::Idle }), + (21, 20, balance_factor * if self.fare { 1000 } else { 2000 }, StakerStatus::::Validator), + (31, 30, balance_factor * 1000, if self.validator_pool { StakerStatus::::Validator } else { StakerStatus::::Idle }), + (41, 40, balance_factor * 1000, if self.validator_pool { StakerStatus::::Validator } else { StakerStatus::::Idle }), // nominator (101, 100, balance_factor * 500, if self.nominate { StakerStatus::::Nominator(vec![10, 20]) } else { StakerStatus::::Nominator(vec![]) }) ] } else { vec![ (11, 10, balance_factor * 1000, StakerStatus::::Validator), - (21, 20, balance_factor * 2000, StakerStatus::::Validator), + (21, 20, balance_factor * if self.fare { 1000 } else { 2000 }, StakerStatus::::Validator), // nominator (101, 100, balance_factor * 500, if self.nominate { StakerStatus::::Nominator(vec![10, 20]) } else { StakerStatus::::Nominator(vec![]) }) ] diff --git a/srml/staking/src/phragmen.rs b/srml/staking/src/phragmen.rs index 02380fd2c35b0..53b383b3502f7 100644 --- a/srml/staking/src/phragmen.rs +++ b/srml/staking/src/phragmen.rs @@ -103,9 +103,21 @@ pub fn elect( // Just to be used when we are below minimum validator count let original_candidates = candidates.clone(); + // 1.1- Add phantom votes. + let mut nominations: Vec>> = Vec::with_capacity(candidates.len()); + candidates.iter_mut().for_each(|c| { + c.approval_stake += c.exposure.total; + nominations.push(Nominations { + who: c.who.clone(), + nominees: vec![ Vote { who: c.who.clone(), ..Default::default() }], + stake: c.exposure.total, + load: Perquintill::zero(), + }) + }); + // 2- Collect the nominators with the associated votes. // Also collect approval stake along the way. - let mut nominations = get_nominators().map(|(who, nominees)| { + nominations.extend(get_nominators().map(|(who, nominees)| { let nominator_stake = stash_of(&who); for n in &nominees { candidates.iter_mut().filter(|i| i.who == *n).for_each(|c| { @@ -121,18 +133,11 @@ pub fn elect( stake: nominator_stake, load: Perquintill::zero(), } - }).collect::>>>(); + })); + + println!("Candidates : {:?}", candidates); + println!("Nominations: {:?}", nominations); - // 2.1- Add self-vote - candidates.iter().for_each(|v| { - nominations.push(Nominations { - who: v.who.clone(), - nominees: vec![Vote { who: v.who.clone(), ..Default::default() }], - stake: v.exposure.total, - load: Perquintill::zero(), - }) - }); - // 3- optimization: // Candidates who have 0 stake => have no votes or all null-votes. Kick them out not. let mut candidates = candidates.into_iter().filter(|c| c.approval_stake > BalanceOf::::zero()) @@ -215,6 +220,7 @@ pub fn elect( elected_candidates = original_candidates; } } - + + println!("Elected : {:?}", elected_candidates); elected_candidates } \ No newline at end of file diff --git a/srml/staking/src/tests.rs b/srml/staking/src/tests.rs index bff1043839780..f3daa69d57779 100644 --- a/srml/staking/src/tests.rs +++ b/srml/staking/src/tests.rs @@ -39,7 +39,7 @@ fn basic_setup_works() { // Account 10 controls the stash from account 11, which is 100 * balance_factor units assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000, active: 1000, unlocking: vec![] })); // Account 20 controls the stash from account 21, which is 200 * balance_factor units - assert_eq!(Staking::ledger(&20), Some(StakingLedger { stash: 21, total: 2000, active: 2000, unlocking: vec![] })); + assert_eq!(Staking::ledger(&20), Some(StakingLedger { stash: 21, total: 1000, active: 1000, unlocking: vec![] })); // Account 1 does not control any stash assert_eq!(Staking::ledger(&1), None); @@ -53,9 +53,10 @@ fn basic_setup_works() { assert_eq!(Staking::ledger(100), Some(StakingLedger { stash: 101, total: 500, active: 500, unlocking: vec![] })); assert_eq!(Staking::nominators(100), vec![10, 20]); - // Account 10 is exposed by 100 * balance_factor from their own stash in account 11 + // Account 10 is exposed by 1000 * balance_factor from their own stash in account 11 + the default nominator vote assert_eq!(Staking::stakers(10), Exposure { total: 1500, own: 1000, others: vec![ IndividualExposure { who: 100, value: 500 }] }); - assert_eq!(Staking::stakers(20), Exposure { total: 2500, own: 2000, others: vec![ IndividualExposure { who: 100, value: 500 }] }); + // Account 20 is exposed by 1000 * balance_factor from their own stash in account 21 + the default nominator vote + assert_eq!(Staking::stakers(20), Exposure { total: 1500, own: 1000, others: vec![ IndividualExposure { who: 100, value: 500 }] }); // The number of validators required. assert_eq!(Staking::validator_count(), 2); @@ -68,7 +69,7 @@ fn basic_setup_works() { assert_eq!(Staking::current_session_reward(), 10); // initial slot_stake - assert_eq!(Staking::slot_stake(), 1500); + assert_eq!(Staking::slot_stake(), 1500); // initial slash_count of validators assert_eq!(Staking::slash_count(&10), 0); @@ -440,42 +441,35 @@ fn staking_should_work() { with_externalities(&mut ExtBuilder::default() .sessions_per_era(3) .nominate(false) + .fare(false) // to give 20 more staked value .build(), || { - assert_eq!(Staking::era_length(), 3); // remember + compare this along with the test. assert_eq!(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 { Balances::set_free_balance(&i, 1000); } - - // bond one account pair and state interest in nomination. - // this is needed to keep 10 and 20 in the validator list with phragmen - assert_ok!(Staking::bond(Origin::signed(1), 2, 500, RewardDestination::default())); - assert_ok!(Staking::nominate(Origin::signed(2), vec![20, 4])); + for i in 1..5 { Balances::set_free_balance(&i, 2000); } // --- Block 1: System::set_block_number(1); + Session::check_rotate_session(System::block_number()); + assert_eq!(Staking::current_era(), 0); // add a new candidate for being a validator. account 3 controlled by 4. - assert_ok!(Staking::bond(Origin::signed(3), 4, 1500, RewardDestination::Controller)); // balance of 3 = 3000, stashed = 1500 + assert_ok!(Staking::bond(Origin::signed(3), 4, 1500, RewardDestination::Controller)); + assert_ok!(Staking::validate(Origin::signed(4), ValidatorPrefs::default())); - Session::check_rotate_session(System::block_number()); - assert_eq!(Staking::current_era(), 0); - // No effects will be seen so far.s + // No effects will be seen so far. assert_eq!(Session::validators(), vec![20, 10]); - // --- Block 2: System::set_block_number(2); - // Explicitly state the desire to validate - // note that the controller account will state interest as representative of the stash-controller pair. - assert_ok!(Staking::validate(Origin::signed(4), ValidatorPrefs::default())); - Session::check_rotate_session(System::block_number()); assert_eq!(Staking::current_era(), 0); + // No effects will be seen so far. Era has not been yet triggered. assert_eq!(Session::validators(), vec![20, 10]); @@ -486,32 +480,26 @@ fn staking_should_work() { // 2 only voted for 4 and 20 assert_eq!(Session::validators().len(), 2); - assert_eq!(Session::validators(), vec![4, 20]); + assert_eq!(Session::validators(), vec![20, 4]); assert_eq!(Staking::current_era(), 1); // --- 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()); - // unlock the entire stashed value. - // Note that this will ne be enough to remove 4 as a validator candidate! - Staking::unbond(Origin::signed(4), Staking::ledger(&4).unwrap().active).unwrap(); - // explicit chill indicated that 4 no longer wants to be a validator. + // 4 will chill Staking::chill(Origin::signed(4)).unwrap(); - // nominator votes for 10 - assert_ok!(Staking::nominate(Origin::signed(2), vec![20, 10])); - - Session::check_rotate_session(System::block_number()); // nothing should be changed so far. - assert_eq!(Session::validators(), vec![4, 20]); + assert_eq!(Session::validators(), vec![20, 4]); assert_eq!(Staking::current_era(), 1); // --- Block 5: nothing. 4 is still there. System::set_block_number(5); Session::check_rotate_session(System::block_number()); - assert_eq!(Session::validators(), vec![4, 20]); + assert_eq!(Session::validators(), vec![20, 4]); assert_eq!(Staking::current_era(), 1); @@ -521,6 +509,12 @@ fn staking_should_work() { assert_eq!(Staking::current_era(), 2); assert_eq!(Session::validators().contains(&4), false); assert_eq!(Session::validators(), vec![20, 10]); + + // Note: the stashed value of 4 is still lock + assert_eq!(Staking::ledger(&4), Some(StakingLedger { stash: 3, total: 1500, active: 1500, unlocking: vec![] })); + // e.g. it cannot spend more than 500 that it has free from the total 2000 + assert_noop!(Balances::reserve(&3, 501), "account liquidity restrictions prevent withdrawal"); + assert_ok!(Balances::reserve(&3, 409)); }); } @@ -532,21 +526,14 @@ fn less_than_needed_candidates_works() { .minimum_validator_count(1) .validator_count(3) .nominate(false) - .validator_pool(true) .build(), || { assert_eq!(Staking::era_length(), 1); assert_eq!(Staking::validator_count(), 3); - assert_eq!(Staking::minimum_validator_count(), 1); - assert_eq!(Staking::validator_count(), 3); // initial validators - assert_eq!(Session::validators(), vec![40, 30, 20, 10]); - - // only one nominator will exist and it will - assert_ok!(Staking::bond(Origin::signed(1), 2, 500, RewardDestination::default())); - assert_ok!(Staking::nominate(Origin::signed(2), vec![10, 20])); + assert_eq!(Session::validators(), vec![20, 10]); // 10 and 20 are now valid candidates. // trigger era @@ -557,9 +544,9 @@ fn less_than_needed_candidates_works() { // both validators will be chosen again. NO election algorithm is even executed. assert_eq!(Session::validators(), vec![20, 10]); - // But the exposure is updated in a simple way. Each nominators vote is applied - assert_eq!(Staking::stakers(10).others.iter().map(|e| e.who).collect::>>(), vec![2]); - assert_eq!(Staking::stakers(20).others.iter().map(|e| e.who).collect::>>(), vec![2]); + // But the exposure is updated in a simple way. No external votes exists. This is purely self-vote. + assert_eq!(Staking::stakers(10).others.iter().map(|e| e.who).collect::>>(), vec![]); + assert_eq!(Staking::stakers(20).others.iter().map(|e| e.who).collect::>>(), vec![]); }); } @@ -568,17 +555,14 @@ fn no_candidate_emergency_condition() { // Test the situation where the number of validators are less than `ValidatorCount` and less than // The expected behavior is to choose all candidates from the previous era. with_externalities(&mut ExtBuilder::default() - .minimum_validator_count(1) - .validator_count(3) - .nominate(false) + .minimum_validator_count(10) + .validator_count(15) .validator_pool(true) + .nominate(false) .build(), || { assert_eq!(Staking::era_length(), 1); - assert_eq!(Staking::validator_count(), 3); - - assert_eq!(Staking::minimum_validator_count(), 1); - assert_eq!(Staking::validator_count(), 3); + assert_eq!(Staking::validator_count(), 15); // initial validators assert_eq!(Session::validators(), vec![40, 30, 20, 10]); @@ -600,27 +584,39 @@ fn nominating_and_rewards_should_work() { // // PHRAGMEN OUTPUT: running this test with the reference impl gives: // - // Votes [('2', 500, ['10', '20', '30']), ('4', 500, ['10', '20', '40'])] + // Votes [('10', 1000, ['10']), ('20', 1000, ['20']), ('30', 1000, ['30']), ('40', 1000, ['40']), ('2', 1000, ['10', '20', '30']), ('4', 1000, ['10', '20', '40'])] // Sequential Phragmén gives - // 10 is elected with stake 500.0 and score 0.001 - // 20 is elected with stake 500.0 and score 0.002 - // - // 2 has load 0.002 and supported - // 10 with stake 250.0 20 with stake 250.0 30 with stake 0.0 - // 4 has load 0.002 and supported - // 10 with stake 250.0 20 with stake 250.0 40 with stake 0.0 + // 10 is elected with stake 2200.0 and score 0.0003333333333333333 + // 20 is elected with stake 1800.0 and score 0.0005555555555555556 + + // 10 has load 0.0003333333333333333 and supported + // 10 with stake 1000.0 + // 20 has load 0.0005555555555555556 and supported + // 20 with stake 1000.0 + // 30 has load 0 and supported + // 30 with stake 0 + // 40 has load 0 and supported + // 40 with stake 0 + // 2 has load 0.0005555555555555556 and supported + // 10 with stake 600.0 20 with stake 400.0 30 with stake 0.0 + // 4 has load 0.0005555555555555556 and supported + // 10 with stake 600.0 20 with stake 400.0 40 with stake 0.0 + + with_externalities(&mut ExtBuilder::default() .nominate(false) .validator_pool(true) .build(), || { - // initial validators - assert_eq!(Session::validators(), vec![40, 30, 20, 10]); + // initial validators -- everyone is actually even. + assert_eq!(Session::validators(), vec![40, 30]); // Set payee to controller assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller)); assert_ok!(Staking::set_payee(Origin::signed(20), RewardDestination::Controller)); + assert_ok!(Staking::set_payee(Origin::signed(30), RewardDestination::Controller)); + assert_ok!(Staking::set_payee(Origin::signed(40), RewardDestination::Controller)); // default reward for the first session. let session_reward = 10; @@ -637,53 +633,59 @@ fn nominating_and_rewards_should_work() { // bond two account pairs and state interest in nomination. // 2 will nominate for 10, 20, 30 - assert_ok!(Staking::bond(Origin::signed(1), 2, 500, RewardDestination::Controller)); + assert_ok!(Staking::bond(Origin::signed(1), 2, 1000, RewardDestination::Controller)); assert_ok!(Staking::nominate(Origin::signed(2), vec![10, 20, 30])); // 4 will nominate for 10, 20, 40 - assert_ok!(Staking::bond(Origin::signed(3), 4, 500, RewardDestination::Stash)); + assert_ok!(Staking::bond(Origin::signed(3), 4, 1000, RewardDestination::Controller)); assert_ok!(Staking::nominate(Origin::signed(4), vec![10, 20, 40])); System::set_block_number(1); Session::check_rotate_session(System::block_number()); assert_eq!(Staking::current_era(), 1); + // 10 and 20 have more votes, they will be chosen by phragmen. assert_eq!(Session::validators(), vec![20, 10]); - // validators must have already received some rewards. - assert_eq!(Balances::total_balance(&10), initial_balance + session_reward); - assert_eq!(Balances::total_balance(&20), initial_balance + session_reward); + + // 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); // ------ check the staked value of all parties. - // total expo of 10, with 500 coming from nominators (externals), according to phragmen. + + // total expo of 10, with 1200 coming from nominators (externals), according to phragmen. assert_eq!(Staking::stakers(10).own, 1000); - assert_eq!(Staking::stakers(10).total, 1000 + 500); - // 2 and 4 supported 10, each with stake 250, according to phragmen. - assert_eq!(Staking::stakers(10).others.iter().map(|e| e.value).collect::>>(), vec![250, 250]); + assert_eq!(Staking::stakers(10).total, 1000 + 800); + // 2 and 4 supported 10, each with stake 600, according to phragmen. + assert_eq!(Staking::stakers(10).others.iter().map(|e| e.value).collect::>>(), vec![400, 400]); assert_eq!(Staking::stakers(10).others.iter().map(|e| e.who).collect::>>(), vec![4, 2]); // total expo of 20, with 500 coming from nominators (externals), according to phragmen. - assert_eq!(Staking::stakers(20).own, 2000); - assert_eq!(Staking::stakers(20).total, 2000 + 500); + assert_eq!(Staking::stakers(20).own, 1000); + assert_eq!(Staking::stakers(20).total, 1000 + 1200); // 2 and 4 supported 20, each with stake 250, according to phragmen. - assert_eq!(Staking::stakers(20).others.iter().map(|e| e.value).collect::>>(), vec![250, 250]); + assert_eq!(Staking::stakers(20).others.iter().map(|e| e.value).collect::>>(), vec![600, 600]); assert_eq!(Staking::stakers(20).others.iter().map(|e| e.who).collect::>>(), vec![4, 2]); + + // They are not chosen anymore + assert_eq!(Staking::stakers(30).total, 0); + assert_eq!(Staking::stakers(40).total, 0); System::set_block_number(2); + Session::check_rotate_session(System::block_number()); // next session reward. let new_session_reward = Staking::session_reward() * 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 - Session::check_rotate_session(System::block_number()); - // Nominator 2: has [250/1500 ~ 1/6 from 10] + [250/2500 ~ 1/10 from 20]'s reward. ==> 1/6 + 1/10 - assert_eq!(Balances::total_balance(&2), initial_balance + (new_session_reward/6 + new_session_reward/10)); - // The Associated validator will get the other 4/6 --> 1500(total) minus 1/6(250) by each nominator -> 6/6 - 1/6 - 1/6 - assert_eq!(Balances::total_balance(&10), initial_balance + session_reward + 4*new_session_reward/6) ; + // 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)); + // 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)); - // Nominator 4: has [250/1500 ~ 1/6 from 10] + [250/2500 ~ 1/10 from 20]'s reward. ==> 1/6 + 1/10 - // This nominator chose stash as the reward destination. This means that the reward will go to 3, which is bonded as the stash of 4. - assert_eq!(Balances::total_balance(&3), initial_balance + (new_session_reward/6 + new_session_reward/10)); - // The Associated validator will get the other 8/10 --> 2500(total) minus 1/10(250) by each nominator -> 10/10 - 1/10 - 1/10 - assert_eq!(Balances::total_balance(&20), initial_balance + session_reward + 8*new_session_reward/10); + // 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); }); } @@ -831,13 +833,13 @@ fn session_and_eras_work() { #[test] fn cannot_transfer_staked_balance() { // Tests that a stash account cannot transfer funds - with_externalities(&mut ExtBuilder::default().build(), || { + with_externalities(&mut ExtBuilder::default().nominate(false).build(), || { // Confirm account 11 is stashed assert_eq!(Staking::bonded(&11), Some(10)); // Confirm account 11 has some free balance assert_eq!(Balances::free_balance(&11), 1000); // Confirm account 11 (via controller 10) is totally staked - assert_eq!(Staking::stakers(&10).total, 1000 + 500); + assert_eq!(Staking::stakers(&10).total, 1000); // Confirm account 11 cannot transfer as a result assert_noop!(Balances::transfer(Origin::signed(11), 20, 1), "account liquidity restrictions prevent withdrawal"); @@ -848,6 +850,30 @@ fn cannot_transfer_staked_balance() { }); } +#[test] +fn cannot_transfer_staked_balance_2() { + // Tests that a stash account cannot transfer funds + // Same test as above but with 20 + // 21 has 2000 free balance but 1000 at stake + with_externalities(&mut ExtBuilder::default() + .nominate(false) + .fare(true) + .build(), + || { + // Confirm account 21 is stashed + assert_eq!(Staking::bonded(&21), Some(20)); + // Confirm account 21 has some free balance + assert_eq!(Balances::free_balance(&21), 2000); + // Confirm account 21 (via controller 20) is totally staked + assert_eq!(Staking::stakers(&20).total, 1000); + // Confirm account 21 cannot transfer more than 1000 + assert_noop!(Balances::transfer(Origin::signed(21), 20, 1500), "account liquidity restrictions prevent withdrawal"); + + // Confirm that account 21 can transfer less than 1000 + assert_ok!(Balances::transfer(Origin::signed(21), 20, 500)); + }); +} + #[test] fn cannot_reserve_staked_balance() { // Checks that a bonded account cannot reserve balance from free balance @@ -871,7 +897,7 @@ fn cannot_reserve_staked_balance() { #[test] fn reward_destination_works() { // Rewards go to the correct destination as determined in Payee - with_externalities(&mut ExtBuilder::default().build(), || { + with_externalities(&mut ExtBuilder::default().nominate(false).build(), || { // Check that account 10 is a validator assert!(>::exists(10)); // Check the balance of the validator account @@ -895,13 +921,11 @@ fn reward_destination_works() { // Check current session reward is 10 assert_eq!(current_session_reward, 10); // Check that reward went to the stash account of validator - // 1/3 of the reward is for the nominator. - let validator_reward = (10. * (2./3.)) as u64; // = 6 - assert_eq!(Balances::free_balance(&11), 1000 + validator_reward); + assert_eq!(Balances::free_balance(&11), 1000 + current_session_reward); // Check that amount at stake increased accordingly - assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000 + 6, active: 1000 + 6, unlocking: vec![] })); + assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000 + 10, active: 1000 + 10, unlocking: vec![] })); // Update current session reward - current_session_reward = Staking::current_session_reward(); + current_session_reward = Staking::current_session_reward(); // 1010 (1* slot_stake) //Change RewardDestination to Stash >::insert(&10, RewardDestination::Stash); @@ -914,18 +938,19 @@ fn reward_destination_works() { // Check that RewardDestination is Stash assert_eq!(Staking::payee(&10), RewardDestination::Stash); // Check that reward went to the stash account - let new_validator_reward = ((1000 + 6) as f64 / ( (1000 + 6) + (500 + 4) ) as f64) * current_session_reward as f64; - assert_eq!(Balances::free_balance(&11), 1000 + validator_reward + new_validator_reward as u64); - // Check that amount at stake is not increased - assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1006, active: 1006, unlocking: vec![] })); + assert_eq!(Balances::free_balance(&11), 1000 + 10 + current_session_reward); + // Record this value + let recorded_stash_balance = 1000 + 10 + current_session_reward; + + // Check that amount at stake is NOT increased + assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000 + 10, active: 1000 + 10, unlocking: vec![] })); - //Change RewardDestination to Controller + // Change RewardDestination to Controller >::insert(&10, RewardDestination::Controller); // Check controller balance assert_eq!(Balances::free_balance(&10), 1); - // Move forward the system for payment System::set_block_number(3); Timestamp::set_timestamp(15); @@ -934,10 +959,11 @@ fn reward_destination_works() { // Check that RewardDestination is Controller assert_eq!(Staking::payee(&10), RewardDestination::Controller); // Check that reward went to the controller account - let reward_of = |w| Staking::stakers(w).own * Staking::current_session_reward() / Staking::stakers(w).total; - assert_eq!(Balances::free_balance(&10), 1 + reward_of(&10)); - // Check that amount at stake is not increased - assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1006, active: 1006, unlocking: vec![] })); + assert_eq!(Balances::free_balance(&10), 1 + 1010); + // Check that amount at stake is NOT increased + assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000 + 10, active: 1000 + 10, unlocking: vec![] })); + // Check that amount in staked account is NOT increased. + assert_eq!(Balances::free_balance(&11), recorded_stash_balance); }); } @@ -1162,13 +1188,19 @@ fn slot_stake_is_least_staked_validator_and_limits_maximum_punishment() { // Test that slot_stake is the maximum punishment that can happen to a validator // Note that rewardDestination is the stash account by default // Note that unlike reward slash will affect free_balance, not the stash account. - with_externalities(&mut ExtBuilder::default().nominate(false).build(), || { + with_externalities(&mut ExtBuilder::default() + .nominate(false) + .fare(false) + .build(), + || { + // Give the man some money. // Confirm validator count is 2 assert_eq!(Staking::validator_count(), 2); // Confirm account 10 and 20 are validators assert!(>::exists(&10) && >::exists(&20)); // Confirm 10 has less stake than 20 assert!(Staking::stakers(&10).total < Staking::stakers(&20).total); + assert_eq!(Staking::stakers(&10).total, 1000); assert_eq!(Staking::stakers(&20).total, 2000); @@ -1337,92 +1369,78 @@ fn on_free_balance_zero_stash_removes_nominator() { fn phragmen_poc_works() { // Tests the POC test of the phragmen, mentioned in the paper and reference implementation. // Initial votes: - // vote_list = [ - // ("A", 10.0, ["X", "Y"]), - // ("B", 20.0, ["X", "Z"]), - // ("C", 30.0, ["Y", "Z"]) - // ] + // Votes [ + // ('2', 500, ['10', '20', '30']), + // ('4', 500, ['10', '20', '40']), + // ('10', 1000, ['10']), + // ('20', 1000, ['20']), + // ('30', 1000, ['30']), + // ('40', 1000, ['40'])] // // Sequential Phragmén gives - // Z is elected with stake 35.0 and score 0.02 - // Y is elected with stake 25.0 and score 0.04 - // - // A has load 0.04 and supported - // X with stake 0.0 Y with stake 10.0 - // B has load 0.02 and supported - // X with stake 0.0 Z with stake 20.0 - // C has load 0.04 and supported - // Y with stake 15.0 Z with stake 15.0 - // - // NOTE: doesn't X/Y/Z's stash value make a difference here in phragmen? + // 10 is elected with stake 1666.6666666666665 and score 0.0005 + // 20 is elected with stake 1333.3333333333333 and score 0.00075 + + // 2 has load 0.00075 and supported + // 10 with stake 333.3333333333333 20 with stake 166.66666666666666 30 with stake 0.0 + // 4 has load 0.00075 and supported + // 10 with stake 333.3333333333333 20 with stake 166.66666666666666 40 with stake 0.0 + // 10 has load 0.0005 and supported + // 10 with stake 1000.0 + // 20 has load 0.00075 and supported + // 20 with stake 1000.0 + // 30 has load 0 and supported + // 30 with stake 0 + // 40 has load 0 and supported + // 40 with stake 0 with_externalities(&mut ExtBuilder::default() .nominate(false) + .validator_pool(true) .build(), || { - // initial setup of 10 and 20, both validators. - assert_eq!(Session::validators(), vec![20, 10]); + // We don't really care about this. At this point everything is even. + // assert_eq!(Session::validators(), vec![40, 30]); assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000, active: 1000, unlocking: vec![] })); - assert_eq!(Staking::ledger(&20), Some(StakingLedger { stash: 21, total: 2000, active: 2000, unlocking: vec![] })); - - assert_eq!(Staking::validators(10), ValidatorPrefs::default()); - assert_eq!(Staking::validators(20), ValidatorPrefs::default()); - - assert_eq!(Balances::free_balance(10), 1); - assert_eq!(Balances::free_balance(20), 1); + assert_eq!(Staking::ledger(&20), Some(StakingLedger { stash: 21, total: 1000, active: 1000, unlocking: vec![] })); + assert_eq!(Staking::ledger(&30), Some(StakingLedger { stash: 31, total: 1000, active: 1000, unlocking: vec![] })); + assert_eq!(Staking::ledger(&40), Some(StakingLedger { stash: 41, total: 1000, active: 1000, unlocking: vec![] })); // no one is a nominator assert_eq!(>::enumerate().count(), 0 as usize); - // Bond [30, 31] as the third validator - assert_ok!(Staking::bond(Origin::signed(31), 30, 1000, RewardDestination::default())); - assert_ok!(Staking::validate(Origin::signed(30), ValidatorPrefs::default())); - - // bond [2,1](A), [4,3](B), [6,5](C) as the 3 nominators - // Give all of them some balance to be able to bond properly. - for i in &[1, 3, 5] { Balances::set_free_balance(i, 50); } - // Linking names to the above test: - // 10 => X - // 20 => Y - // 30 => Z - assert_ok!(Staking::bond(Origin::signed(1), 2, 10, RewardDestination::default())); - assert_ok!(Staking::nominate(Origin::signed(2), vec![10, 20])); - - assert_ok!(Staking::bond(Origin::signed(3), 4, 20, RewardDestination::default())); - assert_ok!(Staking::nominate(Origin::signed(4), vec![10, 30])); - - assert_ok!(Staking::bond(Origin::signed(5), 6, 30, RewardDestination::default())); - assert_ok!(Staking::nominate(Origin::signed(6), vec![20, 30])); + // bond [2,1] / [4,3] a nominator + Balances::set_free_balance(&1, 1000); + Balances::set_free_balance(&3, 1000); + + assert_ok!(Staking::bond(Origin::signed(1), 2, 500, RewardDestination::default())); + assert_ok!(Staking::nominate(Origin::signed(2), vec![10, 20, 30])); + + assert_ok!(Staking::bond(Origin::signed(3), 4, 500, RewardDestination::default())); + assert_ok!(Staking::nominate(Origin::signed(4), vec![10, 20, 40])); // New era => election algorithm will trigger System::set_block_number(1); Session::check_rotate_session(System::block_number()); - // Z and Y are chosen - assert_eq!(Session::validators(), vec![30, 20]); - - // with stake 35 and 25 respectively + assert_eq!(Session::validators(), vec![20, 10]); - // This is only because 30 has been bonded on the fly, exposures are stored at the very end of the era. - // 35 is the point, not 'own' Exposure. - assert_eq!(Staking::stakers(30).own, 1000); - assert_eq!(Staking::stakers(30).total, 1000 + 35); - // same as above. +25 is the point - assert_eq!(Staking::stakers(20).own, 2010); - assert_eq!(Staking::stakers(20).total, 2010 + 25); + // with stake 1666 and 1333 respectively + assert_eq!(Staking::stakers(10).own, 1000); + assert_eq!(Staking::stakers(10).total, 1000 + 332); + assert_eq!(Staking::stakers(20).own, 1000); + assert_eq!(Staking::stakers(20).total, 1000 + 666); - // 30(Z) was supported by B-4 and C-6 with stake 20 and 15 respectively. - assert_eq!(Staking::stakers(30).others.iter().map(|e| e.value).collect::>>(), vec![15, 20]); - assert_eq!(Staking::stakers(30).others.iter().map(|e| e.who).collect::>>(), vec![6, 4]); - - // 20(Y) was supported by A-2 and C-6 with stake 10 and 15 respectively. - assert_eq!(Staking::stakers(20).others.iter().map(|e| e.value).collect::>>(), vec![15, 10]); - assert_eq!(Staking::stakers(20).others.iter().map(|e| e.who).collect::>>(), vec![6, 2]); + // Nominator's stake distribution. + assert_eq!(Staking::stakers(10).others.iter().map(|e| e.value).collect::>>(), vec![166, 166]); + assert_eq!(Staking::stakers(10).others.iter().map(|e| e.who).collect::>>(), vec![4, 2]); + assert_eq!(Staking::stakers(20).others.iter().map(|e| e.value).collect::>>(), vec![333, 333]); + assert_eq!(Staking::stakers(20).others.iter().map(|e| e.who).collect::>>(), vec![4, 2]); }); } #[test] -fn phragmen_election_works() { +fn phragmen_election_works_example_2() { // tests the encapsulated phragmen::elect function. with_externalities(&mut ExtBuilder::default().nominate(false).build(), || { // initial setup of 10 and 20, both validators @@ -1437,11 +1455,11 @@ fn phragmen_election_works() { // bond [2,1](A), [4,3](B), as 2 nominators // Give all of them some balance to be able to bond properly. - for i in &[1, 3] { Balances::set_free_balance(i, 50); } - assert_ok!(Staking::bond(Origin::signed(1), 2, 5, RewardDestination::default())); + for i in &[1, 3] { Balances::set_free_balance(i, 2000); } + assert_ok!(Staking::bond(Origin::signed(1), 2, 50, RewardDestination::default())); assert_ok!(Staking::nominate(Origin::signed(2), vec![10, 20])); - assert_ok!(Staking::bond(Origin::signed(3), 4, 45, RewardDestination::default())); + assert_ok!(Staking::bond(Origin::signed(3), 4, 1000, RewardDestination::default())); assert_ok!(Staking::nominate(Origin::signed(4), vec![10, 30])); let rounds = || 2 as usize; @@ -1466,23 +1484,38 @@ fn phragmen_election_works() { // python implementation output: /* - 10 is elected with stake 26.31578947368421 and score 0.02 - 30 is elected with stake 23.684210526315788 and score 0.042222222222222223 + Votes [ + ('10', 1000, ['10']), + ('20', 1000, ['20']), + ('30', 1000, ['30']), + ('2', 50, ['10', '20']), + ('4', 1000, ['10', '30']) + ] + Sequential Phragmén gives + 10 is elected with stake 1705.7377049180327 and score 0.0004878048780487805 + 30 is elected with stake 1344.2622950819673 and score 0.0007439024390243903 + + 10 has load 0.0004878048780487805 and supported + 10 with stake 1000.0 + 20 has load 0 and supported + 20 with stake 0 + 30 has load 0.0007439024390243903 and supported + 30 with stake 1000.0 + 2 has load 0.0004878048780487805 and supported + 10 with stake 50.0 20 with stake 0.0 + 4 has load 0.0007439024390243903 and supported + 10 with stake 655.7377049180328 30 with stake 344.26229508196724 - 2 has load 0.02 and supported - 10 with stake 5.0 20 with stake 0.0 - 4 has load 0.042222222222222223 and supported - 10 with stake 21.31578947368421 30 with stake 23.684210526315788 */ - assert_eq!(winner_10.exposure.total, 1000 + 26); - assert_eq!(winner_10.score, Perquintill::from_fraction(0.02)); - assert_eq!(winner_10.exposure.others[0].value, 21); - assert_eq!(winner_10.exposure.others[1].value, 5); + assert_eq!(winner_10.exposure.total, 1000 + 705); + assert_eq!(winner_10.score, Perquintill::from_quintillionths(487804878048780)); + assert_eq!(winner_10.exposure.others[0].value, 655); + assert_eq!(winner_10.exposure.others[1].value, 50); - assert_eq!(winner_30.exposure.total, 1000 + 23); - assert_eq!(winner_30.score, Perquintill::from_quintillionths(42222222222222222)); - assert_eq!(winner_30.exposure.others[0].value, 23); + assert_eq!(winner_30.exposure.total, 1000 + 344); + assert_eq!(winner_30.score, Perquintill::from_quintillionths(743902439024390)); + assert_eq!(winner_30.exposure.others[0].value, 344); }) } @@ -1494,20 +1527,23 @@ fn switching_roles() { .sessions_per_era(3) .build(), || { + // Reset reward destination + for i in &[10, 20] { assert_ok!(Staking::set_payee(Origin::signed(*i), RewardDestination::Controller)); } + assert_eq!(Session::validators(), vec![20, 10]); // put some money in account that we'll use. for i in 1..7 { Balances::set_free_balance(&i, 5000); } // add 2 nominators - assert_ok!(Staking::bond(Origin::signed(1), 2, 2000, RewardDestination::default())); + assert_ok!(Staking::bond(Origin::signed(1), 2, 2000, RewardDestination::Controller)); assert_ok!(Staking::nominate(Origin::signed(2), vec![10, 6])); - assert_ok!(Staking::bond(Origin::signed(3), 4, 500, RewardDestination::default())); + assert_ok!(Staking::bond(Origin::signed(3), 4, 500, RewardDestination::Controller)); assert_ok!(Staking::nominate(Origin::signed(4), vec![20, 2])); // add a new validator candidate - assert_ok!(Staking::bond(Origin::signed(5), 6, 1500, RewardDestination::Controller)); + assert_ok!(Staking::bond(Origin::signed(5), 6, 1000, RewardDestination::Controller)); assert_ok!(Staking::validate(Origin::signed(6), ValidatorPrefs::default())); // new block @@ -1528,13 +1564,15 @@ fn switching_roles() { System::set_block_number(3); Session::check_rotate_session(System::block_number()); - // with current nominators 10 and 4 have the most stake + // with current nominators 10 and 5 have the most stake assert_eq!(Session::validators(), vec![6, 10]); // 2 decides to be a validator. Consequences: - // 6 will not be chosen in the next round (no votes) - // 2 itself will be chosen + 20 who now has the higher votes - // 10 wil have no votes. + // new stakes: + // 10: 1000 self vote + // 6: 1000 self vote + // 20: 1000 self vote + 500 vote + // 2: 2000 self vote + 500 vote. assert_ok!(Staking::validate(Origin::signed(2), ValidatorPrefs::default())); System::set_block_number(4); @@ -1555,14 +1593,11 @@ fn switching_roles() { #[test] fn wrong_vote_is_null() { with_externalities(&mut ExtBuilder::default() - .session_length(1) - .sessions_per_era(1) .nominate(false) .validator_pool(true) .build(), || { - // from the first era onward, only two will be chosen - assert_eq!(Session::validators(), vec![40, 30, 20, 10]); + assert_eq!(Session::validators(), vec![40, 30]); // put some money in account that we'll use. for i in 1..3 { Balances::set_free_balance(&i, 5000); } From 5eb6f2499f7dff5fc06908f9de6d1b00a9d1cec0 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Sun, 17 Mar 2019 22:01:09 +0100 Subject: [PATCH 23/54] Refactor some names to match the reference. --- srml/staking/src/phragmen.rs | 57 ++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/srml/staking/src/phragmen.rs b/srml/staking/src/phragmen.rs index 53b383b3502f7..2e54623582a2a 100644 --- a/srml/staking/src/phragmen.rs +++ b/srml/staking/src/phragmen.rs @@ -44,26 +44,25 @@ pub struct Nominations { // The nominator's account. who: AccountId, // List of validators proposed by this nominator. - nominees: Vec>, + edges: Vec>, // the stake amount proposed by the nominator as a part of the vote. - // Same as `nom.budget` in Phragmén reference. - stake: Balance, + budget: Balance, // Incremented each time a nominee that this nominator voted for has been elected. load: Perquintill, } -// Wrapper around a nominator vote and the load of that vote. -// -// Referred to as 'edge' in the Phragmén reference implementation. +// Wrapper around a nominator vote and the load of that vote. #[derive(Clone, Encode, Decode)] #[cfg_attr(feature = "std", derive(Debug, Default))] -pub struct Vote { +pub struct Edge { // Account being voted for who: AccountId, // Load of this vote. load: Perquintill, // Final backing stake of this vote. - backing_stake: Balance + backing_stake: Balance, + // Reference to the target candidate object + // candidate: &'a Candidate, } /// Perform election based on Phragmén algorithm. @@ -109,8 +108,8 @@ pub fn elect( c.approval_stake += c.exposure.total; nominations.push(Nominations { who: c.who.clone(), - nominees: vec![ Vote { who: c.who.clone(), ..Default::default() }], - stake: c.exposure.total, + edges: vec![ Edge { who: c.who.clone(), ..Default::default() }], + budget: c.exposure.total, load: Perquintill::zero(), }) }); @@ -119,18 +118,18 @@ pub fn elect( // Also collect approval stake along the way. nominations.extend(get_nominators().map(|(who, nominees)| { let nominator_stake = stash_of(&who); + let mut edges: Vec>> = Vec::with_capacity(nominees.len()); for n in &nominees { - candidates.iter_mut().filter(|i| i.who == *n).for_each(|c| { + if let Some(c) = candidates.iter_mut().find(|i| i.who == *n) { c.approval_stake += nominator_stake; - }); + edges.push(Edge { who: n.clone(), ..Default::default() }); + } } - + Nominations { who, - nominees: nominees.into_iter() - .map(|n| Vote { who: n, ..Default::default() }) - .collect::>>>(), - stake: nominator_stake, + edges: edges, + budget: nominator_stake, load: Perquintill::zero(), } })); @@ -150,17 +149,17 @@ pub fn elect( for _round in 0..rounds { // Loop 1: initialize score for nomination in &nominations { - for vote in &nomination.nominees { - if let Some(c) = candidates.iter_mut().find(|i| i.who == vote.who) { + for edge in &nomination.edges { + if let Some(c) = candidates.iter_mut().find(|i| i.who == edge.who) { c.score = Perquintill::from_xth(c.approval_stake.as_()); } } } // Loop 2: increment score. for nomination in &nominations { - for vote in &nomination.nominees { - if let Some(c) = candidates.iter_mut().find(|i| i.who == vote.who) { - let temp = nomination.stake.as_() * *nomination.load / c.approval_stake.as_(); + for edge in &nomination.edges { + if let Some(c) = candidates.iter_mut().find(|i| i.who == edge.who) { + let temp = nomination.budget.as_() * *nomination.load / c.approval_stake.as_(); c.score = Perquintill::from_quintillionths(*c.score + temp); } } @@ -173,7 +172,7 @@ pub fn elect( // loop 3: update nominator and vote load let winner = candidates.remove(winner_index); for n in &mut nominations { - for v in &mut n.nominees { + for v in &mut n.edges { if v.who == winner.who { v.load = Perquintill::from_quintillionths(*winner.score - *n.load); n.load = winner.score; @@ -187,10 +186,10 @@ pub fn elect( // 4.1- Update backing stake of candidates and nominators for n in &mut nominations { let nominator = n.who.clone(); - for v in &mut n.nominees { + for v in &mut n.edges { // if the target of this vote is among the winners, otherwise let go. if let Some(c) = elected_candidates.iter_mut().find(|c| c.who == v.who && c.who != nominator) { - v.backing_stake = as As>::sa(n.stake.as_() * *v.load / *n.load); + v.backing_stake = as As>::sa(n.budget.as_() * *v.load / *n.load); c.exposure.total += v.backing_stake; // Update IndividualExposure of those who nominated and their vote won c.exposure.others.push( @@ -206,11 +205,11 @@ pub fn elect( // `Exposure.others` still needs an update for n in &mut nominations { let nominator = n.who.clone(); - for v in &mut n.nominees { - if let Some(c) = elected_candidates.iter_mut().find(|c| c.who == v.who && c.who != nominator) { - c.exposure.total += n.stake; + for e in &mut n.edges { + if let Some(c) = elected_candidates.iter_mut().find(|c| c.who == e.who && c.who != nominator) { + c.exposure.total += n.budget; c.exposure.others.push( - IndividualExposure { who: n.who.clone(), value: n.stake } + IndividualExposure { who: n.who.clone(), value: n.budget } ); } } From c4bdb819d0552d6a102e2baec97cd6d9ffa41152 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Mon, 18 Mar 2019 10:57:16 +0100 Subject: [PATCH 24/54] Remove redundant inner loops from election round. --- srml/staking/src/phragmen.rs | 49 ++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/srml/staking/src/phragmen.rs b/srml/staking/src/phragmen.rs index 2e54623582a2a..71b0fe1294732 100644 --- a/srml/staking/src/phragmen.rs +++ b/srml/staking/src/phragmen.rs @@ -30,11 +30,13 @@ pub struct Candidate { pub who: AccountId, // Exposure struct, holding info about the value that the validator has in stake. pub exposure: Exposure, - // Accumulator of the stake of this candidate based on received votes. - approval_stake: Balance, // Intermediary value used to sort candidates. - // See Phragmén reference implementation. pub score: Perquintill, + // Accumulator of the stake of this candidate based on received votes. + approval_stake: Balance, + // Flag for being elected. + elected: bool, + } // Wrapper around the nomination info of a single nominator for a group of validators. @@ -61,8 +63,8 @@ pub struct Edge { load: Perquintill, // Final backing stake of this vote. backing_stake: Balance, - // Reference to the target candidate object - // candidate: &'a Candidate, + // Index of the candidate stored in the 'candidates' vecotr + candidate_idx: usize, } /// Perform election based on Phragmén algorithm. @@ -104,11 +106,11 @@ pub fn elect( // 1.1- Add phantom votes. let mut nominations: Vec>> = Vec::with_capacity(candidates.len()); - candidates.iter_mut().for_each(|c| { + candidates.iter_mut().enumerate().for_each(|(idx, c)| { c.approval_stake += c.exposure.total; nominations.push(Nominations { who: c.who.clone(), - edges: vec![ Edge { who: c.who.clone(), ..Default::default() }], + edges: vec![ Edge { who: c.who.clone(), candidate_idx: idx, ..Default::default() }], budget: c.exposure.total, load: Perquintill::zero(), }) @@ -120,9 +122,9 @@ pub fn elect( let nominator_stake = stash_of(&who); let mut edges: Vec>> = Vec::with_capacity(nominees.len()); for n in &nominees { - if let Some(c) = candidates.iter_mut().find(|i| i.who == *n) { - c.approval_stake += nominator_stake; - edges.push(Edge { who: n.clone(), ..Default::default() }); + if let Some(idx) = candidates.iter_mut().position(|i| i.who == *n) { + candidates[idx].approval_stake += nominator_stake; + edges.push(Edge { who: n.clone(), candidate_idx: idx, ..Default::default() }); } } @@ -150,7 +152,8 @@ pub fn elect( // Loop 1: initialize score for nomination in &nominations { for edge in &nomination.edges { - if let Some(c) = candidates.iter_mut().find(|i| i.who == edge.who) { + let c = &mut candidates[edge.candidate_idx]; + if !c.elected { c.score = Perquintill::from_xth(c.approval_stake.as_()); } } @@ -158,19 +161,23 @@ pub fn elect( // Loop 2: increment score. for nomination in &nominations { for edge in &nomination.edges { - if let Some(c) = candidates.iter_mut().find(|i| i.who == edge.who) { - let temp = nomination.budget.as_() * *nomination.load / c.approval_stake.as_(); + let c = &mut candidates[edge.candidate_idx]; + let temp = nomination.budget.as_() * *nomination.load / c.approval_stake.as_(); + if !c.elected { c.score = Perquintill::from_quintillionths(*c.score + temp); } } } // Find the best - let (winner_index, _) = candidates.iter().enumerate().min_by_key(|&(_i, c)| *c.score) + let winner = candidates + .iter_mut() + .filter(|c| !c.elected) + .min_by_key(|c| *c.score) .expect("candidates length is checked to be >0; qed"); // loop 3: update nominator and vote load - let winner = candidates.remove(winner_index); + winner.elected = true; for n in &mut nominations { for v in &mut n.edges { if v.who == winner.who { @@ -180,20 +187,20 @@ pub fn elect( } } - elected_candidates.push(winner); + elected_candidates.push(winner.clone()); } // end of all rounds // 4.1- Update backing stake of candidates and nominators for n in &mut nominations { let nominator = n.who.clone(); - for v in &mut n.edges { + for e in &mut n.edges { // if the target of this vote is among the winners, otherwise let go. - if let Some(c) = elected_candidates.iter_mut().find(|c| c.who == v.who && c.who != nominator) { - v.backing_stake = as As>::sa(n.budget.as_() * *v.load / *n.load); - c.exposure.total += v.backing_stake; + if let Some(c) = elected_candidates.iter_mut().find(|c| c.who == e.who && c.who != nominator) { + e.backing_stake = as As>::sa(n.budget.as_() * *e.load / *n.load); + c.exposure.total += e.backing_stake; // Update IndividualExposure of those who nominated and their vote won c.exposure.others.push( - IndividualExposure { who: n.who.clone(), value: v.backing_stake } + IndividualExposure { who: n.who.clone(), value: e.backing_stake } ); } } From 03d7583e0add4462ac2f765c4122287ecc11e3b0 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 19 Mar 2019 14:24:30 +0100 Subject: [PATCH 25/54] Introduce phragmen post-processing. --- srml/staking/src/lib.rs | 118 +++++++++++----------- srml/staking/src/phragmen.rs | 187 ++++++++++++++++++++++++++++------- srml/staking/src/tests.rs | 125 +++++++++++++++++------ 3 files changed, 306 insertions(+), 124 deletions(-) diff --git a/srml/staking/src/lib.rs b/srml/staking/src/lib.rs index 08135a4db8798..07bd887302bff 100644 --- a/srml/staking/src/lib.rs +++ b/srml/staking/src/lib.rs @@ -17,105 +17,105 @@ // along with Substrate. If not, see . //! # Staking Module -//! +//! //! //! 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 their duties properly. +//! normal operation but are held at pain of "slash" (expropriation) should the staked maintainer be found not to be +//! discharging their duties properly. //! You can start using the Staking module by implementing the staking [`Trait`]. -//! -//! ## Overview -//! +//! +//! ## Overview +//! //! ### Terminology //! -//! +//! //! - Staking: The process of locking up funds for some time, placing them at risk of slashing (loss) in order to become a rewarded maintainer of the network. //! - Validating: The process of running a node to actively maintain the network, either by producing blocks or guaranteeing finality of the chain. //! - Nominating: The process of placing staked funds behind one or more validators in order to share in any reward, and punishment, they take. //! - Stash account: The account holding an owner's funds used for staking. //! - Controller account: The account which controls an owner's funds for staking. //! - Era: A (whole) number of sessions, which is the period that the validator set (and each validator's active nominator set) is recalculated and where rewards are paid out. -//! - Slash: The punishment of a staker by reducing their funds ([reference](#references)). -//! +//! - Slash: The punishment of a staker by reducing their funds ([reference](#references)). +//! //! ### Goals //! -//! +//! //! The staking system in Substrate NPoS is designed to achieve three goals: //! - It should be possible to stake funds that are controlled by a cold wallet. //! - It should be possible to withdraw some, or deposit more, funds without interrupting the role of an entity. //! - It should be possible to switch between roles (nominator, validator, idle) with minimal overhead. -//! +//! //! ### Scenarios -//! -//! #### Staking -//! -//! Almost any interaction with the staking module requires at least one account to become **bonded**, also known as +//! +//! #### Staking +//! +//! Almost any interaction with the staking module requires at least one account to become **bonded**, also known as //! being a **staker**. For this, all that it is needed is a secondary _**stash account**_ which will hold the staked funds. //! Henceforth, the former account that initiated the interest is called the **controller** and the latter, holding the -//! funds, is named the **stash**. Also, note that this implies that entering the staking process requires an _account -//! pair_, one to take the role of the controller and one to be the frozen stash account (any value locked in -//! stash cannot be used, hence called _frozen_). This process in the public API is mostly referred to as _bonding_ via -//! the `bond()` function. -//! -//! Any account pair successfully placed at stake can accept three possible roles, namely: `validate`, `nominate` or +//! funds, is named the **stash**. Also, note that this implies that entering the staking process requires an _account +//! pair_, one to take the role of the controller and one to be the frozen stash account (any value locked in +//! stash cannot be used, hence called _frozen_). This process in the public API is mostly referred to as _bonding_ via +//! the `bond()` function. +//! +//! Any account pair successfully placed at stake can accept three possible roles, namely: `validate`, `nominate` or //! simply `chill`. Note that during the process of accepting these roles, the _controller_ account is always responsible -//! for declaring interest and the _stash_ account stays untouched, without directly interacting in any operation. -//! +//! for declaring interest and the _stash_ account stays untouched, without directly interacting in any operation. +//! //! #### Validating -//! -//! A **validator** takes the role of either validating blocks or ensuring their finality, maintaining the veracity of +//! +//! A **validator** takes the role of either validating blocks or ensuring their finality, maintaining the veracity of //! the network. A validator should avoid both any sort of malicious misbehavior and going offline. //! Bonded accounts that state interest in being a validator do NOT get immediately chosen as a validator. Instead, they //! are declared as a _candidate_ and they _might_ get elected at the _next **era**_ as a validator. The result of the //! election is determined by nominators and their votes. An account can become a validator via the `validate()` call. -//! -//! #### Nomination -//! +//! +//! #### Nomination +//! //! A **nominator** does not take any _direct_ role in maintaining the network, instead, it votes on a set of validators //! to be elected. Once interest in nomination is stated by an account, it takes effect _immediately_, meaning that its //! votes will be taken into account at the next election round. As mentioned above, a nominator must also place some //! funds in a stash account, essentially indicating the _weight_ of its vote. In some sense, the nominator bets on the -//! honesty of a set of validators by voting for them, with the goal of having a share of the reward granted to them. -//! Any rewards given to a validator is shared among that validator and all of the nominators that voted for it. The -//! same logic applies to the slash of a validator; if a validator misbehaves all of its nominators also get slashed. +//! honesty of a set of validators by voting for them, with the goal of having a share of the reward granted to them. +//! Any rewards given to a validator is shared among that validator and all of the nominators that voted for it. The +//! same logic applies to the slash of a validator; if a validator misbehaves all of its nominators also get slashed. //! 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 +//! because the nominators will also lose funds if they vote poorly. An account can become a nominator via the //! `nominate()` call. -//! +//! //! #### Rewards and Slash -//! -//! The **reward and slashing** procedure are the core of the staking module, attempting to _embrace valid behavior_ -//! while _punishing any misbehavior or lack of availability_. Slashing can occur at any point in time, once +//! +//! The **reward and slashing** procedure are the core of the staking module, attempting to _embrace valid behavior_ +//! while _punishing any misbehavior or lack of availability_. Slashing can occur at any point in time, once //! misbehavior is reported. One such misbehavior is a validator being detected as offline more than a certain number of -//! times. Once slashing is determined, a value is deducted from the balance of the validator and all the nominators who -//! voted for this validator. Same rules apply to the rewards in the sense of being shared among a validator and its -//! associated nominators. -//! -//! Finally, any of the roles above can choose to step back temporarily and just chill for a while. This means that if -//! they are a nominator, they will not be considered as voters anymore and if they are validators, they will no longer -//! be a candidate for the next election (again, both effects apply at the beginning of the next era). An account can +//! times. Once slashing is determined, a value is deducted from the balance of the validator and all the nominators who +//! voted for this validator. Same rules apply to the rewards in the sense of being shared among a validator and its +//! associated nominators. +//! +//! Finally, any of the roles above can choose to step back temporarily and just chill for a while. This means that if +//! they are a nominator, they will not be considered as voters anymore and if they are validators, they will no longer +//! be a candidate for the next election (again, both effects apply at the beginning of the next era). An account can //! step back via the `chill()` call. -//! +//! //! ## Interface -//! +//! //! ### Types -//! +//! //! - `Currency`: Used as the measurement means of staking and funds management. -//! -//! ### Dispatchable //! -//! The Dispatchable functions of the staking module enable the steps needed for entities to accept and change their +//! ### Dispatchable +//! +//! 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. -//! +//! //! Please refer to the [`Call`] enum and its associated variants for a detailed list of dispatchable functions. -//! -//! ### Public +//! +//! ### Public //! The staking module contains many public storage items and (im)mutable functions. Please refer to the [struct list](#structs) //! below and the [`Module`](https://crates.parity.io/srml_staking/struct.Module.html) struct definition for more details. -//! +//! //! ## Usage -//! +//! //! //! ### Snippet: Bonding and Accepting Roles //! @@ -210,9 +210,9 @@ //! - [**Balances**](https://crates.parity.io/srml_balances/index.html): Used to manage values at stake. //! - [**Sessions**](https://crates.parity.io/srml_session/index.html): Used to manage sessions. Also, a list of new validators is also stored in the sessions module's `Validators` at the end of each era. //! - [**System**](https://crates.parity.io/srml_system/index.html): Used to obtain block number and time, among other details. -//! +//! //! # References -//! +//! //! 1. This document is written as a more verbose version of the original [Staking.md](../Staking.md) file. Some sections, are taken directly from the aforementioned document. @@ -897,7 +897,9 @@ impl Module { // Populate Stakers and figure out the minimum stake behind a slot. let mut slot_stake = elected_candidates[0].exposure.total; for candidate in &elected_candidates { - if candidate.exposure.total < slot_stake { slot_stake = candidate.exposure.total; } + if candidate.exposure.total < slot_stake { + slot_stake = candidate.exposure.total; + } >::insert(candidate.who.clone(), candidate.exposure.clone()); } >::put(&slot_stake); @@ -906,7 +908,7 @@ impl Module { >::set_validators( &elected_candidates.into_iter().map(|i| i.who).collect::>() ); - + slot_stake } diff --git a/srml/staking/src/phragmen.rs b/srml/staking/src/phragmen.rs index 71b0fe1294732..4857a3be6473d 100644 --- a/srml/staking/src/phragmen.rs +++ b/srml/staking/src/phragmen.rs @@ -18,7 +18,7 @@ use rstd::{prelude::*}; use primitives::Perquintill; -use primitives::traits::{Zero, As}; +use primitives::traits::{Zero, One, As, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub}; use parity_codec::{HasCompact, Encode, Decode}; use crate::{Exposure, BalanceOf, Trait, ValidatorPrefs, IndividualExposure}; @@ -36,13 +36,12 @@ pub struct Candidate { approval_stake: Balance, // Flag for being elected. elected: bool, - } // Wrapper around the nomination info of a single nominator for a group of validators. #[derive(Clone, Encode, Decode)] #[cfg_attr(feature = "std", derive(Debug))] -pub struct Nominations { +pub struct Nominator { // The nominator's account. who: AccountId, // List of validators proposed by this nominator. @@ -53,7 +52,7 @@ pub struct Nominations { load: Perquintill, } -// Wrapper around a nominator vote and the load of that vote. +// Wrapper around a nominator vote and the load of that vote. #[derive(Clone, Encode, Decode)] #[cfg_attr(feature = "std", derive(Debug, Default))] pub struct Edge { @@ -63,8 +62,12 @@ pub struct Edge { load: Perquintill, // Final backing stake of this vote. backing_stake: Balance, - // Index of the candidate stored in the 'candidates' vecotr + // Index of the candidate stored in the 'candidates' vector candidate_idx: usize, + // Index of the candidate stored in the 'elected_candidates' vector. Used only with equalise. + elected_idx: usize, + // Indicates if this edge is a vote for an elected candidate. Used only with equalise. + elected: bool, } /// Perform election based on Phragmén algorithm. @@ -90,7 +93,7 @@ pub fn elect( { let rounds = get_rounds(); let mut elected_candidates; - + // 1- Pre-process candidates and place them in a container let mut candidates = get_validators().map(|(who, _)| { let stash_balance = stash_of(&who); @@ -103,12 +106,12 @@ pub fn elect( // Just to be used when we are below minimum validator count let original_candidates = candidates.clone(); - + // 1.1- Add phantom votes. - let mut nominations: Vec>> = Vec::with_capacity(candidates.len()); + let mut nominators: Vec>> = Vec::with_capacity(candidates.len()); candidates.iter_mut().enumerate().for_each(|(idx, c)| { c.approval_stake += c.exposure.total; - nominations.push(Nominations { + nominators.push(Nominator { who: c.who.clone(), edges: vec![ Edge { who: c.who.clone(), candidate_idx: idx, ..Default::default() }], budget: c.exposure.total, @@ -118,7 +121,7 @@ pub fn elect( // 2- Collect the nominators with the associated votes. // Also collect approval stake along the way. - nominations.extend(get_nominators().map(|(who, nominees)| { + nominators.extend(get_nominators().map(|(who, nominees)| { let nominator_stake = stash_of(&who); let mut edges: Vec>> = Vec::with_capacity(nominees.len()); for n in &nominees { @@ -127,18 +130,15 @@ pub fn elect( edges.push(Edge { who: n.clone(), candidate_idx: idx, ..Default::default() }); } } - - Nominations { + + Nominator { who, - edges: edges, + edges: edges, budget: nominator_stake, load: Perquintill::zero(), } })); - println!("Candidates : {:?}", candidates); - println!("Nominations: {:?}", nominations); - // 3- optimization: // Candidates who have 0 stake => have no votes or all null-votes. Kick them out not. let mut candidates = candidates.into_iter().filter(|c| c.approval_stake > BalanceOf::::zero()) @@ -150,19 +150,19 @@ pub fn elect( // Main election loop for _round in 0..rounds { // Loop 1: initialize score - for nomination in &nominations { - for edge in &nomination.edges { - let c = &mut candidates[edge.candidate_idx]; + for n in &nominators { + for e in &n.edges { + let c = &mut candidates[e.candidate_idx]; if !c.elected { c.score = Perquintill::from_xth(c.approval_stake.as_()); } } } // Loop 2: increment score. - for nomination in &nominations { - for edge in &nomination.edges { - let c = &mut candidates[edge.candidate_idx]; - let temp = nomination.budget.as_() * *nomination.load / c.approval_stake.as_(); + for n in &nominators { + for e in &n.edges { + let c = &mut candidates[e.candidate_idx]; + let temp = n.budget.as_() * *n.load / c.approval_stake.as_(); if !c.elected { c.score = Perquintill::from_quintillionths(*c.score + temp); } @@ -171,14 +171,14 @@ pub fn elect( // Find the best let winner = candidates - .iter_mut() + .iter_mut() .filter(|c| !c.elected) .min_by_key(|c| *c.score) .expect("candidates length is checked to be >0; qed"); // loop 3: update nominator and vote load winner.elected = true; - for n in &mut nominations { + for n in &mut nominators { for v in &mut n.edges { if v.who == winner.who { v.load = Perquintill::from_quintillionths(*winner.score - *n.load); @@ -191,26 +191,51 @@ pub fn elect( } // end of all rounds // 4.1- Update backing stake of candidates and nominators - for n in &mut nominations { - let nominator = n.who.clone(); + for n in &mut nominators { for e in &mut n.edges { // if the target of this vote is among the winners, otherwise let go. - if let Some(c) = elected_candidates.iter_mut().find(|c| c.who == e.who && c.who != nominator) { + if let Some(c) = elected_candidates.iter_mut().find(|c| c.who == e.who) { + e.elected = true; e.backing_stake = as As>::sa(n.budget.as_() * *e.load / *n.load); - c.exposure.total += e.backing_stake; - // Update IndividualExposure of those who nominated and their vote won - c.exposure.others.push( - IndividualExposure { who: n.who.clone(), value: e.backing_stake } - ); + if c.who != n.who { + c.exposure.total += e.backing_stake; + // Update IndividualExposure of those who nominated and their vote won + c.exposure.others.push( + IndividualExposure { who: n.who.clone(), value: e.backing_stake } + ); + } } } } + + // equalise? + let do_equalise = true; + if do_equalise { + let tolerance = >::sa(10); + let equalise_iterations = 2; + for _ in 0..equalise_iterations { + let mut max_diff = >::zero(); + nominators.iter().for_each(|n| { + // TODO: equalise should accept a reference to a nominator maybe? + let diff = equalise::(n.clone(), &mut elected_candidates, tolerance); + if diff > max_diff { + max_diff = diff; + } + }); + if max_diff < tolerance { + break; + } + } + } + } else { if candidates.len() > minimum_validator_count { // if we don't have enough candidates, just choose all that have some vote. elected_candidates = candidates; + // TODO: I still don't really trust this. + // Exposure should either be updated in both cases or neither. // `Exposure.others` still needs an update - for n in &mut nominations { + for n in &mut nominators { let nominator = n.who.clone(); for e in &mut n.edges { if let Some(c) = elected_candidates.iter_mut().find(|c| c.who == e.who && c.who != nominator) { @@ -226,7 +251,97 @@ pub fn elect( elected_candidates = original_candidates; } } - - println!("Elected : {:?}", elected_candidates); elected_candidates +} + +pub fn equalise( + mut nominator: Nominator>, + elected_candidates: &mut Vec>>, + tolerance: BalanceOf +) -> BalanceOf { + // TODO: Might be more efficinet to do this once? maybe... + // update indexes. + nominator.edges.iter_mut().for_each(|e| { + if let Some(idx) = elected_candidates.iter().position(|c| c.who == e.who) { + e.elected_idx = idx; + } + }); + + // TODO: double check the direction of the sort. + nominator.edges.sort_unstable_by_key(|e| elected_candidates[e.elected_idx].exposure.total); + + // TODO: clone and into is not really optimal. + let mut elected_edges = nominator.edges.clone() + .into_iter() + .filter(|e| e.elected) + .collect::>>>(); + if elected_edges.len() == 0 { return >::zero(); } + let stake_used = elected_edges + .iter() + .fold(>::zero(), |s, e| s + e.backing_stake); + let backed_stakes = elected_edges + .iter() + .map(|e| elected_candidates[e.elected_idx].exposure.total) + .collect::>>(); + let backing_backed_stake = elected_edges + .iter() + .filter(|e| e.backing_stake > >::zero()) + .map(|e| elected_candidates[e.elected_idx].exposure.total) + .collect::>>(); + + let mut difference; + if backing_backed_stake.len() > 0 { + let max_stake = backing_backed_stake + .iter() + .max() + .expect("vector with positive length will have a max; qed") + .to_owned(); + let min_stake = backed_stakes + .iter() + .min() + .expect("vector with positive length will have a max; qed") + .to_owned(); + difference = max_stake - min_stake; + difference += nominator.budget - stake_used; + if difference < tolerance { + return difference; + } + } else { + difference = nominator.budget; + } + + // Undo updates to exposure + elected_edges.iter_mut().for_each(|e| { + assert_eq!(elected_candidates[e.elected_idx].who, e.who); + elected_candidates[e.elected_idx].exposure.total -= e.backing_stake; + e.backing_stake = >::zero(); + }); + + let mut cumulative_stake = >::zero(); + let mut last_index = elected_edges.len() - 1; + + elected_edges.iter_mut().enumerate().for_each(|(idx, e)| { + let stake = elected_candidates[e.elected_idx].exposure.total; + + let stake_mul = stake.checked_mul(&>::sa(idx as u64)).unwrap_or(>::one()); + let stake_sub = stake_mul.checked_sub(&cumulative_stake).unwrap_or_default(); + if stake_sub > nominator.budget { + last_index = idx.checked_sub(1).unwrap_or(0); + return + } + cumulative_stake += stake; + }); + + let last_stake = elected_candidates[elected_edges[last_index].elected_idx].exposure.total; + let split_ways = last_index + 1; + let excess = nominator.budget + cumulative_stake - last_stake * >::sa(split_ways as u64); + elected_edges.iter_mut().take(split_ways).for_each(|e| { + let c = &mut elected_candidates[e.elected_idx]; + e.backing_stake = excess / >::sa(split_ways as u64) + last_stake -c.exposure.total; + c.exposure.total += e.backing_stake; + if let Some(i_expo) = c.exposure.others.iter_mut().find(|i| i.who == nominator.who) { + i_expo.value = e.backing_stake; + } + }); + difference } \ No newline at end of file diff --git a/srml/staking/src/tests.rs b/srml/staking/src/tests.rs index f3daa69d57779..d99fdac8e8dff 100644 --- a/srml/staking/src/tests.rs +++ b/srml/staking/src/tests.rs @@ -602,7 +602,22 @@ fn nominating_and_rewards_should_work() { // 4 has load 0.0005555555555555556 and supported // 10 with stake 600.0 20 with stake 400.0 40 with stake 0.0 + // Sequential Phragmén with post processing gives + // 10 is elected with stake 2000.0 and score 0.0003333333333333333 + // 20 is elected with stake 2000.0 and score 0.0005555555555555556 + // 10 has load 0.0003333333333333333 and supported + // 10 with stake 1000.0 + // 20 has load 0.0005555555555555556 and supported + // 20 with stake 1000.0 + // 30 has load 0 and supported + // 30 with stake 0 + // 40 has load 0 and supported + // 40 with stake 0 + // 2 has load 0.0005555555555555556 and supported + // 10 with stake 400.0 20 with stake 600.0 30 with stake 0 + // 4 has load 0.0005555555555555556 and supported + // 10 with stake 600.0 20 with stake 400.0 40 with stake 0.0 with_externalities(&mut ExtBuilder::default() .nominate(false) @@ -638,11 +653,11 @@ fn nominating_and_rewards_should_work() { // 4 will nominate for 10, 20, 40 assert_ok!(Staking::bond(Origin::signed(3), 4, 1000, RewardDestination::Controller)); assert_ok!(Staking::nominate(Origin::signed(4), vec![10, 20, 40])); - + System::set_block_number(1); Session::check_rotate_session(System::block_number()); assert_eq!(Staking::current_era(), 1); - + // 10 and 20 have more votes, they will be chosen by phragmen. assert_eq!(Session::validators(), vec![20, 10]); @@ -654,21 +669,21 @@ fn nominating_and_rewards_should_work() { // total expo of 10, with 1200 coming from nominators (externals), according to phragmen. assert_eq!(Staking::stakers(10).own, 1000); - assert_eq!(Staking::stakers(10).total, 1000 + 800); + assert_eq!(Staking::stakers(10).total, 1000 + 1000); // 2 and 4 supported 10, each with stake 600, according to phragmen. - assert_eq!(Staking::stakers(10).others.iter().map(|e| e.value).collect::>>(), vec![400, 400]); + assert_eq!(Staking::stakers(10).others.iter().map(|e| e.value).collect::>>(), vec![600, 400]); assert_eq!(Staking::stakers(10).others.iter().map(|e| e.who).collect::>>(), vec![4, 2]); // total expo of 20, with 500 coming from nominators (externals), according to phragmen. assert_eq!(Staking::stakers(20).own, 1000); - assert_eq!(Staking::stakers(20).total, 1000 + 1200); + assert_eq!(Staking::stakers(20).total, 1000 + 1000); // 2 and 4 supported 20, each with stake 250, according to phragmen. - assert_eq!(Staking::stakers(20).others.iter().map(|e| e.value).collect::>>(), vec![600, 600]); + assert_eq!(Staking::stakers(20).others.iter().map(|e| e.value).collect::>>(), vec![400, 600]); assert_eq!(Staking::stakers(20).others.iter().map(|e| e.who).collect::>>(), vec![4, 2]); // They are not chosen anymore assert_eq!(Staking::stakers(30).total, 0); assert_eq!(Staking::stakers(40).total, 0); - + System::set_block_number(2); Session::check_rotate_session(System::block_number()); @@ -677,15 +692,15 @@ fn nominating_and_rewards_should_work() { // 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)); - // 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)); + // Nominator 2: has [400/2000 ~ 1/5 from 10] + [600/2000 ~ 3/10 from 20]'s reward. + assert_eq!(Balances::total_balance(&2), initial_balance + (new_session_reward/5 + 3*new_session_reward/10)); + // Nominator 4: has [600/2000 ~ 3/10 from 10] + [400/2000 ~ 1/5 from 20]'s reward. + assert_eq!(Balances::total_balance(&4), initial_balance + (new_session_reward/5 + 3*new_session_reward/10)); - // 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); + // 10 got 1000/2000 external stake => Validator's share = 1/2 + assert_eq!(Balances::total_balance(&10), initial_balance + new_session_reward/2); + // 20 got 1000/2000 external stake => Validator's share = 1/2 + assert_eq!(Balances::total_balance(&20), initial_balance + new_session_reward/2); }); } @@ -1021,7 +1036,7 @@ fn validator_payment_prefs_work() { // session triggered: the reward value stashed should be 10 -- defined in ExtBuilder genesis. 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); // a little late. @@ -1054,7 +1069,7 @@ fn validator_payment_prefs_work() { #[test] fn bond_extra_works() { // Tests that extra `free_balance` in the stash can be added to stake - // NOTE: this tests only verifies `StakingLedger` for correct updates. + // NOTE: this tests only verifies `StakingLedger` for correct updates // See `bond_extra_and_withdraw_unbonded_works` for more details and updates on `Exposure`. with_externalities(&mut ExtBuilder::default().build(), || { @@ -1118,7 +1133,7 @@ fn bond_extra_and_withdraw_unbonded_works() { // confirm that 10 is a normal validator and gets paid at the end of the era. System::set_block_number(1); Timestamp::set_timestamp(5); - Session::check_rotate_session(System::block_number()); + Session::check_rotate_session(System::block_number()); assert_eq!(Staking::current_era(), 1); assert_eq!(Session::current_index(), 1); @@ -1131,7 +1146,8 @@ fn bond_extra_and_withdraw_unbonded_works() { assert_eq!(Staking::stakers(&10), Exposure { total: 1000, own: 1000, others: vec![] }); - // deposit the extra 100 units + + // deposit the extra 100 units Staking::bond_extra(Origin::signed(10), 100).unwrap(); assert_eq!(Staking::ledger(&10), Some(StakingLedger { stash: 11, total: 1000 + 100, active: 1000 + 100, unlocking: vec![] })); @@ -1393,6 +1409,25 @@ fn phragmen_poc_works() { // 30 with stake 0 // 40 has load 0 and supported // 40 with stake 0 + + // Sequential Phragmén with post processing gives + // 10 is elected with stake 1500.0 and score 0.0005 + // 20 is elected with stake 1500.0 and score 0.00075 + // + // 10 has load 0.0005 and supported + // 10 with stake 1000.0 + // 20 has load 0.00075 and supported + // 20 with stake 1000.0 + // 30 has load 0 and supported + // 30 with stake 0 + // 40 has load 0 and supported + // 40 with stake 0 + // 2 has load 0.00075 and supported + // 10 with stake 166.66666666666674 20 with stake 333.33333333333326 30 with stake 0 + // 4 has load 0.00075 and supported + // 10 with stake 333.3333333333333 20 with stake 166.66666666666666 40 with stake 0.0 + + with_externalities(&mut ExtBuilder::default() .nominate(false) .validator_pool(true) @@ -1405,14 +1440,19 @@ fn phragmen_poc_works() { assert_eq!(Staking::ledger(&20), Some(StakingLedger { stash: 21, total: 1000, active: 1000, unlocking: vec![] })); assert_eq!(Staking::ledger(&30), Some(StakingLedger { stash: 31, total: 1000, active: 1000, unlocking: vec![] })); assert_eq!(Staking::ledger(&40), Some(StakingLedger { stash: 41, total: 1000, active: 1000, unlocking: vec![] })); - + + assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller)); + assert_ok!(Staking::set_payee(Origin::signed(20), RewardDestination::Controller)); + assert_ok!(Staking::set_payee(Origin::signed(30), RewardDestination::Controller)); + assert_ok!(Staking::set_payee(Origin::signed(40), RewardDestination::Controller)); + // no one is a nominator assert_eq!(>::enumerate().count(), 0 as usize); // bond [2,1] / [4,3] a nominator Balances::set_free_balance(&1, 1000); Balances::set_free_balance(&3, 1000); - + assert_ok!(Staking::bond(Origin::signed(1), 2, 500, RewardDestination::default())); assert_ok!(Staking::nominate(Origin::signed(2), vec![10, 20, 30])); @@ -1426,15 +1466,18 @@ fn phragmen_poc_works() { assert_eq!(Session::validators(), vec![20, 10]); // with stake 1666 and 1333 respectively - assert_eq!(Staking::stakers(10).own, 1000); - assert_eq!(Staking::stakers(10).total, 1000 + 332); + assert_eq!(Staking::stakers(10).own, 1000); + assert_eq!(Staking::stakers(10).total, 1000 + 499); assert_eq!(Staking::stakers(20).own, 1000); - assert_eq!(Staking::stakers(20).total, 1000 + 666); + assert_eq!(Staking::stakers(20).total, 1000 + 499); // Nominator's stake distribution. - assert_eq!(Staking::stakers(10).others.iter().map(|e| e.value).collect::>>(), vec![166, 166]); + assert_eq!(Staking::stakers(10).others.iter().map(|e| e.value).collect::>>(), vec![333, 166]); + assert_eq!(Staking::stakers(10).others.iter().map(|e| e.value).sum::>(), 499); assert_eq!(Staking::stakers(10).others.iter().map(|e| e.who).collect::>>(), vec![4, 2]); - assert_eq!(Staking::stakers(20).others.iter().map(|e| e.value).collect::>>(), vec![333, 333]); + + assert_eq!(Staking::stakers(20).others.iter().map(|e| e.value).collect::>>(), vec![166, 333]); + assert_eq!(Staking::stakers(20).others.iter().map(|e| e.value).sum::>(), 499); assert_eq!(Staking::stakers(20).others.iter().map(|e| e.who).collect::>>(), vec![4, 2]); }); } @@ -1506,16 +1549,32 @@ fn phragmen_election_works_example_2() { 4 has load 0.0007439024390243903 and supported 10 with stake 655.7377049180328 30 with stake 344.26229508196724 + Sequential Phragmén with post processing gives + 10 is elected with stake 1525.0 and score 0.0004878048780487805 + 30 is elected with stake 1525.0 and score 0.0007439024390243903 + + 10 has load 0.0004878048780487805 and supported + 10 with stake 1000.0 + 20 has load 0 and supported + 20 with stake 0 + 30 has load 0.0007439024390243903 and supported + 30 with stake 1000.0 + 2 has load 0.0004878048780487805 and supported + 10 with stake 50.0 20 with stake 0.0 + 4 has load 0.0007439024390243903 and supported + 10 with stake 475.0 30 with stake 525.0 + + */ - assert_eq!(winner_10.exposure.total, 1000 + 705); + assert_eq!(winner_10.exposure.total, 1000 + 525); assert_eq!(winner_10.score, Perquintill::from_quintillionths(487804878048780)); - assert_eq!(winner_10.exposure.others[0].value, 655); + assert_eq!(winner_10.exposure.others[0].value, 475); assert_eq!(winner_10.exposure.others[1].value, 50); - assert_eq!(winner_30.exposure.total, 1000 + 344); + assert_eq!(winner_30.exposure.total, 1000 + 525); assert_eq!(winner_30.score, Perquintill::from_quintillionths(743902439024390)); - assert_eq!(winner_30.exposure.others[0].value, 344); + assert_eq!(winner_30.exposure.others[0].value, 525); }) } @@ -1616,3 +1675,9 @@ fn wrong_vote_is_null() { assert_eq!(Session::validators(), vec![20, 10]); }); } + +#[test] +fn bond_with_no_staked_value() { + // Behavior when someone bonds with no staked value. + // Particularly when she votes and the candidate is elected. +} From bf8212f7d029896b47860625c699cca309a21869 Mon Sep 17 00:00:00 2001 From: kianenigma Date: Tue, 19 Mar 2019 18:12:58 +0100 Subject: [PATCH 26/54] Some fixes and todos. --- srml/staking/src/lib.rs | 13 +++++-- srml/staking/src/phragmen.rs | 69 ++++++++++++++++++++++-------------- srml/staking/src/tests.rs | 7 +++- 3 files changed, 58 insertions(+), 31 deletions(-) diff --git a/srml/staking/src/lib.rs b/srml/staking/src/lib.rs index 07bd887302bff..f2e2682d150a9 100644 --- a/srml/staking/src/lib.rs +++ b/srml/staking/src/lib.rs @@ -239,6 +239,8 @@ mod mock; mod tests; mod phragmen; +use phragmen::{elect, ElectionConfig}; + const RECENT_OFFLINE_COUNT: usize = 32; const DEFAULT_MINIMUM_VALIDATOR_COUNT: u32 = 4; const MAX_NOMINATIONS: usize = 16; @@ -871,18 +873,23 @@ impl Module { /// Returns the new SlotStake value. fn select_validators() -> BalanceOf { // Map of (would-be) validator account to amount of stake backing it. - + let rounds = || >::get() as usize; let validators = || >::enumerate(); let nominators = || >::enumerate(); let stash_of = |w: &T::AccountId| -> BalanceOf { Self::stash_balance(w) }; let min_validator_count = Self::minimum_validator_count() as usize; - let elected_candidates = phragmen::elect::( + let elected_candidates = elect::( rounds, validators, nominators, stash_of, - min_validator_count + min_validator_count, + ElectionConfig::> { + equalise: true, + tolerance: >::sa(10 as u64), + iterations: 10, + } ); // Clear Stakers and reduce their slash_count. diff --git a/srml/staking/src/phragmen.rs b/srml/staking/src/phragmen.rs index 4857a3be6473d..a50a01251b941 100644 --- a/srml/staking/src/phragmen.rs +++ b/srml/staking/src/phragmen.rs @@ -22,6 +22,18 @@ use primitives::traits::{Zero, One, As, CheckedAdd, CheckedDiv, CheckedMul, Chec use parity_codec::{HasCompact, Encode, Decode}; use crate::{Exposure, BalanceOf, Trait, ValidatorPrefs, IndividualExposure}; + +// Configure the behavior of the Phragmen election. +// Might be deprecated. +pub struct ElectionConfig { + // Perform equalise?. + pub equalise: bool, + // Number of equalise iterations. + pub iterations: usize, + // Tolerance of max change per equalise iteration. + pub tolerance: Balance, +} + // Wrapper around validation candidates some metadata. #[derive(Clone, Encode, Decode)] #[cfg_attr(feature = "std", derive(Debug, Default))] @@ -81,6 +93,7 @@ pub fn elect( get_nominators: FN, stash_of: FS, minimum_validator_count: usize, + config: ElectionConfig>, ) -> Vec>> where FR: Fn() -> usize, FV: Fn() -> Box( } } - // equalise? - let do_equalise = true; - if do_equalise { - let tolerance = >::sa(10); - let equalise_iterations = 2; + // Optionally perform equalise post-processing. + if config.equalise { + let tolerance = config.tolerance; + let equalise_iterations = config.iterations; + + // Fix indexes + nominators.iter_mut().for_each(|n| { + n.edges.iter_mut().for_each(|e| { + if let Some(idx) = elected_candidates.iter().position(|c| c.who == e.who) { + e.elected_idx = idx; + } + }); + }); + for _ in 0..equalise_iterations { let mut max_diff = >::zero(); - nominators.iter().for_each(|n| { - // TODO: equalise should accept a reference to a nominator maybe? - let diff = equalise::(n.clone(), &mut elected_candidates, tolerance); + nominators.iter_mut().for_each(|mut n| { + let diff = equalise::(&mut n, &mut elected_candidates, tolerance); if diff > max_diff { max_diff = diff; } @@ -255,26 +276,15 @@ pub fn elect( } pub fn equalise( - mut nominator: Nominator>, + nominator: &mut Nominator>, elected_candidates: &mut Vec>>, tolerance: BalanceOf ) -> BalanceOf { - // TODO: Might be more efficinet to do this once? maybe... - // update indexes. - nominator.edges.iter_mut().for_each(|e| { - if let Some(idx) = elected_candidates.iter().position(|c| c.who == e.who) { - e.elected_idx = idx; - } - }); - - // TODO: double check the direction of the sort. - nominator.edges.sort_unstable_by_key(|e| elected_candidates[e.elected_idx].exposure.total); - // TODO: clone and into is not really optimal. - let mut elected_edges = nominator.edges.clone() - .into_iter() + let mut elected_edges = nominator.edges + .iter_mut() .filter(|e| e.elected) - .collect::>>>(); + .collect::>>>(); if elected_edges.len() == 0 { return >::zero(); } let stake_used = elected_edges .iter() @@ -317,31 +327,36 @@ pub fn equalise( e.backing_stake = >::zero(); }); + elected_edges.sort_unstable_by_key(|e| elected_candidates[e.elected_idx].exposure.total); + let mut cumulative_stake = >::zero(); let mut last_index = elected_edges.len() - 1; - + let budget = nominator.budget; elected_edges.iter_mut().enumerate().for_each(|(idx, e)| { let stake = elected_candidates[e.elected_idx].exposure.total; let stake_mul = stake.checked_mul(&>::sa(idx as u64)).unwrap_or(>::one()); let stake_sub = stake_mul.checked_sub(&cumulative_stake).unwrap_or_default(); - if stake_sub > nominator.budget { - last_index = idx.checked_sub(1).unwrap_or(0); + if stake_sub > budget { + last_index = idx.clone().checked_sub(1).unwrap_or(0); return } cumulative_stake += stake; }); + // TODO: safe arithmatic here. let last_stake = elected_candidates[elected_edges[last_index].elected_idx].exposure.total; let split_ways = last_index + 1; let excess = nominator.budget + cumulative_stake - last_stake * >::sa(split_ways as u64); + let nominator_address = nominator.who.clone(); elected_edges.iter_mut().take(split_ways).for_each(|e| { let c = &mut elected_candidates[e.elected_idx]; e.backing_stake = excess / >::sa(split_ways as u64) + last_stake -c.exposure.total; c.exposure.total += e.backing_stake; - if let Some(i_expo) = c.exposure.others.iter_mut().find(|i| i.who == nominator.who) { + if let Some(i_expo) = c.exposure.others.iter_mut().find(|i| i.who == nominator_address) { i_expo.value = e.backing_stake; } }); + difference } \ No newline at end of file diff --git a/srml/staking/src/tests.rs b/srml/staking/src/tests.rs index d99fdac8e8dff..40532611773b6 100644 --- a/srml/staking/src/tests.rs +++ b/srml/staking/src/tests.rs @@ -1516,7 +1516,12 @@ fn phragmen_election_works_example_2() { validators, nominators, stash_of, - min_validator_count + min_validator_count, + ElectionConfig::> { + equalise: true, + tolerance: >::sa(10 as u64), + iterations: 10, + } ); // 10 and 30 must be the winners From 85f12946bb37d208baa7d3f849b155505df35c0f Mon Sep 17 00:00:00 2001 From: kianenigma Date: Thu, 21 Mar 2019 10:18:58 +0100 Subject: [PATCH 27/54] Fix some tests with new phragmen params --- srml/staking/src/lib.rs | 8 +- srml/staking/src/phragmen.rs | 53 ++++----- srml/staking/src/tests.rs | 214 ++++++++++++++++++++++++++++++++--- 3 files changed, 228 insertions(+), 47 deletions(-) diff --git a/srml/staking/src/lib.rs b/srml/staking/src/lib.rs index f2e2682d150a9..e243b58f7895f 100644 --- a/srml/staking/src/lib.rs +++ b/srml/staking/src/lib.rs @@ -903,11 +903,11 @@ impl Module { // Populate Stakers and figure out the minimum stake behind a slot. let mut slot_stake = elected_candidates[0].exposure.total; - for candidate in &elected_candidates { - if candidate.exposure.total < slot_stake { - slot_stake = candidate.exposure.total; + for c in &elected_candidates { + if c.exposure.total < slot_stake { + slot_stake = c.exposure.total; } - >::insert(candidate.who.clone(), candidate.exposure.clone()); + >::insert(c.who.clone(), c.exposure.clone()); } >::put(&slot_stake); diff --git a/srml/staking/src/phragmen.rs b/srml/staking/src/phragmen.rs index a50a01251b941..48d019e48c071 100644 --- a/srml/staking/src/phragmen.rs +++ b/srml/staking/src/phragmen.rs @@ -18,7 +18,7 @@ use rstd::{prelude::*}; use primitives::Perquintill; -use primitives::traits::{Zero, One, As, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub}; +use primitives::traits::{Zero, As, Bounded, CheckedMul, CheckedSub}; use parity_codec::{HasCompact, Encode, Decode}; use crate::{Exposure, BalanceOf, Trait, ValidatorPrefs, IndividualExposure}; @@ -48,6 +48,8 @@ pub struct Candidate { approval_stake: Balance, // Flag for being elected. elected: bool, + // This is most often equal to `Exposure.total` but not always. Needed for [`equalise`] + backing_stake: Balance } // Wrapper around the nomination info of a single nominator for a group of validators. @@ -158,25 +160,22 @@ pub fn elect( .collect::>>>(); // 4- If we have more candidates then needed, run Phragmén. - if candidates.len() > rounds { + if candidates.len() >= rounds { elected_candidates = Vec::with_capacity(rounds); // Main election loop for _round in 0..rounds { // Loop 1: initialize score - for n in &nominators { - for e in &n.edges { - let c = &mut candidates[e.candidate_idx]; - if !c.elected { - c.score = Perquintill::from_xth(c.approval_stake.as_()); - } + for c in &mut candidates { + if !c.elected { + c.score = Perquintill::from_xth(c.approval_stake.as_()); } } // Loop 2: increment score. for n in &nominators { for e in &n.edges { let c = &mut candidates[e.candidate_idx]; - let temp = n.budget.as_() * *n.load / c.approval_stake.as_(); if !c.elected { + let temp = n.budget.as_() * *n.load / c.approval_stake.as_(); c.score = Perquintill::from_quintillionths(*c.score + temp); } } @@ -189,12 +188,12 @@ pub fn elect( .min_by_key(|c| *c.score) .expect("candidates length is checked to be >0; qed"); - // loop 3: update nominator and vote load + // loop 3: update nominator and edge load winner.elected = true; for n in &mut nominators { - for v in &mut n.edges { - if v.who == winner.who { - v.load = Perquintill::from_quintillionths(*winner.score - *n.load); + for e in &mut n.edges { + if e.who == winner.who { + e.load = Perquintill::from_quintillionths(*winner.score - *n.load); n.load = winner.score; } } @@ -209,10 +208,11 @@ pub fn elect( // if the target of this vote is among the winners, otherwise let go. if let Some(c) = elected_candidates.iter_mut().find(|c| c.who == e.who) { e.elected = true; - e.backing_stake = as As>::sa(n.budget.as_() * *e.load / *n.load); + e.backing_stake = >::sa(n.budget.as_() * (*e.load / *n.load)); + c.backing_stake += e.backing_stake; if c.who != n.who { + // Only update the exposure if this vote is from some other account. c.exposure.total += e.backing_stake; - // Update IndividualExposure of those who nominated and their vote won c.exposure.others.push( IndividualExposure { who: n.who.clone(), value: e.backing_stake } ); @@ -235,7 +235,7 @@ pub fn elect( }); }); - for _ in 0..equalise_iterations { + for _i in 0..equalise_iterations { let mut max_diff = >::zero(); nominators.iter_mut().for_each(|mut n| { let diff = equalise::(&mut n, &mut elected_candidates, tolerance); @@ -253,9 +253,6 @@ pub fn elect( if candidates.len() > minimum_validator_count { // if we don't have enough candidates, just choose all that have some vote. elected_candidates = candidates; - // TODO: I still don't really trust this. - // Exposure should either be updated in both cases or neither. - // `Exposure.others` still needs an update for n in &mut nominators { let nominator = n.who.clone(); for e in &mut n.edges { @@ -291,12 +288,12 @@ pub fn equalise( .fold(>::zero(), |s, e| s + e.backing_stake); let backed_stakes = elected_edges .iter() - .map(|e| elected_candidates[e.elected_idx].exposure.total) + .map(|e| elected_candidates[e.elected_idx].backing_stake) .collect::>>(); let backing_backed_stake = elected_edges .iter() .filter(|e| e.backing_stake > >::zero()) - .map(|e| elected_candidates[e.elected_idx].exposure.total) + .map(|e| elected_candidates[e.elected_idx].backing_stake) .collect::>>(); let mut difference; @@ -323,19 +320,20 @@ pub fn equalise( // Undo updates to exposure elected_edges.iter_mut().for_each(|e| { assert_eq!(elected_candidates[e.elected_idx].who, e.who); + elected_candidates[e.elected_idx].backing_stake -= e.backing_stake; elected_candidates[e.elected_idx].exposure.total -= e.backing_stake; e.backing_stake = >::zero(); }); - elected_edges.sort_unstable_by_key(|e| elected_candidates[e.elected_idx].exposure.total); + elected_edges.sort_unstable_by_key(|e| elected_candidates[e.elected_idx].backing_stake); let mut cumulative_stake = >::zero(); let mut last_index = elected_edges.len() - 1; let budget = nominator.budget; elected_edges.iter_mut().enumerate().for_each(|(idx, e)| { - let stake = elected_candidates[e.elected_idx].exposure.total; + let stake = elected_candidates[e.elected_idx].backing_stake; - let stake_mul = stake.checked_mul(&>::sa(idx as u64)).unwrap_or(>::one()); + let stake_mul = stake.checked_mul(&>::sa(idx as u64)).unwrap_or(>::max_value()); let stake_sub = stake_mul.checked_sub(&cumulative_stake).unwrap_or_default(); if stake_sub > budget { last_index = idx.clone().checked_sub(1).unwrap_or(0); @@ -344,19 +342,18 @@ pub fn equalise( cumulative_stake += stake; }); - // TODO: safe arithmatic here. - let last_stake = elected_candidates[elected_edges[last_index].elected_idx].exposure.total; + let last_stake = elected_candidates[elected_edges[last_index].elected_idx].backing_stake; let split_ways = last_index + 1; let excess = nominator.budget + cumulative_stake - last_stake * >::sa(split_ways as u64); let nominator_address = nominator.who.clone(); elected_edges.iter_mut().take(split_ways).for_each(|e| { let c = &mut elected_candidates[e.elected_idx]; - e.backing_stake = excess / >::sa(split_ways as u64) + last_stake -c.exposure.total; + e.backing_stake = excess / >::sa(split_ways as u64) + last_stake - c.backing_stake; c.exposure.total += e.backing_stake; + c.backing_stake += e.backing_stake; if let Some(i_expo) = c.exposure.others.iter_mut().find(|i| i.who == nominator_address) { i_expo.value = e.backing_stake; } }); - difference } \ No newline at end of file diff --git a/srml/staking/src/tests.rs b/srml/staking/src/tests.rs index 40532611773b6..5d318df61b4f4 100644 --- a/srml/staking/src/tests.rs +++ b/srml/staking/src/tests.rs @@ -54,9 +54,9 @@ fn basic_setup_works() { assert_eq!(Staking::nominators(100), vec![10, 20]); // Account 10 is exposed by 1000 * balance_factor from their own stash in account 11 + the default nominator vote - assert_eq!(Staking::stakers(10), Exposure { total: 1500, own: 1000, others: vec![ IndividualExposure { who: 100, value: 500 }] }); + assert_eq!(Staking::stakers(10), Exposure { total: 1250, own: 1000, others: vec![ IndividualExposure { who: 100, value: 250 }] }); // Account 20 is exposed by 1000 * balance_factor from their own stash in account 21 + the default nominator vote - assert_eq!(Staking::stakers(20), Exposure { total: 1500, own: 1000, others: vec![ IndividualExposure { who: 100, value: 500 }] }); + assert_eq!(Staking::stakers(20), Exposure { total: 1250, own: 1000, others: vec![ IndividualExposure { who: 100, value: 250 }] }); // The number of validators required. assert_eq!(Staking::validator_count(), 2); @@ -69,7 +69,7 @@ fn basic_setup_works() { assert_eq!(Staking::current_session_reward(), 10); // initial slot_stake - assert_eq!(Staking::slot_stake(), 1500); + assert_eq!(Staking::slot_stake(), 1250); // initial slash_count of validators assert_eq!(Staking::slash_count(&10), 0); @@ -671,13 +671,13 @@ fn nominating_and_rewards_should_work() { assert_eq!(Staking::stakers(10).own, 1000); assert_eq!(Staking::stakers(10).total, 1000 + 1000); // 2 and 4 supported 10, each with stake 600, according to phragmen. - assert_eq!(Staking::stakers(10).others.iter().map(|e| e.value).collect::>>(), vec![600, 400]); + assert_eq!(Staking::stakers(10).others.iter().map(|e| e.value).collect::>>(), vec![500, 500]); assert_eq!(Staking::stakers(10).others.iter().map(|e| e.who).collect::>>(), vec![4, 2]); // total expo of 20, with 500 coming from nominators (externals), according to phragmen. assert_eq!(Staking::stakers(20).own, 1000); assert_eq!(Staking::stakers(20).total, 1000 + 1000); // 2 and 4 supported 20, each with stake 250, according to phragmen. - assert_eq!(Staking::stakers(20).others.iter().map(|e| e.value).collect::>>(), vec![400, 600]); + assert_eq!(Staking::stakers(20).others.iter().map(|e| e.value).collect::>>(), vec![500, 500]); assert_eq!(Staking::stakers(20).others.iter().map(|e| e.who).collect::>>(), vec![4, 2]); // They are not chosen anymore @@ -898,7 +898,7 @@ fn cannot_reserve_staked_balance() { // Confirm account 11 has some free balance assert_eq!(Balances::free_balance(&11), 1000); // Confirm account 11 (via controller 10) is totally staked - assert_eq!(Staking::stakers(&10).total, 1000 + 500); + assert_eq!(Staking::stakers(&10).total, 1000 + 250); // Confirm account 11 cannot transfer as a result assert_noop!(Balances::reserve(&11, 1), "account liquidity restrictions prevent withdrawal"); @@ -1455,7 +1455,7 @@ fn phragmen_poc_works() { assert_ok!(Staking::bond(Origin::signed(1), 2, 500, RewardDestination::default())); assert_ok!(Staking::nominate(Origin::signed(2), vec![10, 20, 30])); - + assert_ok!(Staking::bond(Origin::signed(3), 4, 500, RewardDestination::default())); assert_ok!(Staking::nominate(Origin::signed(4), vec![10, 20, 40])); @@ -1467,17 +1467,17 @@ fn phragmen_poc_works() { // with stake 1666 and 1333 respectively assert_eq!(Staking::stakers(10).own, 1000); - assert_eq!(Staking::stakers(10).total, 1000 + 499); + assert_eq!(Staking::stakers(10).total, 1000 + 500); assert_eq!(Staking::stakers(20).own, 1000); - assert_eq!(Staking::stakers(20).total, 1000 + 499); + assert_eq!(Staking::stakers(20).total, 1000 + 500); // Nominator's stake distribution. - assert_eq!(Staking::stakers(10).others.iter().map(|e| e.value).collect::>>(), vec![333, 166]); - assert_eq!(Staking::stakers(10).others.iter().map(|e| e.value).sum::>(), 499); + assert_eq!(Staking::stakers(10).others.iter().map(|e| e.value).collect::>>(), vec![250, 250]); + assert_eq!(Staking::stakers(10).others.iter().map(|e| e.value).sum::>(), 500); assert_eq!(Staking::stakers(10).others.iter().map(|e| e.who).collect::>>(), vec![4, 2]); - assert_eq!(Staking::stakers(20).others.iter().map(|e| e.value).collect::>>(), vec![166, 333]); - assert_eq!(Staking::stakers(20).others.iter().map(|e| e.value).sum::>(), 499); + assert_eq!(Staking::stakers(20).others.iter().map(|e| e.value).collect::>>(), vec![250, 250]); + assert_eq!(Staking::stakers(20).others.iter().map(|e| e.value).sum::>(), 500); assert_eq!(Staking::stakers(20).others.iter().map(|e| e.who).collect::>>(), vec![4, 2]); }); } @@ -1669,11 +1669,11 @@ fn wrong_vote_is_null() { // add 1 nominators assert_ok!(Staking::bond(Origin::signed(1), 2, 2000, RewardDestination::default())); assert_ok!(Staking::nominate(Origin::signed(2), vec![ - 10, 20, // good votes + 10, 20, // good votes 1, 2, 15, 1000, 25 // crap votes. No effect. ])); - // new block + // new block System::set_block_number(1); Session::check_rotate_session(System::block_number()); @@ -1685,4 +1685,188 @@ fn wrong_vote_is_null() { fn bond_with_no_staked_value() { // Behavior when someone bonds with no staked value. // Particularly when she votes and the candidate is elected. + with_externalities(&mut ExtBuilder::default() + .validator_count(3) + .nominate(false) + .minimum_validator_count(1) + .build(), || { + // setup + assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller)); + assert_ok!(Staking::set_payee(Origin::signed(20), RewardDestination::Controller)); + Balances::set_free_balance(&3, 1000); + Balances::set_free_balance(&4, 1000); + Balances::set_free_balance(&2, 1000); + + // initial validators + assert_eq!(Session::validators(), vec![20, 10]); + + // Stingy validator. + assert_ok!(Staking::bond(Origin::signed(1), 2, 0, RewardDestination::Controller)); + assert_ok!(Staking::validate(Origin::signed(2), ValidatorPrefs::default())); + + System::set_block_number(1); + Session::check_rotate_session(System::block_number()); + + // Not elected even though we want 3. + assert_eq!(Session::validators(), vec![20, 10]); + + // min of 10 and 20. + assert_eq!(Staking::slot_stake(), 1000); + + // let's make the stingy one elected. + assert_ok!(Staking::bond(Origin::signed(3), 4, 500, RewardDestination::Controller)); + assert_ok!(Staking::nominate(Origin::signed(4), vec![2])); + + assert_eq!(Staking::ledger(4), Some(StakingLedger { stash: 3, active: 500, total: 500, unlocking: vec![]})); + + System::set_block_number(1); + Session::check_rotate_session(System::block_number()); + + assert_eq!(Session::validators(), vec![20, 10, 2]); + assert_eq!(Staking::stakers(2), Exposure { own: 0, total: 500, others: vec![IndividualExposure { who: 4, value: 500}]}); + + assert_eq!(Staking::slot_stake(), 500); + + // no rewards paid to 2 and 4 yet + assert_eq!(Balances::free_balance(&2), 1000); + assert_eq!(Balances::free_balance(&4), 1000); + + System::set_block_number(1); + Session::check_rotate_session(System::block_number()); + + let reward = Staking::current_session_reward(); + // 2 will not get any reward + // 4 will get all the reward share + assert_eq!(Balances::free_balance(&2), 1000); + assert_eq!(Balances::free_balance(&4), 1000 + reward); + }); +} +#[test] +fn bond_with_little_staked_value() { + // Behavior when someone bonds with little staked value. + // Particularly when she votes and the candidate is elected. + with_externalities(&mut ExtBuilder::default() + .validator_count(3) + .nominate(false) + .minimum_validator_count(1) + .build(), + || { + // setup + assert_ok!(Staking::set_payee(Origin::signed(10), RewardDestination::Controller)); + assert_ok!(Staking::set_payee(Origin::signed(20), RewardDestination::Controller)); + Balances::set_free_balance(&2, 1000); + + // initial validators + assert_eq!(Session::validators(), vec![20, 10]); + + // Stingy validator. + 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()); + + // 2 is elected. + // and fucks up the slot stake. + assert_eq!(Session::validators(), vec![20, 10, 2]); + assert_eq!(Staking::slot_stake(), 1); + + // Old ones are rewarded. + assert_eq!(Balances::free_balance(&10), 1 + 10); + assert_eq!(Balances::free_balance(&20), 1 + 10); + // no rewards paid to 2. This was initial election. + assert_eq!(Balances::free_balance(&2), 1000); + + System::set_block_number(1); + Session::check_rotate_session(System::block_number()); + + assert_eq!(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), 1000 + reward.max(1)); + }); +} + + +#[test] +fn phragmen_linear_worse_case_equalise() { + with_externalities(&mut ExtBuilder::default() + .nominate(false) + .validator_pool(true) + .fare(true) + .build(), + || { + let bond_validator = |a, b| { + Balances::set_free_balance(&(a-1), b); + assert_ok!(Staking::bond(Origin::signed(a-1), a, b, RewardDestination::Controller)); + assert_ok!(Staking::validate(Origin::signed(a), ValidatorPrefs::default())); + }; + let bond_nominator = |a, b, v| { + Balances::set_free_balance(&(a-1), b); + assert_ok!(Staking::bond(Origin::signed(a-1), a, b, RewardDestination::Controller)); + assert_ok!(Staking::nominate(Origin::signed(a), v)); + }; + + for i in &[10, 20, 30, 40] { assert_ok!(Staking::set_payee(Origin::signed(*i), RewardDestination::Controller)); } + + bond_validator(50, 1000); + bond_validator(60, 1000); + bond_validator(70, 1000); + + bond_nominator(2, 2000, vec![10]); + bond_nominator(4, 1000, vec![10, 20]); + bond_nominator(6, 1000, vec![20, 30]); + bond_nominator(8, 1000, vec![30, 40]); + bond_nominator(110, 1000, vec![40, 50]); + bond_nominator(112, 1000, vec![50, 60]); + bond_nominator(114, 1000, vec![60, 70]); + + assert_eq!(Session::validators(), vec![40, 30]); + assert_ok!(Staking::set_validator_count(7)); + + System::set_block_number(1); + Session::check_rotate_session(System::block_number()); + + assert_eq!(Session::validators(), vec![10, 60, 40, 20, 50, 30, 70]); + + // Sequential Phragmén with post processing gives + // 10 is elected with stake 3000.0 and score 0.00025 + // 30 is elected with stake 2008.8712884829595 and score 0.0003333333333333333 + // 50 is elected with stake 2000.0001049958742 and score 0.0003333333333333333 + // 60 is elected with stake 1991.128921508789 and score 0.0004444444444444444 + // 20 is elected with stake 2017.7421569824219 and score 0.0005277777777777777 + // 40 is elected with stake 2000.0001049958742 and score 0.0005555555555555556 + // 70 is elected with stake 1982.2574230340813 and score 0.0007222222222222222 + + assert_eq!(Staking::stakers(10).total, 3000); + assert_eq!(Staking::stakers(30).total, 2035); + assert_eq!(Staking::stakers(50).total, 2000); + assert_eq!(Staking::stakers(60).total, 1968); + assert_eq!(Staking::stakers(20).total, 2035); + assert_eq!(Staking::stakers(40).total, 2024); + assert_eq!(Staking::stakers(70).total, 1936); + }) +} + +#[test] +fn phragmen_chooses_correct_validators() { + with_externalities(&mut ExtBuilder::default() + .nominate(true) + .validator_pool(true) + .fare(true) + .validator_count(1) + .build(), + || { + // 4 validator candidates + // self vote + default account 100 is nominator. + assert_eq!(Staking::validator_count(), 1); + assert_eq!(Session::validators().len(), 1); + + System::set_block_number(1); + Session::check_rotate_session(System::block_number()); + + assert_eq!(Session::validators().len(), 1); + }) } From 54c141711fc2209e95ce5a028b08b7e95d3a539d Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 21 Mar 2019 19:20:14 +0100 Subject: [PATCH 28/54] Fix test --- srml/staking/src/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/srml/staking/src/tests.rs b/srml/staking/src/tests.rs index 813056d1e7a78..e648c7fa426ee 100644 --- a/srml/staking/src/tests.rs +++ b/srml/staking/src/tests.rs @@ -448,7 +448,7 @@ fn staking_should_work() { assert_eq!(Staking::bonding_duration(), 2); // put some money in account that we'll use. - for i in 1..5 { let _ = Balances::deposit_creating(&i, 2000); } + for i in 1..5 { let _ = Balances::ensure_free_balance_is(&i, 2000); } // --- Block 1: System::set_block_number(1); From abc168532da5745959ddc6f7458878de378b4252 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 21 Mar 2019 19:21:44 +0100 Subject: [PATCH 29/54] Bump spec --- node/runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index 1f52620cd4880..5d5dd7caaddb4 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -58,7 +58,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("node"), impl_name: create_runtime_str!("substrate-node"), authoring_version: 10, - spec_version: 38, + spec_version: 39, impl_version: 39, apis: RUNTIME_API_VERSIONS, }; From 0cdc3c160adc5938dd87401eb29a0c573686ee14 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 21 Mar 2019 19:30:16 +0100 Subject: [PATCH 30/54] Fix wasm build --- core/sr-std/src/lib.rs | 1 + .../release/node_runtime.compact.wasm | Bin 901302 -> 921755 bytes srml/staking/src/phragmen.rs | 15 ++++++++------- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/core/sr-std/src/lib.rs b/core/sr-std/src/lib.rs index 45857b33eda78..3fe520b8a584f 100644 --- a/core/sr-std/src/lib.rs +++ b/core/sr-std/src/lib.rs @@ -45,6 +45,7 @@ pub mod prelude { pub use crate::boxed::Box; pub use crate::cmp::{Eq, PartialEq}; pub use crate::clone::Clone; + pub use crate::alloc::prelude::*; // Re-export `vec!` macro here, but not in `std` mode, since // std's prelude already brings `vec!` into the scope. diff --git a/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm b/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm index 446e9eaad9b7c42c8435e38f28f298bd48fa373d..b5b05d034cdd11d9b1258905540c0f316418187b 100644 GIT binary patch delta 308066 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 z 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 delta 286133 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
Ad5D_Fxd%>_VWn*#;VvRe5Tn^P@<*8lCBU=t394 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