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//!
9999#![ cfg_attr( not( feature = "std" ) , no_std) ]
100100
101101use codec:: { Decode , Encode } ;
102+ use frame_election_provider_support:: NposSolver ;
102103use frame_support:: {
103104 traits:: {
104105 defensive_prelude:: * , ChangeMembers , Contains , ContainsLengthBound , Currency ,
@@ -111,7 +112,7 @@ use scale_info::TypeInfo;
111112use sp_npos_elections:: { ElectionResult , ExtendedBalance } ;
112113use sp_runtime:: {
113114 traits:: { Saturating , StaticLookup , Zero } ,
114- DispatchError , Perbill , RuntimeDebug ,
115+ DispatchError , RuntimeDebug ,
115116} ;
116117use 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) ]
11571168mod 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