mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 16:31:07 +00:00
Rename all the election operations (#6245)
* Rename and move sp-phragmen * More renames for equalise * Update main module doc * Fix line width * Line width
This commit is contained in:
@@ -64,7 +64,7 @@ TO_RENAME=(
|
||||
"substrate-keyring sp-keyring"
|
||||
"substrate-offchain-primitives sp-offchain"
|
||||
"substrate-panic-handler sp-panic-handler"
|
||||
"substrate-phragmen sp-phragmen"
|
||||
"substrate-phragmen sp-npos-elections"
|
||||
"substrate-rpc-primitives sp-rpc"
|
||||
"substrate-runtime-interface sp-runtime-interface"
|
||||
"substrate-runtime-interface-proc-macro sp-runtime-interface-proc-macro"
|
||||
|
||||
Generated
+39
-39
@@ -4044,7 +4044,7 @@ dependencies = [
|
||||
"serde",
|
||||
"sp-core",
|
||||
"sp-io",
|
||||
"sp-phragmen",
|
||||
"sp-npos-elections",
|
||||
"sp-runtime",
|
||||
"sp-std",
|
||||
"substrate-test-utils",
|
||||
@@ -4435,7 +4435,7 @@ dependencies = [
|
||||
"sp-application-crypto",
|
||||
"sp-core",
|
||||
"sp-io",
|
||||
"sp-phragmen",
|
||||
"sp-npos-elections",
|
||||
"sp-runtime",
|
||||
"sp-staking",
|
||||
"sp-std",
|
||||
@@ -4460,7 +4460,7 @@ dependencies = [
|
||||
"parity-scale-codec",
|
||||
"sp-core",
|
||||
"sp-io",
|
||||
"sp-phragmen",
|
||||
"sp-npos-elections",
|
||||
"sp-runtime",
|
||||
"sp-std",
|
||||
]
|
||||
@@ -7473,6 +7473,42 @@ dependencies = [
|
||||
"strum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-npos-elections"
|
||||
version = "2.0.0-rc2"
|
||||
dependencies = [
|
||||
"parity-scale-codec",
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
"sp-arithmetic",
|
||||
"sp-npos-elections",
|
||||
"sp-npos-elections-compact",
|
||||
"sp-runtime",
|
||||
"sp-std",
|
||||
"substrate-test-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-npos-elections-compact"
|
||||
version = "2.0.0-rc2"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote 1.0.3",
|
||||
"syn 1.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-npos-elections-fuzzer"
|
||||
version = "2.0.0-alpha.5"
|
||||
dependencies = [
|
||||
"honggfuzz",
|
||||
"rand 0.7.3",
|
||||
"sp-npos-elections",
|
||||
"sp-runtime",
|
||||
"sp-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-offchain"
|
||||
version = "2.0.0-rc2"
|
||||
@@ -7491,42 +7527,6 @@ dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-phragmen"
|
||||
version = "2.0.0-rc2"
|
||||
dependencies = [
|
||||
"parity-scale-codec",
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
"sp-arithmetic",
|
||||
"sp-phragmen",
|
||||
"sp-phragmen-compact",
|
||||
"sp-runtime",
|
||||
"sp-std",
|
||||
"substrate-test-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-phragmen-compact"
|
||||
version = "2.0.0-rc2"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
"proc-macro2",
|
||||
"quote 1.0.3",
|
||||
"syn 1.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-phragmen-fuzzer"
|
||||
version = "2.0.0-alpha.5"
|
||||
dependencies = [
|
||||
"honggfuzz",
|
||||
"rand 0.7.3",
|
||||
"sp-phragmen",
|
||||
"sp-runtime",
|
||||
"sp-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-rpc"
|
||||
version = "2.0.0-rc2"
|
||||
|
||||
@@ -137,9 +137,9 @@ members = [
|
||||
"primitives/keyring",
|
||||
"primitives/offchain",
|
||||
"primitives/panic-handler",
|
||||
"primitives/phragmen",
|
||||
"primitives/phragmen/fuzzer",
|
||||
"primitives/phragmen/compact",
|
||||
"primitives/npos-elections",
|
||||
"primitives/npos-elections/fuzzer",
|
||||
"primitives/npos-elections/compact",
|
||||
"primitives/rpc",
|
||||
"primitives/runtime-interface",
|
||||
"primitives/runtime-interface/proc-macro",
|
||||
|
||||
@@ -470,7 +470,7 @@ parameter_types! {
|
||||
pub const ElectionsPhragmenModuleId: LockIdentifier = *b"phrelect";
|
||||
}
|
||||
|
||||
// Make sure that there are no more than `MAX_MEMBERS` members elected via phragmen.
|
||||
// Make sure that there are no more than `MAX_MEMBERS` members elected via elections-phragmen.
|
||||
const_assert!(DesiredMembers::get() <= pallet_collective::MAX_MEMBERS);
|
||||
|
||||
impl pallet_elections_phragmen::Trait for Runtime {
|
||||
@@ -580,7 +580,7 @@ impl pallet_sudo::Trait for Runtime {
|
||||
parameter_types! {
|
||||
pub const SessionDuration: BlockNumber = EPOCH_DURATION_IN_SLOTS as _;
|
||||
pub const ImOnlineUnsignedPriority: TransactionPriority = TransactionPriority::max_value();
|
||||
/// We prioritize im-online heartbeats over phragmen solution submission.
|
||||
/// We prioritize im-online heartbeats over election solution submission.
|
||||
pub const StakingUnsignedPriority: TransactionPriority = TransactionPriority::max_value() / 2;
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
/frame/staking/ @kianenigma
|
||||
/frame/elections/ @kianenigma
|
||||
/frame/elections-phragmen/ @kianenigma
|
||||
/primitives/phragmen/ @kianenigma
|
||||
/primitives/npos-elections/ @kianenigma
|
||||
|
||||
# Fixed point arithmetic
|
||||
/primitives/sp-arithmetic/ @kianenigma
|
||||
|
||||
@@ -6,7 +6,7 @@ edition = "2018"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://substrate.dev"
|
||||
repository = "https://github.com/paritytech/substrate/"
|
||||
description = "FRAME election pallet for PHRAGMEN"
|
||||
description = "FRAME pallet based on seq-Phragmén election method."
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
@@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"]
|
||||
codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] }
|
||||
serde = { version = "1.0.101", optional = true }
|
||||
sp-runtime = { version = "2.0.0-rc2", default-features = false, path = "../../primitives/runtime" }
|
||||
sp-phragmen = { version = "2.0.0-rc2", default-features = false, path = "../../primitives/phragmen" }
|
||||
sp-npos-elections = { version = "2.0.0-rc2", default-features = false, path = "../../primitives/npos-elections" }
|
||||
frame-support = { version = "2.0.0-rc2", default-features = false, path = "../support" }
|
||||
frame-system = { version = "2.0.0-rc2", default-features = false, path = "../system" }
|
||||
sp-std = { version = "2.0.0-rc2", default-features = false, path = "../../primitives/std" }
|
||||
@@ -35,7 +35,7 @@ std = [
|
||||
"codec/std",
|
||||
"frame-support/std",
|
||||
"sp-runtime/std",
|
||||
"sp-phragmen/std",
|
||||
"sp-npos-elections/std",
|
||||
"frame-system/std",
|
||||
"sp-std/std",
|
||||
]
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! # Phragmen Election Module.
|
||||
//! # Phragmén Election Module.
|
||||
//!
|
||||
//! An election module based on sequential phragmen.
|
||||
//!
|
||||
@@ -100,7 +100,7 @@ use frame_support::{
|
||||
ContainsLengthBound,
|
||||
}
|
||||
};
|
||||
use sp_phragmen::{build_support_map, ExtendedBalance, VoteWeight, PhragmenResult};
|
||||
use sp_npos_elections::{build_support_map, ExtendedBalance, VoteWeight, ElectionResult};
|
||||
use frame_system::{self as system, ensure_signed, ensure_root};
|
||||
|
||||
mod benchmarking;
|
||||
@@ -245,7 +245,6 @@ decl_storage! {
|
||||
}
|
||||
|
||||
decl_error! {
|
||||
/// Error for the elections-phragmen module.
|
||||
pub enum Error for Module<T: Trait> {
|
||||
/// Cannot vote when no candidates or members exist.
|
||||
UnableToVote,
|
||||
@@ -610,7 +609,7 @@ decl_module! {
|
||||
/// the outgoing member is slashed.
|
||||
///
|
||||
/// If a runner-up is available, then the best runner-up will be removed and replaces the
|
||||
/// outgoing member. Otherwise, a new phragmen round is started.
|
||||
/// outgoing member. Otherwise, a new phragmen election is started.
|
||||
///
|
||||
/// Note that this does not affect the designated block number of the next election.
|
||||
///
|
||||
@@ -840,13 +839,10 @@ impl<T: Trait> Module<T> {
|
||||
|
||||
/// Run the phragmen election with all required side processes and state updates.
|
||||
///
|
||||
/// Calls the appropriate `ChangeMembers` function variant internally.
|
||||
/// Calls the appropriate [`ChangeMembers`] function variant internally.
|
||||
///
|
||||
/// # <weight>
|
||||
/// #### State
|
||||
/// Reads: O(C + V*E) where C = candidates, V voters and E votes per voter exits.
|
||||
/// Writes: O(M + R) with M desired members and R runners_up.
|
||||
/// # </weight>
|
||||
fn do_phragmen() {
|
||||
let desired_seats = Self::desired_members() as usize;
|
||||
let desired_runners_up = Self::desired_runners_up() as usize;
|
||||
@@ -876,14 +872,14 @@ impl<T: Trait> Module<T> {
|
||||
let voters_and_votes = Voting::<T>::iter()
|
||||
.map(|(voter, (stake, targets))| { (voter, to_votes(stake), targets) })
|
||||
.collect::<Vec<_>>();
|
||||
let maybe_phragmen_result = sp_phragmen::elect::<T::AccountId, Perbill>(
|
||||
let maybe_phragmen_result = sp_npos_elections::seq_phragmen::<T::AccountId, Perbill>(
|
||||
num_to_elect,
|
||||
0,
|
||||
candidates,
|
||||
voters_and_votes.clone(),
|
||||
);
|
||||
|
||||
if let Some(PhragmenResult { winners, assignments }) = maybe_phragmen_result {
|
||||
if let Some(ElectionResult { winners, assignments }) = maybe_phragmen_result {
|
||||
let old_members_ids = <Members<T>>::take().into_iter()
|
||||
.map(|(m, _)| m)
|
||||
.collect::<Vec<T::AccountId>>();
|
||||
@@ -907,7 +903,7 @@ impl<T: Trait> Module<T> {
|
||||
// exposed candidates, cleaning any previous members, and so on. For now, in favour of
|
||||
// readability and veracity, we keep it simple.
|
||||
|
||||
let staked_assignments = sp_phragmen::assignment_ratio_to_staked(
|
||||
let staked_assignments = sp_npos_elections::assignment_ratio_to_staked(
|
||||
assignments,
|
||||
stake_of,
|
||||
);
|
||||
|
||||
@@ -16,7 +16,7 @@ static_assertions = "1.1.0"
|
||||
serde = { version = "1.0.101", optional = true }
|
||||
codec = { package = "parity-scale-codec", version = "1.3.0", default-features = false, features = ["derive"] }
|
||||
sp-std = { version = "2.0.0-rc2", default-features = false, path = "../../primitives/std" }
|
||||
sp-phragmen = { version = "2.0.0-rc2", default-features = false, path = "../../primitives/phragmen" }
|
||||
sp-npos-elections = { version = "2.0.0-rc2", default-features = false, path = "../../primitives/npos-elections" }
|
||||
sp-io ={ version = "2.0.0-rc2", default-features = false, path = "../../primitives/io" }
|
||||
sp-runtime = { version = "2.0.0-rc2", default-features = false, path = "../../primitives/runtime" }
|
||||
sp-staking = { version = "2.0.0-rc2", default-features = false, path = "../../primitives/staking" }
|
||||
@@ -49,7 +49,7 @@ std = [
|
||||
"serde",
|
||||
"codec/std",
|
||||
"sp-std/std",
|
||||
"sp-phragmen/std",
|
||||
"sp-npos-elections/std",
|
||||
"sp-io/std",
|
||||
"frame-support/std",
|
||||
"sp-runtime/std",
|
||||
|
||||
+5
-5
@@ -1003,7 +1003,7 @@ dependencies = [
|
||||
"sp-application-crypto",
|
||||
"sp-core",
|
||||
"sp-io",
|
||||
"sp-phragmen",
|
||||
"sp-npos-elections",
|
||||
"sp-runtime",
|
||||
"sp-staking",
|
||||
"sp-std",
|
||||
@@ -1027,7 +1027,7 @@ dependencies = [
|
||||
"rand 0.7.3",
|
||||
"sp-core",
|
||||
"sp-io",
|
||||
"sp-phragmen",
|
||||
"sp-npos-elections",
|
||||
"sp-runtime",
|
||||
"sp-std",
|
||||
]
|
||||
@@ -1751,18 +1751,18 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-phragmen"
|
||||
name = "sp-npos-elections"
|
||||
version = "2.0.0-alpha.5"
|
||||
dependencies = [
|
||||
"parity-scale-codec",
|
||||
"serde",
|
||||
"sp-phragmen-compact",
|
||||
"sp-npos-elections-compact",
|
||||
"sp-runtime",
|
||||
"sp-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-phragmen-compact"
|
||||
name = "sp-npos-elections-compact"
|
||||
version = "2.0.0-rc2"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
|
||||
@@ -26,7 +26,7 @@ frame-support = { version = "2.0.0-rc2", path = "../../support" }
|
||||
sp-std = { version = "2.0.0-rc2", path = "../../../primitives/std" }
|
||||
sp-io ={ version = "2.0.0-rc2", path = "../../../primitives/io" }
|
||||
sp-core = { version = "2.0.0-rc2", path = "../../../primitives/core" }
|
||||
sp-phragmen = { version = "2.0.0-rc2", path = "../../../primitives/phragmen" }
|
||||
sp-npos-elections = { version = "2.0.0-rc2", path = "../../../primitives/npos-elections" }
|
||||
sp-runtime = { version = "2.0.0-rc2", path = "../../../primitives/runtime" }
|
||||
|
||||
[[bin]]
|
||||
|
||||
@@ -565,7 +565,7 @@ benchmarks! {
|
||||
let caller: T::AccountId = account("caller", n, SEED);
|
||||
let era = <Staking<T>>::current_era().unwrap_or(0);
|
||||
|
||||
// submit a seq-phragmen with all the good stuff on chain
|
||||
// submit a seq-phragmen with all the good stuff on chain.
|
||||
{
|
||||
let (winners, compact, score, size) = get_seq_phragmen_solution::<T>(true);
|
||||
assert!(
|
||||
|
||||
@@ -25,25 +25,25 @@
|
||||
//!
|
||||
//! ## Overview
|
||||
//!
|
||||
//! The Staking module is the means by which a set of network maintainers (known as _authorities_
|
||||
//! in some contexts and _validators_ in others) are chosen based upon those who voluntarily place
|
||||
//! funds under deposit. Under deposit, those funds are rewarded under normal operation but are
|
||||
//! held at pain of _slash_ (expropriation) should the staked maintainer be found not to be
|
||||
//! discharging its duties properly.
|
||||
//! The Staking module is the means by which a set of network maintainers (known as _authorities_ in
|
||||
//! some contexts and _validators_ in others) are chosen based upon those who voluntarily place
|
||||
//! funds under deposit. Under deposit, those funds are rewarded under normal operation but are held
|
||||
//! at pain of _slash_ (expropriation) should the staked maintainer be found not to be discharging
|
||||
//! its duties properly.
|
||||
//!
|
||||
//! ### Terminology
|
||||
//! <!-- Original author of paragraph: @gavofyork -->
|
||||
//!
|
||||
//! - Staking: The process of locking up funds for some time, placing them at risk of slashing
|
||||
//! (loss) in order to become a rewarded maintainer of the network.
|
||||
//! (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.
|
||||
//! 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.
|
||||
//! share in any reward, and punishment, they take.
|
||||
//! - Stash account: The account holding an owner's funds used for staking.
|
||||
//! - Controller account: The account that controls an owner's funds for staking.
|
||||
//! - Era: A (whole) number of sessions, which is the period that the validator set (and each
|
||||
//! validator's active nominator set) is recalculated and where rewards are paid out.
|
||||
//! validator's active nominator set) is recalculated and where rewards are paid out.
|
||||
//! - Slash: The punishment of a staker by reducing its funds.
|
||||
//!
|
||||
//! ### Goals
|
||||
@@ -106,10 +106,10 @@
|
||||
//! valid behavior_ while _punishing any misbehavior or lack of availability_.
|
||||
//!
|
||||
//! Rewards must be claimed for each era before it gets too old by `$HISTORY_DEPTH` using the
|
||||
//! `payout_stakers` call. Any account can call `payout_stakers`, which pays the reward to
|
||||
//! the validator as well as its nominators.
|
||||
//! Only the [`Trait::MaxNominatorRewardedPerValidator`] biggest stakers can claim their reward. This
|
||||
//! is to limit the i/o cost to mutate storage for each nominator's account.
|
||||
//! `payout_stakers` call. Any account can call `payout_stakers`, which pays the reward to the
|
||||
//! validator as well as its nominators. Only the [`Trait::MaxNominatorRewardedPerValidator`]
|
||||
//! biggest stakers can claim their reward. This is to limit the i/o cost to mutate storage for each
|
||||
//! nominator's account.
|
||||
//!
|
||||
//! Slashing can occur at any point in time, once misbehavior is reported. Once slashing is
|
||||
//! determined, a value is deducted from the balance of the validator and all the nominators who
|
||||
@@ -118,8 +118,8 @@
|
||||
//! Slashing logic is further described in the documentation of the `slashing` module.
|
||||
//!
|
||||
//! Similar to slashing, rewards are also shared among a validator and its associated nominators.
|
||||
//! Yet, the reward funds are not always transferred to the stash account and can be configured.
|
||||
//! See [Reward Calculation](#reward-calculation) for more details.
|
||||
//! Yet, the reward funds are not always transferred to the stash account and can be configured. See
|
||||
//! [Reward Calculation](#reward-calculation) for more details.
|
||||
//!
|
||||
//! #### Chilling
|
||||
//!
|
||||
@@ -157,15 +157,15 @@
|
||||
//! pub trait Trait: staking::Trait {}
|
||||
//!
|
||||
//! decl_module! {
|
||||
//! pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
//! /// Reward a validator.
|
||||
//! #[weight = 0]
|
||||
//! pub fn reward_myself(origin) -> dispatch::DispatchResult {
|
||||
//! let reported = ensure_signed(origin)?;
|
||||
//! <staking::Module<T>>::reward_by_ids(vec![(reported, 10)]);
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! }
|
||||
//! pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||
//! /// Reward a validator.
|
||||
//! #[weight = 0]
|
||||
//! pub fn reward_myself(origin) -> dispatch::DispatchResult {
|
||||
//! let reported = ensure_signed(origin)?;
|
||||
//! <staking::Module<T>>::reward_by_ids(vec![(reported, 10)]);
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//! # fn main() { }
|
||||
//! ```
|
||||
@@ -208,8 +208,8 @@
|
||||
//! The validator and its nominator split their reward as following:
|
||||
//!
|
||||
//! The validator can declare an amount, named
|
||||
//! [`commission`](./struct.ValidatorPrefs.html#structfield.commission), that does not
|
||||
//! get shared with the nominators at each reward payout through its
|
||||
//! [`commission`](./struct.ValidatorPrefs.html#structfield.commission), that does not get shared
|
||||
//! with the nominators at each reward payout through its
|
||||
//! [`ValidatorPrefs`](./struct.ValidatorPrefs.html). This value gets deducted from the total reward
|
||||
//! that is paid to the validator and its nominators. The remaining portion is split among the
|
||||
//! validator and all of the nominators that nominated the validator, proportional to the value
|
||||
@@ -218,8 +218,8 @@
|
||||
//! [`others`](./struct.Exposure.html#structfield.others) by
|
||||
//! [`total`](./struct.Exposure.html#structfield.total) in [`Exposure`](./struct.Exposure.html)).
|
||||
//!
|
||||
//! All entities who receive a reward have the option to choose their reward destination
|
||||
//! through the [`Payee`](./struct.Payee.html) storage item (see
|
||||
//! All entities who receive a reward have the option to choose their reward destination through the
|
||||
//! [`Payee`](./struct.Payee.html) storage item (see
|
||||
//! [`set_payee`](enum.Call.html#variant.set_payee)), to be one of the following:
|
||||
//!
|
||||
//! - Controller account, (obviously) not increasing the staked value.
|
||||
@@ -244,9 +244,8 @@
|
||||
//!
|
||||
//! ### Election Algorithm
|
||||
//!
|
||||
//! The current election algorithm is implemented based on Phragmén.
|
||||
//! The reference implementation can be found
|
||||
//! [here](https://github.com/w3f/consensus/tree/master/NPoS).
|
||||
//! The current election algorithm is implemented based on Phragmén. The reference implementation
|
||||
//! can be found [here](https://github.com/w3f/consensus/tree/master/NPoS).
|
||||
//!
|
||||
//! The election algorithm, aside from electing the validators with the most stake value and votes,
|
||||
//! tries to divide the nominator votes among candidates in an equal manner. To further assure this,
|
||||
@@ -256,8 +255,8 @@
|
||||
//!
|
||||
//! ## GenesisConfig
|
||||
//!
|
||||
//! The Staking module depends on the [`GenesisConfig`](./struct.GenesisConfig.html).
|
||||
//! The `GenesisConfig` is optional and allow to set some initial stakers.
|
||||
//! The Staking module depends on the [`GenesisConfig`](./struct.GenesisConfig.html). The
|
||||
//! `GenesisConfig` is optional and allow to set some initial stakers.
|
||||
//!
|
||||
//! ## Related Modules
|
||||
//!
|
||||
@@ -325,9 +324,10 @@ use frame_system::{
|
||||
self as system, ensure_signed, ensure_root, ensure_none,
|
||||
offchain::SendTransactionTypes,
|
||||
};
|
||||
use sp_phragmen::{
|
||||
ExtendedBalance, Assignment, PhragmenScore, PhragmenResult, build_support_map, evaluate_support,
|
||||
elect, generate_compact_solution_type, is_score_better, VotingLimit, SupportMap, VoteWeight,
|
||||
use sp_npos_elections::{
|
||||
ExtendedBalance, Assignment, ElectionScore, ElectionResult as PrimitiveElectionResult,
|
||||
build_support_map, evaluate_support, seq_phragmen, generate_compact_solution_type,
|
||||
is_score_better, VotingLimit, SupportMap, VoteWeight,
|
||||
};
|
||||
|
||||
const DEFAULT_MINIMUM_VALIDATOR_COUNT: u32 = 4;
|
||||
@@ -383,10 +383,10 @@ pub struct ActiveEraInfo {
|
||||
start: Option<u64>,
|
||||
}
|
||||
|
||||
/// Accuracy used for on-chain phragmen.
|
||||
/// Accuracy used for on-chain election.
|
||||
pub type ChainAccuracy = Perbill;
|
||||
|
||||
/// Accuracy used for off-chain phragmen. This better be small.
|
||||
/// Accuracy used for off-chain election. This better be small.
|
||||
pub type OffchainAccuracy = PerU16;
|
||||
|
||||
/// The balance type of this module.
|
||||
@@ -841,8 +841,9 @@ pub trait Trait: frame_system::Trait + SendTransactionTypes<Call<Self>> {
|
||||
|
||||
/// Convert a balance into a number used for election calculation. This must fit into a `u64`
|
||||
/// but is allowed to be sensibly lossy. The `u64` is used to communicate with the
|
||||
/// [`sp_phragmen`] crate which accepts u64 numbers and does operations in 128. Consequently,
|
||||
/// the backward convert is used convert the u128s from phragmen back to a [`BalanceOf`].
|
||||
/// [`sp_npos_elections`] crate which accepts u64 numbers and does operations in 128.
|
||||
/// Consequently, the backward convert is used convert the u128s from sp-elections back to a
|
||||
/// [`BalanceOf`].
|
||||
type CurrencyToVote: Convert<BalanceOf<Self>, VoteWeight> + Convert<u128, BalanceOf<Self>>;
|
||||
|
||||
/// Tokens have been minted and are unused for validator-reward.
|
||||
@@ -864,9 +865,9 @@ pub trait Trait: frame_system::Trait + SendTransactionTypes<Call<Self>> {
|
||||
/// Number of eras that staked funds must remain bonded for.
|
||||
type BondingDuration: Get<EraIndex>;
|
||||
|
||||
/// Number of eras that slashes are deferred by, after computation. This
|
||||
/// should be less than the bonding duration. Set to 0 if slashes should be
|
||||
/// applied immediately, without opportunity for intervention.
|
||||
/// Number of eras that slashes are deferred by, after computation. This should be less than the
|
||||
/// bonding duration. Set to 0 if slashes should be applied immediately, without opportunity for
|
||||
/// intervention.
|
||||
type SlashDeferDuration: Get<EraIndex>;
|
||||
|
||||
/// The origin which can cancel a deferred slash. Root can always do this.
|
||||
@@ -882,16 +883,19 @@ pub trait Trait: frame_system::Trait + SendTransactionTypes<Call<Self>> {
|
||||
/// Something that can estimate the next session change, accurately or as a best effort guess.
|
||||
type NextNewSession: EstimateNextNewSession<Self::BlockNumber>;
|
||||
|
||||
/// How many blocks ahead of the era, within the last do we try to run the phragmen offchain?
|
||||
/// The number of blocks before the end of the era from which election submissions are allowed.
|
||||
/// Setting this to zero will disable the offchain compute and only on-chain seq-phragmen will
|
||||
/// be used.
|
||||
///
|
||||
/// This is bounded by being within the last session. Hence, setting it to a value more than the
|
||||
/// length of a session will be pointless.
|
||||
type ElectionLookahead: Get<Self::BlockNumber>;
|
||||
|
||||
/// The overarching call type.
|
||||
type Call: Dispatchable + From<Call<Self>> + IsSubType<Module<Self>, Self> + Clone;
|
||||
|
||||
/// Maximum number of equalise iterations to run in the offchain submission. If set to 0,
|
||||
/// equalize will not be executed at all.
|
||||
/// Maximum number of balancing iterations to run in the offchain submission. If set to 0,
|
||||
/// balance_solution will not be executed at all.
|
||||
type MaxIterations: Get<u32>;
|
||||
|
||||
/// The threshold of improvement that should be provided for a new solution to be accepted.
|
||||
@@ -951,9 +955,9 @@ decl_storage! {
|
||||
///
|
||||
/// Information is kept for eras in `[current_era - history_depth; current_era]`.
|
||||
///
|
||||
/// Must be more than the number of eras delayed by session otherwise.
|
||||
/// I.e. active era must always be in history.
|
||||
/// I.e. `active_era > current_era - history_depth` must be guaranteed.
|
||||
/// Must be more than the number of eras delayed by session otherwise. I.e. active era must
|
||||
/// always be in history. I.e. `active_era > current_era - history_depth` must be
|
||||
/// guaranteed.
|
||||
HistoryDepth get(fn history_depth) config(): u32 = 84;
|
||||
|
||||
/// The ideal number of staking participants.
|
||||
@@ -1113,7 +1117,7 @@ decl_storage! {
|
||||
pub QueuedElected get(fn queued_elected): Option<ElectionResult<T::AccountId, BalanceOf<T>>>;
|
||||
|
||||
/// The score of the current [`QueuedElected`].
|
||||
pub QueuedScore get(fn queued_score): Option<PhragmenScore>;
|
||||
pub QueuedScore get(fn queued_score): Option<ElectionScore>;
|
||||
|
||||
/// Flag to control the execution of the offchain election. When `Open(_)`, we accept
|
||||
/// solutions to be submitted.
|
||||
@@ -1356,7 +1360,7 @@ decl_module! {
|
||||
log!(debug, "skipping offchain worker in open election window due to [{}]", why);
|
||||
} else {
|
||||
if let Err(e) = compute_offchain_election::<T>() {
|
||||
log!(error, "💸 Error in phragmen offchain worker: {:?}", e);
|
||||
log!(error, "💸 Error in election offchain worker: {:?}", e);
|
||||
} else {
|
||||
log!(debug, "Executed offchain worker thread without errors.");
|
||||
}
|
||||
@@ -2067,7 +2071,7 @@ decl_module! {
|
||||
T::Currency::remove_lock(STAKING_ID, &stash);
|
||||
}
|
||||
|
||||
/// Submit a phragmen result to the chain. If the solution:
|
||||
/// Submit an election result to the chain. If the solution:
|
||||
///
|
||||
/// 1. is valid.
|
||||
/// 2. has a better score than a potentially existing solution on chain.
|
||||
@@ -2080,7 +2084,7 @@ decl_module! {
|
||||
/// 2. `assignments`: the compact version of an assignment vector that encodes the edge
|
||||
/// weights.
|
||||
///
|
||||
/// Both of which may be computed using [`phragmen`], or any other algorithm.
|
||||
/// Both of which may be computed using _phragmen_, or any other algorithm.
|
||||
///
|
||||
/// Additionally, the submitter must provide:
|
||||
///
|
||||
@@ -2119,7 +2123,7 @@ decl_module! {
|
||||
origin,
|
||||
winners: Vec<ValidatorIndex>,
|
||||
compact: CompactAssignments,
|
||||
score: PhragmenScore,
|
||||
score: ElectionScore,
|
||||
era: EraIndex,
|
||||
size: ElectionSize,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
@@ -2148,7 +2152,7 @@ decl_module! {
|
||||
origin,
|
||||
winners: Vec<ValidatorIndex>,
|
||||
compact: CompactAssignments,
|
||||
score: PhragmenScore,
|
||||
score: ElectionScore,
|
||||
era: EraIndex,
|
||||
size: ElectionSize,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
@@ -2413,7 +2417,7 @@ impl<T: Trait> Module<T> {
|
||||
///
|
||||
/// This function does weight refund in case of errors, which is based upon the fact that it is
|
||||
/// called at the very beginning of the call site's function.
|
||||
pub fn pre_dispatch_checks(score: PhragmenScore, era: EraIndex) -> DispatchResultWithPostInfo {
|
||||
pub fn pre_dispatch_checks(score: ElectionScore, era: EraIndex) -> DispatchResultWithPostInfo {
|
||||
// discard solutions that are not in-time
|
||||
// check window open
|
||||
ensure!(
|
||||
@@ -2446,7 +2450,7 @@ impl<T: Trait> Module<T> {
|
||||
winners: Vec<ValidatorIndex>,
|
||||
compact_assignments: CompactAssignments,
|
||||
compute: ElectionCompute,
|
||||
claimed_score: PhragmenScore,
|
||||
claimed_score: ElectionScore,
|
||||
era: EraIndex,
|
||||
election_size: ElectionSize,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
@@ -2576,7 +2580,7 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
// convert into staked assignments.
|
||||
let staked_assignments = sp_phragmen::assignment_ratio_to_staked(
|
||||
let staked_assignments = sp_npos_elections::assignment_ratio_to_staked(
|
||||
assignments,
|
||||
Self::slashable_balance_of_vote_weight,
|
||||
);
|
||||
@@ -2764,7 +2768,7 @@ impl<T: Trait> Module<T> {
|
||||
elected_stashes,
|
||||
exposures,
|
||||
compute,
|
||||
}) = Self::try_do_phragmen() {
|
||||
}) = Self::try_do_election() {
|
||||
// Totally close the election round and data.
|
||||
Self::close_election_window();
|
||||
|
||||
@@ -2810,12 +2814,12 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
|
||||
/// Select a new validator set from the assembled stakers and their role preferences. It tries
|
||||
/// first to peek into [`QueuedElected`]. Otherwise, it runs a new phragmen.
|
||||
/// first to peek into [`QueuedElected`]. Otherwise, it runs a new on-chain phragmen election.
|
||||
///
|
||||
/// If [`QueuedElected`] and [`QueuedScore`] exists, they are both removed. No further storage
|
||||
/// is updated.
|
||||
fn try_do_phragmen() -> Option<ElectionResult<T::AccountId, BalanceOf<T>>> {
|
||||
// a phragmen result from either a stored submission or locally executed one.
|
||||
fn try_do_election() -> Option<ElectionResult<T::AccountId, BalanceOf<T>>> {
|
||||
// an election result from either a stored submission or locally executed one.
|
||||
let next_result = <QueuedElected<T>>::take().or_else(||
|
||||
Self::do_phragmen_with_post_processing::<ChainAccuracy>(ElectionCompute::OnChain)
|
||||
);
|
||||
@@ -2827,11 +2831,11 @@ impl<T: Trait> Module<T> {
|
||||
next_result
|
||||
}
|
||||
|
||||
/// Execute phragmen and return the new results. The edge weights are processed into support
|
||||
/// Execute election and return the new results. The edge weights are processed into support
|
||||
/// values.
|
||||
///
|
||||
/// This is basically a wrapper around [`do_phragmen`] which translates `PhragmenResult` into
|
||||
/// `ElectionResult`.
|
||||
/// This is basically a wrapper around [`do_phragmen`] which translates
|
||||
/// `PrimitiveElectionResult` into `ElectionResult`.
|
||||
///
|
||||
/// No storage item is updated.
|
||||
fn do_phragmen_with_post_processing<Accuracy: PerThing>(compute: ElectionCompute)
|
||||
@@ -2846,7 +2850,7 @@ impl<T: Trait> Module<T> {
|
||||
.collect::<Vec<T::AccountId>>();
|
||||
let assignments = phragmen_result.assignments;
|
||||
|
||||
let staked_assignments = sp_phragmen::assignment_ratio_to_staked(
|
||||
let staked_assignments = sp_npos_elections::assignment_ratio_to_staked(
|
||||
assignments,
|
||||
Self::slashable_balance_of_vote_weight,
|
||||
);
|
||||
@@ -2877,13 +2881,13 @@ impl<T: Trait> Module<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute phragmen and return the new results. No post-processing is applied and the raw edge
|
||||
/// weights are returned.
|
||||
/// Execute phragmen election and return the new results. No post-processing is applied and the
|
||||
/// raw edge weights are returned.
|
||||
///
|
||||
/// Self votes are added and nominations before the most recent slashing span are reaped.
|
||||
///
|
||||
/// No storage item is updated.
|
||||
fn do_phragmen<Accuracy: PerThing>() -> Option<PhragmenResult<T::AccountId, Accuracy>> {
|
||||
fn do_phragmen<Accuracy: PerThing>() -> Option<PrimitiveElectionResult<T::AccountId, Accuracy>> {
|
||||
let mut all_nominators: Vec<(T::AccountId, VoteWeight, Vec<T::AccountId>)> = Vec::new();
|
||||
let mut all_validators = Vec::new();
|
||||
for (validator, _) in <Validators<T>>::iter() {
|
||||
@@ -2912,7 +2916,7 @@ impl<T: Trait> Module<T> {
|
||||
(n, s, ns)
|
||||
}));
|
||||
|
||||
elect::<_, Accuracy>(
|
||||
seq_phragmen::<_, Accuracy>(
|
||||
Self::validator_count() as usize,
|
||||
Self::minimum_validator_count().max(1) as usize,
|
||||
all_validators,
|
||||
@@ -2920,7 +2924,7 @@ impl<T: Trait> Module<T> {
|
||||
)
|
||||
}
|
||||
|
||||
/// Consume a set of [`Supports`] from [`sp_phragmen`] and collect them into a [`Exposure`]
|
||||
/// Consume a set of [`Supports`] from [`sp_npos_elections`] and collect them into a [`Exposure`]
|
||||
fn collect_exposure(supports: SupportMap<T::AccountId>) -> Vec<(T::AccountId, Exposure<T::AccountId, BalanceOf<T>>)> {
|
||||
let to_balance = |e: ExtendedBalance|
|
||||
<T::CurrencyToVote as Convert<ExtendedBalance, BalanceOf<T>>>::convert(e);
|
||||
|
||||
@@ -31,8 +31,8 @@ use frame_support::{
|
||||
weights::{Weight, constants::RocksDbWeight},
|
||||
};
|
||||
use sp_io;
|
||||
use sp_phragmen::{
|
||||
build_support_map, evaluate_support, reduce, ExtendedBalance, StakedAssignment, PhragmenScore,
|
||||
use sp_npos_elections::{
|
||||
build_support_map, evaluate_support, reduce, ExtendedBalance, StakedAssignment, ElectionScore,
|
||||
VoteWeight,
|
||||
};
|
||||
use crate::*;
|
||||
@@ -783,7 +783,7 @@ pub(crate) fn add_slash(who: &AccountId) {
|
||||
// distributed evenly.
|
||||
pub(crate) fn horrible_phragmen_with_post_processing(
|
||||
do_reduce: bool,
|
||||
) -> (CompactAssignments, Vec<ValidatorIndex>, PhragmenScore) {
|
||||
) -> (CompactAssignments, Vec<ValidatorIndex>, ElectionScore) {
|
||||
let mut backing_stake_of: BTreeMap<AccountId, Balance> = BTreeMap::new();
|
||||
|
||||
// self stake
|
||||
@@ -855,7 +855,7 @@ pub(crate) fn horrible_phragmen_with_post_processing(
|
||||
let support = build_support_map::<AccountId>(&winners, &staked_assignment).0;
|
||||
let score = evaluate_support(&support);
|
||||
|
||||
assert!(sp_phragmen::is_score_better::<Perbill>(
|
||||
assert!(sp_npos_elections::is_score_better::<Perbill>(
|
||||
better_score,
|
||||
score,
|
||||
MinSolutionScoreBump::get(),
|
||||
@@ -879,7 +879,7 @@ pub(crate) fn horrible_phragmen_with_post_processing(
|
||||
|
||||
// convert back to ratio assignment. This takes less space.
|
||||
let assignments_reduced =
|
||||
sp_phragmen::assignment_staked_to_ratio::<AccountId, OffchainAccuracy>(staked_assignment);
|
||||
sp_npos_elections::assignment_staked_to_ratio::<AccountId, OffchainAccuracy>(staked_assignment);
|
||||
|
||||
let compact =
|
||||
CompactAssignments::from_assignment(assignments_reduced, nominator_index, validator_index)
|
||||
@@ -897,13 +897,13 @@ pub(crate) fn prepare_submission_with(
|
||||
do_reduce: bool,
|
||||
iterations: usize,
|
||||
tweak: impl FnOnce(&mut Vec<StakedAssignment<AccountId>>),
|
||||
) -> (CompactAssignments, Vec<ValidatorIndex>, PhragmenScore) {
|
||||
// run phragmen on the default stuff.
|
||||
let sp_phragmen::PhragmenResult {
|
||||
) -> (CompactAssignments, Vec<ValidatorIndex>, ElectionScore) {
|
||||
// run election on the default stuff.
|
||||
let sp_npos_elections::ElectionResult {
|
||||
winners,
|
||||
assignments,
|
||||
} = Staking::do_phragmen::<OffchainAccuracy>().unwrap();
|
||||
let winners = sp_phragmen::to_without_backing(winners);
|
||||
let winners = sp_npos_elections::to_without_backing(winners);
|
||||
|
||||
let stake_of = |who: &AccountId| -> VoteWeight {
|
||||
<CurrencyToVoteHandler as Convert<Balance, VoteWeight>>::convert(
|
||||
@@ -911,11 +911,11 @@ pub(crate) fn prepare_submission_with(
|
||||
)
|
||||
};
|
||||
|
||||
let mut staked = sp_phragmen::assignment_ratio_to_staked(assignments, stake_of);
|
||||
let mut staked = sp_npos_elections::assignment_ratio_to_staked(assignments, stake_of);
|
||||
let (mut support_map, _) = build_support_map::<AccountId>(&winners, &staked);
|
||||
|
||||
if iterations > 0 {
|
||||
sp_phragmen::equalize(
|
||||
sp_npos_elections::balance_solution(
|
||||
&mut staked,
|
||||
&mut support_map,
|
||||
Zero::zero(),
|
||||
@@ -952,11 +952,11 @@ pub(crate) fn prepare_submission_with(
|
||||
)
|
||||
};
|
||||
|
||||
let assignments_reduced = sp_phragmen::assignment_staked_to_ratio(staked);
|
||||
let assignments_reduced = sp_npos_elections::assignment_staked_to_ratio(staked);
|
||||
|
||||
// re-compute score by converting, yet again, into staked type
|
||||
let score = {
|
||||
let staked = sp_phragmen::assignment_ratio_to_staked(
|
||||
let staked = sp_npos_elections::assignment_ratio_to_staked(
|
||||
assignments_reduced.clone(),
|
||||
Staking::slashable_balance_of_vote_weight,
|
||||
);
|
||||
|
||||
@@ -23,9 +23,9 @@ use crate::{
|
||||
ElectionSize,
|
||||
};
|
||||
use frame_system::offchain::SubmitTransaction;
|
||||
use sp_phragmen::{
|
||||
build_support_map, evaluate_support, reduce, Assignment, ExtendedBalance, PhragmenResult,
|
||||
PhragmenScore, equalize,
|
||||
use sp_npos_elections::{
|
||||
build_support_map, evaluate_support, reduce, Assignment, ExtendedBalance, ElectionResult,
|
||||
ElectionScore, balance_solution,
|
||||
};
|
||||
use sp_runtime::offchain::storage::StorageValueRef;
|
||||
use sp_runtime::{PerThing, RuntimeDebug, traits::{TrailingZeroInput, Zero}};
|
||||
@@ -35,22 +35,22 @@ use sp_std::{convert::TryInto, prelude::*};
|
||||
/// Error types related to the offchain election machinery.
|
||||
#[derive(RuntimeDebug)]
|
||||
pub enum OffchainElectionError {
|
||||
/// Phragmen election returned None. This means less candidate that minimum number of needed
|
||||
/// election returned None. This means less candidate that minimum number of needed
|
||||
/// validators were present. The chain is in trouble and not much that we can do about it.
|
||||
ElectionFailed,
|
||||
/// Submission to the transaction pool failed.
|
||||
PoolSubmissionFailed,
|
||||
/// The snapshot data is not available.
|
||||
SnapshotUnavailable,
|
||||
/// Error from phragmen crate. This usually relates to compact operation.
|
||||
PhragmenError(sp_phragmen::Error),
|
||||
/// Error from npos-election crate. This usually relates to compact operation.
|
||||
InternalElectionError(sp_npos_elections::Error),
|
||||
/// One of the computed winners is invalid.
|
||||
InvalidWinner,
|
||||
}
|
||||
|
||||
impl From<sp_phragmen::Error> for OffchainElectionError {
|
||||
fn from(e: sp_phragmen::Error) -> Self {
|
||||
Self::PhragmenError(e)
|
||||
impl From<sp_npos_elections::Error> for OffchainElectionError {
|
||||
fn from(e: sp_npos_elections::Error) -> Self {
|
||||
Self::InternalElectionError(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ pub(crate) fn set_check_offchain_execution_status<T: Trait>(
|
||||
/// unsigned transaction, without any signature.
|
||||
pub(crate) fn compute_offchain_election<T: Trait>() -> Result<(), OffchainElectionError> {
|
||||
// compute raw solution. Note that we use `OffchainAccuracy`.
|
||||
let PhragmenResult {
|
||||
let ElectionResult {
|
||||
winners,
|
||||
assignments,
|
||||
} = <Module<T>>::do_phragmen::<OffchainAccuracy>()
|
||||
@@ -133,7 +133,7 @@ pub(crate) fn compute_offchain_election<T: Trait>() -> Result<(), OffchainElecti
|
||||
}
|
||||
|
||||
|
||||
/// Takes a phragmen result and spits out some data that can be submitted to the chain.
|
||||
/// Takes an election result and spits out some data that can be submitted to the chain.
|
||||
///
|
||||
/// This does a lot of stuff; read the inline comments.
|
||||
pub fn prepare_submission<T: Trait>(
|
||||
@@ -143,7 +143,7 @@ pub fn prepare_submission<T: Trait>(
|
||||
) -> Result<(
|
||||
Vec<ValidatorIndex>,
|
||||
CompactAssignments,
|
||||
PhragmenScore,
|
||||
ElectionScore,
|
||||
ElectionSize,
|
||||
), OffchainElectionError> where
|
||||
ExtendedBalance: From<<OffchainAccuracy as PerThing>::Inner>,
|
||||
@@ -169,26 +169,26 @@ pub fn prepare_submission<T: Trait>(
|
||||
};
|
||||
|
||||
// Clean winners.
|
||||
let winners = sp_phragmen::to_without_backing(winners);
|
||||
let winners = sp_npos_elections::to_without_backing(winners);
|
||||
|
||||
// convert into absolute value and to obtain the reduced version.
|
||||
let mut staked = sp_phragmen::assignment_ratio_to_staked(
|
||||
let mut staked = sp_npos_elections::assignment_ratio_to_staked(
|
||||
assignments,
|
||||
<Module<T>>::slashable_balance_of_vote_weight,
|
||||
);
|
||||
|
||||
let (mut support_map, _) = build_support_map::<T::AccountId>(&winners, &staked);
|
||||
// equalize a random number of times.
|
||||
// balance a random number of times.
|
||||
let iterations_executed = match T::MaxIterations::get() {
|
||||
0 => {
|
||||
// Don't run equalize at all
|
||||
// Don't run balance_solution at all
|
||||
0
|
||||
}
|
||||
iterations @ _ => {
|
||||
let seed = sp_io::offchain::random_seed();
|
||||
let iterations = <u32>::decode(&mut TrailingZeroInput::new(seed.as_ref()))
|
||||
.expect("input is padded with zeroes; qed") % iterations.saturating_add(1);
|
||||
equalize(
|
||||
balance_solution(
|
||||
&mut staked,
|
||||
&mut support_map,
|
||||
Zero::zero(),
|
||||
@@ -203,7 +203,7 @@ pub fn prepare_submission<T: Trait>(
|
||||
}
|
||||
|
||||
// Convert back to ratio assignment. This takes less space.
|
||||
let low_accuracy_assignment = sp_phragmen::assignment_staked_to_ratio(staked);
|
||||
let low_accuracy_assignment = sp_npos_elections::assignment_staked_to_ratio(staked);
|
||||
|
||||
// convert back to staked to compute the score in the receiver's accuracy. This can be done
|
||||
// nicer, for now we do it as such since this code is not time-critical. This ensure that the
|
||||
@@ -214,7 +214,7 @@ pub fn prepare_submission<T: Trait>(
|
||||
// assignment set is also all multiples of this value. After reduce, this no longer holds. Hence
|
||||
// converting to ratio thereafter is not trivially reversible.
|
||||
let score = {
|
||||
let staked = sp_phragmen::assignment_ratio_to_staked(
|
||||
let staked = sp_npos_elections::assignment_ratio_to_staked(
|
||||
low_accuracy_assignment.clone(),
|
||||
<Module<T>>::slashable_balance_of_vote_weight,
|
||||
);
|
||||
|
||||
@@ -24,7 +24,7 @@ use frame_benchmarking::{account};
|
||||
use frame_system::RawOrigin;
|
||||
use sp_io::hashing::blake2_256;
|
||||
use rand_chacha::{rand_core::{RngCore, SeedableRng}, ChaChaRng};
|
||||
use sp_phragmen::*;
|
||||
use sp_npos_elections::*;
|
||||
|
||||
const SEED: u32 = 0;
|
||||
|
||||
@@ -138,7 +138,7 @@ pub fn create_validators_with_nominators_for_era<T: Trait>(
|
||||
/// which has a less score than the seq-phragmen.
|
||||
pub fn get_weak_solution<T: Trait>(
|
||||
do_reduce: bool,
|
||||
) -> (Vec<ValidatorIndex>, CompactAssignments, PhragmenScore, ElectionSize) {
|
||||
) -> (Vec<ValidatorIndex>, CompactAssignments, ElectionScore, ElectionSize) {
|
||||
let mut backing_stake_of: BTreeMap<T::AccountId, BalanceOf<T>> = BTreeMap::new();
|
||||
|
||||
// self stake
|
||||
@@ -252,8 +252,8 @@ pub fn get_weak_solution<T: Trait>(
|
||||
/// worker code.
|
||||
pub fn get_seq_phragmen_solution<T: Trait>(
|
||||
do_reduce: bool,
|
||||
) -> (Vec<ValidatorIndex>, CompactAssignments, PhragmenScore, ElectionSize) {
|
||||
let sp_phragmen::PhragmenResult {
|
||||
) -> (Vec<ValidatorIndex>, CompactAssignments, ElectionScore, ElectionSize) {
|
||||
let sp_npos_elections::ElectionResult {
|
||||
winners,
|
||||
assignments,
|
||||
} = <Module<T>>::do_phragmen::<OffchainAccuracy>().unwrap();
|
||||
@@ -264,7 +264,7 @@ pub fn get_seq_phragmen_solution<T: Trait>(
|
||||
/// Returns a solution in which only one winner is elected with just a self vote.
|
||||
pub fn get_single_winner_solution<T: Trait>(
|
||||
winner: T::AccountId
|
||||
) -> Result<(Vec<ValidatorIndex>, CompactAssignments, PhragmenScore, ElectionSize), &'static str> {
|
||||
) -> Result<(Vec<ValidatorIndex>, CompactAssignments, ElectionScore, ElectionSize), &'static str> {
|
||||
let snapshot_validators = <Module<T>>::snapshot_validators().unwrap();
|
||||
let snapshot_nominators = <Module<T>>::snapshot_nominators().unwrap();
|
||||
|
||||
|
||||
@@ -406,41 +406,6 @@ fn no_candidate_emergency_condition() {
|
||||
|
||||
#[test]
|
||||
fn nominating_and_rewards_should_work() {
|
||||
// PHRAGMEN OUTPUT: running this test with the reference impl gives:
|
||||
//
|
||||
// Sequential Phragmén gives
|
||||
// 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
|
||||
|
||||
// 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
|
||||
ExtBuilder::default()
|
||||
.nominate(false)
|
||||
.validator_pool(true)
|
||||
@@ -477,7 +442,7 @@ fn nominating_and_rewards_should_work() {
|
||||
|
||||
mock::start_era(1);
|
||||
|
||||
// 10 and 20 have more votes, they will be chosen by phragmen.
|
||||
// 10 and 20 have more votes, they will be chosen.
|
||||
assert_eq_uvec!(validator_controllers(), vec![20, 10]);
|
||||
|
||||
// OLD validators must have already received some rewards.
|
||||
@@ -2765,7 +2730,7 @@ mod offchain_phragmen {
|
||||
OffchainExt, TransactionPoolExt,
|
||||
};
|
||||
use sp_io::TestExternalities;
|
||||
use sp_phragmen::StakedAssignment;
|
||||
use sp_npos_elections::StakedAssignment;
|
||||
use frame_support::traits::OffchainWorker;
|
||||
use std::sync::Arc;
|
||||
use substrate_test_utils::assert_eq_uvec;
|
||||
@@ -2822,7 +2787,7 @@ mod offchain_phragmen {
|
||||
origin: Origin,
|
||||
winners: Vec<ValidatorIndex>,
|
||||
compact: CompactAssignments,
|
||||
score: PhragmenScore,
|
||||
score: ElectionScore,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
Staking::submit_election_solution(
|
||||
origin,
|
||||
@@ -3355,7 +3320,7 @@ mod offchain_phragmen {
|
||||
&inner,
|
||||
),
|
||||
TransactionValidity::Ok(ValidTransaction {
|
||||
// the proposed slot stake, with equalize.
|
||||
// the proposed slot stake, with balance_solution.
|
||||
priority: UnsignedPriority::get() + 1250,
|
||||
requires: vec![],
|
||||
provides: vec![("StakingOffchain", active_era()).encode()],
|
||||
|
||||
@@ -215,7 +215,7 @@ mod tests {
|
||||
assert_eq!(r(MAX128 - 10, MAX128).to_den(10), Ok(r(10, 10)));
|
||||
assert_eq!(r(MAX128 / 2, MAX128).to_den(10), Ok(r(5, 10)));
|
||||
|
||||
// large to perbill. This is very well needed for phragmen.
|
||||
// large to perbill. This is very well needed for npos-elections.
|
||||
assert_eq!(
|
||||
r(MAX128 / 2, MAX128).to_den(1000_000_000),
|
||||
Ok(r(500_000_000, 1000_000_000))
|
||||
|
||||
+4
-4
@@ -1,12 +1,12 @@
|
||||
[package]
|
||||
name = "sp-phragmen"
|
||||
name = "sp-npos-elections"
|
||||
version = "2.0.0-rc2"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://substrate.dev"
|
||||
repository = "https://github.com/paritytech/substrate/"
|
||||
description = "Phragmen primitives"
|
||||
description = "NPoS election algorithm primitives"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
@@ -15,13 +15,13 @@ targets = ["x86_64-unknown-linux-gnu"]
|
||||
codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] }
|
||||
serde = { version = "1.0.101", optional = true, features = ["derive"] }
|
||||
sp-std = { version = "2.0.0-rc2", default-features = false, path = "../std" }
|
||||
sp-phragmen-compact = { version = "2.0.0-rc2", path = "./compact" }
|
||||
sp-npos-elections-compact = { version = "2.0.0-rc2", path = "./compact" }
|
||||
sp-arithmetic = { version = "2.0.0-rc2", default-features = false, path = "../arithmetic" }
|
||||
|
||||
[dev-dependencies]
|
||||
substrate-test-utils = { version = "2.0.0-rc2", path = "../../test-utils" }
|
||||
rand = "0.7.3"
|
||||
sp-phragmen = { version = "2.0.0-rc2", path = "." }
|
||||
sp-npos-elections = { version = "2.0.0-rc2", path = "." }
|
||||
sp-runtime = { version = "2.0.0-rc2", path = "../../primitives/runtime" }
|
||||
|
||||
[features]
|
||||
+11
-13
@@ -25,10 +25,15 @@ extern crate test;
|
||||
use test::Bencher;
|
||||
|
||||
use rand::{self, Rng};
|
||||
use sp_phragmen::{PhragmenResult, VoteWeight};
|
||||
use sp_npos_elections::{ElectionResult, VoteWeight};
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use sp_runtime::{Perbill, traits::Zero};
|
||||
use sp_runtime::{Perbill, PerThing, traits::Zero};
|
||||
use sp_npos_elections::{
|
||||
balance_solution, assignment_ratio_to_staked, build_support_map, to_without_backing, VoteWeight,
|
||||
ExtendedBalance, Assignment, StakedAssignment, IdentifierT, assignment_ratio_to_staked,
|
||||
seq_phragmen,
|
||||
};
|
||||
|
||||
// default params. Each will be scaled by the benchmarks individually.
|
||||
const VALIDATORS: u64 = 100;
|
||||
@@ -42,13 +47,7 @@ const PREFIX: AccountId = 1000_000;
|
||||
type AccountId = u64;
|
||||
|
||||
mod bench_closure_and_slice {
|
||||
use sp_phragmen::{
|
||||
VoteWeight, ExtendedBalance, Assignment, StakedAssignment, IdentifierT,
|
||||
assignment_ratio_to_staked,
|
||||
};
|
||||
use sp_runtime::{Perbill, PerThing};
|
||||
use rand::{self, Rng, RngCore};
|
||||
use test::Bencher;
|
||||
use super::*;
|
||||
|
||||
fn random_assignment() -> Assignment<u32, Perbill> {
|
||||
let mut rng = rand::thread_rng();
|
||||
@@ -135,7 +134,7 @@ fn do_phragmen(
|
||||
});
|
||||
|
||||
b.iter(|| {
|
||||
let PhragmenResult { winners, assignments } = sp_phragmen::elect::<AccountId, Perbill>(
|
||||
let ElectionResult { winners, assignments } = seq_phragmen::<AccountId, Perbill>(
|
||||
to_elect,
|
||||
Zero::zero(),
|
||||
candidates.clone(),
|
||||
@@ -146,14 +145,13 @@ fn do_phragmen(
|
||||
*stake_of_tree.get(who).unwrap()
|
||||
};
|
||||
|
||||
// Do the benchmarking with equalize.
|
||||
// Do the benchmarking with balancing.
|
||||
if eq_iters > 0 {
|
||||
use sp_phragmen::{equalize, assignment_ratio_to_staked, build_support_map, to_without_backing};
|
||||
let staked = assignment_ratio_to_staked(assignments, &stake_of);
|
||||
let winners = to_without_backing(winners);
|
||||
let mut support = build_support_map(winners.as_ref(), staked.as_ref()).0;
|
||||
|
||||
equalize(
|
||||
balance_solution(
|
||||
staked.into_iter().map(|a| (a.clone(), stake_of(&a.who))).collect(),
|
||||
&mut support,
|
||||
eq_tolerance,
|
||||
+2
-2
@@ -1,12 +1,12 @@
|
||||
[package]
|
||||
name = "sp-phragmen-compact"
|
||||
name = "sp-npos-elections-compact"
|
||||
version = "2.0.0-rc2"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
license = "Apache-2.0"
|
||||
homepage = "https://substrate.dev"
|
||||
repository = "https://github.com/paritytech/substrate/"
|
||||
description = "Phragmen Compact Solution"
|
||||
description = "NPoS Compact Solution Type"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
+7
-1
@@ -57,7 +57,13 @@ fn from_impl(count: usize) -> TokenStream2 {
|
||||
let last = quote!(index_of_target(&distribution[#last_index].0).ok_or(_phragmen::Error::CompactInvalidIndex)?);
|
||||
|
||||
quote!(
|
||||
#c => compact.#field_name.push((index_of_voter(&who).ok_or(_phragmen::Error::CompactInvalidIndex)?, [#inner], #last)),
|
||||
#c => compact.#field_name.push(
|
||||
(
|
||||
index_of_voter(&who).ok_or(_phragmen::Error::CompactInvalidIndex)?,
|
||||
[#inner],
|
||||
#last,
|
||||
)
|
||||
),
|
||||
)
|
||||
}).collect::<TokenStream2>();
|
||||
|
||||
+6
-6
@@ -15,7 +15,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Proc macro for phragmen compact assignment.
|
||||
//! Proc macro for a npos compact assignment.
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::{TokenStream as TokenStream2, Span, Ident};
|
||||
@@ -29,7 +29,7 @@ mod staked;
|
||||
// prefix used for struct fields in compact.
|
||||
const PREFIX: &'static str = "votes";
|
||||
|
||||
/// Generates a struct to store the phragmen assignments in a compact way. The struct can only store
|
||||
/// Generates a struct to store the election assignments in a compact way. The struct can only store
|
||||
/// distributions up to the given input count. The given count must be greater than 2.
|
||||
///
|
||||
/// ```ignore
|
||||
@@ -176,7 +176,7 @@ fn struct_def(
|
||||
}).collect::<TokenStream2>();
|
||||
|
||||
Ok(quote! (
|
||||
/// A struct to encode a Phragmen assignment in a compact way.
|
||||
/// A struct to encode a election assignment in a compact way.
|
||||
#[derive(
|
||||
Default,
|
||||
PartialEq,
|
||||
@@ -224,9 +224,9 @@ fn struct_def(
|
||||
}
|
||||
|
||||
fn imports() -> Result<TokenStream2> {
|
||||
let sp_phragmen_imports = match crate_name("sp-phragmen") {
|
||||
Ok(sp_phragmen) => {
|
||||
let ident = syn::Ident::new(&sp_phragmen, Span::call_site());
|
||||
let sp_phragmen_imports = match crate_name("sp-npos-elections") {
|
||||
Ok(sp_npos_elections) => {
|
||||
let ident = syn::Ident::new(&sp_npos_elections, Span::call_site());
|
||||
quote!( extern crate #ident as _phragmen; )
|
||||
}
|
||||
Err(e) => return Err(syn::Error::new(Span::call_site(), &e)),
|
||||
+3
-1
@@ -57,7 +57,9 @@ fn from_impl(count: usize) -> TokenStream2 {
|
||||
let last = quote!(index_of_target(&distribution[#last_index].0).ok_or(_phragmen::Error::CompactInvalidIndex)?);
|
||||
|
||||
quote!(
|
||||
#c => compact.#field_name.push((index_of_voter(&who).ok_or(_phragmen::Error::CompactInvalidIndex)?, [#inner], #last)),
|
||||
#c => compact.#field_name.push(
|
||||
(index_of_voter(&who).ok_or(_phragmen::Error::CompactInvalidIndex)?, [#inner], #last)
|
||||
),
|
||||
)
|
||||
}).collect::<TokenStream2>();
|
||||
|
||||
+5
-5
@@ -1234,19 +1234,19 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-phragmen"
|
||||
name = "sp-npos-elections"
|
||||
version = "2.0.0-alpha.3"
|
||||
dependencies = [
|
||||
"parity-scale-codec",
|
||||
"serde",
|
||||
"sp-core",
|
||||
"sp-phragmen-compact",
|
||||
"sp-npos-elections-compact",
|
||||
"sp-runtime",
|
||||
"sp-std",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-phragmen-compact"
|
||||
name = "sp-npos-elections-compact"
|
||||
version = "2.0.0-rc2"
|
||||
dependencies = [
|
||||
"proc-macro-crate",
|
||||
@@ -1256,12 +1256,12 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sp-phragmen-fuzzer"
|
||||
name = "sp-npos-elections-fuzzer"
|
||||
version = "2.0.0"
|
||||
dependencies = [
|
||||
"honggfuzz",
|
||||
"rand 0.7.3",
|
||||
"sp-phragmen",
|
||||
"sp-npos-elections",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "sp-phragmen-fuzzer"
|
||||
name = "sp-npos-elections-fuzzer"
|
||||
version = "2.0.0-alpha.5"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
@@ -7,14 +7,14 @@ license = "Apache-2.0"
|
||||
homepage = "https://substrate.dev"
|
||||
repository = "https://github.com/paritytech/substrate/"
|
||||
description = "Fuzzer for phragmén implementation."
|
||||
documentation = "https://docs.rs/sp-phragmen-fuzzer"
|
||||
documentation = "https://docs.rs/sp-npos-elections-fuzzer"
|
||||
publish = false
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
sp-phragmen = { version = "2.0.0-rc2", path = ".." }
|
||||
sp-npos-elections = { version = "2.0.0-rc2", path = ".." }
|
||||
sp-std = { version = "2.0.0-rc2", path = "../../std" }
|
||||
sp-runtime = { version = "2.0.0-rc2", path = "../../runtime" }
|
||||
honggfuzz = "0.5"
|
||||
@@ -25,5 +25,5 @@ name = "reduce"
|
||||
path = "src/reduce.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "equalize"
|
||||
path = "src/equalize.rs"
|
||||
name = "balance_solution"
|
||||
path = "src/balance_solution.rs"
|
||||
+16
-9
@@ -15,7 +15,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Fuzzing fro the equalize algorithm
|
||||
//! Fuzzing fro the balance_solution algorithm
|
||||
//!
|
||||
//! It ensures that any solution which gets equalized will lead into a better or equally scored
|
||||
//! one.
|
||||
@@ -23,9 +23,9 @@
|
||||
mod common;
|
||||
use common::to_range;
|
||||
use honggfuzz::fuzz;
|
||||
use sp_phragmen::{
|
||||
equalize, assignment_ratio_to_staked, build_support_map, to_without_backing, elect,
|
||||
PhragmenResult, VoteWeight, evaluate_support, is_score_better,
|
||||
use sp_npos_elections::{
|
||||
balance_solution, assignment_ratio_to_staked, build_support_map, to_without_backing, seq_phragmen,
|
||||
ElectionResult, VoteWeight, evaluate_support, is_score_better,
|
||||
};
|
||||
use sp_std::collections::btree_map::BTreeMap;
|
||||
use sp_runtime::Perbill;
|
||||
@@ -39,7 +39,7 @@ fn generate_random_phragmen_result(
|
||||
to_elect: usize,
|
||||
edge_per_voter: u64,
|
||||
mut rng: impl RngCore,
|
||||
) -> (PhragmenResult<AccountId, Perbill>, BTreeMap<AccountId, VoteWeight>) {
|
||||
) -> (ElectionResult<AccountId, Perbill>, BTreeMap<AccountId, VoteWeight>) {
|
||||
let prefix = 100_000;
|
||||
// Note, it is important that stakes are always bigger than ed and
|
||||
let base_stake: u64 = 1_000_000_000;
|
||||
@@ -73,7 +73,7 @@ fn generate_random_phragmen_result(
|
||||
});
|
||||
|
||||
(
|
||||
elect::<AccountId, sp_runtime::Perbill>(
|
||||
seq_phragmen::<AccountId, sp_runtime::Perbill>(
|
||||
to_elect,
|
||||
0,
|
||||
candidates,
|
||||
@@ -86,7 +86,14 @@ fn generate_random_phragmen_result(
|
||||
fn main() {
|
||||
loop {
|
||||
fuzz!(|data: (usize, usize, usize, usize, usize, u64)| {
|
||||
let (mut target_count, mut voter_count, mut iterations, mut edge_per_voter, mut to_elect, seed) = data;
|
||||
let (
|
||||
mut target_count,
|
||||
mut voter_count,
|
||||
mut iterations,
|
||||
mut edge_per_voter,
|
||||
mut to_elect,
|
||||
seed,
|
||||
) = data;
|
||||
let rng = rand::rngs::SmallRng::seed_from_u64(seed);
|
||||
target_count = to_range(target_count, 50, 2000);
|
||||
voter_count = to_range(voter_count, 50, 1000);
|
||||
@@ -95,7 +102,7 @@ fn main() {
|
||||
edge_per_voter = to_range(edge_per_voter, 1, target_count);
|
||||
|
||||
println!("++ [{} / {} / {} / {}]", voter_count, target_count, to_elect, iterations);
|
||||
let (PhragmenResult { winners, assignments }, stake_of_tree) = generate_random_phragmen_result(
|
||||
let (ElectionResult { winners, assignments }, stake_of_tree) = generate_random_phragmen_result(
|
||||
voter_count as u64,
|
||||
target_count as u64,
|
||||
to_elect,
|
||||
@@ -117,7 +124,7 @@ fn main() {
|
||||
return;
|
||||
}
|
||||
|
||||
let i = equalize(
|
||||
let i = balance_solution(
|
||||
&mut staked,
|
||||
&mut support,
|
||||
10,
|
||||
+1
-1
@@ -34,7 +34,7 @@ use honggfuzz::fuzz;
|
||||
|
||||
mod common;
|
||||
use common::to_range;
|
||||
use sp_phragmen::{StakedAssignment, ExtendedBalance, build_support_map, reduce};
|
||||
use sp_npos_elections::{StakedAssignment, ExtendedBalance, build_support_map, reduce};
|
||||
use rand::{self, Rng, SeedableRng, RngCore};
|
||||
|
||||
type Balance = u128;
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Helper methods for phragmen.
|
||||
//! Helper methods for npos-elections.
|
||||
|
||||
use crate::{Assignment, ExtendedBalance, VoteWeight, IdentifierT, StakedAssignment, WithApprovalOf};
|
||||
use sp_arithmetic::PerThing;
|
||||
+29
-36
@@ -15,22 +15,16 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Rust implementation of the Phragmén election algorithm. This is used in several pallets to
|
||||
//! optimally distribute the weight of a set of voters among an elected set of candidates. In the
|
||||
//! context of staking this is mapped to validators and nominators.
|
||||
//! A set of election algorithms to be used with a substrate runtime, typically within the staking
|
||||
//! sub-system. Notable implementation include
|
||||
//!
|
||||
//! The algorithm has two phases:
|
||||
//! - Sequential phragmen: performed in [`elect`] function which is first pass of the distribution
|
||||
//! The results are not optimal but the execution time is less.
|
||||
//! - Equalize post-processing: tries to further distribute the weight fairly among candidates.
|
||||
//! Incurs more execution time.
|
||||
//! - [`seq_phragmen`]: Implements the Phragmén Sequential Method. An un-ranked, relatively fast
|
||||
//! election method that ensures PJR, but does not provide a constant factor approximation of the
|
||||
//! maximin problem.
|
||||
//! - [`balance_solution`]: Implements the star balancing algorithm. This iterative process can
|
||||
//! increase a solutions score, as described in [`evaluate_support`].
|
||||
//!
|
||||
//! The main objective of the assignments done by phragmen is to maximize the minimum backed
|
||||
//! candidate in the elected set.
|
||||
//!
|
||||
//! Reference implementation: https://github.com/w3f/consensus
|
||||
//! Further details:
|
||||
//! https://research.web3.foundation/en/latest/polkadot/NPoS/4.%20Sequential%20Phragm%C3%A9n%E2%80%99s%20method/
|
||||
//! More information can be found at: https://arxiv.org/abs/2004.12990
|
||||
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
@@ -67,7 +61,7 @@ pub use codec;
|
||||
pub use sp_arithmetic;
|
||||
|
||||
// re-export the compact solution type.
|
||||
pub use sp_phragmen_compact::generate_compact_solution_type;
|
||||
pub use sp_npos_elections_compact::generate_compact_solution_type;
|
||||
|
||||
/// A trait to limit the number of votes per voter. The generated compact type will implement this.
|
||||
pub trait VotingLimit {
|
||||
@@ -100,7 +94,7 @@ pub type VoteWeight = u64;
|
||||
pub type ExtendedBalance = u128;
|
||||
|
||||
/// The score of an assignment. This can be computed from the support map via [`evaluate_support`].
|
||||
pub type PhragmenScore = [ExtendedBalance; 3];
|
||||
pub type ElectionScore = [ExtendedBalance; 3];
|
||||
|
||||
/// A winner, with their respective approval stake.
|
||||
pub type WithApprovalOf<A> = (A, ExtendedBalance);
|
||||
@@ -110,7 +104,7 @@ pub type WithApprovalOf<A> = (A, ExtendedBalance);
|
||||
/// bigger than u64::max_value() is needed. For maximum accuracy we simply use u128;
|
||||
const DEN: u128 = u128::max_value();
|
||||
|
||||
/// A candidate entity for phragmen election.
|
||||
/// A candidate entity for the election.
|
||||
#[derive(Clone, Default, Debug)]
|
||||
struct Candidate<AccountId> {
|
||||
/// Identifier.
|
||||
@@ -147,9 +141,9 @@ struct Edge<AccountId> {
|
||||
candidate_index: usize,
|
||||
}
|
||||
|
||||
/// Final result of the phragmen election.
|
||||
/// Final result of the election.
|
||||
#[derive(Debug)]
|
||||
pub struct PhragmenResult<AccountId, T: PerThing> {
|
||||
pub struct ElectionResult<AccountId, T: PerThing> {
|
||||
/// Just winners zipped with their approval stake. Note that the approval stake is merely the
|
||||
/// sub of their received stake and could be used for very basic sorting and approval voting.
|
||||
pub winners: Vec<WithApprovalOf<AccountId>>,
|
||||
@@ -298,10 +292,10 @@ impl<AccountId> StakedAssignment<AccountId> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A structure to demonstrate the phragmen result from the perspective of the candidate, i.e. how
|
||||
/// A structure to demonstrate the election result from the perspective of the candidate, i.e. how
|
||||
/// much support each candidate is receiving.
|
||||
///
|
||||
/// This complements the [`PhragmenResult`] and is needed to run the equalize post-processing.
|
||||
/// This complements the [`ElectionResult`] and is needed to run the balancing post-processing.
|
||||
///
|
||||
/// This, at the current version, resembles the `Exposure` defined in the Staking pallet, yet
|
||||
/// they do not necessarily have to be the same.
|
||||
@@ -332,12 +326,12 @@ pub type SupportMap<A> = BTreeMap<A, Support<A>>;
|
||||
/// responsibility of the caller to make sure only those candidates who have a sensible economic
|
||||
/// value are passed in. From the perspective of this function, a candidate can easily be among the
|
||||
/// winner with no backing stake.
|
||||
pub fn elect<AccountId, R>(
|
||||
pub fn seq_phragmen<AccountId, R>(
|
||||
candidate_count: usize,
|
||||
minimum_candidate_count: usize,
|
||||
initial_candidates: Vec<AccountId>,
|
||||
initial_voters: Vec<(AccountId, VoteWeight, Vec<AccountId>)>,
|
||||
) -> Option<PhragmenResult<AccountId, R>> where
|
||||
) -> Option<ElectionResult<AccountId, R>> where
|
||||
AccountId: Default + Ord + Clone,
|
||||
R: PerThing,
|
||||
{
|
||||
@@ -388,7 +382,6 @@ pub fn elect<AccountId, R>(
|
||||
|
||||
|
||||
// we have already checked that we have more candidates than minimum_candidate_count.
|
||||
// run phragmen.
|
||||
let to_elect = candidate_count.min(candidates.len());
|
||||
elected_candidates = Vec::with_capacity(candidate_count);
|
||||
assigned = Vec::with_capacity(candidate_count);
|
||||
@@ -524,13 +517,13 @@ pub fn elect<AccountId, R>(
|
||||
}
|
||||
}
|
||||
|
||||
Some(PhragmenResult {
|
||||
Some(ElectionResult {
|
||||
winners: elected_candidates,
|
||||
assignments: assigned,
|
||||
})
|
||||
}
|
||||
|
||||
/// Build the support map from the given phragmen result. It maps a flat structure like
|
||||
/// Build the support map from the given election result. It maps a flat structure like
|
||||
///
|
||||
/// ```nocompile
|
||||
/// assignments: vec![
|
||||
@@ -588,7 +581,7 @@ pub fn build_support_map<AccountId>(
|
||||
(supports, errors)
|
||||
}
|
||||
|
||||
/// Evaluate a phragmen result, given the support map. The returned tuple contains:
|
||||
/// Evaluate a support map. The returned tuple contains:
|
||||
///
|
||||
/// - Minimum support. This value must be **maximized**.
|
||||
/// - Sum of all supports. This value must be **maximized**.
|
||||
@@ -597,7 +590,7 @@ pub fn build_support_map<AccountId>(
|
||||
/// `O(E)` where `E` is the total number of edges.
|
||||
pub fn evaluate_support<AccountId>(
|
||||
support: &SupportMap<AccountId>,
|
||||
) -> PhragmenScore {
|
||||
) -> ElectionScore {
|
||||
let mut min_support = ExtendedBalance::max_value();
|
||||
let mut sum: ExtendedBalance = Zero::zero();
|
||||
// NOTE: The third element might saturate but fine for now since this will run on-chain and need
|
||||
@@ -614,14 +607,14 @@ pub fn evaluate_support<AccountId>(
|
||||
[min_support, sum, sum_squared]
|
||||
}
|
||||
|
||||
/// Compares two sets of phragmen scores based on desirability and returns true if `this` is
|
||||
/// Compares two sets of election scores based on desirability and returns true if `this` is
|
||||
/// better than `that`.
|
||||
///
|
||||
/// Evaluation is done in a lexicographic manner, and if each element of `this` is `that * epsilon`
|
||||
/// greater or less than `that`.
|
||||
///
|
||||
/// Note that the third component should be minimized.
|
||||
pub fn is_score_better<P: PerThing>(this: PhragmenScore, that: PhragmenScore, epsilon: P) -> bool
|
||||
pub fn is_score_better<P: PerThing>(this: ElectionScore, that: ElectionScore, epsilon: P) -> bool
|
||||
where ExtendedBalance: From<sp_arithmetic::InnerOf<P>>
|
||||
{
|
||||
match this
|
||||
@@ -648,17 +641,17 @@ pub fn is_score_better<P: PerThing>(this: PhragmenScore, that: PhragmenScore, ep
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs equalize post-processing to the output of the election algorithm. This happens in
|
||||
/// Performs balancing post-processing to the output of the election algorithm. This happens in
|
||||
/// rounds. The number of rounds and the maximum diff-per-round tolerance can be tuned through input
|
||||
/// parameters.
|
||||
///
|
||||
/// Returns the number of iterations that were preformed.
|
||||
///
|
||||
/// - `assignments`: exactly the same is the output of phragmen.
|
||||
/// - `assignments`: exactly the same as the output of [`seq_phragmen`].
|
||||
/// - `supports`: mutable reference to s `SupportMap`. This parameter is updated.
|
||||
/// - `tolerance`: maximum difference that can occur before an early quite happens.
|
||||
/// - `iterations`: maximum number of iterations that will be processed.
|
||||
pub fn equalize<AccountId>(
|
||||
pub fn balance_solution<AccountId>(
|
||||
assignments: &mut Vec<StakedAssignment<AccountId>>,
|
||||
supports: &mut SupportMap<AccountId>,
|
||||
tolerance: ExtendedBalance,
|
||||
@@ -672,7 +665,7 @@ pub fn equalize<AccountId>(
|
||||
for assignment in assignments.iter_mut() {
|
||||
let voter_budget = assignment.total();
|
||||
let StakedAssignment { who, distribution } = assignment;
|
||||
let diff = do_equalize(
|
||||
let diff = do_balancing(
|
||||
who,
|
||||
voter_budget,
|
||||
distribution,
|
||||
@@ -689,9 +682,9 @@ pub fn equalize<AccountId>(
|
||||
}
|
||||
}
|
||||
|
||||
/// actually perform equalize. same interface is `equalize`. Just called in loops with a check for
|
||||
/// actually perform balancing. same interface is `balance_solution`. Just called in loops with a check for
|
||||
/// maximum difference.
|
||||
fn do_equalize<AccountId>(
|
||||
fn do_balancing<AccountId>(
|
||||
voter: &AccountId,
|
||||
budget: ExtendedBalance,
|
||||
elected_edges: &mut Vec<(AccountId, ExtendedBalance)>,
|
||||
+3
-3
@@ -15,11 +15,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Mock file for phragmen.
|
||||
//! Mock file for npos-elections.
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use crate::{elect, PhragmenResult, Assignment, VoteWeight, ExtendedBalance};
|
||||
use crate::{seq_phragmen, ElectionResult, Assignment, VoteWeight, ExtendedBalance};
|
||||
use sp_arithmetic::{PerThing, traits::{SaturatedConversion, Zero, One}};
|
||||
use sp_std::collections::btree_map::BTreeMap;
|
||||
use sp_runtime::assert_eq_error_rate;
|
||||
@@ -326,7 +326,7 @@ pub(crate) fn run_and_compare<Output: PerThing>(
|
||||
min_to_elect: usize,
|
||||
) {
|
||||
// run fixed point code.
|
||||
let PhragmenResult { winners, assignments } = elect::<_, Output>(
|
||||
let ElectionResult { winners, assignments } = seq_phragmen::<_, Output>(
|
||||
to_elect,
|
||||
min_to_elect,
|
||||
candidates.clone(),
|
||||
+21
-18
@@ -15,14 +15,14 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Tests for phragmen.
|
||||
//! Tests for npos-elections.
|
||||
|
||||
#![cfg(test)]
|
||||
|
||||
use crate::mock::*;
|
||||
use crate::{
|
||||
elect, equalize, build_support_map, is_score_better, helpers::*,
|
||||
Support, StakedAssignment, Assignment, PhragmenResult, ExtendedBalance,
|
||||
seq_phragmen, balance_solution, build_support_map, is_score_better, helpers::*,
|
||||
Support, StakedAssignment, Assignment, ElectionResult, ExtendedBalance,
|
||||
};
|
||||
use substrate_test_utils::assert_eq_uvec;
|
||||
use sp_arithmetic::{Perbill, Permill, Percent, PerU16};
|
||||
@@ -83,7 +83,7 @@ fn phragmen_poc_works() {
|
||||
];
|
||||
|
||||
let stake_of = create_stake_of(&[(10, 10), (20, 20), (30, 30)]);
|
||||
let PhragmenResult { winners, assignments } = elect::<_, Perbill>(
|
||||
let ElectionResult { winners, assignments } = seq_phragmen::<_, Perbill>(
|
||||
2,
|
||||
2,
|
||||
candidates,
|
||||
@@ -146,7 +146,7 @@ fn phragmen_poc_works() {
|
||||
Support::<AccountId> { total: 35, voters: vec![(20, 20), (30, 15)] },
|
||||
);
|
||||
|
||||
equalize(
|
||||
balance_solution(
|
||||
&mut staked,
|
||||
&mut support_map,
|
||||
0,
|
||||
@@ -240,11 +240,14 @@ fn phragmen_accuracy_on_large_scale_only_validators() {
|
||||
(5, (u64::max_value() - 2).into()),
|
||||
]);
|
||||
|
||||
let PhragmenResult { winners, assignments } = elect::<_, Perbill>(
|
||||
let ElectionResult { winners, assignments } = seq_phragmen::<_, Perbill>(
|
||||
2,
|
||||
2,
|
||||
candidates.clone(),
|
||||
auto_generate_self_voters(&candidates).iter().map(|(ref v, ref vs)| (v.clone(), stake_of(v), vs.clone())).collect::<Vec<_>>(),
|
||||
auto_generate_self_voters(&candidates)
|
||||
.iter()
|
||||
.map(|(ref v, ref vs)| (v.clone(), stake_of(v), vs.clone()))
|
||||
.collect::<Vec<_>>(),
|
||||
).unwrap();
|
||||
|
||||
assert_eq_uvec!(winners, vec![(1, 18446744073709551614u128), (5, 18446744073709551613u128)]);
|
||||
@@ -270,7 +273,7 @@ fn phragmen_accuracy_on_large_scale_validators_and_nominators() {
|
||||
(14, u64::max_value().into()),
|
||||
]);
|
||||
|
||||
let PhragmenResult { winners, assignments } = elect::<_, Perbill>(
|
||||
let ElectionResult { winners, assignments } = seq_phragmen::<_, Perbill>(
|
||||
2,
|
||||
2,
|
||||
candidates,
|
||||
@@ -313,7 +316,7 @@ fn phragmen_accuracy_on_small_scale_self_vote() {
|
||||
(30, 1),
|
||||
]);
|
||||
|
||||
let PhragmenResult { winners, assignments: _ } = elect::<_, Perbill>(
|
||||
let ElectionResult { winners, assignments: _ } = seq_phragmen::<_, Perbill>(
|
||||
3,
|
||||
3,
|
||||
candidates,
|
||||
@@ -343,7 +346,7 @@ fn phragmen_accuracy_on_small_scale_no_self_vote() {
|
||||
(3, 1),
|
||||
]);
|
||||
|
||||
let PhragmenResult { winners, assignments: _ } = elect::<_, Perbill>(
|
||||
let ElectionResult { winners, assignments: _ } = seq_phragmen::<_, Perbill>(
|
||||
3,
|
||||
3,
|
||||
candidates,
|
||||
@@ -376,7 +379,7 @@ fn phragmen_large_scale_test() {
|
||||
(50, 990000000000000000),
|
||||
]);
|
||||
|
||||
let PhragmenResult { winners, assignments } = elect::<_, Perbill>(
|
||||
let ElectionResult { winners, assignments } = seq_phragmen::<_, Perbill>(
|
||||
2,
|
||||
2,
|
||||
candidates,
|
||||
@@ -402,7 +405,7 @@ fn phragmen_large_scale_test_2() {
|
||||
(50, nom_budget.into()),
|
||||
]);
|
||||
|
||||
let PhragmenResult { winners, assignments } = elect::<_, Perbill>(
|
||||
let ElectionResult { winners, assignments } = seq_phragmen::<_, Perbill>(
|
||||
2,
|
||||
2,
|
||||
candidates,
|
||||
@@ -478,7 +481,7 @@ fn elect_has_no_entry_barrier() {
|
||||
(2, 10),
|
||||
]);
|
||||
|
||||
let PhragmenResult { winners, assignments: _ } = elect::<_, Perbill>(
|
||||
let ElectionResult { winners, assignments: _ } = seq_phragmen::<_, Perbill>(
|
||||
3,
|
||||
3,
|
||||
candidates,
|
||||
@@ -505,7 +508,7 @@ fn minimum_to_elect_is_respected() {
|
||||
(2, 10),
|
||||
]);
|
||||
|
||||
let maybe_result = elect::<_, Perbill>(
|
||||
let maybe_result = seq_phragmen::<_, Perbill>(
|
||||
10,
|
||||
10,
|
||||
candidates,
|
||||
@@ -531,7 +534,7 @@ fn self_votes_should_be_kept() {
|
||||
(1, 8),
|
||||
]);
|
||||
|
||||
let result = elect::<_, Perbill>(
|
||||
let result = seq_phragmen::<_, Perbill>(
|
||||
2,
|
||||
2,
|
||||
candidates,
|
||||
@@ -570,7 +573,7 @@ fn self_votes_should_be_kept() {
|
||||
&Support { total: 24u128, voters: vec![(20u64, 20u128), (1u64, 4u128)] },
|
||||
);
|
||||
|
||||
equalize(
|
||||
balance_solution(
|
||||
&mut staked_assignments,
|
||||
&mut supports,
|
||||
0,
|
||||
@@ -771,8 +774,8 @@ mod compact {
|
||||
use codec::{Decode, Encode};
|
||||
use crate::{generate_compact_solution_type, VoteWeight};
|
||||
use super::{AccountId};
|
||||
// these need to come from the same dev-dependency `sp-phragmen`, not from the crate.
|
||||
use sp_phragmen::{Assignment, StakedAssignment, Error as PhragmenError, ExtendedBalance};
|
||||
// these need to come from the same dev-dependency `sp-npos-elections`, not from the crate.
|
||||
use sp_npos_elections::{Assignment, StakedAssignment, Error as PhragmenError, ExtendedBalance};
|
||||
use sp_std::{convert::{TryInto, TryFrom}, fmt::Debug};
|
||||
use sp_arithmetic::Percent;
|
||||
|
||||
Reference in New Issue
Block a user