diff --git a/Cargo.lock b/Cargo.lock index 46c12fde8cd1..f02522f12679 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14312,19 +14312,6 @@ dependencies = [ "substrate-test-utils", ] -[[package]] -name = "pallet-scored-pool" -version = "28.0.0" -dependencies = [ - "frame-support", - "frame-system", - "pallet-balances", - "parity-scale-codec", - "scale-info", - "sp-io 30.0.0", - "sp-runtime 31.0.1", -] - [[package]] name = "pallet-session" version = "28.0.0" @@ -17439,7 +17426,6 @@ dependencies = [ "pallet-safe-mode", "pallet-salary", "pallet-scheduler", - "pallet-scored-pool", "pallet-session", "pallet-session-benchmarking", "pallet-skip-feeless-payment", diff --git a/Cargo.toml b/Cargo.toml index 17fcab380408..24413360fd77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -446,7 +446,6 @@ members = [ "substrate/frame/salary", "substrate/frame/sassafras", "substrate/frame/scheduler", - "substrate/frame/scored-pool", "substrate/frame/session", "substrate/frame/session/benchmarking", "substrate/frame/society", diff --git a/prdoc/pr_12746.prdoc b/prdoc/pr_12746.prdoc new file mode 100644 index 000000000000..c543c54577c9 --- /dev/null +++ b/prdoc/pr_12746.prdoc @@ -0,0 +1,16 @@ +title: Remove `pallet-scored-pool` +doc: +- audience: Runtime Dev + description: |- + `pallet-scored-pool` has been removed. It was not used by any runtime in the + Polkadot SDK and had received no functional changes in years. + + Runtimes depending on it should migrate to one of the maintained membership + pallets, such as `pallet-membership`, `pallet-ranked-collective` or + `pallet-core-fellowship`, depending on the desired semantics. + + Users of the `polkadot-sdk` umbrella crate should remove the + `pallet-scored-pool` feature from their dependency declaration. +crates: +- name: polkadot-sdk + bump: major diff --git a/substrate/frame/scored-pool/Cargo.toml b/substrate/frame/scored-pool/Cargo.toml deleted file mode 100644 index fb65a85a4828..000000000000 --- a/substrate/frame/scored-pool/Cargo.toml +++ /dev/null @@ -1,45 +0,0 @@ -[package] -name = "pallet-scored-pool" -version = "28.0.0" -authors.workspace = true -edition.workspace = true -license = "Apache-2.0" -homepage.workspace = true -repository.workspace = true -description = "FRAME pallet for scored pools" -readme = "README.md" - -[lints] -workspace = true - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { features = ["derive"], workspace = true } -frame-support = { workspace = true } -frame-system = { workspace = true } -scale-info = { features = ["derive"], workspace = true } -sp-io = { workspace = true } -sp-runtime = { workspace = true } - -[dev-dependencies] -pallet-balances = { workspace = true, default-features = true } - -[features] -default = ["std"] -std = [ - "codec/std", - "frame-support/std", - "frame-system/std", - "pallet-balances/std", - "scale-info/std", - "sp-io/std", - "sp-runtime/std", -] -try-runtime = [ - "frame-support/try-runtime", - "frame-system/try-runtime", - "pallet-balances/try-runtime", - "sp-runtime/try-runtime", -] diff --git a/substrate/frame/scored-pool/README.md b/substrate/frame/scored-pool/README.md deleted file mode 100644 index 455bae24e795..000000000000 --- a/substrate/frame/scored-pool/README.md +++ /dev/null @@ -1,73 +0,0 @@ -# Scored Pool Module - -The module maintains a scored membership pool. Each entity in the -pool can be attributed a `Score`. From this pool a set `Members` -is constructed. This set contains the `MemberCount` highest -scoring entities. Unscored entities are never part of `Members`. - -If an entity wants to be part of the pool a deposit is required. -The deposit is returned when the entity withdraws or when it -is removed by an entity with the appropriate authority. - -Every `Period` blocks the set of `Members` is refreshed from the -highest scoring members in the pool and, no matter if changes -occurred, `T::MembershipChanged::set_members_sorted` is invoked. -On first load `T::MembershipInitialized::initialize_members` is -invoked with the initial `Members` set. - -It is possible to withdraw candidacy/resign your membership at any -time. If an entity is currently a member, this results in removal -from the `Pool` and `Members`; the entity is immediately replaced -by the next highest scoring candidate in the pool, if available. - -- [`scored_pool::Trait`](https://docs.rs/pallet-scored-pool/latest/pallet_scored_pool/trait.Config.html) -- [`Call`](https://docs.rs/pallet-scored-pool/latest/pallet_scored_pool/enum.Call.html) -- [`Module`](https://docs.rs/pallet-scored-pool/latest/pallet_scored_pool/struct.Module.html) - -## Interface - -### Public Functions - -- `submit_candidacy` - Submit candidacy to become a member. Requires a deposit. -- `withdraw_candidacy` - Withdraw candidacy. Deposit is returned. -- `score` - Attribute a quantitative score to an entity. -- `kick` - Remove an entity from the pool and members. Deposit is returned. -- `change_member_count` - Changes the amount of candidates taken into `Members`. - -## Usage - -```rust -use pallet_scored_pool::{self as scored_pool}; - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::config] - pub trait Config: frame_system::Config + scored_pool::Config {} - - #[pallet::call] - impl Pallet { - #[pallet::weight(0)] - pub fn candidate(origin: OriginFor) -> DispatchResult { - let who = ensure_signed(origin)?; - - let _ = >::submit_candidacy( - T::RuntimeOrigin::from(Some(who.clone()).into()) - ); - Ok(()) - } - } -} -``` - -## Dependencies - -This module depends on the [System module](https://docs.rs/frame-system/latest/frame_system/). - -License: Apache-2.0 diff --git a/substrate/frame/scored-pool/src/lib.rs b/substrate/frame/scored-pool/src/lib.rs deleted file mode 100644 index db22c3b1ce75..000000000000 --- a/substrate/frame/scored-pool/src/lib.rs +++ /dev/null @@ -1,520 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! # Scored Pool Pallet -//! -//! The pallet maintains a scored membership pool. Each entity in the -//! pool can be attributed a `Score`. From this pool a set `Members` -//! is constructed. This set contains the `MemberCount` highest -//! scoring entities. Unscored entities are never part of `Members`. -//! -//! If an entity wants to be part of the pool a deposit is required. -//! The deposit is returned when the entity withdraws or when it -//! is removed by an entity with the appropriate authority. -//! -//! Every `Period` blocks the set of `Members` is refreshed from the -//! highest scoring members in the pool and, no matter if changes -//! occurred, `T::MembershipChanged::set_members_sorted` is invoked. -//! On first load `T::MembershipInitialized::initialize_members` is -//! invoked with the initial `Members` set. -//! -//! It is possible to withdraw candidacy/resign your membership at any -//! time. If an entity is currently a member, this results in removal -//! from the `Pool` and `Members`; the entity is immediately replaced -//! by the next highest scoring candidate in the pool, if available. -//! -//! - [`Config`] -//! - [`Call`] -//! - [`Pallet`] -//! -//! ## Interface -//! -//! ### Public Functions -//! -//! - `submit_candidacy` - Submit candidacy to become a member. Requires a deposit. -//! - `withdraw_candidacy` - Withdraw candidacy. Deposit is returned. -//! - `score` - Attribute a quantitative score to an entity. -//! - `kick` - Remove an entity from the pool and members. Deposit is returned. -//! - `change_member_count` - Changes the amount of candidates taken into `Members`. -//! -//! ## Usage -//! -//! ``` -//! use pallet_scored_pool::{self as scored_pool}; -//! -//! #[frame_support::pallet] -//! pub mod pallet { -//! use super::*; -//! use frame_support::pallet_prelude::*; -//! use frame_system::pallet_prelude::*; -//! -//! #[pallet::pallet] -//! pub struct Pallet(_); -//! -//! #[pallet::config] -//! pub trait Config: frame_system::Config + scored_pool::Config {} -//! -//! #[pallet::call] -//! impl Pallet { -//! #[pallet::weight({0})] -//! pub fn candidate(origin: OriginFor) -> DispatchResult { -//! let who = ensure_signed(origin)?; -//! -//! let _ = >::submit_candidacy( -//! T::RuntimeOrigin::from(Some(who.clone()).into()) -//! ); -//! Ok(()) -//! } -//! } -//! } -//! -//! # fn main() { } -//! ``` -//! -//! ## Dependencies -//! -//! This pallet depends on the [System pallet](../frame_system/index.html). - -// Ensure we're `no_std` when compiling for Wasm. -#![cfg_attr(not(feature = "std"), no_std)] - -#[cfg(test)] -// We do not declare all features used by `construct_runtime` -#[allow(unexpected_cfgs)] -mod mock; - -#[cfg(test)] -mod tests; - -extern crate alloc; - -use alloc::vec::Vec; -use codec::{FullCodec, MaxEncodedLen}; -use core::{cmp::Reverse, fmt::Debug}; -use frame_support::{ - ensure, - traits::{ChangeMembers, Currency, Get, InitializeMembers, ReservableCurrency}, - BoundedVec, -}; -pub use pallet::*; -use sp_runtime::traits::{AtLeast32Bit, StaticLookup, Zero}; - -type BalanceOf = - <>::Currency as Currency<::AccountId>>::Balance; -type PoolT = BoundedVec< - (::AccountId, Option<>::Score>), - >::MaximumMembers, ->; -type MembersT = - BoundedVec<::AccountId, >::MaximumMembers>; -type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; - -/// The enum is supplied when refreshing the members set. -/// Depending on the enum variant the corresponding associated -/// type function will be invoked. -enum ChangeReceiver { - /// Should call `T::MembershipInitialized`. - MembershipInitialized, - /// Should call `T::MembershipChanged`. - MembershipChanged, -} - -#[frame_support::pallet] -pub mod pallet { - use super::*; - use frame_support::pallet_prelude::*; - use frame_system::pallet_prelude::*; - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::config] - pub trait Config: frame_system::Config { - /// The currency used for deposits. - type Currency: Currency + ReservableCurrency; - - /// Maximum members length allowed. - #[pallet::constant] - type MaximumMembers: Get; - - /// The score attributed to a member or candidate. - type Score: AtLeast32Bit - + Clone - + Copy - + Default - + FullCodec - + MaybeSerializeDeserialize - + Debug - + scale_info::TypeInfo - + MaxEncodedLen; - - /// The overarching event type. - #[allow(deprecated)] - type RuntimeEvent: From> - + IsType<::RuntimeEvent>; - - // The deposit which is reserved from candidates if they want to - // start a candidacy. The deposit gets returned when the candidacy is - // withdrawn or when the candidate is kicked. - #[pallet::constant] - type CandidateDeposit: Get>; - - /// Every `Period` blocks the `Members` are filled with the highest scoring - /// members in the `Pool`. - #[pallet::constant] - type Period: Get>; - - /// The receiver of the signal for when the membership has been initialized. - /// This happens pre-genesis and will usually be the same as `MembershipChanged`. - /// If you need to do something different on initialization, then you can change - /// this accordingly. - type MembershipInitialized: InitializeMembers; - - /// The receiver of the signal for when the members have changed. - type MembershipChanged: ChangeMembers; - - /// Allows a configurable origin type to set a score to a candidate in the pool. - type ScoreOrigin: EnsureOrigin; - - /// Required origin for removing a member (though can always be Root). - /// Configurable origin which enables removing an entity. If the entity - /// is part of the `Members` it is immediately replaced by the next - /// highest scoring candidate, if available. - type KickOrigin: EnsureOrigin; - } - - #[pallet::event] - #[pallet::generate_deposit(pub(super) fn deposit_event)] - pub enum Event, I: 'static = ()> { - /// The given member was removed. See the transaction for who. - MemberRemoved, - /// An entity has issued a candidacy. See the transaction for who. - CandidateAdded, - /// An entity withdrew candidacy. See the transaction for who. - CandidateWithdrew, - /// The candidacy was forcefully removed for an entity. - /// See the transaction for who. - CandidateKicked, - /// A score was attributed to the candidate. - /// See the transaction for who. - CandidateScored, - } - - /// Error for the scored-pool pallet. - #[pallet::error] - pub enum Error { - /// Already a member. - AlreadyInPool, - /// Index out of bounds. - InvalidIndex, - /// Index does not match requested account. - WrongAccountIndex, - /// Number of members exceeds `MaximumMembers`. - TooManyMembers, - } - - /// The current pool of candidates, stored as an ordered Bounded Vec - /// (ordered descending by score, `None` last, highest first). - #[pallet::storage] - #[pallet::getter(fn pool)] - pub(crate) type Pool, I: 'static = ()> = StorageValue<_, PoolT, ValueQuery>; - - /// A Map of the candidates. The information in this Map is redundant - /// to the information in the `Pool`. But the Map enables us to easily - /// check if a candidate is already in the pool, without having to - /// iterate over the entire pool (the `Pool` is not sorted by - /// `T::AccountId`, but by `T::Score` instead). - #[pallet::storage] - #[pallet::getter(fn candidate_exists)] - pub(crate) type CandidateExists, I: 'static = ()> = - StorageMap<_, Twox64Concat, T::AccountId, bool, ValueQuery>; - - /// The current membership, stored as an ordered Vec. - #[pallet::storage] - #[pallet::getter(fn members)] - pub(crate) type Members, I: 'static = ()> = - StorageValue<_, MembersT, ValueQuery>; - - /// Size of the `Members` set. - #[pallet::storage] - #[pallet::getter(fn member_count)] - pub(crate) type MemberCount = StorageValue<_, u32, ValueQuery>; - - #[pallet::genesis_config] - #[derive(frame_support::DefaultNoBound)] - pub struct GenesisConfig, I: 'static = ()> { - pub pool: PoolT, - pub member_count: u32, - } - - #[pallet::genesis_build] - impl, I: 'static> BuildGenesisConfig for GenesisConfig { - fn build(&self) { - let mut pool = self.pool.clone(); - - // reserve balance for each candidate in the pool. - // panicking here is ok, since this just happens one time, pre-genesis. - pool.iter().for_each(|(who, _)| { - T::Currency::reserve(who, T::CandidateDeposit::get()) - .expect("balance too low to create candidacy"); - >::insert(who, true); - }); - - // Sorts the `Pool` by score in a descending order. Entities which - // have a score of `None` are sorted to the end of the bounded vec. - pool.sort_by_key(|(_, maybe_score)| Reverse(maybe_score.unwrap_or_default())); - >::update_member_count(self.member_count) - .expect("Number of allowed members exceeded"); - >::put(&pool); - >::refresh_members(pool, ChangeReceiver::MembershipInitialized); - } - } - - #[pallet::hooks] - impl, I: 'static> Hooks> for Pallet { - /// Every `Period` blocks the `Members` set is refreshed from the - /// highest scoring members in the pool. - fn on_initialize(n: frame_system::pallet_prelude::BlockNumberFor) -> Weight { - if n % T::Period::get() == Zero::zero() { - let pool = >::get(); - >::refresh_members(pool, ChangeReceiver::MembershipChanged); - } - Weight::zero() - } - } - - #[pallet::call] - impl, I: 'static> Pallet { - /// Add `origin` to the pool of candidates. - /// - /// This results in `CandidateDeposit` being reserved from - /// the `origin` account. The deposit is returned once - /// candidacy is withdrawn by the candidate or the entity - /// is kicked by `KickOrigin`. - /// - /// The dispatch origin of this function must be signed. - /// - /// The `index` parameter of this function must be set to - /// the index of the transactor in the `Pool`. - #[pallet::call_index(0)] - #[pallet::weight({0})] - pub fn submit_candidacy(origin: OriginFor) -> DispatchResult { - let who = ensure_signed(origin)?; - ensure!(!>::contains_key(&who), Error::::AlreadyInPool); - - let deposit = T::CandidateDeposit::get(); - T::Currency::reserve(&who, deposit)?; - - // can be inserted as last element in pool, since entities with - // `None` are always sorted to the end. - >::try_append((who.clone(), Option::<>::Score>::None)) - .map_err(|_| Error::::TooManyMembers)?; - - >::insert(&who, true); - - Self::deposit_event(Event::::CandidateAdded); - Ok(()) - } - - /// An entity withdraws candidacy and gets its deposit back. - /// - /// If the entity is part of the `Members`, then the highest member - /// of the `Pool` that is not currently in `Members` is immediately - /// placed in the set instead. - /// - /// The dispatch origin of this function must be signed. - /// - /// The `index` parameter of this function must be set to - /// the index of the transactor in the `Pool`. - #[pallet::call_index(1)] - #[pallet::weight({0})] - pub fn withdraw_candidacy(origin: OriginFor, index: u32) -> DispatchResult { - let who = ensure_signed(origin)?; - - let pool = >::get(); - Self::ensure_index(&pool, &who, index)?; - - Self::remove_member(pool, who, index)?; - Self::deposit_event(Event::::CandidateWithdrew); - Ok(()) - } - - /// Kick a member `who` from the set. - /// - /// May only be called from `T::KickOrigin`. - /// - /// The `index` parameter of this function must be set to - /// the index of `dest` in the `Pool`. - #[pallet::call_index(2)] - #[pallet::weight({0})] - pub fn kick( - origin: OriginFor, - dest: AccountIdLookupOf, - index: u32, - ) -> DispatchResult { - T::KickOrigin::ensure_origin(origin)?; - - let who = T::Lookup::lookup(dest)?; - - let pool = >::get(); - Self::ensure_index(&pool, &who, index)?; - - Self::remove_member(pool, who, index)?; - Self::deposit_event(Event::::CandidateKicked); - Ok(()) - } - - /// Score a member `who` with `score`. - /// - /// May only be called from `T::ScoreOrigin`. - /// - /// The `index` parameter of this function must be set to - /// the index of the `dest` in the `Pool`. - #[pallet::call_index(3)] - #[pallet::weight({0})] - pub fn score( - origin: OriginFor, - dest: AccountIdLookupOf, - index: u32, - score: T::Score, - ) -> DispatchResult { - T::ScoreOrigin::ensure_origin(origin)?; - - let who = T::Lookup::lookup(dest)?; - - let mut pool = >::get(); - Self::ensure_index(&pool, &who, index)?; - - pool.remove(index as usize); - - // we binary search the pool (which is sorted descending by score). - // if there is already an element with `score`, we insert - // right before that. if not, the search returns a location - // where we can insert while maintaining order. - let item = (who, Some(score)); - let location = pool - .binary_search_by_key(&Reverse(score), |(_, maybe_score)| { - Reverse(maybe_score.unwrap_or_default()) - }) - .unwrap_or_else(|l| l); - pool.try_insert(location, item).map_err(|_| Error::::TooManyMembers)?; - - >::put(&pool); - Self::deposit_event(Event::::CandidateScored); - Ok(()) - } - - /// Dispatchable call to change `MemberCount`. - /// - /// This will only have an effect the next time a refresh happens - /// (this happens each `Period`). - /// - /// May only be called from root. - #[pallet::call_index(4)] - #[pallet::weight({0})] - pub fn change_member_count(origin: OriginFor, count: u32) -> DispatchResult { - ensure_root(origin)?; - Self::update_member_count(count).map_err(Into::into) - } - } -} - -impl, I: 'static> Pallet { - /// Fetches the `MemberCount` highest scoring members from - /// `Pool` and puts them into `Members`. - /// - /// The `notify` parameter is used to deduct which associated - /// type function to invoke at the end of the method. - fn refresh_members(pool: PoolT, notify: ChangeReceiver) { - let count = MemberCount::::get(); - let old_members = >::get(); - - let new_members: Vec = pool - .into_iter() - .filter(|(_, score)| score.is_some()) - .take(count as usize) - .map(|(account_id, _)| account_id) - .collect(); - - // It's safe to truncate_from at this point since MemberCount - // is verified that it does not exceed the MaximumMembers value - let mut new_members_bounded: MembersT = BoundedVec::truncate_from(new_members); - - new_members_bounded.sort(); - - >::put(&new_members_bounded); - - match notify { - ChangeReceiver::MembershipInitialized => { - T::MembershipInitialized::initialize_members(&new_members_bounded) - }, - ChangeReceiver::MembershipChanged => { - T::MembershipChanged::set_members_sorted(&new_members_bounded[..], &old_members[..]) - }, - } - } - - /// Removes an entity `remove` at `index` from the `Pool`. - /// - /// If the entity is a member it is also removed from `Members` and - /// the deposit is returned. - fn remove_member( - mut pool: PoolT, - remove: T::AccountId, - index: u32, - ) -> Result<(), Error> { - // all callers of this function in this pallet also check - // the index for validity before calling this function. - // nevertheless we check again here, to assert that there was - // no mistake when invoking this sensible function. - Self::ensure_index(&pool, &remove, index)?; - - pool.remove(index as usize); - >::put(&pool); - - // remove from set, if it was in there - let members = >::get(); - if members.binary_search(&remove).is_ok() { - Self::refresh_members(pool, ChangeReceiver::MembershipChanged); - } - - >::remove(&remove); - - T::Currency::unreserve(&remove, T::CandidateDeposit::get()); - - Self::deposit_event(Event::::MemberRemoved); - Ok(()) - } - - /// Checks if `index` is a valid number and if the element found - /// at `index` in `Pool` is equal to `who`. - fn ensure_index(pool: &PoolT, who: &T::AccountId, index: u32) -> Result<(), Error> { - ensure!(index < pool.len() as u32, Error::::InvalidIndex); - - let (index_who, _index_score) = &pool[index as usize]; - ensure!(index_who == who, Error::::WrongAccountIndex); - - Ok(()) - } - - /// Make sure the new member count value does not exceed the MaximumMembers - fn update_member_count(new_member_count: u32) -> Result<(), Error> { - ensure!(new_member_count <= T::MaximumMembers::get(), Error::::TooManyMembers); - >::put(new_member_count); - Ok(()) - } -} diff --git a/substrate/frame/scored-pool/src/mock.rs b/substrate/frame/scored-pool/src/mock.rs deleted file mode 100644 index 5eb9df528924..000000000000 --- a/substrate/frame/scored-pool/src/mock.rs +++ /dev/null @@ -1,133 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Test utilities - -use super::*; -use crate as pallet_scored_pool; - -use frame_support::{ - construct_runtime, derive_impl, ord_parameter_types, parameter_types, - traits::{ConstU32, ConstU64}, -}; -use frame_system::EnsureSignedBy; -use sp_runtime::{bounded_vec, BuildStorage}; - -type Block = frame_system::mocking::MockBlock; - -construct_runtime!( - pub enum Test - { - System: frame_system, - Balances: pallet_balances, - ScoredPool: pallet_scored_pool, - } -); - -parameter_types! { - pub const CandidateDeposit: u64 = 25; -} -ord_parameter_types! { - pub const KickOrigin: u64 = 2; - pub const ScoreOrigin: u64 = 3; -} - -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl frame_system::Config for Test { - type Block = Block; - type AccountData = pallet_balances::AccountData; -} - -#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)] -impl pallet_balances::Config for Test { - type AccountStore = System; -} - -parameter_types! { - pub static MembersTestValue: BoundedVec> = bounded_vec![0,10]; -} - -pub struct TestChangeMembers; -impl ChangeMembers for TestChangeMembers { - fn change_members_sorted(incoming: &[u64], outgoing: &[u64], new: &[u64]) { - let mut old_plus_incoming = MembersTestValue::get().into_inner(); - old_plus_incoming.extend_from_slice(incoming); - old_plus_incoming.sort(); - - let mut new_plus_outgoing = new.to_vec(); - new_plus_outgoing.extend_from_slice(outgoing); - new_plus_outgoing.sort(); - - assert_eq!(old_plus_incoming, new_plus_outgoing); - - MembersTestValue::set(>>::truncate_from(new.to_vec())); - } -} - -impl InitializeMembers for TestChangeMembers { - fn initialize_members(new_members: &[u64]) { - MembersTestValue::set(>>::truncate_from( - new_members.to_vec(), - )); - } -} - -impl Config for Test { - type RuntimeEvent = RuntimeEvent; - type KickOrigin = EnsureSignedBy; - type MembershipInitialized = TestChangeMembers; - type MembershipChanged = TestChangeMembers; - type Currency = Balances; - type CandidateDeposit = CandidateDeposit; - type Period = ConstU64<4>; - type Score = u64; - type ScoreOrigin = EnsureSignedBy; - type MaximumMembers = ConstU32<10>; -} - -pub fn new_test_ext() -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::::default().build_storage().unwrap(); - let mut balances = vec![]; - for i in 1..31 { - balances.push((i, 500_000)); - } - balances.push((31, 500_000)); - balances.push((40, 500_000)); - balances.push((99, 1)); - - pallet_balances::GenesisConfig:: { balances, ..Default::default() } - .assimilate_storage(&mut t) - .unwrap(); - pallet_scored_pool::GenesisConfig:: { - pool: bounded_vec![(10, Some(1)), (20, Some(2)), (31, Some(2)), (40, Some(3)), (5, None)], - member_count: 2, - } - .assimilate_storage(&mut t) - .unwrap(); - t.into() -} - -/// Fetch an entity from the pool, if existent. -pub fn fetch_from_pool(who: u64) -> Option<(u64, Option)> { - >::pool().into_iter().find(|item| item.0 == who) -} - -/// Find an entity in the pool. -/// Returns its position in the `Pool` vec, if existent. -pub fn find_in_pool(who: u64) -> Option { - >::pool().into_iter().position(|item| item.0 == who) -} diff --git a/substrate/frame/scored-pool/src/tests.rs b/substrate/frame/scored-pool/src/tests.rs deleted file mode 100644 index 96c94a6c1c65..000000000000 --- a/substrate/frame/scored-pool/src/tests.rs +++ /dev/null @@ -1,321 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Tests for the pallet. - -use super::*; -use mock::*; - -use frame_support::{assert_noop, assert_ok, traits::OnInitialize}; -use sp_runtime::traits::BadOrigin; - -type ScoredPool = Pallet; -type System = frame_system::Pallet; -type Balances = pallet_balances::Pallet; - -#[test] -fn query_membership_works() { - new_test_ext().execute_with(|| { - assert_eq!(ScoredPool::members(), vec![20, 40]); - assert_eq!(Balances::reserved_balance(31), CandidateDeposit::get()); - assert_eq!(Balances::reserved_balance(40), CandidateDeposit::get()); - assert_eq!(MembersTestValue::get().clone(), vec![20, 40]); - }); -} - -#[test] -fn submit_candidacy_must_not_work() { - new_test_ext().execute_with(|| { - assert_noop!( - ScoredPool::submit_candidacy(RuntimeOrigin::signed(99)), - pallet_balances::Error::::InsufficientBalance, - ); - assert_noop!( - ScoredPool::submit_candidacy(RuntimeOrigin::signed(40)), - Error::::AlreadyInPool - ); - }); -} - -#[test] -fn submit_candidacy_works() { - new_test_ext().execute_with(|| { - // given - let who = 15; - - // when - assert_ok!(ScoredPool::submit_candidacy(RuntimeOrigin::signed(who))); - assert_eq!(fetch_from_pool(15), Some((who, None))); - - // then - assert_eq!(Balances::reserved_balance(who), CandidateDeposit::get()); - }); -} - -#[test] -fn scoring_works() { - new_test_ext().execute_with(|| { - // given - let who = 15; - let score = 99; - assert_ok!(ScoredPool::submit_candidacy(RuntimeOrigin::signed(who))); - - // when - let index = find_in_pool(who).expect("entity must be in pool") as u32; - assert_ok!(ScoredPool::score(RuntimeOrigin::signed(ScoreOrigin::get()), who, index, score)); - - // then - assert_eq!(fetch_from_pool(who), Some((who, Some(score)))); - assert_eq!(find_in_pool(who), Some(0)); // must be first element, since highest scored - }); -} - -#[test] -fn scoring_same_element_with_same_score_works() { - new_test_ext().execute_with(|| { - // given - let who = 31; - let index = find_in_pool(who).expect("entity must be in pool") as u32; - let score = 2; - - // when - assert_ok!(ScoredPool::score(RuntimeOrigin::signed(ScoreOrigin::get()), who, index, score)); - - // then - assert_eq!(fetch_from_pool(who), Some((who, Some(score)))); - - // must have been inserted right before the `20` element which is - // of the same score as `31`. so sort order is maintained. - assert_eq!(find_in_pool(who), Some(1)); - }); -} - -#[test] -fn kicking_works_only_for_authorized() { - new_test_ext().execute_with(|| { - let who = 40; - let index = find_in_pool(who).expect("entity must be in pool") as u32; - assert_noop!(ScoredPool::kick(RuntimeOrigin::signed(99), who, index), BadOrigin); - }); -} - -#[test] -fn kicking_works() { - new_test_ext().execute_with(|| { - // given - let who = 40; - assert_eq!(Balances::reserved_balance(who), CandidateDeposit::get()); - assert_eq!(find_in_pool(who), Some(0)); - - // when - let index = find_in_pool(who).expect("entity must be in pool") as u32; - assert_ok!(ScoredPool::kick(RuntimeOrigin::signed(KickOrigin::get()), who, index)); - - // then - assert_eq!(find_in_pool(who), None); - assert_eq!(ScoredPool::members(), vec![20, 31]); - assert_eq!(MembersTestValue::get().clone(), ScoredPool::members()); - assert_eq!(Balances::reserved_balance(who), 0); // deposit must have been returned - }); -} - -#[test] -fn unscored_entities_must_not_be_used_for_filling_members() { - new_test_ext().execute_with(|| { - // given - // we submit a candidacy, score will be `None` - assert_ok!(ScoredPool::submit_candidacy(RuntimeOrigin::signed(15))); - - // when - // we remove every scored member - ScoredPool::pool().into_iter().for_each(|(who, score)| { - if let Some(_) = score { - let index = find_in_pool(who).expect("entity must be in pool") as u32; - assert_ok!(ScoredPool::kick(RuntimeOrigin::signed(KickOrigin::get()), who, index)); - } - }); - - // then - // the `None` candidates should not have been filled in - assert!(ScoredPool::members().is_empty()); - assert_eq!(MembersTestValue::get().clone(), ScoredPool::members()); - }); -} - -#[test] -fn refreshing_works() { - new_test_ext().execute_with(|| { - // given - let who = 15; - assert_ok!(ScoredPool::submit_candidacy(RuntimeOrigin::signed(who))); - let index = find_in_pool(who).expect("entity must be in pool") as u32; - assert_ok!(ScoredPool::score(RuntimeOrigin::signed(ScoreOrigin::get()), who, index, 99)); - - // when - ScoredPool::refresh_members(ScoredPool::pool(), ChangeReceiver::MembershipChanged); - - // then - assert_eq!(ScoredPool::members(), vec![15, 40]); - assert_eq!(MembersTestValue::get().clone(), ScoredPool::members()); - }); -} - -#[test] -fn refreshing_happens_every_period() { - new_test_ext().execute_with(|| { - // given - System::set_block_number(1); - assert_ok!(ScoredPool::submit_candidacy(RuntimeOrigin::signed(15))); - let index = find_in_pool(15).expect("entity must be in pool") as u32; - assert_ok!(ScoredPool::score(RuntimeOrigin::signed(ScoreOrigin::get()), 15, index, 99)); - assert_eq!(ScoredPool::members(), vec![20, 40]); - - // when - System::set_block_number(4); - ScoredPool::on_initialize(4); - - // then - assert_eq!(ScoredPool::members(), vec![15, 40]); - assert_eq!(MembersTestValue::get().clone(), ScoredPool::members()); - }); -} - -#[test] -fn withdraw_candidacy_must_only_work_for_members() { - new_test_ext().execute_with(|| { - let who = 77; - let index = 0; - assert_noop!( - ScoredPool::withdraw_candidacy(RuntimeOrigin::signed(who), index), - Error::::WrongAccountIndex - ); - }); -} - -#[test] -fn oob_index_should_abort() { - new_test_ext().execute_with(|| { - let who = 40; - let oob_index = ScoredPool::pool().len() as u32; - assert_noop!( - ScoredPool::withdraw_candidacy(RuntimeOrigin::signed(who), oob_index), - Error::::InvalidIndex - ); - assert_noop!( - ScoredPool::score(RuntimeOrigin::signed(ScoreOrigin::get()), who, oob_index, 99), - Error::::InvalidIndex - ); - assert_noop!( - ScoredPool::kick(RuntimeOrigin::signed(KickOrigin::get()), who, oob_index), - Error::::InvalidIndex - ); - }); -} - -#[test] -fn index_mismatches_should_abort() { - new_test_ext().execute_with(|| { - let who = 40; - let index = 3; - assert_noop!( - ScoredPool::withdraw_candidacy(RuntimeOrigin::signed(who), index), - Error::::WrongAccountIndex - ); - assert_noop!( - ScoredPool::score(RuntimeOrigin::signed(ScoreOrigin::get()), who, index, 99), - Error::::WrongAccountIndex - ); - assert_noop!( - ScoredPool::kick(RuntimeOrigin::signed(KickOrigin::get()), who, index), - Error::::WrongAccountIndex - ); - }); -} - -#[test] -fn withdraw_unscored_candidacy_must_work() { - new_test_ext().execute_with(|| { - // given - let who = 5; - - // when - let index = find_in_pool(who).expect("entity must be in pool") as u32; - assert_ok!(ScoredPool::withdraw_candidacy(RuntimeOrigin::signed(who), index)); - - // then - assert_eq!(fetch_from_pool(5), None); - }); -} - -#[test] -fn withdraw_scored_candidacy_must_work() { - new_test_ext().execute_with(|| { - // given - let who = 40; - assert_eq!(Balances::reserved_balance(who), CandidateDeposit::get()); - - // when - let index = find_in_pool(who).expect("entity must be in pool") as u32; - assert_ok!(ScoredPool::withdraw_candidacy(RuntimeOrigin::signed(who), index)); - - // then - assert_eq!(fetch_from_pool(who), None); - assert_eq!(ScoredPool::members(), vec![20, 31]); - assert_eq!(Balances::reserved_balance(who), 0); - }); -} - -#[test] -fn candidacy_resubmitting_works() { - new_test_ext().execute_with(|| { - // given - let who = 15; - - // when - assert_ok!(ScoredPool::submit_candidacy(RuntimeOrigin::signed(who))); - assert_eq!(ScoredPool::candidate_exists(who), true); - let index = find_in_pool(who).expect("entity must be in pool") as u32; - assert_ok!(ScoredPool::withdraw_candidacy(RuntimeOrigin::signed(who), index)); - assert_eq!(ScoredPool::candidate_exists(who), false); - assert_ok!(ScoredPool::submit_candidacy(RuntimeOrigin::signed(who))); - - // then - assert_eq!(ScoredPool::candidate_exists(who), true); - }); -} - -#[test] -fn pool_candidates_exceeded() { - new_test_ext().execute_with(|| { - for i in [1, 2, 3, 4, 6] { - let who = i as u64; - assert_ok!(ScoredPool::submit_candidacy(RuntimeOrigin::signed(who))); - let index = find_in_pool(who).expect("entity must be in pool") as u32; - assert_ok!(ScoredPool::score( - RuntimeOrigin::signed(ScoreOrigin::get()), - who, - index, - 99 - )); - } - - assert_noop!( - ScoredPool::submit_candidacy(RuntimeOrigin::signed(8)), - Error::::TooManyMembers - ); - }); -} diff --git a/umbrella/Cargo.toml b/umbrella/Cargo.toml index a5b2ac1be8d1..8468874c9623 100644 --- a/umbrella/Cargo.toml +++ b/umbrella/Cargo.toml @@ -146,7 +146,6 @@ std = [ "pallet-safe-mode?/std", "pallet-salary?/std", "pallet-scheduler?/std", - "pallet-scored-pool?/std", "pallet-session-benchmarking?/std", "pallet-session?/std", "pallet-skip-feeless-payment?/std", @@ -496,7 +495,6 @@ try-runtime = [ "pallet-safe-mode?/try-runtime", "pallet-salary?/try-runtime", "pallet-scheduler?/try-runtime", - "pallet-scored-pool?/try-runtime", "pallet-session?/try-runtime", "pallet-skip-feeless-payment?/try-runtime", "pallet-society?/try-runtime", @@ -724,7 +722,6 @@ runtime-full = [ "pallet-safe-mode", "pallet-salary", "pallet-scheduler", - "pallet-scored-pool", "pallet-session", "pallet-session-benchmarking", "pallet-skip-feeless-payment", @@ -1771,11 +1768,6 @@ default-features = false optional = true path = "../substrate/frame/scheduler" -[dependencies.pallet-scored-pool] -default-features = false -optional = true -path = "../substrate/frame/scored-pool" - [dependencies.pallet-session] default-features = false optional = true diff --git a/umbrella/src/lib.rs b/umbrella/src/lib.rs index 6bb57891042f..c6913ad0a892 100644 --- a/umbrella/src/lib.rs +++ b/umbrella/src/lib.rs @@ -706,10 +706,6 @@ pub use pallet_salary; #[cfg(feature = "pallet-scheduler")] pub use pallet_scheduler; -/// FRAME pallet for scored pools. -#[cfg(feature = "pallet-scored-pool")] -pub use pallet_scored_pool; - /// FRAME sessions pallet. #[cfg(feature = "pallet-session")] pub use pallet_session;