Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pallet-membership weights #3324

Merged
merged 11 commits into from
Feb 23, 2024
13 changes: 13 additions & 0 deletions prdoc/pr_3324.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json

title: "Fix weight calculation and event emission in pallet-membership"

doc:
- audience: Runtime Dev
description: |
Bug fix for the membership pallet to use correct weights. Also no event will be emitted
anymore when `change_key` is called with identical accounts.

crates:
- name: pallet-membership
124 changes: 80 additions & 44 deletions substrate/frame/membership/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use frame_support::{
traits::{ChangeMembers, Contains, Get, InitializeMembers, SortedMembers},
BoundedVec,
};
use sp_runtime::traits::StaticLookup;
use sp_runtime::traits::{StaticLookup, UniqueSaturatedInto};
use sp_std::prelude::*;

pub mod migrations;
Expand Down Expand Up @@ -163,12 +163,16 @@ pub mod pallet {
///
/// May only be called from `T::AddOrigin`.
#[pallet::call_index(0)]
#[pallet::weight({50_000_000})]
pub fn add_member(origin: OriginFor<T>, who: AccountIdLookupOf<T>) -> DispatchResult {
#[pallet::weight(T::WeightInfo::add_member(T::MaxMembers::get()))]
pub fn add_member(
origin: OriginFor<T>,
who: AccountIdLookupOf<T>,
) -> DispatchResultWithPostInfo {
T::AddOrigin::ensure_origin(origin)?;
let who = T::Lookup::lookup(who)?;

let mut members = <Members<T, I>>::get();
let init_length = members.len();
let location = members.binary_search(&who).err().ok_or(Error::<T, I>::AlreadyMember)?;
members
.try_insert(location, who.clone())
Expand All @@ -179,19 +183,24 @@ pub mod pallet {
T::MembershipChanged::change_members_sorted(&[who], &[], &members[..]);

Self::deposit_event(Event::MemberAdded);
Ok(())

Ok(Some(T::WeightInfo::add_member(init_length as u32)).into())
}

/// Remove a member `who` from the set.
///
/// May only be called from `T::RemoveOrigin`.
#[pallet::call_index(1)]
#[pallet::weight({50_000_000})]
pub fn remove_member(origin: OriginFor<T>, who: AccountIdLookupOf<T>) -> DispatchResult {
#[pallet::weight(T::WeightInfo::remove_member(T::MaxMembers::get()))]
pub fn remove_member(
origin: OriginFor<T>,
who: AccountIdLookupOf<T>,
) -> DispatchResultWithPostInfo {
T::RemoveOrigin::ensure_origin(origin)?;
let who = T::Lookup::lookup(who)?;

let mut members = <Members<T, I>>::get();
let init_length = members.len();
let location = members.binary_search(&who).ok().ok_or(Error::<T, I>::NotMember)?;
members.remove(location);

Expand All @@ -201,7 +210,7 @@ pub mod pallet {
Self::rejig_prime(&members);

Self::deposit_event(Event::MemberRemoved);
Ok(())
Ok(Some(T::WeightInfo::remove_member(init_length as u32)).into())
}

/// Swap out one member `remove` for another `add`.
Expand All @@ -210,18 +219,18 @@ pub mod pallet {
///
/// Prime membership is *not* passed from `remove` to `add`, if extant.
#[pallet::call_index(2)]
#[pallet::weight({50_000_000})]
#[pallet::weight(T::WeightInfo::swap_member(T::MaxMembers::get()))]
pub fn swap_member(
origin: OriginFor<T>,
remove: AccountIdLookupOf<T>,
add: AccountIdLookupOf<T>,
) -> DispatchResult {
) -> DispatchResultWithPostInfo {
T::SwapOrigin::ensure_origin(origin)?;
let remove = T::Lookup::lookup(remove)?;
let add = T::Lookup::lookup(add)?;

if remove == add {
return Ok(())
return Ok(().into());
}

let mut members = <Members<T, I>>::get();
Expand All @@ -236,15 +245,15 @@ pub mod pallet {
Self::rejig_prime(&members);

Self::deposit_event(Event::MembersSwapped);
Ok(())
Ok(Some(T::WeightInfo::swap_member(members.len() as u32)).into())
}

/// Change the membership to a new set, disregarding the existing membership. Be nice and
/// pass `members` pre-sorted.
///
/// May only be called from `T::ResetOrigin`.
#[pallet::call_index(3)]
#[pallet::weight({50_000_000})]
#[pallet::weight(T::WeightInfo::reset_members(members.len().unique_saturated_into()))]
pub fn reset_members(origin: OriginFor<T>, members: Vec<T::AccountId>) -> DispatchResult {
T::ResetOrigin::ensure_origin(origin)?;

Expand All @@ -267,56 +276,65 @@ pub mod pallet {
///
/// Prime membership is passed from the origin account to `new`, if extant.
#[pallet::call_index(4)]
#[pallet::weight({50_000_000})]
pub fn change_key(origin: OriginFor<T>, new: AccountIdLookupOf<T>) -> DispatchResult {
#[pallet::weight(T::WeightInfo::change_key(T::MaxMembers::get()))]
pub fn change_key(
origin: OriginFor<T>,
new: AccountIdLookupOf<T>,
) -> DispatchResultWithPostInfo {
let remove = ensure_signed(origin)?;
let new = T::Lookup::lookup(new)?;

if remove != new {
let mut members = <Members<T, I>>::get();
let location =
members.binary_search(&remove).ok().ok_or(Error::<T, I>::NotMember)?;
let _ = members.binary_search(&new).err().ok_or(Error::<T, I>::AlreadyMember)?;
members[location] = new.clone();
members.sort();

<Members<T, I>>::put(&members);

T::MembershipChanged::change_members_sorted(
&[new.clone()],
&[remove.clone()],
&members[..],
);

if Prime::<T, I>::get() == Some(remove) {
Prime::<T, I>::put(&new);
T::MembershipChanged::set_prime(Some(new));
}
if remove == new {
return Ok(().into());
}

let mut members = <Members<T, I>>::get();
let members_length = members.len() as u32;
let location = members.binary_search(&remove).ok().ok_or(Error::<T, I>::NotMember)?;
let _ = members.binary_search(&new).err().ok_or(Error::<T, I>::AlreadyMember)?;
members[location] = new.clone();
members.sort();

<Members<T, I>>::put(&members);

T::MembershipChanged::change_members_sorted(
&[new.clone()],
&[remove.clone()],
&members[..],
);

if Prime::<T, I>::get() == Some(remove) {
Prime::<T, I>::put(&new);
T::MembershipChanged::set_prime(Some(new));
}

Self::deposit_event(Event::KeyChanged);
Ok(())
Ok(Some(T::WeightInfo::change_key(members_length)).into())
}

/// Set the prime member. Must be a current member.
///
/// May only be called from `T::PrimeOrigin`.
#[pallet::call_index(5)]
#[pallet::weight({50_000_000})]
pub fn set_prime(origin: OriginFor<T>, who: AccountIdLookupOf<T>) -> DispatchResult {
#[pallet::weight(T::WeightInfo::set_prime(T::MaxMembers::get()))]
pub fn set_prime(
origin: OriginFor<T>,
who: AccountIdLookupOf<T>,
) -> DispatchResultWithPostInfo {
T::PrimeOrigin::ensure_origin(origin)?;
let who = T::Lookup::lookup(who)?;
Self::members().binary_search(&who).ok().ok_or(Error::<T, I>::NotMember)?;
let members = Self::members();
members.binary_search(&who).ok().ok_or(Error::<T, I>::NotMember)?;
Prime::<T, I>::put(&who);
T::MembershipChanged::set_prime(Some(who));
Ok(())
Ok(Some(T::WeightInfo::set_prime(members.len() as u32)).into())
}

/// Remove the prime member if it exists.
///
/// May only be called from `T::PrimeOrigin`.
#[pallet::call_index(6)]
#[pallet::weight({50_000_000})]
#[pallet::weight(T::WeightInfo::clear_prime())]
pub fn clear_prime(origin: OriginFor<T>) -> DispatchResult {
T::PrimeOrigin::ensure_origin(origin)?;
Prime::<T, I>::kill();
Expand Down Expand Up @@ -442,7 +460,7 @@ mod benchmark {
}

// er keep the prime common between incoming and outgoing to make sure it is rejigged.
reset_member {
reset_members {
let m in 1 .. T::MaxMembers::get();

let members = (1..m+1).map(|i| account("member", i, SEED)).collect::<Vec<T::AccountId>>();
Expand Down Expand Up @@ -500,8 +518,7 @@ mod benchmark {
}

clear_prime {
let m in 1 .. T::MaxMembers::get();
ggwpez marked this conversation as resolved.
Show resolved Hide resolved
let members = (0..m).map(|i| account("member", i, SEED)).collect::<Vec<T::AccountId>>();
let members = (0..T::MaxMembers::get()).map(|i| account("member", i, SEED)).collect::<Vec<T::AccountId>>();
let prime = members.last().cloned().unwrap();
set_members::<T, I>(members, None);
}: {
Expand All @@ -526,7 +543,8 @@ mod tests {
use sp_runtime::{bounded_vec, traits::BadOrigin, BuildStorage};

use frame_support::{
assert_noop, assert_ok, derive_impl, ord_parameter_types, parameter_types,
assert_noop, assert_ok, assert_storage_noop, derive_impl, ord_parameter_types,
parameter_types,
traits::{ConstU32, StorageVersion},
};
use frame_system::EnsureSignedBy;
Expand Down Expand Up @@ -716,6 +734,17 @@ mod tests {
});
}

#[test]
fn swap_member_with_identical_arguments_changes_nothing() {
new_test_ext().execute_with(|| {
assert_storage_noop!(assert_ok!(Membership::swap_member(
RuntimeOrigin::signed(3),
10,
10
)));
});
}

#[test]
fn change_key_works() {
new_test_ext().execute_with(|| {
Expand Down Expand Up @@ -745,6 +774,13 @@ mod tests {
});
}

#[test]
fn change_key_with_same_caller_as_argument_changes_nothing() {
new_test_ext().execute_with(|| {
assert_storage_noop!(assert_ok!(Membership::change_key(RuntimeOrigin::signed(10), 10)));
});
}

#[test]
fn reset_members_works() {
new_test_ext().execute_with(|| {
Expand Down
18 changes: 6 additions & 12 deletions substrate/frame/membership/src/weights.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading