4343//! ### Voting
4444//!
4545//! Voters can vote for a limited number of the candidates by providing a list of account ids,
46- //! bounded by [`MAXIMUM_VOTE `]. Invalid votes (voting for non-candidates) and duplicate votes are
47- //! ignored during election. Yet, a voter _might_ vote for a future candidate. Voters reserve a bond
48- //! as they vote. Each vote defines a `value`. This amount is locked from the account of the voter
49- //! and indicates the weight of the vote. Voters can update their votes at any time by calling
50- //! `vote()` again. This can update the vote targets (which might update the deposit) or update the
51- //! vote's stake ([`Voter::stake`]). After a round, votes are kept and might still be valid for
52- //! further rounds. A voter is responsible for calling `remove_voter` once they are done to have
53- //! their bond back and remove the lock.
46+ //! bounded by [`Config::MaxVotesPerVoter `]. Invalid votes (voting for non-candidates) and duplicate
47+ //! votes are ignored during election. Yet, a voter _might_ vote for a future candidate. Voters
48+ //! reserve a bond as they vote. Each vote defines a `value`. This amount is locked from the account
49+ //! of the voter and indicates the weight of the vote. Voters can update their votes at any time by
50+ //! calling `vote()` again. This can update the vote targets (which might update the deposit) or
51+ //! update the vote's stake ([`Voter::stake`]). After a round, votes are kept and might still be
52+ //! valid for further rounds. A voter is responsible for calling `remove_voter` once they are done
53+ //! to have their bond back and remove the lock.
5454//!
5555//! See [`Call::vote`], [`Call::remove_voter`].
5656//!
@@ -124,9 +124,6 @@ pub mod migrations;
124124
125125const LOG_TARGET : & str = "runtime::elections-phragmen" ;
126126
127- /// The maximum votes allowed per voter.
128- pub const MAXIMUM_VOTE : usize = 16 ;
129-
130127type BalanceOf < T > =
131128 <<T as Config >:: Currency as Currency < <T as frame_system:: Config >:: AccountId > >:: Balance ;
132129type NegativeImbalanceOf < T > = <<T as Config >:: Currency as Currency <
@@ -254,19 +251,29 @@ pub mod pallet {
254251
255252 /// The maximum number of candidates in a phragmen election.
256253 ///
257- /// Warning: The election happens onchain, and this value will determine
258- /// the size of the election. When this limit is reached no more
259- /// candidates are accepted in the election.
254+ /// Warning: This impacts the size of the election which is run onchain. Chose wisely, and
255+ /// consider how it will impact `T::WeightInfo::election_phragmen`.
256+ ///
257+ /// When this limit is reached no more candidates are accepted in the election.
260258 #[ pallet:: constant]
261259 type MaxCandidates : Get < u32 > ;
262260
263261 /// The maximum number of voters to allow in a phragmen election.
264262 ///
265- /// Warning: This impacts the size of the election which is run onchain.
263+ /// Warning: This impacts the size of the election which is run onchain. Chose wisely, and
264+ /// consider how it will impact `T::WeightInfo::election_phragmen`.
265+ ///
266266 /// When the limit is reached the new voters are ignored.
267267 #[ pallet:: constant]
268268 type MaxVoters : Get < u32 > ;
269269
270+ /// Maximum numbers of votes per voter.
271+ ///
272+ /// Warning: This impacts the size of the election which is run onchain. Chose wisely, and
273+ /// consider how it will impact `T::WeightInfo::election_phragmen`.
274+ #[ pallet:: constant]
275+ type MaxVotesPerVoter : Get < u32 > ;
276+
270277 /// Weight information for extrinsics in this pallet.
271278 type WeightInfo : WeightInfo ;
272279 }
@@ -284,6 +291,41 @@ pub mod pallet {
284291 Weight :: zero ( )
285292 }
286293 }
294+
295+ fn integrity_test ( ) {
296+ let block_weight = T :: BlockWeights :: get ( ) . max_block ;
297+ // mind the order.
298+ let election_weight = T :: WeightInfo :: election_phragmen (
299+ T :: MaxCandidates :: get ( ) ,
300+ T :: MaxVoters :: get ( ) ,
301+ T :: MaxVotesPerVoter :: get ( ) * T :: MaxVoters :: get ( ) ,
302+ ) ;
303+
304+ let to_seconds = |w : & Weight | {
305+ w. ref_time ( ) as f32 /
306+ frame_support:: weights:: constants:: WEIGHT_REF_TIME_PER_SECOND as f32
307+ } ;
308+
309+ frame_support:: log:: debug!(
310+ target: LOG_TARGET ,
311+ "election weight {}s ({:?}) // chain's block weight {}s ({:?})" ,
312+ to_seconds( & election_weight) ,
313+ election_weight,
314+ to_seconds( & block_weight) ,
315+ block_weight,
316+ ) ;
317+ assert ! (
318+ election_weight. all_lt( block_weight) ,
319+ "election weight {}s ({:?}) will exceed a {}s chain's block weight ({:?}) (MaxCandidates {}, MaxVoters {}, MaxVotesPerVoter {} -- tweak these parameters)" ,
320+ election_weight,
321+ to_seconds( & election_weight) ,
322+ to_seconds( & block_weight) ,
323+ block_weight,
324+ T :: MaxCandidates :: get( ) ,
325+ T :: MaxVoters :: get( ) ,
326+ T :: MaxVotesPerVoter :: get( ) ,
327+ ) ;
328+ }
287329 }
288330
289331 #[ pallet:: call]
@@ -307,10 +349,6 @@ pub mod pallet {
307349 ///
308350 /// It is the responsibility of the caller to **NOT** place all of their balance into the
309351 /// lock and keep some for further operations.
310- ///
311- /// # <weight>
312- /// We assume the maximum weight among all 3 cases: vote_equal, vote_more and vote_less.
313- /// # </weight>
314352 #[ pallet:: call_index( 0 ) ]
315353 #[ pallet:: weight(
316354 T :: WeightInfo :: vote_more( votes. len( ) as u32 )
@@ -324,8 +362,10 @@ pub mod pallet {
324362 ) -> DispatchResultWithPostInfo {
325363 let who = ensure_signed ( origin) ?;
326364
327- // votes should not be empty and more than `MAXIMUM_VOTE` in any case.
328- ensure ! ( votes. len( ) <= MAXIMUM_VOTE , Error :: <T >:: MaximumVotesExceeded ) ;
365+ ensure ! (
366+ votes. len( ) <= T :: MaxVotesPerVoter :: get( ) as usize ,
367+ Error :: <T >:: MaximumVotesExceeded
368+ ) ;
329369 ensure ! ( !votes. is_empty( ) , Error :: <T >:: NoVotes ) ;
330370
331371 let candidates_count = <Candidates < T > >:: decode_len ( ) . unwrap_or ( 0 ) ;
@@ -1006,15 +1046,15 @@ impl<T: Config> Pallet<T> {
10061046 // count](https://en.wikipedia.org/wiki/Borda_count). We weigh everyone's vote for
10071047 // that new member by a multiplier based on the order of the votes. i.e. the
10081048 // first person a voter votes for gets a 16x multiplier, the next person gets a
1009- // 15x multiplier, an so on... (assuming `MAXIMUM_VOTE ` = 16)
1049+ // 15x multiplier, an so on... (assuming `T::MaxVotesPerVoter ` = 16)
10101050 let mut prime_votes = new_members_sorted_by_id
10111051 . iter ( )
10121052 . map ( |c| ( & c. 0 , BalanceOf :: < T > :: zero ( ) ) )
10131053 . collect :: < Vec < _ > > ( ) ;
10141054 for ( _, stake, votes) in voters_and_stakes. into_iter ( ) {
10151055 for ( vote_multiplier, who) in
10161056 votes. iter ( ) . enumerate ( ) . map ( |( vote_position, who) | {
1017- ( ( MAXIMUM_VOTE - vote_position) as u32 , who)
1057+ ( ( T :: MaxVotesPerVoter :: get ( ) as usize - vote_position) as u32 , who)
10181058 } ) {
10191059 if let Ok ( i) = prime_votes. binary_search_by_key ( & who, |k| k. 0 ) {
10201060 prime_votes[ i] . 1 = prime_votes[ i]
@@ -1173,16 +1213,9 @@ mod tests {
11731213 } ;
11741214 use substrate_test_utils:: assert_eq_uvec;
11751215
1176- parameter_types ! {
1177- pub BlockWeights : frame_system:: limits:: BlockWeights =
1178- frame_system:: limits:: BlockWeights :: simple_max(
1179- frame_support:: weights:: Weight :: from_ref_time( 1024 ) . set_proof_size( u64 :: MAX ) ,
1180- ) ;
1181- }
1182-
11831216 impl frame_system:: Config for Test {
11841217 type BaseCallFilter = frame_support:: traits:: Everything ;
1185- type BlockWeights = BlockWeights ;
1218+ type BlockWeights = ( ) ;
11861219 type BlockLength = ( ) ;
11871220 type DbWeight = ( ) ;
11881221 type RuntimeOrigin = RuntimeOrigin ;
@@ -1297,6 +1330,7 @@ mod tests {
12971330 type KickedMember = ( ) ;
12981331 type WeightInfo = ( ) ;
12991332 type MaxVoters = PhragmenMaxVoters ;
1333+ type MaxVotesPerVoter = ConstU32 < 16 > ;
13001334 type MaxCandidates = PhragmenMaxCandidates ;
13011335 }
13021336
0 commit comments