2323//! The pallet assumes that the amount of members stays at or below `MaxMembers` for its weight
2424//! calculations, but enforces this neither in `set_members` nor in `change_members_sorted`.
2525//!
26- //! A "prime" member may be set allowing their vote to act as the default vote in case of any
27- //! abstentions after the voting period.
26+ //! A "prime" member may be set to help determine the default vote behavior based on chain
27+ //! config. If `PreimDefaultVote` is used, the prime vote acts as the default vote in case of any
28+ //! abstentions after the voting period. If `MoreThanMajorityThenPrimeDefaultVote` is used, then
29+ //! abstentations will first follow the majority of the collective voting, and then the prime
30+ //! member.
2831//!
2932//! Voting happens through motions comprising a proposal (i.e. a curried dispatchable) plus a
3033//! number of approvals required for it to pass and be called. Motions are open for members to
@@ -71,6 +74,52 @@ pub type ProposalIndex = u32;
7174/// vote exactly once, therefore also the number of votes for any given motion.
7275pub type MemberCount = u32 ;
7376
77+ /// Default voting strategy when a member is inactive.
78+ pub trait DefaultVote {
79+ /// Get the default voting strategy, given:
80+ ///
81+ /// - Whether the prime member voted Aye.
82+ /// - Raw number of yes votes.
83+ /// - Raw number of no votes.
84+ /// - Total number of member count.
85+ fn default_vote (
86+ prime_vote : Option < bool > ,
87+ yes_votes : MemberCount ,
88+ no_votes : MemberCount ,
89+ len : MemberCount ,
90+ ) -> bool ;
91+ }
92+
93+ /// Set the prime member's vote as the default vote.
94+ pub struct PrimeDefaultVote ;
95+
96+ impl DefaultVote for PrimeDefaultVote {
97+ fn default_vote (
98+ prime_vote : Option < bool > ,
99+ _yes_votes : MemberCount ,
100+ _no_votes : MemberCount ,
101+ _len : MemberCount ,
102+ ) -> bool {
103+ prime_vote. unwrap_or ( false )
104+ }
105+ }
106+
107+ /// First see if yes vote are over majority of the whole collective. If so, set the default vote
108+ /// as yes. Otherwise, use the prime meber's vote as the default vote.
109+ pub struct MoreThanMajorityThenPrimeDefaultVote ;
110+
111+ impl DefaultVote for MoreThanMajorityThenPrimeDefaultVote {
112+ fn default_vote (
113+ prime_vote : Option < bool > ,
114+ yes_votes : MemberCount ,
115+ _no_votes : MemberCount ,
116+ len : MemberCount ,
117+ ) -> bool {
118+ let more_than_majority = yes_votes * 2 > len;
119+ more_than_majority || prime_vote. unwrap_or ( false )
120+ }
121+ }
122+
74123pub trait WeightInfo {
75124 fn set_members ( m : u32 , n : u32 , p : u32 , ) -> Weight ;
76125 fn execute ( b : u32 , m : u32 , ) -> Weight ;
@@ -110,6 +159,9 @@ pub trait Trait<I: Instance=DefaultInstance>: frame_system::Trait {
110159 /// + This pallet assumes that dependents keep to the limit without enforcing it.
111160 type MaxMembers : Get < MemberCount > ;
112161
162+ /// Default vote strategy of this collective.
163+ type DefaultVote : DefaultVote ;
164+
113165 /// Weight information for extrinsics in this pallet.
114166 type WeightInfo : WeightInfo ;
115167}
@@ -157,8 +209,7 @@ decl_storage! {
157209 pub ProposalCount get( fn proposal_count) : u32 ;
158210 /// The current members of the collective. This is stored sorted (just by value).
159211 pub Members get( fn members) : Vec <T :: AccountId >;
160- /// The member who provides the default vote for any other members that do not vote before
161- /// the timeout. If None, then no member has that privilege.
212+ /// The prime member that helps determine the default vote behavior in case of absentations.
162213 pub Prime get( fn prime) : Option <T :: AccountId >;
163214 }
164215 add_extra_genesis {
@@ -587,8 +638,10 @@ decl_module! {
587638 // Only allow actual closing of the proposal after the voting period has ended.
588639 ensure!( system:: Module :: <T >:: block_number( ) >= voting. end, Error :: <T , I >:: TooEarly ) ;
589640
590- // default to true only if there's a prime and they voted in favour.
591- let default = Self :: prime( ) . map_or( false , |who| voting. ayes. iter( ) . any( |a| a == & who) ) ;
641+ let prime_vote = Self :: prime( ) . map( |who| voting. ayes. iter( ) . any( |a| a == & who) ) ;
642+
643+ // default voting strategy.
644+ let default = T :: DefaultVote :: default_vote( prime_vote, yes_votes, no_votes, seats) ;
592645
593646 let abstentions = seats - ( yes_votes + no_votes) ;
594647 match default {
@@ -945,6 +998,17 @@ mod tests {
945998 type MotionDuration = MotionDuration ;
946999 type MaxProposals = MaxProposals ;
9471000 type MaxMembers = MaxMembers ;
1001+ type DefaultVote = PrimeDefaultVote ;
1002+ type WeightInfo = ( ) ;
1003+ }
1004+ impl Trait < Instance2 > for Test {
1005+ type Origin = Origin ;
1006+ type Proposal = Call ;
1007+ type Event = Event ;
1008+ type MotionDuration = MotionDuration ;
1009+ type MaxProposals = MaxProposals ;
1010+ type MaxMembers = MaxMembers ;
1011+ type DefaultVote = MoreThanMajorityThenPrimeDefaultVote ;
9481012 type WeightInfo = ( ) ;
9491013 }
9501014 impl Trait for Test {
@@ -954,6 +1018,7 @@ mod tests {
9541018 type MotionDuration = MotionDuration ;
9551019 type MaxProposals = MaxProposals ;
9561020 type MaxMembers = MaxMembers ;
1021+ type DefaultVote = PrimeDefaultVote ;
9571022 type WeightInfo = ( ) ;
9581023 }
9591024
@@ -968,6 +1033,7 @@ mod tests {
9681033 {
9691034 System : system:: { Module , Call , Event <T >} ,
9701035 Collective : collective:: <Instance1 >:: { Module , Call , Event <T >, Origin <T >, Config <T >} ,
1036+ CollectiveMajority : collective:: <Instance2 >:: { Module , Call , Event <T >, Origin <T >, Config <T >} ,
9711037 DefaultCollective : collective:: { Module , Call , Event <T >, Origin <T >, Config <T >} ,
9721038 }
9731039 ) ;
@@ -978,12 +1044,20 @@ mod tests {
9781044 members : vec ! [ 1 , 2 , 3 ] ,
9791045 phantom : Default :: default ( ) ,
9801046 } ) ,
1047+ collective_Instance2 : Some ( collective:: GenesisConfig {
1048+ members : vec ! [ 1 , 2 , 3 , 4 , 5 ] ,
1049+ phantom : Default :: default ( ) ,
1050+ } ) ,
9811051 collective : None ,
9821052 } . build_storage ( ) . unwrap ( ) . into ( ) ;
9831053 ext. execute_with ( || System :: set_block_number ( 1 ) ) ;
9841054 ext
9851055 }
9861056
1057+ fn make_proposal ( value : u64 ) -> Call {
1058+ Call :: System ( frame_system:: Call :: remark ( value. encode ( ) ) )
1059+ }
1060+
9871061 #[ test]
9881062 fn motions_basic_environment_works ( ) {
9891063 new_test_ext ( ) . execute_with ( || {
@@ -992,10 +1066,6 @@ mod tests {
9921066 } ) ;
9931067 }
9941068
995- fn make_proposal ( value : u64 ) -> Call {
996- Call :: System ( frame_system:: Call :: remark ( value. encode ( ) ) )
997- }
998-
9991069 #[ test]
10001070 fn close_works ( ) {
10011071 new_test_ext ( ) . execute_with ( || {
@@ -1114,6 +1184,34 @@ mod tests {
11141184 } ) ;
11151185 }
11161186
1187+ #[ test]
1188+ fn close_with_no_prime_but_majority_works ( ) {
1189+ new_test_ext ( ) . execute_with ( || {
1190+ let proposal = make_proposal ( 42 ) ;
1191+ let proposal_len: u32 = proposal. using_encoded ( |p| p. len ( ) as u32 ) ;
1192+ let proposal_weight = proposal. get_dispatch_info ( ) . weight ;
1193+ let hash = BlakeTwo256 :: hash_of ( & proposal) ;
1194+ assert_ok ! ( CollectiveMajority :: set_members( Origin :: root( ) , vec![ 1 , 2 , 3 , 4 , 5 ] , Some ( 5 ) , MaxMembers :: get( ) ) ) ;
1195+
1196+ assert_ok ! ( CollectiveMajority :: propose( Origin :: signed( 1 ) , 5 , Box :: new( proposal. clone( ) ) , proposal_len) ) ;
1197+ assert_ok ! ( CollectiveMajority :: vote( Origin :: signed( 2 ) , hash. clone( ) , 0 , true ) ) ;
1198+ assert_ok ! ( CollectiveMajority :: vote( Origin :: signed( 3 ) , hash. clone( ) , 0 , true ) ) ;
1199+
1200+ System :: set_block_number ( 4 ) ;
1201+ assert_ok ! ( CollectiveMajority :: close( Origin :: signed( 4 ) , hash. clone( ) , 0 , proposal_weight, proposal_len) ) ;
1202+
1203+ let record = |event| EventRecord { phase : Phase :: Initialization , event, topics : vec ! [ ] } ;
1204+ assert_eq ! ( System :: events( ) , vec![
1205+ record( Event :: collective_Instance2( RawEvent :: Proposed ( 1 , 0 , hash. clone( ) , 5 ) ) ) ,
1206+ record( Event :: collective_Instance2( RawEvent :: Voted ( 2 , hash. clone( ) , true , 2 , 0 ) ) ) ,
1207+ record( Event :: collective_Instance2( RawEvent :: Voted ( 3 , hash. clone( ) , true , 3 , 0 ) ) ) ,
1208+ record( Event :: collective_Instance2( RawEvent :: Closed ( hash. clone( ) , 5 , 0 ) ) ) ,
1209+ record( Event :: collective_Instance2( RawEvent :: Approved ( hash. clone( ) ) ) ) ,
1210+ record( Event :: collective_Instance2( RawEvent :: Executed ( hash. clone( ) , Err ( DispatchError :: BadOrigin ) ) ) )
1211+ ] ) ;
1212+ } ) ;
1213+ }
1214+
11171215 #[ test]
11181216 fn removal_of_old_voters_votes_works ( ) {
11191217 new_test_ext ( ) . execute_with ( || {
0 commit comments