difftreelog
refactor(collator-selection) rename revoke to release + update tx + cargo fmt
in: master
13 files changed
pallets/collator-selection/src/lib.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617// Original license:18// Copyright (C) 2021 Parity Technologies (UK) Ltd.19// SPDX-License-Identifier: Apache-2.02021// Licensed under the Apache License, Version 2.0 (the "License");22// you may not use this file except in compliance with the License.23// You may obtain a copy of the License at24//25// http://www.apache.org/licenses/LICENSE-2.026//27// Unless required by applicable law or agreed to in writing, software28// distributed under the License is distributed on an "AS IS" BASIS,29// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.30// See the License for the specific language governing permissions and31// limitations under the License.3233// todo:collator documentation34//! Collator Selection pallet.35//!36//! A pallet to manage collators in a parachain.37//!38//! ## Overview39//!40//! The Collator Selection pallet manages the collators of a parachain. **Collation is _not_ a41//! secure activity** and this pallet does not implement any game-theoretic mechanisms to meet BFT42//! safety assumptions of the chosen set.43//!44//! ## Terminology45//!46//! - Collator: A parachain block producer.47//! - Bond: An amount of `Balance` _reserved_ for candidate registration.48//! - Invulnerable: An account guaranteed to be in the collator set.49//!50//! ## Implementation51//!52//! The final `Collators` are aggregated from two individual lists:53//!54//! 1. [`Invulnerables`]: a set of collators appointed by governance. These accounts will always be55//! collators.56//! 2. [`Candidates`]: these are *candidates to the collation task* and may or may not be elected as57//! a final collator.58//!59//! The current implementation resolves congestion of [`Candidates`] in a first-come-first-serve60//! manner.61//!62//! Candidates will not be allowed to get kicked or leave_intent if the total number of candidates63//! fall below MinCandidates. This is for potential disaster recovery scenarios.64//!65//! ### Rewards66//!67//! The Collator Selection pallet maintains an on-chain account (the "Pot"). In each block, the68//! collator who authored it receives:69//!70//! - Half the value of the Pot.71//! - Half the value of the transaction fees within the block. The other half of the transaction72//! fees are deposited into the Pot.73//!74//! To initiate rewards an ED needs to be transferred to the pot address.75//!76//! Note: Eventually the Pot distribution may be modified as discussed in77//! [this issue](https://github.com/paritytech/statemint/issues/21#issuecomment-810481073).7879#![cfg_attr(not(feature = "std"), no_std)]8081pub use pallet::*;8283#[cfg(test)]84mod mock;8586#[cfg(test)]87mod tests;8889#[cfg(feature = "runtime-benchmarks")]90mod benchmarking;91pub mod weights;9293#[frame_support::pallet]94pub mod pallet {95 pub use crate::weights::WeightInfo;96 use core::ops::Div;97 use frame_support::{98 dispatch::{DispatchClass, DispatchResultWithPostInfo},99 inherent::Vec,100 pallet_prelude::*,101 sp_runtime::traits::{AccountIdConversion, CheckedSub, Saturating, Zero},102 traits::{103 Currency, EnsureOrigin, ExistenceRequirement::KeepAlive, ReservableCurrency,104 ValidatorRegistration,105 },106 BoundedVec, PalletId,107 };108 use frame_system::pallet_prelude::*;109 use pallet_session::SessionManager;110 use sp_runtime::{Perbill, traits::Convert};111 use pallet_configuration::{112 CollatorSelectionDesiredCollatorsOverride as DesiredCollators,113 CollatorSelectionLicenseBondOverride as LicenseBond,114 CollatorSelectionKickThresholdOverride as KickThreshold, BalanceOf,115 };116 use sp_staking::SessionIndex;117118 /// A convertor from collators id. Since this pallet does not have stash/controller, this is119 /// just identity.120 pub struct IdentityCollator;121 impl<T> sp_runtime::traits::Convert<T, Option<T>> for IdentityCollator {122 fn convert(t: T) -> Option<T> {123 Some(t)124 }125 }126127 /// Configure the pallet by specifying the parameters and types on which it depends.128 #[pallet::config]129 pub trait Config: frame_system::Config + pallet_configuration::Config {130 /// Overarching event type.131 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;132133 /// Origin that can dictate updating parameters of this pallet.134 type UpdateOrigin: EnsureOrigin<Self::RuntimeOrigin>;135136 /// Account Identifier that holds the chain's treasury.137 type TreasuryAccountId: Get<Self::AccountId>;138139 /// Account Identifier from which the internal Pot is generated.140 type PotId: Get<PalletId>;141142 /// Maximum number of candidates and invulnerables that we should have. This is enforced in code.143 type MaxCollators: Get<u32>;144145 /// If kicked, how much of the collator's deposit will be slashed and sent to the slash destination.146 type SlashRatio: Get<Perbill>;147148 /// A stable ID for a validator.149 type ValidatorId: Member + Parameter;150151 /// A conversion from account ID to validator ID.152 ///153 /// Its cost must be at most one storage read.154 type ValidatorIdOf: Convert<Self::AccountId, Option<Self::ValidatorId>>;155156 /// Validate a user is registered157 type ValidatorRegistration: ValidatorRegistration<Self::ValidatorId>;158159 /// The weight information of this pallet.160 type WeightInfo: WeightInfo;161 }162163 #[pallet::pallet]164 #[pallet::generate_store(pub(super) trait Store)]165 pub struct Pallet<T>(_);166167 /// The invulnerable, fixed collators.168 #[pallet::storage]169 #[pallet::getter(fn invulnerables)]170 pub type Invulnerables<T: Config> =171 StorageValue<_, BoundedVec<T::AccountId, T::MaxCollators>, ValueQuery>;172173 /// The (community) collation license holders.174 #[pallet::storage]175 #[pallet::getter(fn license_deposit_of)]176 pub type LicenseDepositOf<T: Config> =177 StorageMap<_, Blake2_128Concat, T::AccountId, BalanceOf<T>, ValueQuery>;178179 /// The (community, limited) collation candidates.180 #[pallet::storage]181 #[pallet::getter(fn candidates)]182 pub type Candidates<T: Config> =183 StorageValue<_, BoundedVec<T::AccountId, T::MaxCollators>, ValueQuery>;184185 /// Last block authored by collator.186 #[pallet::storage]187 #[pallet::getter(fn last_authored_block)]188 pub type LastAuthoredBlock<T: Config> =189 StorageMap<_, Twox64Concat, T::AccountId, T::BlockNumber, ValueQuery>;190191 #[pallet::genesis_config]192 pub struct GenesisConfig<T: Config> {193 pub invulnerables: Vec<T::AccountId>,194 }195196 #[cfg(feature = "std")]197 impl<T: Config> Default for GenesisConfig<T> {198 fn default() -> Self {199 Self {200 invulnerables: Default::default(),201 }202 }203 }204205 #[pallet::genesis_build]206 impl<T: Config> GenesisBuild<T> for GenesisConfig<T> {207 fn build(&self) {208 let duplicate_invulnerables = self209 .invulnerables210 .iter()211 .collect::<std::collections::BTreeSet<_>>();212 assert!(213 duplicate_invulnerables.len() == self.invulnerables.len(),214 "duplicate invulnerables in genesis."215 );216217 let bounded_invulnerables =218 BoundedVec::<_, T::MaxCollators>::try_from(self.invulnerables.clone())219 .expect("genesis invulnerables are more than T::MaxCollators");220 221 <Invulnerables<T>>::put(bounded_invulnerables);222 }223 }224225 #[pallet::event]226 #[pallet::generate_deposit(pub(super) fn deposit_event)]227 pub enum Event<T: Config> {228 InvulnerableAdded {229 invulnerable: T::AccountId,230 },231 InvulnerableRemoved {232 invulnerable: T::AccountId,233 },234 LicenseObtained {235 account_id: T::AccountId,236 deposit: BalanceOf<T>,237 },238 LicenseForfeited {239 account_id: T::AccountId,240 deposit_returned: BalanceOf<T>,241 },242 CandidateAdded {243 account_id: T::AccountId,244 },245 CandidateRemoved {246 account_id: T::AccountId,247 },248 }249250 // Errors inform users that something went wrong.251 #[pallet::error]252 pub enum Error<T> {253 /// Too many candidates254 TooManyCandidates,255 /// Unknown error256 Unknown,257 /// Permission issue258 Permission,259 /// User already holds license to collate260 AlreadyHoldingLicense,261 /// User does not hold a license to collate262 NoLicense,263 /// User is already a candidate264 AlreadyCandidate,265 /// User is not a candidate266 NotCandidate,267 /// Too many invulnerables268 TooManyInvulnerables,269 /// Too few invulnerables270 TooFewInvulnerables,271 /// User is already an Invulnerable272 AlreadyInvulnerable,273 /// User is not an Invulnerable274 NotInvulnerable,275 /// Account has no associated validator ID276 NoAssociatedValidatorId,277 /// Validator ID is not yet registered278 ValidatorNotRegistered,279 }280281 #[pallet::hooks]282 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {}283284 #[pallet::call]285 impl<T: Config> Pallet<T> {286 /// Add a collator to the list of invulnerable (fixed) collators.287 #[pallet::weight(T::WeightInfo::set_invulnerables(1u32))] // todo:collator weight288 pub fn add_invulnerable(289 origin: OriginFor<T>,290 new: T::AccountId,291 ) -> DispatchResultWithPostInfo {292 T::UpdateOrigin::ensure_origin(origin)?;293294 // check if the new invulnerable has associated validator keys before it is added295 let validator_key = T::ValidatorIdOf::convert(new.clone())296 .ok_or(Error::<T>::NoAssociatedValidatorId)?;297 ensure!(298 T::ValidatorRegistration::is_registered(&validator_key),299 Error::<T>::ValidatorNotRegistered300 );301 if Self::invulnerables().contains(&new) {302 return Ok(().into());303 }304305 <Invulnerables<T>>::try_append(new.clone())306 .map_err(|_| Error::<T>::TooManyInvulnerables)?;307308 // try to offboard the new invulnerable if it was a collator candidate before309 let _ = Self::try_remove_candidate(&new);310311 Self::deposit_event(Event::InvulnerableAdded { invulnerable: new });312 Ok(().into())313 }314315 /// Remove a collator from the list of invulnerable (fixed) collators.316 #[pallet::weight(T::WeightInfo::set_invulnerables(1))] // todo:collator weight317 pub fn remove_invulnerable(318 origin: OriginFor<T>,319 who: T::AccountId,320 ) -> DispatchResultWithPostInfo {321 T::UpdateOrigin::ensure_origin(origin)?;322323 <Invulnerables<T>>::try_mutate(|invulnerables| -> DispatchResult {324 if invulnerables.len() <= 1 {325 return Err(Error::<T>::TooFewInvulnerables.into());326 }327328 let index = invulnerables329 .into_iter()330 .position(|r| *r == who)331 .ok_or(Error::<T>::NotInvulnerable)?;332 invulnerables.remove(index);333 Ok(())334 })?;335 Self::deposit_event(Event::InvulnerableRemoved { invulnerable: who });336 Ok(().into())337 }338339 /// Purchase a license on block collation for this account.340 /// It does not make it a collator candidate, use `onboard` afterward. The account must341 /// (a) already have registered session keys and (b) be able to reserve the `LicenseBond`.342 ///343 /// This call is not available to `Invulnerable` collators.344 #[pallet::weight(T::WeightInfo::register_as_candidate(T::MaxCollators::get()))] // todo:collator weight345 pub fn get_license(origin: OriginFor<T>) -> DispatchResultWithPostInfo {346 // register_as_candidate347 let who = ensure_signed(origin)?;348349 if LicenseDepositOf::<T>::contains_key(&who) {350 return Err(Error::<T>::AlreadyHoldingLicense.into());351 }352353 let validator_key = T::ValidatorIdOf::convert(who.clone())354 .ok_or(Error::<T>::NoAssociatedValidatorId)?;355 ensure!(356 T::ValidatorRegistration::is_registered(&validator_key),357 Error::<T>::ValidatorNotRegistered358 );359360 let deposit = <LicenseBond<T>>::get();361362 T::Currency::reserve(&who, deposit)?;363 LicenseDepositOf::<T>::insert(who.clone(), deposit);364365 Self::deposit_event(Event::LicenseObtained {366 account_id: who,367 deposit,368 });369 Ok(().into()) // Some(T::WeightInfo::register_as_candidate(current_count as u32)).into())370 }371372 /// Register this account as a candidate for collators for next sessions.373 /// The account must already hold a license, and cannot offboard immediately during a session.374 ///375 /// This call is not available to `Invulnerable` collators.376 #[pallet::weight(T::WeightInfo::register_as_candidate(T::MaxCollators::get()))] // todo:collator weight377 pub fn onboard(origin: OriginFor<T>) -> DispatchResultWithPostInfo {378 // register_as_candidate379 let who = ensure_signed(origin)?;380381 // ensure the user obtained the license.382 ensure!(383 LicenseDepositOf::<T>::contains_key(&who),384 Error::<T>::NoLicense385 );386 // ensure we are below limit.387 let length = <Candidates<T>>::decode_len().unwrap_or_default()388 + <Invulnerables<T>>::decode_len().unwrap_or_default();389 ensure!(390 (length as u32) < <DesiredCollators<T>>::get(),391 Error::<T>::TooManyCandidates392 );393 ensure!(394 !Self::invulnerables().contains(&who),395 Error::<T>::AlreadyInvulnerable396 );397398 let current_count =399 <Candidates<T>>::try_mutate(|candidates| -> Result<usize, DispatchError> {400 if candidates.iter().any(|candidate| *candidate == who) {401 Err(Error::<T>::AlreadyCandidate)?402 } else {403 candidates404 .try_push(who.clone())405 .map_err(|_| Error::<T>::TooManyCandidates)?;406 // First authored block is current block plus kick threshold to handle session delay407 <LastAuthoredBlock<T>>::insert(408 who.clone(),409 frame_system::Pallet::<T>::block_number() + <KickThreshold<T>>::get(),410 );411 Ok(candidates.len())412 }413 })?;414415 Self::deposit_event(Event::CandidateAdded { account_id: who });416 Ok(Some(T::WeightInfo::register_as_candidate(current_count as u32)).into())417 }418419 /// Deregister `origin` as a collator candidate. Note that the collator can only leave on420 /// session change. The license to `onboard` later at any other time will remain.421 #[pallet::weight(T::WeightInfo::leave_intent(T::MaxCollators::get()))] // todo:collator weight422 pub fn offboard(origin: OriginFor<T>) -> DispatchResultWithPostInfo {423 // leave_intent424 let who = ensure_signed(origin)?;425 let current_count = Self::try_remove_candidate(&who)?;426427 Ok(Some(T::WeightInfo::leave_intent(current_count as u32)).into()) // todo:collator weight428 }429430 /// Forfeit `origin`'s own license. The `LicenseBond` will be unreserved immediately.431 ///432 /// This call is not available to `Invulnerable` collators.433 #[pallet::weight(T::WeightInfo::leave_intent(T::MaxCollators::get()))] // todo:collator weight434 pub fn release_license(origin: OriginFor<T>) -> DispatchResultWithPostInfo {435 // leave_intent436 let who = ensure_signed(origin)?;437438 let current_count = Self::try_remove_candidate_and_release_license(&who, false, true)?;439440 Ok(Some(T::WeightInfo::leave_intent(current_count as u32)).into()) // todo:collator weight441 }442443 /// Force deregister `origin` as a collator candidate as a governing authority, and revoke its license.444 /// Note that the collator can only leave on session change.445 /// The `LicenseBond` will be unreserved and returned immediately.446 ///447 /// This call is, of course, not applicable to `Invulnerable` collators.448 #[pallet::weight(T::WeightInfo::leave_intent(T::MaxCollators::get()))] // todo:collator weight449 pub fn force_revoke_license(450 origin: OriginFor<T>,451 who: T::AccountId,452 ) -> DispatchResultWithPostInfo {453 // leave_intent454 T::UpdateOrigin::ensure_origin(origin)?;455456 let current_count = Self::try_remove_candidate_and_release_license(&who, false, true)?;457458 Ok(Some(T::WeightInfo::leave_intent(current_count as u32)).into()) // todo:collator weight459 }460 }461462 impl<T: Config> Pallet<T> {463 /// Get a unique, inaccessible account id from the `PotId`.464 pub fn account_id() -> T::AccountId {465 T::PotId::get().into_account_truncating()466 }467468 /// Removes a candidate and their license, optionally slashed and optionally ignoring,469 /// whether or not they actually are a candidate.470 fn try_remove_candidate_and_release_license(471 who: &T::AccountId,472 should_slash: bool,473 ignore_if_not_candidate: bool,474 ) -> Result<usize, DispatchError> {475 let current_count = Self::try_remove_candidate(who);476 let current_count = if ignore_if_not_candidate477 && current_count == Err(Error::<T>::NotCandidate.into())478 {479 <Candidates<T>>::decode_len().unwrap_or_default()480 } else {481 current_count?482 };483 Self::try_release_license(who, should_slash)?;484 Ok(current_count)485 }486487 /// Removes a candidate from the collator pool for the next session if they exist.488 fn try_remove_candidate(who: &T::AccountId) -> Result<usize, DispatchError> {489 let current_count =490 <Candidates<T>>::try_mutate(|candidates| -> Result<usize, DispatchError> {491 let index = candidates492 .iter()493 .position(|candidate| *candidate == *who)494 .ok_or(Error::<T>::NotCandidate)?;495 candidates.remove(index);496 <LastAuthoredBlock<T>>::remove(who.clone());497 Ok(candidates.len())498 })?;499 Self::deposit_event(Event::CandidateRemoved {500 account_id: who.clone(),501 });502 Ok(current_count)503 }504505 /// Removes a candidate if they exist and sends them back their deposit, optionally slashed.506 fn try_release_license(who: &T::AccountId, should_slash: bool) -> DispatchResult {507 let mut deposit_returned = BalanceOf::<T>::default();508 LicenseDepositOf::<T>::try_mutate_exists(who, |deposit| -> DispatchResult {509 if let Some(deposit) = deposit.take() {510 if should_slash {511 let slashed = T::SlashRatio::get() * deposit;512 let remaining = deposit - slashed;513514 let (imbalance, _) = T::Currency::slash_reserved(who, slashed);515 //T::Currency::unreserve(who, remaining);516 deposit_returned = remaining;517518 T::Currency::resolve_creating(&T::TreasuryAccountId::get(), imbalance);519 } else {520 //T::Currency::unreserve(who, deposit);521 deposit_returned = deposit;522 }523524 T::Currency::unreserve(who, deposit_returned);525 Ok(())526 } else {527 Err(Error::<T>::NoLicense.into())528 }529 })?;530 Self::deposit_event(Event::LicenseForfeited {531 account_id: who.clone(),532 deposit_returned,533 });534 Ok(())535 }536537 /// Assemble the current set of candidates and invulnerables into the next collator set.538 ///539 /// This is done on the fly, as frequent as we are told to do so, as the session manager.540 pub fn assemble_collators(541 candidates: BoundedVec<T::AccountId, T::MaxCollators>,542 ) -> Vec<T::AccountId> {543 let mut collators = Self::invulnerables().to_vec();544 collators.extend(candidates);545 collators546 }547548 /// Kicks out candidates that did not produce a block in the kick threshold549 /// and **confiscates** their deposits to the treasury.550 pub fn kick_stale_candidates(551 candidates: BoundedVec<T::AccountId, T::MaxCollators>,552 ) -> BoundedVec<T::AccountId, T::MaxCollators> {553 let now = frame_system::Pallet::<T>::block_number();554 let kick_threshold = <KickThreshold<T>>::get();555 candidates556 .into_iter()557 .filter_map(|c| {558 let last_block = <LastAuthoredBlock<T>>::get(c.clone());559 let since_last = now.saturating_sub(last_block);560 if since_last < kick_threshold {561 Some(c)562 } else {563 let outcome = Self::try_remove_candidate_and_release_license(&c, true, false);564 if let Err(why) = outcome {565 log::warn!("Failed to kick collator and release license {:?}", why);566 debug_assert!(false, "failed to kick collator and release license {why:?}");567 }568 None569 }570 })571 .collect::<Vec<_>>()572 .try_into()573 .expect("filter_map operation can't result in a bounded vec larger than its original; qed")574 }575 }576577 /// Keep track of number of authored blocks per authority, uncles are counted as well since578 /// they're a valid proof of being online.579 impl<T: Config + pallet_authorship::Config>580 pallet_authorship::EventHandler<T::AccountId, T::BlockNumber> for Pallet<T>581 {582 fn note_author(author: T::AccountId) {583 let pot = Self::account_id();584 // assumes an ED will be sent to pot.585 let reward = T::Currency::free_balance(&pot)586 .checked_sub(&T::Currency::minimum_balance())587 .unwrap_or_else(Zero::zero)588 .div(2u32.into());589 // `reward` is half of pot account minus ED, this should never fail.590 let _success = T::Currency::transfer(&pot, &author, reward, KeepAlive);591 debug_assert!(_success.is_ok());592 <LastAuthoredBlock<T>>::insert(author, frame_system::Pallet::<T>::block_number());593594 frame_system::Pallet::<T>::register_extra_weight_unchecked(595 T::WeightInfo::note_author(),596 DispatchClass::Mandatory,597 );598 }599600 fn note_uncle(_author: T::AccountId, _age: T::BlockNumber) {601 //TODO can we ignore this?602 }603 }604605 /// Play the role of the session manager.606 impl<T: Config> SessionManager<T::AccountId> for Pallet<T> {607 fn new_session(index: SessionIndex) -> Option<Vec<T::AccountId>> {608 log::info!(609 "assembling new collators for new session {} at #{:?}",610 index,611 <frame_system::Pallet<T>>::block_number(),612 );613614 let candidates = Self::candidates();615 let candidates_len_before = candidates.len();616 let active_candidates = Self::kick_stale_candidates(candidates);617 let removed = candidates_len_before - active_candidates.len();618 let result = Self::assemble_collators(active_candidates);619620 frame_system::Pallet::<T>::register_extra_weight_unchecked(621 T::WeightInfo::new_session(candidates_len_before as u32, removed as u32),622 DispatchClass::Mandatory,623 );624 Some(result)625 }626 fn start_session(_: SessionIndex) {627 // we don't care.628 }629 fn end_session(_: SessionIndex) {630 // we don't care.631 }632 }633}pallets/collator-selection/src/mock.rsdiffbeforeafterboth--- a/pallets/collator-selection/src/mock.rs
+++ b/pallets/collator-selection/src/mock.rs
@@ -281,9 +281,7 @@
)
})
.collect::<Vec<_>>();
- let collator_selection = collator_selection::GenesisConfig::<Test> {
- invulnerables,
- };
+ let collator_selection = collator_selection::GenesisConfig::<Test> { invulnerables };
let session = pallet_session::GenesisConfig::<Test> { keys };
pallet_balances::GenesisConfig::<Test> { balances }
.assimilate_storage(&mut t)
pallets/collator-selection/src/tests.rsdiffbeforeafterboth--- a/pallets/collator-selection/src/tests.rs
+++ b/pallets/collator-selection/src/tests.rs
@@ -380,7 +380,7 @@
}
#[test]
-fn force_revoke_license() {
+fn force_release_license() {
new_test_ext().execute_with(|| {
// obtain a license to collate and reserve the bond.
assert_ok!(CollatorSelection::get_license(RuntimeOrigin::signed(3)));
@@ -388,12 +388,12 @@
// cannot execute the operation as non-root
assert_noop!(
- CollatorSelection::force_revoke_license(RuntimeOrigin::signed(3), 3),
+ CollatorSelection::force_release_license(RuntimeOrigin::signed(3), 3),
BadOrigin
);
// release the license and get the bond back.
- assert_ok!(CollatorSelection::force_revoke_license(
+ assert_ok!(CollatorSelection::force_release_license(
RuntimeOrigin::signed(RootAccount::get()),
3
));
@@ -404,7 +404,7 @@
assert_eq!(Balances::free_balance(3), 90);
// can release license even if onboarded.
- assert_ok!(CollatorSelection::force_revoke_license(
+ assert_ok!(CollatorSelection::force_release_license(
RuntimeOrigin::signed(RootAccount::get()),
3
));
@@ -532,9 +532,7 @@
.unwrap();
let invulnerables = vec![1, 1];
- let collator_selection = collator_selection::GenesisConfig::<Test> {
- invulnerables,
- };
+ let collator_selection = collator_selection::GenesisConfig::<Test> { invulnerables };
// collator selection must be initialized before session.
collator_selection.assimilate_storage(&mut t).unwrap();
}
pallets/configuration/src/lib.rsdiffbeforeafterboth--- a/pallets/configuration/src/lib.rs
+++ b/pallets/configuration/src/lib.rs
@@ -201,6 +201,7 @@
Ok(())
}
+ #[pallet::call_index(4)]
#[pallet::weight(T::DbWeight::get().writes(1))]
pub fn set_collator_selection_desired_collators(
origin: OriginFor<T>,
@@ -216,10 +217,13 @@
} else {
<CollatorSelectionDesiredCollatorsOverride<T>>::kill();
}
- Self::deposit_event(Event::NewDesiredCollators { desired_collators: max });
+ Self::deposit_event(Event::NewDesiredCollators {
+ desired_collators: max,
+ });
Ok(())
}
+ #[pallet::call_index(5)]
#[pallet::weight(T::DbWeight::get().writes(1))]
pub fn set_collator_selection_license_bond(
origin: OriginFor<T>,
@@ -235,6 +239,7 @@
Ok(())
}
+ #[pallet::call_index(6)]
#[pallet::weight(T::DbWeight::get().writes(1))]
pub fn set_collator_selection_kick_threshold(
origin: OriginFor<T>,
@@ -246,7 +251,9 @@
} else {
<CollatorSelectionKickThresholdOverride<T>>::kill();
}
- Self::deposit_event(Event::NewCollatorKickThreshold { length_in_blocks: threshold });
+ Self::deposit_event(Event::NewCollatorKickThreshold {
+ length_in_blocks: threshold,
+ });
Ok(())
}
}
runtime/common/data_management.rsdiffbeforeafterboth--- a/runtime/common/data_management.rs
+++ b/runtime/common/data_management.rs
@@ -58,10 +58,10 @@
_info: &DispatchInfoOf<Self::Call>,
_len: usize,
) -> TransactionValidity {
- match call {
- #[cfg(feature = "collator-selection")]
- RuntimeCall::Identity(_) => Err(TransactionValidityError::Invalid(InvalidTransaction::Call)),
- _ => Ok(ValidTransaction::default()),
- }
+ match call {
+ #[cfg(feature = "collator-selection")]
+ RuntimeCall::Identity(_) => Err(TransactionValidityError::Invalid(InvalidTransaction::Call)),
+ _ => Ok(ValidTransaction::default()),
+ }
}
}
runtime/common/mod.rsdiffbeforeafterboth--- a/runtime/common/mod.rs
+++ b/runtime/common/mod.rs
@@ -16,11 +16,11 @@
pub mod config;
pub mod construct_runtime;
+pub mod data_management;
pub mod dispatch;
pub mod ethereum;
pub mod instance;
pub mod maintenance;
-pub mod data_management;
pub mod runtime_apis;
pub mod xcm;
runtime/common/tests/mod.rsdiffbeforeafterboth--- a/runtime/common/tests/mod.rs
+++ b/runtime/common/tests/mod.rs
@@ -63,9 +63,7 @@
.collect::<Vec<_>>();
let cfg = GenesisConfig {
- collator_selection: CollatorSelectionConfig {
- invulnerables,
- },
+ collator_selection: CollatorSelectionConfig { invulnerables },
session: SessionConfig { keys },
parachain_info: ParachainInfoConfig {
parachain_id: para_id.into(),
tests/src/collatorSelection.seqtest.tsdiffbeforeafterboth--- a/tests/src/collatorSelection.seqtest.ts
+++ b/tests/src/collatorSelection.seqtest.ts
@@ -209,7 +209,7 @@
expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(licenseBond);
// force-releasing a license un-reserves the license bond cost as well
- await helper.getSudo().collatorSelection.forceRevokeLicense(superuser, account.address);
+ await helper.getSudo().collatorSelection.forceReleaseLicense(superuser, account.address);
expect(await helper.collatorSelection.hasLicense(account.address)).to.be.equal(previousBalance.reserved);
const balance = await helper.balance.getSubstrateFull(account.address);
@@ -243,7 +243,7 @@
itSub('Cannot force revoke a license as non-sudo', async ({helper}) => {
const account = crowd.pop()!;
await helper.collatorSelection.obtainLicense(account);
- await expect(helper.collatorSelection.forceRevokeLicense(superuser, account.address))
+ await expect(helper.collatorSelection.forceReleaseLicense(superuser, account.address))
.to.be.rejectedWith(/BadOrigin/);
});
});
@@ -459,7 +459,7 @@
const candidates = await helper.collatorSelection.getCandidates();
let nonce = await helper.chain.getNonce(superuser.address);
await Promise.all(candidates.map(candidate =>
- helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.forceRevokeLicense', [candidate], true, {nonce: nonce++})));
+ helper.getSudo().executeExtrinsic(superuser, 'api.tx.collatorSelection.forceReleaseLicense', [candidate], true, {nonce: nonce++})));
});
});
});
\ No newline at end of file
tests/src/interfaces/augment-api-tx.tsdiffbeforeafterboth--- a/tests/src/interfaces/augment-api-tx.ts
+++ b/tests/src/interfaces/augment-api-tx.ts
@@ -237,7 +237,7 @@
*
* This call is, of course, not applicable to `Invulnerable` collators.
**/
- forceRevokeLicense: AugmentedSubmittable<(who: AccountId32 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [AccountId32]>;
+ forceReleaseLicense: AugmentedSubmittable<(who: AccountId32 | string | Uint8Array) => SubmittableExtrinsic<ApiType>, [AccountId32]>;
/**
* Purchase a license on block collation for this account.
* It does not make it a collator candidate, use `onboard` afterward. The account must
tests/src/interfaces/default/types.tsdiffbeforeafterboth--- a/tests/src/interfaces/default/types.ts
+++ b/tests/src/interfaces/default/types.ts
@@ -1243,11 +1243,11 @@
readonly isOnboard: boolean;
readonly isOffboard: boolean;
readonly isReleaseLicense: boolean;
- readonly isForceRevokeLicense: boolean;
- readonly asForceRevokeLicense: {
+ readonly isForceReleaseLicense: boolean;
+ readonly asForceReleaseLicense: {
readonly who: AccountId32;
} & Struct;
- readonly type: 'AddInvulnerable' | 'RemoveInvulnerable' | 'GetLicense' | 'Onboard' | 'Offboard' | 'ReleaseLicense' | 'ForceRevokeLicense';
+ readonly type: 'AddInvulnerable' | 'RemoveInvulnerable' | 'GetLicense' | 'Onboard' | 'Offboard' | 'ReleaseLicense' | 'ForceReleaseLicense';
}
/** @name PalletCollatorSelectionError */
tests/src/interfaces/lookup.tsdiffbeforeafterboth--- a/tests/src/interfaces/lookup.ts
+++ b/tests/src/interfaces/lookup.ts
@@ -1692,7 +1692,7 @@
onboard: 'Null',
offboard: 'Null',
release_license: 'Null',
- force_revoke_license: {
+ force_release_license: {
who: 'AccountId32'
}
}
tests/src/interfaces/types-lookup.tsdiffbeforeafterboth--- a/tests/src/interfaces/types-lookup.ts
+++ b/tests/src/interfaces/types-lookup.ts
@@ -1867,11 +1867,11 @@
readonly isOnboard: boolean;
readonly isOffboard: boolean;
readonly isReleaseLicense: boolean;
- readonly isForceRevokeLicense: boolean;
- readonly asForceRevokeLicense: {
+ readonly isForceReleaseLicense: boolean;
+ readonly asForceReleaseLicense: {
readonly who: AccountId32;
} & Struct;
- readonly type: 'AddInvulnerable' | 'RemoveInvulnerable' | 'GetLicense' | 'Onboard' | 'Offboard' | 'ReleaseLicense' | 'ForceRevokeLicense';
+ readonly type: 'AddInvulnerable' | 'RemoveInvulnerable' | 'GetLicense' | 'Onboard' | 'Offboard' | 'ReleaseLicense' | 'ForceReleaseLicense';
}
/** @name PalletCollatorSelectionError (185) */
tests/src/util/playgrounds/unique.tsdiffbeforeafterboth--- a/tests/src/util/playgrounds/unique.ts
+++ b/tests/src/util/playgrounds/unique.ts
@@ -2772,8 +2772,8 @@
return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.releaseLicense', []);
}
- forceRevokeLicense(signer: TSigner, released: string) {
- return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.forceRevokeLicense', [released]);
+ forceReleaseLicense(signer: TSigner, released: string) {
+ return this.helper.executeExtrinsic(signer, 'api.tx.collatorSelection.forceReleaseLicense', [released]);
}
async hasLicense(address: string): Promise<bigint> {