From f4bd86e98e37e55b2f01184ca3104615f2795917 Mon Sep 17 00:00:00 2001 From: Keith Yeung <kungfukeith11@gmail.com> Date: Tue, 14 Jun 2022 14:36:14 +0200 Subject: [PATCH] Bounded collator selection pallet (#1337) * Bounded collator selection pallet * Update pallets/collator-selection/src/lib.rs Co-authored-by: Squirrel <gilescope@gmail.com> Co-authored-by: Squirrel <gilescope@gmail.com> --- cumulus/pallets/collator-selection/src/lib.rs | 67 ++++++++++--------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/cumulus/pallets/collator-selection/src/lib.rs b/cumulus/pallets/collator-selection/src/lib.rs index 0aeb44dc68e..918ec95a3d4 100644 --- a/cumulus/pallets/collator-selection/src/lib.rs +++ b/cumulus/pallets/collator-selection/src/lib.rs @@ -89,7 +89,7 @@ pub mod pallet { ValidatorRegistration, }, weights::DispatchClass, - PalletId, + BoundedVec, PalletId, }; use frame_system::{pallet_prelude::*, Config as SystemConfig}; use pallet_session::SessionManager; @@ -123,8 +123,7 @@ pub mod pallet { /// Account Identifier from which the internal Pot is generated. type PotId: Get<PalletId>; - /// Maximum number of candidates that we should have. This is used for benchmarking and is not - /// enforced. + /// Maximum number of candidates that we should have. This is enforced in code. /// /// This does not take into account the invulnerables. type MaxCandidates: Get<u32>; @@ -134,9 +133,7 @@ pub mod pallet { /// This does not take into account the invulnerables. type MinCandidates: Get<u32>; - /// Maximum number of invulnerables. - /// - /// Used only for benchmarking. + /// Maximum number of invulnerables. This is enforced in code. type MaxInvulnerables: Get<u32>; // Will be kicked if block is not produced in threshold. @@ -158,7 +155,9 @@ pub mod pallet { } /// Basic information about a collation candidate. - #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, scale_info::TypeInfo)] + #[derive( + PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, scale_info::TypeInfo, MaxEncodedLen, + )] pub struct CandidateInfo<AccountId, Balance> { /// Account identifier. pub who: AccountId, @@ -168,19 +167,22 @@ pub mod pallet { #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] - #[pallet::without_storage_info] pub struct Pallet<T>(_); /// The invulnerable, fixed collators. #[pallet::storage] #[pallet::getter(fn invulnerables)] - pub type Invulnerables<T: Config> = StorageValue<_, Vec<T::AccountId>, ValueQuery>; + pub type Invulnerables<T: Config> = + StorageValue<_, BoundedVec<T::AccountId, T::MaxInvulnerables>, ValueQuery>; /// The (community, limited) collation candidates. #[pallet::storage] #[pallet::getter(fn candidates)] - pub type Candidates<T: Config> = - StorageValue<_, Vec<CandidateInfo<T::AccountId, BalanceOf<T>>>, ValueQuery>; + pub type Candidates<T: Config> = StorageValue< + _, + BoundedVec<CandidateInfo<T::AccountId, BalanceOf<T>>, T::MaxCandidates>, + ValueQuery, + >; /// Last block authored by collator. #[pallet::storage] @@ -230,10 +232,9 @@ pub mod pallet { "duplicate invulnerables in genesis." ); - assert!( - T::MaxInvulnerables::get() >= (self.invulnerables.len() as u32), - "genesis invulnerables are more than T::MaxInvulnerables", - ); + let bounded_invulnerables = + BoundedVec::<_, T::MaxInvulnerables>::try_from(self.invulnerables.clone()) + .expect("genesis invulnerables are more than T::MaxInvulnerables"); assert!( T::MaxCandidates::get() >= self.desired_candidates, "genesis desired_candidates are more than T::MaxCandidates", @@ -241,7 +242,7 @@ pub mod pallet { <DesiredCandidates<T>>::put(&self.desired_candidates); <CandidacyBond<T>>::put(&self.candidacy_bond); - <Invulnerables<T>>::put(&self.invulnerables); + <Invulnerables<T>>::put(bounded_invulnerables); } } @@ -270,6 +271,8 @@ pub mod pallet { AlreadyCandidate, /// User is not a candidate NotCandidate, + /// Too many invulnerables + TooManyInvulnerables, /// User is already an Invulnerable AlreadyInvulnerable, /// Account has no associated validator ID @@ -290,15 +293,11 @@ pub mod pallet { new: Vec<T::AccountId>, ) -> DispatchResultWithPostInfo { T::UpdateOrigin::ensure_origin(origin)?; - // we trust origin calls, this is just a for more accurate benchmarking - if (new.len() as u32) > T::MaxInvulnerables::get() { - log::warn!( - "invulnerables > T::MaxInvulnerables; you might need to run benchmarks again" - ); - } + let bounded_invulnerables = BoundedVec::<_, T::MaxInvulnerables>::try_from(new) + .map_err(|_| Error::<T>::TooManyInvulnerables)?; // check if the invulnerables have associated validator keys before they are set - for account_id in &new { + for account_id in bounded_invulnerables.iter() { let validator_key = T::ValidatorIdOf::convert(account_id.clone()) .ok_or(Error::<T>::NoAssociatedValidatorId)?; ensure!( @@ -307,8 +306,10 @@ pub mod pallet { ); } - <Invulnerables<T>>::put(&new); - Self::deposit_event(Event::NewInvulnerables { invulnerables: new }); + <Invulnerables<T>>::put(&bounded_invulnerables); + Self::deposit_event(Event::NewInvulnerables { + invulnerables: bounded_invulnerables.to_vec(), + }); Ok(().into()) } @@ -372,7 +373,7 @@ pub mod pallet { Err(Error::<T>::AlreadyCandidate)? } else { T::Currency::reserve(&who, deposit)?; - candidates.push(incoming); + candidates.try_push(incoming).map_err(|_| Error::<T>::TooManyCandidates)?; <LastAuthoredBlock<T>>::insert( who.clone(), frame_system::Pallet::<T>::block_number() + T::KickThreshold::get(), @@ -430,8 +431,10 @@ pub mod pallet { /// Assemble the current set of candidates and invulnerables into the next collator set. /// /// This is done on the fly, as frequent as we are told to do so, as the session manager. - pub fn assemble_collators(candidates: Vec<T::AccountId>) -> Vec<T::AccountId> { - let mut collators = Self::invulnerables(); + pub fn assemble_collators( + candidates: BoundedVec<T::AccountId, T::MaxCandidates>, + ) -> Vec<T::AccountId> { + let mut collators = Self::invulnerables().to_vec(); collators.extend(candidates); collators } @@ -439,8 +442,8 @@ pub mod pallet { /// Kicks out candidates that did not produce a block in the kick threshold /// and refund their deposits. pub fn kick_stale_candidates( - candidates: Vec<CandidateInfo<T::AccountId, BalanceOf<T>>>, - ) -> Vec<T::AccountId> { + candidates: BoundedVec<CandidateInfo<T::AccountId, BalanceOf<T>>, T::MaxCandidates>, + ) -> BoundedVec<T::AccountId, T::MaxCandidates> { let now = frame_system::Pallet::<T>::block_number(); let kick_threshold = T::KickThreshold::get(); candidates @@ -461,7 +464,9 @@ pub mod pallet { None } }) - .collect() + .collect::<Vec<_>>() + .try_into() + .expect("filter_map operation can't result in a bounded vec larger than its original; qed") } } -- GitLab