diff --git a/substrate/frame/ranked-collective/src/lib.rs b/substrate/frame/ranked-collective/src/lib.rs index e34cf3d8df71..411fbbb3221f 100644 --- a/substrate/frame/ranked-collective/src/lib.rs +++ b/substrate/frame/ranked-collective/src/lib.rs @@ -96,14 +96,17 @@ pub type Votes = u32; #[codec(mel_bound())] pub struct Tally { bare_ayes: MemberIndex, + out_of_rank_ayes: MemberIndex, + out_of_rank_nays: MemberIndex, ayes: Votes, nays: Votes, dummy: PhantomData<(T, I, M)>, + } impl, I: 'static, M: GetMaxVoters> Tally { - pub fn from_parts(bare_ayes: MemberIndex, ayes: Votes, nays: Votes) -> Self { - Tally { bare_ayes, ayes, nays, dummy: PhantomData } + pub fn from_parts(bare_ayes: MemberIndex, out_of_rank_ayes: MemberIndex, out_of_rank_nays: MemberIndex, ayes: Votes, nays: Votes) -> Self { + Tally { bare_ayes, out_of_rank_ayes, out_of_rank_nays, ayes, nays, dummy: PhantomData } } } @@ -123,7 +126,7 @@ impl, I: 'static, M: GetMaxVoters>> VoteTally> for Tally { fn new(_: ClassOf) -> Self { - Self { bare_ayes: 0, ayes: 0, nays: 0, dummy: PhantomData } + Self { bare_ayes: 0, out_of_rank_ayes: 0, out_of_rank_nays: 0, ayes: 0, nays: 0, dummy: PhantomData } } fn ayes(&self, _: ClassOf) -> Votes { self.bare_ayes @@ -192,16 +195,18 @@ impl MemberRecord { #[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen)] pub enum VoteRecord { /// Vote was an aye with given vote weight. - Aye(Votes), + Aye(Option), /// Vote was a nay with given vote weight. - Nay(Votes), + Nay(Option), } -impl From<(bool, Votes)> for VoteRecord { - fn from((aye, votes): (bool, Votes)) -> Self { - match aye { - true => VoteRecord::Aye(votes), - false => VoteRecord::Nay(votes), +impl From<(bool, Option)> for VoteRecord { + fn from((aye, votes): (bool, Option)) -> Self { + match (aye, votes) { + (true, Some(votes)) => VoteRecord::Aye(Some(votes)), + (true, None) => VoteRecord::Aye(None), + (false, Some(votes)) => VoteRecord::Nay(Some(votes)), + (false, None) => VoteRecord::Nay(None), } } } @@ -514,8 +519,6 @@ pub mod pallet { NoneRemaining, /// Unexpected error in state. Corruption, - /// The member's rank is too low to vote. - RankTooLow, /// The information provided is incorrect. InvalidWitness, /// The origin is not sufficiently privileged to do the operation. @@ -630,23 +633,46 @@ pub mod pallet { Err(Error::::NotPolling)?, PollStatus::Ongoing(ref mut tally, class) => { match Voting::::get(&poll, &who) { - Some(Aye(votes)) => { - tally.bare_ayes.saturating_dec(); - tally.ayes.saturating_reduce(votes); + Some(Aye(Some(votes))) => { + tally.bare_ayes.saturating_dec(); + tally.ayes.saturating_reduce(votes); + } + Some(Aye(None)) => { + tally.out_of_rank_ayes.saturating_dec(); + }, + Some(Nay(Some(votes))) => { + tally.nays.saturating_reduce(votes) + }, + Some(Nay(None)) => { + tally.out_of_rank_nays.saturating_dec(); }, - Some(Nay(votes)) => tally.nays.saturating_reduce(votes), None => pays = Pays::No, } let min_rank = T::MinRankOfClass::convert(class); - let votes = Self::rank_to_votes(record.rank, min_rank)?; + let votes = Self::rank_to_votes(record.rank, min_rank); let vote = VoteRecord::from((aye, votes)); - match aye { - true => { - tally.bare_ayes.saturating_inc(); - tally.ayes.saturating_accrue(votes); + match votes { + None => { + match aye { + true => { + tally.out_of_rank_ayes.saturating_inc(); + }, + false => { + tally.out_of_rank_nays.saturating_inc(); + }, + } + }, + Some(votes) => { + match aye { + true => { + tally.bare_ayes.saturating_inc(); + tally.ayes.saturating_accrue(votes); + }, + false => tally.nays.saturating_accrue(votes), + } }, - false => tally.nays.saturating_accrue(votes), } + Voting::::insert(&poll, &who, &vote); Ok((tally.clone(), vote)) }, @@ -741,9 +767,9 @@ pub mod pallet { Members::::get(who).ok_or(Error::::NotMember.into()) } - fn rank_to_votes(rank: Rank, min: Rank) -> Result { - let excess = rank.checked_sub(min).ok_or(Error::::RankTooLow)?; - Ok(T::VoteWeight::convert(excess)) + fn rank_to_votes(rank: Rank, min: Rank) -> Option { + let excess = rank.checked_sub(min)?; + Some(T::VoteWeight::convert(excess)) } fn remove_from_rank(who: &T::AccountId, rank: Rank) -> DispatchResult { diff --git a/substrate/frame/ranked-collective/src/tests.rs b/substrate/frame/ranked-collective/src/tests.rs index 3a85928b76d5..e2f180b05e7a 100644 --- a/substrate/frame/ranked-collective/src/tests.rs +++ b/substrate/frame/ranked-collective/src/tests.rs @@ -59,7 +59,7 @@ parameter_types! { pub static Polls: BTreeMap = vec![ (1, Completed(1, true)), (2, Completed(2, false)), - (3, Ongoing(Tally::from_parts(0, 0, 0), 1)), + (3, Ongoing(Tally::from_parts(0, 0, 0, 0, 0), 1)), ].into_iter().collect(); } @@ -253,7 +253,7 @@ fn completed_poll_should_panic() { #[test] fn basic_stuff() { ExtBuilder::default().build_and_execute(|| { - assert_eq!(tally(3), Tally::from_parts(0, 0, 0)); + assert_eq!(tally(3), Tally::from_parts(0, 0, 0, 0, 0)); }); } @@ -411,23 +411,25 @@ fn voting_works() { assert_ok!(Club::promote_member(RuntimeOrigin::root(), 3)); assert_ok!(Club::promote_member(RuntimeOrigin::root(), 3)); - assert_noop!(Club::vote(RuntimeOrigin::signed(0), 3, true), Error::::RankTooLow); - assert_eq!(tally(3), Tally::from_parts(0, 0, 0)); + assert_ok!(Club::vote(RuntimeOrigin::signed(0), 3, true)); + assert_eq!(tally(3), Tally::from_parts(0, 1, 0, 0, 0)); + assert_ok!(Club::vote(RuntimeOrigin::signed(0), 3, false)); + assert_eq!(tally(3), Tally::from_parts(0, 0, 1, 0, 0)); assert_ok!(Club::vote(RuntimeOrigin::signed(1), 3, true)); - assert_eq!(tally(3), Tally::from_parts(1, 1, 0)); + assert_eq!(tally(3), Tally::from_parts(1, 0, 1, 1, 0)); assert_ok!(Club::vote(RuntimeOrigin::signed(1), 3, false)); - assert_eq!(tally(3), Tally::from_parts(0, 0, 1)); + assert_eq!(tally(3), Tally::from_parts(0, 0, 1, 0, 1)); assert_ok!(Club::vote(RuntimeOrigin::signed(2), 3, true)); - assert_eq!(tally(3), Tally::from_parts(1, 3, 1)); + assert_eq!(tally(3), Tally::from_parts(1, 0, 1, 3, 1)); assert_ok!(Club::vote(RuntimeOrigin::signed(2), 3, false)); - assert_eq!(tally(3), Tally::from_parts(0, 0, 4)); + assert_eq!(tally(3), Tally::from_parts(0, 0, 1, 0, 4)); assert_ok!(Club::vote(RuntimeOrigin::signed(3), 3, true)); - assert_eq!(tally(3), Tally::from_parts(1, 6, 4)); + assert_eq!(tally(3), Tally::from_parts(1, 0, 1, 6, 4)); assert_ok!(Club::vote(RuntimeOrigin::signed(3), 3, false)); - assert_eq!(tally(3), Tally::from_parts(0, 0, 10)); + assert_eq!(tally(3), Tally::from_parts(0, 0, 1, 0, 10)); }); } @@ -588,7 +590,7 @@ fn tally_support_correct() { assert_ok!(Club::promote_member(RuntimeOrigin::root(), 3)); // init tally with 1 aye vote. - let tally: TallyOf = Tally::from_parts(1, 1, 0); + let tally: TallyOf = Tally::from_parts(1, 0, 0, 1, 0); // with minRank(Class) = Class // for class 3, 100% support.