Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit b714023

Browse files
committed
Abstracts elections-phragmen pallet to use NposSolver
1 parent 5fbd2a3 commit b714023

3 files changed

Lines changed: 75 additions & 50 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frame/elections-phragmen/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ frame-system = { version = "4.0.0-dev", default-features = false, path = "../sys
2424
sp-core = { version = "6.0.0", default-features = false, path = "../../primitives/core" }
2525
sp-io = { version = "6.0.0", default-features = false, path = "../../primitives/io" }
2626
sp-npos-elections = { version = "4.0.0-dev", default-features = false, path = "../../primitives/npos-elections" }
27+
frame-election-provider-support = { version = "4.0.0-dev", default-features = false, path = "../election-provider-support" }
2728
sp-runtime = { version = "6.0.0", default-features = false, path = "../../primitives/runtime" }
2829
sp-std = { version = "4.0.0", default-features = false, path = "../../primitives/std" }
2930

frame/elections-phragmen/src/lib.rs

Lines changed: 73 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@
1515
// See the License for the specific language governing permissions and
1616
// limitations under the License.
1717

18-
//! # Phragmén Election Module.
18+
//! # Generic Election Module.
1919
//!
20-
//! An election module based on sequential phragmen.
20+
//! An election module based on a generic election algorithm.
2121
//!
2222
//! ### Term and Round
2323
//!
@@ -99,6 +99,7 @@
9999
#![cfg_attr(not(feature = "std"), no_std)]
100100

101101
use codec::{Decode, Encode};
102+
use frame_election_provider_support::NposSolver;
102103
use frame_support::{
103104
traits::{
104105
defensive_prelude::*, ChangeMembers, Contains, ContainsLengthBound, Currency,
@@ -111,7 +112,7 @@ use scale_info::TypeInfo;
111112
use sp_npos_elections::{ElectionResult, ExtendedBalance};
112113
use sp_runtime::{
113114
traits::{Saturating, StaticLookup, Zero},
114-
DispatchError, Perbill, RuntimeDebug,
115+
DispatchError, RuntimeDebug,
115116
};
116117
use sp_std::{cmp::Ordering, prelude::*};
117118

@@ -197,7 +198,7 @@ pub mod pallet {
197198
pub trait Config: frame_system::Config {
198199
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
199200

200-
/// Identifier for the elections-phragmen pallet's lock
201+
/// Identifier for the elections pallet's lock
201202
#[pallet::constant]
202203
type PalletId: Get<LockIdentifier>;
203204

@@ -250,21 +251,27 @@ pub mod pallet {
250251
#[pallet::constant]
251252
type TermDuration: Get<Self::BlockNumber>;
252253

253-
/// The maximum number of candidates in a phragmen election.
254+
/// The maximum number of candidates in an election.
254255
///
255256
/// Warning: The election happens onchain, and this value will determine
256257
/// the size of the election. When this limit is reached no more
257258
/// candidates are accepted in the election.
258259
#[pallet::constant]
259260
type MaxCandidates: Get<u32>;
260261

261-
/// The maximum number of voters to allow in a phragmen election.
262+
/// The maximum number of voters to allow in an election.
262263
///
263264
/// Warning: This impacts the size of the election which is run onchain.
264265
/// When the limit is reached the new voters are ignored.
265266
#[pallet::constant]
266267
type MaxVoters: Get<u32>;
267268

269+
/// Something that will calculate the result of elections.
270+
type ElectionSolver: NposSolver<AccountId = Self::AccountId>;
271+
272+
/// Weight information for the `ElectionSolver`.
273+
type SolverWeightInfo: frame_election_provider_support::WeightInfo;
274+
268275
/// Weight information for extrinsics in this pallet.
269276
type WeightInfo: WeightInfo;
270277
}
@@ -277,7 +284,7 @@ pub mod pallet {
277284
fn on_initialize(n: T::BlockNumber) -> Weight {
278285
let term_duration = T::TermDuration::get();
279286
if !term_duration.is_zero() && (n % term_duration).is_zero() {
280-
Self::do_phragmen()
287+
Self::do_election()
281288
} else {
282289
Weight::zero()
283290
}
@@ -486,8 +493,8 @@ pub mod pallet {
486493
/// the outgoing member is slashed.
487494
///
488495
/// If a runner-up is available, then the best runner-up will be removed and replaces the
489-
/// outgoing member. Otherwise, if `rerun_election` is `true`, a new phragmen election is
490-
/// started, else, nothing happens.
496+
/// outgoing member. Otherwise, if `rerun_election` is `true`, a new election is started,
497+
/// else, nothing happens.
491498
///
492499
/// If `slash_bond` is set to true, the bond of the member being removed is slashed. Else,
493500
/// it is returned.
@@ -498,7 +505,7 @@ pub mod pallet {
498505
///
499506
/// # <weight>
500507
/// If we have a replacement, we use a small weight. Else, since this is a root call and
501-
/// will go into phragmen, we assume full block for now.
508+
/// will go into the `NposSolver` election, we assume full block for now.
502509
/// # </weight>
503510
#[pallet::weight(if *rerun_election {
504511
T::WeightInfo::remove_member_without_replacement()
@@ -518,7 +525,7 @@ pub mod pallet {
518525
Self::deposit_event(Event::MemberKicked { member: who });
519526

520527
if rerun_election {
521-
Self::do_phragmen();
528+
Self::do_election();
522529
}
523530

524531
// no refund needed.
@@ -695,7 +702,7 @@ pub mod pallet {
695702
Members::<T>::mutate(|members| {
696703
match members.binary_search_by(|m| m.who.cmp(member)) {
697704
Ok(_) => {
698-
panic!("Duplicate member in elections-phragmen genesis: {}", member)
705+
panic!("Duplicate member in elections genesis: {}", member)
699706
},
700707
Err(pos) => members.insert(
701708
pos,
@@ -784,7 +791,7 @@ impl<T: Config> Pallet<T> {
784791
// overlap. This can never happen. If so, it seems like our intended replacement
785792
// is already a member, so not much more to do.
786793
log::error!(
787-
target: "runtime::elections-phragmen",
794+
target: "runtime::elections",
788795
"A member seems to also be a runner-up.",
789796
);
790797
}
@@ -890,11 +897,12 @@ impl<T: Config> Pallet<T> {
890897
debug_assert!(_remainder.is_zero());
891898
}
892899

893-
/// Run the phragmen election with all required side processes and state updates, if election
894-
/// succeeds. Else, it will emit an `ElectionError` event.
900+
/// Run an election with all required side processes and state updates, if election
901+
/// succeeds. Else, it will emit an `ElectionError` event. The election algorithm is defined
902+
/// by the implementor of `Self::NposSolver`.
895903
///
896904
/// Calls the appropriate [`ChangeMembers`] function variant internally.
897-
fn do_phragmen() -> Weight {
905+
fn do_election() -> Weight {
898906
let desired_seats = T::DesiredMembers::get() as usize;
899907
let desired_runners_up = T::DesiredRunnersUp::get() as usize;
900908
let num_to_elect = desired_runners_up + desired_seats;
@@ -908,7 +916,7 @@ impl<T: Config> Pallet<T> {
908916
return T::DbWeight::get().reads(3)
909917
}
910918

911-
// All of the new winners that come out of phragmen will thus have a deposit recorded.
919+
// All of the new winners that come out of the election will thus have a deposit recorded.
912920
let candidate_ids =
913921
candidates_and_deposit.iter().map(|(x, _)| x).cloned().collect::<Vec<_>>();
914922

@@ -933,15 +941,15 @@ impl<T: Config> Pallet<T> {
933941
Ok(_) => (),
934942
Err(_) => {
935943
log::error!(
936-
target: "runtime::elections-phragmen",
944+
target: "runtime::elections",
937945
"Failed to run election. Number of voters exceeded",
938946
);
939947
Self::deposit_event(Event::ElectionError);
940948
return T::DbWeight::get().reads(3 + max_voters as u64)
941949
},
942950
}
943951

944-
// used for phragmen.
952+
// used for elections.
945953
let voters_and_votes = voters_and_stakes
946954
.iter()
947955
.cloned()
@@ -951,12 +959,14 @@ impl<T: Config> Pallet<T> {
951959
})
952960
.collect::<Vec<_>>();
953961

954-
let weight_candidates = candidates_and_deposit.len() as u32;
955-
let weight_voters = voters_and_votes.len() as u32;
956-
let weight_edges = num_edges;
957-
let _ =
958-
sp_npos_elections::seq_phragmen(num_to_elect, candidate_ids, voters_and_votes, None)
959-
.map(|ElectionResult::<T::AccountId, Perbill> { winners, assignments: _ }| {
962+
let num_candidates = candidates_and_deposit.len() as u32;
963+
let num_voters = voters_and_votes.len() as u32;
964+
let _ = T::ElectionSolver::solve(num_to_elect, candidate_ids, voters_and_votes)
965+
.map(
966+
|ElectionResult::<T::AccountId, <T::ElectionSolver as NposSolver>::Accuracy> {
967+
winners,
968+
assignments: _,
969+
}| {
960970
// this is already sorted by id.
961971
let old_members_ids_sorted = <Members<T>>::take()
962972
.into_iter()
@@ -1059,7 +1069,7 @@ impl<T: Config> Pallet<T> {
10591069
// write final values to storage.
10601070
let deposit_of_candidate = |x: &T::AccountId| -> BalanceOf<T> {
10611071
// defensive-only. This closure is used against the new members and new
1062-
// runners-up, both of which are phragmen winners and thus must have
1072+
// runners-up, both of which are election winners and thus must have
10631073
// deposit.
10641074
candidates_and_deposit
10651075
.iter()
@@ -1095,17 +1105,18 @@ impl<T: Config> Pallet<T> {
10951105

10961106
Self::deposit_event(Event::NewTerm { new_members: new_members_sorted_by_id });
10971107
<ElectionRounds<T>>::mutate(|v| *v += 1);
1098-
})
1099-
.map_err(|e| {
1100-
log::error!(
1101-
target: "runtime::elections-phragmen",
1102-
"Failed to run election [{:?}].",
1103-
e,
1104-
);
1105-
Self::deposit_event(Event::ElectionError);
1106-
});
1108+
},
1109+
)
1110+
.map_err(|e| {
1111+
log::error!(
1112+
target: "runtime::elections",
1113+
"Failed to run election [{:?}].",
1114+
e,
1115+
);
1116+
Self::deposit_event(Event::ElectionError);
1117+
});
11071118

1108-
T::WeightInfo::election_phragmen(weight_candidates, weight_voters, weight_edges)
1119+
T::ElectionSolver::weight::<T::SolverWeightInfo>(num_voters, num_candidates, num_edges)
11091120
}
11101121
}
11111122

@@ -1156,7 +1167,8 @@ impl<T: Config> ContainsLengthBound for Pallet<T> {
11561167
#[cfg(test)]
11571168
mod tests {
11581169
use super::*;
1159-
use crate as elections_phragmen;
1170+
use crate as elections;
1171+
use frame_election_provider_support::SequentialPhragmen;
11601172
use frame_support::{
11611173
assert_noop, assert_ok,
11621174
dispatch::DispatchResultWithPostInfo,
@@ -1168,7 +1180,7 @@ mod tests {
11681180
use sp_runtime::{
11691181
testing::Header,
11701182
traits::{BlakeTwo256, IdentityLookup},
1171-
BuildStorage,
1183+
BuildStorage, Perbill,
11721184
};
11731185
use substrate_test_utils::assert_eq_uvec;
11741186

@@ -1274,13 +1286,13 @@ mod tests {
12741286
}
12751287

12761288
parameter_types! {
1277-
pub const ElectionsPhragmenPalletId: LockIdentifier = *b"phrelect";
1278-
pub const PhragmenMaxVoters: u32 = 1000;
1279-
pub const PhragmenMaxCandidates: u32 = 100;
1289+
pub const ElectionsPalletId: LockIdentifier = *b"elects__";
1290+
pub const MaxVoters: u32 = 1000;
1291+
pub const MaxCandidates: u32 = 100;
12801292
}
12811293

12821294
impl Config for Test {
1283-
type PalletId = ElectionsPhragmenPalletId;
1295+
type PalletId = ElectionsPalletId;
12841296
type RuntimeEvent = RuntimeEvent;
12851297
type Currency = Balances;
12861298
type CurrencyToVote = frame_support::traits::SaturatingCurrencyToVote;
@@ -1295,8 +1307,21 @@ mod tests {
12951307
type LoserCandidate = ();
12961308
type KickedMember = ();
12971309
type WeightInfo = ();
1298-
type MaxVoters = PhragmenMaxVoters;
1299-
type MaxCandidates = PhragmenMaxCandidates;
1310+
type MaxVoters = MaxVoters;
1311+
type MaxCandidates = MaxCandidates;
1312+
type ElectionSolver = SequentialPhragmen<Self::AccountId, Perbill>;
1313+
type SolverWeightInfo = NposWeightInfo;
1314+
}
1315+
1316+
pub struct NposWeightInfo;
1317+
impl frame_election_provider_support::WeightInfo for NposWeightInfo {
1318+
fn phragmen(_v: u32, _t: u32, _d: u32) -> Weight {
1319+
Weight::zero()
1320+
}
1321+
1322+
fn phragmms(_v: u32, _t: u32, _d: u32) -> Weight {
1323+
Weight::zero()
1324+
}
13001325
}
13011326

13021327
pub type Block = sp_runtime::generic::Block<Header, UncheckedExtrinsic>;
@@ -1311,7 +1336,7 @@ mod tests {
13111336
{
13121337
System: frame_system::{Pallet, Call, Event<T>},
13131338
Balances: pallet_balances::{Pallet, Call, Event<T>, Config<T>},
1314-
Elections: elections_phragmen::{Pallet, Call, Event<T>, Config<T>},
1339+
Elections: elections::{Pallet, Call, Event<T>, Config<T>},
13151340
}
13161341
);
13171342

@@ -1372,9 +1397,7 @@ mod tests {
13721397
(6, 60 * self.balance_factor),
13731398
],
13741399
},
1375-
elections: elections_phragmen::GenesisConfig::<Test> {
1376-
members: self.genesis_members,
1377-
},
1400+
elections: elections::GenesisConfig::<Test> { members: self.genesis_members },
13781401
}
13791402
.build_storage()
13801403
.unwrap()
@@ -1432,7 +1455,7 @@ mod tests {
14321455
.get(0)
14331456
.cloned()
14341457
.map(|lock| {
1435-
assert_eq!(lock.id, ElectionsPhragmenPalletId::get());
1458+
assert_eq!(lock.id, ElectionsPalletId::get());
14361459
lock.amount
14371460
})
14381461
.unwrap_or_default()
@@ -1623,7 +1646,7 @@ mod tests {
16231646
}
16241647

16251648
#[test]
1626-
#[should_panic = "Duplicate member in elections-phragmen genesis: 2"]
1649+
#[should_panic = "Duplicate member in elections genesis: 2"]
16271650
fn genesis_members_cannot_be_duplicate() {
16281651
ExtBuilder::default()
16291652
.desired_members(3)

0 commit comments

Comments
 (0)