[FRAME] Make core-fellowship ans salary work for swapped members (#3156)

Fixup for https://github.com/paritytech/polkadot-sdk/pull/2587 to make
the `core-fellowship` crate work with swapped members.

Adds a `MemberSwappedHandler` to the `ranked-collective` pallet that are
implemented by `core-fellowship+salary`.
There is are exhaustive tests
[here](https://github.com/paritytech/polkadot-sdk/blob/72aa7ac17a0e5b16faab5d2992aa2db2e01b05d0/substrate/frame/core-fellowship/src/tests/integration.rs#L338)
and
[here](https://github.com/paritytech/polkadot-sdk/blob/ab3cdb05a5ebc1ff841f8dda67edef0ea40bbba5/substrate/frame/salary/src/tests/integration.rs#L224)
to check that adding member `1` is equivalent to adding member `0` and
then swapping.

---------

Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io>
This commit is contained in:
Oliver Tale-Yazdi
2024-01-31 17:29:48 +01:00
committed by GitHub
parent 6ea472ad5a
commit 07e55006ad
25 changed files with 795 additions and 69 deletions
@@ -33,6 +33,10 @@ fn assert_last_event<T: Config<I>, I: 'static>(generic_event: <T as Config<I>>::
frame_system::Pallet::<T>::assert_last_event(generic_event.into());
}
fn assert_has_event<T: Config<I>, I: 'static>(generic_event: <T as Config<I>>::RuntimeEvent) {
frame_system::Pallet::<T>::assert_has_event(generic_event.into());
}
fn make_member<T: Config<I>, I: 'static>(rank: Rank) -> T::AccountId {
let who = account::<T::AccountId>("member", MemberCount::<T, I>::get(0), SEED);
let who_lookup = T::Lookup::unlookup(who.clone());
@@ -175,17 +179,18 @@ benchmarks_instance_pallet! {
exchange_member {
let who = make_member::<T, I>(1);
T::BenchmarkSetup::ensure_member(&who);
let who_lookup = T::Lookup::unlookup(who.clone());
let new_who = account::<T::AccountId>("new-member", 0, SEED);
let new_who_lookup = T::Lookup::unlookup(new_who.clone());
let origin =
T::ExchangeOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
let call = Call::<T, I>::exchange_member { who: who_lookup, new_who:new_who_lookup};
let call = Call::<T, I>::exchange_member { who: who_lookup, new_who: new_who_lookup };
}: { call.dispatch_bypass_filter(origin)? }
verify {
assert_eq!(Members::<T, I>::get(&new_who).unwrap().rank, 1);
assert_eq!(Members::<T, I>::get(&who), None);
assert_last_event::<T, I>(Event::MemberExchanged { who, new_who }.into());
assert_has_event::<T, I>(Event::MemberExchanged { who, new_who }.into());
}
impl_benchmark_test_suite!(RankedCollective, crate::tests::new_test_ext(), crate::tests::Test);
+27 -2
View File
@@ -54,7 +54,10 @@ use sp_std::{marker::PhantomData, prelude::*};
use frame_support::{
dispatch::{DispatchResultWithPostInfo, PostDispatchInfo},
ensure, impl_ensure_origin_with_arg_ignoring_arg,
traits::{EnsureOrigin, EnsureOriginWithArg, PollStatus, Polling, RankedMembers, VoteTally},
traits::{
EnsureOrigin, EnsureOriginWithArg, PollStatus, Polling, RankedMembers,
RankedMembersSwapHandler, VoteTally,
},
CloneNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound,
};
@@ -365,6 +368,13 @@ impl_ensure_origin_with_arg_ignoring_arg! {
{}
}
/// Helper functions to setup benchmarking.
#[impl_trait_for_tuples::impl_for_tuples(8)]
pub trait BenchmarkSetup<AccountId> {
/// Ensure that this member is registered correctly.
fn ensure_member(acc: &AccountId);
}
#[frame_support::pallet]
pub mod pallet {
use super::*;
@@ -402,11 +412,21 @@ pub mod pallet {
/// "a rank of at least the poll class".
type MinRankOfClass: Convert<ClassOf<Self, I>, Rank>;
/// An external handler that will be notified when two members are swapped.
type MemberSwappedHandler: RankedMembersSwapHandler<
<Pallet<Self, I> as RankedMembers>::AccountId,
<Pallet<Self, I> as RankedMembers>::Rank,
>;
/// Convert a rank_delta into a number of votes the rank gets.
///
/// Rank_delta is defined as the number of ranks above the minimum required to take part
/// in the poll.
type VoteWeight: Convert<Rank, Votes>;
/// Setup a member for benchmarking.
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkSetup: BenchmarkSetup<Self::AccountId>;
}
/// The number of members in the collective who have at least the rank according to the index
@@ -679,7 +699,12 @@ pub mod pallet {
Self::do_remove_member_from_rank(&who, rank)?;
Self::do_add_member_to_rank(new_who.clone(), rank, false)?;
Self::deposit_event(Event::MemberExchanged { who, new_who });
Self::deposit_event(Event::MemberExchanged {
who: who.clone(),
new_who: new_who.clone(),
});
T::MemberSwappedHandler::swapped(&who, &new_who, rank);
Ok(())
}
}
@@ -172,7 +172,10 @@ impl Config for Test {
>;
type Polls = TestPolls;
type MinRankOfClass = MinRankOfClass<MinRankOfClassDelta>;
type MemberSwappedHandler = ();
type VoteWeight = Geometric;
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkSetup = ();
}
pub fn new_test_ext() -> sp_io::TestExternalities {
@@ -602,3 +605,21 @@ fn exchange_member_works() {
);
});
}
#[test]
fn exchange_member_same_noops() {
new_test_ext().execute_with(|| {
assert_ok!(Club::add_member(RuntimeOrigin::root(), 1));
assert_ok!(Club::promote_member(RuntimeOrigin::root(), 1));
assert_ok!(Club::add_member(RuntimeOrigin::root(), 2));
assert_ok!(Club::promote_member(RuntimeOrigin::root(), 2));
// Swapping the same accounts is a noop:
assert_noop!(Club::exchange_member(RuntimeOrigin::root(), 1, 1), Error::<Test>::SameMember);
// Swapping with a different member is a noop:
assert_noop!(
Club::exchange_member(RuntimeOrigin::root(), 1, 2),
Error::<Test>::AlreadyMember
);
});
}